Weekend trivia: your process' memory is a file

surprisetalk1 pts0 comments

Weekend trivia: your process' memory is a file

/mem"/>/mem"/>/mem"/>

lcamtuf’s thing

SubscribeSign in

Weekend trivia: your process' memory is a file<br>The underappreciated gem of /proc//mem<br>Jun 01, 2026

Share

Some folks say that the design philosophy of Unix is that “everything is a file”. If you’re familiar with Unix-like platforms, you probably know that they don’t quite live up to the hype. It’s true that these systems allow convenient access to hardware through file-like objects in directories such as /sys or /dev. At the same time, there’s plenty of OS functionality that isn’t exposed via files; for example, you can’t connect to a remote webserver without using a dedicated system call.<br>This is something people would probably have liked to do! A popular shell called bash comes with a workaround: it special-cases certain file paths, letting you construct the following shell monstrosity:<br>$ (echo -e 'GET / HTTP/1.0\nHost: coredump.cx\n' 1>&0; cat)

That said, the /dev/tcp// trick works only for the files opened by the shell itself. If you pass such a path to any other program, you won’t get the expected result:<br>$ cat /dev/tcp/coredump.cx/80<br>cat: /dev/tcp/coredump.cx/80: No such file or directory

If you complain about this issue, you might get corrected that not everything is a file; instead, “everything is a file descriptor”. That is, you might need to do something special to initiate a TCP/IP connection, but once this is done, the returned connection identifier has file-like semantics and can be passed to standard file APIs such as read(…) and write(…).<br>But then, not everything is a file descriptor! Some parts of the OS use separate namespaces; a good example are process identifiers (PIDs). You can peek at process metadata via a pseudo-filesystem called /proc, but the data doesn’t seem particularly interesting. It’s the stuff you see in the output of ps or top:<br>$ cat /proc/self/status<br>Name: cat<br>Umask: 0077<br>State: R (running)<br>...<br>Pid: 29329<br>PPid: 17523<br>...<br>Uid: 1000 1000 1000 1000<br>Gid: 1000 1000 1000 1000<br>...<br>VmSize: 2420 kB<br>...

But is that all? Well, if you ever snooped around /proc// on Linux, you might have noticed a mysterious file that seemingly can’t be opened:<br>$ cat /proc/self/mem<br>cat: /proc/self/mem: I/O error

To actually access it, you first need to attach to the target process using ptrace(…), the system’s debugging API. After that, you can call open(…), lseek(…) to a specific offset, and then read(…) or write(…) as you would with a normal file. These operations let you fetch or modify the memory of the target program in real time.<br>Here’s how it works in practice (demo link):<br>#include<br>#include<br>#include<br>#include<br>#include

#include<br>#include

volatile int my_val = 0;

int main() {

pid_t child_pid = fork();

if (!child_pid) {<br>sleep(1);<br>printf("[child] my_val = %d\n", my_val);<br>exit(0);

assert(child_pid > 0);<br>assert(ptrace(PTRACE_ATTACH, child_pid, 0, 0) == 0);

char path[64];<br>sprintf(path, "/proc/%d/mem", child_pid);

int mem_fd = open(path, O_RDWR);<br>assert(mem_fd >= 0);

assert(lseek(mem_fd, (off_t)&my_val, SEEK_SET) == (off_t)&my_val);

int tmp = 123;<br>write(mem_fd, &tmp, sizeof(tmp));

printf("[parent] my_val = %d\n", my_val);

assert(ptrace(PTRACE_DETACH, child_pid, 0, 0) == 0);<br>waitpid(child_pid, &tmp, 0);

You can also download a version of this code with additional comments by clicking here.<br>When I first discovered this API some 25 years ago, I felt that it’s remarkably elegant; the ptrace(…) function also supports a better-known PTRACE_PEEKDATA and PTRACE_POKEDATA interface, but come on!<br>Anyway — in 2002, my fascination with this API led me to writing a program called memfetch; the utility allowed you to grab a non-destructive “screenshot” of the memory of any process of your choice, be it to satiate curiosity or to recover data from a non-responsive app. This weekend, I dug it up and overhauled it to work on modern 64-bit systems. You can download the source here.<br>Some readers might find it amusing that back then, Linux 2.2 allowed you to call mmap(…) on the resulting /proc//mem file descriptor, mirroring the memory of the target process to your address space. This was too good to be true: the initial version of memfetch would sometimes crash or hang the entire system due to page tables getting out of sync. Soon after, the entire mmap(…) logic was yanked out.

Subscribe

Share

Discussion about this post<br>CommentsRestacks

TopLatestDiscussions

No posts

Ready for more?

Subscribe

© 2026 lcamtuf · Publisher Privacy<br>Substack · Privacy ∙ Terms ∙ Collection notice

Start your SubstackGet the app<br>Substack is the home for great culture

This site requires JavaScript to run correctly. Please turn on JavaScript or unblock scripts

file process proc include my_val child_pid

Related Articles