Blog

Sep 2026 · 12 min read

BLOG

RustOSDevSystems

I Built an OS in Rust That Can Browse the Web — Here's What Broke Along the Way

CottonOS started as 'print text to VGA' and ended with TLS 1.3 running inside my own kernel. The bugs in between taught me more than any course.

RustOSDevSystems

CottonOS began with a simple, slightly arrogant question: what actually happens between pressing the power button and seeing a desktop? The honest way to find out is to build the whole path yourself. So I did — a 64-bit kernel in Rust, booted by GRUB through a small NASM stub that walks the CPU into long mode and jumps into _start64. Everything after that jump is mine: the frame allocator, the paging code, the scheduler, the filesystem, the drivers, the window manager, and eventually a web browser that loads real sites over HTTPS with TLS 1.3 running inside the kernel.

The architecture is a straightforward layer cake. At the bottom, the x86_64 essentials: GDT, IDT, the PIC and PIT, port I/O and serial output for debugging. Above that, memory management — a bitmap frame allocator, four-level paging, and a linked-list heap. Then processes and a preemptive round-robin scheduler ticking at 1000 Hz, a VFS with my own persistent filesystem (CottonFS) on an ATA disk, drivers for keyboard, mouse and an RTL8139 network card, and finally the GUI: a back-buffered framebuffer desktop with draggable windows, a terminal, a file manager, an editor and the browser.

The first networking stack was hand-rolled TCP, and it behaved exactly like hand-rolled TCP: no retransmission, no reordering, and a tendency to fall over after two requests. I replaced it with smoltcp, a proper TCP/IP implementation in Rust designed for bare-metal use, and my driver shrank to an implementation of smoltcp's Device trait that hands frames upward. If you are building an OS and tempted to write TCP yourself: do it once for the scar tissue, then use smoltcp.

The bug I will tell interviewers about for years was not in the network code at all. The kernel heap lives in an identity-mapped region — but the physical frame allocator did not know that, so it cheerfully handed out heap memory as DMA buffers for the network card. The GUI's back buffer landed on top of the NIC's receive ring. The result: every time the desktop repainted, the wallpaper overwrote incoming packets. I spent days auditing checksums and ring-buffer indices before noticing that the 'corrupted' packets were the same four bytes repeating — my background colour. If your driver receives garbage that looks suspiciously regular, go and ask your allocator what else it has been giving away.

There is plenty CottonOS does not do, and the README says so plainly: the browser trusts any TLS certificate, everything still runs in ring 0, one TCP connection at a time, no CSS or JavaScript. I consider the honest-limitations section the most professional part of the project. Every real system has a list like this; the difference is whether the author admits it.

What the project actually gave me is a working intuition for the invisible machinery under every program I write — what a page fault costs, why DMA and caches must be negotiated with, what a scheduler tick does to latency. That intuition transfers to web backends, to browsers, to everything. The code is on GitHub, limitations and all, if you want to read or run it in QEMU.

I Built an OS in Rust That Can Browse the Web — Here's What Broke Along the Way