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

tipc: move bc link creation back to tipc_node_create

Shuang Li reported a NULL pointer dereference crash:

[] BUG: kernel NULL pointer dereference, address: 0000000000000068
[] RIP: 0010:tipc_link_is_up+0x5/0x10 [tipc]
[] Call Trace:
[] <IRQ>
[] tipc_bcast_rcv+0xa2/0x190 [tipc]
[] tipc_node_bc_rcv+0x8b/0x200 [tipc]
[] tipc_rcv+0x3af/0x5b0 [tipc]
[] tipc_udp_recv+0xc7/0x1e0 [tipc]

It was caused by the 'l' passed into tipc_bcast_rcv() is NULL. When it
creates a node in tipc_node_check_dest(), after inserting the new node
into hashtable in tipc_node_create(), it creates the bc link. However,
there is a gap between this insert and bc link creation, a bc packet
may come in and get the node from the hashtable then try to dereference
its bc link, which is NULL.

This patch is to fix it by moving the bc link creation before inserting
into the hashtable.

Note that for a preliminary node becoming "real", the bc link creation
should also be called before it's rehashed, as we don't create it for
preliminary nodes.

Fixes: 4cbf8ac2fe5a ("tipc: enable creating a "preliminary" node")
Reported-by: Shuang Li <shuali@redhat.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Jon Maloy <jmaloy@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Xin Long and committed by
David S. Miller
cb8092d7 853a7614

+22 -19
+22 -19
net/tipc/node.c
··· 472 472 bool preliminary) 473 473 { 474 474 struct tipc_net *tn = net_generic(net, tipc_net_id); 475 + struct tipc_link *l, *snd_l = tipc_bc_sndlink(net); 475 476 struct tipc_node *n, *temp_node; 476 - struct tipc_link *l; 477 477 unsigned long intv; 478 478 int bearer_id; 479 479 int i; ··· 488 488 goto exit; 489 489 /* A preliminary node becomes "real" now, refresh its data */ 490 490 tipc_node_write_lock(n); 491 + if (!tipc_link_bc_create(net, tipc_own_addr(net), addr, peer_id, U16_MAX, 492 + tipc_link_min_win(snd_l), tipc_link_max_win(snd_l), 493 + n->capabilities, &n->bc_entry.inputq1, 494 + &n->bc_entry.namedq, snd_l, &n->bc_entry.link)) { 495 + pr_warn("Broadcast rcv link refresh failed, no memory\n"); 496 + tipc_node_write_unlock_fast(n); 497 + tipc_node_put(n); 498 + n = NULL; 499 + goto exit; 500 + } 491 501 n->preliminary = false; 492 502 n->addr = addr; 493 503 hlist_del_rcu(&n->hash); ··· 577 567 n->signature = INVALID_NODE_SIG; 578 568 n->active_links[0] = INVALID_BEARER_ID; 579 569 n->active_links[1] = INVALID_BEARER_ID; 580 - n->bc_entry.link = NULL; 570 + if (!preliminary && 571 + !tipc_link_bc_create(net, tipc_own_addr(net), addr, peer_id, U16_MAX, 572 + tipc_link_min_win(snd_l), tipc_link_max_win(snd_l), 573 + n->capabilities, &n->bc_entry.inputq1, 574 + &n->bc_entry.namedq, snd_l, &n->bc_entry.link)) { 575 + pr_warn("Broadcast rcv link creation failed, no memory\n"); 576 + kfree(n); 577 + n = NULL; 578 + goto exit; 579 + } 581 580 tipc_node_get(n); 582 581 timer_setup(&n->timer, tipc_node_timeout, 0); 583 582 /* Start a slow timer anyway, crypto needs it */ ··· 1174 1155 bool *respond, bool *dupl_addr) 1175 1156 { 1176 1157 struct tipc_node *n; 1177 - struct tipc_link *l, *snd_l; 1158 + struct tipc_link *l; 1178 1159 struct tipc_link_entry *le; 1179 1160 bool addr_match = false; 1180 1161 bool sign_match = false; ··· 1194 1175 return; 1195 1176 1196 1177 tipc_node_write_lock(n); 1197 - if (unlikely(!n->bc_entry.link)) { 1198 - snd_l = tipc_bc_sndlink(net); 1199 - if (!tipc_link_bc_create(net, tipc_own_addr(net), 1200 - addr, peer_id, U16_MAX, 1201 - tipc_link_min_win(snd_l), 1202 - tipc_link_max_win(snd_l), 1203 - n->capabilities, 1204 - &n->bc_entry.inputq1, 1205 - &n->bc_entry.namedq, snd_l, 1206 - &n->bc_entry.link)) { 1207 - pr_warn("Broadcast rcv link creation failed, no mem\n"); 1208 - tipc_node_write_unlock_fast(n); 1209 - tipc_node_put(n); 1210 - return; 1211 - } 1212 - } 1213 1178 1214 1179 le = &n->links[b->identity]; 1215 1180