Browse Source

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>
Simon McVittie 1 year ago
parent
commit
91198baed4
3 changed files with 49 additions and 1 deletions
  1. 41 0
      src/core/linux/SDL_dbus.c
  2. 2 0
      src/core/linux/SDL_dbus.h
  3. 6 1
      src/core/linux/SDL_ibus.c

+ 41 - 0
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

+ 2 - 0
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 */
 

+ 6 - 1
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",