This series of patches fix some critical bugs such as memory leak in compression
 flows, kernel panic when handling errors, and swapon failure due to newly added
 condition check.
 -----BEGIN PGP SIGNATURE-----
 
 iQIyBAABCgAdFiEE00UqedjCtOrGVvQiQBSofoJIUNIFAmCeTNcACgkQQBSofoJI
 UNKc0Q/3X9Tngns5DpnzQw9kbWochucG/7Vf1HjdvV2gE7IxruDPofGtBdhPdSYV
 uR+nP9dxhXLQQNfWS2KICzdj0yceKCKq8xpnnNdq9SGjVJmUjCD39ByGJV3GMOGM
 fY6dizcywltH7iBQboMZ0Eh3ivPh6ugl5klDo20WzYsH6F/UF7CPSuSJ3K5ezGuY
 T6R3NkqG8v1cS6+5u+teDpmdCCHOCBEeizBFQ6XskNDBavbw7KEA0liwKOv6eghB
 PdoeqYemg1VfOHAKqP6F3o+eSlsT5Ljs0Zmc5x8h8qRS76JK/hb9REFngrcERaIw
 GPDnMPCHniCHMd61z90oqGtMf4PFqLUVnBTdxhxLK5G4+u874dlZLciKWGDIGTLv
 eNU2W+8c9s+KdJAZFJbYN5zVoyJUR5SW7RcYTuvZWt8wX38Ch+FZEGqOC8FxWyWU
 i1WHXZiGeifIlIeqUOPJP5sbslL2hfK5OqMYJotAeIW/E2RyJWnc+Yo2UwvmvXVU
 xPOKFOn9nAAQz2GGgSpvGaWAVfMNqhoLw7/gzwdkabP0EASIzHuW2PCJhp59c1NO
 Fb9eUi7yhgd94vDbYftXRBgAhrUmCd0u+/gySyou4vujWtfWcO+AvOEreV7VRFfL
 Su0bGkBJ03ThFEFAZnY14RenydGSwpk5Fd9wkRy4Qk7mBv9sRg==
 =NeKH
 -----END PGP SIGNATURE-----

Merge tag 'f2fs-5.13-rc1-fix' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs

Pull f2fs fixes from Jaegeuk Kim:
 "This fixes some critical bugs such as memory leak in compression
  flows, kernel panic when handling errors, and swapon failure due to
  newly added condition check"

* tag 'f2fs-5.13-rc1-fix' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs:
  f2fs: return EINVAL for hole cases in swap file
  f2fs: avoid swapon failure by giving a warning first
  f2fs: compress: fix to assign cc.cluster_idx correctly
  f2fs: compress: fix race condition of overwrite vs truncate
  f2fs: compress: fix to free compress page correctly
  f2fs: support iflag change given the mask
  f2fs: avoid null pointer access when handling IPU error
This commit is contained in:
Linus Torvalds 2021-05-14 10:49:20 -07:00
Родитель b5304a4f9a f395183f95
Коммит ac524ece21
5 изменённых файлов: 56 добавлений и 47 удалений

Просмотреть файл

@ -117,19 +117,6 @@ static void f2fs_unlock_rpages(struct compress_ctx *cc, int len)
f2fs_drop_rpages(cc, len, true); f2fs_drop_rpages(cc, len, true);
} }
static void f2fs_put_rpages_mapping(struct address_space *mapping,
pgoff_t start, int len)
{
int i;
for (i = 0; i < len; i++) {
struct page *page = find_get_page(mapping, start + i);
put_page(page);
put_page(page);
}
}
static void f2fs_put_rpages_wbc(struct compress_ctx *cc, static void f2fs_put_rpages_wbc(struct compress_ctx *cc,
struct writeback_control *wbc, bool redirty, int unlock) struct writeback_control *wbc, bool redirty, int unlock)
{ {
@ -158,13 +145,14 @@ int f2fs_init_compress_ctx(struct compress_ctx *cc)
return cc->rpages ? 0 : -ENOMEM; return cc->rpages ? 0 : -ENOMEM;
} }
void f2fs_destroy_compress_ctx(struct compress_ctx *cc) void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse)
{ {
page_array_free(cc->inode, cc->rpages, cc->cluster_size); page_array_free(cc->inode, cc->rpages, cc->cluster_size);
cc->rpages = NULL; cc->rpages = NULL;
cc->nr_rpages = 0; cc->nr_rpages = 0;
cc->nr_cpages = 0; cc->nr_cpages = 0;
cc->cluster_idx = NULL_CLUSTER; if (!reuse)
cc->cluster_idx = NULL_CLUSTER;
} }
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page) void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page)
@ -1036,7 +1024,7 @@ retry:
} }
if (PageUptodate(page)) if (PageUptodate(page))
unlock_page(page); f2fs_put_page(page, 1);
else else
f2fs_compress_ctx_add_page(cc, page); f2fs_compress_ctx_add_page(cc, page);
} }
@ -1046,33 +1034,35 @@ retry:
ret = f2fs_read_multi_pages(cc, &bio, cc->cluster_size, ret = f2fs_read_multi_pages(cc, &bio, cc->cluster_size,
&last_block_in_bio, false, true); &last_block_in_bio, false, true);
f2fs_destroy_compress_ctx(cc); f2fs_put_rpages(cc);
f2fs_destroy_compress_ctx(cc, true);
if (ret) if (ret)
goto release_pages; goto out;
if (bio) if (bio)
f2fs_submit_bio(sbi, bio, DATA); f2fs_submit_bio(sbi, bio, DATA);
ret = f2fs_init_compress_ctx(cc); ret = f2fs_init_compress_ctx(cc);
if (ret) if (ret)
goto release_pages; goto out;
} }
for (i = 0; i < cc->cluster_size; i++) { for (i = 0; i < cc->cluster_size; i++) {
f2fs_bug_on(sbi, cc->rpages[i]); f2fs_bug_on(sbi, cc->rpages[i]);
page = find_lock_page(mapping, start_idx + i); page = find_lock_page(mapping, start_idx + i);
f2fs_bug_on(sbi, !page); if (!page) {
/* page can be truncated */
goto release_and_retry;
}
f2fs_wait_on_page_writeback(page, DATA, true, true); f2fs_wait_on_page_writeback(page, DATA, true, true);
f2fs_compress_ctx_add_page(cc, page); f2fs_compress_ctx_add_page(cc, page);
f2fs_put_page(page, 0);
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
release_and_retry:
f2fs_put_rpages(cc);
f2fs_unlock_rpages(cc, i + 1); f2fs_unlock_rpages(cc, i + 1);
f2fs_put_rpages_mapping(mapping, start_idx, f2fs_destroy_compress_ctx(cc, true);
cc->cluster_size);
f2fs_destroy_compress_ctx(cc);
goto retry; goto retry;
} }
} }
@ -1103,10 +1093,10 @@ retry:
} }
unlock_pages: unlock_pages:
f2fs_put_rpages(cc);
f2fs_unlock_rpages(cc, i); f2fs_unlock_rpages(cc, i);
release_pages: f2fs_destroy_compress_ctx(cc, true);
f2fs_put_rpages_mapping(mapping, start_idx, i); out:
f2fs_destroy_compress_ctx(cc);
return ret; return ret;
} }
@ -1141,7 +1131,7 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
set_cluster_dirty(&cc); set_cluster_dirty(&cc);
f2fs_put_rpages_wbc(&cc, NULL, false, 1); f2fs_put_rpages_wbc(&cc, NULL, false, 1);
f2fs_destroy_compress_ctx(&cc); f2fs_destroy_compress_ctx(&cc, false);
return first_index; return first_index;
} }
@ -1361,7 +1351,7 @@ unlock_continue:
f2fs_put_rpages(cc); f2fs_put_rpages(cc);
page_array_free(cc->inode, cc->cpages, cc->nr_cpages); page_array_free(cc->inode, cc->cpages, cc->nr_cpages);
cc->cpages = NULL; cc->cpages = NULL;
f2fs_destroy_compress_ctx(cc); f2fs_destroy_compress_ctx(cc, false);
return 0; return 0;
out_destroy_crypt: out_destroy_crypt:
@ -1372,7 +1362,8 @@ out_destroy_crypt:
for (i = 0; i < cc->nr_cpages; i++) { for (i = 0; i < cc->nr_cpages; i++) {
if (!cc->cpages[i]) if (!cc->cpages[i])
continue; continue;
f2fs_put_page(cc->cpages[i], 1); f2fs_compress_free_page(cc->cpages[i]);
cc->cpages[i] = NULL;
} }
out_put_cic: out_put_cic:
kmem_cache_free(cic_entry_slab, cic); kmem_cache_free(cic_entry_slab, cic);
@ -1522,7 +1513,7 @@ write:
err = f2fs_write_raw_pages(cc, submitted, wbc, io_type); err = f2fs_write_raw_pages(cc, submitted, wbc, io_type);
f2fs_put_rpages_wbc(cc, wbc, false, 0); f2fs_put_rpages_wbc(cc, wbc, false, 0);
destroy_out: destroy_out:
f2fs_destroy_compress_ctx(cc); f2fs_destroy_compress_ctx(cc, false);
return err; return err;
} }

Просмотреть файл

@ -2287,7 +2287,7 @@ static int f2fs_mpage_readpages(struct inode *inode,
max_nr_pages, max_nr_pages,
&last_block_in_bio, &last_block_in_bio,
rac != NULL, false); rac != NULL, false);
f2fs_destroy_compress_ctx(&cc); f2fs_destroy_compress_ctx(&cc, false);
if (ret) if (ret)
goto set_error_page; goto set_error_page;
} }
@ -2332,7 +2332,7 @@ next_page:
max_nr_pages, max_nr_pages,
&last_block_in_bio, &last_block_in_bio,
rac != NULL, false); rac != NULL, false);
f2fs_destroy_compress_ctx(&cc); f2fs_destroy_compress_ctx(&cc, false);
} }
} }
#endif #endif
@ -3033,7 +3033,7 @@ next:
} }
} }
if (f2fs_compressed_file(inode)) if (f2fs_compressed_file(inode))
f2fs_destroy_compress_ctx(&cc); f2fs_destroy_compress_ctx(&cc, false);
#endif #endif
if (retry) { if (retry) {
index = 0; index = 0;
@ -3801,6 +3801,7 @@ static int f2fs_is_file_aligned(struct inode *inode)
block_t pblock; block_t pblock;
unsigned long nr_pblocks; unsigned long nr_pblocks;
unsigned int blocks_per_sec = BLKS_PER_SEC(sbi); unsigned int blocks_per_sec = BLKS_PER_SEC(sbi);
unsigned int not_aligned = 0;
int ret = 0; int ret = 0;
cur_lblock = 0; cur_lblock = 0;
@ -3833,13 +3834,20 @@ static int f2fs_is_file_aligned(struct inode *inode)
if ((pblock - main_blkaddr) & (blocks_per_sec - 1) || if ((pblock - main_blkaddr) & (blocks_per_sec - 1) ||
nr_pblocks & (blocks_per_sec - 1)) { nr_pblocks & (blocks_per_sec - 1)) {
f2fs_err(sbi, "Swapfile does not align to section"); if (f2fs_is_pinned_file(inode)) {
ret = -EINVAL; f2fs_err(sbi, "Swapfile does not align to section");
goto out; ret = -EINVAL;
goto out;
}
not_aligned++;
} }
cur_lblock += nr_pblocks; cur_lblock += nr_pblocks;
} }
if (not_aligned)
f2fs_warn(sbi, "Swapfile (%u) is not align to section: \n"
"\t1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate()",
not_aligned);
out: out:
return ret; return ret;
} }
@ -3858,6 +3866,7 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
int nr_extents = 0; int nr_extents = 0;
unsigned long nr_pblocks; unsigned long nr_pblocks;
unsigned int blocks_per_sec = BLKS_PER_SEC(sbi); unsigned int blocks_per_sec = BLKS_PER_SEC(sbi);
unsigned int not_aligned = 0;
int ret = 0; int ret = 0;
/* /*
@ -3887,7 +3896,7 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
/* hole */ /* hole */
if (!(map.m_flags & F2FS_MAP_FLAGS)) { if (!(map.m_flags & F2FS_MAP_FLAGS)) {
f2fs_err(sbi, "Swapfile has holes\n"); f2fs_err(sbi, "Swapfile has holes\n");
ret = -ENOENT; ret = -EINVAL;
goto out; goto out;
} }
@ -3896,9 +3905,12 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
if ((pblock - SM_I(sbi)->main_blkaddr) & (blocks_per_sec - 1) || if ((pblock - SM_I(sbi)->main_blkaddr) & (blocks_per_sec - 1) ||
nr_pblocks & (blocks_per_sec - 1)) { nr_pblocks & (blocks_per_sec - 1)) {
f2fs_err(sbi, "Swapfile does not align to section"); if (f2fs_is_pinned_file(inode)) {
ret = -EINVAL; f2fs_err(sbi, "Swapfile does not align to section");
goto out; ret = -EINVAL;
goto out;
}
not_aligned++;
} }
if (cur_lblock + nr_pblocks >= sis->max) if (cur_lblock + nr_pblocks >= sis->max)
@ -3927,6 +3939,11 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
sis->max = cur_lblock; sis->max = cur_lblock;
sis->pages = cur_lblock - 1; sis->pages = cur_lblock - 1;
sis->highest_bit = cur_lblock - 1; sis->highest_bit = cur_lblock - 1;
if (not_aligned)
f2fs_warn(sbi, "Swapfile (%u) is not align to section: \n"
"\t1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate()",
not_aligned);
out: out:
return ret; return ret;
} }
@ -4035,7 +4052,7 @@ out:
return ret; return ret;
bad_bmap: bad_bmap:
f2fs_err(sbi, "Swapfile has holes\n"); f2fs_err(sbi, "Swapfile has holes\n");
return -ENOENT; return -EINVAL;
} }
static int f2fs_swap_activate(struct swap_info_struct *sis, struct file *file, static int f2fs_swap_activate(struct swap_info_struct *sis, struct file *file,

Просмотреть файл

@ -3956,7 +3956,7 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc);
void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed); void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed);
void f2fs_put_page_dic(struct page *page); void f2fs_put_page_dic(struct page *page);
int f2fs_init_compress_ctx(struct compress_ctx *cc); int f2fs_init_compress_ctx(struct compress_ctx *cc);
void f2fs_destroy_compress_ctx(struct compress_ctx *cc); void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse);
void f2fs_init_compress_info(struct f2fs_sb_info *sbi); void f2fs_init_compress_info(struct f2fs_sb_info *sbi);
int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi); int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi);
void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi); void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi);

Просмотреть файл

@ -1817,7 +1817,8 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
u32 masked_flags = fi->i_flags & mask; u32 masked_flags = fi->i_flags & mask;
f2fs_bug_on(F2FS_I_SB(inode), (iflags & ~mask)); /* mask can be shrunk by flags_valid selector */
iflags &= mask;
/* Is it quota file? Do not allow user to mess with it */ /* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode)) if (IS_NOQUOTA(inode))

Просмотреть файл

@ -3574,12 +3574,12 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
return err; return err;
drop_bio: drop_bio:
if (fio->bio) { if (fio->bio && *(fio->bio)) {
struct bio *bio = *(fio->bio); struct bio *bio = *(fio->bio);
bio->bi_status = BLK_STS_IOERR; bio->bi_status = BLK_STS_IOERR;
bio_endio(bio); bio_endio(bio);
fio->bio = NULL; *(fio->bio) = NULL;
} }
return err; return err;
} }