diff --git a/tools/rimage/src/elf_file.c b/tools/rimage/src/elf_file.c index 991766d45062..c40d3a69abfb 100644 --- a/tools/rimage/src/elf_file.c +++ b/tools/rimage/src/elf_file.c @@ -531,10 +531,23 @@ int elf_strings_read_by_index(const struct elf_file *elf, int index, struct elf_ int elf_strings_get(const struct elf_strings *strings, int index, char **str) { - if (index >= strings->section.header.data.size) + size_t size = strings->section.header.data.size; + const char *base = (const char *)strings->section.data; + + if (index < 0 || (size_t)index >= size) + return -EINVAL; + + /* + * A crafted ELF may provide a string table that is not NUL-terminated; + * make sure a terminator exists within the section before strdup() so + * it cannot read past the end of the mapped section. + */ + if (strnlen(base + index, size - index) == size - index) { + fprintf(stderr, "error: unterminated string in string table\n"); return -EINVAL; + } - *str = strdup((const char *)strings->section.data + index); + *str = strdup(base + index); if (!*str) return -ENOMEM; diff --git a/tools/rimage/src/ext_manifest.c b/tools/rimage/src/ext_manifest.c index 03a157c08397..8beaa4943a7b 100644 --- a/tools/rimage/src/ext_manifest.c +++ b/tools/rimage/src/ext_manifest.c @@ -69,6 +69,12 @@ static int ext_man_validate(uint32_t section_size, const void *section_data) /* copy each head to local struct to omit memory align issues */ while (offset < section_size) { + /* make sure a whole header remains before copying it out */ + if (offset + sizeof(head) > section_size) { + fprintf(stderr, + "error: extended manifest header straddles section end\n"); + return -EINVAL; + } memcpy(&head, &sbuf[offset], sizeof(head)); fprintf(stdout, "Extended manifest found module, type: 0x%04X size: 0x%04X (%4d) offset: 0x%04X\n", head.type, head.elem_size, head.elem_size, offset); @@ -150,10 +156,12 @@ int ext_man_write(struct image *image) /* validate metadata section */ ret = ext_man_validate(ext_man->full_size - ext_man->header_size, (char *)ext_man + ext_man->header_size); - if (ret) { - ret = -errno; + if (ret) + /* ext_man_validate() already returns a negative errno; do not + * overwrite it with -errno, which is not set on validation + * failure and would mask the error + */ goto out; - } /* write extended metadata to file */ count = fwrite(ext_man, 1, ext_man->full_size, image->out_ext_man_fd); diff --git a/tools/rimage/src/hash.c b/tools/rimage/src/hash.c index 0604670e9c6e..9aff3ae3625a 100644 --- a/tools/rimage/src/hash.c +++ b/tools/rimage/src/hash.c @@ -174,7 +174,10 @@ int hash_single(const void *data, size_t size, const EVP_MD *algo, void *output, if (algo_out_size <= 0) return -EINVAL; - if (output_len > algo_out_size) + /* EVP_Digest writes algo_out_size bytes into output, so the buffer + * must be at least that large; reject an undersized output buffer + */ + if (output_len < (size_t)algo_out_size) return -ENOBUFS; if (!EVP_Digest(data, size, output, NULL, algo, NULL)) { diff --git a/tools/rimage/src/manifest.c b/tools/rimage/src/manifest.c index fb30dc7ab219..085320d60663 100644 --- a/tools/rimage/src/manifest.c +++ b/tools/rimage/src/manifest.c @@ -534,6 +534,20 @@ static int man_module_create_reloc(struct image *image, struct manifest_module * return -ENOEXEC; } + /* + * n_mod comes from the (potentially untrusted) ELF and each manifest + * consumes a sof_man_module descriptor slot written into fw_image. + * Bound the cumulative count across all input modules (this ELF adds + * n_mod to the running output_mod_cfg_count) so a crafted .module + * section cannot overflow the fixed manifest descriptor array. + */ + if (modules->output_mod_cfg_count + n_mod > MAX_MODULES) { + fprintf(stderr, "error: too many module manifests (%u + %u > %u).\n", + modules->output_mod_cfg_count, n_mod, MAX_MODULES); + elf_section_free(§ion); + return -ENOEXEC; + } + unsigned int i; for (i = 0, sof_mod = section.data; i < n_mod; i++, sof_mod++) { @@ -1664,11 +1678,25 @@ int verify_image(struct image *image) ret = file_error("unable to read whole file", image->verify_file); goto out; } - for (i = 0; i < size; i += sizeof(uint32_t)) { + for (i = 0; i + sizeof(uint32_t) <= size; i += sizeof(uint32_t)) { /* find CSE header marker "$CPD" */ if (*(uint32_t *)(buffer + i) == CSE_HEADER_MAKER) { image->fw_image = buffer + i; + /* size of the image from the CSE header to the end of the + * file, used by v1.5 verification and the signed-payload + * bounds checks + */ + image->image_end = size - i; ret = image->adsp->verify_firmware(image); + /* verify_firmware() returns 1 = valid, 0 = invalid and + * < 0 = error (OpenSSL RSA_verify semantics); normalize to + * 0 on success and a negative errno otherwise so the CLI + * exit code reflects the verification result + */ + if (ret == 1) + ret = 0; + else if (ret >= 0) + ret = -EINVAL; goto out; } } @@ -1676,9 +1704,13 @@ int verify_image(struct image *image) /* no header found */ fprintf(stderr, "error: could not find valid CSE header $CPD in %s\n", image->verify_file); + ret = -EINVAL; out: fclose(in_file); - return 0; + /* propagate verification result so callers (and the exit code) can + * detect a failed/missing verification instead of always seeing success + */ + return ret; } @@ -1717,7 +1749,7 @@ int resign_image(struct image *image) fclose(in_file); - for (i = 0; i < size; i += sizeof(uint32_t)) { + for (i = 0; i + sizeof(uint32_t) <= size; i += sizeof(uint32_t)) { /* find CSE header marker "$CPD" */ if (*(uint32_t *)(buffer + i) == CSE_HEADER_MAKER) { image->fw_image = buffer + i; diff --git a/tools/rimage/src/pkcs1_5.c b/tools/rimage/src/pkcs1_5.c index de063ee03bec..cbd31bc101bf 100644 --- a/tools/rimage/src/pkcs1_5.c +++ b/tools/rimage/src/pkcs1_5.c @@ -929,9 +929,28 @@ int ri_manifest_verify_v1_8(struct image *image) MAN_RSA_SIGNATURE_LEN); char *const data2 = (char *)man + MAN_SIG_PKG_OFFSET_V1_8; + + /* css.size and css.header_len come from the (untrusted) image; reject + * a reversed ordering that would underflow the unsigned size2 below. + */ + if (man->css.size < man->css.header_len) { + fprintf(stderr, "error: invalid css size %u < header_len %u\n", + man->css.size, man->css.header_len); + return -EINVAL; + } + unsigned const size2 = (man->css.size - man->css.header_len) * sizeof(uint32_t); + /* size2 is derived from untrusted css fields and is hashed starting at + * data2; make sure that range stays within the verified image buffer. + */ + if (image->image_end < MAN_SIG_PKG_OFFSET_V1_8 || + size2 > image->image_end - MAN_SIG_PKG_OFFSET_V1_8) { + fprintf(stderr, "error: signed payload size 0x%x exceeds image\n", size2); + return -EINVAL; + } + return pkcs_v1_5_verify_man_v1_8(image, man, data1, size1, data2, size2); } @@ -946,9 +965,28 @@ int ri_manifest_verify_v2_5(struct image *image) MAN_RSA_SIGNATURE_LEN_2_5); char *const data2 = (char *)man + MAN_SIG_PKG_OFFSET_V2_5; + + /* css.size and css.header_len come from the (untrusted) image; reject + * a reversed ordering that would underflow the unsigned size2 below. + */ + if (man->css.size < man->css.header_len) { + fprintf(stderr, "error: invalid css size %u < header_len %u\n", + man->css.size, man->css.header_len); + return -EINVAL; + } + unsigned const size2 = (man->css.size - man->css.header_len) * sizeof(uint32_t); + /* size2 is derived from untrusted css fields and is hashed starting at + * data2; make sure that range stays within the verified image buffer. + */ + if (image->image_end < MAN_SIG_PKG_OFFSET_V2_5 || + size2 > image->image_end - MAN_SIG_PKG_OFFSET_V2_5) { + fprintf(stderr, "error: signed payload size 0x%x exceeds image\n", size2); + return -EINVAL; + } + return pkcs_v1_5_verify_man_v2_5(image, man, data1, size1, data2, size2); }