From: Zizhi Wo wozizhi@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB5UKT
--------------------------------
At present, the object->file has the NULL pointer dereference problem. The root cause is that the allocated anon_fd and object->file lifetime are inconsistent, and the user-space invocation to anon_fd uses object->file. Following is the process that triggers the issue:
process A process B cachefiles_ondemand_fd_write_iter fscache_drop_object cachefiles_drop_object fput(object->file) object->file = NULL vfs_iocb_iter_write(object->file...)
Fix this issue by add an additional reference count to the object->file before write, and decrement after it finished.
Signed-off-by: Zizhi Wo wozizhi@huawei.com Signed-off-by: Baokun Li libaokun1@huawei.com --- fs/cachefiles/interface.c | 8 +++++--- fs/cachefiles/internal.h | 2 +- fs/cachefiles/namei.c | 2 +- fs/cachefiles/ondemand.c | 14 +++++++++++--- fs/cachefiles/rdwr.c | 9 +++++---- 5 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index a13089d06e16..9c819d0d626f 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c @@ -270,6 +270,7 @@ static void cachefiles_drop_object(struct fscache_object *_object) struct cachefiles_cache *cache; const struct cred *saved_cred; struct inode *inode; + struct file *file; blkcnt_t i_blocks = 0;
ASSERT(_object); @@ -315,9 +316,10 @@ static void cachefiles_drop_object(struct fscache_object *_object) }
/* clean up file descriptor for non-index object */ - if (object->file) { - fput(object->file); - object->file = NULL; + file = rcu_dereference_protected(object->file, true); + if (file) { + fput(file); + rcu_assign_pointer(object->file, NULL); }
/* note that the object is now inactive */ diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 71c651b305bf..38c33146f4a8 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -54,7 +54,7 @@ struct cachefiles_object { struct cachefiles_lookup_data *lookup_data; /* cached lookup data */ struct dentry *dentry; /* the file/dir representing this object */ struct dentry *backer; /* backing file */ - struct file *file; /* backing file in on-demand mode */ + struct file __rcu *file; /* backing file in on-demand mode */ loff_t i_size; /* object size */ unsigned long flags; #define CACHEFILES_OBJECT_ACTIVE 0 /* T if marked active */ diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 3c7168d0beec..88afa4a80dfb 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -720,7 +720,7 @@ int cachefiles_walk_to_object(struct cachefiles_object *parent, * to force_page_cache_readahead() */ file->f_mode |= FMODE_RANDOM; - object->file = file; + rcu_assign_pointer(object->file, file); }
object->backer = object->dentry; diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c index 4fb5cdfde51c..da5afd162f64 100644 --- a/fs/cachefiles/ondemand.c +++ b/fs/cachefiles/ondemand.c @@ -69,13 +69,20 @@ static ssize_t cachefiles_ondemand_fd_write_iter(struct kiocb *kiocb, struct cachefiles_object *object = kiocb->ki_filp->private_data; size_t len = iter->count; struct kiocb iocb; + struct file *file; int ret;
- if (!object->file) + rcu_read_lock(); + file = rcu_dereference(object->file); + if (!file || !get_file_rcu(file)) + file = NULL; + rcu_read_unlock(); + + if (!file) return -ENOBUFS;
iocb = (struct kiocb) { - .ki_filp = object->file, + .ki_filp = file, .ki_pos = kiocb->ki_pos, .ki_flags = IOCB_WRITE, .ki_ioprio = get_current_ioprio(), @@ -84,7 +91,8 @@ static ssize_t cachefiles_ondemand_fd_write_iter(struct kiocb *kiocb, if (!cachefiles_buffered_ondemand) iocb.ki_flags |= IOCB_DIRECT;
- ret = vfs_iocb_iter_write(object->file, &iocb, iter); + ret = vfs_iocb_iter_write(file, &iocb, iter); + fput(file); if (ret != len) return -EIO; return len; diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c index 0e1992bedf71..453bf7cc88b3 100644 --- a/fs/cachefiles/rdwr.c +++ b/fs/cachefiles/rdwr.c @@ -798,7 +798,7 @@ int cachefiles_read_or_alloc_pages(struct fscache_retrieval *op, static int cachefiles_ondemand_check(struct cachefiles_object *object, loff_t start_pos, size_t len) { - struct file *file = object->file; + struct file *file = rcu_dereference_raw(object->file); size_t remained; loff_t pos; int ret; @@ -892,12 +892,14 @@ int cachefiles_prepare_read(struct fscache_retrieval *op, pgoff_t index) unsigned int n, nr_pages = atomic_read(&op->n_pages); size_t len = nr_pages << PAGE_SHIFT; struct page **pages; + struct file *file; size_t size; int i, ret;
object = container_of(op->op.object, struct cachefiles_object, fscache); if (!object->backer) goto all_enobufs; + file = rcu_dereference_raw(object->file);
/* * 1. Check if there's hole in the requested range, and trigger an @@ -914,8 +916,7 @@ int cachefiles_prepare_read(struct fscache_retrieval *op, pgoff_t index) * to force_page_cache_readahead(). */ page_cache_sync_readahead(d_inode(object->backer)->i_mapping, - &object->file->f_ra, object->file, - start_pos / PAGE_SIZE, nr_pages); + &file->f_ra, file, start_pos / PAGE_SIZE, nr_pages);
size = sizeof(struct cachefiles_kiocb) + nr_pages * sizeof(struct bio_vec); ki = kzalloc(size, GFP_KERNEL); @@ -940,7 +941,7 @@ int cachefiles_prepare_read(struct fscache_retrieval *op, pgoff_t index) } iov_iter_bvec(&ki->iter, READ, ki->bvs, n, n * PAGE_SIZE);
- ki->iocb.ki_filp = object->file; + ki->iocb.ki_filp = file; ki->iocb.ki_pos = start_pos; ki->iocb.ki_ioprio = get_current_ioprio(); ki->op = fscache_get_retrieval(op);