Skip to content

Commit 0c74d9f

Browse files
Sebastian Andrzej Siewiorkuba-moo
authored andcommitted
hsr: Avoid double remove of a node.
Due to the hashed-MAC optimisation one problem become visible: hsr_handle_sup_frame() walks over the list of available nodes and merges two node entries into one if based on the information in the supervision both MAC addresses belong to one node. The list-walk happens on a RCU protected list and delete operation happens under a lock. If the supervision arrives on both slave interfaces at the same time then this delete operation can occur simultaneously on two CPUs. The result is the first-CPU deletes the from the list and the second CPUs BUGs while attempting to dereference a poisoned list-entry. This happens more likely with the optimisation because a new node for the mac_B entry is created once a packet has been received and removed (merged) once the supervision frame has been received. Avoid removing/ cleaning up a hsr_node twice by adding a `removed' field which is set to true after the removal and checked before the removal. Fixes: f266a68 ("net/hsr: Better frame dispatch") Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 5aa2820 commit 0c74d9f

File tree

2 files changed

+12
-5
lines changed

2 files changed

+12
-5
lines changed

net/hsr/hsr_framereg.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,12 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
366366
node_real->addr_B_port = port_rcv->type;
367367

368368
spin_lock_bh(&hsr->list_lock);
369-
list_del_rcu(&node_curr->mac_list);
369+
if (!node_curr->removed) {
370+
list_del_rcu(&node_curr->mac_list);
371+
node_curr->removed = true;
372+
kfree_rcu(node_curr, rcu_head);
373+
}
370374
spin_unlock_bh(&hsr->list_lock);
371-
kfree_rcu(node_curr, rcu_head);
372375

373376
done:
374377
/* Push back here */
@@ -539,9 +542,12 @@ void hsr_prune_nodes(struct timer_list *t)
539542
if (time_is_before_jiffies(timestamp +
540543
msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
541544
hsr_nl_nodedown(hsr, node->macaddress_A);
542-
list_del_rcu(&node->mac_list);
543-
/* Note that we need to free this entry later: */
544-
kfree_rcu(node, rcu_head);
545+
if (!node->removed) {
546+
list_del_rcu(&node->mac_list);
547+
node->removed = true;
548+
/* Note that we need to free this entry later: */
549+
kfree_rcu(node, rcu_head);
550+
}
545551
}
546552
}
547553
spin_unlock_bh(&hsr->list_lock);

net/hsr/hsr_framereg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ struct hsr_node {
8080
bool san_a;
8181
bool san_b;
8282
u16 seq_out[HSR_PT_PORTS];
83+
bool removed;
8384
struct rcu_head rcu_head;
8485
};
8586

0 commit comments

Comments
 (0)