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


Reply via email to