On Tue, May 24, 2016 at 05:03:32PM +0200, Martin Pieuchot wrote:
> Hrvoje Popovski founds that as soon as his ARP cache reached 2097152
> entries his machine started leaking route entries.
> 
> Turns out that with that many entries he exhausted malloc(9) and
> rt_setgate() started failing, leaking the rt->rt_parent reference.
> 
> The diff below fixes the issue, ok?
> 
> Index: net/route.c
> ===================================================================
> RCS file: /cvs/src/sys/net/route.c,v
> retrieving revision 1.300
> diff -u -p -r1.300 route.c
> --- net/route.c       2 May 2016 22:15:49 -0000       1.300
> +++ net/route.c       24 May 2016 15:02:06 -0000
> @@ -1091,6 +1091,10 @@ rtrequest(int req, struct rt_addrinfo *i
>                * it to (re)order routes.
>                */
>               if ((error = rt_setgate(rt, info->rti_info[RTAX_GATEWAY]))) {
> +                     ifafree(ifa);
> +                     rtfree(rt->rt_parent);
> +                     rtfree(rt->rt_gwroute);
> +                     free(rt->rt_gateway, M_RTABLE, 0);

If rt_setgate() fails, neither rt_gwroute nor rt_gateway have been
allocated.  So freeing them should not be necessary.  But it does
not hurt either and may be safer.

>                       free(ndst, M_RTABLE, dlen);
>                       pool_put(&rtentry_pool, rt);
>                       return (error);
> @@ -1118,12 +1122,9 @@ rtrequest(int req, struct rt_addrinfo *i
>               }
>               if (error != 0) {
>                       ifafree(ifa);
> -                     if ((rt->rt_flags & RTF_CLONED) != 0 && rt->rt_parent)
> -                             rtfree(rt->rt_parent);
> -                     if (rt->rt_gwroute)
> -                             rtfree(rt->rt_gwroute);
> -                     if (rt->rt_gateway)
> -                             free(rt->rt_gateway, M_RTABLE, 0);
> +                     rtfree(rt->rt_parent);
> +                     rtfree(rt->rt_gwroute);
> +                     free(rt->rt_gateway, M_RTABLE, 0);
>                       free(ndst, M_RTABLE, dlen);
>                       pool_put(&rtentry_pool, rt);
>                       return (EEXIST);

Have you considered to move those two error handlings into a goto
error to avoid duplication?  Do you know why we return (EEXIST)
instead of (error) here?

> @@ -1163,10 +1164,7 @@ rt_setgate(struct rtentry *rt, struct so
>       }
>       memmove(rt->rt_gateway, gate, glen);
>  
> -     if (rt->rt_gwroute != NULL) {
> -             rtfree(rt->rt_gwroute);
> -             rt->rt_gwroute = NULL;
> -     }
> +     rtfree(rt->rt_gwroute);

Why have you removed the rt->rt_gwroute = NULL here?  It results
in a freed pointer that may be used later.

>  
>       return (0);
>  }

Reply via email to