The Most Stupid Bug in Embedded C Language History

I believe this kind of bug can happen to anyone, even experts. Let’s take a look at the bug made by the author.

First, the author wanted to write a program to create a file. If a filename is provided, it creates the actual file; if not, it calls tmpfile() to create a temporary file.

This program is a C program for HTTP downloading. code==200 is the HTTP response code.

else if (code == 200) {     // Downloading whole file
    /* Write new file (plus allow reading once we finish) */
    g = fname ? fopen(fname, "w+") : tmpfile();
}

However, this program only works on Unix/Linux because Microsoft’s implementation of tmpfile() chooses C:\ as the temporary file storage directory, which can be a big problem for those without admin rights. Even with admin rights on Windows 7, issues can still occur.

Therefore, the above program needs to handle things differently on the Windows platform and cannot directly use the Windows tmpfile() function.

So the author noted this issue and wrote FIX ME in the comments:

else if (code == 200) {     // Downloading whole file
    /* Write new file (plus allow reading once we finish) */

    // FIXME Win32 native version fails here because
    //   Microsoft's version of tmpfile() creates the file in C:\
    g = fname ? fopen(fname, "w+") : tmpfile();
}

Next, the author thought it necessary to write a cross-platform version:

FILE * tmpfile ( void ) {
#ifndef _WIN32
    return tmpfile();
#else
    //code for Windows;
#endif
}

Then, the author felt that this implementation was not good and would lead to name conflicts, as this function would be too awkward. So, he refactored his code—writing his own implementation of tmpfile() called w32_tmpfile, and then using a macro definition to rename this function to tmpfile() on Windows. (Note: This usage is a fairly standard way of writing cross-platform code.)

#ifdef _WIN32
  #define tmpfile w32_tmpfile
#endif

FILE * w32_tmpfile ( void ) {
    //code for Windows;
}

Done! Compile and run the program.

What the heck! It didn’t call my w32_tmpfile()—what’s the problem? Debugging, step by step tracking, and indeed it wasn’t called!

Could it be an issue with the ternary operator? Changing it to an if-else statement worked!

if(NULL != fname) {
    g = fopen(fname, "w+");
} else {
    g = tmpfile();
}

The ternary operator shouldn’t have had a problem, right? Could it be that our macro doesn’t work with the ternary operator? Is this a bug in the compiler’s preprocessing? the author wondered.

Now, let’s look at all the code together and compare:

Code that works correctly

#ifdef _WIN32
#  define tmpfile w32_tmpfile
#endif

FILE * w32_tmpfile ( void ) {
    //code for Windows;
}

else if (code == 200) {     // Downloading whole file
    /* Write new file (plus allow reading once we finish) */
    // FIXME Win32 native version fails here because
    //     Microsoft's version of tmpfile() creates the file in C:\
    if(NULL != fname) {
        g = fopen(fname, "w+");
    } else {
        g = tmpfile();
    }
}

Code that doesn’t work correctly

#ifdef _WIN32
#  define tmpfile w32_tmpfile
#endif

FILE * w32_tmpfile ( void ) {
    //code for Windows;
}

else if (code == 200) {     // Downloading whole file
    /* Write new file (plus allow reading once we finish) */
    // FIXME Win32 native version fails here because
    //    Microsoft's version of tmpfile() creates the file in C:\
    g = fname ? fopen(fname, "w+") : tmpfile();
}

Maybe you saw this bug from the beginning, but the author did not. All the problems lie in the comment:

/* Write new file (plus allow reading once we finish) */
// FIXME Win32 native version fails here because
//     Microsoft's version of tmpfile() creates the file in C:\

Did you see that last C:\? In C, “\” indicates that the line does not end, so the following code also becomes a comment. This is the real reason for this bug!

The reason changing to if-else worked is that the author commented out the old ternary operator code, so that working code became:

/* Write new file (plus allow reading once we finish) */
// FIXME Win32 native version fails here because Microsoft's version of tmpfile() creates the file in C:    //g = fname ? fopen(fname, "w+") : tmpfile();
if(NULL != fname) {
    g = fopen(fname, "w+");
} else {
    g = tmpfile();
}

I believe that when the author found the reason for this problem, he must have cursed, “Damn it!” I also believe this bug cost the author a lot of time!

Finally, I want to share a mistake I made before.

I had a small function that needed to pass an int* pInt type, and then I needed to use this int* pInt as a divisor in my code. So my code became:

float result = num/*pInt; ….

/* some comments */

-x<10 ? f(result):f(-result);

Because I was using vi to write the code at the time, there was no syntax highlighting, and my program compiled without errors, but some very strange things happened.

I didn’t know, but when I debugged with gdb, I found that some statements just passed.

This problem cost me a lot of time, and I eventually discovered that the issue was due to the lack of space, damn it. Below, I will use a code highlighting plugin to show the above code:

float result = num/*pInt;
....

/*  some comments */

-x&lt;10 ? f(result):f(-result);

Holy Shit! My code became:

float result = num-x&lt;10 ? f(result):f(-result);

Damn it! My mistake is on par with the error made by the author above.

Recently, many friends have been asking me for essential materials for programmers, so I dug out some treasures and am sharing them for free!

Scan the QR code on the poster to get it for free.

The Most Stupid Bug in Embedded C Language History

Leave a Comment