29

I had a conversation with @anger32 who states that zeroing a physical memory page frame when passing the page backed by that frame to another process is not the responsibility of OSes like Windows and Linux (though they do that, they don't guarantee that this will happen), but a responsibility of OSes with a certificate allowing it to work with classified information.

Is it possible to make the following attack on another (maybe more privileged) process?

  1. map enough memory pages and start consuming enough CPU time to prevent zeroing thread, which has the lowest priority (at least on Windows), from getting CPU time.

  2. another process places sensitive data into memory

  3. context switch occurs

  4. we ask the OS for a memory page, OS evicts that process' page and gives us the new page backed by the same page frame without zeroing it.

  5. we scan the page for secrets.

He also states that there are ways to read another process' memory messing with mmap, its flags and physical addresses on Linux. Do you know any? Is it really possible to get another process' memory on Linux, for example memory of the process of another user or SELinux domain? If they are it looks like a very dangerous vulnerability.

forest
  • 64,616
  • 20
  • 206
  • 257
KOLANICH
  • 892
  • 6
  • 14
  • 9
    Your small edits keep bringing this to the top of the homepage.When you do edits, I would ask that you do a bunch of them together, rather than one every 30 minutes. – Mike Ounsworth Jan 13 '16 at 14:30
  • 1
    I very seriously doubt that Linux doesn't zero memory before handing it off to a new process. Because…if it didn't…then an unprivileged process could potentially read sensitive information belonging to another process. It is not only operating systems blessed with a seal of approval by some corporation that have a responsibility to protect processes from each other; ALL operating systems should do so, and I would consider those that do not as _insane and unfit for general use._ – Blacklight Shining Jan 13 '16 at 15:39
  • 1
    Wasn't this the weakness of a previous version of Windows? – Martijn Jan 13 '16 at 17:24
  • 3
    `the OS has no responsibility to protect one process' memory` - If he really stated this without an "after the process exited" he should start learning the security basics. If an OS doesn't do this, any user can execute any code (or read information to do so in case it has only ro access). – Sebb Jan 13 '16 at 19:09
  • No he haven't, sorry. Fixed the question. He meant zeroing the memory pages by OS. – KOLANICH Jan 13 '16 at 19:48
  • 8
    It's quite plausible that the Windows 9x line didn't do this, because they weren't designed to be secure. – user253751 Jan 13 '16 at 21:13
  • @BlacklightShining: Calling it *insane* is a little bit strong, many embedded OSes are little more than tasking libraries, and do no such thing. For certain, sanitization of freed memory before reuse is a requirement for any *multiuser* OS. If you generally work on multiuser systems then I agree, OSes without this function are *unfit for general use*, but it can be quite sensible to use one in specialized cases where it is not possible to load arbitrary code that could do something harmful with the residual data. – Ben Voigt Jan 14 '16 at 03:12
  • 1
    GPU memory doesn't get zeroed reliably (at least on some OS) and the GPU vendors seem to be unwilling to fix that in their drivers, despite knowing about that security vulnerability for years. – CodesInChaos Jan 14 '16 at 14:06
  • (next person to make a significant edit, please remove the tag 'sandbox' and also consider removing the tag 'access-control' and adding relevant OS/memory tags; not doing it for now so as not to re-bump the question following Mike's comment) – Steve Dodier-Lazaro Jan 14 '16 at 14:44
  • Related https://charliehorse55.wordpress.com/2016/01/09/how-nvidia-breaks-chrome-incognito/ – BlackBear Jan 14 '16 at 16:11
  • *"they don't guarantee that this will happen*" - citation/evidence needed. That premise seems faulty. It seems like your question *should* be: do they guarantee that memory will be zeroed before being reused by another process? Don't assume that it's not guaranteed (unless you have evidence to the contrary). – D.W. Jan 14 '16 at 19:21
  • @Sebb To be fair, it is not necessarily the responsibility to protect process' memory, especially on systems that lack an MMU. It is often enough simply to protect the kernel from userspace even if any userspace process can mess with any other userspace process. – forest May 11 '18 at 22:17

7 Answers7

24

I was curious of this myself once, and wrote a small program under linux that malloc'ed all available memory and dumped it to disk.

It turned out that it was all zeroed out before it was handed to my application.

Later, I also checked the kernel code, and could confirm it was the kernel who did it.

--

I think it makes perfectly sense that it is the OS responsibility to make sure old data is not made available to another process. Where else would you implement such a security measure?

Edit:

I do not remember if I tested against SWAP memory. Due to the disk IO required to zero out allocated disk space (memory), it might be implemented differently.

Dog eat cat world
  • 5,759
  • 1
  • 27
  • 46
  • 2
    `Where else would you implement such a security measure?` It could be implemented by a process before exiting, where it zeroes out memory for any data which is sensitive. But it definitely makes the most sense for the OS to handle it. – Martin Jan 13 '16 at 17:51
  • 1
    When a context switch occurs the process is not able to zero memory before the switch. Manual zeroing is done to prevent the data from leaking if someone exploited out of bounds read vuln. But it can be [not enough](http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html). – KOLANICH Jan 13 '16 at 19:03
  • 3
    Any clue if this is also true on Windows or any other OS? The fact that linux happens to do it does not mean its an OS responsibility. It could just he a nice extra feature of Linux – David says Reinstate Monica Jan 13 '16 at 19:36
  • As I know, Windows [does](http://blogs.msdn.com/b/tims/archive/2010/10/29/pdc10-mysteries-of-windows-memory-management-revealed-part-two.aspx). – KOLANICH Jan 13 '16 at 20:05
  • P.S. According to the linked post, if I understood it right, Windows has really no responsibility to give zeroed pages. – KOLANICH Jan 13 '16 at 20:12
  • @KOLANICH If I'm reading that correctly (flow chart and next para) any page sent to a user application will have old data overwritten; the kernel may be able to see them. Free but dirty pages can either be zeroized and made available for normal allocations, used for memory mapped files (in this case the old data is overwritten by the file before it's given to the application), or used for allocations by the kernel. What happens in the latter case isn't discussed: It's possible the kernel sanitizes them itself. It's also possible that kernel level code is implicitly trusted (I hope it's not). – Dan Is Fiddling By Firelight Jan 13 '16 at 22:33
  • 1
    The physical pages are not zeroed, but rather the memory that you see is mapped to a zeroed page. The pages likely contain all their data until they are committed, which happens on the first write to them unless committed explicitly. – user541686 Jan 14 '16 at 01:22
  • 9
    @DanNeely: You hope kernel level code isn't implicitly trusted? Well... I guess you could say that, because in reality it is **explicitly trusted**. Kernel code has unlimited access to the entire system. It doesn't need to pick data out of unsanitized allocations, because it can directly snoop on, or even modify any page it wants to (as a direct consequence of ring 0 ability to reconfigure page tables). – Ben Voigt Jan 14 '16 at 03:08
  • @BenVoigt Implementations of TEEs like SGX can make this less true. – forest Dec 20 '18 at 23:27
23

In Linux, processes is able to read another process memory when any of the following conditions applies:

  1. The process had root permission or it can read /proc/$PID/mem or /dev/mem, by default /proc/$PID/mem and /dev/mem are only accessible by root
  2. Parent process can fork()/clone() in such a way that allows it to read some or all memory of its child processes
  3. A parent process can fork a child in such a way to allow the child process to read some or all the parent's memory
  4. A process can set up shared memory area

A process can't read or write to the memory of an arbitrary, unrelated process unless the process runs with elevated privilege. In all other cases, you'd need to either be the parent process or the target process had to deliberately set up shared memory area.

That parent process can access a child process memory is the defining feature of most Unix systems. In Unix systems (including Linux), new process are created using fork() system call. fork() creates a copy of the existing process by creating a new entry in the OS process table, and sets up the new process virtual memory as copy on write of the parent's virtual memory. This does mean that the new process can read the parent's memory, but at this point, the new process is still executing the parent's code, so this is not a security issue. The new process may then call one of the exec*() system call to remap a new executable file into its own memory and jump to the start symbol of that executable file. The remapping means that now the only entry in the paging table on the new process virtual memory is the new executable. When the new process tries to read/write to the remapped area, it will cause a page fault and the OS will page the corresponding part of the executable file that was exec*()-ed into memory. If the new process tries to read/write to an unmapped memory area, this will cause a segmentation fault. In more advanced uses of fork and exec, a process can fork and then map the child process memory in such a way that all or parts of the child's memory will be accessible by the parent after exec*().

In Linux, when a process allocates memory (e.g. using malloc) from the OS, the process calls mmap() to allocate anonymous map. Anonymous map is served either from the RAM or swap. Anonymous mmap is zero filled by the kernel, unless the process requested MAP_UNINITIALIZED, which is only honoured on very constrained embedded systems for performance reasons, the kernel had to be compiled to allow this.

Additionally, for high security scenarios, Linux allows a process to request all or parts of its memory to be unswappable by using mlock and/or MAP_LOCKED. Locked memory is always served from RAM, and is usually used to prevent encryption keys and memory used for decryption to be swapped to persistent storage.

gerrit
  • 1,829
  • 1
  • 17
  • 26
Lie Ryan
  • 31,089
  • 6
  • 68
  • 93
  • All nice information, but not relevant to the question, which is about whether a race condition task the page zeroing task makes **implicit** transfer of data between processes possible. You are discussing the **explicit** transfers, which being designed for, also are explicitly secured. Securing unanticipated channels is much harder of course. – Ben Voigt Jan 14 '16 at 03:04
  • @BenVoigt: the question is based on the implicit assumption that memory need to be zeroed when they are paged in and out. What happens is a bit more complicated, on that memory allocation is actually just a page table and vmm operation, the actual loading of mapped memory with mapped data or zeroing happens during page fault. During a page fault, the process/thread/task is in unrunnable state, it won't be scheduled so concurrency errors can't happen. The first half of my answer tries to address this misconception by explaining how virtual memory works in modern OS. – Lie Ryan Jan 14 '16 at 03:47
  • 1
    @BenVoigt: The second to last paragraph addresses the real question. According to kernel documentation, all anonymous memory making are guaranteed to be zeroed when the user process reads it right after mapping. The actual zeroing doesn't happen when the new process is executed, but rather during memory reads/page faults. – Lie Ryan Jan 14 '16 at 03:50
9

The attack you describe doesn't work on Windows. Starving the page-zeroing thread doesn't prevent zeroing, it only delays it. The existence of the page-zeroing background task is a performance optimization.

Basically, a naive memory manager with a privacy guarantee works like this:

  • reserve a page from the freed list
  • zero it
  • make it available to application code (set page table entry to permit ring 3 access)

The optimized version looks more like:

  • get a page from the zeroed list, if there is one
  • if first step succeeded, skip to last step
  • reserve a page from the freed list
  • zero it
  • make it available to application code (set page table entry to permit ring 3 access)

Starving the zeroing thread will result in slow allocation, because the zeroing hasn't been done yet. It won't result in data leakage, because the data structure keeps zeroed and leftover pages segregated, and when the allocator runs out of pre-zeroed pages, it has to do the zeroing just-in-time.

Ben Voigt
  • 760
  • 1
  • 10
  • 17
5

It is absolutely possible to read the memory of another process but this is only possible with administrative privileges and of course the OS won't allow any process to access any space of the memory that isn't assigned to that process.

For Administrative users this is of course possible. In Windows for example this functionality is implemented by default to debug a process. You can do this using the Task Manager like described here.

But its also possible to dump the whole memory including all processes and everything that is stored in memory at that time. This isn't that easy anymore because windows systems don't provide this functionality by default. To do this the application loads it's own memory drivers which allows them to access the memory directly and not through the OS.

On older linux systems we are able to dump the memory directly through the memory device in the /dev partition. This is not possible anymore but there are kernel modules that enable one to dump the whole memory too. This requires root privileges aswell. You can also do this manually for one process like described here.

//EDIT: I just asked a senior developer with 40 years of experience about this. The answer is: It relies on varoius factors. He told me in C++ and Java the variables are initialized which means that an application that is writen in C++ or Java wont be able to obtain the old information because it's overwritten by initializing that variable. But C does not do this so then it might be possible but then it still relies on the OS.

davidb
  • 4,285
  • 3
  • 19
  • 31
  • This requires root priveleges. I meant unpriveleged code hacking the equally or more priveleged. For example the attack in the following way: we run own process, the priveleged one puts the data into memory, context switch occurs, we ask for a page, the os evicts the memory page of priveleged process and gives us a page backed to that page frame without zeroing it (the man insists zeroing is not os responsibility), and we able to scan it for secrets. – KOLANICH Jan 13 '16 at 12:07
  • See edit... I just asked an experienced fellow... – davidb Jan 13 '16 at 12:18
  • 3
    The experienced fellow is wrong regarding C++ - you *can* access uninitialized memory from C++, although there is rarely a good reason to do so. – tucuxi Jan 13 '16 at 15:10
2

Technically, an operating system could recycle pages from processes that had the same security context, because any information the new process could gather from that would also be accessible to the process directly.

This is however completely impractical, because the security context of a process may change over time, and when privileges are dropped (which is a common pattern), the data contained in the process still needs to be protected from anyone with less access permissions than the original privilege set.

Given that privileges can also be fairly fine-grained, the effort to keep track of which processes a page may be given to without scrubbing it first is significantly higher than simply clearing every page returned to the operating system, especially as computer memory architecture heavily favours large sequential writes.

Some embedded architectures also use the DMA controller for this task, reducing the CPU time to a few cycles to set up the controller and acknowledge completion.

Whether a process may assume that freshly mapped pages are cleared is a contract between it and the operating system, but typically this is not assumed, and again there is usually little reason to do so, because processes still need to keep track of what data inside their address space they consider valid, and they would only do so for anything that really holds information.

If a task maps, modifies and unmaps pages rapidly, this will increase system load, and processes may become suspended while waiting for a low priority process to clear a page. Operating systems need to take care to not inadvertently introduce a priority inversion here, by temporarily increasing the priority of the zeroing task to that of the highest-priority task attempting to map a page.

Simon Richter
  • 1,482
  • 11
  • 8
  • One doesn't ever need to muck with the priority of the background zeroing thread, because it is perfectly feasible to perform zeroing in the active context as a fallback. – Ben Voigt Jan 14 '16 at 03:01
  • @BenVoigt, depending on how your OS is set up, that may be more effort to implement, especially if you already have code to deal with priority inversion in locks. – Simon Richter Jan 15 '16 at 13:45
1

Most OSes has to be certified to be used for certain purposes/in certain organisations. Most of them use the Common Criteria framework to different assurance levels and some levels require the OS to clear a page before handing it to another process. An indirect reference to this requirement, which states:

One reason zero-initialized pages are required is to meet various security requirements, such as the Common Criteria. Most Common Criteria profiles specify that user-mode processes must be given initialized page frames to prevent them from reading a previous process’s memory contents. Therefore, the memory manager gives user-mode processes zeroed page frames unless the page is being read in from a backing store. If that’s the case, the memory manager prefers to use nonzeroed page frames, initializing them with the data off the disk or remote storage.

(from Windows Internals, Part 2 6th ed. By Mark E. Russinovich, David A. Solomon, Alex Ionescu. Copyright © 2012 by David Solomon and Mark Russinovich)

Enabling such a feature most often requires the memory management architecture to be designed accordingly and it does not make sense to exclude this feature in a "civilian"/insecure version for non-obvious performance gains.

Specifically, it is important that kernel pages and pages from other users do not get leaked to unprivileged processes so they will have to cleared at some stage. It is also inefficient for a page to be zeroed unless it actually crosses above boundry (in case a page gets allocated back to the same process/kernel). So the only sensible time to do it is at the time of allocation and since the receiver cannot be trusted to do this, the OS will have to bear the responsibility. (Of course, real world OS will adopt all kinds of optimisations to reduce the delay of page allocation.)

billc.cn
  • 3,852
  • 1
  • 16
  • 24
-3

The only working way to perform what are you talking about is a Cold Boot Attack, when you're destroying kernel by powering it off. OR you can try to play with kexec() calls, but it will not work in most of the cases.

Alexey Vesnin
  • 1,565
  • 1
  • 8
  • 11
  • This question is not about coldboot, coldboot attack is the last resort and requires control over hardware, not only single unpriveleged process. Could you clarify about ```kexec```, if you can play with it to get access to the memory you mustn't have access, it is a clear vulnerability? – KOLANICH Jan 13 '16 at 18:54
  • @KOLANICH take a look at some practical examples [here](https://www.linux.com/community/blogs/129-servers/413862) – Alexey Vesnin Jan 13 '16 at 18:57
  • I don't see any vuln here, because kexec requires root privileges. – KOLANICH Jan 13 '16 at 19:13
  • @KOLANICH it's not a vuln from unprivileged user - the memory is zero-filled by kernel – Alexey Vesnin Jan 13 '16 at 20:14