Buffer overflows
Buffer overflows
This is just a thought on buffer overflows after reading the recent article on Slashdot/OSNews about the x86-64 'executable' bit preventing non-code pages from being executed.
If the stack were to grow upwards instead of downwards, buffer overflows would not be able to overwrite the return address of the stack (since any overflowing data gets written upwards, although the return address would be below the stack pointer). Since buffer overflows cannot overwrite the return address, no malicious code would be jumped to in the event of your typical buffer overflow exploit.
The only implication I can think of would be that the optimal organization of program memory like this:
[ code ] [ data ] --> free space <-- [ stack ]
would no longer be possible. What do you think of this? I have a feeling that I'm overlooking an important factor here, but I can't discern what it could be...
If the stack were to grow upwards instead of downwards, buffer overflows would not be able to overwrite the return address of the stack (since any overflowing data gets written upwards, although the return address would be below the stack pointer). Since buffer overflows cannot overwrite the return address, no malicious code would be jumped to in the event of your typical buffer overflow exploit.
The only implication I can think of would be that the optimal organization of program memory like this:
[ code ] [ data ] --> free space <-- [ stack ]
would no longer be possible. What do you think of this? I have a feeling that I'm overlooking an important factor here, but I can't discern what it could be...
Re:Buffer overflows
Just a wild idea, why not make it work this way instead:nullify wrote:
The only implication I can think of would be that the optimal organization of program memory like this:
Code: Select all
<code> <stack> --> freespace <-- <data>
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Buffer overflows
it's not practical to change the direction of the stack's expansion because it is defined by the CPU instructions like Push/Pop Call/Ret Enter/Leave, etc.
The same way, having an expand-down heap isn't practical either because none of the realloc() calls are likely to succeed ...
The way you can protect against buffer overflow on a IA-32 architecture is to keep the code segment as small as your .text section is ... The protection isn't perfect though: there's no real need to write *code* to invoke "exec("sh")": you just have to replace the return address by the address of exec() and the word before that return address (thus the last argument of the function) with the pointer to the 'sh' string ...
Note also that the relocation information shipped in your executable lists all the 'regular' calls to library functions, so for 'sensible' functions calls (like exec), it could be theorically possible to cross-check that the 'return address' for that call comes from something that was foreseen at compile-time ...
The same way, having an expand-down heap isn't practical either because none of the realloc() calls are likely to succeed ...
The way you can protect against buffer overflow on a IA-32 architecture is to keep the code segment as small as your .text section is ... The protection isn't perfect though: there's no real need to write *code* to invoke "exec("sh")": you just have to replace the return address by the address of exec() and the word before that return address (thus the last argument of the function) with the pointer to the 'sh' string ...
Note also that the relocation information shipped in your executable lists all the 'regular' calls to library functions, so for 'sensible' functions calls (like exec), it could be theorically possible to cross-check that the 'return address' for that call comes from something that was foreseen at compile-time ...
Re:Buffer overflows
I still don't see why people are so incredibly set against levering the segmentation mechanism since, if using paging, they are already using it.
Split code, data, stack into three segments with appropriate permissions and individual ranges in the address space (With tweaking of segment base address it can look like a flat address space anyway). There is no extra overhead because you have to switch segments anyway in a context switch, and if using paging then you're already going through the segmentation translation step.
Ok, so it's not portable, but the chances of you being able to take your Mem Manager and dump it unchanged onto a different architecture are slim anyway. So long as you set things up so that everything else believes the address space is flat you limit the bits that aren't portable anyway.
Split code, data, stack into three segments with appropriate permissions and individual ranges in the address space (With tweaking of segment base address it can look like a flat address space anyway). There is no extra overhead because you have to switch segments anyway in a context switch, and if using paging then you're already going through the segmentation translation step.
Ok, so it's not portable, but the chances of you being able to take your Mem Manager and dump it unchanged onto a different architecture are slim anyway. So long as you set things up so that everything else believes the address space is flat you limit the bits that aren't portable anyway.
Re:Buffer overflows
Yes, this is the big problem. Had Intel considered the implications of this beforehand, would a "stack expanding upwards" implementation have done the trick to minimize damage of buffer overflows on today's x86 systems?Pype.Clicker wrote:it's not practical to change the direction of the stack's expansion because it is defined by the CPU instructions like Push/Pop Call/Ret Enter/Leave, etc.
On the other hand, I suppose Intel would be expecting OS writers to make use of its segmentation's executable flag to protect against arbitrary memory executed as code (as Curufir states).
This is an interesting idea, although I don't see how the address space could be made to look flat without segments spanning the entire 4GB. Perhaps you could elaborate on this?Curufir wrote:Split code, data, stack into three segments with appropriate permissions and individual ranges in the address space (With tweaking of segment base address it can look like a flat address space anyway).
Re:Buffer overflows
Egnullify wrote: This is an interesting idea, although I don't see how the address space could be made to look flat without segments spanning the entire 4GB. Perhaps you could elaborate on this?
Code segment size 1Gb, Base 0
Data segment size 1Gb, Base 1gb
Stack segment size 1Gb, Base 2gb
Then the segmentation hardware handles logical -> linear address translation (Eg 0x100 in the data segment would produce a linear address of 1Gb+0x100 in the above example) then you head through paging for the linear -> physical translation. Admittedly in the example scheme the 4Gb address space has effectively been reduced to 1Gb (I left 1Gb for the kernel), but as far as anything outside the kernel knows the address space is just a flat 1Gb space. Doing this increases complexity in page management etc, but ensures absolutely that code/data is never going to be at risk from stack operations.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Buffer overflows
if you want to execute a C program, you *must* have DS.base == SS.base.
Just consider how
would be assembled: in do_something, the compiler has no clue about what's on the stack and what's on the heap. so the same referencing code should work for both ...
Also, having CS.base == DS.base is not a problem for security, as long as the pages that hold the code are read-only...
Just consider how
Code: Select all
myFunction()
{
structure *on_the_heap=malloc(sizeof(structure));
structure on_the_stack;
do_something(on_the_heap, &on_the_stack);
do_something(&on_the_stack,on_the_heap);
}
do_something(structure *x, structure *y)
{
x->field=y->field;
y->otherfield=x->otherfield;
}
Also, having CS.base == DS.base is not a problem for security, as long as the pages that hold the code are read-only...
Re:Buffer overflows
And all this because coders are too lazy / dumb / unconcerned to use the "n" variants of functions... snprintf() instead of sprintf(), strncmp() instead of strcmp()...
Every good solution is obvious once you've found it.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Buffer overflows
fortunately enough, dumb programmers can now code in sandboxed environments ;D