Retain the dbus reply when getting string results

The string result points at memory in the reply, so we can't unref that until the string is no longer needed.

Fixes https://github.com/libsdl-org/SDL/issues/14839
This commit is contained in:
Sam Lantinga
2026-01-20 17:08:20 -08:00
parent 7d27ca282e
commit 453d3e5c8c
6 changed files with 108 additions and 76 deletions

View File

@@ -536,17 +536,17 @@ static void check_upower_device(DBusConnection *conn, const char *path, SDL_Powe
Sint64 si64 = 0;
double d = 0.0;
if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Type", DBUS_TYPE_UINT32, &ui32)) {
if (!SDL_DBus_QueryPropertyOnConnection(conn, NULL, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Type", DBUS_TYPE_UINT32, &ui32)) {
return; // Don't know _what_ we're looking at. Give up on it.
} else if (ui32 != 2) { // 2==Battery
return; // we don't care about UPS and such.
} else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "PowerSupply", DBUS_TYPE_BOOLEAN, &ui32)) {
} else if (!SDL_DBus_QueryPropertyOnConnection(conn, NULL, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "PowerSupply", DBUS_TYPE_BOOLEAN, &ui32)) {
return;
} else if (!ui32) {
return; // we don't care about random devices with batteries, like wireless controllers, etc
}
if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "IsPresent", DBUS_TYPE_BOOLEAN, &ui32)) {
if (!SDL_DBus_QueryPropertyOnConnection(conn, NULL, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "IsPresent", DBUS_TYPE_BOOLEAN, &ui32)) {
return;
}
if (!ui32) {
@@ -555,9 +555,11 @@ static void check_upower_device(DBusConnection *conn, const char *path, SDL_Powe
/* Get updated information on the battery status
* This can occasionally fail, and we'll just return slightly stale data in that case
*/
SDL_DBus_CallMethodOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Refresh", DBUS_TYPE_INVALID, DBUS_TYPE_INVALID);
SDL_DBus_CallMethodOnConnection(conn, NULL, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Refresh",
DBUS_TYPE_INVALID,
DBUS_TYPE_INVALID);
if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "State", DBUS_TYPE_UINT32, &ui32)) {
if (!SDL_DBus_QueryPropertyOnConnection(conn, NULL, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "State", DBUS_TYPE_UINT32, &ui32)) {
st = SDL_POWERSTATE_UNKNOWN; // uh oh
} else if (ui32 == 1) { // 1 == charging
st = SDL_POWERSTATE_CHARGING;
@@ -578,14 +580,14 @@ static void check_upower_device(DBusConnection *conn, const char *path, SDL_Powe
}
}
if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Percentage", DBUS_TYPE_DOUBLE, &d)) {
if (!SDL_DBus_QueryPropertyOnConnection(conn, NULL, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Percentage", DBUS_TYPE_DOUBLE, &d)) {
pct = -1; // some old/cheap batteries don't set this property.
} else {
pct = (int)d;
pct = (pct > 100) ? 100 : pct; // clamp between 0%, 100%
}
if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "TimeToEmpty", DBUS_TYPE_INT64, &si64)) {
if (!SDL_DBus_QueryPropertyOnConnection(conn, NULL, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "TimeToEmpty", DBUS_TYPE_INT64, &si64)) {
secs = -1;
} else {
secs = (int)si64;
@@ -620,6 +622,7 @@ bool SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *state, int *s
#ifdef SDL_USE_LIBDBUS
SDL_DBusContext *dbus = SDL_DBus_GetContext();
DBusMessage *reply = NULL;
char **paths = NULL;
char *path = NULL;
int i, numpaths = 0;
@@ -628,22 +631,20 @@ bool SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *state, int *s
return false; // try a different approach than UPower.
}
if (SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "GetDisplayDevice", DBUS_TYPE_INVALID, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
if (SDL_DBus_CallMethodOnConnection(dbus->system_conn, &reply, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "GetDisplayDevice",
DBUS_TYPE_INVALID,
DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
result = true; // Clearly we can use this interface.
*state = SDL_POWERSTATE_NO_BATTERY; // assume we're just plugged in.
*seconds = -1;
*percent = -1;
// Make a copy of the path before making more dbus calls
path = SDL_strdup(path);
if (!path) {
// Out of memory, try to do something else
return false;
}
check_upower_device(dbus->system_conn, path, state, seconds, percent);
SDL_free(path);
SDL_DBus_FreeReply(&reply);
} else if (SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices", DBUS_TYPE_INVALID, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &numpaths, DBUS_TYPE_INVALID)) {
} else if (SDL_DBus_CallMethodOnConnection(dbus->system_conn, NULL, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices",
DBUS_TYPE_INVALID,
DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &numpaths, DBUS_TYPE_INVALID)) {
result = true; // Clearly we can use this interface.
*state = SDL_POWERSTATE_NO_BATTERY; // assume we're just plugged in.
*seconds = -1;