recent ext4 CVE fixes
cover recent ext4 CVE fixes
A batch of ext4-related CVE fixes were released to other kernels in linux-stable, but don't apply cleanly to 3.18.y. For the most part these are unmodified cherry-picks of Ben Hutchings's 3.16.y backports (exceptions are noted above my Signed-off-by).
Theodore Ts'o (10):
ext4: only look at the bg_flags field if it is valid
ext4: fix check to prevent initializing reserved inodes
ext4: always check block group bounds in ext4_init_block_bitmap()
ext4: fix false negatives *and* false positives in
ext4_check_descriptors()
ext4: add corruption check in ext4_xattr_set_entry()
ext4: always verify the magic number in xattr blocks
ext4: never move the system.data xattr out of the inode body
ext4: add more inode number paranoia checks
jbd2: don't mark block as modified if the handle is out of credits
ext4: avoid running out of journal credits when appending to an inline
file
fs/ext4/balloc.c | 21 ++++++++++++------- fs/ext4/ext4.h | 8 ------- fs/ext4/ialloc.c | 19 ++++++++++++++--- fs/ext4/inline.c | 38 +-------------------------------- fs/ext4/inode.c | 3 ++- fs/ext4/mballoc.c | 6 ++++-- fs/ext4/super.c | 12 +++++++++-- fs/ext4/xattr.c | 49 ++++++++++++++++++++----------------------- fs/jbd2/transaction.c | 2 +- 9 files changed, 70 insertions(+), 88 deletions(-)
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 1/10 ext4: only look at the bg_flags field if it is valid
From: Theodore Ts'o <[email protected]>
commit 8844618d8aa7a9973e7b527d038a2a589665002c upstream.
The bg_flags field in the block group descripts is only valid if the uninit_bg or metadata_csum feature is enabled. We were not consistently looking at this field; fix this.
Also block group #0 must never have uninitialized allocation bitmaps, or need to be zeroed, since that's where the root inode, and other special inodes are set up. Check for these conditions and mark the file system as corrupted if they are detected.
This addresses CVE-2018-10876.
https://bugzilla.kernel.org/show_bug.cgi?id=199403
[bwh: Backported to 3.16:
- ext4_read_block_bitmap_nowait() and ext4_read_inode_bitmap() return a pointer (NULL on error) instead of an error code
- Open-code sb_rdonly()
- Adjust context]
--- fs/ext4/balloc.c | 11 ++++++++++- fs/ext4/ialloc.c | 14 ++++++++++++-- fs/ext4/mballoc.c | 6 ++++-- fs/ext4/super.c | 11 ++++++++++- 4 files changed, 36 insertions(+), 6 deletions(-)
fs/ext4/balloc.c +10 -1
@@ -451,9 +451,18 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group) goto verify; }ext4_lock_group (sb, block_group); -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 ))) {int err; +if (block_group ==0 ) { +ext4_unlock_group (sb, block_group); +unlock_buffer (bh); +ext4_error (sb,"Block bitmap for bg 0 marked " +"uninitialized" ); +put_bh (bh); +return NULL ; + } err =ext4_init_block_bitmap (sb, bh, block_group, desc);set_bitmap_uptodate (bh);set_buffer_uptodate (bh);
fs/ext4/ialloc.c +12 -2
@@ -117,7 +117,16 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) }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" ); +put_bh (bh); +return NULL ; + }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);
@@ -873,7 +882,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));
fs/ext4/mballoc.c +4 -2
@@ -2415,7 +2415,8 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,* 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 {
@@ -2942,7 +2943,8 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,#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,
fs/ext4/super.c +10 -1
@@ -3132,13 +3132,22 @@ static ext4_group_t ext4_has_uninit_itable(struct super_block *sb)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->s_flags &MS_RDONLY ) +return ngroups; }return group;
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 2/10 ext4: fix check to prevent initializing reserved inodes
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit 5012284700775a4e6e3fbe7eac4c543c4874b559 upstream.
Commit 8844618d8aa7: "ext4: only look at the bg_flags field if it is valid" will complain if block group zero does not have the EXT4_BG_INODE_ZEROED flag set. Unfortunately, this is not correct, since a freshly created file system has this flag cleared. It gets almost immediately after the file system is mounted read-write --- but the following somewhat unlikely sequence will end up triggering a false positive report of a corrupted file system:
mkfs.ext4 /dev/vdc
mount -o ro /dev/vdc /vdc
mount -o remount,rw /dev/vdc
Instead, when initializing the inode table for block group zero, test to make sure that itable_unused count is not too large, since that is the case that will result in some or all of the reserved inodes getting cleared.
This fixes the failures reported by Eric Whiteney when running generic/230 and generic/231 in the the nojournal test case.
--- fs/ext4/ialloc.c | 5 ++++- fs/ext4/super.c | 8 +------- 2 files changed, 5 insertions(+), 8 deletions(-)
fs/ext4/ialloc.c +4 -1
@@ -1253,7 +1253,10 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group, ext4_itable_unused_count(sb, gdp)), sbi->s_inodes_per_block); -if ((used_blks <0 ) || (used_blks > sbi->s_itb_per_group)) { +if ((used_blks <0 ) || (used_blks > sbi->s_itb_per_group) || + ((group ==0 ) && ((EXT4_INODES_PER_GROUP (sb) - +ext4_itable_unused_count (sb, gdp)) < +EXT4_FIRST_INO (sb)))) { ext4_error(sb, "Something is wrong with group %u: " "used itable blocks: %d; " "itable unused count: %u",
fs/ext4/super.c +1 -7
@@ -3140,14 +3140,8 @@ static ext4_group_t ext4_has_uninit_itable(struct super_block *sb)if (!gdp)continue ; -if (gdp->bg_flags &cpu_to_le16 (EXT4_BG_INODE_ZEROED )) -continue ; -if (group !=0 ) +if (!(gdp->bg_flags &cpu_to_le16 (EXT4_BG_INODE_ZEROED )))break ; -ext4_error (sb,"Inode table for bg 0 marked as " -"needing zeroing" ); -if (sb->s_flags &MS_RDONLY ) -return ngroups; }return group;
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 3/10 ext4: always check block group bounds in ext4_init_block_bitmap()
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit 819b23f1c501b17b9694325471789e6b5cc2d0d2 upstream.
Regardless of whether the flex_bg feature is set, we should always check to make sure the bits we are setting in the block bitmap are within the block group bounds.
https://bugzilla.kernel.org/show_bug.cgi?id=199865
--- fs/ext4/balloc.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
fs/ext4/balloc.c +3 -7
@@ -184,7 +184,6 @@ static int ext4_init_block_bitmap(struct super_block *sb,unsigned int bit, bit_max;struct ext4_sb_info *sbi =EXT4_SB (sb);ext4_fsblk_t start, tmp; -int flex_bg =0 ;struct ext4_group_info *grp;J_ASSERT_BH (bh,buffer_locked (bh));
@@ -217,22 +216,19 @@ static int ext4_init_block_bitmap(struct super_block *sb, start =ext4_group_first_block_no (sb, block_group); -if (EXT4_HAS_INCOMPAT_FEATURE (sb,EXT4_FEATURE_INCOMPAT_FLEX_BG )) - flex_bg =1 ; -/* Set bits for block and inode bitmaps, and inode table */ tmp =ext4_block_bitmap (sb, gdp); -if (!flex_bg ||ext4_block_in_group (sb, tmp, block_group)) +if (ext4_block_in_group (sb, tmp, block_group))ext4_set_bit (EXT4_B2C (sbi, tmp - start), bh->b_data); tmp =ext4_inode_bitmap (sb, gdp); -if (!flex_bg ||ext4_block_in_group (sb, tmp, block_group)) +if (ext4_block_in_group (sb, tmp, block_group))ext4_set_bit (EXT4_B2C (sbi, tmp - start), bh->b_data); tmp =ext4_inode_table (sb, gdp);for (; tmp <ext4_inode_table (sb, gdp) + sbi->s_itb_per_group; tmp++) { -if (!flex_bg ||ext4_block_in_group (sb, tmp, block_group)) +if (ext4_block_in_group (sb, tmp, block_group))ext4_set_bit (EXT4_B2C (sbi, tmp - start), bh->b_data); }
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 4/10 ext4: fix false negatives *and* false positives in ext4_check_descriptors()
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit 44de022c4382541cebdd6de4465d1f4f465ff1dd upstream.
Ext4_check_descriptors() was getting called before s_gdb_count was initialized. So for file systems w/o the meta_bg feature, allocation bitmaps could overlap the block group descriptors and ext4 wouldn't notice.
For file systems with the meta_bg feature enabled, there was a fencepost error which would cause the ext4_check_descriptors() to incorrectly believe that the block allocation bitmap overlaps with the block group descriptor blocks, and it would reject the mount.
Fix both of these problems.
--- fs/ext4/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/ext4/super.c +2 -2
@@ -2064,7 +2064,7 @@ static int ext4_check_descriptors(struct super_block *sb,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 last_bg_block = sb_block +ext4_bg_num_gdb (sb,0 );ext4_fsblk_t block_bitmap;ext4_fsblk_t inode_bitmap;ext4_fsblk_t inode_table;
@@ -4018,12 +4018,12 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount2; } } + sbi->s_gdb_count = db_count;if (!ext4_check_descriptors (sb, logical_sb_block, &first_not_zeroed)) {ext4_msg (sb,KERN_ERR ,"group descriptors corrupted!" ); goto failed_mount2; } - sbi->s_gdb_count = db_count;get_random_bytes (&sbi->s_next_generation,sizeof (u32));spin_lock_init (&sbi->s_next_gen_lock);
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 5/10 ext4: add corruption check in ext4_xattr_set_entry()
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit 5369a762c882c0b6e9599e4ebbb3a9ba9eee7e2d upstream.
In theory this should have been caught earlier when the xattr list was verified, but in case it got missed, it's simple enough to add check to make sure we don't overrun the xattr buffer.
This addresses CVE-2018-10879.
https://bugzilla.kernel.org/show_bug.cgi?id=200001
[bwh: Backported to 3.16:
- Add inode parameter to ext4_xattr_set_entry() and update callers
- Return -EIO instead of -EFSCORRUPTED on error
- Adjust context]
--- fs/ext4/xattr.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
fs/ext4/xattr.c +14 -8
@@ -637,14 +637,20 @@ static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last, }static int -ext4_xattr_set_entry (struct ext4_xattr_info *i,struct ext4_xattr_search *s) +ext4_xattr_set_entry (struct ext4_xattr_info *i,struct ext4_xattr_search *s, +struct inode *inode) { -struct ext4_xattr_entry *last; +struct ext4_xattr_entry *last, *next;size_t free, min_offs = s->end - s->base, name_len =strlen (i->name);/* Compute min_offs and last. */ last = s->first; -for (; !IS_LAST_ENTRY (last); last =EXT4_XATTR_NEXT (last)) { +for (; !IS_LAST_ENTRY (last); last = next) { + next =EXT4_XATTR_NEXT (last); +if ((void *)next >= s->end) { +EXT4_ERROR_INODE (inode,"corrupted xattr entries" ); +return -EIO ; + }if (!last->e_value_block && last->e_value_size) {size_t offs =le16_to_cpu (last->e_value_offs);if (offs < min_offs)
@@ -825,7 +831,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, ce =NULL ; }ea_bdebug (bs->bh,"modifying in-place" ); - error =ext4_xattr_set_entry (i, s); + error =ext4_xattr_set_entry (i, s, inode);if (!error) {if (!IS_LAST_ENTRY (s->first)) ext4_xattr_rehash(header(s->base),
@@ -877,7 +883,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, s->end = s->base + sb->s_blocksize; } - error =ext4_xattr_set_entry (i, s); + error =ext4_xattr_set_entry (i, s, inode);if (error == -EIO ) goto bad_block;if (error)
@@ -1038,7 +1044,7 @@ int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,if (EXT4_I (inode)->i_extra_isize ==0 )return -ENOSPC ; - error =ext4_xattr_set_entry (i, s); + error =ext4_xattr_set_entry (i, s, inode);if (error) {if (error == -ENOSPC &&ext4_has_inline_data (inode)) {
@@ -1050,7 +1056,7 @@ int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode, error =ext4_xattr_ibody_find (inode, i, is);if (error)return error; - error =ext4_xattr_set_entry (i, s); + error =ext4_xattr_set_entry (i, s, inode); }if (error)return error;
@@ -1076,7 +1082,7 @@ static int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,if (EXT4_I (inode)->i_extra_isize ==0 )return -ENOSPC ; - error =ext4_xattr_set_entry (i, s); + error =ext4_xattr_set_entry (i, s, inode);if (error)return error; header =IHDR (inode,ext4_raw_inode (&is->iloc));
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 6/10 ext4: always verify the magic number in xattr blocks
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit 513f86d73855ce556ea9522b6bfd79f87356dc3a upstream.
If there an inode points to a block which is also some other type of metadata block (such as a block allocation bitmap), the buffer_verified flag can be set when it was validated as that other metadata block type; however, it would make a really terrible external attribute block. The reason why we use the verified flag is to avoid constantly reverifying the block. However, it doesn't take much overhead to make sure the magic number of the xattr block is correct, and this will avoid potential crashes.
This addresses CVE-2018-10879.
https://bugzilla.kernel.org/show_bug.cgi?id=200001
--- fs/ext4/xattr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/ext4/xattr.c +3 -3
@@ -218,12 +218,12 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh) {int error; -if (buffer_verified (bh)) -return 0 ; -if (BHDR (bh)->h_magic !=cpu_to_le32 (EXT4_XATTR_MAGIC ) ||BHDR (bh)->h_blocks !=cpu_to_le32 (1 ))return -EIO ; +if (buffer_verified (bh)) +return 0 ; +if (!ext4_xattr_block_csum_verify (inode, bh->b_blocknr,BHDR (bh)))return -EIO ; error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size,
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 7/10 ext4: never move the system.data xattr out of the inode body
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit 8cdb5240ec5928b20490a2bb34cb87e9a5f40226 upstream.
When expanding the extra isize space, we must never move the system.data xattr out of the inode body. For performance reasons, it doesn't make any sense, and the inline data implementation assumes that system.data xattr is never in the external xattr block.
This addresses CVE-2018-10880
https://bugzilla.kernel.org/show_bug.cgi?id=200005
--- fs/ext4/xattr.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/ext4/xattr.c +5 -0
@@ -1391,6 +1391,11 @@ retry:/* Find the entry best suited to be pushed into EA block */ entry =NULL ;for (; !IS_LAST_ENTRY (last); last =EXT4_XATTR_NEXT (last)) { +/* never move system.data out of the inode */ +if ((last->e_name_len ==4 ) && + (last->e_name_index ==EXT4_XATTR_INDEX_SYSTEM ) && + !memcmp (last->e_name,"data" ,4 )) +continue ; total_size =EXT4_XATTR_SIZE (le32_to_cpu (last->e_value_size)) +EXT4_XATTR_LEN (last->e_name_len);
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 8/10 ext4: add more inode number paranoia checks
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit c37e9e013469521d9adb932d17a1795c139b36db upstream.
If there is a directory entry pointing to a system inode (such as a journal inode), complain and declare the file system to be corrupted.
Also, if the superblock's first inode number field is too small, refuse to mount the file system.
This addresses CVE-2018-10882.
https://bugzilla.kernel.org/show_bug.cgi?id=200069
--- fs/ext4/ext4.h | 5 ----- fs/ext4/inode.c | 3 ++- fs/ext4/super.c | 5 +++++ 3 files changed, 7 insertions(+), 6 deletions(-)
fs/ext4/ext4.h +0 -5
@@ -1386,11 +1386,6 @@ static inline struct timespec ext4_current_time(struct inode *inode)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)); }
fs/ext4/inode.c +2 -1
@@ -3737,7 +3737,8 @@ static int __ext4_get_inode_loc(struct inode *inode,int inodes_per_block, inode_offset; iloc->bh =NULL ; -if (!ext4_valid_inum (sb, inode->i_ino)) +if (inode->i_ino <EXT4_ROOT_INO || + inode->i_ino >le32_to_cpu (EXT4_SB (sb)->s_es->s_inodes_count))return -EIO ; iloc->block_group = (inode->i_ino -1 ) /EXT4_INODES_PER_GROUP (sb);
fs/ext4/super.c +5 -0
@@ -3794,6 +3794,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) }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)) {
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 9/10 jbd2: don't mark block as modified if the handle is out of credits
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit e09463f220ca9a1a1ecfda84fcda658f99a1f12a upstream.
Do not set the b_modified flag in block's journal head should not until after we're sure that jbd2_journal_dirty_metadat() will not abort with an error due to there not being enough space reserved in the jbd2 handle.
Otherwise, future attempts to modify the buffer may lead a large number of spurious errors and warnings.
This addresses CVE-2018-10883.
https://bugzilla.kernel.org/show_bug.cgi?id=200071
[bwh: Backported to 3.16: Drop the added logging statement, as it's on
a code path that doesn't exist here]
--- fs/jbd2/transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/jbd2/transaction.c +1 -1
@@ -1283,11 +1283,11 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)* of the transaction. This needs to be done * once a transaction -bzzz */ - jh->b_modified =1 ;if (handle->h_buffer_credits <=0 ) { ret = -ENOSPC ; goto out_unlock_bh; } + jh->b_modified =1 ; handle->h_buffer_credits--; }
Signature · 1 line
2.19.0.605.g01d371f741-goog
PATCH 10/10 ext4: avoid running out of journal credits when appending to an inline file
In reply to Greg HackmannFrom: Theodore Ts'o <[email protected]>
commit 8bc1379b82b8e809eef77a9fedbb75c6c297be19 upstream.
Use a separate journal transaction if it turns out that we need to convert an inline file to use an data block. Otherwise we could end up failing due to not having journal credits.
This addresses CVE-2018-10883.
https://bugzilla.kernel.org/show_bug.cgi?id=200071
--- fs/ext4/ext4.h | 3 --- fs/ext4/inline.c | 38 +------------------------------------- fs/ext4/xattr.c | 18 ++---------------- 3 files changed, 3 insertions(+), 56 deletions(-)
fs/ext4/ext4.h +0 -3
@@ -2663,9 +2663,6 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode,extern int ext4_inline_data_fiemap (struct inode *inode,struct fiemap_extent_info *fieinfo,int *has_inline); -extern int ext4_try_to_evict_inline_data (handle_t *handle, -struct inode *inode, -int needed);extern void ext4_inline_data_truncate (struct inode *inode,int *has_inline);extern int ext4_convert_inline_data (struct inode *inode);
fs/ext4/inline.c +1 -37
@@ -873,11 +873,11 @@ retry_journal: }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;
@@ -1840,42 +1840,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; -} -void ext4_inline_data_truncate (struct inode *inode,int *has_inline) {handle_t *handle;
fs/ext4/xattr.c +2 -16
@@ -1045,22 +1045,8 @@ int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,if (EXT4_I (inode)->i_extra_isize ==0 )return -ENOSPC ; error =ext4_xattr_set_entry (i, s, inode); -if (error) { -if (error == -ENOSPC && -ext4_has_inline_data (inode)) { - error =ext4_try_to_evict_inline_data (handle, inode, -EXT4_XATTR_LEN (strlen (i->name) + -EXT4_XATTR_SIZE (i->value_len))); -if (error) -return error; - error =ext4_xattr_ibody_find (inode, i, is); -if (error) -return error; - error =ext4_xattr_set_entry (i, s, inode); - } -if (error) -return error; - } +if (error) +return error; header =IHDR (inode,ext4_raw_inode (&is->iloc));if (!IS_LAST_ENTRY (s->first)) { header->h_magic =cpu_to_le32 (EXT4_XATTR_MAGIC );
Signature · 1 line
2.19.0.605.g01d371f741-goog
Quoted 5 lines · Greg Hackmann
A batch of ext4-related CVE fixes were released to other kernels in linux-stable, but don't apply cleanly to 3.18.y. For the most part these are unmodified cherry-picks of Ben Hutchings's 3.16.y backports (exceptions are noted above my Signed-off-by).
Quoted 13 lines
Theodore Ts'o (10):
ext4: only look at thebg_flagsfield if it is valid
ext4: fix check to prevent initializing reserved inodes
ext4: always check block group bounds inext4_init_block_bitmap()
ext4: fix false negatives *and* false positives in
ext4_check_descriptors()
ext4: add corruption check inext4_xattr_set_entry()
ext4: always verify the magic number in xattr blocks
ext4: never move the system.data xattr out of the inode body
ext4: add more inode number paranoia checks
jbd2: don't mark block as modified if the handle is out of credits
ext4: avoid running out of journal credits when appending to an inline
file
All now applied, thanks.
greg k-h