-
Notifications
You must be signed in to change notification settings - Fork 361
rimage: various rimage fixes #10882
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
rimage: various rimage fixes #10882
Changes from all commits
b7b1d87
b0e1da3
ef41cc1
35067ac
95fb7b7
9ba74f6
01a0eed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
Comment on lines
+72
to
+76
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed — |
||
| } | ||
| 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); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is interesting. How did it work before? It isn't the normal case that they are equal, is it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is the normal case that they're equal — every in-tree caller ( |
||
|
|
||
| if (!EVP_Digest(data, size, output, NULL, algo, NULL)) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,21 +1678,39 @@ 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; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this last one now looks really strange to me... Sure not |
||
| goto out; | ||
| } | ||
| } | ||
|
|
||
| /* 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; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for my understanding:
rimageis "just" a helper tool run by a user locally to generate firmware images. So they always can just create corrupted firmware images themselves - using whatever tools they want? It would be only "attackable" in scenarios where users submit ELF images to some deployment service? It's good to tighten up things of course, just trying to understand the scope, the severity levelThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right —
rimageis a local build-time tool, so for a developer building their own firmware the threat model is minimal: they can already produce any image they like. The case these harden is a build/CI pipeline that runsrimageon untrusted inputs — e.g. an automated signer/verifier gating contributed ELF/firmware. There a crafted input could crash the tool, or (for the verify exit-code bug) cause a tampered image to be accepted by arimage -y && deploygate. So the severity is low: robustness / CI-integrity hardening, not a firmware-runtime vulnerability — which is why these are rated Low.