Commit 05923674 authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Jakub Kicinski
Browse files

arp: Validate netmask earlier for SIOCDARP and SIOCSARP in arp_ioctl().



When ioctl(SIOCDARP/SIOCSARP) is issued with ATF_PUBL, r.arp_netmask
must be 0.0.0.0 or 255.255.255.255.

Currently, the netmask is validated in arp_req_delete_public() or
arp_req_set_public() under rtnl_lock().

We have ATF_NETMASK test in arp_ioctl() before holding rtnl_lock(),
so let's move the netmask validation there.

Signed-off-by: default avatarKuniyuki Iwashima <kuniyu@amazon.com>
Link: https://lore.kernel.org/r/20240430015813.71143-3-kuniyu@amazon.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 42033d0c
Loading
Loading
Loading
Loading
+15 −12
Original line number Diff line number Diff line
@@ -1023,11 +1023,8 @@ static int arp_req_set_proxy(struct net *net, struct net_device *dev, int on)
static int arp_req_set_public(struct net *net, struct arpreq *r,
		struct net_device *dev)
{
	__be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
	__be32 mask = ((struct sockaddr_in *)&r->arp_netmask)->sin_addr.s_addr;

	if (mask && mask != htonl(0xFFFFFFFF))
		return -EINVAL;
	if (!dev && (r->arp_flags & ATF_COM)) {
		dev = dev_getbyhwaddr_rcu(net, r->arp_ha.sa_family,
				      r->arp_ha.sa_data);
@@ -1035,6 +1032,8 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,
			return -ENODEV;
	}
	if (mask) {
		__be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;

		if (!pneigh_lookup(&arp_tbl, net, &ip, dev, 1))
			return -ENOBUFS;
		return 0;
@@ -1171,14 +1170,13 @@ int arp_invalidate(struct net_device *dev, __be32 ip, bool force)
static int arp_req_delete_public(struct net *net, struct arpreq *r,
		struct net_device *dev)
{
	__be32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
	__be32 mask = ((struct sockaddr_in *)&r->arp_netmask)->sin_addr.s_addr;

	if (mask == htonl(0xFFFFFFFF))
		return pneigh_delete(&arp_tbl, net, &ip, dev);
	if (mask) {
		__be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;

	if (mask)
		return -EINVAL;
		return pneigh_delete(&arp_tbl, net, &ip, dev);
	}

	return arp_req_set_proxy(net, dev, 0);
}
@@ -1211,9 +1209,10 @@ static int arp_req_delete(struct net *net, struct arpreq *r,

int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
	int err;
	struct arpreq r;
	struct net_device *dev = NULL;
	struct arpreq r;
	__be32 *netmask;
	int err;

	switch (cmd) {
	case SIOCDARP:
@@ -1236,9 +1235,13 @@ int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg)
	if (!(r.arp_flags & ATF_PUBL) &&
	    (r.arp_flags & (ATF_NETMASK | ATF_DONTPUB)))
		return -EINVAL;

	netmask = &((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr;
	if (!(r.arp_flags & ATF_NETMASK))
		((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr =
							   htonl(0xFFFFFFFFUL);
		*netmask = htonl(0xFFFFFFFFUL);
	else if (*netmask && *netmask != htonl(0xFFFFFFFFUL))
		return -EINVAL;

	rtnl_lock();
	if (r.arp_dev[0]) {
		err = -ENODEV;