From 204160394a5955dab928a6ef0353c177e2a569e6 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Tue, 9 Jan 2024 10:02:00 -0800 Subject: [PATCH] ANDROID: fuse-bpf: Fix the issue of abnormal lseek system calls fuse_lseek_backing was returning the offset as an int, which would then be treated as an ERR if in the range 4G-4096 and 4G. Although the call would appear to work correctly, the file position would be incorrect according to a subsequent fseek with SEEK_CUR. Based on a change by chenyuwen who found and fixed this issue. Bug: 319219307 Change-Id: I3aef5fb22751a72ce2bd7674ee081956a89fc752 Signed-off-by: chenyuwen Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 11 +- .../selftests/filesystems/fuse/fuse_test.c | 24 ++- .../selftests/filesystems/fuse/test_bpf.c | 193 +++++++++++++++++- 3 files changed, 220 insertions(+), 8 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 6ca74987f7da..0bc637db568f 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -401,23 +401,26 @@ int fuse_lseek_backing(struct fuse_bpf_args *fa, struct file *file, loff_t offse struct file *backing_file = fuse_file->backing_file; loff_t ret; - /* TODO: Handle changing of the file handle */ if (offset == 0) { if (whence == SEEK_CUR) { flo->offset = file->f_pos; - return flo->offset; + return 0; } if (whence == SEEK_SET) { flo->offset = vfs_setpos(file, 0, 0); - return flo->offset; + return 0; } } inode_lock(file->f_inode); backing_file->f_pos = file->f_pos; ret = vfs_llseek(backing_file, fli->offset, fli->whence); - flo->offset = ret; + + if (!IS_ERR(ERR_PTR(ret))) { + flo->offset = ret; + ret = 0; + } inode_unlock(file->f_inode); return ret; } diff --git a/tools/testing/selftests/filesystems/fuse/fuse_test.c b/tools/testing/selftests/filesystems/fuse/fuse_test.c index ad24ed48853e..c31f6fdcf61d 100644 --- a/tools/testing/selftests/filesystems/fuse/fuse_test.c +++ b/tools/testing/selftests/filesystems/fuse/fuse_test.c @@ -255,7 +255,7 @@ static int bpf_test_partial(const char *mount_dir) TEST(src_fd = open(ft_src, O_DIRECTORY | O_RDONLY | O_CLOEXEC), src_fd != -1); TESTEQUAL(create_file(src_fd, s(test_name), 1, 2), 0); - TESTEQUAL(install_elf_bpf("test_bpf.bpf", "test_trace", + TESTEQUAL(install_elf_bpf("test_bpf.bpf", "test_partial", &bpf_fd, NULL, NULL), 0); TESTEQUAL(mount_fuse(mount_dir, bpf_fd, src_fd, &fuse_dev), 0); @@ -363,7 +363,7 @@ static int bpf_test_readdir(const char *mount_dir) src_fd != -1); TESTEQUAL(create_file(src_fd, s(names[0]), 1, 2), 0); TESTEQUAL(create_file(src_fd, s(names[1]), 1, 2), 0); - TESTEQUAL(install_elf_bpf("test_bpf.bpf", "test_trace", + TESTEQUAL(install_elf_bpf("test_bpf.bpf", "test_partial", &bpf_fd, NULL, NULL), 0); TESTEQUAL(mount_fuse(mount_dir, bpf_fd, src_fd, &fuse_dev), 0); @@ -1490,6 +1490,8 @@ static int bpf_test_statfs(const char *mount_dir) static int bpf_test_lseek(const char *mount_dir) { const char *file = "real"; + const char *sparse_file = "sparse"; + const off_t sparse_length = 0x100000000u; const char *test_data = "data"; int result = TEST_FAILURE; int src_fd = -1; @@ -1504,6 +1506,12 @@ static int bpf_test_lseek(const char *mount_dir) TESTEQUAL(write(fd, test_data, strlen(test_data)), strlen(test_data)); TESTSYSCALL(close(fd)); fd = -1; + TEST(fd = openat(src_fd, sparse_file, O_CREAT | O_RDWR | O_CLOEXEC, + 0777), + fd != -1); + TESTSYSCALL(ftruncate(fd, sparse_length)); + TESTSYSCALL(close(fd)); + fd = -1; TESTEQUAL(install_elf_bpf("test_bpf.bpf", "test_trace", &bpf_fd, NULL, NULL), 0); TESTEQUAL(mount_fuse(mount_dir, bpf_fd, src_fd, &fuse_dev), 0); @@ -1518,6 +1526,18 @@ static int bpf_test_lseek(const char *mount_dir) TESTEQUAL(bpf_test_trace("lseek"), 0); TESTEQUAL(lseek(fd, 1, SEEK_DATA), 1); TESTEQUAL(bpf_test_trace("lseek"), 0); + TESTSYSCALL(close(fd)); + fd = -1; + + TEST(fd = s_open(s_path(s(mount_dir), s(sparse_file)), + O_RDONLY | O_CLOEXEC), + fd != -1); + TESTEQUAL(lseek(fd, -256, SEEK_END), sparse_length - 256); + TESTEQUAL(lseek(fd, 0, SEEK_CUR), sparse_length - 256); + + TESTSYSCALL(close(fd)); + fd = -1; + result = TEST_SUCCESS; out: close(fd); diff --git a/tools/testing/selftests/filesystems/fuse/test_bpf.c b/tools/testing/selftests/filesystems/fuse/test_bpf.c index a014b915c059..be5f59ad8343 100644 --- a/tools/testing/selftests/filesystems/fuse/test_bpf.c +++ b/tools/testing/selftests/filesystems/fuse/test_bpf.c @@ -28,9 +28,9 @@ int readdir_test(struct fuse_bpf_args *fa) } } -SEC("test_trace") +SEC("test_partial") /* return FUSE_BPF_BACKING to use backing fs, 0 to pass to usermode */ -int trace_test(struct fuse_bpf_args *fa) +int partial_test(struct fuse_bpf_args *fa) { switch (fa->opcode) { case FUSE_LOOKUP | FUSE_PREFILTER: { @@ -329,6 +329,195 @@ int trace_test(struct fuse_bpf_args *fa) } } +SEC("test_trace") +/* return FUSE_BPF_BACKING to use backing fs, 0 to pass to usermode */ +int trace_test(struct fuse_bpf_args *fa) +{ + switch (fa->opcode) { + case FUSE_LOOKUP | FUSE_PREFILTER: { + /* real and partial use backing file */ + const char *name = fa->in_args[0].value; + + bpf_printk("lookup %s", name); + return FUSE_BPF_BACKING; + } + + case FUSE_ACCESS | FUSE_PREFILTER: { + bpf_printk("Access: %d", fa->nodeid); + return FUSE_BPF_BACKING; + } + + case FUSE_CREATE | FUSE_PREFILTER: + bpf_printk("Create: %d", fa->nodeid); + return FUSE_BPF_BACKING; + + case FUSE_MKNOD | FUSE_PREFILTER: { + const struct fuse_mknod_in *fmi = fa->in_args[0].value; + const char *name = fa->in_args[1].value; + + bpf_printk("mknod %s %x %x", name, fmi->rdev | fmi->mode, fmi->umask); + return FUSE_BPF_BACKING; + } + + case FUSE_MKDIR | FUSE_PREFILTER: { + const struct fuse_mkdir_in *fmi = fa->in_args[0].value; + const char *name = fa->in_args[1].value; + + bpf_printk("mkdir %s %x %x", name, fmi->mode, fmi->umask); + return FUSE_BPF_BACKING; + } + + case FUSE_RMDIR | FUSE_PREFILTER: { + const char *name = fa->in_args[0].value; + + bpf_printk("rmdir %s", name); + return FUSE_BPF_BACKING; + } + + case FUSE_RENAME | FUSE_PREFILTER: { + const char *oldname = fa->in_args[1].value; + const char *newname = fa->in_args[2].value; + + bpf_printk("rename from %s", oldname); + bpf_printk("rename to %s", newname); + return FUSE_BPF_BACKING; + } + + case FUSE_RENAME2 | FUSE_PREFILTER: { + const struct fuse_rename2_in *fri = fa->in_args[0].value; + uint32_t flags = fri->flags; + const char *oldname = fa->in_args[1].value; + const char *newname = fa->in_args[2].value; + + bpf_printk("rename(%x) from %s", flags, oldname); + bpf_printk("rename to %s", newname); + return FUSE_BPF_BACKING; + } + + case FUSE_UNLINK | FUSE_PREFILTER: { + const char *name = fa->in_args[0].value; + + bpf_printk("unlink %s", name); + return FUSE_BPF_BACKING; + } + + case FUSE_LINK | FUSE_PREFILTER: { + const struct fuse_link_in *fli = fa->in_args[0].value; + const char *link_name = fa->in_args[1].value; + + bpf_printk("link %d %s", fli->oldnodeid, link_name); + return FUSE_BPF_BACKING; + } + + case FUSE_SYMLINK | FUSE_PREFILTER: { + const char *link_name = fa->in_args[0].value; + const char *link_dest = fa->in_args[1].value; + + bpf_printk("symlink from %s", link_name); + bpf_printk("symlink to %s", link_dest); + return FUSE_BPF_BACKING; + } + + case FUSE_READLINK | FUSE_PREFILTER: { + const char *link_name = fa->in_args[0].value; + + bpf_printk("readlink from", link_name); + return FUSE_BPF_BACKING; + } + + case FUSE_OPEN | FUSE_PREFILTER: { + bpf_printk("open"); + return FUSE_BPF_BACKING; + } + + case FUSE_OPEN | FUSE_POSTFILTER: + bpf_printk("open postfilter"); + return FUSE_BPF_USER_FILTER; + + case FUSE_READ | FUSE_PREFILTER: { + const struct fuse_read_in *fri = fa->in_args[0].value; + + bpf_printk("read %llu", fri->offset); + return FUSE_BPF_BACKING; + } + + case FUSE_GETATTR | FUSE_PREFILTER: { + bpf_printk("getattr"); + return FUSE_BPF_BACKING; + } + + case FUSE_SETATTR | FUSE_PREFILTER: { + bpf_printk("setattr"); + return FUSE_BPF_BACKING; + } + + case FUSE_OPENDIR | FUSE_PREFILTER: { + bpf_printk("opendir"); + return FUSE_BPF_BACKING; + } + + case FUSE_READDIR | FUSE_PREFILTER: { + bpf_printk("readdir"); + return FUSE_BPF_BACKING; + } + + case FUSE_FLUSH | FUSE_PREFILTER: { + bpf_printk("Flush"); + return FUSE_BPF_BACKING; + } + + case FUSE_GETXATTR | FUSE_PREFILTER: { + const char *name = fa->in_args[1].value; + + bpf_printk("getxattr %s", name); + return FUSE_BPF_BACKING; + } + + case FUSE_LISTXATTR | FUSE_PREFILTER: { + const char *name = fa->in_args[1].value; + + bpf_printk("listxattr %s", name); + return FUSE_BPF_BACKING; + } + + case FUSE_SETXATTR | FUSE_PREFILTER: { + const char *name = fa->in_args[1].value; + unsigned int size = fa->in_args[2].size; + + bpf_printk("setxattr %s %u", name, size); + return FUSE_BPF_BACKING; + } + + case FUSE_REMOVEXATTR | FUSE_PREFILTER: { + const char *name = fa->in_args[0].value; + + bpf_printk("removexattr %s", name); + return FUSE_BPF_BACKING; + } + + case FUSE_CANONICAL_PATH | FUSE_PREFILTER: { + bpf_printk("canonical_path"); + return FUSE_BPF_BACKING; + } + + case FUSE_STATFS | FUSE_PREFILTER: { + bpf_printk("statfs"); + return FUSE_BPF_BACKING; + } + + case FUSE_LSEEK | FUSE_PREFILTER: { + const struct fuse_lseek_in *fli = fa->in_args[0].value; + + bpf_printk("lseek type:%d, offset:%lld", fli->whence, fli->offset); + return FUSE_BPF_BACKING; + } + + default: + bpf_printk("Unknown opcode %d", fa->opcode); + return FUSE_BPF_BACKING; + } +} + SEC("test_hidden") int trace_hidden(struct fuse_bpf_args *fa) {