Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 509c7a1e authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

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: a9b63918 ("packet: rollover statistics")
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: John Sperbeck <jsperbeck@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c92e8c02
Loading
Loading
Loading
Loading
+16 −8
Original line number Original line Diff line number Diff line
@@ -1769,7 +1769,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)


out:
out:
	if (err && rollover) {
	if (err && rollover) {
		kfree(rollover);
		kfree_rcu(rollover, rcu);
		po->rollover = NULL;
		po->rollover = NULL;
	}
	}
	mutex_unlock(&fanout_mutex);
	mutex_unlock(&fanout_mutex);
@@ -1796,8 +1796,10 @@ static struct packet_fanout *fanout_release(struct sock *sk)
		else
		else
			f = NULL;
			f = NULL;


		if (po->rollover)
		if (po->rollover) {
			kfree_rcu(po->rollover, rcu);
			kfree_rcu(po->rollover, rcu);
			po->rollover = NULL;
		}
	}
	}
	mutex_unlock(&fanout_mutex);
	mutex_unlock(&fanout_mutex);


@@ -3851,6 +3853,7 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
	void *data = &val;
	void *data = &val;
	union tpacket_stats_u st;
	union tpacket_stats_u st;
	struct tpacket_rollover_stats rstats;
	struct tpacket_rollover_stats rstats;
	struct packet_rollover *rollover;


	if (level != SOL_PACKET)
	if (level != SOL_PACKET)
		return -ENOPROTOOPT;
		return -ENOPROTOOPT;
@@ -3929,13 +3932,18 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
		       0);
		       0);
		break;
		break;
	case PACKET_ROLLOVER_STATS:
	case PACKET_ROLLOVER_STATS:
		if (!po->rollover)
		rcu_read_lock();
			return -EINVAL;
		rollover = rcu_dereference(po->rollover);
		rstats.tp_all = atomic_long_read(&po->rollover->num);
		if (rollover) {
		rstats.tp_huge = atomic_long_read(&po->rollover->num_huge);
			rstats.tp_all = atomic_long_read(&rollover->num);
		rstats.tp_failed = atomic_long_read(&po->rollover->num_failed);
			rstats.tp_huge = atomic_long_read(&rollover->num_huge);
			rstats.tp_failed = atomic_long_read(&rollover->num_failed);
			data = &rstats;
			data = &rstats;
			lv = sizeof(rstats);
			lv = sizeof(rstats);
		}
		rcu_read_unlock();
		if (!rollover)
			return -EINVAL;
		break;
		break;
	case PACKET_TX_HAS_OFF:
	case PACKET_TX_HAS_OFF:
		val = po->tp_tx_has_off;
		val = po->tp_tx_has_off;