ANDROID: dm: android-verity: Verify header before fetching table

Move header validation logic before reading the verity_table as
an invalid header implies the table is invalid as well.

(Cherry-picked from:
https://partner-android-review.git.corp.google.com/#/c/625203)

BUG: 29940612
Signed-off-by: Badhri Jagan Sridharan <Badhri@google.com>
Change-Id: Ib34d25c0854202f3e70df0a6d0ef1d96f0250c8e
Git-commit: ad2f6cf0be
Git-repo: https://android.googlesource.com/kernel/common
Signed-off-by: Vikram Mulukutla <markivx@codeaurora.org>
This commit is contained in:
Badhri Jagan Sridharan 2016-07-06 17:16:19 -07:00 committed by Vikram Mulukutla
parent ec17b238f7
commit d152189385

View file

@ -365,12 +365,38 @@ static int find_size(dev_t dev, u64 *device_size)
return 0; return 0;
} }
static struct android_metadata *extract_metadata(dev_t dev, static int verify_header(struct android_metadata_header *header)
struct fec_header *fec) {
int retval = -EINVAL;
if (is_userdebug() && le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_DISABLE)
return VERITY_STATE_DISABLE;
if (!(le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_NUMBER) ||
(le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_DISABLE)) {
DMERR("Incorrect magic number");
return retval;
}
if (le32_to_cpu(header->protocol_version) !=
VERITY_METADATA_VERSION) {
DMERR("Unsupported version %u",
le32_to_cpu(header->protocol_version));
return retval;
}
return 0;
}
static int extract_metadata(dev_t dev, struct fec_header *fec,
struct android_metadata **metadata,
bool *verity_enabled)
{ {
struct block_device *bdev; struct block_device *bdev;
struct android_metadata_header *header; struct android_metadata_header *header;
struct android_metadata *uninitialized_var(metadata);
int i; int i;
u32 table_length, copy_length, offset; u32 table_length, copy_length, offset;
u64 metadata_offset; u64 metadata_offset;
@ -381,7 +407,7 @@ static struct android_metadata *extract_metadata(dev_t dev,
if (IS_ERR_OR_NULL(bdev)) { if (IS_ERR_OR_NULL(bdev)) {
DMERR("blkdev_get_by_dev failed"); DMERR("blkdev_get_by_dev failed");
return ERR_CAST(bdev); return -ENODEV;
} }
find_metadata_offset(fec, bdev, &metadata_offset); find_metadata_offset(fec, bdev, &metadata_offset);
@ -399,7 +425,6 @@ static struct android_metadata *extract_metadata(dev_t dev,
(1 << SECTOR_SHIFT), VERITY_METADATA_SIZE); (1 << SECTOR_SHIFT), VERITY_METADATA_SIZE);
if (err) { if (err) {
DMERR("Error while reading verity metadata"); DMERR("Error while reading verity metadata");
metadata = ERR_PTR(err);
goto blkdev_release; goto blkdev_release;
} }
@ -418,24 +443,42 @@ static struct android_metadata *extract_metadata(dev_t dev,
le32_to_cpu(header->protocol_version), le32_to_cpu(header->protocol_version),
le32_to_cpu(header->table_length)); le32_to_cpu(header->table_length));
metadata = kzalloc(sizeof(*metadata), GFP_KERNEL); err = verify_header(header);
if (!metadata) {
if (err == VERITY_STATE_DISABLE) {
DMERR("Mounting root with verity disabled");
*verity_enabled = false;
/* we would still have to read the metadata to figure out
* the data blocks size. Or may be could map the entire
* partition similar to mounting the device.
*
* Reset error as well as the verity_enabled flag is changed.
*/
err = 0;
} else if (err)
goto free_header;
*metadata = kzalloc(sizeof(**metadata), GFP_KERNEL);
if (!*metadata) {
DMERR("kzalloc for metadata failed"); DMERR("kzalloc for metadata failed");
err = -ENOMEM; err = -ENOMEM;
goto free_header; goto free_header;
} }
metadata->header = header; (*metadata)->header = header;
table_length = le32_to_cpu(header->table_length); table_length = le32_to_cpu(header->table_length);
if (table_length == 0 || if (table_length == 0 ||
table_length > (VERITY_METADATA_SIZE - table_length > (VERITY_METADATA_SIZE -
sizeof(struct android_metadata_header))) sizeof(struct android_metadata_header))) {
DMERR("table_length too long");
err = -EINVAL;
goto free_metadata; goto free_metadata;
}
metadata->verity_table = kzalloc(table_length + 1, GFP_KERNEL); (*metadata)->verity_table = kzalloc(table_length + 1, GFP_KERNEL);
if (!metadata->verity_table) { if (!(*metadata)->verity_table) {
DMERR("kzalloc verity_table failed"); DMERR("kzalloc verity_table failed");
err = -ENOMEM; err = -ENOMEM;
goto free_metadata; goto free_metadata;
@ -443,13 +486,15 @@ static struct android_metadata *extract_metadata(dev_t dev,
if (sizeof(struct android_metadata_header) + if (sizeof(struct android_metadata_header) +
table_length <= PAGE_SIZE) { table_length <= PAGE_SIZE) {
memcpy(metadata->verity_table, page_address(payload.page_io[0]) memcpy((*metadata)->verity_table,
page_address(payload.page_io[0])
+ sizeof(struct android_metadata_header), + sizeof(struct android_metadata_header),
table_length); table_length);
} else { } else {
copy_length = PAGE_SIZE - copy_length = PAGE_SIZE -
sizeof(struct android_metadata_header); sizeof(struct android_metadata_header);
memcpy(metadata->verity_table, page_address(payload.page_io[0]) memcpy((*metadata)->verity_table,
page_address(payload.page_io[0])
+ sizeof(struct android_metadata_header), + sizeof(struct android_metadata_header),
copy_length); copy_length);
table_length -= copy_length; table_length -= copy_length;
@ -457,13 +502,13 @@ static struct android_metadata *extract_metadata(dev_t dev,
i = 1; i = 1;
while (table_length != 0) { while (table_length != 0) {
if (table_length > PAGE_SIZE) { if (table_length > PAGE_SIZE) {
memcpy(metadata->verity_table + offset, memcpy((*metadata)->verity_table + offset,
page_address(payload.page_io[i]), page_address(payload.page_io[i]),
PAGE_SIZE); PAGE_SIZE);
offset += PAGE_SIZE; offset += PAGE_SIZE;
table_length -= PAGE_SIZE; table_length -= PAGE_SIZE;
} else { } else {
memcpy(metadata->verity_table + offset, memcpy((*metadata)->verity_table + offset,
page_address(payload.page_io[i]), page_address(payload.page_io[i]),
table_length); table_length);
table_length = 0; table_length = 0;
@ -471,25 +516,23 @@ static struct android_metadata *extract_metadata(dev_t dev,
i++; i++;
} }
} }
metadata->verity_table[table_length] = '\0'; (*metadata)->verity_table[table_length] = '\0';
DMINFO("verity_table: %s", (*metadata)->verity_table);
goto free_payload; goto free_payload;
free_metadata: free_metadata:
kfree(metadata); kfree(*metadata);
free_header: free_header:
kfree(header); kfree(header);
metadata = ERR_PTR(err);
free_payload: free_payload:
for (i = 0; i < payload.number_of_pages; i++) for (i = 0; i < payload.number_of_pages; i++)
if (payload.page_io[i]) if (payload.page_io[i])
__free_page(payload.page_io[i]); __free_page(payload.page_io[i]);
kfree(payload.page_io); kfree(payload.page_io);
DMINFO("verity_table: %s", metadata->verity_table);
blkdev_release: blkdev_release:
blkdev_put(bdev, FMODE_READ); blkdev_put(bdev, FMODE_READ);
return metadata; return err;
} }
/* helper functions to extract properties from dts */ /* helper functions to extract properties from dts */
@ -522,34 +565,6 @@ static int verity_mode(void)
return DM_VERITY_MODE_EIO; return DM_VERITY_MODE_EIO;
} }
static int verify_header(struct android_metadata_header *header)
{
int retval = -EINVAL;
if (is_userdebug() && le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_DISABLE) {
retval = VERITY_STATE_DISABLE;
return retval;
}
if (!(le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_NUMBER) ||
(le32_to_cpu(header->magic_number) ==
VERITY_METADATA_MAGIC_DISABLE)) {
DMERR("Incorrect magic number");
return retval;
}
if (le32_to_cpu(header->protocol_version) !=
VERITY_METADATA_VERSION) {
DMERR("Unsupported version %u",
le32_to_cpu(header->protocol_version));
return retval;
}
return 0;
}
static int verify_verity_signature(char *key_id, static int verify_verity_signature(char *key_id,
struct android_metadata *metadata) struct android_metadata *metadata)
{ {
@ -649,7 +664,7 @@ static int add_as_linear_device(struct dm_target *ti, char *dev)
static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{ {
dev_t uninitialized_var(dev); dev_t uninitialized_var(dev);
struct android_metadata *uninitialized_var(metadata); struct android_metadata *metadata = NULL;
int err = 0, i, mode; int err = 0, i, mode;
char *key_id, *table_ptr, dummy, *target_device, char *key_id, *table_ptr, dummy, *target_device,
*verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS];
@ -717,26 +732,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
return -EINVAL; return -EINVAL;
} }
metadata = extract_metadata(dev, &fec); err = extract_metadata(dev, &fec, &metadata, &verity_enabled);
if (IS_ERR(metadata)) { if (err) {
DMERR("Error while extracting metadata"); DMERR("Error while extracting metadata");
handle_error(); handle_error();
return -EINVAL;
}
err = verify_header(metadata->header);
if (err == VERITY_STATE_DISABLE) {
DMERR("Mounting root with verity disabled");
verity_enabled = false;
/* we would still have to parse the args to figure out
* the data blocks size. Or may be could map the entire
* partition similar to mounting the device.
*/
} else if (err) {
DMERR("Verity header handle error");
handle_error();
goto free_metadata; goto free_metadata;
} }
@ -869,8 +869,10 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
} }
free_metadata: free_metadata:
kfree(metadata->header); if (metadata) {
kfree(metadata->verity_table); kfree(metadata->header);
kfree(metadata->verity_table);
}
kfree(metadata); kfree(metadata);
return err; return err;
} }