Skip to content

Commit c95b708

Browse files
draconxChristoph Hellwig
authored andcommitted
nvme: fix compat address handling in several ioctls
On a 32-bit kernel, the upper bits of userspace addresses passed via various ioctls are silently ignored by the nvme driver. However on a 64-bit kernel running a compat task, these upper bits are not ignored and are in fact required to be zero for the ioctls to work. Unfortunately, this difference matters. 32-bit smartctl submits the NVME_IOCTL_ADMIN_CMD ioctl with garbage in these upper bits because it seems the pointer value it puts into the nvme_passthru_cmd structure is sign extended. This works fine on 32-bit kernels but fails on a 64-bit one because (at least on my setup) the addresses smartctl uses are consistently above 2G. For example: # smartctl -x /dev/nvme0n1 smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.5.11] (local build) Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org Read NVMe Identify Controller failed: NVME_IOCTL_ADMIN_CMD: Bad address Since changing 32-bit kernels to actually check all of the submitted address bits now would break existing userspace, this patch fixes the compat problem by explicitly zeroing the upper bits in the compat case. This enables 32-bit smartctl to work on a 64-bit kernel. Signed-off-by: Nick Bowler <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]>
1 parent 458ef2a commit c95b708

File tree

1 file changed

+20
-7
lines changed

1 file changed

+20
-7
lines changed

drivers/nvme/host/core.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <linux/blkdev.h>
88
#include <linux/blk-mq.h>
9+
#include <linux/compat.h>
910
#include <linux/delay.h>
1011
#include <linux/errno.h>
1112
#include <linux/hdreg.h>
@@ -1252,6 +1253,18 @@ static void nvme_enable_aen(struct nvme_ctrl *ctrl)
12521253
queue_work(nvme_wq, &ctrl->async_event_work);
12531254
}
12541255

1256+
/*
1257+
* Convert integer values from ioctl structures to user pointers, silently
1258+
* ignoring the upper bits in the compat case to match behaviour of 32-bit
1259+
* kernels.
1260+
*/
1261+
static void __user *nvme_to_user_ptr(uintptr_t ptrval)
1262+
{
1263+
if (in_compat_syscall())
1264+
ptrval = (compat_uptr_t)ptrval;
1265+
return (void __user *)ptrval;
1266+
}
1267+
12551268
static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
12561269
{
12571270
struct nvme_user_io io;
@@ -1275,7 +1288,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
12751288

12761289
length = (io.nblocks + 1) << ns->lba_shift;
12771290
meta_len = (io.nblocks + 1) * ns->ms;
1278-
metadata = (void __user *)(uintptr_t)io.metadata;
1291+
metadata = nvme_to_user_ptr(io.metadata);
12791292

12801293
if (ns->ext) {
12811294
length += meta_len;
@@ -1298,7 +1311,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
12981311
c.rw.appmask = cpu_to_le16(io.appmask);
12991312

13001313
return nvme_submit_user_cmd(ns->queue, &c,
1301-
(void __user *)(uintptr_t)io.addr, length,
1314+
nvme_to_user_ptr(io.addr), length,
13021315
metadata, meta_len, lower_32_bits(io.slba), NULL, 0);
13031316
}
13041317

@@ -1418,9 +1431,9 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
14181431

14191432
effects = nvme_passthru_start(ctrl, ns, cmd.opcode);
14201433
status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c,
1421-
(void __user *)(uintptr_t)cmd.addr, cmd.data_len,
1422-
(void __user *)(uintptr_t)cmd.metadata,
1423-
cmd.metadata_len, 0, &result, timeout);
1434+
nvme_to_user_ptr(cmd.addr), cmd.data_len,
1435+
nvme_to_user_ptr(cmd.metadata), cmd.metadata_len,
1436+
0, &result, timeout);
14241437
nvme_passthru_end(ctrl, effects);
14251438

14261439
if (status >= 0) {
@@ -1465,8 +1478,8 @@ static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
14651478

14661479
effects = nvme_passthru_start(ctrl, ns, cmd.opcode);
14671480
status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c,
1468-
(void __user *)(uintptr_t)cmd.addr, cmd.data_len,
1469-
(void __user *)(uintptr_t)cmd.metadata, cmd.metadata_len,
1481+
nvme_to_user_ptr(cmd.addr), cmd.data_len,
1482+
nvme_to_user_ptr(cmd.metadata), cmd.metadata_len,
14701483
0, &cmd.result, timeout);
14711484
nvme_passthru_end(ctrl, effects);
14721485

0 commit comments

Comments
 (0)