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

packet: avoid panic in packet_getsockopt()

syzkaller got crashes in packet_getsockopt() processing
PACKET_ROLLOVER_STATS command while another thread was managing
to change po->rollover

Using RCU will fix this bug. We might later add proper RCU annotations
for sparse sake.

In v2: I replaced kfree(rollover) in fanout_add() to kfree_rcu()
variant, as spotted by John.

Fixes: a9b6391814d5 ("packet: rollover statistics")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: John Sperbeck <jsperbeck@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
509c7a1e c92e8c02

+16 -8
+16 -8
net/packet/af_packet.c
··· 1769 1769 1770 1770 out: 1771 1771 if (err && rollover) { 1772 - kfree(rollover); 1772 + kfree_rcu(rollover, rcu); 1773 1773 po->rollover = NULL; 1774 1774 } 1775 1775 mutex_unlock(&fanout_mutex); ··· 1796 1796 else 1797 1797 f = NULL; 1798 1798 1799 - if (po->rollover) 1799 + if (po->rollover) { 1800 1800 kfree_rcu(po->rollover, rcu); 1801 + po->rollover = NULL; 1802 + } 1801 1803 } 1802 1804 mutex_unlock(&fanout_mutex); 1803 1805 ··· 3853 3851 void *data = &val; 3854 3852 union tpacket_stats_u st; 3855 3853 struct tpacket_rollover_stats rstats; 3854 + struct packet_rollover *rollover; 3856 3855 3857 3856 if (level != SOL_PACKET) 3858 3857 return -ENOPROTOOPT; ··· 3932 3929 0); 3933 3930 break; 3934 3931 case PACKET_ROLLOVER_STATS: 3935 - if (!po->rollover) 3932 + rcu_read_lock(); 3933 + rollover = rcu_dereference(po->rollover); 3934 + if (rollover) { 3935 + rstats.tp_all = atomic_long_read(&rollover->num); 3936 + rstats.tp_huge = atomic_long_read(&rollover->num_huge); 3937 + rstats.tp_failed = atomic_long_read(&rollover->num_failed); 3938 + data = &rstats; 3939 + lv = sizeof(rstats); 3940 + } 3941 + rcu_read_unlock(); 3942 + if (!rollover) 3936 3943 return -EINVAL; 3937 - rstats.tp_all = atomic_long_read(&po->rollover->num); 3938 - rstats.tp_huge = atomic_long_read(&po->rollover->num_huge); 3939 - rstats.tp_failed = atomic_long_read(&po->rollover->num_failed); 3940 - data = &rstats; 3941 - lv = sizeof(rstats); 3942 3944 break; 3943 3945 case PACKET_TX_HAS_OFF: 3944 3946 val = po->tp_tx_has_off;