From: Parav Pandit <parav@xxxxxxxxxxxx> Restrict user space users from passing a gid_index that refers to a net device outside their net namespace. This covers sysfs GID attribute access and uverbs users. Signed-off-by: Parav Pandit <parav@xxxxxxxxxxxx> Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxxxx> Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxxxx> --- Changelog: v0->v1: * Rephrase title --- drivers/infiniband/core/cache.c | 34 +++++++++++++++++++++++++++++----- drivers/infiniband/core/sysfs.c | 7 ++++--- drivers/infiniband/core/verbs.c | 23 +++++++++++++++-------- include/rdma/ib_cache.h | 19 +++++++++++++++++-- 4 files changed, 65 insertions(+), 18 deletions(-) diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index 0bee1f4b914e..ff3148ac47a6 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -1172,13 +1172,17 @@ int ib_get_cached_port_state(struct ib_device *device, EXPORT_SYMBOL(ib_get_cached_port_state); /** - * rdma_get_gid_attr - Returns GID attributes for a port of a device + * _rdma_get_gid_attr - Returns GID attributes for a port of a device * at a requested gid_index, if a valid GID entry exists. * @device: The device to query. * @port_num: The port number on the device where the GID value * is to be queried. * @index: Index of the GID table entry whose attributes are to * be queried. + * @net: If not NULL, check that the net of gid attritube and + * provided net are same. This is optional parameter + * applicable and meaningful only when a GID attribute + * has valid netdevice. * * rdma_get_gid_attr() acquires reference count of gid attributes from the * cached GID table. Caller must invoke rdma_put_gid_attr() to release @@ -1187,8 +1191,9 @@ EXPORT_SYMBOL(ib_get_cached_port_state); * Returns pointer to valid gid attribute or ERR_PTR for the appropriate error * code. */ -const struct ib_gid_attr * -rdma_get_gid_attr(struct ib_device *device, u8 port_num, int index) +const struct ib_gid_attr *_rdma_get_gid_attr(struct ib_device *device, + u8 port_num, int index, + struct net *net) { const struct ib_gid_attr *attr = ERR_PTR(-EINVAL); struct ib_gid_table *table; @@ -1205,13 +1210,32 @@ rdma_get_gid_attr(struct ib_device *device, u8 port_num, int index) if (!is_gid_entry_valid(table->data_vec[index])) goto done; - get_gid_entry(table->data_vec[index]); attr = &table->data_vec[index]->attr; + + /* + * If this is user space requesting access, the caller must be in a + * process context and must have the same net namespace. + */ + if (net && attr->ndev) { + bool matches; + + rcu_read_lock(); + matches = net_eq(dev_net(attr->ndev), net); + rcu_read_unlock(); + + if (!matches) { + attr = ERR_PTR(-EINVAL); + goto done; + } + } + + get_gid_entry(table->data_vec[index]); + done: read_unlock_irqrestore(&table->rwlock, flags); return attr; } -EXPORT_SYMBOL(rdma_get_gid_attr); +EXPORT_SYMBOL(_rdma_get_gid_attr); /** * rdma_put_gid_attr - Release reference to the GID attribute diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 7fd14ead7b37..edb8ff1a2c32 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -369,10 +369,10 @@ static ssize_t _show_port_gid_attr( const struct ib_gid_attr *gid_attr; ssize_t ret; - gid_attr = rdma_get_gid_attr(p->ibdev, p->port_num, tab_attr->index); + gid_attr = + rdma_get_user_gid_attr(p->ibdev, p->port_num, tab_attr->index); if (IS_ERR(gid_attr)) return PTR_ERR(gid_attr); - ret = print(gid_attr, buf); rdma_put_gid_attr(gid_attr); return ret; @@ -386,7 +386,8 @@ static ssize_t show_port_gid(struct ib_port *p, struct port_attribute *attr, const struct ib_gid_attr *gid_attr; ssize_t ret; - gid_attr = rdma_get_gid_attr(p->ibdev, p->port_num, tab_attr->index); + gid_attr = + rdma_get_user_gid_attr(p->ibdev, p->port_num, tab_attr->index); if (IS_ERR(gid_attr)) { const union ib_gid zgid = {}; diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index b6ceb6fd6a67..cd093d81de4d 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -413,7 +413,8 @@ static int rdma_check_ah_attr(struct ib_device *device, */ static int rdma_fill_sgid_attr(struct ib_device *device, struct rdma_ah_attr *ah_attr, - const struct ib_gid_attr **old_sgid_attr) + const struct ib_gid_attr **old_sgid_attr, + bool is_user) { const struct ib_gid_attr *sgid_attr; struct ib_global_route *grh; @@ -432,8 +433,13 @@ static int rdma_fill_sgid_attr(struct ib_device *device, if (grh->sgid_attr) return 0; - sgid_attr = - rdma_get_gid_attr(device, ah_attr->port_num, grh->sgid_index); + if (is_user) + sgid_attr = rdma_get_user_gid_attr(device, ah_attr->port_num, + grh->sgid_index); + else + sgid_attr = rdma_get_gid_attr(device, ah_attr->port_num, + grh->sgid_index); + if (IS_ERR(sgid_attr)) return PTR_ERR(sgid_attr); @@ -510,7 +516,7 @@ struct ib_ah *rdma_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr) struct ib_ah *ah; int ret; - ret = rdma_fill_sgid_attr(pd->device, ah_attr, &old_sgid_attr); + ret = rdma_fill_sgid_attr(pd->device, ah_attr, &old_sgid_attr, false); if (ret) return ERR_PTR(ret); @@ -542,7 +548,8 @@ struct ib_ah *rdma_create_user_ah(struct ib_pd *pd, struct ib_ah *ah; int err; - err = rdma_fill_sgid_attr(pd->device, ah_attr, &old_sgid_attr); + err = rdma_fill_sgid_attr(pd->device, ah_attr, &old_sgid_attr, + true); if (err) return ERR_PTR(err); @@ -881,7 +888,7 @@ int rdma_modify_ah(struct ib_ah *ah, struct rdma_ah_attr *ah_attr) if (ah->type != ah_attr->type) return -EINVAL; - ret = rdma_fill_sgid_attr(ah->device, ah_attr, &old_sgid_attr); + ret = rdma_fill_sgid_attr(ah->device, ah_attr, &old_sgid_attr, false); if (ret) return ret; @@ -1583,7 +1590,7 @@ static int _ib_modify_qp(struct ib_qp *qp, struct ib_qp_attr *attr, if (attr_mask & IB_QP_AV) { ret = rdma_fill_sgid_attr(qp->device, &attr->ah_attr, - &old_sgid_attr_av); + &old_sgid_attr_av, udata != NULL); if (ret) return ret; } @@ -1596,7 +1603,7 @@ static int _ib_modify_qp(struct ib_qp *qp, struct ib_qp_attr *attr, * counting does not serve any functional purpose. */ ret = rdma_fill_sgid_attr(qp->device, &attr->alt_ah_attr, - &old_sgid_attr_alt_av); + &old_sgid_attr_alt_av, udata != NULL); if (ret) goto out_av; diff --git a/include/rdma/ib_cache.h b/include/rdma/ib_cache.h index 1108d4220276..511ff6be503a 100644 --- a/include/rdma/ib_cache.h +++ b/include/rdma/ib_cache.h @@ -128,8 +128,23 @@ int ib_get_cached_port_state(struct ib_device *device, enum ib_port_state *port_active); bool rdma_is_zero_gid(const union ib_gid *gid); -const struct ib_gid_attr *rdma_get_gid_attr(struct ib_device *device, - u8 port_num, int index); + +const struct ib_gid_attr *_rdma_get_gid_attr(struct ib_device *device, + u8 port_num, int index, + struct net *net); +static inline const struct ib_gid_attr * +rdma_get_gid_attr(struct ib_device *device, u8 port_num, int index) +{ + return _rdma_get_gid_attr(device, port_num, index, NULL); +} + +static inline const struct ib_gid_attr * +rdma_get_user_gid_attr(struct ib_device *device, u8 port_num, int index) +{ + return _rdma_get_gid_attr(device, port_num, + index, current->nsproxy->net_ns); +} + void rdma_put_gid_attr(const struct ib_gid_attr *attr); void rdma_hold_gid_attr(const struct ib_gid_attr *attr); #endif /* _IB_CACHE_H */ -- 2.14.4 -- To unsubscribe from this list: send the line "unsubscribe linux-rdma" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html