Search code examples
cxcodeclanglibwebsockets

C library crashes when optimization is turned off


I'm writing a library in C on Mac OS X, using XCode 6. The library is basically a plugin loaded by X-Plane and provides data out via a web socket server.

The library, in turn, uses the libwebsockets library, which I compiled using the the guide from the developers repository documentation, here. In a nutshell, I checked out the libwebsockets repository, created a build dir and ran

cmake ..
make

My plugin works 100%, X-Plane loads it without complaints...when optimization is turned on!

The moment I disable optimisation for my library in XCode, to None [-O0], the crap hits the fan, and the library crashes when the libwebsockets function libwebsocket_create_context() is called.

How is it that a bug/crash can be introduced when optimisation is turned off? Isn't it normally be the other way around, with turning optimisation on that something can potentially go wrong?

Here's an excerpt of the library code around the point of failure:

PLUGIN_API int XPluginStart(char *outName, char *outSig, char *outDesc) {
    strcpy(outName, "XP Web Socket");
    strcpy(outSig, "sparkbuzz.plugins.xpwebsocket");
    strcpy(outDesc, "Web socket plugin for streaming data to the browser.");

    struct lws_context_creation_info info;
    info.port = port;
    info.iface = NULL;
    info.protocols = protocols;
    info.extensions = libwebsocket_get_internal_extensions();
    info.ssl_cert_filepath = NULL;
    info.ssl_private_key_filepath = NULL;
    info.gid = -1;
    info.uid = -1;
    info.options = opts;

    context = libwebsocket_create_context(&info); // FAILS HERE WITH EXC_BAD_ACCESS

    if (context == NULL) {
        // libwebsockets initialization has failed!
        return -1;
    }

    // Find XPlane datarefs
    gIAS = XPLMFindDataRef("sim/cockpit2/gauges/indicators/airspeed_kts_pilot");

    // Register flight loop callback
    XPLMRegisterFlightLoopCallback(MyFlightLoopCallback, 1.0, NULL);

    return 1;
}

Solution

  • It seems you did not initialize all the fields in the struct lws_context_creation_info, leading to undefined behaviour. Depending on compiler options and other less predictable circumstances, your program may actually seem to function correctly... by pure chance!

    You can fix this easily by clearing the structure with a memset:

    struct lws_context_creation_info info;
    memset(&info, 0, sizeof info);
    info.port = port;
    info.iface = NULL;
    info.protocols = protocols;
    info.extensions = libwebsocket_get_internal_extensions();
    info.ssl_cert_filepath = NULL;
    info.ssl_private_key_filepath = NULL;
    info.gid = -1;
    info.uid = -1;
    info.options = opts;
    

    Another way to do this in C99 is to initialize the structure this way:

    struct lws_context_creation_info info = {
        .port = port,
        .iface = NULL,
        .protocols = protocols,
        .extensions = libwebsocket_get_internal_extensions(),
        .ssl_cert_filepath = NULL,
        .ssl_private_key_filepath = NULL,
        .gid = -1,
        .uid = -1,
        .options = opts };
    

    Any other fields will be initialized to 0, 0.0 or NULL depending on type, so you could omit the initializers for iface, ssl_cert_filepath and ssl_private_key_filepath.

    It is important to always use a generic method to initialize structures with automatic storage to prevent spurious hard to find bugs from creeping in when the structure is later extended with new fields or in places where you forget some initializers.

    Also note that the 3 top lines of this function are risky too:

    strcpy(outName, "XP Web Socket");
    strcpy(outSig, "sparkbuzz.plugins.xpwebsocket");
    strcpy(outDesc, "Web socket plugin for streaming data to the browser.");
    

    The sizes of the output character arrays are not known. Buffer overflows may occur. Either pass the buffer sizes as a parameter or specify them as a constant and check that the contents will fit.