On Sat, Apr 23, 2022 at 04:48:00PM +0300, kasak wrote:
> ipsp_ids_gc(0) at ipsp_ids_gc+0xb4
> softclock_thread(ffff800022baf260) at softclock_thread+0x13b

This code was modified between 7.0 and 7.1.  It crashes here:

/usr/src/sys/netinet/ip_ipsp.c:1266
*     b4:       41 83 7e 64 00          cmpl   $0x0,0x64(%r14)
      b9:       75 46                   jne    101 <ipsp_ids_gc+0x101>
      bb:       4d 8b 3e                mov    (%r14),%r15
/usr/src/sys/netinet/ip_ipsp.c:1269

  1257  /* free ids only from delayed timeout */
  1258  void
  1259  ipsp_ids_gc(void *arg)
  1260  {
  1261          struct ipsec_ids *ids, *tids;
  1262  
  1263          mtx_enter(&ipsec_flows_mtx);
  1264  
  1265          LIST_FOREACH_SAFE(ids, &ipsp_ids_gc_list, id_gc_list, tids) {
* 1266                  KASSERT(ids->id_refcount == 0);
  1267                  DPRINTF("ids %p count %d", ids, ids->id_refcount);
  1268  
  1269                  if ((--ids->id_gc_ttl) > 0)
  1270                          continue;
  1271  
  1272                  LIST_REMOVE(ids, id_gc_list);
  1273                  RBT_REMOVE(ipsec_ids_tree, &ipsec_ids_tree, ids);
  1274                  RBT_REMOVE(ipsec_ids_flows, &ipsec_ids_flows, ids);
  1275                  free(ids->id_local, M_CREDENTIALS, 0);
  1276                  free(ids->id_remote, M_CREDENTIALS, 0);
  1277                  free(ids, M_CREDENTIALS, 0);
  1278          }
  1279  
  1280          if (!LIST_EMPTY(&ipsp_ids_gc_list))
  1281                  timeout_add_sec(&ipsp_ids_gc_timeout, 1);
  1282  
  1283          mtx_leave(&ipsec_flows_mtx);
  1284  }

I would suggest to replace the timeout garbage collector with
reference counting.  This diff is only compile tested as I have no
setup for IPsec ids yet.  Should work on 7.1 an -current.

bluhm

Index: net/pfkeyv2.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/pfkeyv2.c,v
retrieving revision 1.233
diff -u -p -r1.233 pfkeyv2.c
--- net/pfkeyv2.c       13 Mar 2022 21:38:32 -0000      1.233
+++ net/pfkeyv2.c       25 Apr 2022 13:35:56 -0000
@@ -1969,8 +1969,8 @@ pfkeyv2_send(struct socket *so, void *me
 
                ipo->ipo_sproto = SADB_X_GETSPROTO(smsg->sadb_msg_satype);
 
-               if (ipo->ipo_ids) {
-                       ipsp_ids_free(ipo->ipo_ids);
+               if (ipo->ipo_ids != NULL) {
+                       ipsp_ids_unref(ipo->ipo_ids);
                        ipo->ipo_ids = NULL;
                }
 
@@ -2015,8 +2015,8 @@ pfkeyv2_send(struct socket *so, void *me
                                        ipo->ipo_tdb = NULL;
                                }
                                mtx_leave(&ipo_tdb_mtx);
-                               if (ipo->ipo_ids)
-                                       ipsp_ids_free(ipo->ipo_ids);
+                               if (ipo->ipo_ids != NULL)
+                                       ipsp_ids_unref(ipo->ipo_ids);
                                pool_put(&ipsec_policy_pool, ipo);
                                NET_UNLOCK();
                                goto ret;
Index: net/pfkeyv2_convert.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/pfkeyv2_convert.c,v
retrieving revision 1.79
diff -u -p -r1.79 pfkeyv2_convert.c
--- net/pfkeyv2_convert.c       20 Jan 2022 17:13:12 -0000      1.79
+++ net/pfkeyv2_convert.c       25 Apr 2022 13:41:36 -0000
@@ -752,6 +752,7 @@ import_identities(struct ipsec_ids **ids
        import_identity(&tmp->id_local, swapped ? dstid: srcid, &id_local_sz);
        import_identity(&tmp->id_remote, swapped ? srcid: dstid, &id_remote_sz);
        if (tmp->id_local != NULL && tmp->id_remote != NULL) {
+               /* ipsp_ids_insert increments refcount, ref stored in *ids */
                *ids = ipsp_ids_insert(tmp);
                if (*ids == tmp)
                        return;
Index: netinet/ip_ipsp.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_ipsp.c,v
retrieving revision 1.269
diff -u -p -r1.269 ip_ipsp.c
--- netinet/ip_ipsp.c   10 Mar 2022 15:21:08 -0000      1.269
+++ netinet/ip_ipsp.c   25 Apr 2022 13:34:27 -0000
@@ -113,13 +113,6 @@ struct ipsec_ids_flows ipsec_ids_flows;    
 struct ipsec_policy_head ipsec_policy_head =
     TAILQ_HEAD_INITIALIZER(ipsec_policy_head);
 
-void ipsp_ids_gc(void *);
-
-LIST_HEAD(, ipsec_ids) ipsp_ids_gc_list =
-    LIST_HEAD_INITIALIZER(ipsp_ids_gc_list);   /* [F] */
-struct timeout ipsp_ids_gc_timeout =
-    TIMEOUT_INITIALIZER_FLAGS(ipsp_ids_gc, NULL, TIMEOUT_PROC);
-
 static inline int ipsp_ids_cmp(const struct ipsec_ids *,
     const struct ipsec_ids *);
 static inline int ipsp_ids_flow_cmp(const struct ipsec_ids *,
@@ -1088,16 +1081,12 @@ tdb_free(struct tdb *tdbp)
 
        KASSERT(TAILQ_EMPTY(&tdbp->tdb_policy_head));
 
-       if (tdbp->tdb_ids) {
-               ipsp_ids_free(tdbp->tdb_ids);
-               tdbp->tdb_ids = NULL;
-       }
+       if (tdbp->tdb_ids != NULL)
+               ipsp_ids_unref(tdbp->tdb_ids);
 
 #if NPF > 0
-       if (tdbp->tdb_tag) {
+       if (tdbp->tdb_tag)
                pf_tag_unref(tdbp->tdb_tag);
-               tdbp->tdb_tag = 0;
-       }
 #endif
 
        counters_free(tdbp->tdb_counters, tdb_ncounters);
@@ -1204,19 +1193,13 @@ ipsp_ids_insert(struct ipsec_ids *ids)
 
        found = RBT_INSERT(ipsec_ids_tree, &ipsec_ids_tree, ids);
        if (found) {
-               /* if refcount was zero, then timeout is running */
-               if (atomic_inc_int_nv(&found->id_refcount) == 1) {
-                       LIST_REMOVE(found, id_gc_list);
-
-                       if (LIST_EMPTY(&ipsp_ids_gc_list))
-                               timeout_del(&ipsp_ids_gc_timeout);
-               }
+               refcnt_take(&found->id_refcnt);
                mtx_leave (&ipsec_flows_mtx);
                DPRINTF("ids %p count %d", found, found->id_refcount);
                return found;
        }
 
-       ids->id_refcount = 1;
+       refcnt_init(&ids->id_refcnt);
        ids->id_flow = start_flow = ipsec_ids_next_flow;
 
        if (++ipsec_ids_next_flow == 0)
@@ -1233,7 +1216,11 @@ ipsp_ids_insert(struct ipsec_ids *ids)
                        return NULL;
                }
        }
+       /* one ref count for the RB trees and one for the return value */
+       refcnt_take(&ids->id_refcnt);
+
        mtx_leave(&ipsec_flows_mtx);
+
        DPRINTF("new ids %p flow %u", ids, ids->id_flow);
        return ids;
 }
@@ -1248,71 +1235,27 @@ ipsp_ids_lookup(u_int32_t ipsecflowinfo)
 
        mtx_enter(&ipsec_flows_mtx);
        ids = RBT_FIND(ipsec_ids_flows, &ipsec_ids_flows, &key);
-       atomic_inc_int(&ids->id_refcount);
+       refcnt_take(&ids->id_refcnt);
        mtx_leave(&ipsec_flows_mtx);
 
        return ids;
 }
 
-/* free ids only from delayed timeout */
 void
-ipsp_ids_gc(void *arg)
+ipsp_ids_unref(struct ipsec_ids *ids)
 {
-       struct ipsec_ids *ids, *tids;
-
-       mtx_enter(&ipsec_flows_mtx);
-
-       LIST_FOREACH_SAFE(ids, &ipsp_ids_gc_list, id_gc_list, tids) {
-               KASSERT(ids->id_refcount == 0);
-               DPRINTF("ids %p count %d", ids, ids->id_refcount);
-
-               if ((--ids->id_gc_ttl) > 0)
-                       continue;
-
-               LIST_REMOVE(ids, id_gc_list);
-               RBT_REMOVE(ipsec_ids_tree, &ipsec_ids_tree, ids);
-               RBT_REMOVE(ipsec_ids_flows, &ipsec_ids_flows, ids);
-               free(ids->id_local, M_CREDENTIALS, 0);
-               free(ids->id_remote, M_CREDENTIALS, 0);
-               free(ids, M_CREDENTIALS, 0);
-       }
-
-       if (!LIST_EMPTY(&ipsp_ids_gc_list))
-               timeout_add_sec(&ipsp_ids_gc_timeout, 1);
-
-       mtx_leave(&ipsec_flows_mtx);
-}
-
-/* decrements refcount, actual free happens in gc */
-void
-ipsp_ids_free(struct ipsec_ids *ids)
-{
-       if (ids == NULL)
-               return;
-
-       /*
-        * If the refcount becomes zero, then a timeout is started. This
-        * timeout must be cancelled if refcount is increased from zero.
-        */
-       DPRINTF("ids %p count %d", ids, ids->id_refcount);
-       KASSERT(ids->id_refcount > 0);
-
-       if (atomic_dec_int_nv(&ids->id_refcount) > 0)
+       if (refcnt_rele(&ids->id_refcnt) == 0)
                return;
 
        mtx_enter(&ipsec_flows_mtx);
-
-       /*
-        * Add second for the case ipsp_ids_gc() is already running and
-        * awaits netlock to be released.
-        */
-       ids->id_gc_ttl = ipsec_ids_idle + 1;
-
-       if (LIST_EMPTY(&ipsp_ids_gc_list))
-               timeout_add_sec(&ipsp_ids_gc_timeout, 1);
-       LIST_INSERT_HEAD(&ipsp_ids_gc_list, ids, id_gc_list);
-
+       LIST_REMOVE(ids, id_gc_list);
+       RBT_REMOVE(ipsec_ids_tree, &ipsec_ids_tree, ids);
+       RBT_REMOVE(ipsec_ids_flows, &ipsec_ids_flows, ids);
        mtx_leave(&ipsec_flows_mtx);
+
+       free(ids->id_local, M_CREDENTIALS, 0);
+       free(ids->id_remote, M_CREDENTIALS, 0);
+       free(ids, M_CREDENTIALS, sizeof(*ids));
 }
 
 static int
Index: netinet/ip_ipsp.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_ipsp.h,v
retrieving revision 1.238
diff -u -p -r1.238 ip_ipsp.h
--- netinet/ip_ipsp.h   21 Apr 2022 15:22:50 -0000      1.238
+++ netinet/ip_ipsp.h   25 Apr 2022 13:43:24 -0000
@@ -238,11 +238,10 @@ struct ipsec_ids {
        LIST_ENTRY(ipsec_ids)   id_gc_list;     /* [F] */
        RBT_ENTRY(ipsec_ids)    id_node_id;     /* [F] */
        RBT_ENTRY(ipsec_ids)    id_node_flow;   /* [F] */
+       struct refcnt           id_refcnt;
        struct ipsec_id         *id_local;      /* [I] */
        struct ipsec_id         *id_remote;     /* [I] */
        u_int32_t               id_flow;        /* [I] */
-       u_int                   id_refcount;    /* [a] */
-       u_int                   id_gc_ttl;      /* [F] */
 };
 RBT_HEAD(ipsec_ids_flows, ipsec_ids);
 RBT_HEAD(ipsec_ids_tree, ipsec_ids);
@@ -674,7 +673,7 @@ int ipsp_aux_match(struct tdb *, struct 
 int    ipsp_ids_match(struct ipsec_ids *, struct ipsec_ids *);
 struct ipsec_ids *ipsp_ids_insert(struct ipsec_ids *);
 struct ipsec_ids *ipsp_ids_lookup(u_int32_t);
-void   ipsp_ids_free(struct ipsec_ids *);
+void   ipsp_ids_unref(struct ipsec_ids *);
 
 void   ipsp_init(void);
 void   ipsec_init(void);
Index: netinet/ip_output.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.380
diff -u -p -r1.380 ip_output.c
--- netinet/ip_output.c 4 Jan 2022 06:32:39 -0000       1.380
+++ netinet/ip_output.c 25 Apr 2022 13:30:17 -0000
@@ -549,7 +549,8 @@ ip_output_ipsec_lookup(struct mbuf *m, i
                ids = ipsp_ids_lookup(ipsecflowinfo);
        error = ipsp_spd_lookup(m, AF_INET, hlen, IPSP_DIRECTION_OUT,
            NULL, inp, &tdb, ids);
-       ipsp_ids_free(ids);
+       if (ids != NULL)
+               ipsp_ids_unref(ids);
        if (error || tdb == NULL) {
                *tdbout = NULL;
                return error;
Index: netinet/ip_spd.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_spd.c,v
retrieving revision 1.115
diff -u -p -r1.115 ip_spd.c
--- netinet/ip_spd.c    13 Mar 2022 21:38:32 -0000      1.115
+++ netinet/ip_spd.c    25 Apr 2022 13:34:51 -0000
@@ -538,7 +538,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, 
                                goto nomatchin;
 
                        /* Match source/dest IDs. */
-                       if (ipo->ipo_ids)
+                       if (ipo->ipo_ids != NULL)
                                if (tdbp->tdb_ids == NULL ||
                                    !ipsp_ids_match(ipo->ipo_ids, 
tdbp->tdb_ids))
                                        goto nomatchin;
@@ -696,8 +696,8 @@ ipsec_delete_policy(struct ipsec_policy 
 
        TAILQ_REMOVE(&ipsec_policy_head, ipo, ipo_list);
 
-       if (ipo->ipo_ids)
-               ipsp_ids_free(ipo->ipo_ids);
+       if (ipo->ipo_ids != NULL)
+               ipsp_ids_unref(ipo->ipo_ids);
 
        ipsec_in_use--;
 

Reply via email to