Skip to content

Commit a394f83

Browse files
author
Roland Dreier
committed
IB/umad: Fix bit ordering and 32-on-64 problems on big endian systems
The declaration of struct ib_user_mad_reg_req.method_mask[] exported to userspace was an array of __u32, but the kernel internally treated it as a bitmap made up of longs. This makes a difference for 64-bit big-endian kernels, where numbering the bits in an array of__u32 gives: |31.....0|63....31|95....64|127...96| while numbering the bits in an array of longs gives: |63..............0|127............64| 64-bit userspace can handle this by just treating method_mask[] as an array of longs, but 32-bit userspace is really stuck: the meaning of the bits in method_mask[] depends on whether the kernel is 32-bit or 64-bit, and there's no sane way for userspace to know that. Fix this by updating <rdma/ib_user_mad.h> to make it clear that method_mask[] is an array of longs, and using a compat_ioctl method to convert to an array of 64-bit longs to handle the 32-on-64 problem. This fixes the interface description to match existing behavior (so working binaries continue to work) in almost all situations, and gives consistent semantics in the case of 32-bit userspace that can run on either a 32-bit or 64-bit kernel, so that the same binary can work for both 32-on-32 and 32-on-64 systems. Signed-off-by: Roland Dreier <rolandd@cisco.com>
1 parent 2be8e3e commit a394f83

File tree

2 files changed

+61
-10
lines changed

2 files changed

+61
-10
lines changed

drivers/infiniband/core/user_mad.c

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <linux/poll.h>
4545
#include <linux/rwsem.h>
4646
#include <linux/kref.h>
47+
#include <linux/compat.h>
4748

4849
#include <asm/uaccess.h>
4950
#include <asm/semaphore.h>
@@ -607,7 +608,8 @@ static unsigned int ib_umad_poll(struct file *filp, struct poll_table_struct *wa
607608
return mask;
608609
}
609610

610-
static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg)
611+
static int ib_umad_reg_agent(struct ib_umad_file *file, void __user *arg,
612+
int compat_method_mask)
611613
{
612614
struct ib_user_mad_reg_req ureq;
613615
struct ib_mad_reg_req req;
@@ -622,7 +624,7 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg)
622624
goto out;
623625
}
624626

625-
if (copy_from_user(&ureq, (void __user *) arg, sizeof ureq)) {
627+
if (copy_from_user(&ureq, arg, sizeof ureq)) {
626628
ret = -EFAULT;
627629
goto out;
628630
}
@@ -643,8 +645,18 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg)
643645
if (ureq.mgmt_class) {
644646
req.mgmt_class = ureq.mgmt_class;
645647
req.mgmt_class_version = ureq.mgmt_class_version;
646-
memcpy(req.method_mask, ureq.method_mask, sizeof req.method_mask);
647-
memcpy(req.oui, ureq.oui, sizeof req.oui);
648+
memcpy(req.oui, ureq.oui, sizeof req.oui);
649+
650+
if (compat_method_mask) {
651+
u32 *umm = (u32 *) ureq.method_mask;
652+
int i;
653+
654+
for (i = 0; i < BITS_TO_LONGS(IB_MGMT_MAX_METHODS); ++i)
655+
req.method_mask[i] =
656+
umm[i * 2] | ((u64) umm[i * 2 + 1] << 32);
657+
} else
658+
memcpy(req.method_mask, ureq.method_mask,
659+
sizeof req.method_mask);
648660
}
649661

650662
agent = ib_register_mad_agent(file->port->ib_dev, file->port->port_num,
@@ -682,13 +694,13 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg)
682694
return ret;
683695
}
684696

685-
static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg)
697+
static int ib_umad_unreg_agent(struct ib_umad_file *file, u32 __user *arg)
686698
{
687699
struct ib_mad_agent *agent = NULL;
688700
u32 id;
689701
int ret = 0;
690702

691-
if (get_user(id, (u32 __user *) arg))
703+
if (get_user(id, arg))
692704
return -EFAULT;
693705

694706
down_write(&file->port->mutex);
@@ -729,16 +741,33 @@ static long ib_umad_ioctl(struct file *filp, unsigned int cmd,
729741
{
730742
switch (cmd) {
731743
case IB_USER_MAD_REGISTER_AGENT:
732-
return ib_umad_reg_agent(filp->private_data, arg);
744+
return ib_umad_reg_agent(filp->private_data, (void __user *) arg, 0);
733745
case IB_USER_MAD_UNREGISTER_AGENT:
734-
return ib_umad_unreg_agent(filp->private_data, arg);
746+
return ib_umad_unreg_agent(filp->private_data, (__u32 __user *) arg);
735747
case IB_USER_MAD_ENABLE_PKEY:
736748
return ib_umad_enable_pkey(filp->private_data);
737749
default:
738750
return -ENOIOCTLCMD;
739751
}
740752
}
741753

754+
#ifdef CONFIG_COMPAT
755+
static long ib_umad_compat_ioctl(struct file *filp, unsigned int cmd,
756+
unsigned long arg)
757+
{
758+
switch (cmd) {
759+
case IB_USER_MAD_REGISTER_AGENT:
760+
return ib_umad_reg_agent(filp->private_data, compat_ptr(arg), 1);
761+
case IB_USER_MAD_UNREGISTER_AGENT:
762+
return ib_umad_unreg_agent(filp->private_data, compat_ptr(arg));
763+
case IB_USER_MAD_ENABLE_PKEY:
764+
return ib_umad_enable_pkey(filp->private_data);
765+
default:
766+
return -ENOIOCTLCMD;
767+
}
768+
}
769+
#endif
770+
742771
static int ib_umad_open(struct inode *inode, struct file *filp)
743772
{
744773
struct ib_umad_port *port;
@@ -826,7 +855,9 @@ static const struct file_operations umad_fops = {
826855
.write = ib_umad_write,
827856
.poll = ib_umad_poll,
828857
.unlocked_ioctl = ib_umad_ioctl,
829-
.compat_ioctl = ib_umad_ioctl,
858+
#ifdef CONFIG_COMPAT
859+
.compat_ioctl = ib_umad_compat_ioctl,
860+
#endif
830861
.open = ib_umad_open,
831862
.release = ib_umad_close
832863
};

include/rdma/ib_user_mad.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,26 @@ struct ib_user_mad {
147147
__u64 data[0];
148148
};
149149

150+
/*
151+
* Earlier versions of this interface definition declared the
152+
* method_mask[] member as an array of __u32 but treated it as a
153+
* bitmap made up of longs in the kernel. This ambiguity meant that
154+
* 32-bit big-endian applications that can run on both 32-bit and
155+
* 64-bit kernels had no consistent ABI to rely on, and 64-bit
156+
* big-endian applications that treated method_mask as being made up
157+
* of 32-bit words would have their bitmap misinterpreted.
158+
*
159+
* To clear up this confusion, we change the declaration of
160+
* method_mask[] to use unsigned long and handle the conversion from
161+
* 32-bit userspace to 64-bit kernel for big-endian systems in the
162+
* compat_ioctl method. Unfortunately, to keep the structure layout
163+
* the same, we need the method_mask[] array to be aligned only to 4
164+
* bytes even when long is 64 bits, which forces us into this ugly
165+
* typedef.
166+
*/
167+
typedef unsigned long __attribute__((aligned(4))) packed_ulong;
168+
#define IB_USER_MAD_LONGS_PER_METHOD_MASK (128 / (8 * sizeof (long)))
169+
150170
/**
151171
* ib_user_mad_reg_req - MAD registration request
152172
* @id - Set by the kernel; used to identify agent in future requests.
@@ -165,7 +185,7 @@ struct ib_user_mad {
165185
*/
166186
struct ib_user_mad_reg_req {
167187
__u32 id;
168-
__u32 method_mask[4];
188+
packed_ulong method_mask[IB_USER_MAD_LONGS_PER_METHOD_MASK];
169189
__u8 qpn;
170190
__u8 mgmt_class;
171191
__u8 mgmt_class_version;

0 commit comments

Comments
 (0)