Messages in this thread | | | From | Andrey Konovalov <> | Date | Tue, 18 Apr 2017 16:54:50 +0200 | Subject | net/ipv6: slab-out-of-bounds read in seg6_validate_srh |
| |
Hi,
I've got the following error report while fuzzing the kernel with syzkaller.
On commit 4f7d029b9bf009fbee76bb10c0c4351a1870d2f3 (4.11-rc7).
A reproducer and .config are attached.
================================================================== BUG: KASAN: slab-out-of-bounds in seg6_validate_srh+0x203/0x220 net/ipv6/seg6.c:57 at addr ffff88006a759608 Read of size 1 by task syz-executor4/2627 CPU: 2 PID: 2627 Comm: syz-executor4 Not tainted 4.11.0-rc7+ #247 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:16 [inline] dump_stack+0x292/0x398 lib/dump_stack.c:52 sctp: [Deprecated]: syz-executor5 (pid 2636) Use of int in maxseg socket option. Use struct sctp_assoc_value instead kasan_object_err+0x1c/0x70 mm/kasan/report.c:164 print_address_description mm/kasan/report.c:202 [inline] kasan_report_error mm/kasan/report.c:291 [inline] kasan_report+0x252/0x510 mm/kasan/report.c:347 __asan_report_load1_noabort+0x14/0x20 mm/kasan/report.c:365 seg6_validate_srh+0x203/0x220 net/ipv6/seg6.c:57 do_ipv6_setsockopt.isra.7+0x2008/0x3bc0 net/ipv6/ipv6_sockglue.c:439 ipv6_setsockopt+0x9b/0x140 net/ipv6/ipv6_sockglue.c:919 udpv6_setsockopt+0x45/0x80 net/ipv6/udp.c:1349 sock_common_setsockopt+0x95/0xd0 net/core/sock.c:2750 SYSC_setsockopt net/socket.c:1798 [inline] SyS_setsockopt+0x270/0x3a0 net/socket.c:1777 entry_SYSCALL_64_fastpath+0x1f/0xc2 RIP: 0033:0x4458d9 RSP: 002b:00007f6b9696ab58 EFLAGS: 00000292 ORIG_RAX: 0000000000000036 RAX: ffffffffffffffda RBX: 0000000000000018 RCX: 00000000004458d9 RDX: 0000000000000039 RSI: 0000000000000029 RDI: 0000000000000018 RBP: 00000000006e34e0 R08: 00000000000000a8 R09: 0000000000000000 R10: 000000002000efe7 R11: 0000000000000292 R12: 0000000000708000 R13: 0000000000000006 R14: 0000000000000001 R15: 000000000000001a Object at ffff88006a759520, in cache kmalloc-256 size: 256 Allocated: PID = 2627 save_stack_trace+0x16/0x20 arch/x86/kernel/stacktrace.c:59 save_stack+0x43/0xd0 mm/kasan/kasan.c:513 set_track mm/kasan/kasan.c:525 [inline] kasan_kmalloc+0xad/0xe0 mm/kasan/kasan.c:616 __kmalloc+0xa0/0x2d0 mm/slub.c:3745 kmalloc include/linux/slab.h:495 [inline] sock_kmalloc+0x11e/0x1b0 net/core/sock.c:1804 ipv6_renew_options+0x2b5/0xb50 net/ipv6/exthdrs.c:1053 do_ipv6_setsockopt.isra.7+0x1f5c/0x3bc0 net/ipv6/ipv6_sockglue.c:413 ipv6_setsockopt+0x9b/0x140 net/ipv6/ipv6_sockglue.c:919 udpv6_setsockopt+0x45/0x80 net/ipv6/udp.c:1349 sock_common_setsockopt+0x95/0xd0 net/core/sock.c:2750 SYSC_setsockopt net/socket.c:1798 [inline] SyS_setsockopt+0x270/0x3a0 net/socket.c:1777 entry_SYSCALL_64_fastpath+0x1f/0xc2 Freed: PID = 2241 save_stack_trace+0x16/0x20 arch/x86/kernel/stacktrace.c:59 save_stack+0x43/0xd0 mm/kasan/kasan.c:513 set_track mm/kasan/kasan.c:525 [inline] kasan_slab_free+0x73/0xc0 mm/kasan/kasan.c:589 slab_free_hook mm/slub.c:1357 [inline] slab_free_freelist_hook mm/slub.c:1379 [inline] slab_free mm/slub.c:2961 [inline] kfree+0xe8/0x2b0 mm/slub.c:3882 seq_release fs/seq_file.c:379 [inline] single_release+0x80/0xb0 fs/seq_file.c:615 __fput+0x332/0x7f0 fs/file_table.c:209 ____fput+0x15/0x20 fs/file_table.c:245 task_work_run+0x19b/0x270 kernel/task_work.c:116 tracehook_notify_resume include/linux/tracehook.h:191 [inline] exit_to_usermode_loop+0x1c2/0x200 arch/x86/entry/common.c:161 prepare_exit_to_usermode arch/x86/entry/common.c:191 [inline] syscall_return_slowpath+0x3d3/0x420 arch/x86/entry/common.c:260 entry_SYSCALL_64_fastpath+0xc0/0xc2 Memory state around the buggy address: ffff88006a759500: fc fc fc fc 00 00 00 00 00 00 00 00 00 00 00 00 ffff88006a759580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff88006a759600: 00 fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ^ ffff88006a759680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88006a759700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc 00 ================================================================== [unhandled content-type:application/octet-stream]// autogenerated by syzkaller (http://github.com/google/syzkaller)
#ifndef __NR_setsockopt #define __NR_setsockopt 54 #endif #ifndef __NR_mmap #define __NR_mmap 9 #endif #ifndef __NR_socket #define __NR_socket 41 #endif
#define _GNU_SOURCE
#include <sys/ioctl.h> #include <sys/mman.h> #include <sys/mount.h> #include <sys/prctl.h> #include <sys/resource.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h>
#include <linux/capability.h> #include <linux/if.h> #include <linux/if_tun.h> #include <linux/kvm.h> #include <linux/sched.h> #include <net/if_arp.h>
#include <assert.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <grp.h> #include <pthread.h> #include <setjmp.h> #include <signal.h> #include <stdarg.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>
const int kFailStatus = 67; const int kErrorStatus = 68; const int kRetryStatus = 69;
__attribute__((noreturn)) void doexit(int status) { volatile unsigned i; syscall(__NR_exit_group, status); for (i = 0;; i++) { } }
__attribute__((noreturn)) void fail(const char* msg, ...) { int e = errno; fflush(stdout); va_list args; va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, " (errno %d)\n", e); doexit((e == ENOMEM || e == EAGAIN) ? kRetryStatus : kFailStatus); }
__attribute__((noreturn)) void exitf(const char* msg, ...) { int e = errno; fflush(stdout); va_list args; va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, " (errno %d)\n", e); doexit(kRetryStatus); }
static int flag_debug;
void debug(const char* msg, ...) { if (!flag_debug) return; va_list args; va_start(args, msg); vfprintf(stdout, msg, args); va_end(args); fflush(stdout); }
__thread int skip_segv; __thread jmp_buf segv_env;
static void segv_handler(int sig, siginfo_t* info, void* uctx) { uintptr_t addr = (uintptr_t)info->si_addr; const uintptr_t prog_start = 1 << 20; const uintptr_t prog_end = 100 << 20; if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED) && (addr < prog_start || addr > prog_end)) { debug("SIGSEGV on %p, skipping\n", addr); _longjmp(segv_env, 1); } debug("SIGSEGV on %p, exiting\n", addr); doexit(sig); for (;;) { } }
static void install_segv_handler() { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = segv_handler; sa.sa_flags = SA_NODEFER | SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGBUS, &sa, NULL); }
#define NONFAILING(...) \ { \ __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ if (_setjmp(segv_env) == 0) { \ __VA_ARGS__; \ } \ __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ }
#define BITMASK_LEN(type, bf_len) (type)((1ull << (bf_len)) - 1)
#define BITMASK_LEN_OFF(type, bf_off, bf_len) \ (type)(BITMASK_LEN(type, (bf_len)) << (bf_off))
#define STORE_BY_BITMASK(type, addr, val, bf_off, bf_len) \ if ((bf_off) == 0 && (bf_len) == 0) { \ *(type*)(addr) = (type)(val); \ } else { \ type new_val = *(type*)(addr); \ new_val &= ~BITMASK_LEN_OFF(type, (bf_off), (bf_len)); \ new_val |= ((type)(val)&BITMASK_LEN(type, (bf_len))) << (bf_off); \ *(type*)(addr) = new_val; \ }
static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8) { switch (nr) { default: return syscall(nr, a0, a1, a2, a3, a4, a5); } }
static void setup_main_process() { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_IGN; syscall(SYS_rt_sigaction, 0x20, &sa, NULL, 8); syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8); install_segv_handler();
char tmpdir_template[] = "./syzkaller.XXXXXX"; char* tmpdir = mkdtemp(tmpdir_template); if (!tmpdir) fail("failed to mkdtemp"); if (chmod(tmpdir, 0777)) fail("failed to chmod"); if (chdir(tmpdir)) fail("failed to chdir"); }
static void loop();
static void sandbox_common() { prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); setpgrp(); setsid();
struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = 128 << 20; setrlimit(RLIMIT_AS, &rlim); rlim.rlim_cur = rlim.rlim_max = 1 << 20; setrlimit(RLIMIT_FSIZE, &rlim); rlim.rlim_cur = rlim.rlim_max = 1 << 20; setrlimit(RLIMIT_STACK, &rlim); rlim.rlim_cur = rlim.rlim_max = 0; setrlimit(RLIMIT_CORE, &rlim);
unshare(CLONE_NEWNS); unshare(CLONE_NEWIPC); unshare(CLONE_IO); }
static int do_sandbox_none(int executor_pid, bool enable_tun) { int pid = fork(); if (pid) return pid;
sandbox_common();
loop(); doexit(1); }
long r[5]; void loop() { memset(r, -1, sizeof(r)); r[0] = execute_syscall(__NR_mmap, 0x2000e000ul, 0x1000ul, 0x3ul, 0x32ul, 0xfffffffffffffffful, 0x0ul, 0, 0, 0); r[1] = execute_syscall(__NR_mmap, 0x2000f000ul, 0x1000ul, 0x1ul, 0x32ul, 0xfffffffffffffffful, 0x0ul, 0, 0, 0); r[2] = execute_syscall(__NR_socket, 0xaul, 0x2ul, 0x0ul, 0, 0, 0, 0, 0, 0); NONFAILING(memcpy( (void*)0x2000ef92, "\x7c\x14\x04\x00\x00\x00\x07\x97\xee\x0a\xf4\x64\xc8\xde\xb6\x13" "\xe9\x4a\xa8\x5d\xeb\x86\xc2\x3a\x1c\x35\x0d\x36\xc3\x5a\xc1\x4c" "\xbb\x63\x87\xd8\xce\x17\xe5\x37\xf9\x1d\xe8\xb1\xf8\xc0\x51\xec" "\x65\xf8\x37\x70\x65\x81\x24\x2d\x21\x6c\x8c\x20\xf4\xb7\x1c\xd5" "\x80\xbe\x50\x6c\x3b\xf6\x5b\xf6\x76\x4a\x22\xb4\x04\x81\x0b\x58" "\x22\x2d\x07\xe0\xcc\x1a\x84\x5f\x30\x67\x92\xf8\xbb\x59\x0c\x82" "\xc2\x9b\x17\x1c\x4f\x50\xf7\xbd\x91\xf6\xb5\x87\x52\x22\x27\x02" "\xbc\x7e\xff\xc9\x4a\x9b\x9a\x43\xa9\x91\x91\x11\x61\x39\xe8\x3b" "\xcf\x8b\x6e\x07\x00\x00\x00\x34\x10\xad\x07\xa7\x81\x8f\x2d\x38" "\x5b\xdf\x94\xf1\x51\x32\x14\x91\xcf\x80\xbd\x6f\x5b\x06\xd4\x16" "\x57\x75\x01\x99\xa4\x84\x30\x69", 168)); r[4] = execute_syscall(__NR_setsockopt, r[2], 0x29ul, 0x39ul, 0x2000ef92ul, 0xa8ul, 0, 0, 0, 0); } int main() { setup_main_process(); int pid = do_sandbox_none(0, false); int status = 0; while (waitpid(pid, &status, __WALL) != pid) { } return 0; }
| |