xxxxxxxxxx
41void b (int a0, int a1) {
2 int l0 = 0;
3 int l1 = 1;
4}
Can l0, l1, a0, a1
be allocated statically?
n
are there? xxxxxxxxxx
51void a (int n) {
2 if (n == 0) return;
3 a(n - 1);
4 printf("%d\n", n);
5}
xxxxxxxxxx
41void b (int n) {
2 int l = n * n;
3 c(l - 1);
4}
Scope
Lifetime
Activation: execution of a procedure
Activation Frame
Should we allocate activation frames from the heap?
malloc()
to create frame on procedure call?free()
on procedure return?Order of frame allocation and deallocation is special
Simple allocation for frames:
Questions
Stack of activation freames
Stack pointer
Current frame is the "top" of the stack
First activation is the "bottom" or "base" of the stack
Each frame is like a struct
Local variables
Arguments
Return address
Other save registers
Order of storage
Example:
xxxxxxxxxx
51void b (int a0, int a1) {
2 int i0 = 0;
3 int i1 = 1;
4 c();
5}
Frame is stored and accessed similarly to a struct
Example:
xxxxxxxxxx
41int i0 = 0;
2int i1 = 1;
3i0 = a0;
4i1 = a1;
l
in foo when it is active? xxxxxxxxxx
21void goo() { int x = 3; } goo();
2void foo() { int l; } foo();
xxxxxxxxxx
41int *foo() {
2 int l;
3 return &l;
4}
Code that executes just before procedure starts
Allocates activation frame and changes stack pointer
Possibly saves register values
Assigns values to arguments
Code that executes when procedure ends
Deallocates activation frame and restores stack pointer
Possibly restores some saved register values
Caller prologue (before call)
Callee prologue (start of function code)
Callee epilogue (end of function code)
Caller epilogue (after return from call)
xxxxxxxxxx
91void foo() {
2 b(0, 1);
3}
4
5void b(int a0, int a1){
6 int i0 = a0;
7 int i1 = a1;
8 c();
9}
Every program thread starts with a hidden procedure
The start procedure
three()
, assuming a call to foo()
?Number and size of arguments is statically determined
Value of arguments is dynamically determined
Compiler may choose to put arguments on the stack
Sometimes compiler chooses to use registers for arguments
Return value
Return address does not always need to be saved to the stack
Local variables sometimes don't need to be in the stack
xxxxxxxxxx
71int s;
2void foo() {
3 s = add(1, 2);
4}
5void add(int a, int b) {
6 return a + b
7}
Stack is managed by code generated by compiler
Caller prologue
Callee prologue
Callee epilogue
Caller epilogue
The return address is
Attacker's goal
Changing the return address
Hard parts
Making it easier
approximate return address value
start attack string with many copies of return address
follow it with long list of nop instructions
finally include the code for the worm
Works if return address guess within nop slide
Buffer boundary check
What if stack grew down?