280 likes | 309 Views
Learn advanced concepts of using function pointers in C programming to create dynamic and flexible function calls. Understand pointer memory allocation and pointers to functions. Explore potential issues and safety concerns with function pointers.
E N D
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}; • inti; • 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*/ • inti; • 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]();
Just some new functions fun1, fun2, fun3 With parameters • 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}; • inti; • for (i=0;i<3;i++) • printf("%d ",(*array[i])(i)); /*1 3 5*/ • /* printf("%d ", array[i](i)); */ • return 0; • } • int fun1(intx){return x+1;} • int fun2(intx){return x+2;} • int fun3(intx){return x+3;}
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? • 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 • 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
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 memory • Assuming 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 memory • Not 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 attacks • Description 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 %ebp Saved regs. and Local vars Stack frame for proc a proc a() { b(); # call procedure b } increasing addrs return addr %ebp proc b() { char buffer[64]; # alloc 64 bytes on stack gets(buffer); # read STDIN line into buf } Stack frame for proc b 64 bytes for buffer
Buffer overflow attacks • Vulnerability 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 %ebp Saved regs. and Local vars Stack frame for proc a incr addrs New return addr proc b() { char buffer[64]; # alloc 64 bytes on stack gets(buffer); # read STDIN line to buffer } padding Stack frame for proc b exec(“/bin/sh”) Stack region overwritten by gets(buffer)
Overwriting memory • Referencing 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.