]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
FIX security audit: 9 vulnerabilities across kernel and userland
authorTulio A M Mendes <[email protected]>
Tue, 28 Apr 2026 02:49:18 +0000 (23:49 -0300)
committerTulio A M Mendes <[email protected]>
Mon, 4 May 2026 23:52:08 +0000 (20:52 -0300)
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

12 files changed:
src/kernel/syscall.c
src/mm/heap.c
user/cmds/dd/dd.c
user/cmds/fulltest/fulltest.c
user/cmds/grep/grep.c
user/cmds/init/init.c
user/cmds/sed/sed.c
user/ulibc/include/linux/futex.h
user/ulibc/src/futex.c
user/ulibc/src/pthread.c
user/ulibc/src/stdio.c
user/ulibc/src/stdlib.c

index a292b51440a94845d3d65b3b66367f0f5b7d8e99..78ca1120a791a798017bb0e23cd81071528c9681 100644 (file)
@@ -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;
index b510278df456799cb3c34a7135b3d240fc2ded4d..19a7a219d596f27e6ed253486809fe2373d83f0e 100644 (file)
@@ -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;
index 47981eab92dd6e1e4542b6580fd4d2008268a773..54cd62de9c8cec45fc126ce2e887e600f4a010be 100644 (file)
 #include <unistd.h>
 #include <fcntl.h>
 
-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;
index 8aea99dfa643d94c27876230d1214afffc8fe3b2..f5b82bf408a2e2610c970e688f50dd1535393b96 100644 (file)
@@ -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);
 }
 
index 261865a154f589976624cef9c41eeaac3723007f..5caf1d8e22bf29562393263c4dba6f377319495c 100644 (file)
@@ -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) {
index 38bba9c5eea93c010fc55ea428e5ade42fbffaa2..002a1ba761def75d9692c3b8215ca63220f86d80 100644 (file)
@@ -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 */
index 9e0622791480f2e960d39cb753b7dea6bf880504..1b9fe3028d8556708b84b3e9d99ae40a554d812e 100644 (file)
@@ -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;
     }
index 7094fa46841cd6d53751cfd72f6d0201d2e2eaa4..ae44033809cfced353fae61bdf8539c36559a34a 100644 (file)
 #define ULIBC_LINUX_FUTEX_H
 
 #include <stdint.h>
+#include <sys/time.h>
 
 #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
index 32cef8786837a37b668296f6f16a8fa26f9be14f..559b209d8dcec6e0ad98b09fe39e2870466c0c2a 100644 (file)
@@ -9,7 +9,8 @@
 
 #include "linux/futex.h"
 #include "syscall.h"
+#include <sys/time.h>
 
-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);
 }
index 00dc716316028307d4e2ccc725606c90c2a4d83e..046e2d5b1dc90024fa2c0fbef6e3576bf125e66a 100644 (file)
@@ -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) {
index ce6092a2f325a96daf2e23ed1b450e15cea09970..8660f4b7a795dfb7f8f842c4f0e6b6e5748d8a89 100644 (file)
@@ -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;
 }
index 2c9b4a8e3d4dadcff8a591f0f7c01e2ba816fea7..19674eeebb4bf7c8e2299b5805ec78ec0ea2beec 100644 (file)
@@ -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;