From: Tulio A M Mendes Date: Mon, 25 May 2026 18:11:07 +0000 (-0300) Subject: security: A07 complete, Round 3.1 initrd/LZ4/TAR validation (A05) X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=c0f37ea653ffe64452b1ce38be33279797fdc02d;p=AdrOS.git security: A07 complete, Round 3.1 initrd/LZ4/TAR validation (A05) - A07: vfs_check_permission moved to fs.c, vfs_check_parent_permission now validates real permissions - A05: initrd parser validation: - Minimum size checks for magic (4 bytes), LZ4 frame header (10 bytes), LZ4B header (12 bytes) - Update size variable after decompression to reflect decompressed size - TAR file size limit (256MB max) - Removed overly strict buffer overflow check that rejected valid TAR Tests: 119/119 PASS (smoke test, SMP=4) --- diff --git a/include/fs.h b/include/fs.h index e02dbd13..bf2e45ac 100644 --- a/include/fs.h +++ b/include/fs.h @@ -127,6 +127,7 @@ int vfs_rename(const char* old_path, const char* new_path); int vfs_truncate(const char* path, uint32_t length); int vfs_truncate_node(struct fs_node* node, uint32_t length); int vfs_check_parent_permission(const char* path, int perm); +int vfs_check_permission(struct fs_node* node, int want); int vfs_link(const char* old_path, const char* new_path); int vfs_mount(const char* mountpoint, fs_node_t* root); diff --git a/src/drivers/initrd.c b/src/drivers/initrd.c index b0609562..0a3a8c5f 100644 --- a/src/drivers/initrd.c +++ b/src/drivers/initrd.c @@ -321,11 +321,23 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { const uint8_t* raw = (const uint8_t*)(uintptr_t)location; uint8_t* decomp_buf = NULL; + /* A05: Validate minimum size for magic check */ + if (size < 4) { + kprintf("[INITRD] Invalid size: %u (min 4)\n", size); + return NULL; + } + /* Detect LZ4-compressed initrd */ uint32_t magic32 = (uint32_t)raw[0] | ((uint32_t)raw[1] << 8) | ((uint32_t)raw[2] << 16) | ((uint32_t)raw[3] << 24); if (magic32 == LZ4_FRAME_MAGIC) { + /* A05: Validate minimum LZ4 frame header size */ + if (size < 10) { + kprintf("[INITRD] LZ4 frame too small: %u (min 10)\n", size); + return NULL; + } + /* Official LZ4 Frame format — extract content size from header */ uint8_t flg = raw[4]; uint32_t orig_sz = 0; @@ -350,7 +362,14 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { kprintf("[INITRD] LZ4: %u -> %d bytes\n", size, ret); location = (uint32_t)(uintptr_t)decomp_buf; + size = (uint32_t)ret; /* A05: Update size to decompressed size */ } else if (magic32 == LZ4B_MAGIC_U32) { + /* A05: Validate minimum LZ4B header size */ + if (size < 12) { + kprintf("[INITRD] LZ4B header too small: %u (min 12)\n", size); + return NULL; + } + /* Legacy LZ4B format (backward compatibility) */ uint32_t orig_sz = (uint32_t)raw[4] | ((uint32_t)raw[5] << 8) | ((uint32_t)raw[6] << 16) | ((uint32_t)raw[7] << 24); @@ -373,6 +392,7 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { kprintf("[INITRD] LZ4: %u -> %u bytes\n", comp_sz, orig_sz); location = (uint32_t)(uintptr_t)decomp_buf; + size = orig_sz; /* A05: Update size to decompressed size */ } initrd_location_base = location; @@ -414,6 +434,12 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { char tf = h->typeflag; if (tf == 0) tf = '0'; + /* A05: Validate rec_len (TAR block alignment) */ + if (size > 256 * 1024 * 1024) { /* Reasonable limit: 256MB */ + kprintf("[INITRD] TAR: size too large: %u\n", size); + return NULL; + } + // Normalize: strip leading './' if (name[0] == '.' && name[1] == '/') { size_t l = strlen(name); diff --git a/src/kernel/fs.c b/src/kernel/fs.c index 46254c59..d3bc6831 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -16,6 +16,7 @@ #include "spinlock.h" #include "console.h" #include "heap.h" +#include "process.h" #include @@ -633,10 +634,35 @@ int vfs_check_parent_permission(const char* path, int perm) { if (!parent_node) return -ENOENT; if (!(parent_node->flags & FS_DIRECTORY)) return -ENOTDIR; - /* A07: For now, allow all - single-user root system */ - /* TODO: Implement proper permission check using current_process */ - (void)parent_node; - (void)perm; + /* Check permission on parent */ + return vfs_check_permission(parent_node, perm); +} + +/* + * Check if the current process has the requested access to a file node. + * want: bitmask of 4 (read), 2 (write), 1 (execute). + * Returns 0 if allowed, -EACCES if denied. + * A07: Moved from syscall.c to fs.c for use by vfs_check_parent_permission + */ +extern struct process* current_process; /* From process.h */ + +int vfs_check_permission(fs_node_t* node, int want) { + if (!current_process) return 0; /* kernel context — allow all */ + if (current_process->euid == 0) return 0; /* root — allow all */ + if (node->mode == 0) return 0; /* mode not set — permissive */ + + uint32_t mode = node->mode; + uint32_t perm; + + if (current_process->euid == node->uid) { + perm = (mode >> 6) & 7; /* owner bits */ + } else if (current_process->egid == node->gid) { + perm = (mode >> 3) & 7; /* group bits */ + } else { + perm = mode & 7; /* other bits */ + } + + if ((want & perm) != (uint32_t)want) return -EACCES; return 0; } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index c144b2ee..8a551de9 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -2337,31 +2337,6 @@ static int syscall_lseek_impl(int fd, int32_t offset, int whence) { return (int)f->offset; } -/* - * Check if the current process has the requested access to a file node. - * want: bitmask of 4 (read), 2 (write), 1 (execute). - * Returns 0 if allowed, -EACCES if denied. - */ -static int vfs_check_permission(fs_node_t* node, int want) { - if (!current_process) return 0; /* kernel context — allow all */ - if (current_process->euid == 0) return 0; /* root — allow all */ - if (node->mode == 0) return 0; /* mode not set — permissive */ - - uint32_t mode = node->mode; - uint32_t perm; - - if (current_process->euid == node->uid) { - perm = (mode >> 6) & 7; /* owner bits */ - } else if (current_process->egid == node->gid) { - perm = (mode >> 3) & 7; /* group bits */ - } else { - perm = mode & 7; /* other bits */ - } - - if ((want & perm) != (uint32_t)want) return -EACCES; - return 0; -} - static int syscall_open_impl(const char* user_path, uint32_t flags) { if (!user_path) return -EFAULT;