Chapter 9-05

来源:互联网 发布:ilust studio mac 编辑:程序博客网 时间:2024/05/17 09:46

Please indicate the source:http://blog.csdn.net/gaoxiangnumber1.

9.11 Common Memory-Related Bugs in C Programs

9.11.1 Dereferencing Bad Pointers

l  As in Figure 9.26, there are large holes in the virtual address space of a process that are not mapped to any meaningful data. If we attempt to dereference a pointer into one of these holes, the operating system will terminate our program with a segmentation exception. Also, some areas of virtual memory are read-only. Attempting to write to one of these areas terminates the program with a protection exception.

l  An example of dereferencing a bad pointer is the scanf bug. Suppose we want to use scanf to read an integer from stdin into a variable. The correct way to do this is to pass scanf a format string and the address of the variable:

scanf("%d", &val)

l  It is easy to pass the contents of val instead of its address:

scanf("%d", val)

l  In this case, scanf will interpret the contents of val as an address and attempt to write a word to that location. In the best case, the program terminates immediately with an exception. In the worst case, the contents of val correspond to some valid read/write area of virtual memory, and we overwrite memory, usually with disastrous and baffling consequences much later.

9.11.2 Reading Uninitialized Memory

l  Though .bss memory locations (such as uninitialized global C variables) are always initialized to zeros by the loader, this is not true for heap memory. A common error is to assume that heap memory is initialized to zero:

l  In this example, the programmer has incorrectly assumed that vector y has been initialized to zero. A correct implementation would explicitly zero y[i], or use calloc.

9.11.3 Allowing Stack Buffer Overflows

l  As in Section 3.12, a program has a buffer overflow bug if it writes to a target buffer on the stack without examining the size of the input string.

l  The following function has a buffer overflow bug because the gets function copies an arbitrary length string to the buffer. To fix this, we would need to use the fgets function, which limits the size of the input string.

9.11.4 Assuming that Pointers and the Objects They Point to Are the Same Size

l  One common mistake is to assume that pointers to objects are the same size as the objects they point to:

l  The intent here is to create an array of n pointers, each of which points to an array of m ints. However, because the programmer has written sizeof(int) instead of sizeof(int *) in line 5, the code actually creates an array of ints.

l  This code will run fine on machines where ints and pointers to ints are the same size. But if we run this code on a machine where a pointer is larger than an int, then the loop in lines 7–8 will write past the end of the A array.

l  Since one of these words will likely be the boundary tag footer of the allocated block, we may not discover the error until we free the block much later in the program, at which point the coalescing code in the allocator will fail dramatically and for no apparent reason.

9.11.5 Making Off-by-One Errors

l  Off-by-one errors are another common source of overwriting bugs:

9.11.6 Referencing a Pointer Instead of the Object It Points to

l  If we are not careful about the precedence and associativity of C operators, then we incorrectly manipulate a pointer instead of the object it points to.

l  Consider the following function, whose purpose is to remove the first item in a binary heap of *size items, and then reheapify the remaining *size - 1 items:

l  In line 6, the intent is to decrement the integer value pointed to by the size pointer. Because the unary -- and * operators have the same precedence and associate from right to left, the code in line 6 actually decrements the pointer itself instead of the integer value that it points to.

l  If we are lucky, the program will crash immediately; but more likely we will be left scratching our heads when the program produces an incorrect answer much later in its execution.

l  The moral here is to use parentheses whenever in doubt about precedence and associativity. For example, in line 6 we should have clearly stated our intent by using the expression (*size)--.

9.11.7 Misunderstanding Pointer Arithmetic

l  Another common mistake is to forget that arithmetic operations on pointers are performed in units that are the size of the objects they point to, which are not necessarily bytes.

l  For example, the intent of the following function is to scan an array of ints and return a pointer to the first occurrence of val:

l  Because line 4 increments the pointer by 4 (the number of bytes in an integer) each time through the loop, the function incorrectly scans every fourth integer in the array.

9.11.8 Referencing Nonexistent Variables

l  C programmers who do not understand the stack discipline will sometimes reference local variables that are no longer valid, as in the following example:

l  This function returns a pointer (say, p) to a local variable on the stack and then pops its stack frame. Although p still points to a valid memory address, it no longer points to a valid variable. When other functions are called later in the program, the memory will be reused for their stack frames. Later, if the program assigns some value to *p, then it might actually be modifying an entry in another function’s stack frame, with potentially disastrous and baffling consequences.

9.11.9 Referencing Data in Free Heap Blocks

l  A similar error is to reference data in heap blocks that have already been freed.

l  Consider the following example, which allocates an integer array x in line 6, prematurely frees block x in line 10, and then later references it in line 14:

l  Depending on the pattern of malloc and free calls that occur between lines 6 and 10, when the program references x[i] in line 14, the array x might be part of some other allocated heap block and have been overwritten. As with many memory-related bugs, the error will only become evident later in the program when we notice that the values in y are corrupted.

9.11.10 Introducing Memory Leaks

l  Memory leaks are slow, silent killers that occur when programmers inadvertently create garbage in the heap by forgetting to free allocated blocks.

l  The following function allocates a heap block x and then returns without freeing it:

l  If leak is called frequently, then the heap will gradually fill up with garbage, in the worst case consuming the entire virtual address space. Memory leaks are particularly serious for programs such as daemons and servers, which by definition never terminate.

Please indicate the source:http://blog.csdn.net/gaoxiangnumber1.

0 0