Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Sovereign RTC64 Kernel - Security & Architectural Audit

This document lists every identified flaw, potential vulnerability, and architectural weakness discovered during the industrial audit of the NEONT executive core.

## 1. Critical Initialization & Boot Flaws
- **[Limine] Missing Request Markers:** The kernel is missing `LIMINE_REQUESTS_START_MARKER` and `LIMINE_REQUESTS_END_MARKER`. Without these, the bootloader fails to populate responses for HHDM, Memmap, and Framebuffer, resulting in a system-wide null-pointer cascade.
- **[Boot] HHDM Dependency:** The kernel assumes `hhdm_offset` is always valid. If the HHDM request fails, the kernel continues execution with `hhdm_offset = 0`, leading to incorrect virtual-to-physical mapping and Page Faults during driver initialization.
- **[PMM] Unsafe Bitmap Placement:** `pmm_init` does not verify if `hhdm_offset` is non-zero before calculating the virtual address for the physical bitmap.
- **[Heap] Silent Genesis Failure:** If `pmm_alloc_blocks` fails during heap creation, `hal_malloc_init` is called with a NULL pointer. This results in `global_tlsf_control` remaining NULL, causing all subsequent `malloc` calls to return NULL without error.

## 2. Memory Management & Safety
- **[PMM] Inefficient Block Search:** `pmm_alloc_blocks` always starts searching from page 0 rather than using `pmm_last_alloc`, leading to $O(N)$ performance degradation.
- **[PMM] Lack of Initialization Guards:** Core functions like `pmm_alloc` do not verify if `pmm_bitmap` is non-null, leading to early boot null-pointer dereferences.
- **[Scrubbing] Direct PMM Leak:** While `tlsf_free` zeroes memory, memory requested via `pmm_alloc` directly (for DMA) is not zeroed, potentially leaking physical frame data between drivers.

## 3. Concurrency & Synchronization
- **[VFS] Thread-Unsafe Resolver:** `vfs_resolve` uses a `static char res[256]` buffer. Concurrent calls from different tasks (e.g., Shell and App) will result in path corruption.
- **[Serial] Incomplete Syscall Locking:** The `SYS_SERIAL_WRITE` syscall routes directly to `serial_write`, bypassing the spinlock used by `serial_printf`.
- **[Storage] IO Race Conditions:** `ahci_io_wrapper` and `nvme_io_wrapper` use fixed command slots without per-device locking. Concurrent IO requests will corrupt the hardware command rings.

## 4. Hardware Drivers
- **[AHCI] Virtual Address DMA:** The AHCI driver passes virtual buffers directly to the PRDT. The SATA controller (requiring physical addresses) will DMA into incorrect memory.
- **[EHCI] Incomplete Initialization:** The EHCI driver fails to initialize the Periodic or Asynchronous list base registers. USB transfers will never be processed by the hardware.
- **[PS/2] Blocking IO:** `ps2_wait_read` and `ps2_wait_write` are infinite loops without timeouts. A hardware failure will hang the entire kernel.
- **[xHCI] Slot Config Race:** The xHCI driver reads `HCSPARAMS1` and writes `CONFIG` without ensuring the controller is in a stable state for configuration.

## 5. Scheduling & ABI
- **[Scheduler] Task Limit:** The kernel is hard-coded to 16 tasks. `scheduler_spawn` fails silently when the limit is reached.
- **[Scheduler] Delayed Stack Audit:** Stack overflow detection (canary check) only occurs in the Idle task once per second, leaving a large window for memory corruption.
- **[Syscall] Pointer Validation:** VFS syscalls do not verify if provided buffer pointers belong to the calling task's memory space.

## 6. Graphics & UI
- **[VGA] Dirty Rect Missing:** `vga_log` performs a full-screen `memset` on wrap-around, causing visible flicker. No line-scrolling is implemented.
- **[Input] Coordinate Clamping:** `hal_input_push_event` accumulates mouse coordinates without clamping to screen dimensions, allowing the cursor to move into "ghost" memory.
- **[Framebuffer] Unprotected Access:** Multiple tasks (Environment Manager and VGA Log) can write to the framebuffer simultaneously without synchronization.

## 7. Security & Policy
- **[UAC] Implementation Stubs:** `uac_request_permit` in `kernel/uac_policy.c` is currently a placeholder and does not actually perform security challenges or interrupt-based elevation.
- **[Panic] OSOD Recursion:** If `draw_glyph` or `memset` fails inside `osod_render`, the kernel may enter a recursive panic state without a hardware reset.
- **[VFS] FAT32 Auto-Format:** The VFS automatically formats uninitialized disks. This is a potential data-loss risk if a disk with a different filesystem is connected.

## 8. 64-bit Architectural Risks
- **[Boot] Limine Request Markers:** Mandatory `LIMINE_REQUESTS_START_MARKER` and `LIMINE_REQUESTS_END_MARKER` are missing from the kernel entry point, which may cause Limine to ignore the executive's hardware requests (Memmap, HHDM, Framebuffer) on some versions.
- **[MMIO] Hardcoded APIC Address:** `APIC_BASE` is hardcoded to `0xFEE00000`. Industrial kernels should discover the local APIC address via the MP Table or ACPI MADT to ensure compatibility across diverse hardware.
- **[Paging] HHDM Assumption:** The kernel assumes a single `hhdm_offset` applies to all physical addresses. If the bootloader maps different regions with different offsets (unlikely but possible in Limine), the kernel logic will fail.
- **[CPU] Lack of Multi-core Support:** The kernel is strictly single-core. While spinlocks are implemented, they do not handle IPIs (Inter-Processor Interrupts) or cache coherency across multiple physical CPUs.
11 changes: 11 additions & 0 deletions include/hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,15 @@ void hal_usb_poll(void);
void rtc_get_time(int *h, int *m, int *s);
uint64_t hal_get_uptime_ms(void);

/* --- Synchronization --- */
typedef volatile int spinlock_t;
static inline void spin_lock(spinlock_t *lock) {
while (__sync_lock_test_and_set(lock, 1)) {
__asm__ volatile("pause");
}
}
static inline void spin_unlock(spinlock_t *lock) {
__sync_lock_release(lock);
}

#endif
23 changes: 19 additions & 4 deletions kernel/apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define APIC_TMR 0x320
#define APIC_TDCR 0x3E0
#define APIC_TICR 0x380
#define APIC_TCCR 0x390

extern uint64_t hhdm_offset;
extern uint64_t scheduler_switch(uint64_t current_rsp);
Expand Down Expand Up @@ -38,11 +39,25 @@ void apic_init(void) {
/* Step 2: Calibrate Divider (Divide by 16) */
apic_write(APIC_TDCR, 0x03);

/* Step 3: Configure Timer (Vector 32, Periodic mode, start MASKED) */
apic_write(APIC_TMR, 32 | 0x20000 | 0x10000);
/* Step 3: PIT-based Calibration for High-Power Precision */
/* Set PIT to one-shot mode, approx 10ms (1193182 / 100) */
outb(0x43, 0x30);
outb(0x40, 0x9B);
outb(0x40, 0x2E);

apic_write(APIC_TICR, 0xFFFFFFFF);

/* Wait for PIT to finish */
while (1) {
outb(0x43, 0xE2);
if (inb(0x40) & 0x80) break;
}

/* Step 4: Set Initial Count */
apic_write(APIC_TICR, 1000000);
uint32_t ticks_per_10ms = 0xFFFFFFFF - apic_read(APIC_TCCR);

/* Step 4: Configure Timer (Vector 32, Periodic mode, start MASKED) */
apic_write(APIC_TMR, 32 | 0x20000 | 0x10000);
apic_write(APIC_TICR, ticks_per_10ms);
}

uint64_t hal_get_uptime_ms(void) {
Expand Down
10 changes: 9 additions & 1 deletion kernel/drivers/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef struct {
#define MAX_PCI_DEVICES 64
static pci_device_info_t g_pci_devices[MAX_PCI_DEVICES];
static int g_pci_count = 0;
static spinlock_t g_pci_lock = 0;

static uint32_t pci_read_config(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t address = (uint32_t)((uint32_t)bus << 16) | ((uint32_t)slot << 11) |
Expand All @@ -41,6 +42,7 @@ uint64_t pci_get_bar(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_index)
}

void pci_scan(void) {
spin_lock(&g_pci_lock);
serial_printf("[PCI] Starting system hardware scan...\n");
g_pci_count = 0;
memset(g_pci_devices, 0, sizeof(g_pci_devices));
Expand Down Expand Up @@ -101,14 +103,20 @@ void pci_scan(void) {
}
}
serial_printf("[PCI] Scan complete. Total devices: %d\n", g_pci_count);
spin_unlock(&g_pci_lock);
}

int pci_get_device_count(void) { return g_pci_count; }

int pci_get_device_info(int index, char* buf, size_t sz) {
if (index < 0 || index >= g_pci_count) return -1;
spin_lock(&g_pci_lock);
if (index < 0 || index >= g_pci_count) {
spin_unlock(&g_pci_lock);
return -1;
}
pci_device_info_t* d = &g_pci_devices[index];
snprintf(buf, sz, "V:%04X D:%04X C:%02X S:%02X P:%02X",
d->vendor, d->device, d->class_id, d->subclass, d->prog_if);
spin_unlock(&g_pci_lock);
return 0;
}
10 changes: 9 additions & 1 deletion kernel/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
static input_event_t g_input_queue[INPUT_QUEUE_SIZE];
static volatile int g_queue_head = 0;
static volatile int g_queue_tail = 0;
static spinlock_t g_input_lock = 0;

static int g_mouse_abs_x = 0;
static int g_mouse_abs_y = 0;
Expand Down Expand Up @@ -57,6 +58,7 @@ bool hal_input_get_device_info(int index, input_device_info_t *info) {
}

void hal_input_push_event(input_event_t ev) {
spin_lock(&g_input_lock);
int next = (g_queue_head + 1) % INPUT_QUEUE_SIZE;
if (next != g_queue_tail) {
g_input_queue[g_queue_head] = ev;
Expand All @@ -66,12 +68,18 @@ void hal_input_push_event(input_event_t ev) {
g_mouse_abs_x += ev.mouse.x;
g_mouse_abs_y += ev.mouse.y;
}
spin_unlock(&g_input_lock);
}

bool hal_input_pop_event(input_event_t *ev) {
if (g_queue_head == g_queue_tail) return false;
spin_lock(&g_input_lock);
if (g_queue_head == g_queue_tail) {
spin_unlock(&g_input_lock);
return false;
}
*ev = g_input_queue[g_queue_tail];
g_queue_tail = (g_queue_tail + 1) % INPUT_QUEUE_SIZE;
spin_unlock(&g_input_lock);
return true;
}

Expand Down
17 changes: 17 additions & 0 deletions kernel/interrupts.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ static idt_entry_t idt[256];
static idt_ptr_t idt_ptr;

extern void* isr_stub_table[];
extern void isr_stub_128(void);

extern int syscall_dispatch(int num, const void* a1, void* a2, size_t a3);

void syscall_handler(struct cpu_state* state) {
/* SYS_MALLOC and SYS_I18N_TRANSLATE return pointers, others return int.
* For simplicity in this skeleton, we route all through syscall_dispatch
* and store result in RAX. */
state->rax = (uint64_t)syscall_dispatch((int)state->rax, (void*)state->rdi, (void*)state->rsi, (size_t)state->rdx);
}

// Hardware exception gateways from panic.c
extern void handler_divide_by_zero(void);
Expand Down Expand Up @@ -53,6 +63,9 @@ void idt_init(void) {
idt_set_gate(13, (uint64_t)handler_general_protection_fault, 0x08, 0x8E);
idt_set_gate(14, (uint64_t)handler_page_fault, 0x08, 0x8E);

/* System Call Entry: Vector 0x80 */
idt_set_gate(0x80, (uint64_t)isr_stub_128, 0x08, 0xEE); /* DPL=3 for user-space access */

idt_ptr.limit = sizeof(idt) - 1;
idt_ptr.base = (uint64_t)&idt;
__asm__ volatile ("lidt %0" : : "m"(idt_ptr));
Expand All @@ -67,6 +80,10 @@ void irq_install_handler(int i, irq_handler_t handler) {

extern void apic_eoi(void);
void exception_handler(struct cpu_state *state) {
if (state->interrupt_number == 128) {
syscall_handler(state);
return;
}
if (state->interrupt_number >= 32) {
/* POWER: Centralized IRQ Acknowledgement */
if (irq_handlers[state->interrupt_number]) {
Expand Down
20 changes: 18 additions & 2 deletions kernel/isr_stubs.s
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ irq 45
irq 46
irq 47

.global isr_stub_128
isr_stub_128:
pushq $0
pushq $128
jmp isr_common

isr_common:
pushq %rax
pushq %rbx
Expand Down Expand Up @@ -154,7 +160,12 @@ isr_common:
popq %rbx
popq %rax
addq $16, %rsp
iretq
/* ABI Integrity: Zero out scratch registers only if returning to user-space */
testq $3, 8(%rsp) /* Check CS selector on stack for CPL3 */
jz 1f
xorq %r11, %r11
xorq %rcx, %rcx
1: iretq

irq_common:
pushq %rax
Expand Down Expand Up @@ -240,7 +251,12 @@ irq_common:
popq %rbx
popq %rax
addq $16, %rsp
iretq
/* ABI Integrity: Zero out scratch registers only if returning to user-space */
testq $3, 8(%rsp)
jz 1f
xorq %r11, %r11
xorq %rcx, %rcx
1: iretq

.section .data
.global isr_stub_table
Expand Down
15 changes: 13 additions & 2 deletions kernel/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ volatile struct limine_module_request module_request = {
.revision = 0
};

static volatile struct limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0
};

/* --- Global OS State --- */
uint64_t hhdm_offset = 0;
struct limine_framebuffer *primary_fb = NULL;
Expand Down Expand Up @@ -109,10 +114,16 @@ void kernel_main(void) {
gdt_init();
idt_init();

pmm_init(NULL);
hal_malloc_init(pmm_alloc_blocks(1024), 1024 * 4096);
if (memmap_request.response) {
pmm_init(memmap_request.response);
}

/* Industrial Heap Genesis: Allocate physical blocks and map to virtual HHDM space */
void* heap_phys = pmm_alloc_blocks(1024);
hal_malloc_init((void*)((uint64_t)heap_phys + hhdm_offset), 1024 * 4096);

apic_init();
irq_install_handler(32, timer_handler);
init_sse();

scheduler_init();
Expand Down
13 changes: 11 additions & 2 deletions kernel/malloc_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <stdint.h>

static void* global_tlsf_control = NULL;
static spinlock_t g_malloc_lock = 0;

void hal_malloc_init(void* mem, size_t bytes) {
if (mem && bytes > 0) {
Expand All @@ -16,15 +17,23 @@ void* tlsf_get_global(void) {
}

void* malloc(size_t size) {
return tlsf_malloc(global_tlsf_control, size);
spin_lock(&g_malloc_lock);
void* ptr = tlsf_malloc(global_tlsf_control, size);
spin_unlock(&g_malloc_lock);
return ptr;
}

void free(void* ptr) {
spin_lock(&g_malloc_lock);
tlsf_free(global_tlsf_control, ptr);
spin_unlock(&g_malloc_lock);
}

void* realloc(void* ptr, size_t size) {
return tlsf_realloc(global_tlsf_control, ptr, size);
spin_lock(&g_malloc_lock);
void* res = tlsf_realloc(global_tlsf_control, ptr, size);
spin_unlock(&g_malloc_lock);
return res;
}

void* calloc(size_t nmemb, size_t size) {
Expand Down
12 changes: 12 additions & 0 deletions kernel/nuklear_kernel_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ void itoa_meaty(unsigned long long n, char* s, int base, bool neg, int width, ch
if (neg) s[i++] = '-';
while (i < width) s[i++] = pad;
s[i] = '\0'; reverse(s);
/* Correct padding logic: if pad is '0', the '-' should be at the front */
if (neg && pad == '0' && width > 0) {
if (s[0] == '0') {
for (int j = 0; j < (int)strlen(s); j++) {
if (s[j] == '-') { s[j] = '0'; break; }
}
s[0] = '-';
}
}
}

int vsnprintf(char* str, size_t size, const char* format, va_list ap) {
Expand Down Expand Up @@ -183,6 +192,9 @@ int vsnprintf(char* str, size_t size, const char* format, va_list ap) {
const char* s = buf; while (*s && i < size - 1) {
char c = *s++; if (spec == 'X' && c >= 'a' && c <= 'z') c -= 32; str[i++] = c;
}
} else if (*format == 'c') {
char c = (char)va_arg(ap, int);
str[i++] = c;
} else if (*format == '%') { str[i++] = '%'; }
else { /* Skip unknown */ }
} else { str[i++] = *format; }
Expand Down
Loading