Skip to content

Commit d44ae5e

Browse files
committed
input: fix OOM NULL-deref bugs across input_driver overlay visibility and wayland registry handlers
Five unchecked alloc + immediate-deref bugs across the shared input dispatch core and the wayland registry/data-offer handlers. === input/input_driver.c: overlay_visibility calloc === input_overlay_set_visibility lazily allocates the visibility array on first call: if (!input_st->overlay_visibility) { unsigned i; input_st->overlay_visibility = (enum overlay_visibility *)calloc( MAX_VISIBILITY, sizeof(enum overlay_visibility)); for (i = 0; i < MAX_VISIBILITY; i++) input_st->overlay_visibility[i] = OVERLAY_VISIBILITY_DEFAULT; } input_st->overlay_visibility[overlay_idx] = vis; The calloc was unchecked. Both the init loop and the final indexed write dereference input_st->overlay_visibility - OOM was a crash on first visibility change after the overlay was reloaded. Fix: NULL-check and early-return. On failure overlays stay at their compile-time default visibility, which is strictly better than crashing. The next call re-enters this branch and retries the allocation (input_st->overlay_visibility is still NULL), so transient OOM is self-healing. === input/common/wayland_common.c: three unchecked callocs === wl_current_outputs_add: surface_output_t calloc unchecked before os->output and wl_list_insert(&os->link) that NULL-deref. Fixed with early return false - the caller (registry handler) treats false as 'output not tracked', which is strictly better than crashing and is self-healing on the next wl_registry.global re-emission. wl_registry listener wl_output branch: display_output_t + output_info_t callocs both unchecked before od->output = oi / oi->global_id = id / wl_output_add_listener NULL-derefs. Fixed with joint NULL-check, free whichever succeeded (free(NULL) is a no-op), skip adding this output to wl->all_outputs. wl_data_device_handle_data_offer: data_offer_ctx calloc unchecked before offer_data->offer and wl_data_offer_ set_user_data/add_listener NULL-deref. Fixed with early return - consequence is a lost drag-and-drop operation on that specific offer. === input/common/wayland_common_webos.c: two unchecked callocs === wl_registry handler for webos wl_output: twin of the wayland_common.c fix - display_output_t + output_info_t callocs both unchecked, with the same od->output / oi->global_id deref pattern. Same fix. wl_registry handler for webos wl_seat: seat_info_t calloc unchecked before si->seat / global_id / wl writes NULL-deref. Fixed with early return - the compositor re-emits wl_registry.global if a retry is needed. === Swept-clean in the same pass === Verified NULL-checked in the same files: - input_driver.c other 5 sites: input_keyboard_line_append realloc (tmp-pattern, guarded), input_remote_new calloc, osk realloc (tmp-pattern, guarded), overlay images malloc (has defensive num_images=0 on failure to make the subsequent for-loop safely iterate zero times), overlay ol calloc (prior audit commit). - wayland_common.c wl_read_pipe realloc (tmp-pattern, guarded, with invariant-preserving rewind on failure from prior commit). - wayland_common_webos.c: g_register_ctx malloc, *wwl calloc, g_screensaver_ctx malloc, client_name malloc - all guarded. - input/drivers_joypad/winraw_joypad.c: 4 sites guarded. Reachability: all five fixes are reachable on OOM. The overlay_visibility one is the most user-facing - any visibility change hits it. The wayland ones fire on display/seat/drag-drop events which are compositor-driven and arbitrarily frequent. Scope: local to each callsite; no cross-TU changes. All existing state-mutation invariants preserved (on failure we skip the mutation entirely rather than half-apply it). Thread-safety: all sites run on their respective main dispatch threads (input main thread, wayland event loop thread). No new concurrency introduced. Reachability timing: the overlay_visibility bug is reachable on the very first user-invoked overlay visibility change under OOM and survives as a latent crash for the lifetime of the process (the allocation is lazy, retried on each entry until it succeeds). The wayland bugs are reachable on any wl_registry.global or wl_data_device.data_offer event the compositor emits - which for data_offer is every drag-and-drop enter, and for wl_output/wl_seat is every display-plug / input-device hotplug event.
1 parent 9e840aa commit d44ae5e

3 files changed

Lines changed: 71 additions & 13 deletions

File tree

input/common/wayland_common.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,14 @@ static bool wl_current_outputs_add(gfx_ctx_wayland_data_t *wl,
597597
{
598598
surface_output_t *os = (surface_output_t*)
599599
calloc(1, sizeof(surface_output_t));
600+
/* NULL-check: the field writes below NULL-deref on OOM.
601+
* Skip this output from the current_outputs list; the
602+
* subsequent wl_list_for_each traversal in
603+
* wl_current_outputs_remove handles a missing entry
604+
* gracefully (the loop simply doesn't find a match and
605+
* returns false). */
606+
if (!os)
607+
return false;
600608
os->output = oi_found;
601609
wl_list_insert(&wl->current_outputs, &os->link);
602610
return true;
@@ -751,12 +759,26 @@ static void wl_registry_handle_global(void *data, struct wl_registry *reg,
751759
output_info_t *oi = (output_info_t*)
752760
calloc(1, sizeof(output_info_t));
753761

754-
od->output = oi;
755-
oi->global_id = id;
756-
oi->output = (struct wl_output*)wl_registry_bind(reg,
757-
id, &wl_output_interface, MIN(version, 2));
758-
wl_output_add_listener(oi->output, &output_listener, oi);
759-
wl_list_insert(&wl->all_outputs, &od->link);
762+
/* NULL-check both callocs: od->output = oi and
763+
* oi->global_id = id NULL-deref on OOM. Free whichever
764+
* succeeded (free(NULL) is a no-op) and skip adding this
765+
* output to wl->all_outputs - the compositor will re-emit
766+
* wl_registry.global if it needs us to retry, and missing
767+
* outputs fall back to sensible defaults downstream. */
768+
if (!od || !oi)
769+
{
770+
free(od);
771+
free(oi);
772+
}
773+
else
774+
{
775+
od->output = oi;
776+
oi->global_id = id;
777+
oi->output = (struct wl_output*)wl_registry_bind(reg,
778+
id, &wl_output_interface, MIN(version, 2));
779+
wl_output_add_listener(oi->output, &output_listener, oi);
780+
wl_list_insert(&wl->all_outputs, &od->link);
781+
}
760782
}
761783
else if (string_is_equal(interface, xdg_wm_base_interface.name) && found++)
762784
wl->xdg_shell = (struct xdg_wm_base*)
@@ -960,6 +982,15 @@ static void wl_data_device_handle_data_offer(void *data,
960982
{
961983
data_offer_ctx *offer_data = (data_offer_ctx*)calloc(1, sizeof *offer_data);
962984

985+
/* NULL-check: the field writes below NULL-deref on OOM.
986+
* On failure skip the offer - wl_data_offer_set_user_data
987+
* would have attached this pointer for the listener
988+
* callbacks to retrieve, so without it the offer just
989+
* doesn't get handled by this client. That's a lost
990+
* drag-and-drop operation rather than a crash. */
991+
if (!offer_data)
992+
return;
993+
963994
offer_data->offer = offer;
964995
offer_data->data_device = data_device;
965996
offer_data->dropped = false;

input/common/wayland_common_webos.c

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -229,17 +229,36 @@ static void wl_registry_handle_global_webos(void *data,
229229
{
230230
display_output_t *od = (display_output_t*)calloc(1, sizeof(*od));
231231
output_info_t *oi = (output_info_t*)calloc(1, sizeof(*oi));
232-
od->output = oi;
233-
oi->output = (struct wl_output*)wl_registry_bind(reg,
234-
id, &wl_output_interface, MIN(version, 2));
235-
oi->global_id = id;
236-
oi->scale = 1;
237-
wl_output_add_listener(oi->output, &output_listener, oi);
238-
wl_list_insert(&wl->all_outputs, &od->link);
232+
/* NULL-check both callocs: od->output = oi and oi->output/
233+
* global_id/scale writes NULL-deref on OOM. Free whichever
234+
* succeeded and skip adding this output. Mirrors the fix
235+
* in the upstream wayland_common.c sibling. */
236+
if (!od || !oi)
237+
{
238+
free(od);
239+
free(oi);
240+
}
241+
else
242+
{
243+
od->output = oi;
244+
oi->output = (struct wl_output*)wl_registry_bind(reg,
245+
id, &wl_output_interface, MIN(version, 2));
246+
oi->global_id = id;
247+
oi->scale = 1;
248+
wl_output_add_listener(oi->output, &output_listener, oi);
249+
wl_list_insert(&wl->all_outputs, &od->link);
250+
}
239251
}
240252
else if (string_is_equal(interface, "wl_seat"))
241253
{
242254
seat_info_t *si = calloc(1, sizeof(*si));
255+
/* NULL-check: the field writes below NULL-deref on OOM.
256+
* Skip this seat - the compositor will re-emit
257+
* wl_registry.global if it needs us to retry, and missing
258+
* seats are handled gracefully by downstream input
259+
* dispatch (no devices reported from that seat). */
260+
if (!si)
261+
return;
243262
si->seat = (struct wl_seat*)wl_registry_bind(reg, id, &wl_seat_interface, MIN(version, 4));
244263
si->global_id = id;
245264
si->wl = wl;

input/input_driver.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6147,6 +6147,14 @@ void input_overlay_set_visibility(int overlay_idx,
61476147
input_st->overlay_visibility = (enum overlay_visibility *)calloc(
61486148
MAX_VISIBILITY, sizeof(enum overlay_visibility));
61496149

6150+
/* NULL-check: the init loop below and the later
6151+
* overlay_visibility[overlay_idx] = vis write NULL-deref
6152+
* on OOM. Bail early - on failure the overlay stays at
6153+
* its compile-time default visibility rather than being
6154+
* explicitly set, which is strictly better than crashing. */
6155+
if (!input_st->overlay_visibility)
6156+
return;
6157+
61506158
for (i = 0; i < MAX_VISIBILITY; i++)
61516159
input_st->overlay_visibility[i] = OVERLAY_VISIBILITY_DEFAULT;
61526160
}

0 commit comments

Comments
 (0)