linux/fs/btrfs/tree-checker.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) Qu Wenruo 2017.  All rights reserved.
   4 */
   5
   6/*
   7 * The module is used to catch unexpected/corrupted tree block data.
   8 * Such behavior can be caused either by a fuzzed image or bugs.
   9 *
  10 * The objective is to do leaf/node validation checks when tree block is read
  11 * from disk, and check *every* possible member, so other code won't
  12 * need to checking them again.
  13 *
  14 * Due to the potential and unwanted damage, every checker needs to be
  15 * carefully reviewed otherwise so it does not prevent mount of valid images.
  16 */
  17
  18#include "ctree.h"
  19#include "tree-checker.h"
  20#include "disk-io.h"
  21#include "compression.h"
  22
  23/*
  24 * Error message should follow the following format:
  25 * corrupt <type>: <identifier>, <reason>[, <bad_value>]
  26 *
  27 * @type:       leaf or node
  28 * @identifier: the necessary info to locate the leaf/node.
  29 *              It's recommened to decode key.objecitd/offset if it's
  30 *              meaningful.
  31 * @reason:     describe the error
  32 * @bad_value:  optional, it's recommened to output bad value and its
  33 *              expected value (range).
  34 *
  35 * Since comma is used to separate the components, only space is allowed
  36 * inside each component.
  37 */
  38
  39/*
  40 * Append generic "corrupt leaf/node root=%llu block=%llu slot=%d: " to @fmt.
  41 * Allows callers to customize the output.
  42 */
  43__printf(4, 5)
  44__cold
  45static void generic_err(const struct btrfs_fs_info *fs_info,
  46                        const struct extent_buffer *eb, int slot,
  47                        const char *fmt, ...)
  48{
  49        struct va_format vaf;
  50        va_list args;
  51
  52        va_start(args, fmt);
  53
  54        vaf.fmt = fmt;
  55        vaf.va = &args;
  56
  57        btrfs_crit(fs_info,
  58                "corrupt %s: root=%llu block=%llu slot=%d, %pV",
  59                btrfs_header_level(eb) == 0 ? "leaf" : "node",
  60                btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot, &vaf);
  61        va_end(args);
  62}
  63
  64/*
  65 * Customized reporter for extent data item, since its key objectid and
  66 * offset has its own meaning.
  67 */
  68__printf(4, 5)
  69__cold
  70static void file_extent_err(const struct btrfs_fs_info *fs_info,
  71                            const struct extent_buffer *eb, int slot,
  72                            const char *fmt, ...)
  73{
  74        struct btrfs_key key;
  75        struct va_format vaf;
  76        va_list args;
  77
  78        btrfs_item_key_to_cpu(eb, &key, slot);
  79        va_start(args, fmt);
  80
  81        vaf.fmt = fmt;
  82        vaf.va = &args;
  83
  84        btrfs_crit(fs_info,
  85        "corrupt %s: root=%llu block=%llu slot=%d ino=%llu file_offset=%llu, %pV",
  86                btrfs_header_level(eb) == 0 ? "leaf" : "node",
  87                btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot,
  88                key.objectid, key.offset, &vaf);
  89        va_end(args);
  90}
  91
  92/*
  93 * Return 0 if the btrfs_file_extent_##name is aligned to @alignment
  94 * Else return 1
  95 */
  96#define CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, name, alignment)            \
  97({                                                                            \
  98        if (!IS_ALIGNED(btrfs_file_extent_##name((leaf), (fi)), (alignment))) \
  99                file_extent_err((fs_info), (leaf), (slot),                    \
 100        "invalid %s for file extent, have %llu, should be aligned to %u",     \
 101                        (#name), btrfs_file_extent_##name((leaf), (fi)),      \
 102                        (alignment));                                         \
 103        (!IS_ALIGNED(btrfs_file_extent_##name((leaf), (fi)), (alignment)));   \
 104})
 105
 106static int check_extent_data_item(struct btrfs_fs_info *fs_info,
 107                                  struct extent_buffer *leaf,
 108                                  struct btrfs_key *key, int slot)
 109{
 110        struct btrfs_file_extent_item *fi;
 111        u32 sectorsize = fs_info->sectorsize;
 112        u32 item_size = btrfs_item_size_nr(leaf, slot);
 113
 114        if (!IS_ALIGNED(key->offset, sectorsize)) {
 115                file_extent_err(fs_info, leaf, slot,
 116"unaligned file_offset for file extent, have %llu should be aligned to %u",
 117                        key->offset, sectorsize);
 118                return -EUCLEAN;
 119        }
 120
 121        fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
 122
 123        if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) {
 124                file_extent_err(fs_info, leaf, slot,
 125                "invalid type for file extent, have %u expect range [0, %u]",
 126                        btrfs_file_extent_type(leaf, fi),
 127                        BTRFS_FILE_EXTENT_TYPES);
 128                return -EUCLEAN;
 129        }
 130
 131        /*
 132         * Support for new compression/encrption must introduce incompat flag,
 133         * and must be caught in open_ctree().
 134         */
 135        if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) {
 136                file_extent_err(fs_info, leaf, slot,
 137        "invalid compression for file extent, have %u expect range [0, %u]",
 138                        btrfs_file_extent_compression(leaf, fi),
 139                        BTRFS_COMPRESS_TYPES);
 140                return -EUCLEAN;
 141        }
 142        if (btrfs_file_extent_encryption(leaf, fi)) {
 143                file_extent_err(fs_info, leaf, slot,
 144                        "invalid encryption for file extent, have %u expect 0",
 145                        btrfs_file_extent_encryption(leaf, fi));
 146                return -EUCLEAN;
 147        }
 148        if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
 149                /* Inline extent must have 0 as key offset */
 150                if (key->offset) {
 151                        file_extent_err(fs_info, leaf, slot,
 152                "invalid file_offset for inline file extent, have %llu expect 0",
 153                                key->offset);
 154                        return -EUCLEAN;
 155                }
 156
 157                /* Compressed inline extent has no on-disk size, skip it */
 158                if (btrfs_file_extent_compression(leaf, fi) !=
 159                    BTRFS_COMPRESS_NONE)
 160                        return 0;
 161
 162                /* Uncompressed inline extent size must match item size */
 163                if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START +
 164                    btrfs_file_extent_ram_bytes(leaf, fi)) {
 165                        file_extent_err(fs_info, leaf, slot,
 166        "invalid ram_bytes for uncompressed inline extent, have %u expect %llu",
 167                                item_size, BTRFS_FILE_EXTENT_INLINE_DATA_START +
 168                                btrfs_file_extent_ram_bytes(leaf, fi));
 169                        return -EUCLEAN;
 170                }
 171                return 0;
 172        }
 173
 174        /* Regular or preallocated extent has fixed item size */
 175        if (item_size != sizeof(*fi)) {
 176                file_extent_err(fs_info, leaf, slot,
 177        "invalid item size for reg/prealloc file extent, have %u expect %zu",
 178                        item_size, sizeof(*fi));
 179                return -EUCLEAN;
 180        }
 181        if (CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, ram_bytes, sectorsize) ||
 182            CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, disk_bytenr, sectorsize) ||
 183            CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, disk_num_bytes, sectorsize) ||
 184            CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, offset, sectorsize) ||
 185            CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, num_bytes, sectorsize))
 186                return -EUCLEAN;
 187        return 0;
 188}
 189
 190static int check_csum_item(struct btrfs_fs_info *fs_info,
 191                           struct extent_buffer *leaf, struct btrfs_key *key,
 192                           int slot)
 193{
 194        u32 sectorsize = fs_info->sectorsize;
 195        u32 csumsize = btrfs_super_csum_size(fs_info->super_copy);
 196
 197        if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) {
 198                generic_err(fs_info, leaf, slot,
 199                "invalid key objectid for csum item, have %llu expect %llu",
 200                        key->objectid, BTRFS_EXTENT_CSUM_OBJECTID);
 201                return -EUCLEAN;
 202        }
 203        if (!IS_ALIGNED(key->offset, sectorsize)) {
 204                generic_err(fs_info, leaf, slot,
 205        "unaligned key offset for csum item, have %llu should be aligned to %u",
 206                        key->offset, sectorsize);
 207                return -EUCLEAN;
 208        }
 209        if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) {
 210                generic_err(fs_info, leaf, slot,
 211        "unaligned item size for csum item, have %u should be aligned to %u",
 212                        btrfs_item_size_nr(leaf, slot), csumsize);
 213                return -EUCLEAN;
 214        }
 215        return 0;
 216}
 217
 218/*
 219 * Customized reported for dir_item, only important new info is key->objectid,
 220 * which represents inode number
 221 */
 222__printf(4, 5)
 223__cold
 224static void dir_item_err(const struct btrfs_fs_info *fs_info,
 225                         const struct extent_buffer *eb, int slot,
 226                         const char *fmt, ...)
 227{
 228        struct btrfs_key key;
 229        struct va_format vaf;
 230        va_list args;
 231
 232        btrfs_item_key_to_cpu(eb, &key, slot);
 233        va_start(args, fmt);
 234
 235        vaf.fmt = fmt;
 236        vaf.va = &args;
 237
 238        btrfs_crit(fs_info,
 239        "corrupt %s: root=%llu block=%llu slot=%d ino=%llu, %pV",
 240                btrfs_header_level(eb) == 0 ? "leaf" : "node",
 241                btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot,
 242                key.objectid, &vaf);
 243        va_end(args);
 244}
 245
 246static int check_dir_item(struct btrfs_fs_info *fs_info,
 247                          struct extent_buffer *leaf,
 248                          struct btrfs_key *key, int slot)
 249{
 250        struct btrfs_dir_item *di;
 251        u32 item_size = btrfs_item_size_nr(leaf, slot);
 252        u32 cur = 0;
 253
 254        di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
 255        while (cur < item_size) {
 256                u32 name_len;
 257                u32 data_len;
 258                u32 max_name_len;
 259                u32 total_size;
 260                u32 name_hash;
 261                u8 dir_type;
 262
 263                /* header itself should not cross item boundary */
 264                if (cur + sizeof(*di) > item_size) {
 265                        dir_item_err(fs_info, leaf, slot,
 266                "dir item header crosses item boundary, have %zu boundary %u",
 267                                cur + sizeof(*di), item_size);
 268                        return -EUCLEAN;
 269                }
 270
 271                /* dir type check */
 272                dir_type = btrfs_dir_type(leaf, di);
 273                if (dir_type >= BTRFS_FT_MAX) {
 274                        dir_item_err(fs_info, leaf, slot,
 275                        "invalid dir item type, have %u expect [0, %u)",
 276                                dir_type, BTRFS_FT_MAX);
 277                        return -EUCLEAN;
 278                }
 279
 280                if (key->type == BTRFS_XATTR_ITEM_KEY &&
 281                    dir_type != BTRFS_FT_XATTR) {
 282                        dir_item_err(fs_info, leaf, slot,
 283                "invalid dir item type for XATTR key, have %u expect %u",
 284                                dir_type, BTRFS_FT_XATTR);
 285                        return -EUCLEAN;
 286                }
 287                if (dir_type == BTRFS_FT_XATTR &&
 288                    key->type != BTRFS_XATTR_ITEM_KEY) {
 289                        dir_item_err(fs_info, leaf, slot,
 290                        "xattr dir type found for non-XATTR key");
 291                        return -EUCLEAN;
 292                }
 293                if (dir_type == BTRFS_FT_XATTR)
 294                        max_name_len = XATTR_NAME_MAX;
 295                else
 296                        max_name_len = BTRFS_NAME_LEN;
 297
 298                /* Name/data length check */
 299                name_len = btrfs_dir_name_len(leaf, di);
 300                data_len = btrfs_dir_data_len(leaf, di);
 301                if (name_len > max_name_len) {
 302                        dir_item_err(fs_info, leaf, slot,
 303                        "dir item name len too long, have %u max %u",
 304                                name_len, max_name_len);
 305                        return -EUCLEAN;
 306                }
 307                if (name_len + data_len > BTRFS_MAX_XATTR_SIZE(fs_info)) {
 308                        dir_item_err(fs_info, leaf, slot,
 309                        "dir item name and data len too long, have %u max %u",
 310                                name_len + data_len,
 311                                BTRFS_MAX_XATTR_SIZE(fs_info));
 312                        return -EUCLEAN;
 313                }
 314
 315                if (data_len && dir_type != BTRFS_FT_XATTR) {
 316                        dir_item_err(fs_info, leaf, slot,
 317                        "dir item with invalid data len, have %u expect 0",
 318                                data_len);
 319                        return -EUCLEAN;
 320                }
 321
 322                total_size = sizeof(*di) + name_len + data_len;
 323
 324                /* header and name/data should not cross item boundary */
 325                if (cur + total_size > item_size) {
 326                        dir_item_err(fs_info, leaf, slot,
 327                "dir item data crosses item boundary, have %u boundary %u",
 328                                cur + total_size, item_size);
 329                        return -EUCLEAN;
 330                }
 331
 332                /*
 333                 * Special check for XATTR/DIR_ITEM, as key->offset is name
 334                 * hash, should match its name
 335                 */
 336                if (key->type == BTRFS_DIR_ITEM_KEY ||
 337                    key->type == BTRFS_XATTR_ITEM_KEY) {
 338                        char namebuf[max(BTRFS_NAME_LEN, XATTR_NAME_MAX)];
 339
 340                        read_extent_buffer(leaf, namebuf,
 341                                        (unsigned long)(di + 1), name_len);
 342                        name_hash = btrfs_name_hash(namebuf, name_len);
 343                        if (key->offset != name_hash) {
 344                                dir_item_err(fs_info, leaf, slot,
 345                "name hash mismatch with key, have 0x%016x expect 0x%016llx",
 346                                        name_hash, key->offset);
 347                                return -EUCLEAN;
 348                        }
 349                }
 350                cur += total_size;
 351                di = (struct btrfs_dir_item *)((void *)di + total_size);
 352        }
 353        return 0;
 354}
 355
 356/*
 357 * Common point to switch the item-specific validation.
 358 */
 359static int check_leaf_item(struct btrfs_fs_info *fs_info,
 360                           struct extent_buffer *leaf,
 361                           struct btrfs_key *key, int slot)
 362{
 363        int ret = 0;
 364
 365        switch (key->type) {
 366        case BTRFS_EXTENT_DATA_KEY:
 367                ret = check_extent_data_item(fs_info, leaf, key, slot);
 368                break;
 369        case BTRFS_EXTENT_CSUM_KEY:
 370                ret = check_csum_item(fs_info, leaf, key, slot);
 371                break;
 372        case BTRFS_DIR_ITEM_KEY:
 373        case BTRFS_DIR_INDEX_KEY:
 374        case BTRFS_XATTR_ITEM_KEY:
 375                ret = check_dir_item(fs_info, leaf, key, slot);
 376                break;
 377        }
 378        return ret;
 379}
 380
 381static int check_leaf(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf,
 382                      bool check_item_data)
 383{
 384        /* No valid key type is 0, so all key should be larger than this key */
 385        struct btrfs_key prev_key = {0, 0, 0};
 386        struct btrfs_key key;
 387        u32 nritems = btrfs_header_nritems(leaf);
 388        int slot;
 389
 390        /*
 391         * Extent buffers from a relocation tree have a owner field that
 392         * corresponds to the subvolume tree they are based on. So just from an
 393         * extent buffer alone we can not find out what is the id of the
 394         * corresponding subvolume tree, so we can not figure out if the extent
 395         * buffer corresponds to the root of the relocation tree or not. So
 396         * skip this check for relocation trees.
 397         */
 398        if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
 399                struct btrfs_root *check_root;
 400
 401                key.objectid = btrfs_header_owner(leaf);
 402                key.type = BTRFS_ROOT_ITEM_KEY;
 403                key.offset = (u64)-1;
 404
 405                check_root = btrfs_get_fs_root(fs_info, &key, false);
 406                /*
 407                 * The only reason we also check NULL here is that during
 408                 * open_ctree() some roots has not yet been set up.
 409                 */
 410                if (!IS_ERR_OR_NULL(check_root)) {
 411                        struct extent_buffer *eb;
 412
 413                        eb = btrfs_root_node(check_root);
 414                        /* if leaf is the root, then it's fine */
 415                        if (leaf != eb) {
 416                                generic_err(fs_info, leaf, 0,
 417                "invalid nritems, have %u should not be 0 for non-root leaf",
 418                                        nritems);
 419                                free_extent_buffer(eb);
 420                                return -EUCLEAN;
 421                        }
 422                        free_extent_buffer(eb);
 423                }
 424                return 0;
 425        }
 426
 427        if (nritems == 0)
 428                return 0;
 429
 430        /*
 431         * Check the following things to make sure this is a good leaf, and
 432         * leaf users won't need to bother with similar sanity checks:
 433         *
 434         * 1) key ordering
 435         * 2) item offset and size
 436         *    No overlap, no hole, all inside the leaf.
 437         * 3) item content
 438         *    If possible, do comprehensive sanity check.
 439         *    NOTE: All checks must only rely on the item data itself.
 440         */
 441        for (slot = 0; slot < nritems; slot++) {
 442                u32 item_end_expected;
 443                int ret;
 444
 445                btrfs_item_key_to_cpu(leaf, &key, slot);
 446
 447                /* Make sure the keys are in the right order */
 448                if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) {
 449                        generic_err(fs_info, leaf, slot,
 450        "bad key order, prev (%llu %u %llu) current (%llu %u %llu)",
 451                                prev_key.objectid, prev_key.type,
 452                                prev_key.offset, key.objectid, key.type,
 453                                key.offset);
 454                        return -EUCLEAN;
 455                }
 456
 457                /*
 458                 * Make sure the offset and ends are right, remember that the
 459                 * item data starts at the end of the leaf and grows towards the
 460                 * front.
 461                 */
 462                if (slot == 0)
 463                        item_end_expected = BTRFS_LEAF_DATA_SIZE(fs_info);
 464                else
 465                        item_end_expected = btrfs_item_offset_nr(leaf,
 466                                                                 slot - 1);
 467                if (btrfs_item_end_nr(leaf, slot) != item_end_expected) {
 468                        generic_err(fs_info, leaf, slot,
 469                                "unexpected item end, have %u expect %u",
 470                                btrfs_item_end_nr(leaf, slot),
 471                                item_end_expected);
 472                        return -EUCLEAN;
 473                }
 474
 475                /*
 476                 * Check to make sure that we don't point outside of the leaf,
 477                 * just in case all the items are consistent to each other, but
 478                 * all point outside of the leaf.
 479                 */
 480                if (btrfs_item_end_nr(leaf, slot) >
 481                    BTRFS_LEAF_DATA_SIZE(fs_info)) {
 482                        generic_err(fs_info, leaf, slot,
 483                        "slot end outside of leaf, have %u expect range [0, %u]",
 484                                btrfs_item_end_nr(leaf, slot),
 485                                BTRFS_LEAF_DATA_SIZE(fs_info));
 486                        return -EUCLEAN;
 487                }
 488
 489                /* Also check if the item pointer overlaps with btrfs item. */
 490                if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) >
 491                    btrfs_item_ptr_offset(leaf, slot)) {
 492                        generic_err(fs_info, leaf, slot,
 493                "slot overlaps with its data, item end %lu data start %lu",
 494                                btrfs_item_nr_offset(slot) +
 495                                sizeof(struct btrfs_item),
 496                                btrfs_item_ptr_offset(leaf, slot));
 497                        return -EUCLEAN;
 498                }
 499
 500                if (check_item_data) {
 501                        /*
 502                         * Check if the item size and content meet other
 503                         * criteria
 504                         */
 505                        ret = check_leaf_item(fs_info, leaf, &key, slot);
 506                        if (ret < 0)
 507                                return ret;
 508                }
 509
 510                prev_key.objectid = key.objectid;
 511                prev_key.type = key.type;
 512                prev_key.offset = key.offset;
 513        }
 514
 515        return 0;
 516}
 517
 518int btrfs_check_leaf_full(struct btrfs_fs_info *fs_info,
 519                          struct extent_buffer *leaf)
 520{
 521        return check_leaf(fs_info, leaf, true);
 522}
 523
 524int btrfs_check_leaf_relaxed(struct btrfs_fs_info *fs_info,
 525                             struct extent_buffer *leaf)
 526{
 527        return check_leaf(fs_info, leaf, false);
 528}
 529
 530int btrfs_check_node(struct btrfs_fs_info *fs_info, struct extent_buffer *node)
 531{
 532        unsigned long nr = btrfs_header_nritems(node);
 533        struct btrfs_key key, next_key;
 534        int slot;
 535        u64 bytenr;
 536        int ret = 0;
 537
 538        if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(fs_info)) {
 539                btrfs_crit(fs_info,
 540"corrupt node: root=%llu block=%llu, nritems too %s, have %lu expect range [1,%u]",
 541                           btrfs_header_owner(node), node->start,
 542                           nr == 0 ? "small" : "large", nr,
 543                           BTRFS_NODEPTRS_PER_BLOCK(fs_info));
 544                return -EUCLEAN;
 545        }
 546
 547        for (slot = 0; slot < nr - 1; slot++) {
 548                bytenr = btrfs_node_blockptr(node, slot);
 549                btrfs_node_key_to_cpu(node, &key, slot);
 550                btrfs_node_key_to_cpu(node, &next_key, slot + 1);
 551
 552                if (!bytenr) {
 553                        generic_err(fs_info, node, slot,
 554                                "invalid NULL node pointer");
 555                        ret = -EUCLEAN;
 556                        goto out;
 557                }
 558                if (!IS_ALIGNED(bytenr, fs_info->sectorsize)) {
 559                        generic_err(fs_info, node, slot,
 560                        "unaligned pointer, have %llu should be aligned to %u",
 561                                bytenr, fs_info->sectorsize);
 562                        ret = -EUCLEAN;
 563                        goto out;
 564                }
 565
 566                if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) {
 567                        generic_err(fs_info, node, slot,
 568        "bad key order, current (%llu %u %llu) next (%llu %u %llu)",
 569                                key.objectid, key.type, key.offset,
 570                                next_key.objectid, next_key.type,
 571                                next_key.offset);
 572                        ret = -EUCLEAN;
 573                        goto out;
 574                }
 575        }
 576out:
 577        return ret;
 578}
 579