Re: carp(4) fix vs NET_LOCK

2016-12-20 Thread Alexander Bluhm
On Tue, Dec 20, 2016 at 03:09:11PM +0100, Martin Pieuchot wrote:
> No we can't because we're already holding it.  So carp_master_down()
> must be split in two, diff below does that and only grab the lock in
> the timer routine.

OK bluhm@

> 
> Index: netinet/ip_carp.c
> ===
> RCS file: /cvs/src/sys/netinet/ip_carp.c,v
> retrieving revision 1.297
> diff -u -p -r1.297 ip_carp.c
> --- netinet/ip_carp.c 19 Dec 2016 08:36:49 -  1.297
> +++ netinet/ip_carp.c 20 Dec 2016 14:04:55 -
> @@ -221,9 +221,11 @@ int  carp_prepare_ad(struct mbuf *, struc
>   struct carp_header *);
>  void carp_send_ad_all(void);
>  void carp_vhe_send_ad_all(struct carp_softc *);
> -void carp_send_ad(void *);
> +void carp_timer_ad(void *);
> +void carp_send_ad(struct carp_vhost_entry *);
>  void carp_send_arp(struct carp_softc *);
> -void carp_master_down(void *);
> +void carp_timer_down(void *);
> +void carp_master_down(struct carp_vhost_entry *);
>  int  carp_ioctl(struct ifnet *, u_long, caddr_t);
>  int  carp_vhids_ioctl(struct carp_softc *, struct carpreq *);
>  int  carp_check_dup_vhids(struct carp_softc *, struct carp_if *,
> @@ -831,9 +833,9 @@ carp_new_vhost(struct carp_softc *sc, in
>   vhe->vhid = vhid;
>   vhe->advskew = advskew;
>   vhe->state = INIT;
> - timeout_set_proc(>ad_tmo, carp_send_ad, vhe);
> - timeout_set_proc(>md_tmo, carp_master_down, vhe);
> - timeout_set_proc(>md6_tmo, carp_master_down, vhe);
> + timeout_set_proc(>ad_tmo, carp_timer_ad, vhe);
> + timeout_set_proc(>md_tmo, carp_timer_down, vhe);
> + timeout_set_proc(>md6_tmo, carp_timer_down, vhe);
>  
>   KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
>  
> @@ -1027,26 +1029,34 @@ carp_vhe_send_ad_all(struct carp_softc *
>  }
>  
>  void
> -carp_send_ad(void *v)
> +carp_timer_ad(void *v)
> +{
> + int s;
> +
> + NET_LOCK(s);
> + carp_send_ad(v);
> + NET_UNLOCK(s);
> +}
> +
> +void
> +carp_send_ad(struct carp_vhost_entry *vhe)
>  {
>   struct carp_header ch;
>   struct timeval tv;
> - struct carp_vhost_entry *vhe = v;
>   struct carp_softc *sc = vhe->parent_sc;
>   struct carp_header *ch_ptr;
> -
>   struct mbuf *m;
> - int error, len, advbase, advskew, s;
> + int error, len, advbase, advskew;
>   struct ifaddr *ifa;
>   struct sockaddr sa;
>  
> + NET_ASSERT_LOCKED();
> +
>   if (sc->sc_carpdev == NULL) {
>   sc->sc_if.if_oerrors++;
>   return;
>   }
>  
> - NET_LOCK(s);
> -
>   /* bow out if we've gone to backup (the carp interface is going down) */
>   if (sc->sc_bow_out) {
>   advbase = 255;
> @@ -1246,7 +1256,6 @@ carp_send_ad(void *v)
>  
>  retry_later:
>   sc->cur_vhe = NULL;
> - NET_UNLOCK(s);
>   if (advbase != 255 || advskew != 255)
>   timeout_add(>ad_tmo, tvtohz());
>  }
> @@ -1261,7 +1270,6 @@ carp_send_arp(struct carp_softc *sc)
>  {
>   struct ifaddr *ifa;
>   in_addr_t in;
> - int s = splsoftnet();
>  
>   TAILQ_FOREACH(ifa, >sc_if.if_addrlist, ifa_list) {
>  
> @@ -1272,7 +1280,6 @@ carp_send_arp(struct carp_softc *sc)
>   arprequest(>sc_if, , , sc->sc_ac.ac_enaddr);
>   DELAY(1000);/* XXX */
>   }
> - splx(s);
>  }
>  
>  #ifdef INET6
> @@ -1282,7 +1289,6 @@ carp_send_na(struct carp_softc *sc)
>   struct ifaddr *ifa;
>   struct in6_addr *in6;
>   static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
> - int s = splsoftnet();
>  
>   TAILQ_FOREACH(ifa, >sc_if.if_addrlist, ifa_list) {
>  
> @@ -1295,7 +1301,6 @@ carp_send_na(struct carp_softc *sc)
>   (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), 1, NULL);
>   DELAY(1000);/* XXX */
>   }
> - splx(s);
>  }
>  #endif /* INET6 */
>  
> @@ -1504,10 +1509,21 @@ done:
>  }
>  
>  void
> -carp_master_down(void *v)
> +carp_timer_down(void *v)
> +{
> + int s;
> +
> + NET_LOCK(s);
> + carp_master_down(v);
> + NET_UNLOCK(s);
> +}
> +
> +void
> +carp_master_down(struct carp_vhost_entry *vhe)
>  {
> - struct carp_vhost_entry *vhe = v;
>   struct carp_softc *sc = vhe->parent_sc;
> +
> + NET_ASSERT_LOCKED();
>  
>   switch (vhe->state) {
>   case INIT:



Re: carp(4) fix vs NET_LOCK

2016-12-20 Thread Martin Pieuchot
On 20/12/16(Tue) 14:31, Alexander Bluhm wrote:
> On Tue, Dec 20, 2016 at 12:08:57PM +0100, Martin Pieuchot wrote:
> > I introduced a bug in carp(4), one code path tries to take the NET_LOCK()
> > twice:
> > 
> > panic() at panic+0xfe
> > rw_enter() at rw_enter+0x1f8
> > carp_send_ad() at carp_send_ad+0x4d
> > carp_vhe_send_ad_all() at carp_vhe_send_ad_all+0x4b
> > carp_ioctl() at carp_ioctl+0x491
> > ifioctl() at ifioctl+0x7be
> > soo_ioctl() at soo_ioctl+0x260
> > sys_ioctl() at sys_ioctl+0x196
> > syscall() at syscall+0x27b
> > 
> > Diff below fixes that by respecting the rule of always taking the
> > NET_LOCK() at the beginning of every timer. 
> > 
> > While here remove two recursive splsoftnet() dances.
> > 
> > ok?
> 
> What about
> 
> carp_proto_input
> carp_proto_input_if
> carp_proto_input_c
> carp_master_down
> NET_LOCK

Good catch.

> May we take the NET_LOCK in the ip input path?

No we can't because we're already holding it.  So carp_master_down()
must be split in two, diff below does that and only grab the lock in
the timer routine.

Index: netinet/ip_carp.c
===
RCS file: /cvs/src/sys/netinet/ip_carp.c,v
retrieving revision 1.297
diff -u -p -r1.297 ip_carp.c
--- netinet/ip_carp.c   19 Dec 2016 08:36:49 -  1.297
+++ netinet/ip_carp.c   20 Dec 2016 14:04:55 -
@@ -221,9 +221,11 @@ intcarp_prepare_ad(struct mbuf *, struc
struct carp_header *);
 void   carp_send_ad_all(void);
 void   carp_vhe_send_ad_all(struct carp_softc *);
-void   carp_send_ad(void *);
+void   carp_timer_ad(void *);
+void   carp_send_ad(struct carp_vhost_entry *);
 void   carp_send_arp(struct carp_softc *);
-void   carp_master_down(void *);
+void   carp_timer_down(void *);
+void   carp_master_down(struct carp_vhost_entry *);
 intcarp_ioctl(struct ifnet *, u_long, caddr_t);
 intcarp_vhids_ioctl(struct carp_softc *, struct carpreq *);
 intcarp_check_dup_vhids(struct carp_softc *, struct carp_if *,
@@ -831,9 +833,9 @@ carp_new_vhost(struct carp_softc *sc, in
vhe->vhid = vhid;
vhe->advskew = advskew;
vhe->state = INIT;
-   timeout_set_proc(>ad_tmo, carp_send_ad, vhe);
-   timeout_set_proc(>md_tmo, carp_master_down, vhe);
-   timeout_set_proc(>md6_tmo, carp_master_down, vhe);
+   timeout_set_proc(>ad_tmo, carp_timer_ad, vhe);
+   timeout_set_proc(>md_tmo, carp_timer_down, vhe);
+   timeout_set_proc(>md6_tmo, carp_timer_down, vhe);
 
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
 
@@ -1027,26 +1029,34 @@ carp_vhe_send_ad_all(struct carp_softc *
 }
 
 void
-carp_send_ad(void *v)
+carp_timer_ad(void *v)
+{
+   int s;
+
+   NET_LOCK(s);
+   carp_send_ad(v);
+   NET_UNLOCK(s);
+}
+
+void
+carp_send_ad(struct carp_vhost_entry *vhe)
 {
struct carp_header ch;
struct timeval tv;
-   struct carp_vhost_entry *vhe = v;
struct carp_softc *sc = vhe->parent_sc;
struct carp_header *ch_ptr;
-
struct mbuf *m;
-   int error, len, advbase, advskew, s;
+   int error, len, advbase, advskew;
struct ifaddr *ifa;
struct sockaddr sa;
 
+   NET_ASSERT_LOCKED();
+
if (sc->sc_carpdev == NULL) {
sc->sc_if.if_oerrors++;
return;
}
 
-   NET_LOCK(s);
-
/* bow out if we've gone to backup (the carp interface is going down) */
if (sc->sc_bow_out) {
advbase = 255;
@@ -1246,7 +1256,6 @@ carp_send_ad(void *v)
 
 retry_later:
sc->cur_vhe = NULL;
-   NET_UNLOCK(s);
if (advbase != 255 || advskew != 255)
timeout_add(>ad_tmo, tvtohz());
 }
@@ -1261,7 +1270,6 @@ carp_send_arp(struct carp_softc *sc)
 {
struct ifaddr *ifa;
in_addr_t in;
-   int s = splsoftnet();
 
TAILQ_FOREACH(ifa, >sc_if.if_addrlist, ifa_list) {
 
@@ -1272,7 +1280,6 @@ carp_send_arp(struct carp_softc *sc)
arprequest(>sc_if, , , sc->sc_ac.ac_enaddr);
DELAY(1000);/* XXX */
}
-   splx(s);
 }
 
 #ifdef INET6
@@ -1282,7 +1289,6 @@ carp_send_na(struct carp_softc *sc)
struct ifaddr *ifa;
struct in6_addr *in6;
static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
-   int s = splsoftnet();
 
TAILQ_FOREACH(ifa, >sc_if.if_addrlist, ifa_list) {
 
@@ -1295,7 +1301,6 @@ carp_send_na(struct carp_softc *sc)
(ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), 1, NULL);
DELAY(1000);/* XXX */
}
-   splx(s);
 }
 #endif /* INET6 */
 
@@ -1504,10 +1509,21 @@ done:
 }
 
 void
-carp_master_down(void *v)
+carp_timer_down(void *v)
+{
+   int s;
+
+   NET_LOCK(s);
+   carp_master_down(v);
+   NET_UNLOCK(s);
+}
+
+void
+carp_master_down(struct carp_vhost_entry *vhe)
 {
-   struct carp_vhost_entry *vhe = v;
struct carp_softc *sc = 

Re: carp(4) fix vs NET_LOCK

2016-12-20 Thread Alexander Bluhm
On Tue, Dec 20, 2016 at 12:08:57PM +0100, Martin Pieuchot wrote:
> I introduced a bug in carp(4), one code path tries to take the NET_LOCK()
> twice:
> 
>   panic() at panic+0xfe
>   rw_enter() at rw_enter+0x1f8
>   carp_send_ad() at carp_send_ad+0x4d
>   carp_vhe_send_ad_all() at carp_vhe_send_ad_all+0x4b
>   carp_ioctl() at carp_ioctl+0x491
>   ifioctl() at ifioctl+0x7be
>   soo_ioctl() at soo_ioctl+0x260
>   sys_ioctl() at sys_ioctl+0x196
>   syscall() at syscall+0x27b
> 
> Diff below fixes that by respecting the rule of always taking the
> NET_LOCK() at the beginning of every timer. 
> 
> While here remove two recursive splsoftnet() dances.
> 
> ok?

What about

carp_proto_input
carp_proto_input_if
carp_proto_input_c
carp_master_down
NET_LOCK

May we take the NET_LOCK in the ip input path?

bluhm

> 
> Index: netinet/ip_carp.c
> ===
> RCS file: /cvs/src/sys/netinet/ip_carp.c,v
> retrieving revision 1.297
> diff -u -p -r1.297 ip_carp.c
> --- netinet/ip_carp.c 19 Dec 2016 08:36:49 -  1.297
> +++ netinet/ip_carp.c 20 Dec 2016 11:03:22 -
> @@ -221,7 +221,8 @@ int   carp_prepare_ad(struct mbuf *, struc
>   struct carp_header *);
>  void carp_send_ad_all(void);
>  void carp_vhe_send_ad_all(struct carp_softc *);
> -void carp_send_ad(void *);
> +void carp_timer_ad(void *);
> +void carp_send_ad(struct carp_vhost_entry *);
>  void carp_send_arp(struct carp_softc *);
>  void carp_master_down(void *);
>  int  carp_ioctl(struct ifnet *, u_long, caddr_t);
> @@ -831,7 +832,7 @@ carp_new_vhost(struct carp_softc *sc, in
>   vhe->vhid = vhid;
>   vhe->advskew = advskew;
>   vhe->state = INIT;
> - timeout_set_proc(>ad_tmo, carp_send_ad, vhe);
> + timeout_set_proc(>ad_tmo, carp_timer_ad, vhe);
>   timeout_set_proc(>md_tmo, carp_master_down, vhe);
>   timeout_set_proc(>md6_tmo, carp_master_down, vhe);
>  
> @@ -1027,26 +1028,34 @@ carp_vhe_send_ad_all(struct carp_softc *
>  }
>  
>  void
> -carp_send_ad(void *v)
> +carp_timer_ad(void *v)
> +{
> + int s;
> +
> + NET_LOCK(s);
> + carp_send_ad(v);
> + NET_UNLOCK(s);
> +}
> +
> +void
> +carp_send_ad(struct carp_vhost_entry *vhe)
>  {
>   struct carp_header ch;
>   struct timeval tv;
> - struct carp_vhost_entry *vhe = v;
>   struct carp_softc *sc = vhe->parent_sc;
>   struct carp_header *ch_ptr;
> -
>   struct mbuf *m;
> - int error, len, advbase, advskew, s;
> + int error, len, advbase, advskew;
>   struct ifaddr *ifa;
>   struct sockaddr sa;
>  
> + NET_ASSERT_LOCKED();
> +
>   if (sc->sc_carpdev == NULL) {
>   sc->sc_if.if_oerrors++;
>   return;
>   }
>  
> - NET_LOCK(s);
> -
>   /* bow out if we've gone to backup (the carp interface is going down) */
>   if (sc->sc_bow_out) {
>   advbase = 255;
> @@ -1246,7 +1255,6 @@ carp_send_ad(void *v)
>  
>  retry_later:
>   sc->cur_vhe = NULL;
> - NET_UNLOCK(s);
>   if (advbase != 255 || advskew != 255)
>   timeout_add(>ad_tmo, tvtohz());
>  }
> @@ -1261,7 +1269,6 @@ carp_send_arp(struct carp_softc *sc)
>  {
>   struct ifaddr *ifa;
>   in_addr_t in;
> - int s = splsoftnet();
>  
>   TAILQ_FOREACH(ifa, >sc_if.if_addrlist, ifa_list) {
>  
> @@ -1272,7 +1279,6 @@ carp_send_arp(struct carp_softc *sc)
>   arprequest(>sc_if, , , sc->sc_ac.ac_enaddr);
>   DELAY(1000);/* XXX */
>   }
> - splx(s);
>  }
>  
>  #ifdef INET6
> @@ -1282,7 +1288,6 @@ carp_send_na(struct carp_softc *sc)
>   struct ifaddr *ifa;
>   struct in6_addr *in6;
>   static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
> - int s = splsoftnet();
>  
>   TAILQ_FOREACH(ifa, >sc_if.if_addrlist, ifa_list) {
>  
> @@ -1295,7 +1300,6 @@ carp_send_na(struct carp_softc *sc)
>   (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), 1, NULL);
>   DELAY(1000);/* XXX */
>   }
> - splx(s);
>  }
>  #endif /* INET6 */
>  
> @@ -1508,7 +1512,9 @@ carp_master_down(void *v)
>  {
>   struct carp_vhost_entry *vhe = v;
>   struct carp_softc *sc = vhe->parent_sc;
> + int s;
>  
> + NET_LOCK(s);
>   switch (vhe->state) {
>   case INIT:
>   printf("%s: master_down event in INIT state\n",
> @@ -1531,6 +1537,7 @@ carp_master_down(void *v)
>   carpstats.carps_preempt++;
>   break;
>   }
> + NET_UNLOCK(s);
>  }
>  
>  void



carp(4) fix vs NET_LOCK

2016-12-20 Thread Martin Pieuchot
I introduced a bug in carp(4), one code path tries to take the NET_LOCK()
twice:

panic() at panic+0xfe
rw_enter() at rw_enter+0x1f8
carp_send_ad() at carp_send_ad+0x4d
carp_vhe_send_ad_all() at carp_vhe_send_ad_all+0x4b
carp_ioctl() at carp_ioctl+0x491
ifioctl() at ifioctl+0x7be
soo_ioctl() at soo_ioctl+0x260
sys_ioctl() at sys_ioctl+0x196
syscall() at syscall+0x27b

Diff below fixes that by respecting the rule of always taking the
NET_LOCK() at the beginning of every timer. 

While here remove two recursive splsoftnet() dances.

ok?

Index: netinet/ip_carp.c
===
RCS file: /cvs/src/sys/netinet/ip_carp.c,v
retrieving revision 1.297
diff -u -p -r1.297 ip_carp.c
--- netinet/ip_carp.c   19 Dec 2016 08:36:49 -  1.297
+++ netinet/ip_carp.c   20 Dec 2016 11:03:22 -
@@ -221,7 +221,8 @@ int carp_prepare_ad(struct mbuf *, struc
struct carp_header *);
 void   carp_send_ad_all(void);
 void   carp_vhe_send_ad_all(struct carp_softc *);
-void   carp_send_ad(void *);
+void   carp_timer_ad(void *);
+void   carp_send_ad(struct carp_vhost_entry *);
 void   carp_send_arp(struct carp_softc *);
 void   carp_master_down(void *);
 intcarp_ioctl(struct ifnet *, u_long, caddr_t);
@@ -831,7 +832,7 @@ carp_new_vhost(struct carp_softc *sc, in
vhe->vhid = vhid;
vhe->advskew = advskew;
vhe->state = INIT;
-   timeout_set_proc(>ad_tmo, carp_send_ad, vhe);
+   timeout_set_proc(>ad_tmo, carp_timer_ad, vhe);
timeout_set_proc(>md_tmo, carp_master_down, vhe);
timeout_set_proc(>md6_tmo, carp_master_down, vhe);
 
@@ -1027,26 +1028,34 @@ carp_vhe_send_ad_all(struct carp_softc *
 }
 
 void
-carp_send_ad(void *v)
+carp_timer_ad(void *v)
+{
+   int s;
+
+   NET_LOCK(s);
+   carp_send_ad(v);
+   NET_UNLOCK(s);
+}
+
+void
+carp_send_ad(struct carp_vhost_entry *vhe)
 {
struct carp_header ch;
struct timeval tv;
-   struct carp_vhost_entry *vhe = v;
struct carp_softc *sc = vhe->parent_sc;
struct carp_header *ch_ptr;
-
struct mbuf *m;
-   int error, len, advbase, advskew, s;
+   int error, len, advbase, advskew;
struct ifaddr *ifa;
struct sockaddr sa;
 
+   NET_ASSERT_LOCKED();
+
if (sc->sc_carpdev == NULL) {
sc->sc_if.if_oerrors++;
return;
}
 
-   NET_LOCK(s);
-
/* bow out if we've gone to backup (the carp interface is going down) */
if (sc->sc_bow_out) {
advbase = 255;
@@ -1246,7 +1255,6 @@ carp_send_ad(void *v)
 
 retry_later:
sc->cur_vhe = NULL;
-   NET_UNLOCK(s);
if (advbase != 255 || advskew != 255)
timeout_add(>ad_tmo, tvtohz());
 }
@@ -1261,7 +1269,6 @@ carp_send_arp(struct carp_softc *sc)
 {
struct ifaddr *ifa;
in_addr_t in;
-   int s = splsoftnet();
 
TAILQ_FOREACH(ifa, >sc_if.if_addrlist, ifa_list) {
 
@@ -1272,7 +1279,6 @@ carp_send_arp(struct carp_softc *sc)
arprequest(>sc_if, , , sc->sc_ac.ac_enaddr);
DELAY(1000);/* XXX */
}
-   splx(s);
 }
 
 #ifdef INET6
@@ -1282,7 +1288,6 @@ carp_send_na(struct carp_softc *sc)
struct ifaddr *ifa;
struct in6_addr *in6;
static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
-   int s = splsoftnet();
 
TAILQ_FOREACH(ifa, >sc_if.if_addrlist, ifa_list) {
 
@@ -1295,7 +1300,6 @@ carp_send_na(struct carp_softc *sc)
(ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), 1, NULL);
DELAY(1000);/* XXX */
}
-   splx(s);
 }
 #endif /* INET6 */
 
@@ -1508,7 +1512,9 @@ carp_master_down(void *v)
 {
struct carp_vhost_entry *vhe = v;
struct carp_softc *sc = vhe->parent_sc;
+   int s;
 
+   NET_LOCK(s);
switch (vhe->state) {
case INIT:
printf("%s: master_down event in INIT state\n",
@@ -1531,6 +1537,7 @@ carp_master_down(void *v)
carpstats.carps_preempt++;
break;
}
+   NET_UNLOCK(s);
 }
 
 void