Remoteproc coredump segment sizes can originate in firmware ELF headers or remote processor minidump tables. The coredump code adds those sizes to a single allocation without checking for size_t overflow, then copies every segment at the unwrapped offsets. A wrapped allocation therefore leads to out-of-bounds writes while building a buffered dump.
The section-based format also stores its section count in e_shnum without implementing ELF extended numbering. Once the count reaches the reserved range, the truncated header count makes the string-table and section offsets diverge from the allocated layout. Reject unrepresentable aggregate sizes and counts that require ELF extended numbering before allocating the dump buffer. Signed-off-by: Yousef Alhouseen <[email protected]> --- drivers/remoteproc/remoteproc_coredump.c | 39 +++++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/remoteproc/remoteproc_coredump.c b/drivers/remoteproc/remoteproc_coredump.c index ceff380228c7..17a084f34445 100644 --- a/drivers/remoteproc/remoteproc_coredump.c +++ b/drivers/remoteproc/remoteproc_coredump.c @@ -9,6 +9,7 @@ #include <linux/devcoredump.h> #include <linux/device.h> #include <linux/kernel.h> +#include <linux/overflow.h> #include <linux/remoteproc.h> #include "remoteproc_internal.h" #include "remoteproc_elf_helpers.h" @@ -256,14 +257,23 @@ void rproc_coredump(struct rproc *rproc) data_size = elf_size_of_hdr(class); list_for_each_entry(segment, &rproc->dump_segments, node) { + if (phnum >= PN_XNUM - 1) { + dev_err(&rproc->dev, "too many segments for coredump\n"); + return; + } + /* * For default configuration buffer includes headers & segments. * For inline dump buffer just includes headers as segments are * directly read from device memory. */ - data_size += elf_size_of_phdr(class); - if (dump_conf == RPROC_COREDUMP_ENABLED) - data_size += segment->size; + if (check_add_overflow(data_size, elf_size_of_phdr(class), + &data_size) || + (dump_conf == RPROC_COREDUMP_ENABLED && + check_add_overflow(data_size, segment->size, &data_size))) { + dev_err(&rproc->dev, "coredump size overflow\n"); + return; + } phnum++; } @@ -379,14 +389,27 @@ void rproc_coredump_using_sections(struct rproc *rproc) strtbl_size += strlen(str_tbl) + 2; list_for_each_entry(segment, &rproc->dump_segments, node) { - data_size += elf_size_of_shdr(class); - strtbl_size += strlen(segment->priv) + 1; - if (dump_conf == RPROC_COREDUMP_ENABLED) - data_size += segment->size; + if (shnum >= SHN_LORESERVE - 1) { + dev_err(&rproc->dev, "too many sections for coredump\n"); + return; + } + + if (check_add_overflow(data_size, elf_size_of_shdr(class), + &data_size) || + check_add_overflow(strtbl_size, strlen(segment->priv) + 1, + &strtbl_size) || + (dump_conf == RPROC_COREDUMP_ENABLED && + check_add_overflow(data_size, segment->size, &data_size))) { + dev_err(&rproc->dev, "coredump size overflow\n"); + return; + } shnum++; } - data_size += strtbl_size; + if (check_add_overflow(data_size, strtbl_size, &data_size)) { + dev_err(&rproc->dev, "coredump size overflow\n"); + return; + } data = vmalloc(data_size); if (!data) -- 2.54.0

