diff options
| author | Kuniyuki Iwashima <kuniyu@google.com> | 2025-07-17 01:08:09 +0300 | 
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2025-07-18 02:25:20 +0300 | 
| commit | 0e5ac19c78654abbf43dc4ffdae290c8cb81c59c (patch) | |
| tree | 8903446233de02c2ee89f789d24cecdc3ec2ce5f /net/core/neighbour.c | |
| parent | 3dfe0b57dcda070d9f1ed2bfb3a9bf0ec8632e08 (diff) | |
| download | linux-0e5ac19c78654abbf43dc4ffdae290c8cb81c59c.tar.xz | |
neighbour: Move neigh_find_table() to neigh_get().
neigh_valid_get_req() calls neigh_find_table() to fetch neigh_tables[].
neigh_find_table() uses rcu_dereference_rtnl(), but RTNL actually does
not protect it at all; neigh_table_clear() can be called without RTNL
and only waits for RCU readers by synchronize_rcu().
Fortunately, there is no bug because IPv4 is built-in, IPv6 cannot be
unloaded, and DECNET was removed.
To fetch neigh_tables[] by rcu_dereference() later, let's move
neigh_find_table() from neigh_valid_get_req() to neigh_get().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250716221221.442239-5-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/core/neighbour.c')
| -rw-r--r-- | net/core/neighbour.c | 37 | 
1 files changed, 20 insertions, 17 deletions
| diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 761593ea8c91..ffb8d80328ed 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2911,10 +2911,9 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)  }  static struct ndmsg *neigh_valid_get_req(const struct nlmsghdr *nlh, -					 struct neigh_table **tbl, void **dst, +					 struct nlattr **tb,  					 struct netlink_ext_ack *extack)  { -	struct nlattr *tb[NDA_MAX + 1];  	struct ndmsg *ndm;  	int err, i; @@ -2945,12 +2944,6 @@ static struct ndmsg *neigh_valid_get_req(const struct nlmsghdr *nlh,  	if (err < 0)  		return ERR_PTR(err); -	*tbl = neigh_find_table(ndm->ndm_family); -	if (!*tbl) { -		NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); -		return ERR_PTR(-EAFNOSUPPORT); -	} -  	for (i = 0; i <= NDA_MAX; ++i) {  		switch (i) {  		case NDA_DST: @@ -2958,12 +2951,6 @@ static struct ndmsg *neigh_valid_get_req(const struct nlmsghdr *nlh,  				NL_SET_ERR_ATTR_MISS(extack, NULL, NDA_DST);  				return ERR_PTR(-EINVAL);  			} - -			if (nla_len(tb[i]) != (int)(*tbl)->key_len) { -				NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); -				return ERR_PTR(-EINVAL); -			} -			*dst = nla_data(tb[i]);  			break;  		default:  			if (!tb[i]) @@ -3001,16 +2988,17 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,  {  	struct net *net = sock_net(in_skb->sk);  	u32 pid = NETLINK_CB(in_skb).portid; +	struct nlattr *tb[NDA_MAX + 1];  	struct net_device *dev = NULL; -	struct neigh_table *tbl = NULL;  	u32 seq = nlh->nlmsg_seq; +	struct neigh_table *tbl;  	struct neighbour *neigh;  	struct sk_buff *skb;  	struct ndmsg *ndm; -	void *dst = NULL; +	void *dst;  	int err; -	ndm = neigh_valid_get_req(nlh, &tbl, &dst, extack); +	ndm = neigh_valid_get_req(nlh, tb, extack);  	if (IS_ERR(ndm))  		return PTR_ERR(ndm); @@ -3021,6 +3009,21 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,  	if (!skb)  		return -ENOBUFS; +	tbl = neigh_find_table(ndm->ndm_family); +	if (!tbl) { +		NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); +		err = -EAFNOSUPPORT; +		goto err_free_skb; +	} + +	if (nla_len(tb[NDA_DST]) != (int)tbl->key_len) { +		NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); +		err = -EINVAL; +		goto err_free_skb; +	} + +	dst = nla_data(tb[NDA_DST]); +  	if (ndm->ndm_ifindex) {  		dev = __dev_get_by_index(net, ndm->ndm_ifindex);  		if (!dev) { | 
