“It Can’t Be a Bug, My Makefile Depends on It!” 193 bookkeeping information that the C runtime system stores on the stack, such as addresses for returning from subroutine calls. At best, corrupting this information will probably cause a program to crash. We say “probably” because you can corrupt the runtime stack to achieve an effect that the original programmer never intended. Imagine that our func- tion was called upon to read a really long line, over 2,000 characters, and that this line was set up to overwrite the bookkeeping information on the call stack so that when the C function returns, it will call a piece of code that was also embedded in the 2,000 character line. This embedded piece of code may do something truly useful, like exec a shell that can run com- mands on the machine. Robert T. Morris’s Unix Worm employed exactly this mechanism (among others) to gain access to Unix computers. Why anyone would want to do that remains a mystery. Date: Thu, 2 May 91 18:16:44 PDT From: Jim McDonald jlm%missoula@lucid.com To: UNIX-HATERS Subject: how many fingers on your hands? Sad to say, this was part of a message to my manager today: The bug was that a program used to update Makefiles had a pointer that stepped past the array it was supposed to index and scribbled onto some data structures used to compute the dependency lists it was auto-magically writing into a Makefile. The net result was that later on the corrupted Makefile didn’t compile everything it should, so necessary .o files weren’t being written, so the build eventually died. One full day wasted because some idiot thought 10 includes was the most anyone would ever use, and then dangerously optimized code that was going to run for less than a millisecond in the process of creating X Makefiles! The disadvantage of working over networks is that you can’t so eas- ily go into someone else's office and rip their bloody heart out. Exceptional Conditions The main challenge of writing robust software is gracefully handling errors and other exceptions. Unfortunately, C provides almost no support for han- dling exceptional conditions. As a result, few people learning program- ming in today’s schools and universities know what exceptions are.
194 Programming Exceptions are conditions that can arise when a function does not behave as expected. Exceptions frequently occur when requesting system services such as allocating memory or opening files. Since C provides no exception- handling support, the programmer must add several lines of exception-han- dling code for each service request. For example, this is the way that all of the C textbooks say you are sup- posed to use the malloc() memory allocation function: struct bpt *another_function() { struct bpt *result result = malloc(sizeof(struct bpt)) if (result == 0) { fprintf(stderr, “error: malloc: ???\n”) /* recover gracefully from the error */ [...] return 0 } /* Do something interesting */ [...] return result } The function another_function allocates a structure of type bpt and returns a pointer to the new struct. The code fragment shown allocates memory for the new struct. Since C provides no explicit exception-handling support, the C programmer is forced to write exception handlers for each and every system service request (this is the code in bold). Or not. Many C programmers choose not to be bothered with such triviali- ties and simply omit the exception-handling code. Their programs look like this: struct bpt *another_function() { struct bpt *result=malloc(sizeof(struct bpt)) /* Do something interesting */ return result } It’s simpler, cleaner, and most of the time operating system service requests don’t return errors, right? Thus programs ordinarily appear bug
Previous Page Next Page