From e251cfe620f6b1a49abe6057452d06188fce16a1 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Wed, 1 Apr 2020 10:15:12 -0700 Subject: [PATCH] ANDROID: Incremental fs: Protect get_fill_block, and add a field Since INCFS_IOC_GET_FILLED_BLOCKS potentially leaks information about usage patterns, and is only useful to someone filling the file, best protect it in the same way as INCFS_IOC_FILL_BLOCKS. Add useful field data_block_out as well Test: incfs_test passes Bug: 152983639 Signed-off-by: Paul Lawrence Change-Id: I126a8cf711e56592479093e9aadbfd0e7f700752 --- fs/incfs/data_mgmt.c | 1 + fs/incfs/vfs.c | 3 ++ include/uapi/linux/incrementalfs.h | 3 ++ .../selftests/filesystems/incfs/incfs_test.c | 52 +++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c index bf509e2b0314..c0248de8949b 100644 --- a/fs/incfs/data_mgmt.c +++ b/fs/incfs/data_mgmt.c @@ -436,6 +436,7 @@ int incfs_get_filled_blocks(struct data_file *df, if (end_index > df->df_total_block_count) end_index = df->df_total_block_count; arg->total_blocks_out = df->df_total_block_count; + arg->data_blocks_out = df->df_data_block_count; if (df->df_header_flags & INCFS_FILE_COMPLETE) { pr_debug("File marked full, fast get_filled_blocks"); diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index ab53a317dcb6..94b6fde95dfa 100644 --- a/fs/incfs/vfs.c +++ b/fs/incfs/vfs.c @@ -1444,6 +1444,9 @@ static long ioctl_get_filled_blocks(struct file *f, void __user *arg) if (!df) return -EINVAL; + if ((uintptr_t)f->private_data != CAN_FILL) + return -EPERM; + if (copy_from_user(&args, args_usr_ptr, sizeof(args)) > 0) return -EINVAL; diff --git a/include/uapi/linux/incrementalfs.h b/include/uapi/linux/incrementalfs.h index 2d535d365ce9..13c3d5173e14 100644 --- a/include/uapi/linux/incrementalfs.h +++ b/include/uapi/linux/incrementalfs.h @@ -321,6 +321,9 @@ struct incfs_get_filled_blocks_args { /* Actual number of blocks in file */ __u32 total_blocks_out; + /* The number of data blocks in file */ + __u32 data_blocks_out; + /* Number of bytes written to range buffer */ __u32 range_buffer_size_out; diff --git a/tools/testing/selftests/filesystems/incfs/incfs_test.c b/tools/testing/selftests/filesystems/incfs/incfs_test.c index f1a239f6376d..639e2fb1544c 100644 --- a/tools/testing/selftests/filesystems/incfs/incfs_test.c +++ b/tools/testing/selftests/filesystems/incfs/incfs_test.c @@ -2189,12 +2189,29 @@ static int validate_ranges(const char *mount_dir, struct test_file *file) int error = TEST_SUCCESS; int i; int range_cnt; + int cmd_fd = -1; + struct incfs_permit_fill permit_fill; fd = open(filename, O_RDONLY); free(filename); if (fd <= 0) return TEST_FAILURE; + error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); + if (error != -1 || errno != EPERM) { + ksft_print_msg("INCFS_IOC_GET_FILLED_BLOCKS not blocked\n"); + error = -EPERM; + goto out; + } + + cmd_fd = open_commands_file(mount_dir); + permit_fill.file_descriptor = fd; + if (ioctl(cmd_fd, INCFS_IOC_PERMIT_FILL, &permit_fill)) { + print_error("INCFS_IOC_PERMIT_FILL failed"); + return -EPERM; + goto out; + } + error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); if (error && errno != ERANGE) goto out; @@ -2212,6 +2229,11 @@ static int validate_ranges(const char *mount_dir, struct test_file *file) goto out; } + if (fba.data_blocks_out != block_cnt) { + error = -EINVAL; + goto out; + } + range_cnt = (block_cnt + 3) / 4; if (range_cnt > 128) range_cnt = 128; @@ -2282,6 +2304,7 @@ static int validate_ranges(const char *mount_dir, struct test_file *file) out: close(fd); + close(cmd_fd); return error; } @@ -2392,6 +2415,7 @@ static int emit_partial_test_file_hash(char *mount_dir, struct test_file *file) static int validate_hash_ranges(const char *mount_dir, struct test_file *file) { + int block_cnt = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; char *filename = concat_file_name(mount_dir, file->name); int fd; struct incfs_filled_range ranges[128]; @@ -2402,6 +2426,8 @@ static int validate_hash_ranges(const char *mount_dir, struct test_file *file) int error = TEST_SUCCESS; int file_blocks = (file->size + INCFS_DATA_FILE_BLOCK_SIZE - 1) / INCFS_DATA_FILE_BLOCK_SIZE; + int cmd_fd = -1; + struct incfs_permit_fill permit_fill; if (file->size <= 4096 / 32 * 4096) return 0; @@ -2411,10 +2437,35 @@ static int validate_hash_ranges(const char *mount_dir, struct test_file *file) if (fd <= 0) return TEST_FAILURE; + error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); + if (error != -1 || errno != EPERM) { + ksft_print_msg("INCFS_IOC_GET_FILLED_BLOCKS not blocked\n"); + error = -EPERM; + goto out; + } + + cmd_fd = open_commands_file(mount_dir); + permit_fill.file_descriptor = fd; + if (ioctl(cmd_fd, INCFS_IOC_PERMIT_FILL, &permit_fill)) { + print_error("INCFS_IOC_PERMIT_FILL failed"); + return -EPERM; + goto out; + } + error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba); if (error) goto out; + if (fba.total_blocks_out <= block_cnt) { + error = -EINVAL; + goto out; + } + + if (fba.data_blocks_out != block_cnt) { + error = -EINVAL; + goto out; + } + if (fba.range_buffer_size_out != sizeof(struct incfs_filled_range)) { error = -EINVAL; goto out; @@ -2427,6 +2478,7 @@ static int validate_hash_ranges(const char *mount_dir, struct test_file *file) } out: + close(cmd_fd); close(fd); return error; }