From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Sasha Levin To: "stable@vger.kernel.org" , "linux-kernel@vger.kernel.org" CC: Jann Horn , Jason Gunthorpe , Sasha Levin Subject: [PATCH AUTOSEL 4.18 86/92] IB/mlx5: fix uaccess beyond "count" in debugfs read/write handlers Date: Sat, 15 Sep 2018 01:30:52 +0000 Message-ID: <20180915012944.179481-85-alexander.levin@microsoft.com> References: <20180915012944.179481-1-alexander.levin@microsoft.com> In-Reply-To: <20180915012944.179481-1-alexander.levin@microsoft.com> Content-Language: en-US Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org List-ID: From: Jann Horn [ Upstream commit 60e6627f12a78203a093ca05b7bca15627747d81 ] In general, accessing userspace memory beyond the length of the supplied buffer in VFS read/write handlers can lead to both kernel memory corruption (via kernel_read()/kernel_write(), which can e.g. be triggered via sys_splice()) and privilege escalation inside userspace. In this case, the affected files are in debugfs (and should therefore only be accessible to root), and the read handlers check that *pos is zero (meaning that at least sys_splice() can't trigger kernel memory corruption). Because of the root requirement, this is not a security fix, but rather a cleanup. For the read handlers, fix it by using simple_read_from_buffer() instead of custom logic. Add min() calls to the write handlers. Fixes: 4a2da0b8c078 ("IB/mlx5: Add debug control parameters for congestion = control") Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") Signed-off-by: Jann Horn Reviewed-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/cong.c | 9 +-------- drivers/infiniband/hw/mlx5/mr.c | 32 ++++++++----------------------- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/cong.c b/drivers/infiniband/hw/mlx5= /cong.c index 985fa2637390..7e4e358a4fd8 100644 --- a/drivers/infiniband/hw/mlx5/cong.c +++ b/drivers/infiniband/hw/mlx5/cong.c @@ -359,9 +359,6 @@ static ssize_t get_param(struct file *filp, char __user= *buf, size_t count, int ret; char lbuf[11]; =20 - if (*pos) - return 0; - ret =3D mlx5_ib_get_cc_params(param->dev, param->port_num, offset, &var); if (ret) return ret; @@ -370,11 +367,7 @@ static ssize_t get_param(struct file *filp, char __use= r *buf, size_t count, if (ret < 0) return ret; =20 - if (copy_to_user(buf, lbuf, ret)) - return -EFAULT; - - *pos +=3D ret; - return ret; + return simple_read_from_buffer(buf, count, pos, lbuf, ret); } =20 static const struct file_operations dbg_cc_fops =3D { diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/m= r.c index 90a9c461cedc..308456d28afb 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -271,16 +271,16 @@ static ssize_t size_write(struct file *filp, const ch= ar __user *buf, { struct mlx5_cache_ent *ent =3D filp->private_data; struct mlx5_ib_dev *dev =3D ent->dev; - char lbuf[20]; + char lbuf[20] =3D {0}; u32 var; int err; int c; =20 - if (copy_from_user(lbuf, buf, sizeof(lbuf))) + count =3D min(count, sizeof(lbuf) - 1); + if (copy_from_user(lbuf, buf, count)) return -EFAULT; =20 c =3D order2idx(dev, ent->order); - lbuf[sizeof(lbuf) - 1] =3D 0; =20 if (sscanf(lbuf, "%u", &var) !=3D 1) return -EINVAL; @@ -310,19 +310,11 @@ static ssize_t size_read(struct file *filp, char __us= er *buf, size_t count, char lbuf[20]; int err; =20 - if (*pos) - return 0; - err =3D snprintf(lbuf, sizeof(lbuf), "%d\n", ent->size); if (err < 0) return err; =20 - if (copy_to_user(buf, lbuf, err)) - return -EFAULT; - - *pos +=3D err; - - return err; + return simple_read_from_buffer(buf, count, pos, lbuf, err); } =20 static const struct file_operations size_fops =3D { @@ -337,16 +329,16 @@ static ssize_t limit_write(struct file *filp, const c= har __user *buf, { struct mlx5_cache_ent *ent =3D filp->private_data; struct mlx5_ib_dev *dev =3D ent->dev; - char lbuf[20]; + char lbuf[20] =3D {0}; u32 var; int err; int c; =20 - if (copy_from_user(lbuf, buf, sizeof(lbuf))) + count =3D min(count, sizeof(lbuf) - 1); + if (copy_from_user(lbuf, buf, count)) return -EFAULT; =20 c =3D order2idx(dev, ent->order); - lbuf[sizeof(lbuf) - 1] =3D 0; =20 if (sscanf(lbuf, "%u", &var) !=3D 1) return -EINVAL; @@ -372,19 +364,11 @@ static ssize_t limit_read(struct file *filp, char __u= ser *buf, size_t count, char lbuf[20]; int err; =20 - if (*pos) - return 0; - err =3D snprintf(lbuf, sizeof(lbuf), "%d\n", ent->limit); if (err < 0) return err; =20 - if (copy_to_user(buf, lbuf, err)) - return -EFAULT; - - *pos +=3D err; - - return err; + return simple_read_from_buffer(buf, count, pos, lbuf, err); } =20 static const struct file_operations limit_fops =3D { --=20 2.17.1