dnsmasq: fix a race condition possibly leading to lockup

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@42225 3c298f89-4303-0410-b956-a3cf2f4a3e73
master
Steven Barth 2014-08-20 09:52:29 +00:00
parent 97108f1b53
commit 5401347fda
2 changed files with 278 additions and 1 deletions

View 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

View File

@ -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;
}