Commit 6961bc6c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rdma-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland/infiniband

Pull infiniband fixes from Roland Dreier:
 "Last batch of InfiniBand/RDMA changes for 3.13 / 2014:
   - Additional checks for uverbs to ensure forward compatibility,
     handle malformed input better.
   - Fix potential use-after-free in iWARP connection manager.
   - Make a function static"

* tag 'rdma-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland/infiniband:
  IB/uverbs: Check access to userspace response buffer in extended command
  IB/uverbs: Check input length in flow steering uverbs
  IB/uverbs: Set error code when fail to consume all flow_spec items
  IB/uverbs: Check reserved fields in create_flow
  IB/uverbs: Check comp_mask in destroy_flow
  IB/uverbs: Check reserved field in extended command header
  IB/uverbs: New macro to set pointers to NULL if length is 0 in INIT_UDATA()
  IB/core: const'ify inbuf in struct ib_udata
  RDMA/iwcm: Don't touch cm_id after deref in rem_ref
  RDMA/cxgb4: Make _c4iw_write_mem_dma() static
parents f5835372 22f12c60
...@@ -181,9 +181,16 @@ static void add_ref(struct iw_cm_id *cm_id) ...@@ -181,9 +181,16 @@ static void add_ref(struct iw_cm_id *cm_id)
static void rem_ref(struct iw_cm_id *cm_id) static void rem_ref(struct iw_cm_id *cm_id)
{ {
struct iwcm_id_private *cm_id_priv; struct iwcm_id_private *cm_id_priv;
int cb_destroy;
cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
if (iwcm_deref_id(cm_id_priv) &&
test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { /*
* Test bit before deref in case the cm_id gets freed on another
* thread.
*/
cb_destroy = test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags);
if (iwcm_deref_id(cm_id_priv) && cb_destroy) {
BUG_ON(!list_empty(&cm_id_priv->work_list)); BUG_ON(!list_empty(&cm_id_priv->work_list));
free_cm_id(cm_id_priv); free_cm_id(cm_id_priv);
} }
......
...@@ -49,12 +49,20 @@ ...@@ -49,12 +49,20 @@
#define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \ #define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \
do { \ do { \
(udata)->inbuf = (void __user *) (ibuf); \ (udata)->inbuf = (const void __user *) (ibuf); \
(udata)->outbuf = (void __user *) (obuf); \ (udata)->outbuf = (void __user *) (obuf); \
(udata)->inlen = (ilen); \ (udata)->inlen = (ilen); \
(udata)->outlen = (olen); \ (udata)->outlen = (olen); \
} while (0) } while (0)
#define INIT_UDATA_BUF_OR_NULL(udata, ibuf, obuf, ilen, olen) \
do { \
(udata)->inbuf = (ilen) ? (const void __user *) (ibuf) : NULL; \
(udata)->outbuf = (olen) ? (void __user *) (obuf) : NULL; \
(udata)->inlen = (ilen); \
(udata)->outlen = (olen); \
} while (0)
/* /*
* Our lifetime rules for these structs are the following: * Our lifetime rules for these structs are the following:
* *
......
...@@ -2593,6 +2593,9 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file, ...@@ -2593,6 +2593,9 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,
static int kern_spec_to_ib_spec(struct ib_uverbs_flow_spec *kern_spec, static int kern_spec_to_ib_spec(struct ib_uverbs_flow_spec *kern_spec,
union ib_flow_spec *ib_spec) union ib_flow_spec *ib_spec)
{ {
if (kern_spec->reserved)
return -EINVAL;
ib_spec->type = kern_spec->type; ib_spec->type = kern_spec->type;
switch (ib_spec->type) { switch (ib_spec->type) {
...@@ -2646,6 +2649,9 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file, ...@@ -2646,6 +2649,9 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
void *ib_spec; void *ib_spec;
int i; int i;
if (ucore->inlen < sizeof(cmd))
return -EINVAL;
if (ucore->outlen < sizeof(resp)) if (ucore->outlen < sizeof(resp))
return -ENOSPC; return -ENOSPC;
...@@ -2671,6 +2677,10 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file, ...@@ -2671,6 +2677,10 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
(cmd.flow_attr.num_of_specs * sizeof(struct ib_uverbs_flow_spec))) (cmd.flow_attr.num_of_specs * sizeof(struct ib_uverbs_flow_spec)))
return -EINVAL; return -EINVAL;
if (cmd.flow_attr.reserved[0] ||
cmd.flow_attr.reserved[1])
return -EINVAL;
if (cmd.flow_attr.num_of_specs) { if (cmd.flow_attr.num_of_specs) {
kern_flow_attr = kmalloc(sizeof(*kern_flow_attr) + cmd.flow_attr.size, kern_flow_attr = kmalloc(sizeof(*kern_flow_attr) + cmd.flow_attr.size,
GFP_KERNEL); GFP_KERNEL);
...@@ -2731,6 +2741,7 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file, ...@@ -2731,6 +2741,7 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
if (cmd.flow_attr.size || (i != flow_attr->num_of_specs)) { if (cmd.flow_attr.size || (i != flow_attr->num_of_specs)) {
pr_warn("create flow failed, flow %d: %d bytes left from uverb cmd\n", pr_warn("create flow failed, flow %d: %d bytes left from uverb cmd\n",
i, cmd.flow_attr.size); i, cmd.flow_attr.size);
err = -EINVAL;
goto err_free; goto err_free;
} }
flow_id = ib_create_flow(qp, flow_attr, IB_FLOW_DOMAIN_USER); flow_id = ib_create_flow(qp, flow_attr, IB_FLOW_DOMAIN_USER);
...@@ -2791,10 +2802,16 @@ int ib_uverbs_ex_destroy_flow(struct ib_uverbs_file *file, ...@@ -2791,10 +2802,16 @@ int ib_uverbs_ex_destroy_flow(struct ib_uverbs_file *file,
struct ib_uobject *uobj; struct ib_uobject *uobj;
int ret; int ret;
if (ucore->inlen < sizeof(cmd))
return -EINVAL;
ret = ib_copy_from_udata(&cmd, ucore, sizeof(cmd)); ret = ib_copy_from_udata(&cmd, ucore, sizeof(cmd));
if (ret) if (ret)
return ret; return ret;
if (cmd.comp_mask)
return -EINVAL;
uobj = idr_write_uobj(&ib_uverbs_rule_idr, cmd.flow_handle, uobj = idr_write_uobj(&ib_uverbs_rule_idr, cmd.flow_handle,
file->ucontext); file->ucontext);
if (!uobj) if (!uobj)
......
...@@ -668,23 +668,28 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, ...@@ -668,23 +668,28 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
if ((hdr.in_words + ex_hdr.provider_in_words) * 8 != count) if ((hdr.in_words + ex_hdr.provider_in_words) * 8 != count)
return -EINVAL; return -EINVAL;
if (ex_hdr.cmd_hdr_reserved)
return -EINVAL;
if (ex_hdr.response) { if (ex_hdr.response) {
if (!hdr.out_words && !ex_hdr.provider_out_words) if (!hdr.out_words && !ex_hdr.provider_out_words)
return -EINVAL; return -EINVAL;
if (!access_ok(VERIFY_WRITE,
(void __user *) (unsigned long) ex_hdr.response,
(hdr.out_words + ex_hdr.provider_out_words) * 8))
return -EFAULT;
} else { } else {
if (hdr.out_words || ex_hdr.provider_out_words) if (hdr.out_words || ex_hdr.provider_out_words)
return -EINVAL; return -EINVAL;
} }
INIT_UDATA(&ucore, INIT_UDATA_BUF_OR_NULL(&ucore, buf, (unsigned long) ex_hdr.response,
(hdr.in_words) ? buf : 0, hdr.in_words * 8, hdr.out_words * 8);
(unsigned long)ex_hdr.response,
hdr.in_words * 8,
hdr.out_words * 8);
INIT_UDATA(&uhw, INIT_UDATA_BUF_OR_NULL(&uhw,
(ex_hdr.provider_in_words) ? buf + ucore.inlen : 0, buf + ucore.inlen,
(ex_hdr.provider_out_words) ? (unsigned long)ex_hdr.response + ucore.outlen : 0, (unsigned long) ex_hdr.response + ucore.outlen,
ex_hdr.provider_in_words * 8, ex_hdr.provider_in_words * 8,
ex_hdr.provider_out_words * 8); ex_hdr.provider_out_words * 8);
......
...@@ -173,7 +173,7 @@ static int _c4iw_write_mem_inline(struct c4iw_rdev *rdev, u32 addr, u32 len, ...@@ -173,7 +173,7 @@ static int _c4iw_write_mem_inline(struct c4iw_rdev *rdev, u32 addr, u32 len,
return ret; return ret;
} }
int _c4iw_write_mem_dma(struct c4iw_rdev *rdev, u32 addr, u32 len, void *data) static int _c4iw_write_mem_dma(struct c4iw_rdev *rdev, u32 addr, u32 len, void *data)
{ {
u32 remain = len; u32 remain = len;
u32 dmalen; u32 dmalen;
......
...@@ -978,7 +978,7 @@ struct ib_uobject { ...@@ -978,7 +978,7 @@ struct ib_uobject {
}; };
struct ib_udata { struct ib_udata {
void __user *inbuf; const void __user *inbuf;
void __user *outbuf; void __user *outbuf;
size_t inlen; size_t inlen;
size_t outlen; size_t outlen;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment