Buffer overflows and SPARC register windows (part 2)

In my previous blog entry I wrote:

So it does appear that register windows do offer some protection but I’ve never managed to demonstrate this with simple overflow code. If anybody has an example to back me up I’d be very interested to try it.

I had a few comments on the entry which spurred me on to writing an example which shows that overwriting the return value on the stack in memory isn’t always successful on SPARC. Here it is:

/*
* Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
* Use is subject to license terms.
*/
/*
* Example code showing how register windows on SPARC partially save
* you from buffer overflows. This is *no* defence as this is a
* contrived example :-)
*/
#include <stdio.h>
#include <dlfcn.h>
/*
* The SPARC stack layout means you can only overflow into the stack
* above (ie the caller's stack) - in this case bar(), which will have
* a return address into main().
*
* By compiling this and checking the assembler it was found that the
* local a[] array was 0x14 bytes below the frame pointer. The return
* address is 15 words above that. See /usr/include/sys/frame.h for
* details.
*
* The line where we zero the return address (a[i] = 0) should by all
* accounts cause the program to SEGV on exit. In fact it doesn't as
* the return address is still stored in a register window.
*
* If we want it to SEGV we have to make some function calls from
* within foo() to 'push' bar()'s frame out of the register window and
* have it spill onto the stack. Once we've done that then it won't be
* filled back into the register window until we call restore at the
* end of this function.
*
* To see this in action, simply uncomment the printf() before the
* return address is zeroed. There is sufficient function depth in
* the call to printf() to spill bar()'s frame.
*/
void
foo(int x, int y, int z)
{
Dl_info dli;	/* Used to extract symbol names */
int a[1], i;
/* printf("hello, world\n"); */
i = 20;		/* 0x14 (5 words) to the %fp + 15 words */
a[i] = 0;	/* Zero the return address */
printf("Contents of return address in memory are:\n");
if (dladdr((void *)a[i], &dli) <= 0) {
printf("%lx\n", a[i]);
} else {
printf("%s + 0x%lx\n", dli.dli_sname,
(ulong_t)a[i] - (ulong_t)dli.dli_saddr);
}
}
/*
* As explained above, we add an extra function call level to make it
* clearer that the return address is into our program (ie main()).
*/
void
bar()
{
/*
* Add some function arguments that are easy to spot if you
* fancy digging around in the stack.
*/
foo(0x1234, 0xcafebabe, 0x1234);
}
int
main(int argc, char **argv)
{
bar();
return (0);
}

Not the most elegant pieces of code but it’s cstyle clean … in other words, passes Sun’s coding style requirements 🙂

The main point is that it demonstrates that simply overwriting the return value in memory isn’t necessarily sufficient. The register window must have been spilled at some point before this can succeed.

Advertisements
Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: