From: Tulio A M Mendes Date: Tue, 28 Apr 2026 02:49:18 +0000 (-0300) Subject: FIX security audit: 9 vulnerabilities across kernel and userland X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=38e7aa5baddbc84ba23bd96ed3427085f53f788e;p=AdrOS.git FIX security audit: 9 vulnerabilities across kernel and userland CRITICAL: - C3: heap.c corruption handlers — replace for(;;) infinite loop with schedule()+return, marking process ZOMBIE (exit_status=128) HIGH: - H1: syscall.c FCNTL_F_DUPFD_CLOEXEC — replace plain refcount++ with __sync_fetch_and_add for SMP atomicity - H2: ulibc stdlib.c free() — add iteration limit (1024) and null-ptr guard to backward coalescing search to prevent infinite loop on corrupted next_free chain - H3: ulibc stdio.c vfprintf — increase buffer 1024→4096, write full buffer on truncation instead of silently capping MEDIUM: - M1: sed.c parse_cmd — add regfree cleanup for addr1/addr2 regexes when parse_cmd fails after compilation - M2: grep.c — fix -e option to extract pattern from same arg (-ePATTERN) or next arg (-e PATTERN) - M4: syscall.c FUTEX_WAIT — accept timespec* timeout via arg3 instead of fixed 5000-tick (~100s); supports zero-timeout poll; update futex.h/futex.c/pthread.c/fulltest.c ABI LOW: - L1: dd.c parse_size — use long/strtol to prevent integer overflow on large values (e.g. bs=2048M) - L2: init.c parse_inittab — warn when /etc/inittab exceeds 2047-byte buffer (truncation detection) cppcheck: - fulltest.c: initialize oldact struct to silence uninitvar warning Tests: 120/120 QEMU, 152/152 battery, 111/111 host — zero regressions --- diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index a292b514..78ca1120 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -2468,7 +2468,7 @@ static int syscall_fcntl_impl(int fd, int cmd, uint32_t arg) { } if (new_fd < 0) return -EMFILE; current_process->files[new_fd] = f; - f->refcount++; + __sync_fetch_and_add(&f->refcount, 1); current_process->fd_flags[new_fd] = FD_CLOEXEC; return new_fd; } @@ -4362,9 +4362,9 @@ static void posix_ext_syscall_dispatch(struct registers* regs, uint32_t syscall_ uint32_t* uaddr = (uint32_t*)sc_arg0(regs); int op = (int)sc_arg1(regs); uint32_t val = sc_arg2(regs); - + if (!uaddr) { sc_ret(regs) = (uint32_t)-EFAULT; return; } - + if (op == FUTEX_WAIT) { uint32_t cur = 0; if (copy_from_user(&cur, uaddr, sizeof(cur)) < 0) { @@ -4380,8 +4380,26 @@ static void posix_ext_syscall_dispatch(struct registers* regs, uint32_t syscall_ futex_waiters[slot].addr = (uintptr_t)uaddr; futex_waiters[slot].proc = current_process; extern void schedule(void); + + /* Compute timeout: arg3 is a userspace timespec* or NULL */ + uint32_t timeout_ticks = 5000; /* default ~100s */ + const struct k_timeval* user_ts = (const struct k_timeval*)(uintptr_t)sc_arg3(regs); + if (user_ts && user_range_ok(user_ts, sizeof(struct k_timeval))) { + struct k_timeval kts; + if (copy_from_user(&kts, user_ts, sizeof(kts)) == 0) { + if (kts.tv_sec == 0 && kts.tv_usec == 0) { + /* timeout==0 means no wait (poll) */ + futex_waiters[slot].proc = NULL; + futex_waiters[slot].addr = 0; + sc_ret(regs) = (uint32_t)-ETIMEDOUT; + return; + } + timeout_ticks = timeval_to_ticks(&kts); + } + } + current_process->state = PROCESS_SLEEPING; - current_process->wake_at_tick = get_tick_count() + 5000; /* 100s timeout */ + current_process->wake_at_tick = get_tick_count() + timeout_ticks; schedule(); futex_waiters[slot].proc = NULL; futex_waiters[slot].addr = 0; diff --git a/src/mm/heap.c b/src/mm/heap.c index b510278d..19a7a219 100644 --- a/src/mm/heap.c +++ b/src/mm/heap.c @@ -173,8 +173,9 @@ void* kmalloc(size_t size) { if (current_process) { current_process->state = PROCESS_ZOMBIE; current_process->exit_status = 128; + schedule(); } - for (;;) { hal_cpu_disable_interrupts(); schedule(); hal_cpu_idle(); } + return NULL; } /* Split down to the required order */ @@ -211,8 +212,9 @@ void kfree(void* ptr) { if (current_process) { current_process->state = PROCESS_ZOMBIE; current_process->exit_status = 128; + schedule(); } - for (;;) { hal_cpu_disable_interrupts(); schedule(); hal_cpu_idle(); } + return; } if (blk->is_free) { @@ -222,8 +224,9 @@ void kfree(void* ptr) { if (current_process) { current_process->state = PROCESS_ZOMBIE; current_process->exit_status = 128; + schedule(); } - for (;;) { hal_cpu_disable_interrupts(); schedule(); hal_cpu_idle(); } + return; } blk->is_free = 1; diff --git a/user/cmds/dd/dd.c b/user/cmds/dd/dd.c index 47981eab..54cd62de 100644 --- a/user/cmds/dd/dd.c +++ b/user/cmds/dd/dd.c @@ -14,13 +14,13 @@ #include #include -static int parse_size(const char* s) { - int v = atoi(s); +static long parse_size(const char* s) { + long v = strtol(s, NULL, 10); int len = (int)strlen(s); if (len > 0) { char suf = s[len - 1]; - if (suf == 'k' || suf == 'K') v *= 1024; - else if (suf == 'm' || suf == 'M') v *= 1024 * 1024; + if (suf == 'k' || suf == 'K') v *= 1024L; + else if (suf == 'm' || suf == 'M') v *= 1024L * 1024L; } return v; } @@ -35,7 +35,7 @@ static int parse_size(const char* s) { int main(int argc, char** argv) { const char* inf = NULL; const char* outf = NULL; - int bs = 512; + long bs = 512; int count = -1; int skip = 0; int seek_val = 0; diff --git a/user/cmds/fulltest/fulltest.c b/user/cmds/fulltest/fulltest.c index 8aea99df..f5b82bf4 100644 --- a/user/cmds/fulltest/fulltest.c +++ b/user/cmds/fulltest/fulltest.c @@ -606,7 +606,7 @@ static int sys_sigaction(int sig, void (*handler)(int), uintptr_t* old_out) { act.sa_mask = 0; act.sa_flags = 0; - struct sigaction oldact; + struct sigaction oldact = {{0}}; struct sigaction* oldp = old_out ? &oldact : 0; int r = sys_sigaction2(sig, &act, oldp); @@ -1448,7 +1448,7 @@ static int sys_chown(const char* path, uint32_t uid, uint32_t gid) { static int sys_futex(uint32_t* uaddr, int op, uint32_t val) { int ret; - __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_FUTEX), "b"(uaddr), "c"(op), "d"(val) : "memory"); + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_FUTEX), "b"(uaddr), "c"(op), "d"(val), "S"(0) : "memory"); return __syscall_fix(ret); } diff --git a/user/cmds/grep/grep.c b/user/cmds/grep/grep.c index 261865a1..5caf1d8e 100644 --- a/user/cmds/grep/grep.c +++ b/user/cmds/grep/grep.c @@ -163,6 +163,7 @@ static void grep_recursive(const char* path, const char* prefix) { int main(int argc, char** argv) { int i = 1; + const char* e_pattern = NULL; while (i < argc && argv[i][0] == '-' && argv[i][1]) { if (strcmp(argv[i], "--") == 0) { i++; break; } for (int j = 1; argv[i][j]; j++) { @@ -175,10 +176,15 @@ int main(int argc, char** argv) { else if (argv[i][j] == 'r') recursive = 1; else if (argv[i][j] == 'E') use_regex = 2; else if (argv[i][j] == 'F') use_regex = 0; - else if (argv[i][j] == 'e' && j == 1) { - /* -e PATTERN */ - if (argv[i][j+1]) { /* pattern follows in same arg */ } - /* handled below */ + else if (argv[i][j] == 'e') { + if (argv[i][j+1]) { + e_pattern = &argv[i][j+1]; + } else { + i++; + if (i >= argc) { fprintf(stderr, "grep: -e requires an argument\n"); return 2; } + e_pattern = argv[i]; + } + break; /* rest of this arg is the pattern */ } else { fprintf(stderr, "grep: invalid option -- '%c'\n", argv[i][j]); @@ -187,8 +193,11 @@ int main(int argc, char** argv) { } i++; } - if (i >= argc) { fprintf(stderr, "usage: grep [-vcnlqiErF] PATTERN [FILE...]\n"); return 2; } - const char* pattern = argv[i++]; + const char* pattern = e_pattern; + if (!pattern) { + if (i >= argc) { fprintf(stderr, "usage: grep [-vcnlqiErF] PATTERN [FILE...]\n"); return 2; } + pattern = argv[i++]; + } /* Compile regex or set fixed pattern */ if (use_regex == 0) { diff --git a/user/cmds/init/init.c b/user/cmds/init/init.c index 38bba9c5..002a1ba7 100644 --- a/user/cmds/init/init.c +++ b/user/cmds/init/init.c @@ -84,6 +84,10 @@ static int parse_inittab(void) { buf[total] = '\0'; close(fd); + if (total >= (int)sizeof(buf) - 1) { + write(2, "init: warning: /etc/inittab truncated\n", 38); + } + char* p = buf; while (*p && nentries < MAX_ENTRIES) { /* Skip whitespace and comments */ diff --git a/user/cmds/sed/sed.c b/user/cmds/sed/sed.c index 9e062279..1b9fe302 100644 --- a/user/cmds/sed/sed.c +++ b/user/cmds/sed/sed.c @@ -84,12 +84,19 @@ static int parse_cmd(const char* expr, struct sed_cmd* c) { /* Parse first address */ if (*p && *p != 's' && *p != 'd' && *p != 'p' && *p != 'q' && *p != 'a' && *p != 'i' && *p != 'c' && *p != 'y') { - if (parse_addr(&p, &c->addr1) < 0) return -1; + if (parse_addr(&p, &c->addr1) < 0) { + if (c->addr1.has_re) regfree(&c->addr1.re); + return -1; + } while (*p == ' ' || *p == '\t') p++; if (*p == ',') { p++; while (*p == ' ' || *p == '\t') p++; - if (parse_addr(&p, &c->addr2) < 0) return -1; + if (parse_addr(&p, &c->addr2) < 0) { + if (c->addr1.has_re) regfree(&c->addr1.re); + if (c->addr2.has_re) regfree(&c->addr2.re); + return -1; + } c->has_addr2 = 1; while (*p == ' ' || *p == '\t') p++; } @@ -104,13 +111,13 @@ static int parse_cmd(const char* expr, struct sed_cmd* c) { int pi = 0; while (*p && *p != delim && pi < 255) c->s_pat[pi++] = *p++; c->s_pat[pi] = '\0'; - if (*p != delim) return -1; + if (*p != delim) { if (c->addr1.has_re) regfree(&c->addr1.re); if (c->addr2.has_re) regfree(&c->addr2.re); return -1; } p++; int ri = 0; while (*p && *p != delim && ri < 255) c->s_rep[ri++] = *p++; c->s_rep[ri] = '\0'; if (*p == delim) { p++; if (*p == 'g') c->s_global = 1; } - if (regcomp(&c->s_re, c->s_pat, 0) != 0) return -1; + if (regcomp(&c->s_re, c->s_pat, 0) != 0) { if (c->addr1.has_re) regfree(&c->addr1.re); if (c->addr2.has_re) regfree(&c->addr2.re); return -1; } c->s_has_re = 1; break; } diff --git a/user/ulibc/include/linux/futex.h b/user/ulibc/include/linux/futex.h index 7094fa46..ae440338 100644 --- a/user/ulibc/include/linux/futex.h +++ b/user/ulibc/include/linux/futex.h @@ -11,10 +11,11 @@ #define ULIBC_LINUX_FUTEX_H #include +#include #define FUTEX_WAIT 0 #define FUTEX_WAKE 1 -int futex(uint32_t* uaddr, int op, uint32_t val); +int futex(uint32_t* uaddr, int op, uint32_t val, const struct timeval* timeout); #endif diff --git a/user/ulibc/src/futex.c b/user/ulibc/src/futex.c index 32cef878..559b209d 100644 --- a/user/ulibc/src/futex.c +++ b/user/ulibc/src/futex.c @@ -9,7 +9,8 @@ #include "linux/futex.h" #include "syscall.h" +#include -int futex(uint32_t* uaddr, int op, uint32_t val) { - return _syscall3(SYS_FUTEX, (int)uaddr, op, (int)val); +int futex(uint32_t* uaddr, int op, uint32_t val, const struct timeval* timeout) { + return _syscall4(SYS_FUTEX, (int)uaddr, op, (int)val, (int)(uintptr_t)timeout); } diff --git a/user/ulibc/src/pthread.c b/user/ulibc/src/pthread.c index 00dc7163..046e2d5b 100644 --- a/user/ulibc/src/pthread.c +++ b/user/ulibc/src/pthread.c @@ -183,11 +183,11 @@ void pthread_testcancel(void) {} #define FUTEX_WAKE 1 static int futex_wait(volatile int* addr, int val) { - return _syscall3(SYS_FUTEX, (int)addr, FUTEX_WAIT, val); + return _syscall4(SYS_FUTEX, (int)addr, FUTEX_WAIT, val, 0); } static int futex_wake(volatile int* addr, int count) { - return _syscall3(SYS_FUTEX, (int)addr, FUTEX_WAKE, count); + return _syscall4(SYS_FUTEX, (int)addr, FUTEX_WAKE, count, 0); } static int atomic_cas(volatile int* ptr, int old, int new_val) { diff --git a/user/ulibc/src/stdio.c b/user/ulibc/src/stdio.c index ce6092a2..8660f4b7 100644 --- a/user/ulibc/src/stdio.c +++ b/user/ulibc/src/stdio.c @@ -212,12 +212,16 @@ void setbuf(FILE* fp, char* buf) { } int vfprintf(FILE* fp, const char* fmt, va_list ap) { - char buf[1024]; + char buf[4096]; int n = vsnprintf(buf, sizeof(buf), fmt, ap); if (n > 0) { int w = n; - if (w > (int)(sizeof(buf) - 1)) w = (int)(sizeof(buf) - 1); - fwrite(buf, 1, (size_t)w, fp); + if (w > (int)(sizeof(buf) - 1)) { + /* Output was truncated — write the full buffer and report total */ + fwrite(buf, 1, sizeof(buf) - 1, fp); + } else { + fwrite(buf, 1, (size_t)w, fp); + } } return n; } diff --git a/user/ulibc/src/stdlib.c b/user/ulibc/src/stdlib.c index 2c9b4a8e..19674eee 100644 --- a/user/ulibc/src/stdlib.c +++ b/user/ulibc/src/stdlib.c @@ -122,8 +122,11 @@ void free(void* ptr) { /* Coalesce with previous block if adjacent */ struct block_hdr* p_prev = free_list; if (p_prev != b) { - while (p_prev && (struct block_hdr*)(uintptr_t)p_prev->next_free != b) + int iters = 0; + while (p_prev && (struct block_hdr*)(uintptr_t)p_prev->next_free != b) { p_prev = (struct block_hdr*)(uintptr_t)p_prev->next_free; + if (++iters > 1024 || (uintptr_t)p_prev < 0x1000U) { p_prev = NULL; break; } + } if (p_prev && (char*)p_prev + blk_size(p_prev) == (char*)b) { p_prev->size += blk_size(b); p_prev->next_free = b->next_free;