View
221
Download
0
Tags:
Embed Size (px)
Citation preview
Pointers and Memory Allocation – part 2-L. Grewe
2
Objectives
•Pointers to Functions
•More potential Issues with Pointers - advanced
Pointers to Functions(this is NOT passing pointers as arguments to functions!!!
This is a pointer pointing to a function itself!!!!!!)
A pointer can also store the address of a function
A function name is the address in memory of the start of the function
Function pointers can be
– Passed to a function
– Returned to functions
– Stored in arrays
– Assigned to other function pointers
An Array of Pointers to Functions#include <stdio.h>
void fun1(void);
void fun2(void);
void fun3(void);
int main(){
/*declare an array of pointers to functions*/
void (*array[3])(void) = {fun1,fun2,fun3};
int i;
for (i=0;i<3;i++)
(*array[i])();/*make a function call*/
return 0; /*output: 1st 2nd 3rd */
}
void fun1(void){printf("1st ");}
void fun2(void){printf("2nd ");}
void fun3(void){printf("3rd ");}
Alternative Code#include <stdio.h>
void fun1(void);
void fun2(void);
void fun3(void);
int main(){
void (*array[3])(void) = {fun1,fun2,fun3};
/*declare an array of pointers to functions*/
int i;
for (i=0;i<3;i++)
array[i](); /*or (*array[i])(); */
return 0; /*output: 1st 2nd 3rd */
}
void fun1(void){printf("1st ");}
void fun2(void){printf("2nd ");}
void fun3(void){printf("3rd ");}
Pointers to Functions
void (*array[3])(void)={fun1,fun2,fun3};
“array” is an array of 3 pointers to 3 functions with no arguments and return type void
(*array[i])(void); //THESE ARE THE SAME
array[i]();
An Array of Pointers to Functions 2#include <stdio.h>
int fun1(int);
int fun2(int);
int fun3(int);
int main(){
int (*array[3])(int) = {fun1,fun2,fun3};
int i;
for (i=0;i<3;i++)
printf("%d ",(*array[i])(i)); /*1 3 5*/
/* printf("%d ", array[i](i)); */
return 0;
}
int fun1(int x){return x+1;}
int fun2(int x){return x+2;}
int fun3(int x){return x+3;}
Just some new functionsfun1, fun2, fun3With parameters
Pointers to Functions 2
int (*array[3])(int)={fun1,fun2,fun3};
“array” is an array of 3 pointers to 3 functions with one int argument and return type int
(*array[i])(i);
array[i](i);
The function is dereferenced and integer “i” is passed as an argument to the function
Pointers to functions: WHY?
9
They allow for a certain amount of polymorphism:
– “poly” (many) + “morph” (shape)
– A polymorphic language can handle a range of different data types (“shapes”?) with a single statement
Pointers to functions: safety concerns
10
What if uninitialized function pointer value is accessed?
– Safest outcome: memory error, and program is terminated
– But what if the “garbage” value is a valid address?
• Worst case: address contains program instruction –execution continues, with random results
• Hard to trace the cause of the erroneous behavior
Pointer and Memory Failures re-visited and Advanced
11
Memory-related bugs
Dereferencing bad pointers
Reading uninitialized memory
Overwriting memory
Referencing nonexistent variables
Freeing blocks multiple times
Referencing freed blocks
Failing to free blocks
Reading uninitialized memoryAssuming that heap data is initialized to zero
/* return y = Ax */int *matvec(int **A, int *x) { int *y = malloc(N*sizeof(int)); int i, j;
for (i=0; i<N; i++) for (j=0; j<N; j++) y[i] += A[i][j]*x[j]; return y;}
Overwriting memory
Allocating the (possibly) wrong sized object
int **p;
p = malloc(N*sizeof(int));
for (i=0; i<N; i++) { p[i] = malloc(M*sizeof(int));}
Overwriting memory
Off-by-one
int **p;
p = malloc(N*sizeof(int *));
for (i=0; i<=N; i++) { p[i] = malloc(M*sizeof(int));}
Overwriting memoryNot checking the max string size
char s[8];int i;
gets(s); /* reads “123456789” from stdin */
Basis for classic buffer overflow attacks• 1988 Internet worm• modern attacks on Web servers• AOL/Microsoft IM war
Buffer overflow attacksDescription of hole:
– Servers that use C library routines such as gets() that don’t check input sizes when they write into buffers on the stack.
– The following description is based on the IA32 stack conventions. The details will depend on how the stack is organized, which varies between compilers and machines
64 bytesfor buffer
return addr
Saved regs. and Local vars
proc a() { b(); # call procedure b}
proc b() { char buffer[64]; # alloc 64 bytes on stack gets(buffer); # read STDIN line into buf}
Stack frame
for proc a
%ebpStack frame
for proc b
%ebp
increasingaddrs
Buffer overflow attacksVulnerability stems from possibility of the gets() routine overwriting the return address
for b.
– overwrite stack frame with
• machine code instruction(s) that execs a shell
• a bogus return address to the instruction
proc a() { b(); # call procedure b} # b should return here, instead it # returns to an address inside of buffer
proc b() { char buffer[64]; # alloc 64 bytes on stack gets(buffer); # read STDIN line to buffer}
Stack region overwritten by gets(buffer)
exec(“/bin/sh”)
New return addr
Saved regs. and Local vars
Stack frame
for proc a
paddingStack frame
for proc b
%ebp
incraddrs
Overwriting memoryReferencing a pointer instead of the object it points to
int *BinheapDelete(int **binheap, int *size) { int *packet; packet = binheap[0]; binheap[0] = binheap[*size - 1]; *size--; Heapify(binheap, *size, 0); return(packet);}
Overwriting memory
Misunderstanding pointer arithmetic
int *search(int *p, int val) { while (*p && *p != val) p += sizeof(int);//you should increment //array by 1 only //you are missing elements
return p;}
Referencing nonexistent variables
Forgetting that local variables disappear when a function returns
int *foo () { int val; return &val;}
Freeing blocks multiple times
x = malloc(N*sizeof(int));// ….<manipulate x>free(x);
y = malloc(M*sizeof(int));//…<manipulate y>free(x);
Referencing freed blocks
x = malloc(N*sizeof(int));//….<manipulate x>free(x);...
y = malloc(M*sizeof(int));for (i=0; i<M; i++) y[i] = x[i]++;
Failing to free blocks(memory leaks)
Run out of memory as you repeatedly call function …foo(), foo(), foo() ….
foo() { int *x = malloc(N*sizeof(int)); ... return;}
Failing to free blocks(memory leaks)
Freeing only part of a data structure
struct list { int val; struct list *next;};
foo() { struct list *head = malloc(sizeof(struct list)); head->val = 0; head->next = NULL; <create and manipulate the rest of the list> ... free(head); //not freeing other part of list created. return;}
Dealing with memory bugs
Conventional debugger (gdb)
– good for finding bad pointer dereferences
– hard to detect the other memory bugs
Debugging malloc
– wrapper around conventional malloc
– detects memory bugs at malloc and free boundaries
• memory overwrites that corrupt heap structures
• some instances of freeing blocks multiple times
• memory leaks
– Cannot detect all memory bugs
• overwrites into the middle of allocated blocks
• freeing block twice that has been reallocated in the interim
• referencing freed blocks
Dealing with memory bugs (cont.)
Binary translator (i.e. Purify-IBM product)
– powerful debugging and analysis technique
– rewrites text section of executable object file
– can detect all errors as debugging malloc
– can also check each individual reference at runtime
• bad pointers
• overwriting
• referencing outside of allocated block
Garbage collection– let the system free blocks instead of the programmer.
Pointer and Memory Allocation Tips
• Use Pointers only when you need to.
• Pointer problems can be the most difficult to debug
• Whenever you allocate memory – think immediately where/if you need to deallocate it
• Use Data Structures implemented in C++ Collections already provided when possible –as they are written to take care of potential problems like class copying we saw previously.
28