Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 8 Jul 2018 18:10:30 +0000 (11:10 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 8 Jul 2018 18:10:30 +0000 (11:10 -0700)
Pull ext4 bugfixes from Ted Ts'o:
 "Bug fixes for ext4; most of which relate to vulnerabilities where a
  maliciously crafted file system image can result in a kernel OOPS or
  hang.

  At least one fix addresses an inline data bug could be triggered by
  userspace without the need of a crafted file system (although it does
  require that the inline data feature be enabled)"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: check superblock mapped prior to committing
  ext4: add more mount time checks of the superblock
  ext4: add more inode number paranoia checks
  ext4: avoid running out of journal credits when appending to an inline file
  jbd2: don't mark block as modified if the handle is out of credits
  ext4: never move the system.data xattr out of the inode body
  ext4: clear i_data in ext4_inode_info when removing inline data
  ext4: include the illegal physical block in the bad map ext4_error msg
  ext4: verify the depth of extent tree in ext4_find_extent()
  ext4: only look at the bg_flags field if it is valid
  ext4: make sure bitmaps and the inode table don't overlap with bg descriptors
  ext4: always check block group bounds in ext4_init_block_bitmap()
  ext4: always verify the magic number in xattr blocks
  ext4: add corruption check in ext4_xattr_set_entry()
  ext4: add warn_on_error mount option

1  2 
fs/ext4/ext4.h
fs/ext4/extents.c
fs/ext4/ialloc.c
fs/ext4/inline.c
fs/ext4/mballoc.c
fs/ext4/super.c

diff --combined fs/ext4/ext4.h
@@@ -817,14 -817,12 +817,14 @@@ static inline void ext4_decode_extra_ti
        time->tv_nsec = (le32_to_cpu(extra) & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS;
  }
  
 -#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode)                        \
 -do {                                                                         \
 -      (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec);               \
 -      if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra))     \
 -              (raw_inode)->xtime ## _extra =                                 \
 -                              ext4_encode_extra_time(&(inode)->xtime);       \
 +#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode)                         \
 +do {                                                                          \
 +      (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec);                \
 +      if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra))     {\
 +              struct timespec ts = timespec64_to_timespec((inode)->xtime);    \
 +              (raw_inode)->xtime ## _extra =                                  \
 +                              ext4_encode_extra_time(&ts);                    \
 +              }                                                               \
  } while (0)
  
  #define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode)                              \
@@@ -836,20 -834,16 +836,20 @@@ do {                                                                           
                                ext4_encode_extra_time(&(einode)->xtime);      \
  } while (0)
  
 -#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode)                        \
 -do {                                                                         \
 -      (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime);       \
 -      if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra))     \
 -              ext4_decode_extra_time(&(inode)->xtime,                        \
 -                                     raw_inode->xtime ## _extra);            \
 -      else                                                                   \
 -              (inode)->xtime.tv_nsec = 0;                                    \
 +#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode)                         \
 +do {                                                                          \
 +      (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime);        \
 +      if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) {    \
 +              struct timespec ts = timespec64_to_timespec((inode)->xtime);    \
 +              ext4_decode_extra_time(&ts,                                     \
 +                                     raw_inode->xtime ## _extra);             \
 +              (inode)->xtime = timespec_to_timespec64(ts);                    \
 +              }                                                               \
 +      else                                                                    \
 +              (inode)->xtime.tv_nsec = 0;                                     \
  } while (0)
  
 +
  #define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode)                              \
  do {                                                                         \
        if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime))                      \
@@@ -1114,6 -1108,7 +1114,7 @@@ struct ext4_inode_info 
  #define EXT4_MOUNT_DIOREAD_NOLOCK     0x400000 /* Enable support for dio read nolocking */
  #define EXT4_MOUNT_JOURNAL_CHECKSUM   0x800000 /* Journal checksums */
  #define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT       0x1000000 /* Journal Async Commit */
+ #define EXT4_MOUNT_WARN_ON_ERROR      0x2000000 /* Trigger WARN_ON on error */
  #define EXT4_MOUNT_DELALLOC           0x8000000 /* Delalloc support */
  #define EXT4_MOUNT_DATA_ERR_ABORT     0x10000000 /* Abort on file data write */
  #define EXT4_MOUNT_BLOCK_VALIDITY     0x20000000 /* Block validity checking */
@@@ -1507,11 -1502,6 +1508,6 @@@ static inline struct ext4_inode_info *E
  static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
  {
        return ino == EXT4_ROOT_INO ||
-               ino == EXT4_USR_QUOTA_INO ||
-               ino == EXT4_GRP_QUOTA_INO ||
-               ino == EXT4_BOOT_LOADER_INO ||
-               ino == EXT4_JOURNAL_INO ||
-               ino == EXT4_RESIZE_INO ||
                (ino >= EXT4_FIRST_INO(sb) &&
                 ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
  }
@@@ -2396,7 -2386,7 +2392,7 @@@ extern int ext4_init_inode_table(struc
  extern void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate);
  
  /* mballoc.c */
 -extern const struct file_operations ext4_seq_mb_groups_fops;
 +extern const struct seq_operations ext4_mb_seq_groups_ops;
  extern long ext4_mb_stats;
  extern long ext4_mb_max_to_scan;
  extern int ext4_mb_init(struct super_block *);
@@@ -3018,9 -3008,6 +3014,6 @@@ extern int ext4_inline_data_fiemap(stru
  struct iomap;
  extern int ext4_inline_data_iomap(struct inode *inode, struct iomap *iomap);
  
- extern int ext4_try_to_evict_inline_data(handle_t *handle,
-                                        struct inode *inode,
-                                        int needed);
  extern int ext4_inline_data_truncate(struct inode *inode, int *has_inline);
  
  extern int ext4_convert_inline_data(struct inode *inode);
diff --combined fs/ext4/extents.c
@@@ -577,7 -577,7 +577,7 @@@ int ext4_ext_precache(struct inode *ino
        down_read(&ei->i_data_sem);
        depth = ext_depth(inode);
  
 -      path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1),
 +      path = kcalloc(depth + 1, sizeof(struct ext4_ext_path),
                       GFP_NOFS);
        if (path == NULL) {
                up_read(&ei->i_data_sem);
@@@ -869,6 -869,12 +869,12 @@@ ext4_find_extent(struct inode *inode, e
  
        eh = ext_inode_hdr(inode);
        depth = ext_depth(inode);
+       if (depth < 0 || depth > EXT4_MAX_EXTENT_DEPTH) {
+               EXT4_ERROR_INODE(inode, "inode has invalid extent depth: %d",
+                                depth);
+               ret = -EFSCORRUPTED;
+               goto err;
+       }
  
        if (path) {
                ext4_ext_drop_refs(path);
        }
        if (!path) {
                /* account possible depth increase */
 -              path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 2),
 +              path = kcalloc(depth + 2, sizeof(struct ext4_ext_path),
                                GFP_NOFS);
                if (unlikely(!path))
                        return ERR_PTR(-ENOMEM);
@@@ -1063,7 -1069,7 +1069,7 @@@ static int ext4_ext_split(handle_t *han
         * We need this to handle errors and free blocks
         * upon them.
         */
 -      ablocks = kzalloc(sizeof(ext4_fsblk_t) * depth, GFP_NOFS);
 +      ablocks = kcalloc(depth, sizeof(ext4_fsblk_t), GFP_NOFS);
        if (!ablocks)
                return -ENOMEM;
  
@@@ -2921,7 -2927,7 +2927,7 @@@ again
                        path[k].p_block =
                                le16_to_cpu(path[k].p_hdr->eh_entries)+1;
        } else {
 -              path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1),
 +              path = kcalloc(depth + 1, sizeof(struct ext4_ext_path),
                               GFP_NOFS);
                if (path == NULL) {
                        ext4_journal_stop(handle);
diff --combined fs/ext4/ialloc.c
@@@ -150,7 -150,16 +150,16 @@@ ext4_read_inode_bitmap(struct super_blo
        }
  
        ext4_lock_group(sb, block_group);
-       if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
+       if (ext4_has_group_desc_csum(sb) &&
+           (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT))) {
+               if (block_group == 0) {
+                       ext4_unlock_group(sb, block_group);
+                       unlock_buffer(bh);
+                       ext4_error(sb, "Inode bitmap for bg 0 marked "
+                                  "uninitialized");
+                       err = -EFSCORRUPTED;
+                       goto out;
+               }
                memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8);
                ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb),
                                     sb->s_blocksize * 8, bh->b_data);
@@@ -994,7 -1003,8 +1003,8 @@@ got
  
                /* recheck and clear flag under lock if we still need to */
                ext4_lock_group(sb, group);
-               if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
+               if (ext4_has_group_desc_csum(sb) &&
+                   (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
                        gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
                        ext4_free_group_clusters_set(sb, gdp,
                                ext4_free_clusters_after_init(sb, group, gdp));
        inode->i_ino = ino + group * EXT4_INODES_PER_GROUP(sb);
        /* This is the optimal IO size (for stat), not the fs block size */
        inode->i_blocks = 0;
 -      inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
 -                                                     current_time(inode);
 +      inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
 +      ei->i_crtime = timespec64_to_timespec(inode->i_mtime);
  
        memset(ei->i_data, 0, sizeof(ei->i_data));
        ei->i_dir_start_lookup = 0;
diff --combined fs/ext4/inline.c
@@@ -437,6 -437,7 +437,7 @@@ static int ext4_destroy_inline_data_nol
  
        memset((void *)ext4_raw_inode(&is.iloc)->i_block,
                0, EXT4_MIN_INLINE_DATA_SIZE);
+       memset(ei->i_data, 0, EXT4_MIN_INLINE_DATA_SIZE);
  
        if (ext4_has_feature_extents(inode->i_sb)) {
                if (S_ISDIR(inode->i_mode) ||
@@@ -886,11 -887,11 +887,11 @@@ retry_journal
        flags |= AOP_FLAG_NOFS;
  
        if (ret == -ENOSPC) {
+               ext4_journal_stop(handle);
                ret = ext4_da_convert_inline_data_to_extent(mapping,
                                                            inode,
                                                            flags,
                                                            fsdata);
-               ext4_journal_stop(handle);
                if (ret == -ENOSPC &&
                    ext4_should_retry_alloc(inode->i_sb, &retries))
                        goto retry_journal;
@@@ -1841,8 -1842,8 +1842,8 @@@ int ext4_inline_data_iomap(struct inod
        iomap->offset = 0;
        iomap->length = min_t(loff_t, ext4_get_inline_size(inode),
                              i_size_read(inode));
 -      iomap->type = 0;
 -      iomap->flags = IOMAP_F_DATA_INLINE;
 +      iomap->type = IOMAP_INLINE;
 +      iomap->flags = 0;
  
  out:
        up_read(&EXT4_I(inode)->xattr_sem);
@@@ -1890,42 -1891,6 +1891,6 @@@ out
        return (error < 0 ? error : 0);
  }
  
- /*
-  * Called during xattr set, and if we can sparse space 'needed',
-  * just create the extent tree evict the data to the outer block.
-  *
-  * We use jbd2 instead of page cache to move data to the 1st block
-  * so that the whole transaction can be committed as a whole and
-  * the data isn't lost because of the delayed page cache write.
-  */
- int ext4_try_to_evict_inline_data(handle_t *handle,
-                                 struct inode *inode,
-                                 int needed)
- {
-       int error;
-       struct ext4_xattr_entry *entry;
-       struct ext4_inode *raw_inode;
-       struct ext4_iloc iloc;
-       error = ext4_get_inode_loc(inode, &iloc);
-       if (error)
-               return error;
-       raw_inode = ext4_raw_inode(&iloc);
-       entry = (struct ext4_xattr_entry *)((void *)raw_inode +
-                                           EXT4_I(inode)->i_inline_off);
-       if (EXT4_XATTR_LEN(entry->e_name_len) +
-           EXT4_XATTR_SIZE(le32_to_cpu(entry->e_value_size)) < needed) {
-               error = -ENOSPC;
-               goto out;
-       }
-       error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
- out:
-       brelse(iloc.bh);
-       return error;
- }
  int ext4_inline_data_truncate(struct inode *inode, int *has_inline)
  {
        handle_t *handle;
diff --combined fs/ext4/mballoc.c
@@@ -2254,7 -2254,7 +2254,7 @@@ out
  
  static void *ext4_mb_seq_groups_start(struct seq_file *seq, loff_t *pos)
  {
 -      struct super_block *sb = seq->private;
 +      struct super_block *sb = PDE_DATA(file_inode(seq->file));
        ext4_group_t group;
  
        if (*pos < 0 || *pos >= ext4_get_groups_count(sb))
  
  static void *ext4_mb_seq_groups_next(struct seq_file *seq, void *v, loff_t *pos)
  {
 -      struct super_block *sb = seq->private;
 +      struct super_block *sb = PDE_DATA(file_inode(seq->file));
        ext4_group_t group;
  
        ++*pos;
  
  static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
  {
 -      struct super_block *sb = seq->private;
 +      struct super_block *sb = PDE_DATA(file_inode(seq->file));
        ext4_group_t group = (ext4_group_t) ((unsigned long) v);
        int i;
        int err, buddy_loaded = 0;
@@@ -2330,13 -2330,34 +2330,13 @@@ static void ext4_mb_seq_groups_stop(str
  {
  }
  
 -static const struct seq_operations ext4_mb_seq_groups_ops = {
 +const struct seq_operations ext4_mb_seq_groups_ops = {
        .start  = ext4_mb_seq_groups_start,
        .next   = ext4_mb_seq_groups_next,
        .stop   = ext4_mb_seq_groups_stop,
        .show   = ext4_mb_seq_groups_show,
  };
  
 -static int ext4_mb_seq_groups_open(struct inode *inode, struct file *file)
 -{
 -      struct super_block *sb = PDE_DATA(inode);
 -      int rc;
 -
 -      rc = seq_open(file, &ext4_mb_seq_groups_ops);
 -      if (rc == 0) {
 -              struct seq_file *m = file->private_data;
 -              m->private = sb;
 -      }
 -      return rc;
 -
 -}
 -
 -const struct file_operations ext4_seq_mb_groups_fops = {
 -      .open           = ext4_mb_seq_groups_open,
 -      .read           = seq_read,
 -      .llseek         = seq_lseek,
 -      .release        = seq_release,
 -};
 -
  static struct kmem_cache *get_groupinfo_cache(int blocksize_bits)
  {
        int cache_index = blocksize_bits - EXT4_MIN_BLOCK_LOG_SIZE;
@@@ -2423,7 -2444,8 +2423,8 @@@ int ext4_mb_add_groupinfo(struct super_
         * initialize bb_free to be able to skip
         * empty groups without initialization
         */
-       if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
+       if (ext4_has_group_desc_csum(sb) &&
+           (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
                meta_group_info[i]->bb_free =
                        ext4_free_clusters_after_init(sb, group, desc);
        } else {
@@@ -2989,7 -3011,8 +2990,8 @@@ ext4_mb_mark_diskspace_used(struct ext4
  #endif
        ext4_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
                      ac->ac_b_ex.fe_len);
-       if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
+       if (ext4_has_group_desc_csum(sb) &&
+           (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
                gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
                ext4_free_group_clusters_set(sb, gdp,
                                             ext4_free_clusters_after_init(sb,
diff --combined fs/ext4/super.c
@@@ -405,6 -405,9 +405,9 @@@ static void ext4_journal_commit_callbac
  
  static void ext4_handle_error(struct super_block *sb)
  {
+       if (test_opt(sb, WARN_ON_ERROR))
+               WARN_ON_ONCE(1);
        if (sb_rdonly(sb))
                return;
  
@@@ -740,6 -743,9 +743,9 @@@ __acquires(bitlock
                va_end(args);
        }
  
+       if (test_opt(sb, WARN_ON_ERROR))
+               WARN_ON_ONCE(1);
        if (test_opt(sb, ERRORS_CONT)) {
                ext4_commit_super(sb, 0);
                return;
@@@ -1267,13 -1273,19 +1273,13 @@@ static bool ext4_dummy_context(struct i
        return DUMMY_ENCRYPTION_ENABLED(EXT4_SB(inode->i_sb));
  }
  
 -static unsigned ext4_max_namelen(struct inode *inode)
 -{
 -      return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize :
 -              EXT4_NAME_LEN;
 -}
 -
  static const struct fscrypt_operations ext4_cryptops = {
        .key_prefix             = "ext4:",
        .get_context            = ext4_get_context,
        .set_context            = ext4_set_context,
        .dummy_context          = ext4_dummy_context,
        .empty_dir              = ext4_empty_dir,
 -      .max_namelen            = ext4_max_namelen,
 +      .max_namelen            = EXT4_NAME_LEN,
  };
  #endif
  
@@@ -1371,7 -1383,8 +1377,8 @@@ enum 
        Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
        Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
        Opt_usrquota, Opt_grpquota, Opt_prjquota, Opt_i_version, Opt_dax,
-       Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit,
+       Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_warn_on_error,
+       Opt_nowarn_on_error, Opt_mblk_io_submit,
        Opt_lazytime, Opt_nolazytime, Opt_debug_want_extra_isize,
        Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity,
        Opt_inode_readahead_blks, Opt_journal_ioprio,
@@@ -1438,6 -1451,8 +1445,8 @@@ static const match_table_t tokens = 
        {Opt_dax, "dax"},
        {Opt_stripe, "stripe=%u"},
        {Opt_delalloc, "delalloc"},
+       {Opt_warn_on_error, "warn_on_error"},
+       {Opt_nowarn_on_error, "nowarn_on_error"},
        {Opt_lazytime, "lazytime"},
        {Opt_nolazytime, "nolazytime"},
        {Opt_debug_want_extra_isize, "debug_want_extra_isize=%u"},
@@@ -1602,6 -1617,8 +1611,8 @@@ static const struct mount_opts 
         MOPT_EXT4_ONLY | MOPT_SET | MOPT_EXPLICIT},
        {Opt_nodelalloc, EXT4_MOUNT_DELALLOC,
         MOPT_EXT4_ONLY | MOPT_CLEAR},
+       {Opt_warn_on_error, EXT4_MOUNT_WARN_ON_ERROR, MOPT_SET},
+       {Opt_nowarn_on_error, EXT4_MOUNT_WARN_ON_ERROR, MOPT_CLEAR},
        {Opt_nojournal_checksum, EXT4_MOUNT_JOURNAL_CHECKSUM,
         MOPT_EXT4_ONLY | MOPT_CLEAR},
        {Opt_journal_checksum, EXT4_MOUNT_JOURNAL_CHECKSUM,
@@@ -2331,6 -2348,7 +2342,7 @@@ static int ext4_check_descriptors(struc
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        ext4_fsblk_t first_block = le32_to_cpu(sbi->s_es->s_first_data_block);
        ext4_fsblk_t last_block;
+       ext4_fsblk_t last_bg_block = sb_block + ext4_bg_num_gdb(sb, 0) + 1;
        ext4_fsblk_t block_bitmap;
        ext4_fsblk_t inode_bitmap;
        ext4_fsblk_t inode_table;
                        if (!sb_rdonly(sb))
                                return 0;
                }
+               if (block_bitmap >= sb_block + 1 &&
+                   block_bitmap <= last_bg_block) {
+                       ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
+                                "Block bitmap for group %u overlaps "
+                                "block group descriptors", i);
+                       if (!sb_rdonly(sb))
+                               return 0;
+               }
                if (block_bitmap < first_block || block_bitmap > last_block) {
                        ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
                               "Block bitmap for group %u not in group "
                        if (!sb_rdonly(sb))
                                return 0;
                }
+               if (inode_bitmap >= sb_block + 1 &&
+                   inode_bitmap <= last_bg_block) {
+                       ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
+                                "Inode bitmap for group %u overlaps "
+                                "block group descriptors", i);
+                       if (!sb_rdonly(sb))
+                               return 0;
+               }
                if (inode_bitmap < first_block || inode_bitmap > last_block) {
                        ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
                               "Inode bitmap for group %u not in group "
                        if (!sb_rdonly(sb))
                                return 0;
                }
+               if (inode_table >= sb_block + 1 &&
+                   inode_table <= last_bg_block) {
+                       ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
+                                "Inode table for group %u overlaps "
+                                "block group descriptors", i);
+                       if (!sb_rdonly(sb))
+                               return 0;
+               }
                if (inode_table < first_block ||
                    inode_table + sbi->s_itb_per_group - 1 > last_block) {
                        ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
@@@ -3097,13 -3139,22 +3133,22 @@@ static ext4_group_t ext4_has_uninit_ita
        ext4_group_t group, ngroups = EXT4_SB(sb)->s_groups_count;
        struct ext4_group_desc *gdp = NULL;
  
+       if (!ext4_has_group_desc_csum(sb))
+               return ngroups;
        for (group = 0; group < ngroups; group++) {
                gdp = ext4_get_group_desc(sb, group, NULL);
                if (!gdp)
                        continue;
  
-               if (!(gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED)))
+               if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED))
+                       continue;
+               if (group != 0)
                        break;
+               ext4_error(sb, "Inode table for bg 0 marked as "
+                          "needing zeroing");
+               if (sb_rdonly(sb))
+                       return ngroups;
        }
  
        return group;
@@@ -3742,6 -3793,13 +3787,13 @@@ static int ext4_fill_super(struct super
                         le32_to_cpu(es->s_log_block_size));
                goto failed_mount;
        }
+       if (le32_to_cpu(es->s_log_cluster_size) >
+           (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+               ext4_msg(sb, KERN_ERR,
+                        "Invalid log cluster size: %u",
+                        le32_to_cpu(es->s_log_cluster_size));
+               goto failed_mount;
+       }
  
        if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {
                ext4_msg(sb, KERN_ERR,
                                        " that may contain inline data");
                        sbi->s_mount_opt &= ~EXT4_MOUNT_DAX;
                }
 -              err = bdev_dax_supported(sb, blocksize);
 -              if (err) {
 +              if (!bdev_dax_supported(sb->s_bdev, blocksize)) {
                        ext4_msg(sb, KERN_ERR,
                                "DAX unsupported by block device. Turning off DAX.");
                        sbi->s_mount_opt &= ~EXT4_MOUNT_DAX;
        } else {
                sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
                sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
+               if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) {
+                       ext4_msg(sb, KERN_ERR, "invalid first ino: %u",
+                                sbi->s_first_ino);
+                       goto failed_mount;
+               }
                if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) ||
                    (!is_power_of_2(sbi->s_inode_size)) ||
                    (sbi->s_inode_size > blocksize)) {
                                 "block size (%d)", clustersize, blocksize);
                        goto failed_mount;
                }
-               if (le32_to_cpu(es->s_log_cluster_size) >
-                   (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
-                       ext4_msg(sb, KERN_ERR,
-                                "Invalid log cluster size: %u",
-                                le32_to_cpu(es->s_log_cluster_size));
-                       goto failed_mount;
-               }
                sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -
                        le32_to_cpu(es->s_log_block_size);
                sbi->s_clusters_per_group =
                }
        } else {
                if (clustersize != blocksize) {
-                       ext4_warning(sb, "fragment/cluster size (%d) != "
-                                    "block size (%d)", clustersize,
-                                    blocksize);
-                       clustersize = blocksize;
+                       ext4_msg(sb, KERN_ERR,
+                                "fragment/cluster size (%d) != "
+                                "block size (%d)", clustersize, blocksize);
+                       goto failed_mount;
                }
                if (sbi->s_blocks_per_group > blocksize * 8) {
                        ext4_msg(sb, KERN_ERR,
                         ext4_blocks_count(es));
                goto failed_mount;
        }
+       if ((es->s_first_data_block == 0) && (es->s_log_block_size == 0) &&
+           (sbi->s_cluster_ratio == 1)) {
+               ext4_msg(sb, KERN_WARNING, "bad geometry: first data "
+                        "block is 0 with a 1k block and cluster size");
+               goto failed_mount;
+       }
        blocks_count = (ext4_blocks_count(es) -
                        le32_to_cpu(es->s_first_data_block) +
                        EXT4_BLOCKS_PER_GROUP(sb) - 1);
                        goto failed_mount;
                }
        }
 -      sbi->s_group_desc = kvmalloc(db_count *
 -                                        sizeof(struct buffer_head *),
 -                                        GFP_KERNEL);
 +      sbi->s_group_desc = kvmalloc_array(db_count,
 +                                         sizeof(struct buffer_head *),
 +                                         GFP_KERNEL);
        if (sbi->s_group_desc == NULL) {
                ext4_msg(sb, KERN_ERR, "not enough memory");
                ret = -ENOMEM;
                goto failed_mount;
        }
+       if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) !=
+           le32_to_cpu(es->s_inodes_count)) {
+               ext4_msg(sb, KERN_ERR, "inodes count not valid: %u vs %llu",
+                        le32_to_cpu(es->s_inodes_count),
+                        ((u64)sbi->s_groups_count * sbi->s_inodes_per_group));
+               ret = -EINVAL;
+               goto failed_mount;
+       }
  
        bgl_lock_init(sbi->s_blockgroup_lock);
  
@@@ -4736,6 -4808,14 +4801,14 @@@ static int ext4_commit_super(struct sup
  
        if (!sbh || block_device_ejected(sb))
                return error;
+       /*
+        * The superblock bh should be mapped, but it might not be if the
+        * device was hot-removed. Not much we can do but fail the I/O.
+        */
+       if (!buffer_mapped(sbh))
+               return error;
        /*
         * If the file system is mounted read-only, don't update the
         * superblock write time.  This avoids updating the superblock