Skip to content

Commit 46ba4c1

Browse files
committed
gfx/common/x11: NULL-guard xss_screensaver_inhibit()
Found by the new ASan+UBSan CI workflow's headless SDL2 imageviewer smoke (b9777c8 + d967813). Run #2 of the smoke (after the v12 xvideo->sdl2 pivot) caught a SEGV at process startup: AddressSanitizer:DEADLYSIGNAL ==9537==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000968. The signal is caused by a READ memory access. #0 XQueryExtension (libX11.so.6+0x361f6) #1 XInitExtension (libX11.so.6) #2 XextAddDisplay (libXext.so.6) #3 XScreenSaverQueryExtension (libXss.so.1+0x1565) #4 x11_suspend_screensaver #5 video_driver_init_internal #6 drivers_init #7 retroarch_main_init The address 0x968 is a small offset off NULL -- libX11's XQueryExtension dereferencing the Display * argument before checking it for null. Root cause: gfx/common/x11_common.c::xss_screensaver_inhibit() was called via x11_suspend_screensaver() with g_x11_dpy == NULL. The flow: 1. SDL2 video driver inits. 2. sdl2_set_handles() in gfx/common/sdl2_common.c:54 calls video_driver_display_type_set(RARCH_DISPLAY_X11) so the display *type* gate matches X11, and routes the SDL-owned X Display through video_driver_display_set() at line 55. 3. The file-scope `g_x11_dpy` in x11_common.c:66 stays at its initial NULL value. It's only assigned at line 795 inside XOpenDisplay() in the xvideo / GL / X11-direct init paths, which the SDL2 driver doesn't take. 4. drivers_init() -> video_driver_init_internal() -> x11_suspend_screensaver() -- line 286 gates on display_type == RARCH_DISPLAY_X11 (passes; SDL2 set it), then at lines 289-292 tries dbus_suspend_screensaver() if HAVE_DBUS, then at line 293 calls xss_screensaver_inhibit(g_x11_dpy, enable) -- with g_x11_dpy == NULL. 5. xss_screensaver_inhibit() at line 227 calls XScreenSaverQueryExtension(NULL, ...) -> libX11 dereferences -> SEGV. Why hasn't this been hit before? Most desktop builds set HAVE_DBUS -- pkg-config finds dbus-1 on basically every modern Linux distro -- so the dbus_suspend_screensaver() call at line 290 short-circuits and returns true before the xss path is reached. The CI runner doesn't apt-install libdbus-1-dev (HAVE_DBUS off) but libxss1 is pulled in transitively by xvfb / x11-utils (HAVE_XSCRNSAVER on at link time), exposing the corner. A symmetric defensive check already exists at line 255 in xdg_screensaver_inhibit(): if (g_x11_dpy && g_x11_win) ... The xss path was the missing-guard one. * gfx/common/x11_common.c Add a `if (!dpy) return false;` guard at the top of xss_screensaver_inhibit(). Applies only to the HAVE_XSCRNSAVER branch; the no-op stub at line 237 already discards `dpy` with `(void) dpy;` and returns false. Inline comment explaining the failure mode points at the relevant call sites in sdl2_common.c so a future maintainer can trace the invariant without re-deriving it. Verified locally: - The added check returns false for NULL dpy without invoking libX11 (matches the existing "extension not present" return contract). Non-NULL dpy paths are unchanged. - xss_screensaver_inhibit() is `static`, single caller (line 311 in x11_suspend_screensaver) -- no other reachable code paths to audit. Cannot verify locally that this resolves the CI SEGV without rebuilding retroarch with the same apt set the runner has -- the next CI run is the experiment. Expected outcome: xss_screensaver_inhibit() returns false harmlessly, the function falls through to the xdg_screensaver path (which has its own NULL guard), and the SDL2 smoke proceeds into retro_run.
1 parent e8c8bd1 commit 46ba4c1

1 file changed

Lines changed: 18 additions & 0 deletions

File tree

gfx/common/x11_common.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,24 @@ void x11_set_window_attr(Display *dpy, Window win)
224224
static bool xss_screensaver_inhibit(Display *dpy, bool enable)
225225
{
226226
int dummy, min, maj;
227+
/* Guard against being called with a NULL Display. This
228+
* happens with the SDL2 video driver under HAVE_X11 +
229+
* HAVE_XSCRNSAVER builds without HAVE_DBUS: SDL2's
230+
* sdl2_set_handles() in gfx/common/sdl2_common.c sets the
231+
* display *type* to RARCH_DISPLAY_X11 (so the gate in
232+
* x11_suspend_screensaver passes) and routes the SDL-owned
233+
* X Display through video_driver_display_set(), but the
234+
* file-scope g_x11_dpy here stays at its initial NULL --
235+
* it's only assigned in the xvideo / GL / X11-direct init
236+
* paths. libX11's XQueryExtension() then SEGVs at a tiny
237+
* offset off the NULL display pointer. Most desktop builds
238+
* never hit this because HAVE_DBUS is on and
239+
* dbus_suspend_screensaver() short-circuits before this
240+
* line; surfaced by the ASan+UBSan CI workflow's headless
241+
* SDL2 smoke (b9777c8 + d967813), where dbus-1 isn't
242+
* apt-installed but libXss is. */
243+
if (!dpy)
244+
return false;
227245
if ( !XScreenSaverQueryExtension(dpy, &dummy, &dummy)
228246
|| !XScreenSaverQueryVersion(dpy, &maj, &min)
229247
|| (maj < 1)

0 commit comments

Comments
 (0)