| // memory leak in device_add |
| // https://syzkaller.appspot.com/bug?id=00b395ed1c95e10c5623e185b2e3d5912cd72f63 |
| // status:invalid |
| // autogenerated by syzkaller (https://github.com/google/syzkaller) |
| |
| #define _GNU_SOURCE |
| |
| #include <endian.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <setjmp.h> |
| #include <signal.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/syscall.h> |
| #include <sys/types.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| static __thread int skip_segv; |
| static __thread jmp_buf segv_env; |
| |
| static void segv_handler(int sig, siginfo_t* info, void* ctx) |
| { |
| 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)) { |
| _longjmp(segv_env, 1); |
| } |
| exit(sig); |
| } |
| |
| static void install_segv_handler(void) |
| { |
| 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); |
| 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); \ |
| } |
| |
| static uint64_t current_time_ms(void) |
| { |
| struct timespec ts; |
| if (clock_gettime(CLOCK_MONOTONIC, &ts)) |
| exit(1); |
| return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; |
| } |
| |
| static void use_temporary_dir(void) |
| { |
| char tmpdir_template[] = "./syzkaller.XXXXXX"; |
| char* tmpdir = mkdtemp(tmpdir_template); |
| if (!tmpdir) |
| exit(1); |
| if (chmod(tmpdir, 0777)) |
| exit(1); |
| if (chdir(tmpdir)) |
| exit(1); |
| } |
| |
| static bool write_file(const char* file, const char* what, ...) |
| { |
| char buf[1024]; |
| va_list args; |
| va_start(args, what); |
| vsnprintf(buf, sizeof(buf), what, args); |
| va_end(args); |
| buf[sizeof(buf) - 1] = 0; |
| int len = strlen(buf); |
| int fd = open(file, O_WRONLY | O_CLOEXEC); |
| if (fd == -1) |
| return false; |
| if (write(fd, buf, len) != len) { |
| int err = errno; |
| close(fd); |
| errno = err; |
| return false; |
| } |
| close(fd); |
| return true; |
| } |
| |
| static int inject_fault(int nth) |
| { |
| int fd; |
| fd = open("/proc/thread-self/fail-nth", O_RDWR); |
| if (fd == -1) |
| exit(1); |
| char buf[16]; |
| sprintf(buf, "%d", nth + 1); |
| if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) |
| exit(1); |
| return fd; |
| } |
| |
| static void setup_fault() |
| { |
| static struct { |
| const char* file; |
| const char* val; |
| bool fatal; |
| } files[] = { |
| {"/sys/kernel/debug/failslab/ignore-gfp-wait", "N", true}, |
| {"/sys/kernel/debug/fail_futex/ignore-private", "N", false}, |
| {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem", "N", false}, |
| {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait", "N", false}, |
| {"/sys/kernel/debug/fail_page_alloc/min-order", "0", false}, |
| }; |
| unsigned i; |
| for (i = 0; i < sizeof(files) / sizeof(files[0]); i++) { |
| if (!write_file(files[i].file, files[i].val)) { |
| if (files[i].fatal) |
| exit(1); |
| } |
| } |
| } |
| |
| #define KMEMLEAK_FILE "/sys/kernel/debug/kmemleak" |
| |
| static void setup_leak() |
| { |
| if (!write_file(KMEMLEAK_FILE, "scan")) |
| exit(1); |
| sleep(5); |
| if (!write_file(KMEMLEAK_FILE, "scan")) |
| exit(1); |
| if (!write_file(KMEMLEAK_FILE, "clear")) |
| exit(1); |
| } |
| |
| static void check_leaks(void) |
| { |
| int fd = open(KMEMLEAK_FILE, O_RDWR); |
| if (fd == -1) |
| exit(1); |
| uint64_t start = current_time_ms(); |
| if (write(fd, "scan", 4) != 4) |
| exit(1); |
| sleep(1); |
| while (current_time_ms() - start < 4 * 1000) |
| sleep(1); |
| if (write(fd, "scan", 4) != 4) |
| exit(1); |
| static char buf[128 << 10]; |
| ssize_t n = read(fd, buf, sizeof(buf) - 1); |
| if (n < 0) |
| exit(1); |
| int nleaks = 0; |
| if (n != 0) { |
| sleep(1); |
| if (write(fd, "scan", 4) != 4) |
| exit(1); |
| if (lseek(fd, 0, SEEK_SET) < 0) |
| exit(1); |
| n = read(fd, buf, sizeof(buf) - 1); |
| if (n < 0) |
| exit(1); |
| buf[n] = 0; |
| char* pos = buf; |
| char* end = buf + n; |
| while (pos < end) { |
| char* next = strstr(pos + 1, "unreferenced object"); |
| if (!next) |
| next = end; |
| char prev = *next; |
| *next = 0; |
| fprintf(stderr, "BUG: memory leak\n%s\n", pos); |
| *next = prev; |
| pos = next; |
| nleaks++; |
| } |
| } |
| if (write(fd, "clear", 5) != 5) |
| exit(1); |
| close(fd); |
| if (nleaks) |
| exit(1); |
| } |
| |
| uint64_t r[1] = {0xffffffffffffffff}; |
| |
| int main(void) |
| { |
| syscall(__NR_mmap, 0x20000000, 0x1000000, 3, 0x32, -1, 0); |
| setup_leak(); |
| setup_fault(); |
| install_segv_handler(); |
| use_temporary_dir(); |
| intptr_t res = 0; |
| res = syscall(__NR_socket, 0x10, 3, 0); |
| if (res != -1) |
| r[0] = res; |
| syscall(__NR_socketpair, 1, 2, 0, 0); |
| syscall(__NR_ioctl, -1, 0x8912, 0x400200); |
| NONFAILING(*(uint64_t*)0x20000580 = 0); |
| NONFAILING(*(uint32_t*)0x20000588 = 0xc8); |
| NONFAILING(*(uint64_t*)0x20000590 = 0x20000000); |
| NONFAILING(*(uint64_t*)0x20000000 = 0x20000140); |
| NONFAILING(*(uint32_t*)0x20000140 = 0x50); |
| NONFAILING(*(uint16_t*)0x20000144 = 0x10); |
| NONFAILING(*(uint16_t*)0x20000146 = 0x705); |
| NONFAILING(*(uint32_t*)0x20000148 = 0); |
| NONFAILING(*(uint32_t*)0x2000014c = 0); |
| NONFAILING(*(uint8_t*)0x20000150 = 0); |
| NONFAILING(*(uint8_t*)0x20000151 = 0); |
| NONFAILING(*(uint16_t*)0x20000152 = 0); |
| NONFAILING(*(uint32_t*)0x20000154 = 0); |
| NONFAILING(*(uint32_t*)0x20000158 = 0); |
| NONFAILING(*(uint32_t*)0x2000015c = 0); |
| NONFAILING(*(uint16_t*)0x20000160 = 8); |
| NONFAILING(*(uint16_t*)0x20000162 = 0xd); |
| NONFAILING(*(uint8_t*)0x20000164 = 0); |
| NONFAILING(*(uint16_t*)0x20000168 = 0x28); |
| NONFAILING(*(uint16_t*)0x2000016a = 0x12); |
| NONFAILING(*(uint16_t*)0x2000016c = 0xc); |
| NONFAILING(*(uint16_t*)0x2000016e = 1); |
| NONFAILING(memcpy((void*)0x20000170, "veth\000", 5)); |
| NONFAILING(*(uint16_t*)0x20000178 = 0x18); |
| NONFAILING(*(uint16_t*)0x2000017a = 2); |
| NONFAILING(*(uint16_t*)0x2000017c = 0x14); |
| NONFAILING(*(uint16_t*)0x2000017e = 1); |
| NONFAILING(*(uint8_t*)0x20000180 = 0); |
| NONFAILING(*(uint8_t*)0x20000181 = 0); |
| NONFAILING(*(uint16_t*)0x20000182 = 0); |
| NONFAILING(*(uint32_t*)0x20000184 = 0); |
| NONFAILING(*(uint32_t*)0x20000188 = 0); |
| NONFAILING(*(uint32_t*)0x2000018c = 0); |
| NONFAILING(*(uint64_t*)0x20000008 = 0x50); |
| NONFAILING(*(uint64_t*)0x20000598 = 1); |
| NONFAILING(*(uint64_t*)0x200005a0 = 0); |
| NONFAILING(*(uint64_t*)0x200005a8 = 0); |
| NONFAILING(*(uint32_t*)0x200005b0 = 0); |
| inject_fault(92); |
| syscall(__NR_sendmsg, r[0], 0x20000580, 0); |
| check_leaks(); |
| return 0; |
| } |