Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order of variable declaration in asm x86?

Here the piece of code :

int main()
{

  char buffer[64];
  int check;
...

As you can see, check is declared AFTER buffer, so in the stack, we must have check ABOVE buffer in the stack right?

However, when I disassembly (x86) it with gdb, this is what I got :

--> check at 0xbffff4f8

--> buffer at 0xbffff4b8

My question : is there a specific order in the stack for local variable?

Also, I have to tell you that I tried the same thing on another computer (x86 too, same gcc compilation options, but different gdb version and linux distrib), and the order is not the same...:S

Thanks !

PS: if you want more details, please see the screenshot : (left with computer 1 and right with computer 2) enter image description here

like image 910
Duke Nukem Avatar asked Oct 18 '22 16:10

Duke Nukem


1 Answers

There is -fstack-protect in gcc to reorder stack variables, turned on by default in some Linux OS variant for almost 10 years, especially Ubuntu, Redhat, gentoo. Also default since gcc 4.8.3 ("4.9 and later enable -fstack-protector-strong.")

Ubuntu page about gcc defaults: https://wiki.ubuntu.com/ToolChain/CompilerFlags

Ubuntu-specific default compiler flags in the toolchain used to help provide additional security features to Ubuntu. ... Default Flags -fstack-protector ... First enabled in Ubuntu 6.10.

Ubuntu page about stack protection https://wiki.ubuntu.com/GccSsp

gcc 4.1 comes with SSP now, which is a nice technology to mitigate exploitability of many buffer overflows. ... SSP provides a technology to stop exploitability of this class of vulnerabilities by (1) reordering stack variables ... RedHat and gentoo are using SSP by default for years

This reordering requires several O(n^2) walks on all local variables of function which will make compilation slower for long functions, for example "Why does compiling over 100,000 lines of std::vector::push_back take a long time?" - https://stackoverflow.com/a/14034393/196561

Deferred allocation is forced when -fstack-protect is enabled (sometimes it needs to reorder all stack variables). .. cfgexpand.c

969 /* A subroutine of expand_one_var.  VAR is a variable that will be
970    allocated to the local stack frame.  Return true if we wish to
971    add VAR to STACK_VARS so that it will be coalesced with other
972    variables.  Return false to allocate VAR immediately.
973 
974    This function is used to reduce the number of variables considered
975    for coalescing, which reduces the size of the quadratic problem.  */
976 
977 static bool
978 defer_stack_allocation (tree var, bool toplevel)
980   /* If stack protection is enabled, *all* stack variables must be deferred,
981      so that we can re-order the strings to the top of the frame.  */

So, gcc will reorder all stack variables, and strings will be at top of the frame. Try -fno-stack-protector option to disable.

As usual, gcc's author don't document how -fstack-protect works in public documentation https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html:

-fstack-protector Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call alloca, and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits.

-fstack-protector-all Like -fstack-protector except that all functions are protected.

-fstack-protector-strong Like -fstack-protector but includes additional functions to be protected — those that have local array definitions, or have references to local frame addresses.

-fstack-protector-explicit Like -fstack-protector but only protects those functions which have the stack_protect attribute.

And the only documentation of array-before-locals I see is the real, best documentation: source code

https://gcc.gnu.org/viewcvs/gcc/branches/gcc-4_6-branch/gcc/cfgexpand.c?revision=175029&view=markup#l1526 - expand_used_vars()

1533          if (has_protected_decls)
1534            {
1535              /* Phase 1 contains only character arrays.  */
1536              expand_stack_vars (stack_protect_decl_phase_1);
1537    
1538              /* Phase 2 contains other kinds of arrays.  */
1539              if (flag_stack_protect == 2)
1540                expand_stack_vars (stack_protect_decl_phase_2);
1541            }
1542    
1543          expand_stack_vars (NULL);

phase 1 and phase 2 vars are separated by stack_protect_decl_phase() https://gcc.gnu.org/viewcvs/gcc/branches/gcc-4_6-branch/gcc/cfgexpand.c?revision=175029&view=markup#l1235

1235    /* Return nonzero if DECL should be segregated into the "vulnerable" upper
1236       part of the local stack frame.  Remember if we ever return nonzero for
1237       any variable in this function.  The return value is the phase number in
1238       which the variable should be allocated.  */
1239    
1240    static int
1241    stack_protect_decl_phase (tree decl)
 ...
1243      unsigned int bits = stack_protect_classify_type (TREE_TYPE (decl));
 ...
1249      if (flag_stack_protect == 2)
1250        {
1251          if ((bits & (SPCT_HAS_SMALL_CHAR_ARRAY | SPCT_HAS_LARGE_CHAR_ARRAY))
1252              && !(bits & SPCT_HAS_AGGREGATE))
1253            ret = 1;
1254          else if (bits & SPCT_HAS_ARRAY)
1255            ret = 2;
1256        }

stack_protect_classify_type will return bits HAS_ARRAY with HAS_*_CHAR_ARRAY only for arrays of char (both char, unsigned char and signed char)

like image 75
osgx Avatar answered Oct 27 '22 19:10

osgx