Hi!

Interesting crash in dnsmasq were reported to me. I can reproduce it reliably on RHEL9, but not anymore on the most recent Fedora. But the difference seems to be based on used dbus library, not depending on dnsmasq code. RHEL9 dbus libraries installs 2 daemon->watchers, Fedora rawhide version does not. If those watchers are present and dbus system instance is restarted, dnsmasq crashes. RHEL9 uses dbus-libs-1.12.20-7.el9_1.x86_64.

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.

In my first patch I fixed that problem by restarting the loop if list were modified.

Second patch is just optimization of socket events handling of dbus. Reduces calls to locate the file descriptor structure. Should lower CPU usage when monitoring dbus watches. It is not directly related to the issue, I just noticed repeated function calls where just one were needed.

Red Hat bug: https://bugzilla.redhat.com/show_bug.cgi?id=2185878

Cheers,
Petr

--
Petr Menšík
Software Engineer, RHEL
Red Hat, https://www.redhat.com/
PGP: DFCF908DB7C87E8E529925BC4931CA5B6C9FC5CB
From 7abc1f58988b1f299ce1dd008c48890314f3581b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com>
Date: Thu, 13 Apr 2023 14:31:22 +0200
Subject: [PATCH 2/2] Small optimization around dbus socket events

Reduce calls to fd_search function and use get or set all flags by a
single call. Then do some bit operations and set the flags.

This is done in hope compiler will be optimize separate flag checks and
using just single integer. DBUS_WATCH_READABLE should equal to POLLIN on
normal system anyway.
---
 src/dbus.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/src/dbus.c b/src/dbus.c
index c2142b4..f58fb46 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -961,14 +961,14 @@ void set_dbus_listeners(void)
       {
 	unsigned int flags = dbus_watch_get_flags(w->watch);
 	int fd = dbus_watch_get_unix_fd(w->watch);
+	int poll_flags = POLLERR;
 	
 	if (flags & DBUS_WATCH_READABLE)
-	  poll_listen(fd, POLLIN);
-	
+	  poll_flags |= POLLIN;
 	if (flags & DBUS_WATCH_WRITABLE)
-	  poll_listen(fd, POLLOUT);
+	  poll_flags |= POLLOUT;
 	
-	poll_listen(fd, POLLERR);
+	poll_listen(fd, poll_flags);
       }
 }
 
@@ -982,14 +982,13 @@ static int check_dbus_watches()
       {
 	unsigned int flags = 0;
 	int fd = dbus_watch_get_unix_fd(w->watch);
-	
-	if (poll_check(fd, POLLIN))
+	int poll_flags = poll_check(fd, POLLIN|POLLOUT|POLLERR);
+
+	if ((poll_flags & POLLIN) != 0)
 	  flags |= DBUS_WATCH_READABLE;
-	
-	if (poll_check(fd, POLLOUT))
+	if ((poll_flags & POLLOUT) != 0)
 	  flags |= DBUS_WATCH_WRITABLE;
-	
-	if (poll_check(fd, POLLERR))
+	if ((poll_flags & POLLERR) != 0)
 	  flags |= DBUS_WATCH_ERROR;
 
 	if (flags != 0)
-- 
2.39.2

From 9ac7a5433527f88b3603c00769c4545011abaa17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com>
Date: Thu, 13 Apr 2023 12:59:17 +0200
Subject: [PATCH 1/2] Watch modification of dbus watches list

If watches are modified during dbus_watch_handle() call, existing
watch pointers might be invalidated. Always restart the loop if
modification is detected. Should prevent crashes when dbus service is
restarted.
---
 src/dbus.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/src/dbus.c b/src/dbus.c
index 360985a..c2142b4 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -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);
@@ -969,11 +972,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))
       {
@@ -990,9 +993,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);
-- 
2.39.2

_______________________________________________
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss

Reply via email to