13

If you had a very complex and important function in C that you wanted to protect, would it be worth it to put a 65K buffer at the top of the stack to protect from buffer overflows? You would put your important buffers below the 65K buffer so that the stack looks like this:

[Saved EIP] // higher adddresses
[   ...   ]
[   65K   ]
[   ...   ] // other stack variables and buffers

This way if there was a buffer overflow below the 65K, it would overflow into the 65K buffer and would not reach the stack variables.

Is this a feasible defence against buffer overflows?

John
  • 413
  • 4
  • 13
  • 5
    The much safer solution is to put a 4K unallocated page after the buffer. Even if an attacker sent a Gigabyte, the first byte to go to the unallocated page will trigger a CPU fault and the OS will abort the process. Doesn't work with buffers on the stack, though. – MSalters Jun 22 '14 at 22:59
  • @MSalters: Actually it kinda does. You could make your function go through an extra wrapper function that puts a 3x page-size buffer on the stack, and have the wrapped function temporarily `mprotect` the middle page of this buffer with `PROT_NONE` then restore it to `PROT_READ|PROT_WRITE` before returning. – R.. GitHub STOP HELPING ICE Jun 22 '14 at 23:26

4 Answers4

26

No. Most likely, you got that 64k limit from the Heartbleed bug, hovewer it is purely because in HTTPS Heartbeats the length field was 16 bits long. It doesn't mean that in your case your software will not have a buffer overflow reaching far further. So while yeah, this could add a tiny bit of security, you must always assume that buffer overflows can affect your whole address space, both after the buffer and even before it.

MaxSem
  • 1,921
  • 1
  • 13
  • 7
  • 3
    Some modern compilers include [Buffer overflow protection](http://en.wikipedia.org/wiki/Buffer_overflow_protection), but they are limited in their scope. Canaries are used to detect writes outside a buffer's bounds, and bounds checking can be used (with some possible performance degradation) to prevent read overflows. That said, an entire class of bugs exist solely because programmers didn't do proper bounds checking themselves, usually because of a poor implementation of some function that was poorly designed. – phyrfox Jun 22 '14 at 08:42
19

The best advise for avoiding buffer overflow bugs in C is to not use C in the first place. The design philosophy of the language was that whenever they had to choose between efficiency and safety, they picked efficiency. The result is a language which can be used to write very fast and memory-conserving programs, but unfortunately at the expense of security. That means that it is risky to use it in any context where security is relevant, and in todays networked world that's virtually everywhere.

The second best advise is to not use functions which write to buffers but have no maximum length argument, like the following:

  • gets
  • scanf
  • sprintf
  • strcat
  • strcpy

Most of these have alternative versions with an n in the name which have an additional parameter for the maximum length. Use these and make sure that this parameter is never more than the length of the string buffer you pass. But keep in mind that you can still build a buffer overflow vulnerability by accidentally passing a length which is smaller than the target buffer.

Philipp
  • 48,867
  • 8
  • 127
  • 157
  • 8
    The "n" variants of the string functions are harder to use properly than they should be: you need to make sure that "n" is less than the free space in the destination buffer, and they don't guarantee that the end result will be a null-terminated string. If your platform provides them, use the "l" variants instead (eg. `strlcat()`). – Mark Jun 22 '14 at 05:47
  • 1
    `strncpy` creates a null-*padded* string, not a null-*terminated* string. – dan04 Jun 22 '14 at 21:37
  • I agree with @Mark for the most part, except `snprintf`, which is trivial to use correctly. If you do all your string handling in C with `snprintf` and nothing else, you're 95% of the way to making string-based security flaws a thing of the past. `strcpy`/`strcat` should just be dropped/forgotten, not replaced with "n" or "l" variants. – R.. GitHub STOP HELPING ICE Jun 22 '14 at 23:17
  • 2
    You surely mean a *maximum* length argument not a *minimum* length argument – CodesInChaos Jun 23 '14 at 08:02
7

One of the known security approaches is to put a "canary" on the stack that would be damaged (modified) by the buffer overflow, if any would occur. It need not be so big, and it must be initialized with some values that attacking program is unlikely to know.

After doing a IO, you can then check if the contents of this buffer have not been touched by the buffer overflow.

However this protection only works if you are sure you will get the execution control back after the overflow. If the overrun damages the return address that the IO function uses, you may not get the possibility to do the canary check.

However with the typical down-growing stack where return address is the last entry with smaller address, and the buffer overruns up (so away from the return address), the canary should work:

  (higher address values)
  other stack data
  canary
  buffer that overruns up
  return address from the IO routine
  (lower address values)

You should be able to return, check the canary and if it is dead, terminate the program in a fastest way possible.

h22
  • 901
  • 6
  • 10
  • 5
    Note, too, that if the canary is dead, the only safe option is to abort the program. You don't know what else has been modified at that point. – cHao Jun 22 '14 at 09:16
  • Absolutely agree. – h22 Jun 22 '14 at 09:18
  • With that said, the opposite of reserving extra memory would actually be the correct thing to do. Unmap the page above the current stack frame, so you get a trap if someone writes over it. Not an approach without problems, of course. – Damon Jun 22 '14 at 14:27
  • @cHao: +1 your comment is the critical ingredient that's missing in actual implementations like the stock `__stack_chk_fail` in glibc and GCC's libssp. These implementations actually do quite a lot of useless and dangerous introspection to report the crash state after the bad canary was detected, and it's trivial to write your overflows in such a way that you control what the introspection code does and thereby bypass ssp completely. The only safe thing to do is immediately execute an illegal instruction; this is what the compiler should do for SSP, rather than the function call. – R.. GitHub STOP HELPING ICE Jun 22 '14 at 23:23
  • @R.. any example of working exploit? It seems writing current `$pc` to stack overwriting part of the overflowed data would do no more harm after the overflow has occured. The function you call doesn't rely on any locals of its callers. – Ruslan Jun 23 '14 at 13:01
1

Some buffer-counters are 16 bits which means the 65k would protect; however most of the time that is not the case and 65k is a lot of stack space to waste.

In general, a poorly thought out idea.

Joshua
  • 1,090
  • 7
  • 11