# From npm install to insmod: My Journey into Linux Kernel Module
- **URL:** https://fauzanfebrian.my.id/forge/from-npm-install-to-insmod-my-journey-into-linux-kernel-module
- **Type:** LEARNING LOG
- **Date:** 2025-12-23
- **Synopsis:** I moved from Node.js to Linux Kernel Module. A look at how the Kernel is just an Express.js app, and how to build a self-destructing kernel module.
- **Tags:** char device driver, linux, kernel
- **Demo:** https://github.com/fauzanfebrian/linux-kernel-hacking/blob/main/docs/lab-logs/2025-12-20-void-box.md
---# From npm install to insmod: My Journey into Linux Kernel Module

For a long time, I treated the OS as a black box. I knew how to write APIs in NestJS, but I had no idea how the server actually listened to a port. That changed when I joined the [CusDeb Operating System Camp](https://instagram.com/cusdeb_com).

The goal was simple: stop relying on high-level abstractions and understand the machine. I wanted to move from just being a user of the OS to an engineer who could build inside it.

Here is what I learned when I finally broke the "Black Box" and accepted a challenge from an AI to build a self-destructing kernel module.

## 1. The Kernel is just an Express.js App

When I first looked at a Character Device Driver, I was overwhelmed by structs and pointers. But then I realized: The Kernel is just a Server.

Coming from web development, I mapped the concepts, and suddenly, the "magic" disappeared:

- **The Port** (`alloc_chrdev_region`): You reserve a Major/Minor number. This is just like opening port 80. The Kernel needs to know where to route traffic.
- **The Server** (`cdev_init`): This is effectively app.listen(). It tells the Kernel to start accepting traffic for your specific Device ID.
- **The Controller** (`file_operations`): This is where your business logic lives. When a user runs cat /dev/mydevice, the Kernel routes it to your `.read` function.
- **The Response** (`copy_to_user`): You can't just return json. You have to manually copy bytes across the boundary to the client.

Once I treated the Kernel as an API, I stopped fearing the C code.

## 2. The Challenger: _Gemini 3.0_

The CusDeb Camp gave me the foundation, how to build the kernel and run User-Mode Linux (UML). But I didn't want to just copy-paste a "Hello World."

I asked my AI thought partner, **Gemini 3.0**, for a "Boss Fight." I told it: "Don't give me code. Give me a problem."

It gave me [The VoidBox](https://github.com/fauzanfebrian/linux-kernel-hacking/blob/main/docs/challenges/create-voidbox-module.md).

The Spec:

- Read-Once: A secure drop-box. Users write secrets in.
- Incinerate: When read, the data is returned to User Space and immediately zeroed out in Kernel Memory.
- Thread-Safe: Use `mutex` to prevent race conditions.

This wasn't a tutorial. This was a specification sheet.

## 3. The Holy Trinity of Kernel Hacking

Building the VoidBox forced me to confront three things I usually ignore in TypeScript.

### The Boundary (User vs. Kernel)

In JS, variables are just variables. In Kernel space, you never trust a pointer from User Space.

You cannot access the user's buffer directly. You must use `copy_from_user` and `copy_to_user`. If you try to dereference a user pointer directly, you might crash the system.

### Concurrency is Manual

In Node.js, we are spoiled by the single-threaded Event Loop. We rarely worry about two requests modifying a variable at the exact same nanosecond.

In the Kernel, that protection is gone. If two processes try to write to `/dev/voidbox` simultaneously, they will corrupt the memory.

I had to implement a mutex lock.

```C
mutex_lock(&void_device.lock);
// ... Critical Section ...
// Copy data, update length
mutex_unlock(&void_device.lock);
```

If I didn't lock it, the memory will be corrupted.

### The "Filesystem" is a Lie

`/dev/voidbox` looks like a file. You can `echo` into it. You can `cat` it. But it's not a file. It's a stream.

I learned the hard way that if you don't manually update the cursor position (`*ppos`), the `cat` command will read forever, looping your data infinitely until you kill the process.

## 4. The Logic: Self-Destructing Memory

The logic I wrote was ruthless.

**The Write**: I checked if the box was full. If it was, I returned -EBUSY. I threw an I/O Error on purpose because this is a secure box. No overwriting allowed.

**The Read**: I first checked whether any data existed. If it did, I copied the data to user space, then immediately incinerated it `void_device.data[0] = '\0';` and reset the stored length to 0, enforcing strict read-once semantics.

![VoidBox Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1n67rgazasy571gvb3sk.png)

The screenshot above shows the result. I wrote "NUS Msc..." into the void. I read it once. I tried to read it again, and it was gone. Total erasure.

## Conclusion

I am still a "wannabe" Systems Engineer. My Makefile is messy, and the pointers still scare me.

But the "Black Box" is open. I know that `insmod` isn't magic, it's just loading a library. I know that `cat` isn't just reading a file, it's triggering a driver function.

If you are a web developer, stop fearing the low level. Spin up a User-Mode Linux instance and break things. It's the only way to see how the machine actually works.


---
### [ SYSTEM DIRECTORY ]
- [Root](https://fauzanfebrian.my.id/)
- [Forge](https://fauzanfebrian.my.id/forge)
- [Codex](https://fauzanfebrian.my.id/codex)
- [Tools](https://fauzanfebrian.my.id/tools)
