260 likes | 277 Views
Learn about common memory-related bugs like bad pointers, uninitialized memory, overwriting memory, and more. Understand the risks and solutions to prevent such issues in your code.
E N D
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 • Dynamically allocating and then losing the pointer to it
int val; ... scanf(“%d”, val);
Does this work? foo(.. .) { int *val; ... scanf(“%d”, val); }
int *foo(.. .) { int *val; ... *val = 34; return val; }
What’s the matter with this? extern int bar(int *zilch); foo(.. .) { int zorch; int *val; ... zorch = bar(val); ... }
Off by one on subscripts • Why do the subscripts start with zero instead of one? • Would it be less bug-prone to start with one? int bar[99000];
What happens if we leave out the #include? • Will the compiler accept this code? • Will the compiler generate object code? • Will the object code be correct? #include <stdlib.h> . . . foo() { char *p = malloc(N); ... }
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; }
int **p; p = malloc(N*sizeof(int)); for (i=0; i<N; i++) { p[i] = malloc(M*sizeof(int)); }
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 • Basis for classic buffer overflow attacks • 1988 Internet worm, AOL/Microsoft IM war • Why should we use fgets instead of gets? • char *fgets(char *s, int n, FILE *stream); char s[8]; int i; gets(s); /* reads “123456789” from stdin */
int func(char *p[], int size) { char tmp[LENGTH]; int i; for (i = 0; /* something */){ /* Do something that creates a string in tmp */ strcpy(p+i, tmp); } }
int *search(int *p, int val) { while (*p && *p != val) p += sizeof(int); return p; }
int *foo () { int val = 0; // initialized ... return &val; }
x = malloc(N*sizeof(int)); <manipulate x> free(x); y = malloc(M*sizeof(int)); <manipulate y> free(x);
x = malloc(N*sizeof(int)); <manipulate x> free(x); ... y = malloc(M*sizeof(int)); for (i=0; i<M; i++) y[i] = x[i]++;
foo() { int *x = malloc(N*sizeof(int)); ... return; }
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); return; }
Loop to compare characters until it finds a pair that do not match, or the end of one string. while((*p == *q) && *p && *q){ *p++; *q++; }
void foo( char *x ) { x = (char *)malloc(100); if( !x ) exit(1); strcpy( x, "this is a test" ); } int main() { char *x; foo(x); printf( "string = %s\n", x ); free(x); return 0; } A hint. When I ran this it printed: *** glibc detected *** free(): invalid pointer: 0x004c3c80 *** Why?
#define Least(x,y) ((x) < (y) ? (x) : (y)) char *puzzle = "A real puzzle"; int main() { int least=10000; char *p = puzzle; while( *p ) { least = Least( least, *p++ ); } printf( "The least char is '%c'\n", least ); return 0; } This program prints out that the least character in the given string is 'e'. This can't be right. What's wrong?
What are three bugs here? char *tokens[NTOKENS]; char delim[] = “ \t\n”; get_command_line() { int i = 0; char *buf = malloc(BUFSIZ); fgets(buf, ...); /* get data into buffer */ buf = strtok(buf, delim); while (buf != NULLSTRING){ tokens[i] = buf; buf = strtok(NULLSTRING, delim); } free(buf); return; }
char *get_command_line() { int i = 0; char *buf = malloc(BUFSIZ); char *p = buf; fgets(buf, ...); /* get data into buffer */ /* pass leading white space */ while ((*p == ‘ ‘) || (*p == ‘\t’)){ p++; } return p; }
char *getline() { char buf[8]; char *result; gets(buf); result = malloc(strlen(buf)); strcpy(result, buf); return(result); } Write down at least three problems with this code.
Dealing With Memory Bugs • Conventional debugger (gdb) • Good for finding bad pointer dereferences • Hard to detect the other memory bugs • Debugging malloc (CSRI UToronto 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.) • Use local variables whenever possible • Also aptly called “automatic variables” • Use static variables whenever possible • Garbage collection (C++) • Let the system free blocks instead of the programmer.