mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-12-18 08:36:21 +01:00
f2fs: compress: fix UAF of f2fs_inode_info in f2fs_free_dic
[ Upstream commit39868685c2] The decompress_io_ctx may be released asynchronously after I/O completion. If this file is deleted immediately after read, and the kworker of processing post_read_wq has not been executed yet due to high workloads, It is possible that the inode(f2fs_inode_info) is evicted and freed before it is used f2fs_free_dic. The UAF case as below: Thread A Thread B - f2fs_decompress_end_io - f2fs_put_dic - queue_work add free_dic work to post_read_wq - do_unlink - iput - evict - call_rcu This file is deleted after read. Thread C kworker to process post_read_wq - rcu_do_batch - f2fs_free_inode - kmem_cache_free inode is freed by rcu - process_scheduled_works - f2fs_late_free_dic - f2fs_free_dic - f2fs_release_decomp_mem read (dic->inode)->i_compress_algorithm This patch store compress_algorithm and sbi in dic to avoid inode UAF. In addition, the previous solution is deprecated in [1] may cause system hang. [1] https://lore.kernel.org/all/c36ab955-c8db-4a8b-a9d0-f07b5f426c3f@kernel.org Cc: Daeho Jeong <daehojeong@google.com> Fixes:bff139b49d("f2fs: handle decompress only post processing in softirq") Signed-off-by: Zhiguo Niu <zhiguo.niu@unisoc.com> Signed-off-by: Baocong Liu <baocong.liu@unisoc.com> Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org> [ In Linux 6.6.y, the f2fs_vmalloc() function parameters are not related to the f2fs_sb_info structure, the code changes for f2fs_vmalloc() have not been backported. ] Signed-off-by: Bin Lan <lanbincn@139.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
e822e902f8
commit
5d604d40cd
|
|
@ -209,13 +209,13 @@ static int lzo_decompress_pages(struct decompress_io_ctx *dic)
|
|||
ret = lzo1x_decompress_safe(dic->cbuf->cdata, dic->clen,
|
||||
dic->rbuf, &dic->rlen);
|
||||
if (ret != LZO_E_OK) {
|
||||
f2fs_err_ratelimited(F2FS_I_SB(dic->inode),
|
||||
f2fs_err_ratelimited(dic->sbi,
|
||||
"lzo decompress failed, ret:%d", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dic->rlen != PAGE_SIZE << dic->log_cluster_size) {
|
||||
f2fs_err_ratelimited(F2FS_I_SB(dic->inode),
|
||||
f2fs_err_ratelimited(dic->sbi,
|
||||
"lzo invalid rlen:%zu, expected:%lu",
|
||||
dic->rlen, PAGE_SIZE << dic->log_cluster_size);
|
||||
return -EIO;
|
||||
|
|
@ -289,13 +289,13 @@ static int lz4_decompress_pages(struct decompress_io_ctx *dic)
|
|||
ret = LZ4_decompress_safe(dic->cbuf->cdata, dic->rbuf,
|
||||
dic->clen, dic->rlen);
|
||||
if (ret < 0) {
|
||||
f2fs_err_ratelimited(F2FS_I_SB(dic->inode),
|
||||
f2fs_err_ratelimited(dic->sbi,
|
||||
"lz4 decompress failed, ret:%d", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (ret != PAGE_SIZE << dic->log_cluster_size) {
|
||||
f2fs_err_ratelimited(F2FS_I_SB(dic->inode),
|
||||
f2fs_err_ratelimited(dic->sbi,
|
||||
"lz4 invalid ret:%d, expected:%lu",
|
||||
ret, PAGE_SIZE << dic->log_cluster_size);
|
||||
return -EIO;
|
||||
|
|
@ -423,7 +423,7 @@ static int zstd_init_decompress_ctx(struct decompress_io_ctx *dic)
|
|||
|
||||
stream = zstd_init_dstream(max_window_size, workspace, workspace_size);
|
||||
if (!stream) {
|
||||
f2fs_err_ratelimited(F2FS_I_SB(dic->inode),
|
||||
f2fs_err_ratelimited(dic->sbi,
|
||||
"%s zstd_init_dstream failed", __func__);
|
||||
vfree(workspace);
|
||||
return -EIO;
|
||||
|
|
@ -459,14 +459,14 @@ static int zstd_decompress_pages(struct decompress_io_ctx *dic)
|
|||
|
||||
ret = zstd_decompress_stream(stream, &outbuf, &inbuf);
|
||||
if (zstd_is_error(ret)) {
|
||||
f2fs_err_ratelimited(F2FS_I_SB(dic->inode),
|
||||
f2fs_err_ratelimited(dic->sbi,
|
||||
"%s zstd_decompress_stream failed, ret: %d",
|
||||
__func__, zstd_get_error_code(ret));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dic->rlen != outbuf.pos) {
|
||||
f2fs_err_ratelimited(F2FS_I_SB(dic->inode),
|
||||
f2fs_err_ratelimited(dic->sbi,
|
||||
"%s ZSTD invalid rlen:%zu, expected:%lu",
|
||||
__func__, dic->rlen,
|
||||
PAGE_SIZE << dic->log_cluster_size);
|
||||
|
|
@ -726,7 +726,7 @@ static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
|
|||
|
||||
void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
|
||||
struct f2fs_sb_info *sbi = dic->sbi;
|
||||
struct f2fs_inode_info *fi = F2FS_I(dic->inode);
|
||||
const struct f2fs_compress_ops *cops =
|
||||
f2fs_cops[fi->i_compress_algorithm];
|
||||
|
|
@ -799,7 +799,7 @@ void f2fs_end_read_compressed_page(struct page *page, bool failed,
|
|||
{
|
||||
struct decompress_io_ctx *dic =
|
||||
(struct decompress_io_ctx *)page_private(page);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
|
||||
struct f2fs_sb_info *sbi = dic->sbi;
|
||||
|
||||
dec_page_count(sbi, F2FS_RD_DATA);
|
||||
|
||||
|
|
@ -1579,14 +1579,13 @@ static inline bool allow_memalloc_for_decomp(struct f2fs_sb_info *sbi,
|
|||
static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
|
||||
bool pre_alloc)
|
||||
{
|
||||
const struct f2fs_compress_ops *cops =
|
||||
f2fs_cops[F2FS_I(dic->inode)->i_compress_algorithm];
|
||||
const struct f2fs_compress_ops *cops = f2fs_cops[dic->compress_algorithm];
|
||||
int i;
|
||||
|
||||
if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
|
||||
if (!allow_memalloc_for_decomp(dic->sbi, pre_alloc))
|
||||
return 0;
|
||||
|
||||
dic->tpages = page_array_alloc(F2FS_I_SB(dic->inode), dic->cluster_size);
|
||||
dic->tpages = page_array_alloc(dic->sbi, dic->cluster_size);
|
||||
if (!dic->tpages)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -1616,10 +1615,9 @@ static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
|
|||
static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
|
||||
bool bypass_destroy_callback, bool pre_alloc)
|
||||
{
|
||||
const struct f2fs_compress_ops *cops =
|
||||
f2fs_cops[F2FS_I(dic->inode)->i_compress_algorithm];
|
||||
const struct f2fs_compress_ops *cops = f2fs_cops[dic->compress_algorithm];
|
||||
|
||||
if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
|
||||
if (!allow_memalloc_for_decomp(dic->sbi, pre_alloc))
|
||||
return;
|
||||
|
||||
if (!bypass_destroy_callback && cops->destroy_decompress_ctx)
|
||||
|
|
@ -1654,6 +1652,8 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
|
|||
|
||||
dic->magic = F2FS_COMPRESSED_PAGE_MAGIC;
|
||||
dic->inode = cc->inode;
|
||||
dic->sbi = sbi;
|
||||
dic->compress_algorithm = F2FS_I(cc->inode)->i_compress_algorithm;
|
||||
atomic_set(&dic->remaining_pages, cc->nr_cpages);
|
||||
dic->cluster_idx = cc->cluster_idx;
|
||||
dic->cluster_size = cc->cluster_size;
|
||||
|
|
@ -1697,7 +1697,8 @@ static void f2fs_free_dic(struct decompress_io_ctx *dic,
|
|||
bool bypass_destroy_callback)
|
||||
{
|
||||
int i;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
|
||||
/* use sbi in dic to avoid UFA of dic->inode*/
|
||||
struct f2fs_sb_info *sbi = dic->sbi;
|
||||
|
||||
f2fs_release_decomp_mem(dic, bypass_destroy_callback, true);
|
||||
|
||||
|
|
@ -1740,8 +1741,7 @@ static void f2fs_put_dic(struct decompress_io_ctx *dic, bool in_task)
|
|||
f2fs_free_dic(dic, false);
|
||||
} else {
|
||||
INIT_WORK(&dic->free_work, f2fs_late_free_dic);
|
||||
queue_work(F2FS_I_SB(dic->inode)->post_read_wq,
|
||||
&dic->free_work);
|
||||
queue_work(dic->sbi->post_read_wq, &dic->free_work);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1493,6 +1493,7 @@ struct compress_io_ctx {
|
|||
struct decompress_io_ctx {
|
||||
u32 magic; /* magic number to indicate page is compressed */
|
||||
struct inode *inode; /* inode the context belong to */
|
||||
struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */
|
||||
pgoff_t cluster_idx; /* cluster index number */
|
||||
unsigned int cluster_size; /* page count in cluster */
|
||||
unsigned int log_cluster_size; /* log of cluster size */
|
||||
|
|
@ -1533,6 +1534,7 @@ struct decompress_io_ctx {
|
|||
|
||||
bool failed; /* IO error occurred before decompression? */
|
||||
bool need_verity; /* need fs-verity verification after decompression? */
|
||||
unsigned char compress_algorithm; /* backup algorithm type */
|
||||
void *private; /* payload buffer for specified decompression algorithm */
|
||||
void *private2; /* extra payload buffer */
|
||||
struct work_struct verity_work; /* work to verify the decompressed pages */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user