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 <paullawrence@google.com>
Change-Id: I126a8cf711e56592479093e9aadbfd0e7f700752
This commit is contained in:
Paul Lawrence 2020-04-01 10:15:12 -07:00
parent 759d52ee9e
commit e251cfe620
4 changed files with 59 additions and 0 deletions

View file

@ -436,6 +436,7 @@ int incfs_get_filled_blocks(struct data_file *df,
if (end_index > df->df_total_block_count) if (end_index > df->df_total_block_count)
end_index = df->df_total_block_count; end_index = df->df_total_block_count;
arg->total_blocks_out = 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) { if (df->df_header_flags & INCFS_FILE_COMPLETE) {
pr_debug("File marked full, fast get_filled_blocks"); pr_debug("File marked full, fast get_filled_blocks");

View file

@ -1444,6 +1444,9 @@ static long ioctl_get_filled_blocks(struct file *f, void __user *arg)
if (!df) if (!df)
return -EINVAL; return -EINVAL;
if ((uintptr_t)f->private_data != CAN_FILL)
return -EPERM;
if (copy_from_user(&args, args_usr_ptr, sizeof(args)) > 0) if (copy_from_user(&args, args_usr_ptr, sizeof(args)) > 0)
return -EINVAL; return -EINVAL;

View file

@ -321,6 +321,9 @@ struct incfs_get_filled_blocks_args {
/* Actual number of blocks in file */ /* Actual number of blocks in file */
__u32 total_blocks_out; __u32 total_blocks_out;
/* The number of data blocks in file */
__u32 data_blocks_out;
/* Number of bytes written to range buffer */ /* Number of bytes written to range buffer */
__u32 range_buffer_size_out; __u32 range_buffer_size_out;

View file

@ -2189,12 +2189,29 @@ static int validate_ranges(const char *mount_dir, struct test_file *file)
int error = TEST_SUCCESS; int error = TEST_SUCCESS;
int i; int i;
int range_cnt; int range_cnt;
int cmd_fd = -1;
struct incfs_permit_fill permit_fill;
fd = open(filename, O_RDONLY); fd = open(filename, O_RDONLY);
free(filename); free(filename);
if (fd <= 0) if (fd <= 0)
return TEST_FAILURE; 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); error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba);
if (error && errno != ERANGE) if (error && errno != ERANGE)
goto out; goto out;
@ -2212,6 +2229,11 @@ static int validate_ranges(const char *mount_dir, struct test_file *file)
goto out; goto out;
} }
if (fba.data_blocks_out != block_cnt) {
error = -EINVAL;
goto out;
}
range_cnt = (block_cnt + 3) / 4; range_cnt = (block_cnt + 3) / 4;
if (range_cnt > 128) if (range_cnt > 128)
range_cnt = 128; range_cnt = 128;
@ -2282,6 +2304,7 @@ static int validate_ranges(const char *mount_dir, struct test_file *file)
out: out:
close(fd); close(fd);
close(cmd_fd);
return error; 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) 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); char *filename = concat_file_name(mount_dir, file->name);
int fd; int fd;
struct incfs_filled_range ranges[128]; 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 error = TEST_SUCCESS;
int file_blocks = (file->size + INCFS_DATA_FILE_BLOCK_SIZE - 1) / int file_blocks = (file->size + INCFS_DATA_FILE_BLOCK_SIZE - 1) /
INCFS_DATA_FILE_BLOCK_SIZE; INCFS_DATA_FILE_BLOCK_SIZE;
int cmd_fd = -1;
struct incfs_permit_fill permit_fill;
if (file->size <= 4096 / 32 * 4096) if (file->size <= 4096 / 32 * 4096)
return 0; return 0;
@ -2411,10 +2437,35 @@ static int validate_hash_ranges(const char *mount_dir, struct test_file *file)
if (fd <= 0) if (fd <= 0)
return TEST_FAILURE; 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); error = ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &fba);
if (error) if (error)
goto out; 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)) { if (fba.range_buffer_size_out != sizeof(struct incfs_filled_range)) {
error = -EINVAL; error = -EINVAL;
goto out; goto out;
@ -2427,6 +2478,7 @@ static int validate_hash_ranges(const char *mount_dir, struct test_file *file)
} }
out: out:
close(cmd_fd);
close(fd); close(fd);
return error; return error;
} }