next up previous
Next: Stackguard vs. Libsafe Up: Buffer Overflows and Other Previous: Using Stackguard

Using libsafe

Libsafe is a library released by AT&T that tries to protect you from stack smashing attacks involving common functions such as memcpy and sprintf. Libsafe contains replacement versions of these functions that try to detect at run time whether a buffer argument is on the stack, and if that is the case, make sure that writing to that buffer does not clobber the return address.

Libsafe currently works on Linux only, but the code should be portable to other platforms as long as it uses the GNU compiler gcc, because it relies heavily on some of its features.

Libsafe must be compiled as a shared library, and can be used by either linking it into an application explicitly, or by instructing the dynamic loader at run time to load the libsafe library (using the LD_PRELOAD environment variable, or the /etc/ld.so.preload file).

This sounds a lot like the magic silver bullet; so in order to understand the degree of security libsafe provides we have to take a closer look at how it's implemented. Remember that in my description of function call conventions on Linux/ix86 above, I said that functions usually fiddle with the ebp register? On Linux, this reguster is normally used as the frame pointer, i.e. it points to the stack frame. When entering a function, the frame pointer of the calling function is saved on the stack, and the current stack pointer is loaded into the frame pointer. Inside the function, local stack variables can then be referenced by addressing them relatively to this frame pointer.

If this sounds very complicated to you, don't let it worry you. What counts here are not so much the arcane compiler internals, we're more interested in the fact that this gives us a chain of frame pointers on the stack that looks like this:

TODO: bad figure

\epsffile{figures/bof6.eps}

That is, the frame pointer of the current function points to the stack location that holds the frame pointer of the calling function. Which in turn points to the stack location holding the frame pointer of the ``grandparent'' caller, etc, until it reaches the top of the stack.

The second noteworthy item is that the stack location imediately following the frame pointer is the return address.

If we combine these two, we have a rough outline of libsafe's mechanisms. Consider the libsafe implementation of memcpy, which is invoked with a destination buffer dst, a source buffer src, and the amount of data to copy, len. First, libsafe walks the chain of frame pointers until it finds one that is greater than the dst address: this is the stack frame of the function that declared the dst buffer. All it needs to do now is check whether dst+len exceeds the frame pointer address: if that's the case, then the memcpy operation is definitely a buffer overflow, and the program is terminated.

Looking at these facts, what conclusions can we draw from this?

One, libsafe protects certain functions from a overflowing buffer variables allocated on the stack, if that overflow would clobber the frame pointer (and possibly the return address). However, most stack smashing attacks described above work by overwriting the location of the return address; libsafe provides an effective protection against these.

Second, it is instructive to take a look at the things libsafe does not accomplish:

If you keep these items in mind, and don't lead yourself to believe that by using libsafe nothing bad can happen to you, then libsafe can be a useful tool.


next up previous
Next: Stackguard vs. Libsafe Up: Buffer Overflows and Other Previous: Using Stackguard
Olaf Kirch 2002-01-16