dnsmasq: fix a race condition possibly leading to lockup
authorSteven Barth <cyrus@openwrt.org>
Wed, 20 Aug 2014 09:52:29 +0000 (09:52 +0000)
committerSteven Barth <cyrus@openwrt.org>
Wed, 20 Aug 2014 09:52:29 +0000 (09:52 +0000)
SVN-Revision: 42225

package/network/services/dnsmasq/Makefile
package/network/services/dnsmasq/patches/002-fix-race-on-interface-flaps.patch [new file with mode: 0644]

index 1d5b201..17af3c5 100644 (file)
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=dnsmasq
 PKG_VERSION:=2.71
-PKG_RELEASE:=3
+PKG_RELEASE:=4
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=http://thekelleys.org.uk/dnsmasq
diff --git a/package/network/services/dnsmasq/patches/002-fix-race-on-interface-flaps.patch b/package/network/services/dnsmasq/patches/002-fix-race-on-interface-flaps.patch
new file mode 100644 (file)
index 0000000..814c876
--- /dev/null
@@ -0,0 +1,277 @@
+From a0358e5ddbc1ef3dec791f11f95f5dbe56087a5e Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 7 Jun 2014 13:38:48 +0100
+Subject: [PATCH] Handle async notification of address changes using the event
+ system.
+
+---
+ CHANGELOG     |  4 ++++
+ src/bpf.c     |  6 +++---
+ src/dhcp6.c   | 10 ----------
+ src/dnsmasq.c | 13 +++++++++++--
+ src/dnsmasq.h |  6 ++++--
+ src/netlink.c | 39 ++++++++++-----------------------------
+ src/network.c | 11 +++--------
+ 7 files changed, 35 insertions(+), 54 deletions(-)
+
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -15,6 +15,10 @@ version 2.71
+           regression introduced in 2.69. Thanks to James Hunt and
+           the Ubuntu crowd for assistance in fixing this.
++          Fix race condition which could lock up dnsmasq when an 
++          interface goes down and up rapidly. Thanks to Conrad 
++          Kostecki for helping to chase this down.
++          
+ version 2.70
+             Fix crash, introduced in 2.69, on TCP request when dnsmasq
+--- a/src/bpf.c
++++ b/src/bpf.c
+@@ -376,7 +376,7 @@ void route_init(void)
+     die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
+ }
+-void route_sock(time_t now)
++void route_sock(void)
+ {
+   struct if_msghdr *msg;
+   int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
+@@ -401,7 +401,7 @@ void route_sock(time_t now)
+    else if (msg->ifm_type == RTM_NEWADDR)
+      {
+        del_family = 0;
+-       newaddress(now);
++       send_newaddr();
+      }
+    else if (msg->ifm_type == RTM_DELADDR)
+      {
+@@ -439,7 +439,7 @@ void route_sock(time_t now)
+              of += sizeof(long) - (diff & (sizeof(long) - 1));
+          }
+        
+-       newaddress(now);
++       send_newaddr();
+      }
+ }
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -917,10 +917,10 @@ int main (int argc, char **argv)
+ #if defined(HAVE_LINUX_NETWORK)
+       if (FD_ISSET(daemon->netlinkfd, &rset))
+-      netlink_multicast(now);
++      netlink_multicast();
+ #elif defined(HAVE_BSD_NETWORK)
+       if (FD_ISSET(daemon->routefd, &rset))
+-      route_sock(now);
++      route_sock();
+ #endif
+       /* Check for changes to resolv files once per second max. */
+@@ -1037,6 +1037,11 @@ void send_alarm(time_t event, time_t now
+     }
+ }
++void send_newaddr(void)
++{
++  send_event(pipewrite, EVENT_NEWADDR, 0, NULL);
++}
++
+ void send_event(int fd, int event, int data, char *msg)
+ {
+   struct event_desc ev;
+@@ -1230,6 +1235,10 @@ static void async_event(int pipe, time_t
+       if (daemon->log_file != NULL)
+         log_reopen(daemon->log_file);
+       break;
++
++      case EVENT_NEWADDR:
++      newaddress(now);
++      break;
+       
+       case EVENT_TERM:
+       /* Knock all our children on the head. */
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -165,6 +165,7 @@ struct event_desc {
+ #define EVENT_LUA_ERR   19
+ #define EVENT_TFTP_ERR  20
+ #define EVENT_INIT      21
++#define EVENT_NEWADDR   22
+ /* Exit codes. */
+ #define EC_GOOD        0
+@@ -1289,6 +1290,7 @@ unsigned char *extended_hwaddr(int hwtyp
+ int make_icmp_sock(void);
+ int icmp_ping(struct in_addr addr);
+ #endif
++void send_newaddr(void);
+ void send_alarm(time_t event, time_t now);
+ void send_event(int fd, int event, int data, char *msg);
+ void clear_cache_and_reload(time_t now);
+@@ -1297,7 +1299,7 @@ void poll_resolv(int force, int do_reloa
+ /* netlink.c */
+ #ifdef HAVE_LINUX_NETWORK
+ void netlink_init(void);
+-void netlink_multicast(time_t now);
++void netlink_multicast(void);
+ #endif
+ /* bpf.c */
+@@ -1306,7 +1308,7 @@ void init_bpf(void);
+ void send_via_bpf(struct dhcp_packet *mess, size_t len,
+                 struct in_addr iface_addr, struct ifreq *ifr);
+ void route_init(void);
+-void route_sock(time_t now);
++void route_sock(void);
+ #endif
+ /* bpf.c or netlink.c */
+--- a/src/netlink.c
++++ b/src/netlink.c
+@@ -38,7 +38,7 @@
+ static struct iovec iov;
+ static u32 netlink_pid;
+-static int nl_async(struct nlmsghdr *h);
++static void nl_async(struct nlmsghdr *h);
+ void netlink_init(void)
+ {
+@@ -142,7 +142,7 @@ int iface_enumerate(int family, void *pa
+   struct nlmsghdr *h;
+   ssize_t len;
+   static unsigned int seq = 0;
+-  int callback_ok = 1, newaddr = 0;
++  int callback_ok = 1;
+   struct {
+     struct nlmsghdr nlh;
+@@ -191,21 +191,10 @@ int iface_enumerate(int family, void *pa
+       if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
+         {
+           /* May be multicast arriving async */
+-          if (nl_async(h))
+-            {
+-              newaddr = 1; 
+-              enumerate_interfaces(1); /* reset */
+-            }
++          nl_async(h);
+         }
+       else if (h->nlmsg_type == NLMSG_DONE)
+-        {
+-          /* handle async new interface address arrivals, these have to be done
+-             after we complete as we're not re-entrant */
+-          if (newaddr) 
+-            newaddress(dnsmasq_time());
+-              
+-          return callback_ok;
+-        }
++        return callback_ok;
+       else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
+         {
+           struct ifaddrmsg *ifa = NLMSG_DATA(h);  
+@@ -330,11 +319,11 @@ int iface_enumerate(int family, void *pa
+     }
+ }
+-void netlink_multicast(time_t now)
++void netlink_multicast(void)
+ {
+   ssize_t len;
+   struct nlmsghdr *h;
+-  int flags, newaddr = 0;
++  int flags;
+   
+   /* don't risk blocking reading netlink messages here. */
+   if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
+@@ -343,24 +332,19 @@ void netlink_multicast(time_t now)
+   
+   if ((len = netlink_recv()) != -1)
+     for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
+-      if (nl_async(h))
+-      newaddr = 1;
++      nl_async(h);
+   
+   /* restore non-blocking status */
+   fcntl(daemon->netlinkfd, F_SETFL, flags);
+-  
+-  if (newaddr) 
+-    newaddress(now);
+ }
+-static int nl_async(struct nlmsghdr *h)
++static void nl_async(struct nlmsghdr *h)
+ {
+   if (h->nlmsg_type == NLMSG_ERROR)
+     {
+       struct nlmsgerr *err = NLMSG_DATA(h);
+       if (err->error != 0)
+       my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
+-      return 0;
+     }
+   else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) 
+     {
+@@ -385,18 +369,15 @@ static int nl_async(struct nlmsghdr *h)
+             else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
+               fd = daemon->rfd_save->fd;
+             else
+-              return 0;
++              return;
+             
+             while(sendto(fd, daemon->packet, daemon->packet_len, 0,
+                          &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
+           }
+       }
+-      return 0;
+     }
+   else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) 
+-    return 1; /* clever bind mode - rescan */
+-  
+-  return 0;
++    send_newaddr();
+ }
+ #endif
+--- a/src/network.c
++++ b/src/network.c
+@@ -551,7 +551,7 @@ static int iface_allowed_v4(struct in_ad
+ int enumerate_interfaces(int reset)
+ {
+   static struct addrlist *spare = NULL;
+-  static int done = 0, active = 0;
++  static int done = 0;
+   struct iface_param param;
+   int errsave, ret = 1;
+   struct addrlist *addr, *tmp;
+@@ -570,14 +570,11 @@ int enumerate_interfaces(int reset)
+       return 1;
+     }
+-  if (done || active)
++  if (done)
+     return 1;
+   done = 1;
+-  /* protect against recusive calls from iface_enumerate(); */
+-  active = 1;
+-
+   if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+     return 0;
+  
+@@ -677,10 +674,8 @@ int enumerate_interfaces(int reset)
+     }
+   
+   errno = errsave;
+-  
+   spare = param.spare;
+-  active = 0;
+-  
++    
+   return ret;
+ }