Skip to content

Commit 78af708

Browse files
committed
ubsan and watchdogs for task_stack and kheap
1 parent e9fa0d9 commit 78af708

File tree

8 files changed

+721
-35
lines changed

8 files changed

+721
-35
lines changed

Makefile

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ CC = gcc
88
GDB = gdb
99
LD = ld -m elf_i386
1010
CFLAGSNO = -fno-pie -nostdlib -fno-builtin -nodefaultlibs -nostartfiles -Wno-error=comment
11+
USAN = -fsanitize=undefined -fno-sanitize=shift
1112
CFLAGS = -g -O0 -m32 -fno-pie -ffreestanding -nostdlib -fno-builtin -nodefaultlibs -nostartfiles -Werror -Wpedantic -Wall -Wextra -I$(shell pwd)
12-
1313
NETWORKING = -netdev tap,id=my_tap0,ifname=tap0 -device rtl8139,netdev=my_tap0
14+
RUN = qemu-system-i386 -m 4 -serial stdio -kernel $(BUILD_DIR)/kernel.elf -drive file=disk/fat16.img,format=raw
1415

1516
$(shell mkdir -p $(BUILD_DIR)/boot $(BUILD_DIR)/kernel $(BUILD_DIR)/drivers $(BUILD_DIR)/cpu $(BUILD_DIR)/libc $(BUILD_DIR)/apps $(BUILD_DIR)/net)
1617

@@ -28,8 +29,11 @@ $(BUILD_DIR)/%.o: %.asm
2829
$(BUILD_DIR)/%.bin: %.asm
2930
nasm $< -f bin -o $@
3031

32+
net: all
33+
$(RUN) $(NETWORKING)
3134
run: all
32-
qemu-system-i386 -m 4 -serial stdio -kernel $(BUILD_DIR)/kernel.elf -drive file=disk/fat16.img,format=raw $(NETWORKING)
35+
$(RUN)
36+
3337
#################
3438
disk/fat16.img:
3539
cd disk && ./make_disk.sh
@@ -46,7 +50,7 @@ bits:
4650
find . -type f -name "*.h" 2>/dev/null | sed 's|^./||' | awk '{print "#include \"" $$0 "\""}' >> $(OUTPUT_FILE)
4751

4852
debug: all
49-
qemu-system-i386 -m 4 -serial stdio -s -S -kernel $(BUILD_DIR)/kernel.elf -drive file=disk/fat16.img,format=raw -netdev user,id=mynet0 -device rtl8139,netdev=mynet0
53+
$(RUN) $(NETWORKING)
5054

5155
attach:
5256
${GDB} -ex "target remote localhost:1234" -ex "symbol-file $(BUILD_DIR)/kernel.elf"

TODO

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,12 @@ build disk:
3434
on generation save the img, remove it from clean and keep backups
3535
when trying to generate new one, make a file comparator that shows the differences in file
3636

37-
notes:
38-
- make sure that the function really takes va_list and not just ... bc its UB
39-
4037
adjustments:
4138
- adjust the linker file, put heap on _end
4239
- inconsistent type usage
4340

4441
multitaskin:
4542
- im not confident that semaphore actually does what it should, and the interruption logic could be improved with a better model like having priorities, placing currently io-blocked tasks to top-prio, right now its just a hack, wasting some cycles for sure
46-
- detach the timer from the scheduler put it on the separate 0x30 interrupt vecotr
47-
- wrapper so functions can return properly, completed state and de-schedule
4843
- signalling, recovery strategies
4944

5045
forking should work now?
@@ -63,19 +58,15 @@ c9:
6358
- port the rtl8139, arp, dhcp, udp drivers
6459
https://github.com/szhou42/osdev/blob/master/src/kernel/network/udp.c#L12
6560

66-
- change everythiung to debug serial
67-
- actually try it
6861
- the shit that i changed to use the stack, somewhere could crash, if phsy addresses get passed around like that contantly
69-
70-
- maybe add canaries
71-
- maybe write some more tests
72-
73-
https://os.phil-opp.com/allocator-designs/
74-
62+
- what?
7563

7664

7765
TODO:
7866
[x] fix the scheduler, provide a wrapper so fns can finish
79-
watchdog for thread stack and kheap magic
80-
thread the ehternet packets
81-
add some log groups for debugging
67+
[x] usan
68+
[ ] asan
69+
[x] watchdog for thread stack and kheap magic
70+
[ ] more comprehensive thread reg/unreg
71+
[ ] thread the ehternet packets
72+
[ ] add some log groups for debugging

cpu/task.c

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11

22
#include "task.h"
3+
#include "apps/hexdump.h"
34
#include "cpu/semaphore.h"
45
#include "drivers/keyboard.h"
56
#include "drivers/serial.h"
67
#include "libc/mem.h"
78
#include "libc/utils.h"
89

10+
/**/
911
#undef serial_debug
1012
#define serial_debug(...)
13+
/**/
1114

1215
volatile int current_task_idx = 0;
1316
volatile int active_tasks = 0;
14-
static task_t tasks[MAX_TASK] = {0};
17+
task_t tasks[MAX_TASK] = {0};
1518

1619
void switch_stack_and_jump(uint32_t stack, uint32_t task);
1720
void switch_stack(uint32_t esp, uint32_t ebp);
@@ -54,26 +57,30 @@ void schedule(registers_t *regs) {
5457
if (current_task_idx >= active_tasks) {
5558
current_task_idx = 0;
5659
}
57-
serial_debug("schedule pass from %s to %s", tasks[old_task_idx].name,
58-
tasks[current_task_idx].name);
5960

6061
task_t *task_to_run = &tasks[current_task_idx];
6162

6263
if (task_to_run->status == TASK_WAITING_FOR_START) {
6364
task_to_run->status = TASK_RUNNING;
6465
switch_stack_and_jump(task_to_run->esp, (uint32_t)task_wrapper);
6566
return;
67+
} else if (task_to_run->status != TASK_RUNNING) {
68+
6669
} else if (task_to_run->status != TASK_RUNNING) {
6770
return; // skip this task
6871
}
72+
serial_debug("schedule pass from %d:%s to %d:%s", old_task_idx,
73+
tasks[old_task_idx].name, current_task_idx,
74+
tasks[current_task_idx].name);
6975

7076
memcpy(regs, &task_to_run->ctx, sizeof(registers_t));
7177

7278
// no need for switch_stack - the interrupt return will handle it
7379
}
7480

7581
bool check_stack_overflow(int task_id) {
76-
void *stack_bottom = (void *)(tasks[task_id].ebp - tasks[task_id].stack_size);
82+
void *stack_bottom =
83+
(void *)(tasks[task_id].ebp - tasks[task_id].stack_size + 4);
7784
return (*(uint32_t *)stack_bottom != STACK_CANARY);
7885
}
7986

@@ -86,11 +93,8 @@ void create_task(int task_id, char *name, task_function_t f, void *stack,
8693
memcpy(task.name, name, strlen(name));
8794
task.status = TASK_WAITING_FOR_START;
8895

89-
uint32_t stack_top = (uint32_t)stack - stack_size;
90-
uint32_t stack_bottom = (uint32_t)stack; // leave space for canary
91-
92-
serial_debug("stack_top: %x, stack_bottom: %x, size: %d", stack_top,
93-
stack_bottom, stack_size);
96+
uint32_t stack_bottom = (uint32_t)stack;
97+
uint32_t stack_top = (uint32_t)stack + stack_size - 4; // canary
9498

9599
*(uint32_t *)stack_bottom = STACK_CANARY;
96100

@@ -115,9 +119,32 @@ void create_task(int task_id, char *name, task_function_t f, void *stack,
115119
memcpy((void *)&tasks[task_id], (void *)&task, sizeof(task_t));
116120
active_tasks++;
117121

122+
serial_debug("task %d created: name='%s', status=%d, function=%x, "
123+
"stack=%x-%x, esp=%x, ebp=%x, stack_size=%d, canary=%x",
124+
task_id, task.name, task.status, (uint32_t)task.task,
125+
stack_bottom, stack_top, task.esp, task.ebp, task.stack_size);
126+
// hexdump(stack, 16, 8);
118127
asm volatile("sti");
119128
}
120129

130+
void task_stack_check() {
131+
while (1) {
132+
// dont do anything with main
133+
for (int i = 2; i < active_tasks; i++) {
134+
if (check_stack_overflow(i)) {
135+
serial_printff("stack overflow in task %d:%s ebp was at %x", i,
136+
tasks[i].name, tasks[i].ebp);
137+
asm volatile("cli");
138+
void *stack_bottom = (void *)(tasks[i].ebp - tasks[i].stack_size);
139+
hexdump(stack_bottom, 64, 8);
140+
while (1) {
141+
}
142+
}
143+
}
144+
}
145+
}
146+
147+
void kheap_watchdog();
121148
void init_tasking() {
122149
memset(tasks, 0, sizeof(tasks));
123150

@@ -127,4 +154,10 @@ void init_tasking() {
127154

128155
current_task_idx = 0;
129156
active_tasks = 1;
157+
158+
void *ss = kmalloc(1024);
159+
create_task(1, "task_stack_check", task_stack_check, ss, 1024);
160+
161+
void *s2 = kmalloc(5000);
162+
create_task(2, "kheap_watchdog", kheap_watchdog, s2, 5000);
130163
}

kernel/kernel.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void selftest() {
3333

3434
uint32_t ss = 1024;
3535
void *fnstack = kmalloc_a(ss);
36-
create_task(1, "sched_test", &sched_test_fn, fnstack, ss);
36+
create_task(3, "sched_test", &sched_test_fn, fnstack, ss);
3737
int i = 1000000;
3838
while (1 != sem) {
3939
i--;
@@ -42,6 +42,7 @@ void selftest() {
4242
}
4343
}
4444
kfree(fnstack);
45+
serial_debug("selftest finished!");
4546
}
4647

4748
void kernel_main(void) {

libc/heap.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
// https://git.9front.org/plan9front/plan9front/0861d0d0f283a9917721214fa3dc1c51a778213d/sys/src/9/port/xalloc.c/f.html
66
// i have no idea what license it is
77

8+
#undef serial_printff
9+
#define serial_printff(...)
10+
811
#define nil 0
912
#define nelem(x) (sizeof(x) / sizeof((x)[0]))
1013

@@ -314,3 +317,123 @@ void *kmalloc_ap(uint32_t size, uint32_t *phys) {
314317
}
315318

316319
void *kmalloc(uint32_t size) { return kmalloc_int(size, 0, 0); }
320+
321+
void kheap_watchdog() {
322+
serial_debug("heap watchdog started");
323+
324+
while (1) {
325+
326+
hole_t *h;
327+
int free_chunks = 0;
328+
uintptr_t free_memory = 0;
329+
bool free_list_corrupted = false;
330+
331+
for (h = xlists.table; h != NULL; h = h->link) {
332+
free_chunks++;
333+
free_memory += h->size;
334+
335+
if (h->addr + h->size != h->top) {
336+
serial_printff("FREE LIST CORRUPTION: Hole at %x has inconsistent "
337+
"bounds (addr=%x, size=%d, top=%x)",
338+
(uintptr_t)h, h->addr, h->size, h->top);
339+
free_list_corrupted = true;
340+
}
341+
342+
if (h->link != NULL &&
343+
((uintptr_t)h->link < KHEAP_START ||
344+
(uintptr_t)h->link >= KHEAP_START + KHEAP_INITIAL_SIZE)) {
345+
serial_printff("FREE LIST CORRUPTION: Hole at %x has invalid link %x",
346+
(uintptr_t)h, (uintptr_t)h->link);
347+
free_list_corrupted = true;
348+
}
349+
}
350+
351+
// create a map of free regions to avoid checking them for allocated blocks
352+
#define MAX_FREE_REGIONS 32
353+
struct {
354+
uintptr_t start;
355+
uintptr_t end;
356+
} free_regions[MAX_FREE_REGIONS];
357+
int free_region_count = 0;
358+
359+
for (h = xlists.table; h != NULL && free_region_count < MAX_FREE_REGIONS;
360+
h = h->link) {
361+
free_regions[free_region_count].start = KHEAP_START + h->addr;
362+
free_regions[free_region_count].end = KHEAP_START + h->top;
363+
free_region_count++;
364+
}
365+
366+
uintptr_t current_addr = KHEAP_START;
367+
int corrupted_blocks = 0;
368+
int valid_blocks = 0;
369+
uintptr_t allocated_memory = 0;
370+
371+
while (current_addr < KHEAP_START + KHEAP_INITIAL_SIZE) {
372+
// check if current address is in a free region
373+
bool in_free_region = false;
374+
for (int i = 0; i < free_region_count; i++) {
375+
if (current_addr >= free_regions[i].start &&
376+
current_addr < free_regions[i].end) {
377+
// skip to the end of this free region
378+
current_addr = free_regions[i].end;
379+
in_free_region = true;
380+
break;
381+
}
382+
}
383+
384+
if (in_free_region) {
385+
continue;
386+
}
387+
388+
block_header_t *block = (block_header_t *)current_addr;
389+
390+
// Basic validation for what might be a block header
391+
if (block->size >= sizeof(block_header_t) &&
392+
block->size < 65535 && // Max reasonable block size for uint16_t
393+
(current_addr + block->size) <= (KHEAP_START + KHEAP_INITIAL_SIZE)) {
394+
395+
if (block->magix == (uint16_t)magichole) {
396+
// this looks like a valid block
397+
valid_blocks++;
398+
allocated_memory += block->size;
399+
400+
current_addr += block->size;
401+
} else {
402+
if ((block->size & 0x3) == 0 && // size should be aligned to 4 bytes
403+
block->size > 0) {
404+
405+
serial_printff("POSSIBLE CORRUPTED BLOCK at %x: size=%d, magix=%x "
406+
"(expected %x)",
407+
current_addr, block->size, block->magix,
408+
(uint16_t)magichole);
409+
410+
hexdump((const char *)current_addr - 16, 64, 16);
411+
corrupted_blocks++;
412+
413+
current_addr += block->size;
414+
} else {
415+
current_addr += 4;
416+
}
417+
}
418+
} else {
419+
current_addr += 4;
420+
}
421+
}
422+
423+
serial_printff("heap watchdog stats: %d valid blocks (%d bytes), %d "
424+
"corrupted blocks, %d free holes (%d bytes)",
425+
valid_blocks, allocated_memory, corrupted_blocks,
426+
free_chunks, free_memory);
427+
428+
if (free_list_corrupted) {
429+
serial_printff("WARNING: Free list integrity check failed!");
430+
}
431+
432+
serial_printff("heap summary from xlists:");
433+
xsummary();
434+
435+
for (volatile int i = 0; i < 5000000; i++) {
436+
asm volatile("nop");
437+
}
438+
}
439+
}

libc/mem.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,23 @@ void *memcpy(void *dest, const void *src, size_t num_bytes) {
77
uint8_t *d = (uint8_t *)dest;
88
const uint8_t *s = (const uint8_t *)src;
99

10+
// align destination to 4 bytes if possible
1011
while (((uintptr_t)d % 4) && num_bytes) {
1112
*d++ = *s++;
1213
num_bytes--;
1314
}
1415

15-
while (num_bytes >= 4) {
16-
*((uint32_t *)d) = *((uint32_t *)s);
17-
d += 4;
18-
s += 4;
19-
num_bytes -= 4;
16+
// use 4-byte copies if both pointers are aligned
17+
if (((uintptr_t)s % 4) == 0) {
18+
while (num_bytes >= 4) {
19+
*((uint32_t *)d) = *((uint32_t *)s);
20+
d += 4;
21+
s += 4;
22+
num_bytes -= 4;
23+
}
2024
}
2125

26+
// byte-by-byte for remaining or unaligned portions
2227
while (num_bytes--) {
2328
*d++ = *s++;
2429
}

libc/page.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ static uint32_t first_frame() { // free frame
4747
{
4848
// at least one bit is free
4949
for (j = 0; j < 32; j++) {
50-
uint32_t toTest = 0x1 << j;
50+
uint32_t toTest = 0x1 << j; // TODO shift out of bounds
5151
if (!(frames[i] & toTest)) {
5252
return (i * 4 * 8 + j);
5353
}

0 commit comments

Comments
 (0)