class: center, middle ### Secure Computer Architecture and Systems *** # Operating Systems:
Basic Practical Aspects --- # Introduction - Here we cover: - An extremely simplified model of how an OS runs on the hardware (simplified too) - What an OS expects from the hardware (CPU) - The security properties an OS needs to enforce and how it does so - Here we focus on **CPU** and **memory** - We'll see how the OS interfaces with devices (**I/O**) later --- # Basic OS Principles .leftcol[ - A computer is made of **hardware**: - CPU, memory, and I/O (disk and network) - Software runs on top of that hardware, and **the OS manipulates the hardware** directly - **Applications** run on top of the OS - Can't let them access most of the hardware directly for safety/stability ] .rightcol[
] --- # Basic OS Principles (2) .leftcol[ - OS provide **standardised abstractions** for applications to use the hardware safely - E.g. processes and threads for CPU/memory, filesystem for storage, sockets for network, etc. - These abstractions are accessed through a standard API: **system calls** - On Linux: `open`, `read`, `write`, `mmap`, etc. ] .rightcol[
] --- # Boot Time Basic steps (ultra simplified): 1. Power on 2. Motherboard firmware (BIOS) performs basic hardware initialisation and then runs the boot loader 3. Boot loader (e.g. Grub) takes over 4. OS kernel is loaded by the boot loader, starts to run 5. OS initialises more hardware and itself 6. Once ready OS can run applications
--- name: cpu # Execution Model on the CPU - CPU: ALU + control logic + registers (amongst other things) - **Instruction pointer** (AKA program counter): register pointing to the instruction being executed in the code segment - Load/store instructions to read/write data from/to memory --- template: cpu
--- template: cpu
--- template: cpu
--- template: cpu
--- template: cpu
--- template: cpu
--- # Execution Model on the CPU - The content of the registers define the application's state on the CPU - It can be saved and restored to/from memory e.g. for a context switch
--- # Kernel Invocation When does the kernel get invoked, i.e. **when does the CPU starts executing kernel code?** -- Only on 2 occasions: - At boot time after the boot loader finishes to load the kernel - At runtime when an **interrupt** is received by the processor -- There are 2 types of interrupts: - **Hardware interrupts** for I/O: a device sends an interrupt to the CPU - **Software exceptions** (e.g. division by zero): the CPU interrupts itself --- # Kernel Invocation
--- # Kernel Invocation
--- # Kernel Invocation
--- # Kernel Invocation
--- # Kernel Invocation
--- # Security - **We cannot have an application read/write the memory of another application or of the kernel**
--- name: security # Security - OS sets up MMU to perform virtual to physical address mapping - An app is tricked into thinking it has access to 100% of the memory - It cannot address the memory belonging to other apps/the kernel --- template: security
--- template: security
--- template: security
--- # System Calls - **If the application cannot access kernel data/run kernel code for security reasons, how can it invoke OS services?** -- - When invoking a system call, the application executes a special instruction that triggers an exception and the CPU starts running the kernel to manage the system call
--- # System Call ABI - Programmer generally does not invoke system calls directly -- - In a C program, call the functions `read()`, `write()`, etc. implemented by the Libc - Libc is compiled with the app, exposes a language/source-level interface, an **Application Programming Interface (API)** -- - Libc needs to invoke the OS (not compiled together with applications) - It is also written in a different language vs. many applications - Cannot use an API for system calls: we need an **Application Binary Interface (ABI)** - Machine language-level, convention stating how to invoke a system call: - What to put in what registers - What machine instruction(s) to use --- # System Call ABI - Linux uses the **System V ABI**, which state that for x86-64 a system call is invoked by an application as follows: 1. Place arguments in order in `%rdi`, `%rsi`, `%rdx`, `%r10`, `%r8` and `%r9` 2. Place system call id (unique integer identifier) in `%rax` 3. Invoke `syscall` instruction, trigger the exception (trap to the kernel) 4. Upon return to user space, kernel places return value in `%rax` -- Disassembled Libc wrapper for `clock_gettime` (2 arguments): ```asm 00000000004672b0 <__clock_gettime>: #... 4672f8: mov %r12,%rsi 4672fb: mov %ebp,%edi 4672fd: mov $0xe4,%eax * 467302: syscall # ... ``` --- # Manual Syscall Invocation .leftcol[ ```asm .global _start .text _start: # write(1, message, 14) mov $1, %rax mov $1, %rdi mov $message, %rsi mov $14, %rdx syscall # exit(0) mov $60, %rax xor %rdi, %rdi syscall message: .ascii "Hello, world!\n" # ``` .codelink[
`13-os-basic-practical-aspects/syscall.s`
] ] .rightcol[ Assemble, link, and run as follows: ```bash $ as syscall.s -o syscall.o $ ld syscall.o -o syscall $ ./syscall Hello, world! ``` ] --- # Privilege Modes - **Some CPU instructions are privileged**, they should only be executed by the kernel: - Installing a new page table - Communicating with the hardware - Shutting down/resetting the CPU - Etc. -- - **Privilege modes** ensure applications cannot invoke them. - At any time the processor is in one of two modes: - **User mode** when applications run - **Supervisor mode** (AKA kernel mode) when the kernel runs -- - A privileged instruction: - Is successfully executed when invoked in kernel mode - Triggers an exception and traps to the kernel when invoked in user mode --- # Privilege Modes Privilege modes have various names according to the architecture, on x86 they are called **protection rings**, e.g. for x86-32:
Rings 1 and 2 were dropped with x86-64, leaving ring 0 (supervisor mode) and ring 3 (user mode) --- # User/Kernel World Switches - On x86-64 when the `syscall` instruction is invoked: - The CPU switches from user to supervisor mode - It then jumps to a predefined piece of code: **system call handler** (CFI) - Kernel starts to run and handles the syscall - Once done, kernel executes the `sysret` instruction - Switches to user mode - Jumps to the next instruction after `syscall`
- World switches are **costly**: hundreds, up to thousands of cycles --- # Wrapping Up - OS abstracts hardware from applications for ease and **security reasons**: - Applications cannot access each other's memory or the kernel's memory - Applications cannot execute privilege operations e.g. shutting down the computer, installing new memory mappings, etc. - Achieved with help from the hardware: **MMU (virtual memory), privilege levels** - Only way for an application to invoke kernel services is through a safe and controlled interface: **system calls**