SDL: ibus: Handle error when getting the D-Bus machine ID

From 91198baed40d5709020c3001e9234f4580df696a Mon Sep 17 00:00:00 2001
From: Simon McVittie <[EMAIL REDACTED]>
Date: Tue, 20 Jun 2023 13:22:55 +0100
Subject: [PATCH] ibus: Handle error when getting the D-Bus machine ID

It is possible for retrieving the machine ID to fail, either because
dbus was installed incorrectly (machine ID absent or corrupt), or in
32-bit builds, because stat() on the machine ID fails with EOVERFLOW
if it has an out-of-range timestamp or inode number.

dbus has historically treated this as a faulty installation, raising
a warning which by default causes the process to crash. Unfortunately,
dbus_get_local_machine_id() never had a way to report errors, so it has
no alternative for that (bad) error handling.

In dbus >= 1.12.0, we can use dbus_try_get_local_machine_id() to get
the same information, but with the ability to cope gracefully with
errors. ibus won't work in this situation, but that's better than
crashing.

Mitigates: https://github.com/ValveSoftware/steam-for-linux/issues/9605
Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 src/core/linux/SDL_dbus.c | 41 +++++++++++++++++++++++++++++++++++++++
 src/core/linux/SDL_dbus.h |  2 ++
 src/core/linux/SDL_ibus.c |  7 ++++++-
 3 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/src/core/linux/SDL_dbus.c b/src/core/linux/SDL_dbus.c
index 970af56b08a4..e1e4e71748fe 100644
--- a/src/core/linux/SDL_dbus.c
+++ b/src/core/linux/SDL_dbus.c
@@ -33,10 +33,16 @@ static SDL_DBusContext dbus;
 
 static int LoadDBUSSyms(void)
 {
+#define SDL_DBUS_SYM2_OPTIONAL(TYPE, x, y)                   \
+    dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y)
+
 #define SDL_DBUS_SYM2(TYPE, x, y)                            \
     if (!(dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y))) \
     return -1
 
+#define SDL_DBUS_SYM_OPTIONAL(TYPE, x) \
+    SDL_DBUS_SYM2_OPTIONAL(TYPE, x, dbus_##x)
+
 #define SDL_DBUS_SYM(TYPE, x) \
     SDL_DBUS_SYM2(TYPE, x, dbus_##x)
 
@@ -77,6 +83,7 @@ static int LoadDBUSSyms(void)
     SDL_DBUS_SYM(dbus_bool_t (*)(const DBusError *), error_is_set);
     SDL_DBUS_SYM(void (*)(DBusError *), error_free);
     SDL_DBUS_SYM(char *(*)(void), get_local_machine_id);
+    SDL_DBUS_SYM_OPTIONAL(char *(*)(DBusError *), try_get_local_machine_id);
     SDL_DBUS_SYM(void (*)(void *), free);
     SDL_DBUS_SYM(void (*)(char **), free_string_array);
     SDL_DBUS_SYM(void (*)(void), shutdown);
@@ -502,4 +509,38 @@ void SDL_DBus_PumpEvents(void)
         }
     }
 }
+
+/*
+ * Get the machine ID if possible. Result must be freed with dbus->free().
+ */
+char *SDL_DBus_GetLocalMachineId(void)
+{
+    DBusError err;
+    char *result;
+
+    dbus.error_init(&err);
+
+    if (dbus.try_get_local_machine_id) {
+        /* Available since dbus 1.12.0, has proper error-handling */
+        result = dbus.try_get_local_machine_id(&err);
+    } else {
+        /* Available since time immemorial, but has no error-handling:
+         * if the machine ID can't be read, many versions of libdbus will
+         * treat that as a fatal mis-installation and abort() */
+        result = dbus.get_local_machine_id();
+    }
+
+    if (result) {
+        return result;
+    }
+
+    if (dbus.error_is_set(&err)) {
+        SDL_SetError("%s: %s", err.name, err.message);
+        dbus.error_free(&err);
+    } else {
+        SDL_SetError("Error getting D-Bus machine ID");
+    }
+
+    return NULL;
+}
 #endif
diff --git a/src/core/linux/SDL_dbus.h b/src/core/linux/SDL_dbus.h
index 6326ba7c2b77..9234c5926cbc 100644
--- a/src/core/linux/SDL_dbus.h
+++ b/src/core/linux/SDL_dbus.h
@@ -75,6 +75,7 @@ typedef struct SDL_DBusContext
     dbus_bool_t (*error_is_set)(const DBusError *);
     void (*error_free)(DBusError *);
     char *(*get_local_machine_id)(void);
+    char *(*try_get_local_machine_id)(DBusError *);
     void (*free)(void *);
     void (*free_string_array)(char **);
     void (*shutdown)(void);
@@ -99,6 +100,7 @@ extern void SDL_DBus_ScreensaverTickle(void);
 extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit);
 
 extern void SDL_DBus_PumpEvents(void);
+extern char *SDL_DBus_GetLocalMachineId(void);
 
 #endif /* HAVE_DBUS_DBUS_H */
 
diff --git a/src/core/linux/SDL_ibus.c b/src/core/linux/SDL_ibus.c
index 4b5d4bbe083c..92475c2579fa 100644
--- a/src/core/linux/SDL_ibus.c
+++ b/src/core/linux/SDL_ibus.c
@@ -411,7 +411,12 @@ static char *IBus_GetDBusAddressFilename(void)
         (void)SDL_snprintf(config_dir, sizeof(config_dir), "%s/.config", home_env);
     }
 
-    key = dbus->get_local_machine_id();
+    key = SDL_DBus_GetLocalMachineId();
+
+    if (key == NULL) {
+        SDL_free(display);
+        return NULL;
+    }
 
     SDL_memset(file_path, 0, sizeof(file_path));
     (void)SDL_snprintf(file_path, sizeof(file_path), "%s/ibus/bus/%s-%s-%s",