13.3 Preventing Memory from Being Paged to Disk
13.3.1 Problem
Your program stores sensitive
data in memory, and you want to prevent that data from ever being
written to disk.
13.3.2 Solution
On Unix systems, the mlock( ) system call is often
implemented in such a way that locked memory is never swapped to
disk; however, the system call does not necessarily guarantee this
behavior. On Windows, VirtualLock( ) can be used
to achieve the desired behavior; locked memory will never be swapped
to disk.
13.3.3 Discussion
|
The solutions presented here are not foolproof methods. Given enough
time and resources, someone will eventually be able to extract the
data from the program's memory. The best you can
hope for is to make it so difficult to do that an attacker deems it
not worth the time.
|
|
All modern operating systems have virtual memory managers. Among other
things, virtual memory enables the operating system to make more
memory available to running programs by swapping the contents of
physical memory to disk. When a program must store sensitive data in
memory, it risks having the information written to disk when the
operating system runs low on physical memory.
On Windows systems, the
VirtualLock( ) API function allows an application to
"lock" virtual memory into physical
memory. The function guarantees that successfully locked memory will
never be swapped to disk. However, preventing memory from swapping
can have a significant negative performance impact on the system as a
whole. Therefore, the amount of memory that can be locked is severely
limited.
On Unix
systems, the POSIX 1003.1b standard for real-time extensions
introduces an optional system call, mlock(
), which
is intended to guarantee that locked memory is always resident in
physical memory. However, contrary to popular belief, it does not
guarantee that locked memory will never be swapped to disk. On the
other hand, most current implementations are implemented in such a
way that locked memory will not be swapped to disk. The Linux
implementation in particular does make the guarantee, but this is
nonstandard (and thus nonportable) behavior!
Because the mlock( ) system call is an optional
part of the POSIX standard, a feature test macro named
_POSIX_MEMLOCK_RANGE
should be defined in the unistd.h header file if
the system call is available. Unfortunately, there is no sure way to
know whether the system call will actually prevent the memory it
locks from being swapped to disk.
On all modern hardware architectures,
memory is broken up
and managed by the hardware in fixed-size chunks called
pages. On Intel x86 systems, the page size is
4,096 bytes. Most architectures use a similar page size, but never
assume that the page size is a specific size. Because the hardware
manages memory with page-sized granularity, operating system virtual
memory managers must do the same. Therefore, memory can only be
locked in a multiple of the hardware's page size,
whether you're using VirtualLock(
) on Windows or mlock( ) on Unix.
VirtualLock( ) does not require that the address
at which to begin locking is page-aligned, and most implementations
of mlock( ) don't either. In both
cases, the starting address is rounded down to the nearest page
boundary. However, the POSIX standard does not require this behavior,
so for maximum portability, you should always ensure that the address
passed to mlock( ) is page-aligned.
Both Windows and Unix memory locking limit the maximum number of
pages that may be locked by a single process at any one time. In both
cases, the limit can be adjusted, but if you need to lock more memory
than the default maximum limits, you probably need to seriously
reconsider what you are doing. Locking large amounts of memory
can—and, most probably, will—have a negative impact on
overall system performance, affecting all running programs.
The mlock( ) system call on Unix imposes an
additional limitation over VirtualLock( ) on
Window: the process making the call must have superuser privileges.
In addition, when fork( ) is used by a process
that has locked memory, the copy of the memory in the newly created
process will not be locked. In other words, child processes do not
inherit memory locks.
|