br_pass_frame_up() rewrites skb->dev from the ingress port to the bridge master before queueing bridge LOCAL_IN packets. NFQUEUE only holds references on state.in/out and bridge physdevs, so a queued bridge packet can retain a freed bridge master in skb->dev until reinjection. When the verdict is reinjected later, br_netif_receive_skb() re-enters the receive path with skb->dev still pointing at the freed bridge master, triggering a use-after-free. Store skb->dev in the queue entry, hold a reference on it for the queue lifetime, and use the saved device when dropping queued packets during NETDEV_DOWN handling.
Showing the changes to include/net (1 of 3 files). Show all
Unified · Split
| @@ -14,6 +14,7 @@ struct nf_queue_entry { · 13 unmodified lines | |||
| 14 | struct list_head list; | 14 | struct list_head list; |
| 15 | struct rhash_head hash_node; | 15 | struct rhash_head hash_node; |
| 16 | struct sk_buff *skb; | 16 | struct sk_buff *skb; |
| 17 | struct net_device *skb_dev; | ||
| 17 | unsigned int id; | 18 | unsigned int id; |
| 18 | unsigned int hook_index; /* index in hook_entries->hook[] */ | 19 | unsigned int hook_index; /* index in hook_entries->hook[] */ |
| 19 | #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) | 20 | #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) |