An Architectural Approach to Preventing Code Injection Attacks
Ryan Riley Xuxian Jiang Dongyan Xu
Purdue University George Mason University Purdue University
rileyrd@[Link] xjiang@[Link] dxu@[Link]
Abstract This protection methodology is effective for many of the
traditional attacks, however attackers still manage to cir-
Code injection attacks, despite being well researched, cumvent them [4]. In addition, these schemes enforce spe-
continue to be a problem today. Modern architectural solu- cific rules for program layout with regards to separating
tions such as the NX-bit and PaX have been useful in lim- code and data, and as such are unable to protect memory
iting the attacks, however they enforce program layout re- pages that contain both. Compiler based protection mech-
strictions and can often times still be circumvented by a de- anisms [5, 6, 7] are designed to protect crucial memory
termined attacker. We propose a change to the memory ar- locations such as function pointers or the return address
chitecture of modern processors that addresses the code in- and detect when they have been modified. These methods,
jection problem at its very root by virtually splitting memory while effective for a variety of attacks, do not provide broad
into code memory and data memory such that a processor enough coverage to handle a great many modern vulnerabil-
will never be able to fetch injected code for execution. This ities [8]. Both of these techniques, architectural and com-
virtual split memory system can be implemented as a soft- piler based, focus on preventing an attacker from executing
ware only patch to an operating system, and can be used to his injected code, but do nothing to prevent him from inject-
supplement existing schemes for improved protection. Our ing and fetching it in the first place.
experimental results show the system is effective in prevent- The core of the code injection problem is that modern
ing a wide range of code injection attacks while incurring computers implement a von Neumann memory architecture
acceptable overhead. [9]; that is, they use a memory architecture wherein code
Keywords: Code Injection, Secure Memory Architecture and data are both accessible within the same address space.
This property of modern computers is what allows an at-
1. Introduction tacker to inject his attack code into a program as data and
then later execute it as code. Wurster et al [10] proposed a
Despite years of research, code injection attacks con- technique to defeat software self checksumming by chang-
tinue to be a problem today. Systems continue to be vulner- ing this property of modern computers (and hence produc-
able to the traditional attacks, and attackers continue to find ing a Harvard architecture [11, 12]), and inspired us to con-
new ways around existing protection mechanisms in order sider the implications such a change would have on code
to execute their injected code. Code injection attacks and injection.
their prevention has become an arms race with no obvious We propose virtualizing a Harvard architecture on top of
end in site. the existing memory architecture of modern computers so
A code injection attack is a method whereby an attacker as to prevent the injection of malicious code entirely. A Har-
inserts malicious code into a running process and transfers vard architecture is simply one wherein code and data are
execution to his malicious code. In this way he can gain stored separately. Data cannot be loaded as code and vice-
control of a running process, causing it to spawn other pro- versa. In essence, we create an environment wherein any
cesses, modify system files, etc. If the program runs at a code injected by an attacker into a process’ address space
privilege level higher than that of the attacker, he has essen- cannot even be addressed by the processor for execution. In
tially escalated his access level. (Or, if he has no privileges this way, we are attacking the code injection problem at its
on a system, then he has gained some.) root by regarding the injected malicious code as data and
A number of solutions exist that handle the code injec- making it unaddressable to the processor during an instruc-
tion problem on some level or another. Architectural ap- tion fetch. The technique can be implemented as a soft-
proaches [1, 2, 3] attempt to prevent malicious code exe- ware only patch for the operating system, and our imple-
cution by making certain pages of memory non-executable. mentation for the x86 incurs a very reasonable performance
penalty, on average between 10 and 20%. Such a software turn address. Stack Shield [7] uses a separate stack for re-
only technique is possible through careful exploitation of turn addresses as well as adding sanity checking to ret
the two translation lookaside buffers (TLBs) on the x86 ar- and function pointer targets. Due to the fact that these tech-
chitecture in order to split memory in such a way that it niques only make it their goal to prevent control flow hijack-
enforces a strict separation of code and data memory. ing, they tend to only work against known hijacking tech-
niques. That means that while they are effective in some
2. Related Work and Motivation cases, they may miss many of the more complicated attacks.
Research on code injection attacks has been ongoing for Wilander et al [8], for example, found that these techniques
a number of years now, and a large number of protection missed a fairly large percentage (45% in the best case) of at-
methods have been researched and tested. There are two tacks that they implemented in their buffer overflow bench-
classes of techniques that have become widely supported in mark.
modern hardware and operating systems; one is concerned Due to the fact that the stack based approaches above
with preventing the execution of malicious code after con- do not account for a variety of attacks, in this work we are
trol flow hijacking, while the other is concerned with pre- primarily concerned with addressing limitations in the ar-
venting an attacker from hijacking control flow. chitectural support of the execute-disable bit. While this
The first class of technique is concerned with prevent- technique is widely deployed and has proven to be effec-
ing an attacker from executing injected code using non- tive, it has limitations. First, programs must adhere to the
executable memory pages, but does not prevent the at- “code and data are always separated” model. In the event a
tacker from impacting program control flow. This protec- program has pages containing both code and data the pro-
tion comes in the form of hardware support or a software tection scheme cannot be used. In fact, such “mixed pages”
only patch. Hardware support has been put forth by both do exist in real-world software systems. For example, the
Intel and AMD that extends the page-level protections of Linux kernel uses mixed pages for both signal handling
the virtual memory subsystem to allow for non-executable [13] as well as loadable kernel modules. A second prob-
pages. (Intel refers to this as the “execute-disable bit” lem with these schemes is that a crafty attacker can disable
[3].) The usage of this technique is fairly simple: Program or bypass the protection bit using library code already in the
information is separated into code pages and data pages. process’ address space and from there execute the injected
The data pages (stack, heap, bss, etc) are all marked non- code. Such an attack has been demonstrated for the Win-
executable. At the same time, code pages are all marked dows platform by injecting code into non-executable space
read-only. In the event an attacker exploits a vulnerability and then using a well crafted stack containing a series of
to inject code, it is guaranteed to be injected on a page that system calls or library functions to cause the system to cre-
is non-executable and therefore the injected code is never ate a new, executable memory space, copy the injected code
run. Microsoft makes use of this protection mechanism in into it, and then transfer control to it. One such example has
its latest operating systems, calling the feature Data Execu- been shown in [4].
tion Protection (DEP) [1]. This mediation method is very It is these two limitations in existing page-level pro-
effective for traditional code injection attacks, however it tection schemes (the forced code and data separation and
requires hardware support in order to be of use. Legacy x86 the bypass methodology) that provide the motivation for
hardware does not support this feature. This technique is our work, which architecturally addresses the code injec-
also available as a software-only patch to the operating sys- tion problem at its core. Note that our architectural ap-
tem that allows it to simulate the execute-disable bit through proach is orthogonal to research efforts on system ran-
careful mediation of certain memory accesses. PAX PAGE- domization, such as Address Space Layout Randomization
EXEC [2] is an open source implementation of this tech- (ASLR) [14, 15, 16, 17] and Instruction Set Randomization
nique that is applied to the Linux kernel. It functions iden- (ISR) [18, 19, 20]. We are also distinct from other work that
tically to the hardware supported version, however it also focuses specifically on preventing array overflow using a
supports legacy x86 hardware due to being a software only compiler or hardware, such as [21]. We point out that these
patch. alternate systems all work on a single memory architecture
The second class of technique has a goal of preventing wherein code and data are accessible within the same ad-
the attacker from hijacking program flow, but does not con- dress space. Our approach, to be described in the next sec-
cern itself with the injected code. Works such as Stack- tion, instead creates a different memory architecture where
Guard [5] accomplish this goal by emitting a “canary” value code and data are separated.
onto the stack that can help detect a buffer overflow. ProPo-
3. An Architectural Approach
lice [6] (currently included in gcc) builds on this idea by
also rearranging variables to prevent overflowed arrays from At its root, code injection is a problem because proces-
accessing critical items such as function pointers or the re- sors permit code and data to share the same memory address
space. As a result, an attacker can inject his payload as data less of the source, is stored in a different physical memory
and later execute it as code. The underlying assumption re- from instructions. Instructions cannot be addressed as data,
lied on by attackers is that the line between code and data and data cannot be addressed as instructions. This means
is blurred and not enforced. For this reason, we turn to an that in a Harvard architecture based computer, a traditional
alternative memory architecture that does not permit code code injection attack is not possible because the architec-
and data to be interchanged at runtime. ture is not capable of supporting it after a process is initially
setup. The attacker is simply unable to inject any informa-
3.1. The Harvard and von Neumann Mem-
tion whatsoever into the instruction memory’s address space
ory Architectures
and at the same time is unable to execute any code placed
Modern computers and operating systems tend to use in the data memory. The architecture simply does not have
what is known as a von Neumann memory architecture [9]. the “features” required for a successful code injection at-
Under a von Neumann system there is one physical memory tack. However, we point out that this does not prevent an
which is shared by both code and data. As a consequence attacker from mounting non control injection attacks (e.g.,
of this, code can be read and written like data and data can non-control-data attack [23]) on a Harvard architecture. We
be executed like code. Many systems will use segmentation touch on these attacks in section 6.
or paging to help separate code and data from each other or 3.3. Challenges in Using a Harvard Archi-
from other processes, but code and data end up sharing the tecture
same address space. Figure 1a illustrates a von Neumann
architecture. While a Harvard architecture may be effective at mitigat-
An architecture not found in most modern computers ing code injection, the truth of the matter is that for any new
(but found in some embedded devices or operating systems, code injection prevention technique to be practical it must
such as VxWorks [22]) is known as a Harvard architecture be usable on modern commodity hardware. As such, the
[11, 12]. Under the Harvard architecture code and data each challenge is to construct a Harvard architecture on top of a
have its own physical address space. One can think of a widely deployed processor such as the x86. We first present
Harvard architecture as being a machine with two different a few possible methods for creating this Harvard architec-
physical memories, one for code and another for data. Fig- ture on top of the x86.
ure 1b shows a Harvard architecture. Modifying x86
3.2. Harvard and Code Injection One technique for creating such an architecture is to make
changes to the existing architecture and use hardware virtu-
A code injection attack can be thought of as being carried
alization [24] to make them a reality. The changes required
out in four distinct, but related, stages:
in the x86 architecture to produce a Harvard architecture are
1. The attacker injects code into a process’ address space. fairly straight forward modifications to the paging system.
2. The attacker determines the address of the injected Currently, x86 implements paging by having a separate
code. pagetable for each process and having the operating system
3. The attacker somehow hijacks the program counter to maintain a register (CR3) that points to the pagetable for
point to the injected code. the currently running process. One pagetable is used for
4. The injected code is executed. the process’ entire address space, both code and data. In
The mediation methods mentioned in section 2 are designed order to construct a Harvard architecture, one would need
to handle the problem by preventing either step 3 or 4. to maintain two different pagetables, one for code and one
Non-executable pages are designed to prevent step 4, while for data. As such, our proposed change to the x86 archi-
compiler based approaches are meant to prevent step 3. In tecture to allow it to create a Harvard architecture is to cre-
both cases, however, the malicious code is injected, but ex- ate an additional pagetable register in order that one can
ecution is somehow prevented. Our solution, on the other be used for code (CR3-C) and the other for data (CR3-D).
hand, effectively stops the attack at step 1 by preventing the Whenever an instruction fetch occurs, the processor uses
successful injection of the malicious code into a process’ CR3-C to translate the virtual address, while for data reads
code space. (The purist will note that in the implementa- and writes CR3-D is used. An operating system, therefore,
tion method described in section 4 the attack is not tech- would simply need to maintain two separate pagetables for
nically stopped until step 4, however the general approach each process. This capability would also offer backwards
described here handles it at step 1.) compatibility at the process level, as the operating system
The Harvard architecture’s split memory model makes could simply maintain one pagetable and point both regis-
it suitable for the prevention of code injection attacks due ters to it if a process requires a von Neumann architecture.
to the fact that a strict separation between code and data is We note that no changes would need to be made to the pro-
enforced at the hardware level. Any and all data, regard- cessor’s translation lookaside buffer (TLB) as modern x86
Physical Memory
Processor
Instructions Instruction Memory Data Memory
Data Instruction/Data Instructions Data
Fetch
Processor
Instructions Data
… Instruction Data
… Fetch Fetch …
(a) (b)
Figure 1. (a) von Neumann architecture. (b) Harvard architecture
processors already have a separate TLB for code and data. Memory
While this approach to the problem may be effective, the
Instructions
requirement that the protected system be run on top of hard- Instruction
ware virtualization inhibits its practicality. As such, another Fetch
Instructions
approach is needed. Processor
Data
Exploiting x86 Data
Data Fetch
Another technique for creating this Harvard architecture is
to make unconventional use of some of the architecture’s
features in order to create the appearance of a memory that
is split between code and data. Through careful use of the Figure 2. Split memory architecture
pagetable and the TLBs on x86, it is possible to construct 2.
a Harvard memory architecture at the process level using This split memory architecture is an environment
only operating system level modifications. No modifica- wherein an attacker can exploit a vulnerable program and
tions need to be made to the underlying x86 architecture, inject code into its memory space, but never be able to actu-
and the system can be run on conventional x86 hardware ally fetch it for execution. This is because the physical page
without the need for hardware virtualization as in the previ- that contains the data the attacker managed to write into the
ous method. program is not accessible during an instruction fetch, as in-
In the following sections we will further describe this struction fetches will be routed to an un-compromised code
technique as well as its unique advantages. page. This also creates the unique opportunity to support
4. Split Memory: A Harvard Architecture on and protect pages that contain both code and data by keep-
ing the two physically separated but logically combined.
x86
What to Split
Now that we have established that it is our intention to
exploit, not change, the x86 architecture in order to create Before we discuss the technical details behind successfully
a virtual split memory system, we will now describe the splitting a given page, it is important to note that different
technique in greater detail. pages in a process’ address space may be chosen to split
based on how our system will be used.
4.1. Virtualizing Split Memory on x86 One potential use of the system is to augment the exist-
In order to speed up pagetable lookup time, many pro- ing non-executable page methods by expanding their pro-
cessors include a small hardware cache called a translation tection to allow for protecting mixed code and data pages.
lookaside buffer (TLB) which is used to cache pagetable en- Under this usage of the system, the majority of pages un-
tries. In order to better exploit locality, modern processors der a process’ address space would be protected using the
actually split the TLB into two TLBs, one for code and one non-executable pages, while the mixed code and data pages
for data. This feature can be exploited by a keen operating would be protected using our technique. Using this scheme,
system to route data accesses for a given virtual address to chances are high that only a few of the process’ pages would
one physical page, while routing instruction fetches to an- need to be protected using our method. Note that this as-
other. By desynchronizing the TLBs and having each con- sumes we have a good understanding of the memory space
tain a different mapping for the same virtual page, every vir- of the program being protected.
tual page may have two corresponding physical pages: One Another potential use of our system, and the one which
for code fetch and one for data access. In essence, a system we use in our prototype in section 5.1, is to protect every
is produced where any given virtual memory address could page in a process’ memory space. This is a more compre-
be routed to two possible physical memory locations. This hensive type of protection than simply augmenting existing
creates a split memory architecture, as illustrated in Figure schemes. Note that in this case, more pages are chosen to
Algorithm 1: Split memory page fault handler Algorithm 2: Debug interrupt handler
Input: Faulting Address (addr), CPU instruction Input: Pagetable Entry for previously faulting address
pointer (EIP), Pagetable Entry for addr (pte) (pte)
1 if addr == EIP then /* Code Access */ 1 if processor is in single step mode then
2 pte = the code page; 2 restrict(pte);
3 unrestrict(pte); 3 disable single step();
4 enable single step(); 4 end
5 return;
6 else /* Data Access */ try is unrestricted (we unset the supervisor bit in the PTE),
7 pte = the data page; and a read off of that page is performed. As soon as the
8 unrestrict(pte); read occurs, the memory management unit in the hardware
9 read byte(addr); reads the newly modified PTE, loads it into the data-TLB,
10 restrict(pte); and returns the content. At this point the data-TLB con-
11 return; tains the entry to the data page for that particular address
12 end while the instruction-TLB remains untouched. Finally, the
PTE is restricted again to prevent a later instruction access
be split and thus protected. from improperly filling the instruction-TLB. Note that even
though the PTE is restricted, later data accesses to that page
How to Split
can occur unhindered because the data-TLB contains a valid
Once it is determined which pages will be split, the tech- mapping. This loading method is also used in the PAX [2]
nique for splitting a given page is as follows: protection model and is known to bring the overhead for a
1) On program start-up, the page that needs to be split is du- data-TLB load down to reasonable levels.
plicated. This produces two copies of the page in phys- In algorithm 1 this process can be seen in lines 7–11.
ical memory. We choose one page to be the target of First, the pagetable entry is set to point to the data page
instruction fetches, and the other to be the target of data and unrestricted by setting the entry to be user accessible
accesses. instead of supervisor accessible. Next, a byte on the page is
2) The pagetable entry (PTE) corresponding to the page we touched, causing the hardware to load the data-TLB with a
are splitting is set to ensure a page fault will occur on a pagetable entry corresponding to the data page. Finally, the
TLB miss. In this case, the page is considered restricted, pagetable entry is re-protected by setting it into supervisor
meaning it is only accessible when the processor is in mode once again.
supervisor mode. We accomplish it by setting or en- Loading the Instruction-TLB
abling the supervisor bit [3] in the PTE for that page.
If supervisor is marked in a PTE and a user-level pro- The loading of the instruction-TLB has additional compli-
cess attempts to access that page for any reason, a page cations compared to that of the data-TLB, namely because
fault will be generated and the corresponding page fault there does not appear to be a simple procedure such as a
handler will be automatically invoked. pagetable walk that can accomplish the same task. De-
3) Depending on the reasons for the page fault, i.e., either spite these complications, however, a technique introduced
this page fault is caused by a data TLB miss or it is in [10] can be used to load the instruction-TLB on the x86.
caused by an instruction TLB miss, the page fault han- Once it is determined that the instruction-TLB needs
dler behaves differently. Note that for an instruction- to be loaded, the PTE is unrestricted, the processor is
TLB miss, the faulting address (saved in the CR2 reg- placed into single step mode, and the faulting instruction
ister [3]) is equal to the program counter (contained in is restarted. When the instruction runs this time the PTE is
the EIP register); while for a data-TLB miss, the page read out of the pagetable and stored in the instruction-TLB.
fault address is different from the program counter. In After the instruction finishes then the single step mode of
the following, we describe how different TLB misses are the processor generates an interrupt, which is used as an
handled. The algorithm is outlined in algorithm 1. opportunity to restrict the PTE.
This functionality can be seen in algorithm 1 lines 2–5
Loading the Data-TLB
as well as in algorithm 2. First, the PTE is set to point to
The data-TLB is loaded using a technique called a pagetable the corresponding code page and is unprotected. Next, the
walk, which is a procedure for loading the TLB from within processor is placed into single step mode and the page fault
the page fault handler. The pagetable entry (PTE) in ques- handler returns, resulting in the faulting instruction being
tion is set to point to the data page for that address, the en- restarted. Once the single step interrupt occurs, algorithm
2 is run, effectively restricting the PTE and disabling single 5. Implementation and Evaluation
step mode.
5.1. Proof of Concept Implementation
4.2. Effects on Code Injection An x86 implementation of the above method has been
created by modifying version 2.6.13 of the Linux kernel. In
A split memory architecture produces an address space this section, we present a description of the modifications to
where data cannot be fetched by the processor for execu- create the architecture.
tion. For an attacker attempting a code injection, this will
prevent him from fetching and executing any injected code. Modifications to the ELF Loader
A sample code injection attack attempt on a split memory ELF is a format that defines the layout of an executable file
architecture can be seen in Figure 3 and described as fol- stored on disk. The ELF loader is used to load those files
lows: into memory and begin executing them. This work includes
setting up all of the code, data, bss, stack, and heap pages
1. The attacker injects his code into a string buffer start- as well as bringing in most of the dynamic libraries used by
ing at address 0xbf000000. The memory writes are a given program.
routed to physical pages corresponding to data.
The modifications to the loader are as follows: After the
2. At the same time as the injection, the attacker over-
ELF loader maps the code and data pages from the ELF file,
flows the buffer and changes the return address of the
for each one of those pages two new, side-by-side, physical
function to point to 0xbf000000, the expected loca-
pages are created and the original page is copied into both of
tion of his malicious code.
them. This effectively creates two copies of the program’s
3. The function returns and control is transferred to ad-
memory space in physical memory. The pagetable entries
dress 0xbf000000. The processor’s instruction fetch
corresponding to the code and data pages are changed to
is routed to the physical pages corresponding to in-
map to one of those copies of the memory space, leav-
structions.
ing the other copy unused for the moment. In addition,
4. The attacker’s malicious code is not on the instruc-
the pagetable entries for those pages get the supervisor bit
tion page (the code was injected as data and therefore
cleared, placing that page in supervisor mode in order to be
routed to a different physical page) and is not run. In
sure a page fault will occur when that entry is needed. A
all likelihood, the program simply crashes.
previously unused bit in the pagetable entry is used to sig-
nify that the page is being split. In total, about 90 lines of
4.3. Overhead code are added to the ELF loader.
This technique of splitting memory does not come with- In this particular implementation of split memory the
out a cost, there is some overhead associated with the memory usage of an application is effectively doubled,
methodologies described above. however this limitation is not one of the technique itself,
but instead of the prototype. A system can be envisioned
One potential problem is the use of the processor’s sin-
based on demand-paging (only allocating a code or data
gle step mode for the instruction-TLB load. This loading
page when needed) instead of the current method of proac-
process has a fairly significant overhead due to the fact that
tively duplicating every virtual page. We would anticipate
two interrupts (the page fault and the debug interrupt) are
this optimization to not have any noticeable impact on per-
required in order to complete it. This overhead ends up be-
formance.
ing minimal overall for many applications due to the fact
that instruction-TLB loads are fairly infrequent, as it only Modifications to the Page Fault Handler
needs to be done once per page of instructions. Under Linux, the page fault (PF) handler is called in re-
Another problem is that of context switches in the op- sponse to a hardware generated PF interrupt. The handler is
erating system. Whenever a context switch (meaning the responsible for determining what caused the fault, correct-
OS changes running processes) occurs, the TLB is flushed. ing the problem, and restarting the faulting instruction.
This means that every time a protected process is switched For our modifications to the PF handler we simply mod-
out and then back in, any memory accesses it makes will ify it to handle a new reason for a PF: There was a permis-
trigger a page fault and subsequent TLB load. The over- sions problem caused by the supervisor bit in the PTE. We
heard of these TLB loads is significantly higher than a tra- must be careful here to remember that not every PF on a
ditional page fault, and hence causes the majority of our split page is necessarily our fault, some PFs (such as ones
slowdown. The problem of context switches is, in fact, the involving copy-on-write), despite being on split memory
greatest cause of overhead in the implemented system. The pages, must be passed on to the rest of the PF handler in-
experimental details of the overhead can be seen in section stead of being serviced in a split memory way. If it is deter-
5.3. mined that the fault was caused by a split memory page and
Instruction Page Data Page Instruction Page Data Page Instruction Page Data Page
Return 0xbf000000 0xbf000000
Address
Blank Blank Blank
(Zeros) String Buffer (Zeros) Attack Code (Zeros) Attack Code
0xbf000000 0xbf000000 0xbf000000
Data Instruction
Processor Processor Processor
Access Access
(a) (b) (c)
Figure 3. (a) Before the attacker injects code (b) The injection to the data page (c) The execution
attempt that gets routed to the instruction page
that it does need to be serviced, then the instruction pointer Modifications to the Memory Management System
is compared to the faulting address to decide whether the There are a number of features related to memory manage-
instruction-TLB or data-TLB needs to be loaded. (Recall ment that must be slightly modified to properly handle our
from algorithm 1 that this is done by simply checking if the system. First, on program termination any split pages must
two are the same.) be freed specially to ensure that both physical pages (the
If the data-TLB needs to be loaded, then the PTE is set code page and data page) get put back into the kernel’s pool
to user mode, a byte on the page is touched, and the PTE of free memory pages. This is accomplished by simply
is set back to supervisor mode. This pagetable walk loads looking for the split memory PTE bit that was set by the
the data-TLB1 . In the event the instruction-TLB needs to ELF loader above, and if it is found then freeing two pages
be loaded, the PTE is set to user mode (to allow access instead of just one.
to the page) and the trap flag (single-step mode) bit in the Another feature in the memory system that needs to be
EFLAGS register is set. This will ensure that the debug in- updated is the copy-on-write (COW) mechanism. COW is
terrupt handler gets called after the instruction is restarted. used by Linux to make forked processes run more effi-
Before the PF handler returns and that interrupt occurs, ciently. That basic idea is that when a process makes a copy
however, a little bit of bookkeeping is done by saving the of itself using fork both processes get a copy of the orig-
faulting address into the process’ entry in the OS process inal pagetable, but with every entry set read-only. Then, if
table in order to pass it to the debug interrupt handler. either process writes to a given page, the kernel will give
In total there were about 110 lines of code added to the that process its own copy. (This reduces memory usage in
PF handler to facilitate splitting memory. the system because multiple processes can share the same
physical page.) For split memory the COW system must
Modifications to the Debug Interrupt Handler copy both pages in the event of a write, instead of just one.
The debug interrupt handler is used by the kernel to han- A update similar to the COW update is also made to the
dle interrupts related to debugging. For example, using a demand paging system. Demand paging basically means
debugger to step through a running program or watch a par- that a page is not allocated until it is required by a process.
ticular memory location makes use of this interrupt handler. In this way a process can have a large amount of available
For the purposes of split memory, the handler is modified memory space (such as in the BSS or heap) but only have
to check the process table to see if a faulting address has physical pages allocated for portions it actually uses. The
been given, indicating that this interrupt was generated be- demand paging system was modified to allocate two pages
cause the PF handler set the trap flag. If this is the case, instead of just the one page it normally does.
then it is safe to assume that the instruction which originally Overall, about 75 lines of code were added to handle
caused the PF has been restarted and successfully executed these various parts related to memory management.
(meaning the instruction-TLB has been filled) and as such 5.2. Effectiveness
the PTE is set to supervisor mode once again and the trap
flag is cleared. In total, about 40 lines of code were added to The sample implementation was tested for its effective-
the debug interrupt handler to accommodate these changes. ness at preventing code injection attacks using a benchmark
originally put forth by Wilander et al [8]. The benchmark
1 Occasionally the pagetable walk does not successfully load the data- was modified slightly in order to allow it to handle hav-
TLB. In this case, single stepping mode (like the instruction-TLB load) ing the code injected on the data, bss, heap, and stack por-
must be used. tions of the program’s address space. In addition, four of
Table 1. The number of attacks halted when code is injected onto the data, bss, heap, and stack
segments
Injection Destination
Attack Type Hijack Type
Data BSS Heap Stack
Return address
Old base pointer
Function pointer as local variable
Buffer overflow on stack
Function pointer as parameter
Longjmp buffer as local variable
Longjmp buffer as function parameter
Function pointer
Buffer overflow on heap/bss
Longjmp buffer
Return address N/A N/A N/A
Old base pointer N/A N/A N/A N/A
Function pointer as local variable
Buffer overflow of pointers on stack
Function pointer as parameter
Longjmp buffer as local variable
Longjmp buffer as function parameter
Return address N/A N/A N/A
Old base pointer N/A N/A N/A N/A
Buffer overflow on heap/bss
Function pointer as variable
Longjmp buffer as variable
the testcases did not successfully execute an attack on our 100 Plain
Protected
unprotected system, and so have been labeled “N/A.” Table
1 shows the results of running the benchmark. The check- 80
marks indicate that the system successfully halted the at-
% of full speed
tack. As can be seen, the system was effective in preventing 60
all types of code injection attacks present in the benchmark.
The effectiveness of the system is due to the fact that no 40
matter what method of control-flow hijacking the bench-
mark uses, the processor is simply unable to fetch the in- 20
jected code.
0
apache2.2 gzip nbench unixbench
5.3. Performance 32KB pages 256 meg file worst case overall
A number of benchmarks, both applications and micro- Figure 4. Normalized performance for appli-
benchmarks, were used to test the performance of the sys- cations and benchmarks
tem. Our testing platform was a modest system, a Pentium
III 600Mhz with 384 MB of RAM and a 100MBit NIC.
When applicable, benchmarks were run 10 times and the 100 Plain
Protected
results averaged. Details of the configuration for the tests
80
are available in table 2. Each result has been normalized
with respect to the speed of the unprotected system.
% of full speed
60
Four benchmarks that we consider to be a reasonable as-
sessment of the system’s performance can be found in Fig-
40
ure 4. First, the Apache [25] webserver was run in a thread-
ing mode to serve a 32KB page (roughly the size of Purdue
20
University’s main [Link]). The ApacheBench program
was then run on another machine connected via the NIC to
0
determine the request throughput of the system as a whole. unixbench apache2.2
pipe ctxsw 1KB pages
The protected system achieved a little over 89% of the un-
protected system’s throughput. Next, gzip was used to com- Figure 5. Stress-testing the performance
press a 256 MB file, and the operation was timed. The pro- penalties due to context switching
tected system was found to run at 87% of full speed. Third,
mance degrades substantially due to the constant flushing
Table 2. Configuration information used for of the TLB. As can be seen in the graph, both are at or
performance evaluation below 50%. In addition, in Figure 6, we have a more thor-
ough set of Apache benchmarks demonstrating this same
Item Version Configuration phenomena, namely that for low page sizes the system con-
Slackware 10.2.0 Using Linux 2.6.13 text switches heavily and performance suffers, where as for
Apache 2.2.3 Worker mpm mode, set to spawn
larger page sizes that cause Apache to spend more time on
one process with threads
I/O as well as begin to saturate the system’s network link,
ApacheBench 2.0.41-dev -c3 -t 60 <url/file>
Unixbench 4.1.0 N/A the results become significantly better. These tests show
Nbench 2.2.2 N/A very poor performance, however we would like to note that
Gzip 1.3.3 Compress a 256 MB file. they are shown here to be indicative of the system’s worst
case performance under highly stressful (rather than nor-
Plain
Protected
mal) conditions.
100
Overall, the system’s performance is reasonable, in most
80 cases being between 80 and 90% of an unprotected system.
Moreover, if split memory was supported at the hardware
% of full speed
60 level as described in section 3.3, the overheard would be al-
most non-existent. Based on previous work [28], we also
40 have reason to believe that building the split memory sys-
tem on top of an architecture with a software loaded TLB,
20
such as SPARC, would also provide further performance
0 improvements.
1 2 4 8 16 32 64 128 256 512
Page size (kilobytes)
6. Limitations
Figure 6. Closer look into Apache perfor- There are a few limitations to our approach. First, when
mance an attack is stopped by our system the process involved will
crash. We offer no attempt at any sort of recovery. This
the nbench [26] suite was used to show the performance un- means an attacker can still exploit flaws to mount denial-of-
der a set of primarily computation based tests. The slowest service attacks. Second, as shown in other work [29], a split
test in the nbench system came in at just under 97%. Finally, memory architecture does not lend itself well to handling
the Unixbench [27] unix benchmarking suite was used as a self-modifying code. As such, self-modifying programs
micro-benchmark to test various aspects of the system’s per- cannot be protected using our technique. Next, this pro-
formance at tasks such as process creation, pipe throughput, tection scheme offers no protection against attacks which
filesystem throughput, etc. Here, the split memory system do not rely on executing code injected by the attacker. For
ran at 82% of normal speed. This result is slightly disap- example, modifying a function’s return address to point to a
pointing, however it can be easily explained by looking at different part of the original code pages will not be stopped
the specific test which performed poorly, which we do be- by this scheme. Fortunately, address space layout random-
low. As can be seen from these four benchmarks, the system ization [14] could be combined with our technique to help
has very reasonable performance under a variety of tasks. prevent this kind of attack. Along those same lines, non-
If we simply left our description of the system’s per- control-data attacks [23], wherein an attacker modifies a
formance to these four tests, some readers may object that program’s data in order to alter program flow, are also not
given the description of the system so far and the mention protected by this system. We have also not analyzed the sys-
in section 4.3 of the various sources of overhead, something tem’s functionality on programs that include dynamically
must be missing from our benchmarks. As such, two bench- loadable modules (such as DLL files on windows) but do
marks contrived to highlight the system’s weakness can be not anticipate that such programs would be difficult to sup-
found in Figure 5. First, one of the Unixbench testcases port.
called “pipe based context switching” is shown. This pri-
7. Conclusions
marily tests how quickly a system can context switch be-
tween two processes that are passing data between each In this paper, we present an architectural approach to pre-
other. The next test is Apache used to serve a 1KB page. In vent code injection attacks. Instead of maintaining the tradi-
this configuration, Apache will context switch heavily while tional single memory space containing both code and data,
serving requests. In both of these tests, context switching which is often exploited by code injection attacks, our ap-
is taken to an extreme and therefore our system’s perfor- proach creates a split memory that separates code and data
into different memory spaces. Consequently, in a system [12] H. H. Aiken and G. M. Hopper. The automatic sequence
protected by our approach, code injection attacks may re- controlled calculator. 1946. Reprinted in The Origins of
sult in the injection of attack code into the data space. How- Digital Computers Selected Papers, Second Edition, pages
ever, the attack code in the data space can not be fetched for 199–218, 1975.
execution as instructions are only retrieved from the code [13] [Link]: Securing memory. http:
//[Link]/publications/
space. We have implemented a Linux prototype on the x86
security/[Link]. Last accessed Dec 2006.
architecture, and experimental results show the system is ef- [14] Pax aslr documentation. [Link]
fective in preventing a wide range of code injection attacks net/docs/[Link]. Last accessed Dec 2006.
while incurring acceptable overhead. [15] S. Bhatkar, D. C. DuVarney, and R. Sekar. Address Obfus-
cation: An Efficient Approach to Combat a Broad Range of
8. Acknowledgments Memory Error Exploits. 12th USENIX Security, 2003.
We would like to thank Glenn Wurster as well as the [16] S. Bhatkar, R. Sekar, and D. C. DuVarney. Efficient Tech-
anonymous reviewers for their helpful comments and sug- niques for Comprehensive Protection from Memory Error
gestions. This work was supported in part by NSF Grants Exploits. 14th USENIX Security, 2005.
OCI-0438246, OCI-0504261, and CNS-0546173. [17] J. Xu, Z. Kalbarczyk, and R. K. Iyer. Transparent Runtime
Randomization for Security. In Proc. of 22nd Symposium on
References Reliable and Distributed Systems (SRDS) , Florence, Italy,
Oct. 2003.
[1] A detailed description of the data execution prevention
[18] E. G. Barrantes, D. H. Ackley, S. Forrest, T. S. Palmer,
(dep) feature in windows xp service pack 2, windows xp
D. Stefanovic, and D. D. Zovi. Randomized Instruction Set
tablet pc edition 2005, and windows server 2003. http:
Emulation to Disrupt Binary Code Injection Attacks. 10th
//[Link]/kb/875352. Last ac-
ACM CCS, 2003.
cessed Dec 2006.
[19] G. S. Kc, A. D. Keromytis, and V. Prevelakis. Counter-
[2] Pax pageexec documentation. [Link]
ing Code-Injection Attacks With Instruction-Set Random-
[Link]/docs/[Link]. Last
ization. 10th ACM CCS, 2003.
accessed Dec 2006.
[3] I. Corporation. IA-32 Intel Architecture Software Devel- [20] S. Sidiroglou, M. E. Locasto, S. W. Boyd, and A. D.
oper’s Manual Volume 3A: System Programming Guide, Keromytis. Building a Reactive Immune System for Soft-
Part 1. Intel Corp., 2006. Publication number 253668. ware Services. USENIX Annual Technical Conference,
[4] Buffer overflow attacks bypassing dep (nx/xd bits) - part 2 2005.
: Code injection. [Link] [21] L. Lam and T. Chiueh. Checking Array Bound Violation Us-
?p=13. Last accessed Dec 2006. ing Segmentation Hardware. Dependable Systems and Net-
[5] C. Cowan, C. Pu, D. Maier, J. Walpole, P. Bakke, S. Beat- works, 2005. DSN 2005. Proceedings. International Confer-
tie, A. Grier, P. Wagle, Q. Zhang, and H. Hinton. Stack- ence on, pages 388–397, 2005.
Guard: Automatic adaptive detection and prevention of [22] Wind river: Vxworks. [Link]
buffer-overflow attacks. In Proc. 7th USENIX Security Con- vxworks/. Last accessed Mar 2007.
ference, pages 63–78, San Antonio, Texas, jan 1998. [23] S. Chen, J. Xu, E. C. Sezer, P. Gauriar, and R. Iyer. Non-
[6] H. Etoh. Gcc extension for protecting applications from control-data attacks are realistic threats. In Proc. USENIX
stack-smashing attacks. [Link] Security Symposium, aug 2005.
projects/security/ssp/. Last accessed Dec 2006. [24] bochs: The open source ia-32 emulation project. http://
[7] Vendicator. Stack shield: A “stack smashing” technique [Link]/. Last accessed Dec 2006.
protection tool for linux. [Link] [25] The apache http server project. [Link]
com/sk/stackshield/[Link]. Last accessed [Link]/. Last accessed Dec 2006.
Dec 2006. [26] Linux/unix nbench. [Link]
[8] J. Wilander and M. Kamkar. A comparison of publicly avail- linux/[Link]. Last accessed Dec 2006.
able tools for dynamic buffer overflow prevention. In Pro- [27] Unixbench. [Link]
ceedings of the 10th Network and Distributed System Se- benchmarks/System/unixbench/. Last accessed
curity Symposium, pages 149–162, San Diego, California, Dec 2006.
February 2003. [28] G. Wurster. A generic attack on hashing-based software tam-
[9] J. von Neumann. First draft of a report on the edvac. 1945. per resistance. Master’s thesis, Carleton University, Canada,
Reprinted in The Origins of Digital Computers Selected Pa- Apr 2005.
pers, Second Edition, pages 355–364, 1975. [29] J. Giffin, M. Christodorescu, and L. Kruger. Strengthen-
[10] P. C. van Oorschot, A. Somayaji, and G. Wurster. Hardware- ing software self-checksumming via self-modifying code. In
assisted circumvention of self-hashing software tamper re- Proceedings of the 21st Annual Computer Security Applica-
sistance. IEEE Trans. Dependable Secur. Comput., 2(2):82– tions Conference (ACSAC 2005), pages 18–27, Tucson, AZ,
92, 2005. USA, Dec. 2005. Applied Computer Associates, IEEE.
[11] H. H. Aiken. Proposed automatic calculating machine.
1937. Reprinted in The Origins of Digital Computers Se-
lected Papers, Second Edition, pages 191–198, 1975.