From be81557dfa88cd0cdea07d15352f0da9b01824ce Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Mon, 1 Jun 2026 07:28:22 -0700 Subject: [PATCH 1/7] remoteproc: add checked size arithmetic helpers Add local helpers in the ELF loader for checked size_t multiplication, addition, and image chunk range validation. The ELF loader derives allocation sizes and copy ranges from firmware header fields. Centralizing the overflow checks keeps later validation portable across embedded toolchains and avoids relying on compiler builtins. Signed-off-by: Ben Levinsky --- lib/remoteproc/elf_loader.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/remoteproc/elf_loader.c b/lib/remoteproc/elf_loader.c index c0eb1168d..c726ef0bc 100644 --- a/lib/remoteproc/elf_loader.c +++ b/lib/remoteproc/elf_loader.c @@ -6,11 +6,46 @@ */ #include +#include #include #include #include #include +static int elf_mul_size(size_t a, size_t b, size_t *out) +{ + if (a != 0 && b > SIZE_MAX / a) + return -RPROC_EINVAL; + + *out = a * b; + return 0; +} + +static int elf_add_size(size_t a, size_t b, size_t *out) +{ + if (b > SIZE_MAX - a) + return -RPROC_EINVAL; + + *out = a + b; + return 0; +} + +static int elf_range_in_chunk(size_t chunk_offset, size_t chunk_len, + size_t range_offset, size_t range_len) +{ + size_t chunk_end; + size_t range_end; + + if (elf_add_size(chunk_offset, chunk_len, &chunk_end) < 0 || + elf_add_size(range_offset, range_len, &range_end) < 0) + return -RPROC_EINVAL; + + if (chunk_offset > range_offset || chunk_end < range_end) + return 0; + + return 1; +} + static int elf_is_64(const void *elf_info) { const unsigned char *tmp = elf_info; From eb431c6de467b2f25fe7353705d265d57e638339 Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Mon, 1 Jun 2026 07:28:38 -0700 Subject: [PATCH 2/7] remoteproc: validate ELF header shape before table loading Reject malformed ELF headers before using program or section table metadata from the firmware image. Validate the ELF class, ELF header size, program header entry size, section header entry size, and section string-table index. This prevents the loader from allocating or indexing native header arrays using entry sizes that do not match the structures used by OpenAMP. Signed-off-by: Ben Levinsky --- lib/remoteproc/elf_loader.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/lib/remoteproc/elf_loader.c b/lib/remoteproc/elf_loader.c index c726ef0bc..e60b79b25 100644 --- a/lib/remoteproc/elf_loader.c +++ b/lib/remoteproc/elf_loader.c @@ -157,6 +157,43 @@ static int elf_shstrndx(const void *elf_info) } } +static int elf_validate_header(const void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Ehdr *ehdr = elf_info; + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) + return -RPROC_EINVAL; + if (ehdr->e_ehsize != sizeof(Elf32_Ehdr)) + return -RPROC_EINVAL; + if (ehdr->e_phnum != 0 && + ehdr->e_phentsize != sizeof(Elf32_Phdr)) + return -RPROC_EINVAL; + if (ehdr->e_shnum != 0 && + ehdr->e_shentsize != sizeof(Elf32_Shdr)) + return -RPROC_EINVAL; + if (ehdr->e_shnum != 0 && ehdr->e_shstrndx >= ehdr->e_shnum) + return -RPROC_EINVAL; + } else { + const Elf64_Ehdr *ehdr = elf_info; + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) + return -RPROC_EINVAL; + if (ehdr->e_ehsize != sizeof(Elf64_Ehdr)) + return -RPROC_EINVAL; + if (ehdr->e_phnum != 0 && + ehdr->e_phentsize != sizeof(Elf64_Phdr)) + return -RPROC_EINVAL; + if (ehdr->e_shnum != 0 && + ehdr->e_shentsize != sizeof(Elf64_Shdr)) + return -RPROC_EINVAL; + if (ehdr->e_shnum != 0 && ehdr->e_shstrndx >= ehdr->e_shnum) + return -RPROC_EINVAL; + } + + return 0; +} + static void **elf_phtable_ptr(void *elf_info) { if (elf_is_64(elf_info) == 0) { @@ -455,6 +492,8 @@ int elf_load_header(const void *img_data, size_t offset, size_t len, memset(*img_info, 0, infosize); } memcpy(*img_info, img_data, tmpsize); + if (elf_validate_header(*img_info) < 0) + return -RPROC_EINVAL; load_state = elf_load_state(*img_info); *load_state = ELF_STATE_WAIT_FOR_PHDRS; last_load_state = ELF_STATE_WAIT_FOR_PHDRS; From 704073af457791ad6d92d83edfd06927a2e18363 Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Mon, 1 Jun 2026 07:28:56 -0700 Subject: [PATCH 3/7] remoteproc: check ELF program and section table sizes Use checked multiplication when computing ELF program and section header table sizes from e_phnum/e_phentsize and e_shnum/e_shentsize. Also replace wrapping offset-plus-length range checks with overflow-safe image chunk validation before allocation and memcpy. Malformed firmware images with impossible table sizes now fail with -RPROC_EINVAL. Signed-off-by: Ben Levinsky --- lib/remoteproc/elf_loader.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/remoteproc/elf_loader.c b/lib/remoteproc/elf_loader.c index e60b79b25..f75a0cde4 100644 --- a/lib/remoteproc/elf_loader.c +++ b/lib/remoteproc/elf_loader.c @@ -507,14 +507,21 @@ int elf_load_header(const void *img_data, size_t offset, size_t len, if (*load_state == ELF_STATE_WAIT_FOR_PHDRS) { size_t phdrs_size; size_t phdrs_offset; + int range_in_chunk; void **phdrs; const void *img_phdrs; metal_log(METAL_LOG_DEBUG, "Loading ELF program header.\r\n"); phdrs_offset = elf_phoff(*img_info); - phdrs_size = elf_phnum(*img_info) * elf_phentsize(*img_info); - if (offset > phdrs_offset || - offset + len < phdrs_offset + phdrs_size) { + if (elf_mul_size(elf_phnum(*img_info), + elf_phentsize(*img_info), + &phdrs_size) < 0) + return -RPROC_EINVAL; + range_in_chunk = elf_range_in_chunk(offset, len, phdrs_offset, + phdrs_size); + if (range_in_chunk < 0) + return range_in_chunk; + if (!range_in_chunk) { *noffset = phdrs_offset; *nlen = phdrs_size; return *load_state; @@ -534,6 +541,7 @@ int elf_load_header(const void *img_data, size_t offset, size_t len, if ((*load_state & ELF_STATE_WAIT_FOR_SHDRS) != 0) { size_t shdrs_size; size_t shdrs_offset; + int range_in_chunk; void **shdrs; const void *img_shdrs; @@ -545,9 +553,15 @@ int elf_load_header(const void *img_data, size_t offset, size_t len, *nlen = 0; return *load_state; } - shdrs_size = elf_shnum(*img_info) * elf_shentsize(*img_info); - if (offset > shdrs_offset || - offset + len < shdrs_offset + shdrs_size) { + if (elf_mul_size(elf_shnum(*img_info), + elf_shentsize(*img_info), + &shdrs_size) < 0) + return -RPROC_EINVAL; + range_in_chunk = elf_range_in_chunk(offset, len, shdrs_offset, + shdrs_size); + if (range_in_chunk < 0) + return range_in_chunk; + if (!range_in_chunk) { *noffset = shdrs_offset; *nlen = shdrs_size; return *load_state; From 6016412581cc746bc3cb015b0991cfbb65ad8f36 Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Mon, 1 Jun 2026 07:29:09 -0700 Subject: [PATCH 4/7] remoteproc: harden ELF section string-table range checks Use overflow-safe range validation before copying the ELF section string table from the firmware image. The section string-table offset and size are read from untrusted section headers. Validate that the full table is present in the current image chunk without relying on wrapping offset arithmetic. Signed-off-by: Ben Levinsky --- lib/remoteproc/elf_loader.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/remoteproc/elf_loader.c b/lib/remoteproc/elf_loader.c index f75a0cde4..2adef0816 100644 --- a/lib/remoteproc/elf_loader.c +++ b/lib/remoteproc/elf_loader.c @@ -586,6 +586,7 @@ int elf_load_header(const void *img_data, size_t offset, size_t len, int shstrndx; void *shdr; void **shstrtab; + int range_in_chunk; metal_log(METAL_LOG_DEBUG, "Loading ELF shstrtab.\r\n"); shstrndx = elf_shstrndx(*img_info); @@ -596,8 +597,12 @@ int elf_load_header(const void *img_data, size_t offset, size_t len, NULL, &shstrtab_offset, &shstrtab_size, NULL, NULL, NULL, NULL); - if (offset > shstrtab_offset || - offset + len < shstrtab_offset + shstrtab_size) { + range_in_chunk = elf_range_in_chunk(offset, len, + shstrtab_offset, + shstrtab_size); + if (range_in_chunk < 0) + return range_in_chunk; + if (!range_in_chunk) { *noffset = shstrtab_offset; *nlen = shstrtab_size; return *load_state; From 5298d516b8d6e52cea0116b55414bfb6f3c7a880 Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Mon, 1 Jun 2026 07:29:29 -0700 Subject: [PATCH 5/7] remoteproc: track loaded ELF section string-table size Store the loaded section string-table size in the ELF image information for both ELF32 and ELF64 images. Keeping the size alongside the string-table pointer allows later section name lookups to validate sh_name offsets against the actual loaded table bounds. Signed-off-by: Ben Levinsky --- lib/include/openamp/elf_loader.h | 2 ++ lib/remoteproc/elf_loader.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/include/openamp/elf_loader.h b/lib/include/openamp/elf_loader.h index a2950f8d7..126740c53 100644 --- a/lib/include/openamp/elf_loader.h +++ b/lib/include/openamp/elf_loader.h @@ -290,6 +290,7 @@ struct elf32_info { Elf32_Phdr *phdrs; Elf32_Shdr *shdrs; void *shstrtab; + size_t shstrtab_size; }; struct elf64_info { @@ -298,6 +299,7 @@ struct elf64_info { Elf64_Phdr *phdrs; Elf64_Shdr *shdrs; void *shstrtab; + size_t shstrtab_size; }; #define ELF_STATE_INIT 0x0L diff --git a/lib/remoteproc/elf_loader.c b/lib/remoteproc/elf_loader.c index 2adef0816..d39f08cce 100644 --- a/lib/remoteproc/elf_loader.c +++ b/lib/remoteproc/elf_loader.c @@ -233,6 +233,15 @@ static void **elf_shstrtab_ptr(void *elf_info) } } +static void elf_set_shstrtab_size(void *elf_info, size_t size) +{ + if (elf_is_64(elf_info) == 0) { + ((struct elf32_info *)elf_info)->shstrtab_size = size; + } else { + ((struct elf64_info *)elf_info)->shstrtab_size = size; + } +} + static int *elf_load_state(void *elf_info) { if (elf_is_64(elf_info) == 0) { @@ -613,6 +622,7 @@ int elf_load_header(const void *img_data, size_t offset, size_t len, *shstrtab = metal_allocate_memory(shstrtab_size); if (!*shstrtab) return -RPROC_ENOMEM; + elf_set_shstrtab_size(*img_info, shstrtab_size); memcpy(*shstrtab, (const char *)img_data + shstrtab_offset, shstrtab_size); From d933618706e5297670720b832601ad0b2c98fb5e Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Mon, 1 Jun 2026 07:30:27 -0700 Subject: [PATCH 6/7] remoteproc: bound ELF section name lookups Make ELF section-name lookup validate sh_name before reading from the loaded section string table. Skip malformed section names whose sh_name offset is outside the table or whose string is not NUL-terminated within the remaining table bytes. Use bounded comparison for valid candidates so .resource_table lookup cannot read past the loaded string table. Signed-off-by: Ben Levinsky --- lib/remoteproc/elf_loader.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/remoteproc/elf_loader.c b/lib/remoteproc/elf_loader.c index d39f08cce..e37bfc958 100644 --- a/lib/remoteproc/elf_loader.c +++ b/lib/remoteproc/elf_loader.c @@ -255,6 +255,25 @@ static int *elf_load_state(void *elf_info) } } +static int elf_section_name_matches(const char *name, const char *name_table, + size_t name_table_size, size_t sh_name) +{ + size_t name_len; + size_t remaining; + const char *candidate; + + if (sh_name >= name_table_size) + return 0; + + remaining = name_table_size - sh_name; + name_len = strlen(name); + if (name_len >= remaining) + return 0; + + candidate = name_table + sh_name; + return memcmp(name, candidate, name_len + 1) == 0; +} + static void elf_parse_segment(void *elf_info, const void *elf_phdr, unsigned int *p_type, size_t *p_offset, metal_phys_addr_t *p_vaddr, @@ -323,6 +342,7 @@ static void *elf_get_section_from_name(void *elf_info, const char *name) { unsigned int i; const char *name_table; + size_t name_table_size; if (elf_is_64(elf_info) == 0) { struct elf32_info *einfo = elf_info; @@ -330,10 +350,13 @@ static void *elf_get_section_from_name(void *elf_info, const char *name) Elf32_Shdr *shdr = einfo->shdrs; name_table = einfo->shstrtab; + name_table_size = einfo->shstrtab_size; if (!shdr || !name_table) return NULL; for (i = 0; i < ehdr->e_shnum; i++, shdr++) { - if (strcmp(name, name_table + shdr->sh_name)) + if (!elf_section_name_matches(name, name_table, + name_table_size, + shdr->sh_name)) continue; else return shdr; @@ -344,10 +367,13 @@ static void *elf_get_section_from_name(void *elf_info, const char *name) Elf64_Shdr *shdr = einfo->shdrs; name_table = einfo->shstrtab; + name_table_size = einfo->shstrtab_size; if (!shdr || !name_table) return NULL; for (i = 0; i < ehdr->e_shnum; i++, shdr++) { - if (strcmp(name, name_table + shdr->sh_name)) + if (!elf_section_name_matches(name, name_table, + name_table_size, + shdr->sh_name)) continue; else return shdr; From 064b04a05e0d647c3aa751ef95b8cec2efcfdede Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Mon, 1 Jun 2026 07:30:42 -0700 Subject: [PATCH 7/7] remoteproc: avoid wrapping loader offset comparison Replace the non-seekable loader offset comparison that used offset + len with an overflow-safe equivalent. This preserves existing behavior for normal ranges while avoiding a wraparound case when deciding whether required image data is contiguous with the current chunk. Signed-off-by: Ben Levinsky --- lib/remoteproc/remoteproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remoteproc/remoteproc.c b/lib/remoteproc/remoteproc.c index 989d01cbd..11afb42dd 100644 --- a/lib/remoteproc/remoteproc.c +++ b/lib/remoteproc/remoteproc.c @@ -526,7 +526,7 @@ int remoteproc_load(struct remoteproc *rproc, const char *path, } else if ((ret & RPROC_LOADER_READY_TO_LOAD) != 0) { if (nlen == 0) break; - else if ((noffset > (offset + len)) && + else if ((noffset > offset && noffset - offset > len) && (store_ops->features & SUPPORT_SEEK) == 0) { /* Required data is not continued, however * seek is not supported, stop to load