--- /dev/null
+From e6bd18f57aad1a2d1ef40e646d03ed0f2515c9e3 Mon Sep 17 00:00:00 2001
+From: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+Date: Sun, 10 Apr 2016 19:13:13 -0600
+Subject: [PATCH] IB/security: Restrict use of the write() interface
+
+The drivers/infiniband stack uses write() as a replacement for
+bi-directional ioctl(). This is not safe. There are ways to
+trigger write calls that result in the return structure that
+is normally written to user space being shunted off to user
+specified kernel memory instead.
+
+For the immediate repair, detect and deny suspicious accesses to
+the write API.
+
+For long term, update the user space libraries and the kernel API
+to something that doesn't present the same security vulnerabilities
+(likely a structured ioctl() interface).
+
+The impacted uAPI interfaces are generally only available if
+hardware from drivers/infiniband is installed in the system.
+
+Reported-by: Jann Horn <jann@thejh.net>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+[ Expanded check to all known write() entry points ]
+Cc: stable@vger.kernel.org
+Signed-off-by: Doug Ledford <dledford@redhat.com>
+---
+ drivers/infiniband/core/ucm.c | 4 ++++
+ drivers/infiniband/core/ucma.c | 3 +++
+ drivers/infiniband/core/uverbs_main.c | 5 +++++
+ drivers/infiniband/hw/qib/qib_file_ops.c | 5 +++++
+ include/rdma/ib.h | 16 ++++++++++++++++
+ 7 files changed, 40 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
+index 4a9aa04..7713ef0 100644
+--- a/drivers/infiniband/core/ucm.c
++++ b/drivers/infiniband/core/ucm.c
+@@ -48,6 +48,7 @@
+
+ #include <asm/uaccess.h>
+
++#include <rdma/ib.h>
+ #include <rdma/ib_cm.h>
+ #include <rdma/ib_user_cm.h>
+ #include <rdma/ib_marshall.h>
+@@ -1103,6 +1104,9 @@ static ssize_t ib_ucm_write(struct file *filp, const char __user *buf,
+ struct ib_ucm_cmd_hdr hdr;
+ ssize_t result;
+
++ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
++ return -EACCES;
++
+ if (len < sizeof(hdr))
+ return -EINVAL;
+
+diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
+index dd3bcce..c0f3826 100644
+--- a/drivers/infiniband/core/ucma.c
++++ b/drivers/infiniband/core/ucma.c
+@@ -1574,6 +1574,9 @@ static ssize_t ucma_write(struct file *filp, const char __user *buf,
+ struct rdma_ucm_cmd_hdr hdr;
+ ssize_t ret;
+
++ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
++ return -EACCES;
++
+ if (len < sizeof(hdr))
+ return -EINVAL;
+
+diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
+index 28ba2cc..31f422a 100644
+--- a/drivers/infiniband/core/uverbs_main.c
++++ b/drivers/infiniband/core/uverbs_main.c
+@@ -48,6 +48,8 @@
+
+ #include <asm/uaccess.h>
+
++#include <rdma/ib.h>
++
+ #include "uverbs.h"
+
+ MODULE_AUTHOR("Roland Dreier");
+@@ -709,6 +711,9 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
+ int srcu_key;
+ ssize_t ret;
+
++ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
++ return -EACCES;
++
+ if (count < sizeof hdr)
+ return -EINVAL;
+
+diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
+index e449e39..24f4a78 100644
+--- a/drivers/infiniband/hw/qib/qib_file_ops.c
++++ b/drivers/infiniband/hw/qib/qib_file_ops.c
+@@ -45,6 +45,8 @@
+ #include <linux/export.h>
+ #include <linux/uio.h>
+
++#include <rdma/ib.h>
++
+ #include "qib.h"
+ #include "qib_common.h"
+ #include "qib_user_sdma.h"
+@@ -2067,6 +2069,9 @@ static ssize_t qib_write(struct file *fp, const char __user *data,
+ ssize_t ret = 0;
+ void *dest;
+
++ if (WARN_ON_ONCE(!ib_safe_file_access(fp)))
++ return -EACCES;
++
+ if (count < sizeof(cmd.type)) {
+ ret = -EINVAL;
+ goto bail;
+diff --git a/include/rdma/ib.h b/include/rdma/ib.h
+index cf8f9e7..a6b9370 100644
+--- a/include/rdma/ib.h
++++ b/include/rdma/ib.h
+@@ -34,6 +34,7 @@
+ #define _RDMA_IB_H
+
+ #include <linux/types.h>
++#include <linux/sched.h>
+
+ struct ib_addr {
+ union {
+@@ -86,4 +87,19 @@ struct sockaddr_ib {
+ __u64 sib_scope_id;
+ };
+
++/*
++ * The IB interfaces that use write() as bi-directional ioctl() are
++ * fundamentally unsafe, since there are lots of ways to trigger "write()"
++ * calls from various contexts with elevated privileges. That includes the
++ * traditional suid executable error message writes, but also various kernel
++ * interfaces that can write to file descriptors.
++ *
++ * This function provides protection for the legacy API by restricting the
++ * calling context.
++ */
++static inline bool ib_safe_file_access(struct file *filp)
++{
++ return filp->f_cred == current_cred() && segment_eq(get_fs(), USER_DS);
++}
++
+ #endif /* _RDMA_IB_H */
+--
+1.8.3.1
+