mirror of https://github.com/wolfSSL/wolfBoot.git
F-4339: widen uart-flash-server bounds checks to 64-bit to stop uint32_t wrap
uart_flash_erase/read/write read attacker-controlled address and len as raw 4-byte words from the UART peer and guard the mmap region with `address + len > FIRMWARE_PARTITION_SIZE + SWAP_SIZE`. The addition is done in uint32_t, so a large len (e.g. 0xFFFFFFFF) wraps the sum below 0x21000 and the guard passes, after which the loop walks far past the 0x21000-byte mapping — SIGSEGV on a 64-bit host, or wrapped writes over the firmware header on 32-bit. Compute address + len in uint64_t before the comparison so the sum cannot wrap. Add a pty-driven regression test (test_overflow) and a `make test` target that sends an ERASE with a wrapping address+len and asserts the server survives.pull/792/head
parent
6a60ea3d53
commit
71545e96de
|
|
@ -16,6 +16,11 @@ $(EXE): $(EXE).o libwolfboot.o
|
|||
libwolfboot.o: ../../src/libwolfboot.c
|
||||
$(Q)$(CC) $(CFLAGS) -c -o $(@) $(^)
|
||||
|
||||
test_overflow: test_overflow.c
|
||||
$(Q)$(CC) -Wall -g -o $@ $^ -lutil
|
||||
|
||||
test: $(EXE) test_overflow
|
||||
$(Q)./test_overflow ./$(EXE)
|
||||
|
||||
clean:
|
||||
$(Q)rm -f *.o $(EXE)
|
||||
$(Q)rm -f *.o $(EXE) test_overflow
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
/* Regression test for the uint32_t wraparound bounds-check bypass in
|
||||
* uart_flash_erase()/read()/write() (finding F-4339).
|
||||
*
|
||||
* Drives the real ufserver binary over a pseudo-terminal and sends an ERASE
|
||||
* command with address=0x1000 and len=0xFFFFFFFF. With the buggy uint32_t
|
||||
* guard, address+len wraps to 0xFFF (< FIRMWARE_PARTITION_SIZE+SWAP_SIZE), the
|
||||
* guard is bypassed and the erase loop walks far past the 0x21000-byte mmap,
|
||||
* crashing the server with SIGSEGV. With the 64-bit guard the command is
|
||||
* rejected and the server stays alive.
|
||||
*
|
||||
* Usage: test_overflow ./ufserver
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <pty.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define CMD_HDR_WOLF 'W'
|
||||
#define CMD_HDR_ERASE 0x03
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int master, slave;
|
||||
char slavename[256];
|
||||
pid_t pid;
|
||||
char tmpl[] = "/tmp/ufserver_fw_XXXXXX";
|
||||
int fd;
|
||||
uint8_t buf[64];
|
||||
int i;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s ./ufserver\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Create a small (non-WOLF) firmware file; mmap_firmware() pads it to
|
||||
* FIRMWARE_PARTITION_SIZE + SWAP_SIZE (0x21000). */
|
||||
fd = mkstemp(tmpl);
|
||||
if (fd < 0) { perror("mkstemp"); return 2; }
|
||||
memset(buf, 0xA5, sizeof(buf));
|
||||
if (write(fd, buf, sizeof(buf)) != (ssize_t)sizeof(buf)) { perror("write"); return 2; }
|
||||
close(fd);
|
||||
|
||||
if (openpty(&master, &slave, slavename, NULL, NULL) != 0) {
|
||||
perror("openpty");
|
||||
return 2;
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) { perror("fork"); return 2; }
|
||||
if (pid == 0) {
|
||||
/* Child: run the server attached to the slave pty. */
|
||||
close(master);
|
||||
close(slave);
|
||||
fd = open("/dev/null", O_WRONLY);
|
||||
if (fd >= 0) { dup2(fd, STDOUT_FILENO); }
|
||||
execl(argv[1], argv[1], tmpl, slavename, (char *)NULL);
|
||||
perror("execl");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
/* Parent: drive the server. */
|
||||
close(slave);
|
||||
usleep(200000); /* let the child open and configure the port */
|
||||
|
||||
/* STX selecting a flash command, then the ERASE opcode. */
|
||||
buf[0] = CMD_HDR_WOLF;
|
||||
buf[1] = CMD_HDR_ERASE;
|
||||
/* address = 0x00001000 (little-endian raw bytes) */
|
||||
buf[2] = 0x00; buf[3] = 0x10; buf[4] = 0x00; buf[5] = 0x00;
|
||||
/* len = 0xFFFFFFFF -> address+len wraps to 0xFFF in uint32_t */
|
||||
buf[6] = 0xFF; buf[7] = 0xFF; buf[8] = 0xFF; buf[9] = 0xFF;
|
||||
if (write(master, buf, 10) != 10) { perror("write master"); }
|
||||
|
||||
/* Drain ack bytes the server sends back so it never blocks. */
|
||||
fcntl(master, F_SETFL, O_NONBLOCK);
|
||||
|
||||
/* Give the server time to process (and crash, if vulnerable). */
|
||||
for (i = 0; i < 20; i++) {
|
||||
uint8_t drain[64];
|
||||
(void)read(master, drain, sizeof(drain));
|
||||
usleep(50000);
|
||||
int status;
|
||||
pid_t r = waitpid(pid, &status, WNOHANG);
|
||||
if (r == pid) {
|
||||
unlink(tmpl);
|
||||
if (WIFSIGNALED(status)) {
|
||||
fprintf(stderr, "FAIL: server died from signal %d "
|
||||
"(OOB access from wrapped bounds check)\n",
|
||||
WTERMSIG(status));
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr, "FAIL: server exited unexpectedly (status %d)\n",
|
||||
status);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Still alive after processing the malicious command: guard held. */
|
||||
kill(pid, SIGKILL);
|
||||
waitpid(pid, NULL, 0);
|
||||
unlink(tmpl);
|
||||
printf("PASS: server rejected wrapped address+len and stayed alive\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -280,7 +280,7 @@ static void uart_flash_erase(uint8_t *base, int ud)
|
|||
return;
|
||||
if (read_word(ud,&len) != 4)
|
||||
return;
|
||||
if (address + len > (FIRMWARE_PARTITION_SIZE + SWAP_SIZE))
|
||||
if ((uint64_t)address + (uint64_t)len > (FIRMWARE_PARTITION_SIZE + SWAP_SIZE))
|
||||
return;
|
||||
if (address < FIRMWARE_PARTITION_SIZE) {
|
||||
printmsg(msgEraseUpdate);
|
||||
|
|
@ -319,7 +319,7 @@ static void uart_flash_read(uint8_t *base, int ud)
|
|||
#if LOG_FLASH_ADDRESS
|
||||
printf("Read @%x\n", address);
|
||||
#endif
|
||||
if (address + len > (FIRMWARE_PARTITION_SIZE + SWAP_SIZE))
|
||||
if ((uint64_t)address + (uint64_t)len > (FIRMWARE_PARTITION_SIZE + SWAP_SIZE))
|
||||
return;
|
||||
for (i = 0; i < len; i++) {
|
||||
write(ud, base + address + i, 1);
|
||||
|
|
@ -344,7 +344,7 @@ static void uart_flash_write(uint8_t *base, int ud)
|
|||
#if LOG_FLASH_ADDRESS
|
||||
printf("Write @%x\n", address);
|
||||
#endif
|
||||
if (address + len > (FIRMWARE_PARTITION_SIZE + SWAP_SIZE))
|
||||
if ((uint64_t)address + (uint64_t)len > (FIRMWARE_PARTITION_SIZE + SWAP_SIZE))
|
||||
return;
|
||||
for (i = 0; i < len; i++) {
|
||||
read(ud, base + address + i, 1);
|
||||
|
|
|
|||
Loading…
Reference in New Issue