Design Article

How to write secure C/C++ application code for your embedded design: Part 5

Robert Seacord, with Noopur Davis, Chad Doulgherty, Nancy Mead, and Robert Mead

11/6/2007 12:05 AM EST

Many flaws are exploitable because the address space of the vulnerable program has memory that is both writable and executable. As a result, an attacker can write to memory using a buffer overflow, for example, and then transfer control to the address of the injected code.

This problem can be addressed by ensuring that memory pages have the minimal permissions required for proper operation (an application of the principle of least privilege). This is not a comprehensive defense against all exploits, but simply a mechanism designed to extend your defense-in-depth strategy by increasing the difficulty for an attacker to exploit a vulnerability.

Several existing systems enforce reduced privileges in the kernel so that no part of the process address space is both writable and executable. This policy has been named "W xor X" or more concisely WAX. Enforcement of this policy results, for example, in a nonexecutable stack.

OpenBSD is an example of a system that implements a WAX policy. In OpenBSD, an application can still request explicit permissions with mprotect() to override the default.

The implementation of a WnX policy depends on the central processing unit (CPU) and memory management unit (MMU) architecture. For processors that feature an execute bit for each page of memory; OpenBSD implements two changes to make this possible:

1. The signal trampoline is removed from the stack. The signal trampoline page is given read and execute permissions, while the stack segment is given read and write permissions. By doing this, the stack becomes nonexecutable.

2. The mapping of shared libraries and heap memory space is changed so that the data segments do not contain objects which are read, write, and executable. This entails moving the shared library global offset table (GOT), the shared library procedure linkage table (PLT), C++ constructors (.ctors), and C++ destructors (.dtors).

The GOT and PLT are mapped onto their own pages, which are made nonwritable. Minor changes to the dynamic linker are needed to accommodate this change. The . dtors and . ctors sections are moved in with the GOT and consequently become nonwritable as well. Making these changes eliminates executable objects from the data segment.

The implementation for architectures that do not support a per-page execute bit (such as IA-32) is more involved but achieves a similar result.

To supplement this scheme, OpenBSD also reduces permissions on readonly strings and pointers stored as constant data. Historically, these objects were stored in the text segment and had permissions of PROT READ IPROT EXEC. This meant that constant data could potentially be executed as shell code.

Because OpenBSD uses the ELF object file format, a new segment, aptly named . rodata, was created to store this data. As a result, this behavior was changed so that now these objects only have PROT READ and they lose PROT EXEC.

Open BSD's PaX
PaX is a similar set of enhancements for Linux systems that predates the OpenBSD implementation. PaX implements nonexecutable memory pages and address space layout randomization. The PaX implementation is also dependent on the CPU and MMU of the underlying platform.

For systems that do not support the per-page execute bit, PaX implements a technique termed virtual memory area mirroring that duplicates every executable page in a data section in a corresponding code section. If an attempt is made to fetch an instruction at addresses located in the data segment that does not have any code located at the corresponding mirrored address, a page fault occurs and the kernel kills the process.


Next:




Please sign in to post comment

Navigate to related information

Datasheets.com Parts Search

185 million searchable parts
(please enter a part number or hit search to begin)