Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tipc: fix unbalanced reference counter

When a topology subscription is created, we may encounter (or KASAN
may provoke) a failure to create a corresponding service instance in
the binding table. Instead of letting the tipc_nametbl_subscribe()
report the failure back to the caller, the function just makes a warning
printout and returns, without incrementing the subscription reference
counter as expected by the caller.

This makes the caller believe that the subscription was successful, so
it will at a later moment try to unsubscribe the item. This involves
a sub_put() call. Since the reference counter never was incremented
in the first place, we get a premature delete of the subscription item,
followed by a "use-after-free" warning.

We fix this by adding a return value to tipc_nametbl_subscribe() and
make the caller aware of the failure to subscribe.

This bug seems to always have been around, but this fix only applies
back to the commit shown below. Given the low risk of this happening
we believe this to be sufficient.

Fixes: commit 218527fe27ad ("tipc: replace name table service range
array with rb tree")
Reported-by: syzbot+aa245f26d42b8305d157@syzkaller.appspotmail.com

Signed-off-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jon Maloy and committed by
David S. Miller
c3317f4d 1c2734b3

+9 -3
+4 -1
net/tipc/name_table.c
··· 665 665 /** 666 666 * tipc_nametbl_subscribe - add a subscription object to the name table 667 667 */ 668 - void tipc_nametbl_subscribe(struct tipc_subscription *sub) 668 + bool tipc_nametbl_subscribe(struct tipc_subscription *sub) 669 669 { 670 670 struct name_table *nt = tipc_name_table(sub->net); 671 671 struct tipc_net *tn = tipc_net(sub->net); 672 672 struct tipc_subscr *s = &sub->evt.s; 673 673 u32 type = tipc_sub_read(s, seq.type); 674 674 struct tipc_service *sc; 675 + bool res = true; 675 676 676 677 spin_lock_bh(&tn->nametbl_lock); 677 678 sc = tipc_service_find(sub->net, type); ··· 686 685 pr_warn("Failed to subscribe for {%u,%u,%u}\n", type, 687 686 tipc_sub_read(s, seq.lower), 688 687 tipc_sub_read(s, seq.upper)); 688 + res = false; 689 689 } 690 690 spin_unlock_bh(&tn->nametbl_lock); 691 + return res; 691 692 } 692 693 693 694 /**
+1 -1
net/tipc/name_table.h
··· 126 126 struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, 127 127 u32 lower, u32 upper, 128 128 u32 node, u32 key); 129 - void tipc_nametbl_subscribe(struct tipc_subscription *s); 129 + bool tipc_nametbl_subscribe(struct tipc_subscription *s); 130 130 void tipc_nametbl_unsubscribe(struct tipc_subscription *s); 131 131 int tipc_nametbl_init(struct net *net); 132 132 void tipc_nametbl_stop(struct net *net);
+4 -1
net/tipc/subscr.c
··· 153 153 memcpy(&sub->evt.s, s, sizeof(*s)); 154 154 spin_lock_init(&sub->lock); 155 155 kref_init(&sub->kref); 156 - tipc_nametbl_subscribe(sub); 156 + if (!tipc_nametbl_subscribe(sub)) { 157 + kfree(sub); 158 + return NULL; 159 + } 157 160 timer_setup(&sub->timer, tipc_sub_timeout, 0); 158 161 timeout = tipc_sub_read(&sub->evt.s, timeout); 159 162 if (timeout != TIPC_WAIT_FOREVER)