From 09ce4b391b93d363c3cd8912f32e3bb6e76cd03e Mon Sep 17 00:00:00 2001 From: DrDoof <8052722+DrDoof@users.noreply.github.com> Date: Mon, 7 Sep 2026 22:06:09 +0200 Subject: [PATCH] httpd: stop a firmware upload at the end of the image area edc469f made max_upload count, which bounds how many bytes a request may carry. Nothing bounds where those bytes land. The two are not the same check: the first watches the request, the second watches the flash address. uptr walks forward one page at a time from FIRMWARE_UPLOAD_START and has no ceiling. A body whose closing boundary never arrives keeps writing page after page past the staging region, and on a flash exactly twice FIRMWARE_UPLOAD_START the address wraps onto the running image at zero, so a bad upload takes the working firmware with it. That commit's own message notes the related hazard: flash_write_bytes() carries the address in three bytes and drops what does not fit. Bound it where uptr actually moves: refuse the next page once it would leave the area check_and_flash_update_image() reads back, and answer 400. A partial image left behind is harmless - boot rejects it on CRC and erases it. This rebase drops the part of the original patch that removed max_upload; the length limit and the address limit now sit side by side. --- httpd/httpd.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/httpd/httpd.c b/httpd/httpd.c index ab61ca7c..dfd0db21 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -532,6 +532,19 @@ uint8_t stream_upload(void) crc16_bank1(upload_settings.p + upload_settings.bptr); flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++]; if (write_len >= FLASH_PAGE_SIZE) { + /* The staged image spans FIRMWARE_UPLOAD_START to twice that, + * the span check_and_flash_update_image() reads back. Nothing + * else bounds uptr: a body whose closing boundary never arrives + * keeps writing, and on a flash exactly this size the address + * wraps onto the running image at zero. */ + if (uptr >= (uint32_t)FIRMWARE_UPLOAD_START * 2) { + print_string("Upload runs past the image area! Aborting.\n"); + slen = strtox(outbuf, "HTTP/1.1 400 Bad Request\r\nContent-Length: 30\r\n" + "Content-Type: text/plain\r\n\r\n" + "NO: upload exceeds image area\n"); + s->tstate = TSTATE_NONE; + return 0; + } dbg_string("len: "); dbg_short(write_len); dbg_char(' '); dbg_string("CRC16: "); dbg_short(crc_value); dbg_char('\n'); if (uptr % FLASH_SECTOR_SIZE == 0) {