spl: ymodem: Fix buffer overflow during Image copy
authorVignesh Raghavendra <vigneshr@ti.com>
Mon, 31 Jan 2022 04:19:19 +0000 (09:49 +0530)
committerTom Rini <trini@konsulko.com>
Thu, 3 Feb 2022 17:15:33 +0000 (12:15 -0500)
ymodem_read_fit() driver will end copying up to BUF_SIZE boundary even
when requested size of copy operation is less than that.
For example, if offset = 0, size = 1440B, ymodem_read_fit() ends up
copying 2KB from offset = 0, to destination buffer addr

This causes data corruption when malloc'd buffer is passed during UART
boot since commit 03f1f78a9b44 ("spl: fit: Prefer a malloc()'d buffer
for loading images")

With this, UART boot works again on K3 (AM654, J7, AM64) family of
devices.

Fixes: 03f1f78a9b44 ("spl: fit: Prefer a malloc()'d buffer for loading images")
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
common/spl/spl_ymodem.c

index 047df74..fdd5261 100644 (file)
@@ -42,6 +42,7 @@ static ulong ymodem_read_fit(struct spl_load_info *load, ulong offset,
        int res, err, buf_offset;
        struct ymodem_fit_info *info = load->priv;
        char *buf = info->buf;
+       ulong copy_size = size;
 
        while (info->image_read < offset) {
                res = xyzModem_stream_read(buf, BUF_SIZE, &err);
@@ -57,8 +58,14 @@ static ulong ymodem_read_fit(struct spl_load_info *load, ulong offset,
                        buf_offset = (info->image_read % BUF_SIZE);
                else
                        buf_offset = BUF_SIZE;
+
+               if (res > copy_size) {
+                       memcpy(addr, &buf[buf_offset - res], copy_size);
+                       goto done;
+               }
                memcpy(addr, &buf[buf_offset - res], res);
                addr = addr + res;
+               copy_size -= res;
        }
 
        while (info->image_read < offset + size) {
@@ -66,11 +73,17 @@ static ulong ymodem_read_fit(struct spl_load_info *load, ulong offset,
                if (res <= 0)
                        break;
 
-               memcpy(addr, buf, res);
                info->image_read += res;
+               if (res > copy_size) {
+                       memcpy(addr, buf, copy_size);
+                       goto done;
+               }
+               memcpy(addr, buf, res);
                addr += res;
+               copy_size -= res;
        }
 
+done:
        return size;
 }