* Mike Rapoport | 2020-09-24 16:28:58 [+0300]:
This is an implementation of "secret" mappings backed by a file descriptor. I've dropped the boot time reservation patch for now as it is not strictly required for the basic usage and can be easily added later either with or without CMA.
Isn't memfd_secret currently *unnecessarily* designed to be a "one task feature"? memfd_secret fulfills exactly two (generic) features:
- address space isolation from kernel (aka SECRET_EXCLUSIVE, not in kernel's direct map) - hide from kernel, great - disabling processor's memory caches against speculative-execution vulnerabilities (spectre and friends, aka SECRET_UNCACHED), also great
But, what about the following use-case: implementing a hardened IPC mechanism where even the kernel is not aware of any data and optionally via SECRET_UNCACHED even the hardware caches are bypassed! With the patches we are so close to achieving this.
How? Shared, SECRET_EXCLUSIVE and SECRET_UNCACHED mmaped pages for IPC involved tasks required to know this mapping (and memfd_secret fd). After IPC is done, tasks can copy sensitive data from IPC pages into memfd_secret() pages, un-sensitive data can be used/copied everywhere.
One missing piece is still the secure zeroization of the page(s) if the mapping is closed by last process to guarantee a secure cleanup. This can probably done as an general mmap feature, not coupled to memfd_secret() and can be done independently ("reverse" MAP_UNINITIALIZED feature).
PS: thank you Mike for your effort!
See the following pseudo-code as an example:
// simple assume file-descriptor and mapping is inherited // by child for simplicity, ptr is int fd = memfd_secret(SECRETMEM_UNCACHED); ftruncate(fd, PAGE_SIZE); uint32_t *ptr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
pid_t pid_other;
void signal_handler(int sig) { // update IPC data on shared, uncachaed, exclusive mapped page *ptr += 1; // inform other sleep(1); kill(pid_other, SIGUSR1); }
void ipc_loop(void) { signal(SIGUSR1, signal_handler); while (1) { sleep(1); } }
int main(void) { pid_t child_pid;
switch (child_pid = fork()) { case 0: pid_other = getppid(); break; default: pid_other = child_pid break; } ipc_loop(); }
Hagen