3

I was going over some old C code (listed below) with a view to re-using it in a new project, and I realised I had left off the final return statement. The peculiar thing is that the routine worked perfectly and did return the correct file pointer. Can anyone explain to me why this is?

 FILE* openforwrite(char *name, int binary)
 {
    //broken out from main to keep it tidy and allow multiple output files.
    FILE *file;
    //first see if it exists
    file = fopen(name,"r");
    if (file)
    { // it does, delete it
        fclose(file);
        if(remove(name)) bail("Output file already exists and cannot be deleted.");
    }
    //now lets re-create and open it for writing
    if (binary)
        file = fopen(name, "wb");
    else
        file = fopen(name, "w");

    //check it actually opened
    if (!file)
        bail("Error opening output file.");

    //and pass the pointer back
    return file; // <-- I had omitted this line initially but the routine still worked
 }
Tyr
  • 782
  • 8
  • 19
  • You could use `stat()` on the file to see if it exists, rathern than opening/closing. Note that your code is racy, and if used in critical apps, could leave the door open for some one to using timings attacks to slip a symlink in place of the specified filename and have your app scribble data where it should be scribbled – Marc B Jun 14 '12 at 22:00
  • 2
    I'm surprised it compiled -- seems like a function with a non-void return type not returning anything wouldn't compile. – jedwards Jun 14 '12 at 22:01
  • Thank you Marc, that's a good point, I'll do so when I use it next. – Tyr Jun 14 '12 at 22:01
  • You should also use the `-Wall` flag, as it would have warned you: `warning: control reaches end of non-void function.` – jedwards Jun 14 '12 at 22:07
  • 2
    @jedwards: read [here](http://stackoverflow.com/questions/1610030/why-can-you-return-from-a-non-void-function-without-returning-a-value-without-pr) for a Q&A about why it compiles without a return statement. – tinman Jun 14 '12 at 22:11
  • 1
    I vaguely remember seeing this question being asked a lot recently, but I can't find an example other than [this one](http://stackoverflow.com/questions/6638963/checking-return-value-of-a-function-without-return-statement) – tinman Jun 14 '12 at 22:13
  • @tinman, that is really a great link. Thanks! – jedwards Jun 14 '12 at 22:13

3 Answers3

3

The return value of the fopen call would end up putting the file handle in the register that is commonly used for return values (e.g., eax). If nothing changed that register value before the function exited, it could still be available for the caller. If, for example, you had one more function call after the fopen and before the end of the function, then it would likely have overwritten the eax register and failed for sure. As others have said, it is undefined behavior. Nonetheless, it is initially a very puzzling situation (and rather interesting).

Mark Wilkins
  • 40,729
  • 5
  • 57
  • 110
1

It merely happened to work. The compiler uses the stack for passing in arguments to functions, and to receive their return value. It also uses the stack for local variables and for temporary variables during calculations. When the function returned, the caller looked at a specific position in the stack for the return value. That value just happened to contain the file pointer value, because that was the last expression your function calculated.

Do not, ever, under any circumstances, assume that this will work this way again.

It may break for any number of reasons, including different compiler versions, different optimizations, or just because the compiler randomly decided to teach you a lesson.

0

The problem is that you invoked undefined behavior. It can seem to work, it can crash, and in the case of buffer overflows, it can delete files.

Dave
  • 10,964
  • 3
  • 32
  • 54