forked from mirrors/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: a9b6391814 ("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>
			
			
This commit is contained in:
		
							parent
							
								
									c92e8c02fe
								
							
						
					
					
						commit
						509c7a1ecc
					
				
					 1 changed files with 16 additions and 8 deletions
				
			
		|  | @ -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; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric Dumazet
						Eric Dumazet