Message ID | 20240401100005.1799-1-dkirjanov@suse.de (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | [4,net] RDMA/core: fix UAF with ib_device_get_netdev() | expand |
On 2024-04-01 at 15:30:05, Denis Kirjanov (kirjanov@gmail.com) wrote: > A call to ib_device_get_netdev may lead to a race condition > while accessing a netdevice instance since we don't hold > the rtnl lock while checking > the registration state: > if (res && res->reg_state != NETREG_REGISTERED) { > > v2: unlock rtnl on error path > v3: update remaining callers of ib_device_get_netdev > v4: don't call a cb with rtnl lock in ib_enum_roce_netdev > > Reported-by: syzbot+5fe14f2ff4ccbace9a26@syzkaller.appspotmail.com > Fixes: d41861942fc55 ("IB/core: Add generic function to extract IB speed from netdev") > Signed-off-by: Denis Kirjanov <dkirjanov@suse.de> > --- > drivers/infiniband/core/cache.c | 2 ++ > drivers/infiniband/core/device.c | 15 ++++++++++++--- > drivers/infiniband/core/nldev.c | 3 +++ > drivers/infiniband/core/verbs.c | 6 ++++-- > 4 files changed, 21 insertions(+), 5 deletions(-) > > diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c > index c02a96d3572a..cf9c826cd520 100644 > --- a/drivers/infiniband/core/cache.c > +++ b/drivers/infiniband/core/cache.c > @@ -1461,7 +1461,9 @@ static int config_non_roce_gid_cache(struct ib_device *device, > if (rdma_protocol_iwarp(device, port)) { > struct net_device *ndev; > > + rtnl_lock(); > ndev = ib_device_get_netdev(device, port); > + rtnl_unlock(); Why dont you move the rtnl_lock()/_unlock() inside ib_device_get_netdev(). ib_device_get_netdev() hold ref to dev, so can access ndev safely here. > if (!ndev) > continue; > RCU_INIT_POINTER(gid_attr.ndev, ndev); > diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c > index 07cb6c5ffda0..25edb50d2b64 100644 > --- a/drivers/infiniband/core/device.c > +++ b/drivers/infiniband/core/device.c > @@ -2026,9 +2026,12 @@ static int iw_query_port(struct ib_device *device, > > memset(port_attr, 0, sizeof(*port_attr)); > > + rtnl_lock(); > netdev = ib_device_get_netdev(device, port_num); > - if (!netdev) > + if (!netdev) { > + rtnl_unlock(); > return -ENODEV; > + } > > port_attr->max_mtu = IB_MTU_4096; > port_attr->active_mtu = ib_mtu_int_to_enum(netdev->mtu); > @@ -2052,6 +2055,7 @@ static int iw_query_port(struct ib_device *device, > rcu_read_unlock(); > } > > + rtnl_unlock(); > dev_put(netdev); > return device->ops.query_port(device, port_num, port_attr); > } > @@ -2220,6 +2224,8 @@ struct net_device *ib_device_get_netdev(struct ib_device *ib_dev, > struct ib_port_data *pdata; > struct net_device *res; > > + ASSERT_RTNL(); > + > if (!rdma_is_port_valid(ib_dev, port)) > return NULL; > > @@ -2306,8 +2312,11 @@ void ib_enum_roce_netdev(struct ib_device *ib_dev, > > rdma_for_each_port (ib_dev, port) > if (rdma_protocol_roce(ib_dev, port)) { > - struct net_device *idev = > - ib_device_get_netdev(ib_dev, port); > + struct net_device *idev; > + > + rtnl_lock(); > + idev = ib_device_get_netdev(ib_dev, port); > + rtnl_unlock(); > > if (filter(ib_dev, port, idev, filter_cookie)) > cb(ib_dev, port, idev, cookie); > diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c > index 4900a0848124..5cf7cdae8925 100644 > --- a/drivers/infiniband/core/nldev.c > +++ b/drivers/infiniband/core/nldev.c > @@ -360,7 +360,9 @@ static int fill_port_info(struct sk_buff *msg, > if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state)) > return -EMSGSIZE; > > + rtnl_lock(); > netdev = ib_device_get_netdev(device, port); > + > if (netdev && net_eq(dev_net(netdev), net)) { > ret = nla_put_u32(msg, > RDMA_NLDEV_ATTR_NDEV_INDEX, netdev->ifindex); > @@ -371,6 +373,7 @@ static int fill_port_info(struct sk_buff *msg, > } > > out: > + rtnl_unlock(); > dev_put(netdev); > return ret; > } > diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c > index 94a7f3b0c71c..6a3757b00c93 100644 > --- a/drivers/infiniband/core/verbs.c > +++ b/drivers/infiniband/core/verbs.c > @@ -1976,11 +1976,13 @@ int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) > if (rdma_port_get_link_layer(dev, port_num) != IB_LINK_LAYER_ETHERNET) > return -EINVAL; > > + rtnl_lock(); > netdev = ib_device_get_netdev(dev, port_num); > - if (!netdev) > + if (!netdev) { > + rtnl_unlock(); > return -ENODEV; > + } > > - rtnl_lock(); > rc = __ethtool_get_link_ksettings(netdev, &lksettings); > rtnl_unlock(); > > -- > 2.30.2 >
On 4/1/24 16:35, Ratheesh Kannoth wrote: > On 2024-04-01 at 15:30:05, Denis Kirjanov (kirjanov@gmail.com) wrote: >> A call to ib_device_get_netdev may lead to a race condition >> while accessing a netdevice instance since we don't hold >> the rtnl lock while checking >> the registration state: >> if (res && res->reg_state != NETREG_REGISTERED) { >> >> v2: unlock rtnl on error path >> v3: update remaining callers of ib_device_get_netdev >> v4: don't call a cb with rtnl lock in ib_enum_roce_netdev >> >> Reported-by: syzbot+5fe14f2ff4ccbace9a26@syzkaller.appspotmail.com >> Fixes: d41861942fc55 ("IB/core: Add generic function to extract IB speed from netdev") >> Signed-off-by: Denis Kirjanov <dkirjanov@suse.de> >> --- >> drivers/infiniband/core/cache.c | 2 ++ >> drivers/infiniband/core/device.c | 15 ++++++++++++--- >> drivers/infiniband/core/nldev.c | 3 +++ >> drivers/infiniband/core/verbs.c | 6 ++++-- >> 4 files changed, 21 insertions(+), 5 deletions(-) >> >> diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c >> index c02a96d3572a..cf9c826cd520 100644 >> --- a/drivers/infiniband/core/cache.c >> +++ b/drivers/infiniband/core/cache.c >> @@ -1461,7 +1461,9 @@ static int config_non_roce_gid_cache(struct ib_device *device, >> if (rdma_protocol_iwarp(device, port)) { >> struct net_device *ndev; >> >> + rtnl_lock(); >> ndev = ib_device_get_netdev(device, port); >> + rtnl_unlock(); > Why dont you move the rtnl_lock()/_unlock() inside ib_device_get_netdev(). > ib_device_get_netdev() hold ref to dev, so can access ndev safely here. Makes sense. and it makes the whole change pretty small > >> if (!ndev) >> continue; >> RCU_INIT_POINTER(gid_attr.ndev, ndev); >> diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c >> index 07cb6c5ffda0..25edb50d2b64 100644 >> --- a/drivers/infiniband/core/device.c >> +++ b/drivers/infiniband/core/device.c >> @@ -2026,9 +2026,12 @@ static int iw_query_port(struct ib_device *device, >> >> memset(port_attr, 0, sizeof(*port_attr)); >> >> + rtnl_lock(); >> netdev = ib_device_get_netdev(device, port_num); >> - if (!netdev) >> + if (!netdev) { >> + rtnl_unlock(); >> return -ENODEV; >> + } >> >> port_attr->max_mtu = IB_MTU_4096; >> port_attr->active_mtu = ib_mtu_int_to_enum(netdev->mtu); >> @@ -2052,6 +2055,7 @@ static int iw_query_port(struct ib_device *device, >> rcu_read_unlock(); >> } >> >> + rtnl_unlock(); >> dev_put(netdev); >> return device->ops.query_port(device, port_num, port_attr); >> } >> @@ -2220,6 +2224,8 @@ struct net_device *ib_device_get_netdev(struct ib_device *ib_dev, >> struct ib_port_data *pdata; >> struct net_device *res; >> >> + ASSERT_RTNL(); >> + >> if (!rdma_is_port_valid(ib_dev, port)) >> return NULL; >> >> @@ -2306,8 +2312,11 @@ void ib_enum_roce_netdev(struct ib_device *ib_dev, >> >> rdma_for_each_port (ib_dev, port) >> if (rdma_protocol_roce(ib_dev, port)) { >> - struct net_device *idev = >> - ib_device_get_netdev(ib_dev, port); >> + struct net_device *idev; >> + >> + rtnl_lock(); >> + idev = ib_device_get_netdev(ib_dev, port); >> + rtnl_unlock(); >> >> if (filter(ib_dev, port, idev, filter_cookie)) >> cb(ib_dev, port, idev, cookie); >> diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c >> index 4900a0848124..5cf7cdae8925 100644 >> --- a/drivers/infiniband/core/nldev.c >> +++ b/drivers/infiniband/core/nldev.c >> @@ -360,7 +360,9 @@ static int fill_port_info(struct sk_buff *msg, >> if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state)) >> return -EMSGSIZE; >> >> + rtnl_lock(); >> netdev = ib_device_get_netdev(device, port); >> + >> if (netdev && net_eq(dev_net(netdev), net)) { >> ret = nla_put_u32(msg, >> RDMA_NLDEV_ATTR_NDEV_INDEX, netdev->ifindex); >> @@ -371,6 +373,7 @@ static int fill_port_info(struct sk_buff *msg, >> } >> >> out: >> + rtnl_unlock(); >> dev_put(netdev); >> return ret; >> } >> diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c >> index 94a7f3b0c71c..6a3757b00c93 100644 >> --- a/drivers/infiniband/core/verbs.c >> +++ b/drivers/infiniband/core/verbs.c >> @@ -1976,11 +1976,13 @@ int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) >> if (rdma_port_get_link_layer(dev, port_num) != IB_LINK_LAYER_ETHERNET) >> return -EINVAL; >> >> + rtnl_lock(); >> netdev = ib_device_get_netdev(dev, port_num); >> - if (!netdev) >> + if (!netdev) { >> + rtnl_unlock(); >> return -ENODEV; >> + } >> >> - rtnl_lock(); >> rc = __ethtool_get_link_ksettings(netdev, &lksettings); >> rtnl_unlock(); >> >> -- >> 2.30.2 >>
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index c02a96d3572a..cf9c826cd520 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -1461,7 +1461,9 @@ static int config_non_roce_gid_cache(struct ib_device *device, if (rdma_protocol_iwarp(device, port)) { struct net_device *ndev; + rtnl_lock(); ndev = ib_device_get_netdev(device, port); + rtnl_unlock(); if (!ndev) continue; RCU_INIT_POINTER(gid_attr.ndev, ndev); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 07cb6c5ffda0..25edb50d2b64 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -2026,9 +2026,12 @@ static int iw_query_port(struct ib_device *device, memset(port_attr, 0, sizeof(*port_attr)); + rtnl_lock(); netdev = ib_device_get_netdev(device, port_num); - if (!netdev) + if (!netdev) { + rtnl_unlock(); return -ENODEV; + } port_attr->max_mtu = IB_MTU_4096; port_attr->active_mtu = ib_mtu_int_to_enum(netdev->mtu); @@ -2052,6 +2055,7 @@ static int iw_query_port(struct ib_device *device, rcu_read_unlock(); } + rtnl_unlock(); dev_put(netdev); return device->ops.query_port(device, port_num, port_attr); } @@ -2220,6 +2224,8 @@ struct net_device *ib_device_get_netdev(struct ib_device *ib_dev, struct ib_port_data *pdata; struct net_device *res; + ASSERT_RTNL(); + if (!rdma_is_port_valid(ib_dev, port)) return NULL; @@ -2306,8 +2312,11 @@ void ib_enum_roce_netdev(struct ib_device *ib_dev, rdma_for_each_port (ib_dev, port) if (rdma_protocol_roce(ib_dev, port)) { - struct net_device *idev = - ib_device_get_netdev(ib_dev, port); + struct net_device *idev; + + rtnl_lock(); + idev = ib_device_get_netdev(ib_dev, port); + rtnl_unlock(); if (filter(ib_dev, port, idev, filter_cookie)) cb(ib_dev, port, idev, cookie); diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 4900a0848124..5cf7cdae8925 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -360,7 +360,9 @@ static int fill_port_info(struct sk_buff *msg, if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state)) return -EMSGSIZE; + rtnl_lock(); netdev = ib_device_get_netdev(device, port); + if (netdev && net_eq(dev_net(netdev), net)) { ret = nla_put_u32(msg, RDMA_NLDEV_ATTR_NDEV_INDEX, netdev->ifindex); @@ -371,6 +373,7 @@ static int fill_port_info(struct sk_buff *msg, } out: + rtnl_unlock(); dev_put(netdev); return ret; } diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 94a7f3b0c71c..6a3757b00c93 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1976,11 +1976,13 @@ int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) if (rdma_port_get_link_layer(dev, port_num) != IB_LINK_LAYER_ETHERNET) return -EINVAL; + rtnl_lock(); netdev = ib_device_get_netdev(dev, port_num); - if (!netdev) + if (!netdev) { + rtnl_unlock(); return -ENODEV; + } - rtnl_lock(); rc = __ethtool_get_link_ksettings(netdev, &lksettings); rtnl_unlock();
A call to ib_device_get_netdev may lead to a race condition while accessing a netdevice instance since we don't hold the rtnl lock while checking the registration state: if (res && res->reg_state != NETREG_REGISTERED) { v2: unlock rtnl on error path v3: update remaining callers of ib_device_get_netdev v4: don't call a cb with rtnl lock in ib_enum_roce_netdev Reported-by: syzbot+5fe14f2ff4ccbace9a26@syzkaller.appspotmail.com Fixes: d41861942fc55 ("IB/core: Add generic function to extract IB speed from netdev") Signed-off-by: Denis Kirjanov <dkirjanov@suse.de> --- drivers/infiniband/core/cache.c | 2 ++ drivers/infiniband/core/device.c | 15 ++++++++++++--- drivers/infiniband/core/nldev.c | 3 +++ drivers/infiniband/core/verbs.c | 6 ++++-- 4 files changed, 21 insertions(+), 5 deletions(-)