-From ben@decadent.org.uk Sun Aug 14 17:34:33 2016
-From: Ben Hutchings <ben@decadent.org.uk>
-Date: Tue, 31 May 2016 03:40:50 +0100
+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: IB/security: Restrict use of the write() interface
-To: stable@vger.kernel.org
-Cc: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>, Doug Ledford <dledford@redhat.com>, linux-rdma@vger.kernel.org
-Message-ID: <20160531024050.GL7555@decadent.org.uk>
-Content-Disposition: inline
-From: Ben Hutchings <ben@decadent.org.uk>
+From: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
-Commit e6bd18f57aad ("IB/security: Restrict use of the write()
-interface") fixed a security problem with various write()
-implementations in the Infiniband subsystem. In older kernel versions
-the ipath_write() function has the same problem and needs the same
-restriction. (The ipath driver has been completely removed upstream.)
+commit e6bd18f57aad1a2d1ef40e646d03ed0f2515c9e3 upstream.
-Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+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 ]
+Signed-off-by: Doug Ledford <dledford@redhat.com>
+[ Expanded to include removed ipath driver, and dropped non-existent
+ hfi1 driver ]
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
+ drivers/infiniband/core/ucm.c | 4 ++++
+ drivers/infiniband/core/ucma.c | 3 +++
+ drivers/infiniband/core/uverbs_main.c | 5 +++++
drivers/infiniband/hw/ipath/ipath_file_ops.c | 5 +++++
- 1 file changed, 5 insertions(+)
+ drivers/infiniband/hw/qib/qib_file_ops.c | 5 +++++
+ include/rdma/ib.h | 16 ++++++++++++++++
+ 6 files changed, 38 insertions(+)
+--- 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>
+@@ -1104,6 +1105,9 @@ static ssize_t ib_ucm_write(struct file
+ 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;
+
+--- a/drivers/infiniband/core/ucma.c
++++ b/drivers/infiniband/core/ucma.c
+@@ -1487,6 +1487,9 @@ static ssize_t ucma_write(struct file *f
+ 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;
+
+--- 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");
+@@ -605,6 +607,9 @@ static ssize_t ib_uverbs_write(struct fi
+ struct ib_uverbs_cmd_hdr hdr;
+ __u32 flags;
+
++ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
++ return -EACCES;
++
+ if (count < sizeof hdr)
+ return -EINVAL;
+
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -45,6 +45,8 @@
#include <linux/cpu.h>
#include <asm/pgtable.h>
-+#include <rdma/ib.h>
++#include "rdma/ib.h"
+
#include "ipath_kernel.h"
#include "ipath_common.h"
ssize_t ret = 0;
void *dest;
++ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
++ return -EACCES;
++
+ if (count < sizeof(cmd.type)) {
+ ret = -EINVAL;
+ goto bail;
+--- a/drivers/infiniband/hw/qib/qib_file_ops.c
++++ b/drivers/infiniband/hw/qib/qib_file_ops.c
+@@ -45,6 +45,8 @@
+ #include <linux/delay.h>
+ #include <linux/export.h>
+
++#include <rdma/ib.h>
++
+ #include "qib.h"
+ #include "qib_common.h"
+ #include "qib_user_sdma.h"
+@@ -2058,6 +2060,9 @@ static ssize_t qib_write(struct file *fp
+ 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;
+--- 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 */