Where are local stack variables stored in C? - c

I have the following program in C:
1 #include<stdio.h>
2
3 int main(void) {
4 int i=0;
5 for (int k=0; k<10; k++)
6 printf("Number: %d", k);
7 printf("Hello\n");
8 return 0;
9 }
When I run it in gdb it gives me a listing of all the registers, but I don't see the variable k in any of those reigsters. For example, in the below screenshot, I know k=4, but I don't see that value in any of the registers. Where would this number be stored then?

I know k=4, but I don't see that value in any of the registers. Where would this number be stored then?
If you optimized the program, the value would indeed likely be stored in a register (but the program will be much harder to debug).
Without optimization, the value is stored on stack (to be precise, given the disassembly, it is stored at location $rbp-8), and is loaded into a register by the very next instruction (the one before which you have stopped).
If you do stepi and look at the value of $rax, you will find it right there.
P.S. info locals will give you info about local variables.
Update:
What does stepi do?
It executes a single machine instruction, then stops. You can find this out by reading the manual, or by using help stepi GDB command.
What/were is $rbp-8? Could you please explain a bit more about what that is and how it works?
That is something that would be covered in every introductory x86 programming book or tutorial.
Briefly, current state of the program execution can be described as a series of linked activation records or "frames". On x86 without optimization, the $RBP register is usually used as a frame pointer register (i.e. it points to the current frame). Locals are stored at negative offsets from the frame pointer (here, k is stored at offset -8).

Related

what's the memory structure detail of a very simple process, and what's the detail of the crash [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I'm trying to trigger a stack-overflow crash.
most probably, the crash value is not 1684 in you sandbox. but you will find your own value when running.
I just want to know a way to figure these out.
The program is compiled in CentOS 8, using a GNU compiler.
#include <stdio.h>
int main() {
int a[3];
for(int i=4;i<100000;i++){
printf("a[%d]=%d\n", i, a[i]);
a[i] = 0;
}
return 0;
}
Then 1864 will be the first I value which will cause a crash.
I known stack-overflow will cause undefined behavior.
I just want to know the memory structure of this process. why a[1864] will cause a crash.
You crash when you access an address that's not mapped into your address space, which happens when you run off the top of the stack.
You can get some insight by looking at the address of a as well as your program's memory mappings, which you can see by reading /proc/self/maps from within your program. See https://godbolt.org/z/1e5bvK for an example. In my test, a is 0x7ffcd39df7a0, and the stack is mapped into the address range 7ffcd39c0000-7ffcd39e1000, beyond which is a gap of unmapped addresses. Subtracting, a is 6240 bytes from the top of the stack. Each int is 4 bytes, so that's 1560 ints. Thus accessing a[1560] should crash, and that's exactly what happened.
The addresses and offsets will change from run to run due to ASLR and variation in how the startup code uses the stack.
(Just to be clear for other readers: accessing beyond the end of the stack is what will cause an immediate segfault. But as soon as you write even one element beyond the declared size of your array, even though that write instruction may not itself segfault, it is still overwriting other data that is potentially important. That is very likely to cause some sort of misbehavior further along, maybe very soon or maybe much later in your program's execution. The result could eventually be a segfault, if you overwrote a pointer or a return address or something of the kind, or it could be something worse: data corruption, granting access to an intruder, blowing up the machine you're controlling, becoming Skynet, etc.)
why a[1864] will cause crash.
This particular value is by no means guaranteed or reliable. It will depend on the compiler and libc version, compilation flags, and a host of other variables.
In general, the memory layout looks like this (on machines where stack grows down, which is the majority of current machines):
<- inaccessible memory
0x...C000 <- top of stack (page-aligned)
0x...B... <- stack frame for _start
... <- other stack frames, such as __libc_start_main
0x...A... <- stack frame for main (where a[] is located).
You start overwriting stack from &a[0], and continue going until you hit the top of the stack and step into inaccessible memory. The number of ints between &a[0] and top of stack depends on the factors I listed.
Using a powerful debugging tool, I was able to find the source of your troubles.
lstrand#styx:~/tmp$ insure gcc -g -o simple simple.c
lstrand#styx:~/tmp$ ./simple >/dev/null
[simple.c:8] **READ_BAD_INDEX**
>> printf("a[%d]=%d\n", i, a[i]);
Reading array out of range: a[i]
Index used : 4
Valid range: 0 thru 2 (inclusive)
Stack trace where the error occurred:
main() simple.c, 8
[simple.c:9] **WRITE_BAD_INDEX**
>> a[i] = 0;
Writing array out of range: a[i]
Index used : 4
Valid range: 0 thru 2 (inclusive)
Stack trace where the error occurred:
main() simple.c, 9
**Memory corrupted. Program may crash!!**
**Insure trapped signal: 11**
Stack trace where the error occurred:
main() simple.c, 8
Segmentation violation
** TCA log data will be merged with tca.log **
Segmentation fault (core dumped)
lstrand#styx:~/tmp$
Edit:
To clarify, your program seems to assume that you can grow an array in C/C++ simply by writing past the end, as in JavaScript. There is also the subtle suggestion that you are coming from a Fortran background. The first past-the-end array location is at index 3, not 4.
In other words, your test program declares a fixed-size array on the stack. An array of size 3: with valid elements a[0], a[1], and a[2]. At the very first iteration of the loop, you are corrupting the stack.
The proper way to cause a stack overflow, as people on this site should well know, is to do something like this:
void recurse()
{
char buf[1024];
recurse();
}
int main()
{
recurse();
return 0;
}
On Linux/x86_64, this still produces SIGSEGV when you run out of stack, but on other platforms (e.g., Windows) you will get a stack overflow violation.
(gdb) r
Starting program: /home/lstrand/tmp/so
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400df2 in recurse () at so.c:5
5 recurse();
(gdb) where
#0 0x0000000000400df2 in recurse () at so.c:5
#1 0x0000000000400e79 in recurse () at so.c:5
#2 0x0000000000400e79 in recurse () at so.c:5
#3 0x0000000000400e79 in recurse () at so.c:5
[...]

Why does this for loop exit on some platforms and not on others?

I have recently started to learn C and I am taking a class with C as the subject. I'm currently playing around with loops and I'm running into some odd behaviour which I don't know how to explain.
#include <stdio.h>
int main()
{
int array[10],i;
for (i = 0; i <=10 ; i++)
{
array[i]=0; /*code should never terminate*/
printf("test \n");
}
printf("%d \n", sizeof(array)/sizeof(int));
return 0;
}
On my laptop running Ubuntu 14.04, this code does not break. It runs to completion. On my school's computer running CentOS 6.6, it also runs fine. On Windows 8.1, the loop never terminates.
What's even more strange is that when I edit the condition of the for loop to: i <= 11, the code only terminates on my laptop running Ubuntu. It never terminates in CentOS and Windows.
Can anyone explain what's happening in the memory and why the different OSes running the same code give different outcomes?
EDIT: I know the for loop goes out of bounds. I'm doing it intentionally. I just can't figure out how the behaviour can be different across different OSes and computers.
On my laptop running Ubuntu 14.04, this code does not break it runs to completion. On my school's computer running CentOS 6.6, it also runs fine. On Windows 8.1, the loop never terminates.
What is more strange is when I edit the conditional of the for loop to: i <= 11, the code only terminates on my laptop running Ubuntu. CentOS and Windows never terminates.
You've just discovered memory stomping. You can read more about it here: What is a “memory stomp”?
When you allocate int array[10],i;, those variables go into memory (specifically, they're allocated on the stack, which is a block of memory associated with the function). array[] and i are probably adjacent to each other in memory. It seems that on Windows 8.1, i is located at array[10]. On CentOS, i is located at array[11]. And on Ubuntu, it's in neither spot (maybe it's at array[-1]?).
Try adding these debugging statements to your code. You should notice that on iteration 10 or 11, array[i] points at i.
#include <stdio.h>
int main()
{
int array[10],i;
printf ("array: %p, &i: %p\n", array, &i);
printf ("i is offset %d from array\n", &i - array);
for (i = 0; i <=11 ; i++)
{
printf ("%d: Writing 0 to address %p\n", i, &array[i]);
array[i]=0; /*code should never terminate*/
}
return 0;
}
The bug lies between these pieces of code:
int array[10],i;
for (i = 0; i <=10 ; i++)
array[i]=0;
Since array only has 10 elements, in the last iteration array[10] = 0; is a buffer overflow. Buffer overflows are UNDEFINED BEHAVIOR, which means they might format your hard drive or cause demons to fly out of your nose.
It is fairly common for all stack variables to be laid out adjacent to each other. If i is located where array[10] writes to, then the UB will reset i to 0, thus leading to the unterminated loop.
To fix, change the loop condition to i < 10.
In what should be the last run of the loop,you write to array[10], but there are only 10 elements in the array, numbered 0 through 9. The C language specification says that this is “undefined behavior”. What this means in practice is that your program will attempt to write to the int-sized piece of memory that lies immediately after array in memory. What happens then depends on what does, in fact, lie there, and this depends not only on the operating system but more so on the compiler, on the compiler options (such as optimization settings), on the processor architecture, on the surrounding code, etc. It could even vary from execution to execution, e.g. due to address space randomization (probably not on this toy example, but it does happen in real life). Some possibilities include:
The location wasn't used. The loop terminates normally.
The location was used for something which happened to have the value 0. The loop terminates normally.
The location contained the function's return address. The loop terminates normally, but then the program crashes because it tries to jump to the address 0.
The location contains the variable i. The loop never terminates because i restarts at 0.
The location contains some other variable. The loop terminates normally, but then “interesting” things happen.
The location is an invalid memory address, e.g. because array is right at the end of a virtual memory page and the next page isn't mapped.
Demons fly out of your nose. Fortunately most computers lack the requisite hardware.
What you observed on Windows was that the compiler decided to place the variable i immediately after the array in memory, so array[10] = 0 ended up assigning to i. On Ubuntu and CentOS, the compiler didn't place i there. Almost all C implementations do group local variables in memory, on a memory stack, with one major exception: some local variables can be placed entirely in registers. Even if the variable is on the stack, the order of variables is determined by the compiler, and it may depend not only on the order in the source file but also on their types (to avoid wasting memory to alignment constraints that would leave holes), on their names, on some hash value used in a compiler's internal data structure, etc.
If you want to find out what your compiler decided to do, you can tell it to show you the assembler code. Oh, and learn to decipher assembler (it's easier than writing it). With GCC (and some other compilers, especially in the Unix world), pass the option -S to produce assembler code instead of a binary. For example, here's the assembler snippet for the loop from compiling with GCC on amd64 with the optimization option -O0 (no optimization), with comments added manually:
.L3:
movl -52(%rbp), %eax ; load i to register eax
cltq
movl $0, -48(%rbp,%rax,4) ; set array[i] to 0
movl $.LC0, %edi
call puts ; printf of a constant string was optimized to puts
addl $1, -52(%rbp) ; add 1 to i
.L2:
cmpl $10, -52(%rbp) ; compare i to 10
jle .L3
Here the variable i is 52 bytes below the top of the stack, while the array starts 48 bytes below the top of the stack. So this compiler happens to have placed i just before the array; you'd overwrite i if you happened to write to array[-1]. If you change array[i]=0 to array[9-i]=0, you'll get an infinite loop on this particular platform with these particular compiler options.
Now let's compile your program with gcc -O1.
movl $11, %ebx
.L3:
movl $.LC0, %edi
call puts
subl $1, %ebx
jne .L3
That's shorter! The compiler has not only declined to allocate a stack location for i — it's only ever stored in the register ebx — but it hasn't bothered to allocate any memory for array, or to generate code to set its elements, because it noticed that none of the elements are ever used.
To make this example more telling, let's ensure that the array assignments are performed by providing the compiler with something it isn't able to optimize away. An easy way to do that is to use the array from another file — because of separate compilation, the compiler doesn't know what happens in another file (unless it optimizes at link time, which gcc -O0 or gcc -O1 doesn't). Create a source file use_array.c containing
void use_array(int *array) {}
and change your source code to
#include <stdio.h>
void use_array(int *array);
int main()
{
int array[10],i;
for (i = 0; i <=10 ; i++)
{
array[i]=0; /*code should never terminate*/
printf("test \n");
}
printf("%zd \n", sizeof(array)/sizeof(int));
use_array(array);
return 0;
}
Compile with
gcc -c use_array.c
gcc -O1 -S -o with_use_array1.c with_use_array.c use_array.o
This time the assembler code looks like this:
movq %rsp, %rbx
leaq 44(%rsp), %rbp
.L3:
movl $0, (%rbx)
movl $.LC0, %edi
call puts
addq $4, %rbx
cmpq %rbp, %rbx
jne .L3
Now the array is on the stack, 44 bytes from the top. What about i? It doesn't appear anywhere! But the loop counter is kept in the register rbx. It's not exactly i, but the address of the array[i]. The compiler has decided that since the value of i was never used directly, there was no point in performing arithmetic to calculate where to store 0 during each run of the loop. Instead that address is the loop variable, and the arithmetic to determine the boundaries was performed partly at compile time (multiply 11 iterations by 4 bytes per array element to get 44) and partly at run time but once and for all before the loop starts (perform a subtraction to get the initial value).
Even on this very simple example, we've seen how changing compiler options (turn on optimization) or changing something minor (array[i] to array[9-i]) or even changing something apparently unrelated (adding the call to use_array) can make a significant difference to what the executable program generated by the compiler does. Compiler optimizations can do a lot of things that may appear unintuitive on programs that invoke undefined behavior. That's why undefined behavior is left completely undefined. When you deviate ever so slightly from the tracks, in real-world programs, it can be very hard to understand the relationship between what the code does and what it should have done, even for experienced programmers.
Unlike Java, C doesn't do array boundary check, i.e, there's no ArrayIndexOutOfBoundsException, the job of making sure the array index is valid is left to the programmer. Doing this on purpose leads to undefined behavior, anything could happen.
For an array:
int array[10]
indexes are only valid in the range 0 to 9. However, you are trying to:
for (i = 0; i <=10 ; i++)
access array[10] here, change the condition to i < 10
You have a bounds violation, and on the non-terminating platforms, I believe you are inadvertently setting i to zero at the end of the loop, so that it starts over again.
array[10] is invalid; it contains 10 elements, array[0] through array[9], and array[10] is the 11th. Your loop should be written to stop before 10, as follows:
for (i = 0; i < 10; i++)
Where array[10] lands is implementation-defined, and amusingly, on two of your platforms, it lands on i, which those platforms apparently lay out directly after array. i is set to zero and the loop continues forever. For your other platforms, i may be located before array, or array may have some padding after it.
You declare int array[10] means array has index 0 to 9 (total 10 integer elements it can hold). But the following loop,
for (i = 0; i <=10 ; i++)
will loop 0 to 10 means 11 time. Hence when i = 10 it will overflow the buffer and cause Undefined Behavior.
So try this:
for (i = 0; i < 10 ; i++)
or,
for (i = 0; i <= 9 ; i++)
It is undefined at array[10], and gives undefined behavior as described before. Think about it like this:
I have 10 items in my grocery cart. They are:
0: A box of cereal
1: Bread
2: Milk
3: Pie
4: Eggs
5: Cake
6: A 2 liter of soda
7: Salad
8: Burgers
9: Ice cream
cart[10] is undefined, and may give an out of bounds exception in some compilers. But, a lot apparently don't. The apparent 11th item is an item not actually in the cart. The 11th item is pointing to, what I'm going to call, a "poltergeist item." It never existed, but it was there.
Why some compilers give i an index of array[10] or array[11] or even array[-1] is because of your initialization/declaration statement. Some compilers interpret this as:
"Allocate 10 blocks of ints for array[10] and another int block. to make it easier, put them right next to each other."
Same as before, but move it a space or two away, so that array[10] doesn't point to i.
Do the same as before, but allocate i at array[-1] (because an index of an array can't, or shouldn't, be negative), or allocate it at a completely different spot because the OS can handle it, and it's safer.
Some compilers want things to go quicker, and some compilers prefer safety. It's all about the context. If I was developing an app for the ancient BREW OS (the OS of a basic phone), for example, it wouldn't care about safety. If I was developing for an iPhone 6, then it could run fast no matter what, so I would need an emphasis on safety. (Seriously, have you read Apple's App Store Guidelines, or read up on the development of Swift and Swift 2.0?)
Since you created an array of size 10, for loop condition should be as follows:
int array[10],i;
for (i = 0; i <10 ; i++)
{
Currently you are trying to access the unassigned location from the memory using array[10] and it is causing the undefined behavior. Undefined behavior means your program will behave undetermined fashion, so it can give different outputs in each execution.
Well, C compiler traditionally does not check for bounds. You can get a segmentation fault in case you refer to a location that does not "belong" to your process. However, the local variables are allocated on stack and depending on the way the memory is allocated, the area just beyond the array (array[10]) may belong to the process' memory segment. Thus, no segmentation fault trap is thrown and that is what you seem to experience. As others have pointed out, this is undefined behavior in C and your code may be considered erratic. Since you are learning C, you are better off getting into the habit of checking for bounds in your code.
Beyond the possibility that memory might be laid out so that an attempt to write to a[10] actually overwrites i, it would also be possible that an optimizing compiler might determine that the loop test cannot be reached with a value of i greater than ten without code having first accessed the non-existent array element a[10].
Since an attempt to access that element would be undefined behavior, the compiler would have no obligations with regard to what the program might do after that point. More specifically, since the compiler would have no obligation to generate code to check the loop index in any case where it might be greater than ten, it would have no obligation to generate code to check it at all; it could instead assume that the <=10 test will always yield true. Note that this would be true even if the code would read a[10] rather than writing it.
When you iterate past i==9 you assign zero to the 'array items' which are actually located past the array, so you're overwritnig some other data. Most probably you overwrite the i variable, which is located after a[]. That way you simply reset the i variable to zero and thus restart the loop.
You could discover that yourself if you printed i in the loop:
printf("test i=%d\n", i);
instead of just
printf("test \n");
Of course that result strongly depends on the memory allocation for your variables, which in turn depends on a compiler and its settings, so it is generally Undefined Behavior — that's why results on different machines or different operating systems or on different compilers may differ.
the error is in portion array[10] w/c is also address of i (int array[10],i;).
when array[10] is set to 0 then the i would be 0 w/c resets the entire loop and
causes the infinite loop.
there will be infinite loop if array[10] is between 0-10.the correct loop should be for (i = 0; i <10 ; i++) {...}
int array[10],i;
for (i = 0; i <=10 ; i++)
array[i]=0;
I will suggest something that I dint find above:
Try assigning array[i] = 20;
I guess this should terminate the code everywhere.. (given you keep i< =10 or ll)
If this runs you can firmly decide that the answers specified here already are correct [the answer related to memory stomping one for ex.]
There are two things wrong here. The int i is actually an array element, array[10], as seen on the stack. Because you have allowed the indexing to actually make array[10] = 0, the loop index, i, will never exceed 10. Make it for(i=0; i<10; i+=1).
i++ is, as K&R would call it, 'bad style'. It is incrementing i by the size of i, not 1. i++ is for pointer math and i+=1 is for algebra. While this depends on the compiler, it is not a good convention for portability.

How to find the address of variable in a memory hexdump?

While I was doing "Learn C The Hard Way" examples, I thought to myself:
I set int a = 10; but where does that value 10 actually? Can I access it manually from the outside while my program is running?
Here's a little C code snippet for demonstration purposes:
int main (int argc, char const* argv[]) {
int a = 10;
int b = 5;
int c = a + b;
return 0;
}
I opened up the The GNU Project Debugger (GDB) and entered:
break main
run
next 2
From what I understood 0x7fff5bffb04 is a memory address of int c. I then used hexdump -C /dev/mem system call to dump the entire memory into the terminal.
Now the question is where do I look for the variable c in this massive hex dump? My hope is that given the address 0x7fff5bffb04 I can find its value, which is 15. Also, bonus question, what does each column in hexdump -C represent? (I know the last column is ASCII representation)
I then used hexdump -C /dev/mem system call to dump the entire memory into the terminal.
Your hexdump dumped physical memory addresses. The address 0x7fff5bffb04 is a virtual address of the variable in the process you are debugging. It is mapped to some physical address, but you will not be able to find which without examining kernel mapping tables (as Mat already told you in a comment).
To examine virtual address space, use /proc/<pid>/mem (as Barmar already told you in a comment).
But this entire exercise is pointless, because you already can examine the virtual memory in GDB, and you are not going to see anything when you look at virtual memory that GDB didn't already show you much more conveniently [1].
[1] Except you could see GDB-inserted breakpoints, but you are not expected to understand that :-)
Firstly, there is no reason why the values would even exist in ram. More than Likly the machine code for this program simply has the values in cpu registers. You would have to have more bytes (try at least 512) and set them to a random value, which you could then search for in the memory dump.
You are far better of looking at the assembly code produced by the c compiler.

Frame Pointer / Program Counter / Array Overflow

I'm working on a practice problem set for C programming, and I've encountered this question. I'm not entirely sure what the question is asking for... given that xDEADBEEF is the halt instruction, but where do we inject deadbeef? why is the FP relevant in this question? thank you!
You’ve been assigned as the lead computer engineer on an interplanetary space mission to Jupiter. After several months in space, the ship’s main computer, a HAL9000, begins to malfunction and starts killing off the crew members. You’re the last crew member left alive and you need to trick the HAL 9000 computer into executing a HALT instruction. The good news is that you know that the machine code for a halt instruction is (in hexadecimal) xDEADBEEF (in decimal, this is -559,038,737). The bad news is that the only program that the HAL 9000 operating system is willing to actually run is chess. Fortunately, we have a detailed printout of the source code for the chess program (an excerpt of all the important parts is given below). Note that the getValues function reads a set of non-zero integers and places each number in sequence in the array x. The original author of the program obviously expected us to just provide two positive numbers, however there’s nothing in the program that would stop us from inputting three or more numbers. We also know that the stack will use memory locations between 8000 and 8999, and that the initial frame pointer value will be 8996.
void getValues(void) {
int x[2]; // array to hold input values
int k = 0;
int n;
n = readFromKeyboard(); // whatever you type on the keyboard is assigned to n
while (n != 0) {
x[k] = nextNumber;
k = k + 1;
n = readFromKeyboard();// whatever you type on the keyboard is assigned to n
}
/* the rest of this function is not relevant */
}
int main(void) {
int x;
getValues();
/* the rest of main is not relevant */
}
What sequence of numbers should you type on the keyboard to force the computer to execute a halt instruction?
SAMPLE Solution
One of the first three numbers should be -559038737. The fourth number must be the address of where 0xdeadbeef was placed into memory. Typical values for the 4th number are 8992 (0xdeadbeef is the second number) or 8991 (0xdeadbeef is first number).
What you want to do is overflow the input such that the program will return into a set of instructions you have overwritten at the return address.
The problem lies here:
int x[2]; // array to hold input values
By passing more than 3 values in, you can overwrite memory that you shouldn't. Explaining the sample example:
First input -559,038,737 puts xDEADBEEF in memory
Second input -559,038,737, why not.
Third number -559,038,737 can't hurt
Fourth number 8992 is the address we want the function to return into.
When the function call returns, it will return to the address overwrote the return address on the stack with (8992).
Here are some handy resources, and an excerpt:
The actual buffer-overflow hack work slike this:
Find code with overflow potential.
Put the code to be executed in the
buffer, i.e., on the stack.
Point the return address to the same code
you have just put on the stack.
Also a good book on the topic is "Hacking: The art of exploitation" if you like messing around with stacks and calling procedures.
In your case, it seems they are looking for you to encode your instructions in integers passed to the input.
An article on buffer overflowing
Hint: Read about buffer overflow exploits.

Stack Smashing attempt giving segfault

I am trying to do an example from the Smashing the Stack for Fun and Profit in C, but am kind of stuck at a point,
following is the code (I have a 64-bit machine with Ubuntu 64-bit):
int main()
{
int x;
x = 0;
func(1,2,3);
x = 1;
printf("x is : %d\n", x);
}
void func(int a, int b, int c)
{
char buffer[1];
int *ret;
ret = buffer + 17;
(*ret) += 7;
}
The above code works fine and on returning the x=1 line is not executed, but I can't understand the logic behind ret = buffer + 17;, shouldn't it be ret = buffer + 16; i.e, 8bytes for buffer and 8 for the saved base pointer on stack.
Secondly, my understanding is that char buffer[1] is taking 8 bytes (owing to 64-bit arch)
and if I increase this buffer to say buffer[2], still the same code should work fine, BUT this is not happening and it starts giving seg fault.
Regards,
Numan
'char' on every architecture I've used is 8 bits wide irrespective of whether it's an 8 bit micro, a 16 bit micro, a 32 bit PC, or a 64 bit new PC. Int, on the other hand, tends to be the word size.
The order which the locals are put on the stack can be implementation specific. My guess is that your compiler is putting "int *ret" on the stack before "char buffer1". So, to get to the return address, we have to go through "char buffer1" (1 byte), "int *ret" (8 bytes), and the saved base pointer (8 bytes) for a total of 17 bytes.
Here's a description of the stack frame on x86 64-bit:
http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-035-computer-language-engineering-spring-2010/projects/x86-64
Step through the disassembly in gdb (disassemble, stepi, nexti) and look at the registers at each step (info registers).
Here how you can step through disassembly:
gdb ./myprogram
break main
run
display/4i $eip
stepi
stepi
...
info registers
...
You should also know (you probably already do given that you got part of it working) that on many distros, the stack protector is enabled by default in gcc. You can manually disable it with -fno-stack-protector.
With a lot of this stack smashing stuff, your best friend is gdb. Since you're segfaulting already you're already writing memory you're not supposed to be (a good sign). A more effective way to do it right is to change the return address to somewhere else that's a valid address (e.g. to func's address or to some shellcode you've got). A great resource I'd recommend is the Shellcoder's Handbook, but since you're on a 64-bit architecture a lot of the examples need a bit of work to get going.
Aside from (or better yet, in addition to) running a debugger, you can also use the printf "%p" construct to print the addresses of your variables, e.g.:
printf("buf: %p\n", buffer); //&buffer[0] works too; &buffer works for an array
printf("ret: %p\n", &ret):
printf("a: %p\n", &a);
Printing the addresses of various things can give great insight into how your compiler/implementation is arranging things in the background. And you can do it directly from C code, too!
Consider taking a look at stealth's borrowed code chunk technique, if you're interested in x64 buffer overflow exploitation.

Resources