# Linux Kernel Memory Allocation Engine with a Control Plane / Data Plane Architecture



# Building a Linux Kernel Memory Allocation Engine with a Control Plane / Data Plane Architecture

## Introduction

Most Linux kernel module tutorials demonstrate a simple memory allocation example using `kmalloc()` and `kfree()`.

While these examples are useful, they rarely discuss a broader systems-design question:

> Can memory allocation be modeled using the same architectural ideas that power modern distributed systems?

To explore this idea, I built a small Linux kernel project that separates **decision making** from **execution** using a **Control Plane / Data Plane** architecture.

The project combines:

- Linux Kernel Modules (LKM)
- Kernel Heap Memory Management
- Dynamic Allocation Tracking
- Module Parameters
- Dangling Pointer Analysis
- Control Plane / Data Plane Design
- F#-Driven Configuration Generation
- Foundations for Future DSL and Compiler Work

---

# Why Control Plane / Data Plane?

Modern infrastructure systems separate policy from execution.

Examples include:

| System | Control Plane | Data Plane |
|----------|----------|----------|
| Kubernetes | API Server | Pods |
| SDN | Network Controller | Switches |
| eBPF | User-Space Loader | Kernel Program |
| Service Mesh | Management Layer | Proxy Layer |
| This Project | F# Generator | Kernel Module |

Traditional kernel code:

```c
kmalloc(1024, GFP_KERNEL);
kmalloc(4096, GFP_KERNEL);
```

Control-plane driven approach:

```text
Allocation Intent
        ↓
Kernel Execution
```

The control plane decides **what should happen**.

The data plane performs **the actual work**.

---

# High-Level Architecture

```text
┌──────────────────────────────────────────┐
│              CONTROL PLANE               │
│                                          │
│  F# Generator                            │
│  Allocation Definitions                  │
│  Configuration Generation                │
└───────────────────┬──────────────────────┘
                    │
                    ▼
            alloc_plan.txt
                    │
                    ▼
┌──────────────────────────────────────────┐
│               DATA PLANE                 │
│                                          │
│ Linux Kernel Module                      │
│ kmalloc()                                │
│ Allocation Tracking                      │
│ Resource Management                      │
│ kfree()                                  │
└───────────────────┬──────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────┐
│               KERNEL HEAP                │
│                                          │
│ Actual Physical Allocation               │
└──────────────────────────────────────────┘
```

This separation creates a cleaner mental model and mirrors patterns found in large-scale systems.

---

# User-Space Input

Memory requests are provided using module parameters.

```bash
sudo insmod kmalloc_data_plane.ko \
names="buffer,big_buffer" \
values=1024,4096
```

Internally:

```text
names[]  = [buffer, big_buffer]
values[] = [1024, 4096]
count    = 2
```

These arrays become allocation instructions.

---

# Allocation Pipeline

During initialization:

```c
for (i = 0; i < count; i++)
{
    buffers[i] = kmalloc(values[i], GFP_KERNEL);
}
```

Conceptually:

```text
buffer      -> kmalloc(1024)
big_buffer  -> kmalloc(4096)
```

Each allocation produces a pointer to kernel memory.

---

# Memory Lifecycle

The entire project revolves around memory ownership and lifecycle management.

```text
Request Memory
      │
      ▼
kmalloc()
      │
      ▼
Kernel Heap
      │
      ▼
Store Pointer
      │
      ▼
Use Memory
      │
      ▼
kfree()
      │
      ▼
Memory Returned
```

Understanding this lifecycle is one of the most important skills in systems programming.

---

# Kernel Heap Visualization

After allocation:

```text
buffers[0]
      │
      ▼
0xA100
+----------------------+
| buffer               |
| 1024 bytes           |
+----------------------+

buffers[1]
      │
      ▼
0xB200
+----------------------+
| big_buffer           |
| 4096 bytes           |
+----------------------+
```

The addresses are returned by the kernel allocator and stored inside tracking tables.

---

# Allocation Tracking Layer

The kernel module maintains ownership information.

```c
void *buffers[MAX_BUFFERS];
size_t sizes[MAX_BUFFERS];
```

Runtime state:

```text
buffers[0] -> 0xA100
buffers[1] -> 0xB200

sizes[0] = 1024
sizes[1] = 4096
```

This effectively acts as a miniature memory manager.

---

# Why Use void*?

The tracking array is intentionally generic.

```c
void *buffers[MAX_BUFFERS];
```

A `void*` can point to any type of memory.

Examples:

| Memory Type | Example |
|------------|----------|
| Raw Bytes | Buffers |
| Structures | Kernel Objects |
| Strings | Character Arrays |
| Device Data | Driver Memory |
| Network Data | Packet Buffers |

This makes the tracking subsystem reusable.

---

# Understanding kmalloc()

Kernel memory allocation occurs through:

```c
void *ptr = kmalloc(1024, GFP_KERNEL);
```

Parameters:

| Parameter | Purpose |
|------------|------------|
| 1024 | Allocation Size |
| GFP_KERNEL | Standard Allocation Flag |

Unlike user-space `malloc()`, kernel allocations use GFP flags to control allocator behavior.

---

# Module Runtime State

After initialization:

```text
┌────────────────────────────────────┐
│ buffers[]                          │
├────────────────────────────────────┤
│ [0] -> 0xA100                      │
│ [1] -> 0xB200                      │
└────────────────────────────────────┘

┌────────────────────────────────────┐
│ sizes[]                            │
├────────────────────────────────────┤
│ [0] -> 1024                        │
│ [1] -> 4096                        │
└────────────────────────────────────┘
```

The module now owns these allocations.

---

# Cleanup Phase

When the module unloads:

```bash
sudo rmmod kmalloc_data_plane
```

Cleanup executes:

```c
kfree(buffers[i]);
```

Visualization:

```text
Before

0xA100 -> Allocated
0xB200 -> Allocated

After

0xA100 -> Freed
0xB200 -> Freed
```

Memory ownership returns to the kernel allocator.

---

# The Dangling Pointer Problem

Many developers incorrectly assume that a pointer disappears after `kfree()`.

Example:

```c
kfree(ptr);
```

Reality:

```text
Before Free

ptr
 │
 ▼
0xA100
+------------+
| Valid Data |
+------------+
```

After Free:

```text
ptr
 │
 ▼
0xA100

Memory Already Released
```

The pointer still exists.

The memory does not.

This situation is known as a **Dangling Pointer**.

---

# Why Dangling Pointers Matter

Accessing freed memory may cause:

| Problem | Impact |
|----------|----------|
| Garbage Reads | Corrupted Results |
| Use-After-Free | Security Vulnerability |
| Memory Corruption | Undefined Behavior |
| Kernel Panic | System Crash |

Many real-world kernel vulnerabilities originate from stale pointer usage.

---

# Defensive Programming

A common defensive pattern:

```c
kfree(ptr);
ptr = NULL;
```

Result:

```text
ptr -> NULL
```

The stale address is removed.

This significantly reduces accidental use-after-free bugs.

---

# From Control Plane to DSL

The current implementation uses a lightweight control plane.

Current flow:

```text
F# Generator
      ↓
Allocation Plan
      ↓
Kernel Module
      ↓
kmalloc()
```

However, the same idea can evolve into a complete compiler pipeline.

Future architecture:

```text
DSL
 ↓
Lexer
 ↓
Parser
 ↓
AST
 ↓
Semantic Analyzer
 ↓
Intermediate Representation
 ↓
Verification Passes
 ↓
Optimization Passes
 ↓
Code Generator
 ↓
Linux Kernel Module
```

At that point, the control plane becomes a true compiler.

---

# Control Plane vs Compiler vs Pure C

These approaches solve different problems.

| Approach | Goal |
|-----------|-----------|
| Pure C | Direct Kernel Development |
| Control Plane | Runtime Decisions |
| DSL Compiler | Automated Code Generation |

### Pure C

```text
Kernel Code
     ↓
Kernel Module
```

### Control Plane

```text
F#
 ↓
Configuration
 ↓
Kernel Module
```

### Compiler

```text
DSL
 ↓
IR
 ↓
Generated C
 ↓
Kernel Module
```

Each teaches a different layer of systems engineering.

---

# Build Workflow

Generate configuration:

```bash
dotnet fsi control_plane.fsx
cp alloc_plan.txt /tmp/
```

Build:

```bash
make clean
make
```

Sign module:

```bash
sudo /usr/src/linux-headers-$(uname -r)/scripts/sign-file \
sha256 \
~/kernel_keys/MOK.key \
~/kernel_keys/MOK.crt \
kmalloc_data_plane.ko
```

Load:

```bash
sudo insmod kmalloc_data_plane.ko \
names="buffer,big_buffer" \
values=1024,4096
```

Inspect logs:

```bash
dmesg | tail
```

Unload:

```bash
sudo rmmod kmalloc_data_plane
```

---

# Key Takeaways

- `kmalloc()` allocates memory from the kernel heap.
- `kfree()` releases memory ownership.
- Pointers survive after memory is freed.
- Dangling pointers are a major source of bugs.
- Control Plane / Data Plane separation improves architectural clarity.
- The same concepts can evolve into DSLs, IRs, and compiler pipelines.
- Memory ownership and lifetime management are core systems-programming skills.

---

# Conclusion

What started as a simple kernel memory allocation experiment evolved into a broader exploration of systems architecture.

The project demonstrates how concepts from distributed systems, compiler design, and kernel development can intersect in a surprisingly small codebase.

Whether you're interested in:

- Linux Kernel Development
- Memory Management
- Systems Programming
- DSL Design
- Compiler Construction
- Control Plane Architectures

the core lesson remains the same:

> Correct ownership and lifetime management are the foundation of reliable systems.

---


# Repository

GitHub Repository:

https://github.com/aj333git/linux\_kernel\_kmalloc\_f2

* * *


