next up previous
Next: Conclusions Up: Protecting from stack-smashing attacks Previous: Stack Protection Method



The result of penetration tests are shown Figure8. It illustrates the application name which has a known buffer overflow vulnerability, its description, the result of exploits without any protection, and the result of exploits with our protection. Their first three are exploited by attacks on the return address and the last one is exploited by an attack on the arguments which point to a structure with a function pointer.

This is not a comprehensive exploit list, but enough to verify the protection method works well. It showed that all tests terminated with a message that a stack-smashing attack had been detected, they didn't invoke a root shell, and they didn't terminated abnormally.

Figure 8: Penetration Resistance
\begin{figure*}\centering \begin{tabular}{\vert l\vert l\vert l\vert l\vert} \hl... hardware & root shell
& terminated  \hline
\end{tabular} \end{figure*}

Performance Overhead

The guard mechanism imposes an additional cost in program execution. The overhead was defined by Crispin[3] as follows: it is the ratio of the CPU time of a guarded function call per the base cost of the function call. The overhead of our system varies according to the presence of local character arrays. It is obvious that our optimized method has no overhead in the applications that have no local character arrays, such as integer sorting programs, linear programming systems, and so on.

The insertion overhead for a guard variable depends on the size of the function. Program 9 is introduced to measure the upper bound of the overhead. It is written to be an actual string program and to be as small as possible.

Figure 9: Program to Estimate the Upper Bound of the Overhead
\begin{figure}\centering\begin{verbatim}int test()
char buf[128];
return strncmp(buf, ''1234'', 4);
}\end{verbatim} \end{figure}

The experiment was performed on a 600 MHz Pentium III with 512 K of level 2 cache, and 256 M of main memory. The time is based on 50,000,000 runs and is given in seconds.

original our method overhead
run time run time (%)
4.67 5.05 8%

It shows an 8% overhead on function calls, which should be the upper bound on the real costs of running programs under this protection system. The overall overhead of guarded programs varies with how many functions are called that have character array. Figure 10 shows a program's name, its description, the number of functions declared, and the number of functions used with character arrays. In most cases, the usage of a character array is less than 10% of the functions. It isn't the same as the ratio of the number of functions executed, but there is a some correlation between them.

Figure 10: The Usage Counts for Character Buffers
\begin{figure*}\centering \begin{tabular}{\vert l\vert l\vert r\vert r\vert r\ve...
...glibc 2.1.3 &Library & 719 & 106 & 14.7\%  \hline
\end{tabular} \end{figure*}

The copy overhead of an argument is more expensive than the insertion overhead for a guard variable, if the argument is a structure that contains a character array or a pointer variable. Figure 11 shows the number of functions which arguments contain a character array, and the number of functions which arguments contain a pointer variable for each program. There are no structures being passed as parameters to function calls. Therefore, for each pointer argument, the overhead is at most one copy operation from an argument to a local variable.

Figure 11: The Number of Copying Overhead Operations for Arguments
\begin{figure}\centering \begin{tabular}{\vert l\vert r\vert r\vert}\hline
...gcs 1.1.2 & 0 & 0 \\
glibc 2.1.3 & 0 & 0  \hline
\end{tabular} \end{figure}

Figure 12 shows the run-time cost of three real-world applications to compare the overhead of our method, libsafe, and StackGuard. The applications are perlbench (a CPU-bound program) mesuring the time of several operations [7], ctags (an I/O-bound program) indexing egcs-1.1.2 directory, and imapd transmitting $100$ email messages of size two kilobyte each. The programs are selected from programs that mainly process string operations to illustrate the upper bound of the overall overhead. The execution times are based on 100 runs with associated 95% confidence intervals. The times are elapsed times using /bin/time. By looking at Figure 13 showing the comparison of each system's performance overhead, it can be seen that our method is the most efficient.

Figure 12: Mean Execution Times

Figure 13: Comparison of performance overhead
\begin{figure}\centering \begin{tabular}{\vert l\vert r\vert r\vert r\vert} \hli...
...\% & 0\% \\
StackGuard & 8\% & 3\% & 0\%  \hline
\end{tabular} \end{figure}

next up previous
Next: Conclusions Up: Protecting from stack-smashing attacks Previous: Stack Protection Method