We have weird `rt_refcnt' check in rtfree():

497 rtfree(struct rtentry *rt)
498 {
        ...
504         refcnt = (int)atomic_dec_int_nv(&rt->rt_refcnt);
505         if (refcnt <= 0) {
506                 KASSERT(!ISSET(rt->rt_flags, RTF_UP));
507                 KASSERT(!RT_ROOT(rt));
508                 atomic_dec_int(&rttrash);
509                 if (refcnt < 0) {
510                         printf("rtfree: %p not freed (neg refs)\n", rt);
511                         return;
512                 }  

We underflow `rt_refcnt' when we missed to get reference to this `rt' or
we did extra release. This is the bug which should be exposed. But
according current code this `rt' is just leaked. Also it's easy to miss
this error condition because we only print error message.

I propose to put "rt->rt_refcnt > 0" assertion before we decrement
`rt_refcnt'. This makes reference counting errors more notable when they
are. Also I changed `rt_refcnt' definition to unsigned integer.

I didn't find any "rtfree: ... not freed (neg refs)" report, so it
looks like we can't hit this assertion, but I like to commit this
diff after release. But nothing stops to test it and provide feedback :)

Index: sys/net/route.c
===================================================================
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.399
diff -u -p -r1.399 route.c
--- sys/net/route.c     25 May 2021 22:45:09 -0000      1.399
+++ sys/net/route.c     14 Sep 2021 21:47:11 -0000
@@ -496,20 +496,15 @@ rtref(struct rtentry *rt)
 void
 rtfree(struct rtentry *rt)
 {
-       int              refcnt;
-
        if (rt == NULL)
                return;
 
-       refcnt = (int)atomic_dec_int_nv(&rt->rt_refcnt);
-       if (refcnt <= 0) {
+       KASSERT(rt->rt_refcnt > 0);
+
+       if (atomic_dec_int_nv(&rt->rt_refcnt) == 0) {
                KASSERT(!ISSET(rt->rt_flags, RTF_UP));
                KASSERT(!RT_ROOT(rt));
                atomic_dec_int(&rttrash);
-               if (refcnt < 0) {
-                       printf("rtfree: %p not freed (neg refs)\n", rt);
-                       return;
-               }
 
                KERNEL_LOCK();
                rt_timer_remove_all(rt);
Index: sys/net/route.h
===================================================================
RCS file: /cvs/src/sys/net/route.h,v
retrieving revision 1.185
diff -u -p -r1.185 route.h
--- sys/net/route.h     17 Mar 2021 09:05:42 -0000      1.185
+++ sys/net/route.h     14 Sep 2021 21:47:11 -0000
@@ -113,7 +113,7 @@ struct rtentry {
        struct rt_kmetrics rt_rmx;      /* metrics used by rx'ing protocols */
        unsigned int     rt_ifidx;      /* the answer: interface to use */
        unsigned int     rt_flags;      /* up/down?, host/net */
-       int              rt_refcnt;     /* # held references */
+       unsigned int     rt_refcnt;     /* # held references */
        int              rt_plen;       /* prefix length */
        uint16_t         rt_labelid;    /* route label ID */
        uint8_t          rt_priority;   /* routing priority to use */

Reply via email to