0

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;
}
sepp2k
  • 363,768
  • 54
  • 674
  • 675
josef.van.niekerk
  • 11,941
  • 20
  • 97
  • 157
  • You might have some buffer overflow without optimization, which is suppressed with optimizations (e.g. because the compiler deduces that the resulting content of buffer is never useful). Read about [undefined behavior](http://en.wikipedia.org/wiki/Undefined_behavior) – Basile Starynkevitch Feb 26 '15 at 20:41
  • http://stackoverflow.com/questions/11423826/compiler-optimization-makes-program-crash – Milind Dumbare Feb 26 '15 at 20:43
  • Do you initialize all members of `struct lws_context_creation_info` ? The compiler generates code differently with different optimization settings. Uinitialized members of local structures may have different values (among other consequences of undefined behaviour). – chqrlie Feb 26 '15 at 20:49
  • UB for sure............... – Martin James Feb 26 '15 at 20:49
  • Ok, so I'll try next to look at initialising all the outstanding fields for the info struct, hopefully that helps. I am initializing info, but might have left some fields out. Perhaps libwebsockets added some more, and I was following an outdated tutorial. – josef.van.niekerk Feb 26 '15 at 20:50
  • 1
    It's not that turning off optimization *introduces* a bug, it's that turning *on* optimization *hides* a bug. – user253751 Feb 26 '15 at 21:03
  • That's well said, thanks @immibis – josef.van.niekerk Feb 26 '15 at 21:14
  • Ok, so the problem has been sorted, I just made sure I defined all the additional fields on the lws_context_creation_info struct, and the issue went away. Now...who's going to answer this? – josef.van.niekerk Feb 26 '15 at 21:18

1 Answers1

0

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.

chqrlie
  • 131,814
  • 10
  • 121
  • 189