diff options
Diffstat (limited to 'net/core')
| -rw-r--r-- | net/core/dev.c | 25 | ||||
| -rw-r--r-- | net/core/filter.c | 34 | ||||
| -rw-r--r-- | net/core/flow_offload.c | 6 | ||||
| -rw-r--r-- | net/core/net-sysfs.c | 1 | ||||
| -rw-r--r-- | net/core/skmsg.c | 5 | 
5 files changed, 55 insertions, 16 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 08ce317fcec8..8e6f22961206 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -397,16 +397,18 @@ static void list_netdevice(struct net_device *dev)  /* Device list removal   * caller must respect a RCU grace period before freeing/reusing dev   */ -static void unlist_netdevice(struct net_device *dev) +static void unlist_netdevice(struct net_device *dev, bool lock)  {  	ASSERT_RTNL();  	/* Unlink dev from the device chain */ -	write_lock(&dev_base_lock); +	if (lock) +		write_lock(&dev_base_lock);  	list_del_rcu(&dev->dev_list);  	netdev_name_node_del(dev->name_node);  	hlist_del_rcu(&dev->index_hlist); -	write_unlock(&dev_base_lock); +	if (lock) +		write_unlock(&dev_base_lock);  	dev_base_seq_inc(dev_net(dev));  } @@ -10043,11 +10045,11 @@ int register_netdevice(struct net_device *dev)  		goto err_uninit;  	ret = netdev_register_kobject(dev); -	if (ret) { -		dev->reg_state = NETREG_UNREGISTERED; +	write_lock(&dev_base_lock); +	dev->reg_state = ret ? NETREG_UNREGISTERED : NETREG_REGISTERED; +	write_unlock(&dev_base_lock); +	if (ret)  		goto err_uninit; -	} -	dev->reg_state = NETREG_REGISTERED;  	__netdev_update_features(dev); @@ -10329,7 +10331,9 @@ void netdev_run_todo(void)  			continue;  		} +		write_lock(&dev_base_lock);  		dev->reg_state = NETREG_UNREGISTERED; +		write_unlock(&dev_base_lock);  		linkwatch_forget_dev(dev);  	} @@ -10810,9 +10814,10 @@ void unregister_netdevice_many(struct list_head *head)  	list_for_each_entry(dev, head, unreg_list) {  		/* And unlink it from device chain. */ -		unlist_netdevice(dev); - +		write_lock(&dev_base_lock); +		unlist_netdevice(dev, false);  		dev->reg_state = NETREG_UNREGISTERING; +		write_unlock(&dev_base_lock);  	}  	flush_all_backlogs(); @@ -10959,7 +10964,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,  	dev_close(dev);  	/* And unlink it from device chain */ -	unlist_netdevice(dev); +	unlist_netdevice(dev, true);  	synchronize_net(); diff --git a/net/core/filter.c b/net/core/filter.c index 5af58eb48587..5d16d66727fc 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6516,10 +6516,21 @@ __bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,  					   ifindex, proto, netns_id, flags);  	if (sk) { -		sk = sk_to_full_sk(sk); -		if (!sk_fullsock(sk)) { +		struct sock *sk2 = sk_to_full_sk(sk); + +		/* sk_to_full_sk() may return (sk)->rsk_listener, so make sure the original sk +		 * sock refcnt is decremented to prevent a request_sock leak. +		 */ +		if (!sk_fullsock(sk2)) +			sk2 = NULL; +		if (sk2 != sk) {  			sock_gen_put(sk); -			return NULL; +			/* Ensure there is no need to bump sk2 refcnt */ +			if (unlikely(sk2 && !sock_flag(sk2, SOCK_RCU_FREE))) { +				WARN_ONCE(1, "Found non-RCU, unreferenced socket!"); +				return NULL; +			} +			sk = sk2;  		}  	} @@ -6553,10 +6564,21 @@ bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,  					 flags);  	if (sk) { -		sk = sk_to_full_sk(sk); -		if (!sk_fullsock(sk)) { +		struct sock *sk2 = sk_to_full_sk(sk); + +		/* sk_to_full_sk() may return (sk)->rsk_listener, so make sure the original sk +		 * sock refcnt is decremented to prevent a request_sock leak. +		 */ +		if (!sk_fullsock(sk2)) +			sk2 = NULL; +		if (sk2 != sk) {  			sock_gen_put(sk); -			return NULL; +			/* Ensure there is no need to bump sk2 refcnt */ +			if (unlikely(sk2 && !sock_flag(sk2, SOCK_RCU_FREE))) { +				WARN_ONCE(1, "Found non-RCU, unreferenced socket!"); +				return NULL; +			} +			sk = sk2;  		}  	} diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c index 73f68d4625f3..929f6379a279 100644 --- a/net/core/flow_offload.c +++ b/net/core/flow_offload.c @@ -595,3 +595,9 @@ int flow_indr_dev_setup_offload(struct net_device *dev,	struct Qdisc *sch,  	return (bo && list_empty(&bo->cb_list)) ? -EOPNOTSUPP : count;  }  EXPORT_SYMBOL(flow_indr_dev_setup_offload); + +bool flow_indr_dev_exists(void) +{ +	return !list_empty(&flow_block_indr_dev_list); +} +EXPORT_SYMBOL(flow_indr_dev_exists); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index e319e242dddf..a3642569fe53 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -33,6 +33,7 @@ static const char fmt_dec[] = "%d\n";  static const char fmt_ulong[] = "%lu\n";  static const char fmt_u64[] = "%llu\n"; +/* Caller holds RTNL or dev_base_lock */  static inline int dev_isalive(const struct net_device *dev)  {  	return dev->reg_state <= NETREG_REGISTERED; diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 22b983ade0e7..b0fcd0200e84 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -699,6 +699,11 @@ struct sk_psock *sk_psock_init(struct sock *sk, int node)  	write_lock_bh(&sk->sk_callback_lock); +	if (sk_is_inet(sk) && inet_csk_has_ulp(sk)) { +		psock = ERR_PTR(-EINVAL); +		goto out; +	} +  	if (sk->sk_user_data) {  		psock = ERR_PTR(-EBUSY);  		goto out;  | 
