Skip to content

Commit

Permalink
ANDROID: fuse-bpf: Fix the issue of abnormal lseek system calls
Browse files Browse the repository at this point in the history
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 <[email protected]> who found and
fixed this issue.

Bug: 319219307
Change-Id: I3aef5fb22751a72ce2bd7674ee081956a89fc752
Signed-off-by: chenyuwen <[email protected]>
Signed-off-by: Paul Lawrence <[email protected]>
  • Loading branch information
PaulLawrenceGoogle authored and bengris32 committed Jun 22, 2024
1 parent 3c4a608 commit 1946bfe
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 8 deletions.
11 changes: 7 additions & 4 deletions fs/fuse/backing.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,23 +454,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;
}
Expand Down
24 changes: 22 additions & 2 deletions tools/testing/selftests/filesystems/fuse/fuse_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,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);

Expand Down Expand Up @@ -367,7 +367,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);

Expand Down Expand Up @@ -1497,6 +1497,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;
Expand All @@ -1511,6 +1513,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);
Expand All @@ -1525,6 +1533,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);
Expand Down
193 changes: 191 additions & 2 deletions tools/testing/selftests/filesystems/fuse/test_bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ int readdir_test(struct fuse_bpf_args *fa)
return FUSE_BPF_BACKING;
}
}
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: {
Expand Down Expand Up @@ -359,6 +359,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)
Expand Down

0 comments on commit 1946bfe

Please sign in to comment.