• Al Viro's avatar
    net: sched: cls_u32: fix hnode refcounting · 6d4c4077
    Al Viro authored
    cls_u32.c misuses refcounts for struct tc_u_hnode - it counts references
    via ->hlist and via ->tp_root together.  u32_destroy() drops the former
    and, in case when there had been links, leaves the sucker on the list.
    As the result, there's nothing to protect it from getting freed once links
    are dropped.
    That also makes the "is it busy" check incapable of catching the root
    hnode - it *is* busy (there's a reference from tp), but we don't see it as
    something separate.  "Is it our root?" check partially covers that, but
    the problem exists for others' roots as well.
    
    AFAICS, the minimal fix preserving the existing behaviour (where it doesn't
    include oopsen, that is) would be this:
            * count tp->root and tp_c->hlist as separate references.  I.e.
    have u32_init() set refcount to 2, not 1.
    	* in u32_destroy() we always drop the former;
    in u32_destroy_hnode() - the latter.
    
    	That way we have *all* references contributing to refcount.  List
    removal happens in u32_destroy_hnode() (called only when ->refcnt is 1)
    an in u32_destroy() in case of tc_u_common going away, along with
    everything reachable from it.  IOW, that way we know that
    u32_destroy_key() won't free something still on the list (or pointed to by
    someone's ->root).
    
    Reproducer:
    
    tc qdisc add dev eth0 ingress
    tc filter add dev eth0 parent ffff: protocol ip prio 100 handle 1: \
    u32 divisor 1
    tc filter add dev eth0 parent ffff: protocol ip prio 200 handle 2: \
    u32 divisor 1
    tc filter add dev eth0 parent ffff: protocol ip prio 100 \
    handle 1:0:11 u32 ht 1: link 801: offset at 0 mask 0f00 shift 6 \
    plus 0 eat match ip protocol 6 ff
    tc filter delete dev eth0 parent ffff: protocol ip prio 200
    tc filter change dev eth0 parent ffff: protocol ip prio 100 \
    handle 1:0:11 u32 ht 1: link 0: offset at 0 mask 0f00 shift 6 plus 0 \
    eat match ip protocol 6 ff
    tc filter delete dev eth0 parent ffff: protocol ip prio 100
    Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
    Signed-off-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    6d4c4077
cls_u32.c 34.2 KB