0

When you add the /gs argument to microsoft's compiler, it would place a copy of args below the buffer variable.

enter image description here

So, what's the point of keeping a copy of arg?

1 Answer 1

2

The /GS option is a heuristic protection against buffer overflow -- some kinds of buffer overflows, and only if they overflow in the "usual way". Visual C supposes that any variable which "looks array-like" may be subject to array-like treatment, and thus possibly overflow. It wants to detect whether an overflow has occurred, and do that before returning from the function, because traditional buffer overflows try to overwrite the return address. To detect such an occurrence, the compiler writes a "cookie" value between the potentially overflowed variable, and the return address; and the code checks that the cookie is still there when it is about to exit the function.

The compiler will do that for all "local variables", and arguments are such local variables. The detection system works only as long as the cookie is between the return address and the variable, so this may imply moving things around, which is what you talk about.

Consider this:

struct foo { int a; int b; int c; int d; };

void f(struct foo arg)
{
    char buf[20];
    // ...
}

In this function, both buf and arg are considered "array-like" (arg is deemed array-like because it is larger than 8 bytes). Remember that when you pass a struct to a C function (the struct itself, not a pointer to a struct) then the function receives a copy of the structure to play with. On the stack, you would find things like this:

buf
return address
arg

(increasing addresses in top-to-bottom order; the x86 stack grows towards low addresses, so "up" in the schema above.)

The "/GS" code will want to add a cookie, and put that cookie between any array-like variable and the return address. Since bytes beyond (below) the return address belong to the caller, the function cannot play with them as much as it would want, so it has to do its job in the free stack space. Thus, the code will want this:

buf
arg (copy of the received structure)
cookie
return address
arg (structure passed as parameter by the caller)

This involves copying the arg structure upon function entry. Since the structure is, semantically, already a copy, that the function can play with at will, the copy does not change the observable behaviour of the function. With this copy, any "normal" overflow on arg will hit the cookie before it overwrites the return address, and thus be (hopefully) detected when the function exits.


I must of course point out that such a protection is effective only if all of the following hold true:

  • The overflow is on a local variable.
  • The overflow is toward high addresses (overflow from the array top, not the array bottom).
  • The overflow is contiguous.
  • The attacker did not guess the cookie value (otherwise he might overwrite the cookie with the same value).
  • Damage on variables between the overflowing buffer and the return address are not a problem.

So you should not put too much credit in the effectiveness of such a system.

1
  • Thanks Tom, I get it. Copy of arg has mainly the same purpose as reordering of local variables. That is, to make sure the local function runs even if buffer is overflowed. +1 for the copy does not change the observable behavior of the function
    – daisy
    Commented Sep 26, 2013 at 13:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .