This seems to be a fairly common pattern e.g. in hexchat (may not compile, see also plugin docs. also note that hexchat_plugin_get_info
hasn't been used in forever so I'm omitting it for simplicity):
static hexchat_plugin *ph;
static int timer_cb(void *userdata) {
if (hexchat_set_context(ph, userdata)) { /* <-- is this line UB? */
/* omitted */
}
return 0;
}
static int do_ub(char *word[], char *word_eol[], void *userdata) {
void *context = hexchat_get_context(ph);
hexchat_hook_timer(ph, 1000, timer_cb, context);
hexchat_command(ph, "close"); /* free the context - in practice this would be done by another plugin or by the user, not like this, but for the purposes of this example this simulates the user closing the context. */
return HEXCHAT_EAT_ALL;
}
int hexchat_plugin_init(hexchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg) {
*plugin_name = "do_ub";
*plugin_desc = "does ub when you /do_ub";
*plugin_version = "1.0.0";
ph = plugin_handle;
/* etc */
hexchat_hook_command(ph, "do_ub", 0, do_ub, "does UB", NULL);
return 1;
}
The line in timer_cb
causes hexchat to compare the (potentially free'd - definitely free'd in this example, see the comment in do_ub
) pointer with another pointer, if you follow from here (plugin.c#L1089, hexchat_set_context) you'll end up in here (hexchat.c#L191, is_session). To invoke this code, run /do_ub
in hexchat.
Relevant code:
int
hexchat_set_context (hexchat_plugin *ph, hexchat_context *context)
{
if (is_session (context))
{
ph->context = context;
return 1;
}
return 0;
}
int
is_session (session * sess)
{
return g_slist_find (sess_list, sess) ? 1 : 0;
}
Is this sort of thing UB?
Using a value of a pointer after the object it is pointing to have reached it's lifetime end is indeterminate as stated in the C11 Standard draft 6.2.4p2 (Storage durations of objects) (the emphasis is mine):
And using it's value (just for anything) is an explicit undefined behavior as stated in Annex J.2(Undefined behavior):