Reading — step 1 of 5
Read
User Mode & Privilege Rings
The very first lesson of this course drew the line: ring 0 is the kernel, ring 3 is everything else, and the CPU enforces the border in hardware. You've since built the machinery that lives behind that border — allocators, the scheduler, page tables. Now it's time to understand the border itself as a set of rules the CPU checks on every privileged action, because a kernel that gets these rules wrong doesn't crash — it silently lets user code do kernel things, which is worse.
The one number that matters: CPL
The Current Privilege Level lives in the low 2 bits of the CS register — 0, 1, 2, or 3 — and it is your authority. The GDT lesson noted this in passing; here's its full weight: every privileged operation compares CPL against the privilege stamped on the thing you're touching, and lower number = more power (ring 0 outranks ring 3). Get the direction backwards and every check inverts; say it out loud until it sticks — smaller is stronger.
Three checks cover the border, and your exercise implements all three.
Check 1: crossing IN (interrupts/syscalls) — the gate's DPL
User code cannot jump to ring 0. It can only request a crossing through a gate the kernel installed (the IDT lesson's interrupt/trap gates). Each gate carries a DPL — the minimum privilege allowed to invoke it deliberately with int n:
allowed ⟺ gate_DPL >= CPL
Read it as: "the gate is open to you if it's set to your level or a less-privileged one." A syscall gate is DPL=3 (user may invoke it — the whole point). A gate meant only for hardware or kernel use is DPL=0; user int on it faults with #GP (general protection). Crucially, this DPL check is only for software int — a hardware interrupt bypasses it entirely (the timer must fire regardless of what ring is running). That asymmetry is why the same IDT serves both, gated differently.
Check 2: crossing OUT (iret) — you can only descend
iret returns from a handler to some privilege level. The rule is a one-way valve:
allowed ⟺ target >= CPL
From ring 0 you may iret to ring 0, 1, 2, or 3 — descending into a less-privileged program is how the kernel launches user code (build the stack frame, iret "into" ring 3). But from ring 3 you may only iret to ring 3. There is no instruction that raises your own privilege — the only way up is through a gate the kernel controls (Check 1). This valve is the mathematical heart of isolation: privilege flows down freely and up never, except through kernel-chosen doors.
Check 3: touching data — max(CPL, RPL) vs DPL
Loading a data segment adds a wrinkle: selectors carry their own Requested Privilege Level (RPL), and the effective privilege for the access is the weaker of CPL and RPL:
allowed ⟺ max(CPL, RPL) <= seg_DPL
Why max (the less-privileged of the two)? Because of the attack it stops. A user program hands the kernel a pointer (a system-call argument) with RPL=3. The kernel runs at CPL=0 and could, naively, access anything. But if the kernel loads that user selector, the RPL drags the effective privilege down to 3 — so the kernel accessing kernel data (DPL=0) through a user-supplied selector faults, exactly as if the user had tried it directly. This is the CPU helping the kernel not get tricked into abusing its own power on a user's behalf — the hardware half of the "validate every user pointer" discipline the System Calls lesson preaches. (In real code the kernel uses the ARPL/access-check instructions to enforce this deliberately.)
Why this lesson comes before System Calls
You can't understand why a syscall is a gate, why it can't be a function call, or why the kernel distrusts every pointer a syscall hands it, without these three checks. They're the physics; the syscall is an application of them. Build the checker, and the next lesson's mechanics — syscall/sysret, the entry stub, argument validation — read as consequences instead of rituals.
Your exercise: User-Mode Privilege Checks
Implement the three rules — INT (gate DPL ≥ CPL), IRET (target ≥ CPL), DATA (max(CPL,RPL) ≤ seg DPL) — emitting OK or GP per line. The graded traps are exactly the conceptual ones: the direction of the inequality (smaller ring = more privilege, so the comparisons feel backwards until they don't), the iret one-way valve (upward returns are GP), and the RPL max in data access (the case where a kernel action gets a GP because it used a user selector — the security check that surprises everyone). Three inequalities, and you've encoded the border every operating system is built to defend.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…