Setuid Binaries

Setuid Binaries # ls -la /bin/su -rwsr-xr-x 1 root root 67816 Aug 1 20:00 /bin/su Types of permissions and actions 1. The Three Types of Users (Who) Linux divides the entire universe of users into three distinct categories. Permissions are always listed in this exact order: Owner (User): The specific person who owns the file (usually the creator). Group: A defined collection of users who share access (e.g., a “developers” or “finance” group). Others: Everyone else on the system who is not the owner and not in the group. The owner and group are stored in inode. ...

August 2, 2026 · 5 min

Kernel Lab 2: Observing the Page Cache

Kernel Lab 2: Observing the Page Cache The Program The below program opens a file via mmap and performs read/write operations via commands. It also shows page faults, resident pages via mincore, etc. // page_cache_lab.c #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include <sys/resource.h> static void print_help(void) { printf("\nCommands:\n"); printf(" help Show commands\n"); printf(" info Mapping information\n"); printf(" read <page> Read first byte of page\n"); printf(" write <page> <char> Fill entire page with character\n"); printf(" dump <page> Dump first 64 bytes\n"); printf(" msync Flush dirty pages\n"); printf(" dontneed madvise(MADV_DONTNEED)\n"); printf(" pause Wait for ENTER\n"); printf(" mincore Show all pages compact (R=resident .=not)\n"); printf(" mincore <page> Check if specific page is resident\n"); printf(" quit\n\n"); } // compact: prints [R....R..R] style, 64 pages per line static void print_mincore_compact(char *region, size_t filesize, long pagesize) { size_t npages = (filesize + pagesize - 1) / pagesize; unsigned char *vec = calloc(npages, 1); if (!vec) { perror("calloc"); return; } if (mincore(region, filesize, vec) != 0) { perror("mincore"); free(vec); return; } printf("\nPage residency (%zu pages, R=resident .=not):\n\n", npages); for (size_t i = 0; i < npages; i++) { if (i % 64 == 0) printf("%5zu: ", i); putchar((vec[i] & 1) ? 'R' : '.'); if ((i + 1) % 64 == 0 || i == npages - 1) putchar('\n'); } // print resident page numbers explicitly printf("\nResident pages: "); int any = 0; for (size_t i = 0; i < npages; i++) { if (vec[i] & 1) { printf("R(%zu) ", i); any = 1; } } if (!any) printf("none"); printf("\n\n"); free(vec); } // single page check static void print_mincore_page(char *region, size_t filesize, long pagesize, size_t page) { size_t npages = (filesize + pagesize - 1) / pagesize; if (page >= npages) { printf("Page %zu out of range (max %zu)\n", page, npages - 1); return; } unsigned char *vec = calloc(npages, 1); if (!vec) { perror("calloc"); return; } if (mincore(region, filesize, vec) != 0) { perror("mincore"); free(vec); return; } printf("Page %zu: %s\n", page, (vec[page] & 1) ? "resident" : "not resident"); free(vec); } typedef struct { unsigned long minflt; unsigned long majflt; char vmrss[64]; char rssanon[64]; char rssfile[64]; char rssshmem[64]; } ProcStats; static void get_proc_stats(ProcStats *s) { memset(s, 0, sizeof(*s)); /* ---------- /proc/self/stat ---------- */ FILE *fp = fopen("/proc/self/stat", "r"); if (fp) { char buf[4096]; if (fgets(buf, sizeof(buf), fp)) { /* Skip "pid (comm)" because comm may contain spaces */ char *p = strrchr(buf, ')'); if (p) { unsigned long values[64] = {0}; int n = 0; char *tok = strtok(p + 2, " "); while (tok && n < 64) { values[n++] = strtoul(tok, NULL, 10); tok = strtok(NULL, " "); } /* * After ')' the fields begin with: * * 0 state * 1 ppid * ... * 7 flags * 8 minflt * 10 majflt */ if (n > 10) { s->minflt = values[7]; s->majflt = values[9]; } } } fclose(fp); } /* ---------- /proc/self/status ---------- */ fp = fopen("/proc/self/status", "r"); if (fp) { char line[256]; while (fgets(line, sizeof(line), fp)) { sscanf(line, "VmRSS: %63[^\n]", s->vmrss); sscanf(line, "RssAnon: %63[^\n]", s->rssanon); sscanf(line, "RssFile: %63[^\n]", s->rssfile); sscanf(line, "RssShmem: %63[^\n]", s->rssshmem); } fclose(fp); } } static void print_stats(void) { static unsigned long last_min = 0; static unsigned long last_maj = 0; ProcStats s; get_proc_stats(&s); printf("\n---------------------------------------\n"); printf("Minor Faults : %-8lu (%+ld)\n", s.minflt, (long)s.minflt - (long)last_min); printf("Major Faults : %-8lu (%+ld)\n", s.majflt, (long)s.majflt - (long)last_maj); printf("---------------------------------------\n\n"); last_min = s.minflt; last_maj = s.majflt; } struct rusage ru; static long last_minflt = 0; static long last_majflt = 0; int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "Usage: %s <file>\n", argv[0]); return 1; } const char *filename = argv[1]; int fd = open(filename, O_RDWR); if (fd < 0) { perror("open"); return 1; } struct stat st; if (fstat(fd, &st) != 0) { perror("fstat"); return 1; } size_t filesize = st.st_size; long pagesize = sysconf(_SC_PAGESIZE); if (filesize == 0) { fprintf(stderr, "File is empty.\n"); return 1; } char *region = mmap(NULL, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (region == MAP_FAILED) { perror("mmap"); return 1; } printf("\n=====================================\n"); printf("Page Cache Lab\n"); printf("=====================================\n"); printf("PID : %d\n", getpid()); printf("File : %s\n", filename); printf("Size : %zu bytes\n", filesize); printf("Page Size : %ld\n", pagesize); printf("Pages : %zu\n", filesize / pagesize); printf("Mapping : %p\n", region); print_help(); char line[256]; while (1) { printf("pagecache> "); fflush(stdout); if (!fgets(line, sizeof(line), stdin)) break; if (strncmp(line, "help", 4) == 0) { print_help(); } else if (strncmp(line, "info", 4) == 0) { printf("\n"); printf("PID : %d\n", getpid()); printf("Mapping : %p\n", region); printf("Size : %zu bytes\n", filesize); printf("Pages : %zu\n", filesize / pagesize); printf("\n"); } else if (strncmp(line, "read", 4) == 0) { size_t page; if (sscanf(line, "read %zu", &page) != 1) { printf("Usage: read <page>\n"); continue; } size_t offset = page * pagesize; if (offset >= filesize) { printf("Out of range\n"); continue; } volatile char c = region[offset]; printf("Read page %zu : '%c' (0x%02x)\n", page, (c >= 32 && c <= 126) ? c : '.', (unsigned char)c); } else if (strncmp(line, "write", 5) == 0) { size_t page; char ch; if (sscanf(line, "write %zu %c", &page, &ch) != 2) { printf("Usage: write <page> <char>\n"); continue; } size_t offset = page * pagesize; if (offset >= filesize) { printf("Out of range\n"); continue; } size_t remaining = filesize - offset; size_t len = remaining < (size_t)pagesize ? remaining : (size_t)pagesize; memset(region + offset, ch, len); printf("Filled page %zu with '%c'\n", page, ch); } else if (strncmp(line, "dump", 4) == 0) { size_t page; if (sscanf(line, "dump %zu", &page) != 1) { printf("Usage: dump <page>\n"); continue; } size_t offset = page * pagesize; if (offset >= filesize) { printf("Out of range\n"); continue; } printf("\n"); for (int i = 0; i < 64; i++) { if (offset + i >= filesize) break; unsigned char c = region[offset + i]; if (c >= 32 && c <= 126) putchar(c); else putchar('.'); } printf("\n\n"); } else if (strncmp(line, "msync", 5) == 0) { if (msync(region, filesize, MS_SYNC) != 0) perror("msync"); else printf("Pages flushed.\n"); } else if (strncmp(line, "dontneed", 8) == 0) { if (madvise(region, filesize, MADV_DONTNEED) != 0) perror("madvise"); else printf("MADV_DONTNEED completed.\n"); } else if (strncmp(line, "stats", 5) == 0) { print_stats(); continue; } else if (strncmp(line, "mincore", 7) == 0) { size_t page; if (sscanf(line, "mincore %zu", &page) == 1) { // single page query print_mincore_page(region, filesize, pagesize, page); } else { // compact full view print_mincore_compact(region, filesize, pagesize); } continue; } else if (strncmp(line, "pause", 5) == 0) { printf("Press ENTER..."); getchar(); } else if (strncmp(line, "quit", 4) == 0) { break; } else { printf("Unknown command.\n"); } print_stats(); } munmap(region, filesize); close(fd); return 0; } For the setup, i had to make some changes for this to work. ...

July 25, 2026 · 46 min

Kernel Lab 1: Observing Process Structs

Kernel Lab 1: Observing Process and VFS Structs Primer on Kernel Data Structures 1. Process & Memory Management (MM) Struct Description Noteworthy Fields task_struct The kernel’s representation of a thread/process. mm (memory space), files (open files), fs (cwd/root), pid, comm (name). mm_struct The descriptor for an entire memory address space. mm_mt (the Maple Tree of VMAs), pgd (page global directory for hardware MMU). vm_area_struct A contiguous range of virtual memory with shared permissions (a VMA). vm_start, vm_end, vm_file (if it’s a memory-mapped file), vm_flags. 2. Virtual File System (VFS) The VFS bridges the gap between user-space file operations (like open() and read()) and the underlying hardware. ...

July 16, 2026 · 45 min

The Pipes

The Pipes File Descriptors To understand file descriptors (FDs), you have to look at one of the core design philosophies of Unix and Linux: “Everything is a file.” Whether a process is interacting with a regular text file, a directory, a hardware device (like a hard drive), a network socket, or an in-memory pipe, the kernel represents all of them as “files.” A file descriptor is simply the lightweight handle that user-space processes use to talk to these files. Here is the low-level architectural breakdown of how they work. ...

July 8, 2026 · 19 min

The Page Cache and Page Writeback

The Linux Page Cache & Page Writeback The Core Concept: Disk access is measured in milliseconds; RAM access is measured in nanoseconds. To bridge this massive performance gap, Linux dynamically uses free physical RAM to cache blocks of disk data. This relies on temporal locality: the computing principle that if data is accessed once, it is highly likely to be accessed again very soon. 1. Reading from Disk The cache is granular; Linux caches specific pages of files based on what you actually access, not whole files by default. ...

July 5, 2026 · 25 min

The Page Tables

Modern Linux Page Tables (4-Level Architecture) Modern 64-bit processors require much larger address spaces, and the Linux kernel adapted by shifting to a 4-level (and more recently, a 5-level) page table architecture. The Core Concept: Because a 64-bit address space is astronomically large and mostly empty, the kernel cannot use a single, massive translation array. Instead, it uses a hierarchical, multi-level tree of tables to map Virtual Addresses to Physical Addresses efficiently. ...

July 2, 2026 · 18 min

The Process Address Space

The Process Address Space — Intro Same flat (single contiguous range) address space model you already have from the article — nothing new conceptually. Key term to lock in: a memory area (this book’s name for what the article calls a VMA) is a permission-tagged interval within that address space. Access outside any valid area, or against an area’s permissions (write to read-only, execute non-executable) → segfault. The list of “what memory areas contain” is just a slightly different cut of the same segments from the article: text, data, bss, stack, shared library mappings, mmap’d files, shared memory, anonymous mappings (malloc). All non-overlapping — every valid address belongs to exactly one area. ...

June 26, 2026 · 32 min

The Virtual Filesystem

The Virtual Filesystem What is a Filesystem? Imagine a bare hard drive as a massive, empty warehouse. You can throw billions of bytes of data in there, but without a system, you will never find anything again. A filesystem is the specific set of rules, data structures, and “ledgers” (like the Inodes and Superblocks we discussed) used to organize, index, and retrieve that data. It dictates how large a file can be, how folders are nested, and how permissions are handled. ...

June 20, 2026 · 27 min

Memory Management in Kernel

Memory Management in Kernel Why Kernel Space Memory Management is Harder? Userspace can fail safely and wait patiently. Kernel-space cannot, making its allocation fundamentally harder. Key Differences in Kernel Memory Allocation Sleeping is often banned: Userspace can block while waiting for memory. Kernel contexts (interrupts, spinlocks) cannot, requiring instant success or failure flags like GFP_ATOMIC. Failure is catastrophic: App failures just kill the app; kernel allocation failures crash the entire system. The kernel must rely on emergency reserves, reclaim, and the OOM killer to survive. Dangerous recursion: The kernel manages memory using memory. A reclaim operation can trigger filesystem actions that need more memory, causing deadlocks (prevented by flags like GFP_NOFS). Strict physical constraints: Userspace only worries about virtual memory. The kernel must manage physical pages, DMA limits, and NUMA locality. Physical fragmentation matters: The kernel frequently requires physically contiguous pages, making fragmentation a critical, system-halting roadblock. Interrupt context is brutal: Interrupt handlers need immediate memory without sleeping or waiting on locks, relying heavily on per-CPU caches and lockless structures. High deadlock risk: Allocators interact with reclaim, writebacks, and system locks. GFP flags are essential to dictate exactly what an allocator is safely allowed to do. Predictable latency is required: Kernel subsystems (networking, real-time workloads) cannot tolerate unpredictable allocation pauses, necessitating highly optimized allocators like SLAB/SLUB. The Mental Shift ...

May 19, 2026 · 31 min

Rust Notes — Module 6

Rust Notes — Module 6: Error Handling 1. The Philosophy Language Error Mechanism Problem C Return codes (-1, NULL, errno) Easy to ignore, no enforcement Go (value, error) tuples Better, but still ignorable Rust Result<T, E> in the type system Impossible to ignore — compiler enforced If a function can fail, its return type says so. You cannot use the success value without handling the error case first. No hidden exceptions, no surprise crashes from ignored error codes. ...

March 29, 2026 · 7 min