mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-23 07:23:12 +02:00
f2fs: compress: fix UAF of f2fs_inode_info in f2fs_free_dic
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>
This commit is contained in:
parent
8e2a9b6564
commit
39868685c2
|
@ -214,13 +214,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;
|
||||
|
@ -294,13 +294,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;
|
||||
|
@ -422,13 +422,13 @@ static int zstd_init_decompress_ctx(struct decompress_io_ctx *dic)
|
|||
|
||||
workspace_size = zstd_dstream_workspace_bound(max_window_size);
|
||||
|
||||
workspace = f2fs_vmalloc(F2FS_I_SB(dic->inode), workspace_size);
|
||||
workspace = f2fs_vmalloc(dic->sbi, workspace_size);
|
||||
if (!workspace)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
|
@ -464,14 +464,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);
|
||||
|
@ -733,7 +733,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];
|
||||
|
@ -806,7 +806,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);
|
||||
|
||||
|
@ -1632,14 +1632,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;
|
||||
|
||||
|
@ -1669,10 +1668,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)
|
||||
|
@ -1707,6 +1705,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;
|
||||
|
@ -1750,7 +1750,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);
|
||||
|
||||
|
@ -1793,8 +1794,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1536,6 +1536,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 */
|
||||
|
@ -1576,6 +1577,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