§6.1–6.2Basic Technique: LDE … Problem #1: Restricted Operations

Part I OSTEP pp. 49–54 · ~9 min read

  • limited direct execution
  • trap table
  • trap handler
  • kernel stack
  • system call number
  • exception

Chapter 4 promised mechanisms; here is the big one. To time-share the CPU the OS must run one process a while, then another — while solving two problems at once: performance (don’t make everything slow) and control (don’t lose the machine to a runaway or malicious process). Without control, a process could run forever or read anything it likes; the OS would be, as the book puts it, “just a library.”

The Crux: How To Efficiently Virtualize The CPU With Control

The OS must virtualize the CPU efficiently while retaining control over the system. Both hardware and OS support will be required — the OS often uses a judicious bit of hardware help to do its work effectively.

6.1 Basic Technique: Limited Direct Execution

The “direct execution” half of limited direct execution is simple: just run the program on the CPU. Create a process-list entry, allocate memory, load the code, set up the stack, call main(). Step through the unlimited version and watch for the moment things go wrong:

Figure 6.1: Direct Execution Protocol (Without Limits)
OS
Program
step 1 / 10 · time flows downward

The OS does its bookkeeping first — a new process-list entry (chapter 4's machinery).

Fast? Absolutely — the program runs natively. But two questions have no answer in that protocol: (1) how does the OS make sure the program doesn’t do anything it shouldn’t, while still running efficiently, and (2) how does the OS ever stop it and switch to another process? The “limited” half of the name is the answer to both.

6.2 Problem #1: Restricted Operations

The Crux: How To Perform Restricted Operations

A process must be able to do I/O and other restricted operations — without being given complete control over the system. How can hardware and OS work together?

Letting any process issue raw I/O would destroy protection: a file system that checks permissions is meaningless if any process can read or write the whole disk directly. So the hardware provides modes: in user mode, a process attempting a restricted operation (like an I/O request) causes the processor to raise an exception — and the OS likely kills it (“one strike and you’re out”). The kernel, in kernel mode, may do anything.

For legitimate privileged needs, there’s the controlled doorway you met in chapter 2: the system call (pioneered on the Atlas machine; POSIX systems export a few hundred, early UNIX about twenty). The trap instruction jumps into the kernel and raises privilege in one step; the OS finishes with return-from-trap, which does the reverse. Along the way, the hardware saves the caller’s registers — on x86, PC, flags and more are pushed onto the process’s per-process kernel stack — so return-from-trap can pop them and resume exactly where the program left off.

Aside: Why system calls look like procedure calls

Because they are! Calling open() is a procedure call into the C library — but hidden inside is the trap instruction. The library puts the arguments and the system-call number in agreed-upon places (stack or registers), executes the trap, then unpacks return values after it. Those parts of libc are hand-written assembly; someone wrote it decades ago so you don’t have to.

One crucial detail remains: where does the trap jump? The calling process can’t be allowed to name a kernel address — jumping just past a permission check would be, in the book’s words, a Very Bad Idea. Instead:

user codesyscall # → registertraptrap tableinstalled at boot (privileged)#0 → read handler#1 → write handler#2 → open handler …kernel codevalidate number + args,then do the workonly entry points the OS chose✗ “jump to address 0xk3rn3l” — never allowed: user code names a service, never an address

The indirection that keeps traps safe: at boot (in kernel mode) the OS installs the trap table ; forever after, a trap consults it and only ever lands on a registered trap handler . User code supplies a system-call number — a request, not a destination.

The kernel checks that the number is valid and runs the corresponding code — indirection as protection. And because installing the trap table is itself privileged, a user-mode attempt earns the usual reward (adios, offending program). Point to ponder, from the book: what horrible things could you do if you could install your own trap table?

Tip: Be wary of user inputs in secure systems

The trapping mechanism protects entry into the kernel — but the kernel must also distrust what comes through the door. A write() passes a buffer address: if it points into kernel memory and the OS doesn’t check, a user could read all of kernel memory (which typically maps all physical memory — every process’s secrets). Treat user inputs with great suspicion, or enjoy easily-hacked software and diminished job security.

Now the whole protocol, both phases, with every privileged step badged. This is the book’s most important table — worth stepping slowly:

Figure 6.2: Limited Direct Execution Protocol — bold-badged steps are privileged
OS(kernel mode)
Hardware
Program(user mode)
Phase 1 — at boot
Phase 2 — running a process
step 1 / 22 · time flows downward

The machine boots in kernel mode, so the OS is free to configure the hardware: it registers where the syscall handler (and other handlers) live. Telling the hardware where trap tables are is itself privileged — imagine if a user program could install its own!

Two phases: at boot, the kernel initializes the trap table and the CPU remembers where the handlers live. At run, the kernel sets up the process, launches it with a return-from-trap (a lovely trick: it fills the kernel stack so that “returning” lands at main() in user mode), the process traps in for services and gets return-from-trapped back out, and finally exits via a last trap. Efficient — the program mostly runs directly — yet the OS never loses control of what the program may do.

Except… one thing is still missing. What if the program simply never traps — an infinite loop, no system calls? Nothing in this protocol lets the OS take the CPU back. That’s Problem #2.

Check yourself

1.What are the TWO problems with pure (unlimited) direct execution?

2.A user-mode process tries to issue a raw disk I/O instruction. What happens?

3.Why must user code supply a system-call NUMBER instead of the address of the kernel code to run?

4.In Figure 6.2, the OS starts a brand-new process by executing… return-from-trap?! Why does that work?

5.After all of section 6.2's machinery (modes, traps, trap table), what can the OS still NOT handle?

5 questions