net: validate veth and vxcan peer ifindexes
authorJakub Kicinski <kuba@kernel.org>
Sat, 19 Aug 2023 01:26:02 +0000 (18:26 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sun, 20 Aug 2023 10:40:03 +0000 (11:40 +0100)
veth and vxcan need to make sure the ifindexes of the peer
are not negative, core does not validate this.

Using iproute2 with user-space-level checking removed:

Before:

  # ./ip link add index 10 type veth peer index -1
  # ip link show
  1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:74:b2:03 brd ff:ff:ff:ff:ff:ff
  10: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 8a:90:ff:57:6d:5d brd ff:ff:ff:ff:ff:ff
  -1: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ae:ed:18:e6:fa:7f brd ff:ff:ff:ff:ff:ff

Now:

  $ ./ip link add index 10 type veth peer index -1
  Error: ifindex can't be negative.

This problem surfaced in net-next because an explicit WARN()
was added, the root cause is older.

Fixes: e6f8f1a739b6 ("veth: Allow to create peer link with given ifindex")
Fixes: a8f820a380a2 ("can: add Virtual CAN Tunnel driver (vxcan)")
Reported-by: syzbot+5ba06978f34abb058571@syzkaller.appspotmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/can/vxcan.c
drivers/net/veth.c
include/net/rtnetlink.h
net/core/rtnetlink.c

index 4068d96..98c669a 100644 (file)
@@ -192,12 +192,7 @@ static int vxcan_newlink(struct net *net, struct net_device *dev,
 
                nla_peer = data[VXCAN_INFO_PEER];
                ifmp = nla_data(nla_peer);
-               err = rtnl_nla_parse_ifla(peer_tb,
-                                         nla_data(nla_peer) +
-                                         sizeof(struct ifinfomsg),
-                                         nla_len(nla_peer) -
-                                         sizeof(struct ifinfomsg),
-                                         NULL);
+               err = rtnl_nla_parse_ifinfomsg(peer_tb, nla_peer, extack);
                if (err < 0)
                        return err;
 
index 509e901..ef8eacb 100644 (file)
@@ -1861,10 +1861,7 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
 
                nla_peer = data[VETH_INFO_PEER];
                ifmp = nla_data(nla_peer);
-               err = rtnl_nla_parse_ifla(peer_tb,
-                                         nla_data(nla_peer) + sizeof(struct ifinfomsg),
-                                         nla_len(nla_peer) - sizeof(struct ifinfomsg),
-                                         NULL);
+               err = rtnl_nla_parse_ifinfomsg(peer_tb, nla_peer, extack);
                if (err < 0)
                        return err;
 
index d9076a7..6506221 100644 (file)
@@ -190,8 +190,8 @@ int rtnl_delete_link(struct net_device *dev, u32 portid, const struct nlmsghdr *
 int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm,
                        u32 portid, const struct nlmsghdr *nlh);
 
-int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len,
-                       struct netlink_ext_ack *exterr);
+int rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer,
+                            struct netlink_ext_ack *exterr);
 struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid);
 
 #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind)
index aef25aa..bcebdeb 100644 (file)
@@ -2268,13 +2268,27 @@ out_err:
        return err;
 }
 
-int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len,
-                       struct netlink_ext_ack *exterr)
+int rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer,
+                            struct netlink_ext_ack *exterr)
 {
-       return nla_parse_deprecated(tb, IFLA_MAX, head, len, ifla_policy,
+       const struct ifinfomsg *ifmp;
+       const struct nlattr *attrs;
+       size_t len;
+
+       ifmp = nla_data(nla_peer);
+       attrs = nla_data(nla_peer) + sizeof(struct ifinfomsg);
+       len = nla_len(nla_peer) - sizeof(struct ifinfomsg);
+
+       if (ifmp->ifi_index < 0) {
+               NL_SET_ERR_MSG_ATTR(exterr, nla_peer,
+                                   "ifindex can't be negative");
+               return -EINVAL;
+       }
+
+       return nla_parse_deprecated(tb, IFLA_MAX, attrs, len, ifla_policy,
                                    exterr);
 }
-EXPORT_SYMBOL(rtnl_nla_parse_ifla);
+EXPORT_SYMBOL(rtnl_nla_parse_ifinfomsg);
 
 struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[])
 {