Re: usercopy whitelist woe in scsi_sense_cache

From: Kees Cook
Date: Wed Apr 04 2018 - 16:22:02 EST


On Wed, Apr 4, 2018 at 12:07 PM, Oleksandr Natalenko
<oleksandr@xxxxxxxxxxxxxx> wrote:
> With v4.16 I get the following dump while using smartctl:
> [...]
> [ 261.262135] Bad or missing usercopy whitelist? Kernel memory exposure
> attempt detected from SLUB object 'scsi_sense_cache' (offset 94, size 22)!
> [...]
> [ 261.345976] Call Trace:
> [ 261.350620] __check_object_size+0x130/0x1a0
> [ 261.355775] sg_io+0x269/0x3f0
> [ 261.360729] ? path_lookupat+0xaa/0x1f0
> [ 261.364027] ? current_time+0x18/0x70
> [ 261.366684] scsi_cmd_ioctl+0x257/0x410
> [ 261.369871] ? xfs_bmapi_read+0x1c3/0x340 [xfs]
> [ 261.372231] sd_ioctl+0xbf/0x1a0 [sd_mod]
> [ 261.375456] blkdev_ioctl+0x8ca/0x990
> [ 261.381156] ? read_null+0x10/0x10
> [ 261.384984] block_ioctl+0x39/0x40
> [ 261.388739] do_vfs_ioctl+0xa4/0x630
> [ 261.392624] ? vfs_write+0x164/0x1a0
> [ 261.396658] SyS_ioctl+0x74/0x80
> [ 261.399563] do_syscall_64+0x74/0x190
> [ 261.402685] entry_SYSCALL_64_after_hwframe+0x3d/0xa2

This is:

sg_io+0x269/0x3f0:
blk_complete_sghdr_rq at block/scsi_ioctl.c:280
(inlined by) sg_io at block/scsi_ioctl.c:376

which is:

if (req->sense_len && hdr->sbp) {
int len = min((unsigned int) hdr->mx_sb_len, req->sense_len);

if (!copy_to_user(hdr->sbp, req->sense, len))
hdr->sb_len_wr = len;
else
ret = -EFAULT;
}

> [...]
> I can easily reproduce it with a qemu VM and 2 virtual SCSI disks by calling
> smartctl in a loop and doing some usual background I/O. The warning is
> triggered within 3 minutes or so (not instantly).
>
> Initially, it was produced on my server after a kernel update (because disks
> are monitored with smartctl via Zabbix).
>
> Looks like the thing was introduced with
> 0afe76e88c57d91ef5697720aed380a339e3df70.
>
> Any idea how to deal with this please? If needed, I can provide any additional
> info, and also I'm happy/ready to test any proposed patches.

Interesting, and a little confusing. So, what's strange here is that
the scsi_sense_cache already has a full whitelist:

kmem_cache_create_usercopy("scsi_sense_cache",
SCSI_SENSE_BUFFERSIZE, 0, SLAB_HWCACHE_ALIGN,
0, SCSI_SENSE_BUFFERSIZE, NULL);

Arg 2 is the buffer size, arg 5 is the whitelist offset (0), and the
whitelist size (same as arg2). In other words, the entire buffer
should be whitelisted.

include/scsi/scsi_cmnd.h says:

#define SCSI_SENSE_BUFFERSIZE 96

That means scsi_sense_cache should be 96 bytes in size? But a 22 byte
read starting at offset 94 happened? That seems like a 20 byte read
beyond the end of the SLUB object? Though if it were reading past the
actual end of the object, I'd expect the hardened usercopy BUG (rather
than the WARN) to kick in. Ah, it looks like
/sys/kernel/slab/scsi_sense_cache/slab_size shows this to be 128 bytes
of actual allocation, so the 20 bytes doesn't strictly overlap another
object (hence no BUG):

/sys/kernel/slab/scsi_sense_cache# grep . object_size usersize slab_size
object_size:96
usersize:96
slab_size:128

Ah, right, due to SLAB_HWCACHE_ALIGN, the allocation is rounded up to
the next cache line size, so there's 32 bytes of padding to reach 128.

James or Martin, is this over-read "expected" behavior? i.e. does the
sense cache buffer usage ever pull the ugly trick of silently
expanding its allocation into the space the slab allocator has given
it? If not, this looks like a real bug.

What I don't see is how req->sense is _not_ at offset 0 in the
scsi_sense_cache object...

-Kees

--
Kees Cook
Pixel Security