Skip to content

Commit 30f9dfb

Browse files
committed
Fix use-after-free in custom widget destruction
This commit ensures custom widgets are unregistered from dispatch map before memory is freed. Previously, widget memory could be freed while still registered in the dispatch map, causing crashes when accessing freed widget pointers. The fix modifies custom_widget_dispatch() to handle TwinEventDestroy, ensuring unregistration occurs before base widget destruction.
1 parent 3f83361 commit 30f9dfb

File tree

1 file changed

+26
-0
lines changed

1 file changed

+26
-0
lines changed

src/widget.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ static void unregister_custom_widget(twin_widget_t *widget)
320320
custom_widget_map_t **prev = &custom_widget_map;
321321
custom_widget_map_t *entry = custom_widget_map;
322322

323+
if (!widget)
324+
return;
325+
323326
while (entry) {
324327
if (entry->widget == widget) {
325328
*prev = entry->next;
@@ -334,6 +337,26 @@ static void unregister_custom_widget(twin_widget_t *widget)
334337
static twin_dispatch_result_t custom_widget_dispatch(twin_widget_t *widget,
335338
twin_event_t *event)
336339
{
340+
/* Handle destroy events specially to ensure proper cleanup order */
341+
if (event->kind == TwinEventDestroy) {
342+
/* Find and call user dispatch first if it exists */
343+
custom_widget_map_t *entry = custom_widget_map;
344+
while (entry) {
345+
if (entry->widget == widget) {
346+
if (entry->user_dispatch)
347+
entry->user_dispatch(widget, event);
348+
break;
349+
}
350+
entry = entry->next;
351+
}
352+
353+
/* Unregister from custom widget map before base destruction */
354+
unregister_custom_widget(widget);
355+
356+
/* Now call base widget dispatch to complete destruction */
357+
return _twin_widget_dispatch(widget, event);
358+
}
359+
337360
/* First call the base widget dispatch to handle standard widget behavior */
338361
twin_dispatch_result_t result = _twin_widget_dispatch(widget, event);
339362
if (result == TwinDispatchDone)
@@ -396,6 +419,9 @@ void twin_custom_widget_destroy(twin_custom_widget_t *custom)
396419
return;
397420

398421
if (custom->widget) {
422+
/* Unregister from dispatch map (idempotent if already done during
423+
* TwinEventDestroy)
424+
*/
399425
unregister_custom_widget(custom->widget);
400426
/* Note: widget destruction should be handled by the parent/container */
401427
}

0 commit comments

Comments
 (0)