diff options
author | Petr Menšík <pemensik@redhat.com> | 2023-04-17 20:55:31 +0100 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2023-04-17 20:55:31 +0100 |
commit | 33635d8564f96cedcef9bf9826cbbca76f28aa81 (patch) | |
tree | 16c3b6b8db89a8750026e302dcc25f1096a40e4b | |
parent | bd188e306a06ca0acb6c213ecc0e1dc0dc22c3f6 (diff) | |
download | dnsmasq-33635d8564f96cedcef9bf9826cbbca76f28aa81.tar.gz |
Fix crash in dbus code.
If I configure dnsmasq to use dbus and then restart dbus.service with watchers present,
it crashes dnsmasq. The reason is simple, it uses loop to walk over watchers to call
dbus handling code. But from that code the same list can be modified and watchers removed.
But the list iteration continues anyway.
Restart the loop if list were modified.
-rw-r--r-- | src/dbus.c | 24 |
1 files changed, 20 insertions, 4 deletions
@@ -106,6 +106,7 @@ const char* introspection_xml_template = "</node>\n"; static char *introspection_xml = NULL; +static int watches_modified = 0; struct watch { DBusWatch *watch; @@ -127,6 +128,7 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) w->watch = watch; w->next = daemon->watches; daemon->watches = w; + watches_modified++; (void)data; /* no warning */ return TRUE; @@ -134,7 +136,7 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data) static void remove_watch(DBusWatch *watch, void *data) { - struct watch **up, *w, *tmp; + struct watch **up, *w, *tmp; for (up = &(daemon->watches), w = daemon->watches; w; w = tmp) { @@ -143,6 +145,7 @@ static void remove_watch(DBusWatch *watch, void *data) { *up = tmp; free(w); + watches_modified++; } else up = &(w->next); @@ -966,11 +969,11 @@ void set_dbus_listeners(void) } } -void check_dbus_listeners() +static int check_dbus_watches() { - DBusConnection *connection = (DBusConnection *)daemon->dbus; struct watch *w; + watches_modified = 0; for (w = daemon->watches; w; w = w->next) if (dbus_watch_get_enabled(w->watch)) { @@ -987,9 +990,22 @@ void check_dbus_listeners() flags |= DBUS_WATCH_ERROR; if (flags != 0) - dbus_watch_handle(w->watch, flags); + { + dbus_watch_handle(w->watch, flags); + if (watches_modified) + return 0; + } } + return 1; +} + +void check_dbus_listeners() +{ + DBusConnection *connection = (DBusConnection *)daemon->dbus; + + while (!check_dbus_watches()) ; + if (connection) { dbus_connection_ref (connection); |