]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
security: A07 complete, Round 3.1 initrd/LZ4/TAR validation (A05)
authorTulio A M Mendes <[email protected]>
Mon, 25 May 2026 18:11:07 +0000 (15:11 -0300)
committerTulio A M Mendes <[email protected]>
Mon, 25 May 2026 18:11:07 +0000 (15:11 -0300)
- 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)

include/fs.h
src/drivers/initrd.c
src/kernel/fs.c
src/kernel/syscall.c

index e02dbd13925b995b7eda188d348a1a58908ea7f9..bf2e45acfdede75b733bf8fb89b1b5a531effdbf 100644 (file)
@@ -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);
index b060956233a28a8e713dca6582b568a4d0174a41..0a3a8c5fd0f807be3aee5a7f4f7bce89baa4a5ee 100644 (file)
@@ -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);
index 46254c59829b2c31916a60eb006423ea1d3f7c68..d3bc68319543ec7cb2af8ed0ea60c638905ec4fb 100644 (file)
@@ -16,6 +16,7 @@
 #include "spinlock.h"
 #include "console.h"
 #include "heap.h"
+#include "process.h"
 
 #include <string.h>
 
@@ -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;
 }
 
index c144b2ee9191cdb8d013fa87b379d512b2eccfdd..8a551de9aa21d4eee8beed38e4cd3c16da1f0c45 100644 (file)
@@ -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;