mlock(2)
Lock pages into RAM so they can't be swapped out; mlockall() does the whole address space.
- Since
- POSIX.1-1996 (SVr4)check your kernel →
- glibc
- unknown (mlock2(): glibc 2.27)
- System
- Linux
- Man page
- mlock(2)
Synopsis
#include <sys/mman.h>
int mlock(const void addr[size], size_t size);
int mlock2(const void addr[size], size_t size, unsigned int flags);
int munlock(const void addr[size], size_t size);
int mlockall(int flags);
int munlockall(void);Rod's take
Your private key leaks three ways: swap, core dumps and fork, and mlock only fixes the first. Lock the pages, and if mlockall comes back ENOMEM, die. Logging a warning and carrying on means the key hits swap during some 3am memory crunch and nobody ever notices. Raise RLIMIT_MEMLOCK or hold CAP_IPC_LOCK. Then add MADV_DONTDUMP for crash dumps, MADV_WIPEONFORK so children see zeros, and explicit_bzero when you're done, because the compiler will happily delete your memset. None of it stops root reading your memory. It stops the boring leaks.
Flags and arguments that matter
| Name | What it does |
|---|---|
| MCL_CURRENT | mlockall(): lock every page currently mapped into the address space. |
| MCL_FUTURE | mlockall(): lock pages mapped in the future too; later mmap/brk can fail if over the limit. |
| MCL_ONFAULTLinux 4.4 | With MCL_CURRENT/MCL_FUTURE: lock pages as they're faulted in instead of populating them up front. |
| MLOCK_ONFAULTmlock2(), Linux 4.4 | mlock2(): lock resident pages now, the rest of the range when they're first faulted in. |
| RLIMIT_MEMLOCK | Caps how much an unprivileged process can lock (since Linux 2.6.9). CAP_IPC_LOCK bypasses it. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| ENOMEM | Would exceed the RLIMIT_MEMLOCK soft limit without CAP_IPC_LOCK, or the range isn't fully mapped. |
| EPERM | Caller is not privileged but needs CAP_IPC_LOCK for the requested operation. |
| EAGAIN | Some or all of the specified range could not be locked. |
| EINVAL | addr+size overflowed, unknown flags, or MCL_ONFAULT without MCL_CURRENT/MCL_FUTURE. |
Example
unsigned char *alloc_secret(size_t len) {
unsigned char *buf;
if (posix_memalign((void **)&buf, getpagesize(), len) != 0)
return NULL;
if (mlock(buf, len) != 0) { free(buf); return NULL; }
madvise(buf, len, MADV_DONTDUMP);
madvise(buf, len, MADV_WIPEONFORK);
return buf;
}
void free_secret(unsigned char *buf, size_t len) {
explicit_bzero(buf, len);
munlock(buf, len);
free(buf);
}Trimmed from the full walkthrough in mlock, MADV_DONTDUMP, and where your secrets actually leak.
BSD equivalent
FreeBSD: mlock(2)/mlockall(2) (mlock since 4.4BSD); madvise(2) MADV_NOCORE for core-dump exclusion. OpenBSD: mlock(2)/mlockall(2) (4.4BSD).
The long version, from the console
Sources
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
