Last week I got a weird bug report about Guikachu segfaulting at startup in g_object_newv, on AMD64. My friend Ice hooked me up with an account on an AMD64 box so I compiled a library stack with debug symbols, and it turned out the crash originated from gnome_program_init, which is declared as following:
GnomeProgram * gnome_program_init (const char *app_id, const char *app_version, const GnomeModuleInfo *module_info, int argc, char **argv, const char *first_property_name, ...);
This forwards the vararg list to g_object_newv which basically works like this:
prop_name = first_property_name; while (prop_name) { GType type = lookup_prop_type (prop_name); val = va_arg (vararg, type); /* Of course this doesn't really work like this, there's a huge switch() block hidden beneath macro magic */ set_prop (prop_name, val);prop_name = va_arg (vararg, char*); }
So far so good, right? Well, gnomemm, which is a C++ library, calls gnome_program_init as such:
GnomeProgram* pProgram = gnome_program_init (app_id_, app_version_, module_info.gobj (), argc, argv, GNOME_PARAM_POPT_TABLE, options, GNOME_PARAM_POPT_FLAGS, flags, 0);
Looks harmless, doesn't it? The problem is, while in C++ 0 is used for NULL, in the above code the compiler has no way of knowing what 0's type above should be. So it goes with 'int', which is a reasonable default. This (int)0 eventually gets to g_object_newv, which reads it as a char*, and tries to see if it's NULL.
Now here comes the tricky part: remember, we're on amd64, so sizeof (int) == 4, but sizeof (char*) == sizeof (void*) == 8. So when va_arg (vararg, char*) reads the last property name, it gets 32 bits of zeros, and 32 bits of garbage. Resulting in a char* of garbage, which then gets passed to lookup_prop_type. Which makes baby Jesus cry.
So the moral of this all is:
- varargs are evil.
- varargs are even more evil than they look at first sight
- when using varargs from c++, make sure you use something like (void*)0 instead of vanilla 0 to denote a null pointer
- Have I mentioned how varargs are evil?