github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/executor/executor_bsd.h (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  #include <fcntl.h>
     5  #include <stdlib.h>
     6  #include <sys/ioctl.h>
     7  #include <sys/kcov.h>
     8  #include <sys/mman.h>
     9  #include <sys/resource.h>
    10  #include <sys/stat.h>
    11  #include <sys/types.h>
    12  #include <unistd.h>
    13  
    14  #if GOOS_openbsd
    15  #include <sys/sysctl.h>
    16  #endif
    17  
    18  static void os_init(int argc, char** argv, void* data, size_t data_size)
    19  {
    20  #if GOOS_openbsd
    21  	// W^X not allowed by default on OpenBSD.
    22  	int prot = PROT_READ | PROT_WRITE;
    23  #elif GOOS_netbsd
    24  	// W^X not allowed by default on NetBSD (PaX MPROTECT).
    25  	int prot = PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC);
    26  #else
    27  	int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
    28  #endif
    29  
    30  	int flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED_EXCLUSIVE;
    31  #if GOOS_freebsd
    32  	// Fail closed if the chosen data offset conflicts with an existing mapping.
    33  	flags |= MAP_EXCL;
    34  #endif
    35  
    36  	void* got = mmap(data, data_size, prot, flags, -1, 0);
    37  	if (data != got)
    38  		failmsg("mmap of data segment failed", "want %p, got %p", data, got);
    39  
    40  	// Makes sure the file descriptor limit is sufficient to map control pipes.
    41  	struct rlimit rlim;
    42  	rlim.rlim_cur = rlim.rlim_max = kMaxFd;
    43  	setrlimit(RLIMIT_NOFILE, &rlim);
    44  
    45  	// A SIGCHLD handler makes sleep in loop exit immediately return with EINTR with a child exits.
    46  	struct sigaction act = {};
    47  	act.sa_handler = [](int) {};
    48  	sigaction(SIGCHLD, &act, nullptr);
    49  }
    50  
    51  static intptr_t execute_syscall(const call_t* c, intptr_t a[kMaxArgs])
    52  {
    53  	if (c->call)
    54  		return c->call(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
    55  #if GOOS_openbsd
    56  	failmsg("no call", "missing target for %s", c->name);
    57  #else
    58  	return __syscall(c->sys_nr, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
    59  #endif
    60  }
    61  
    62  static void cover_open(cover_t* cov, bool extra)
    63  {
    64  	int fd = open("/dev/kcov", O_RDWR);
    65  	if (fd == -1)
    66  		fail("open of /dev/kcov failed");
    67  	if (dup2(fd, cov->fd) < 0)
    68  		failmsg("failed to dup cover fd", "from=%d, to=%d", fd, cov->fd);
    69  	close(fd);
    70  
    71  #if GOOS_freebsd
    72  	if (ioctl(cov->fd, KIOSETBUFSIZE, kCoverSize))
    73  		fail("ioctl init trace write failed");
    74  	cov->data_size = kCoverSize * KCOV_ENTRY_SIZE;
    75  #elif GOOS_openbsd
    76  	unsigned long cover_size = kCoverSize;
    77  	if (ioctl(cov->fd, KIOSETBUFSIZE, &cover_size))
    78  		fail("ioctl init trace write failed");
    79  	if (extra) {
    80  		struct kio_remote_attach args;
    81  		args.subsystem = KCOV_REMOTE_COMMON;
    82  		args.id = 0;
    83  		if (ioctl(cov->fd, KIOREMOTEATTACH, &args))
    84  			fail("ioctl remote attach failed");
    85  	}
    86  	cov->data_size = kCoverSize * (is_kernel_64_bit ? 8 : 4);
    87  #elif GOOS_netbsd
    88  	uint64_t cover_size;
    89  	if (extra) {
    90  		// USB coverage, the size is fixed to the maximum
    91  		cover_size = (256 << 10); // maximum size
    92  		struct kcov_ioc_remote_attach args;
    93  		args.subsystem = KCOV_REMOTE_VHCI;
    94  		args.id = KCOV_REMOTE_VHCI_ID(procid, 1); // first port
    95  		if (ioctl(cov->fd, KCOV_IOC_REMOTE_ATTACH, &args))
    96  			fail("ioctl remote attach failed");
    97  	} else {
    98  		// Normal coverage
    99  		cover_size = kCoverSize;
   100  		if (ioctl(cov->fd, KCOV_IOC_SETBUFSIZE, &cover_size))
   101  			fail("ioctl init trace write failed");
   102  	}
   103  	cov->data_size = cover_size * KCOV_ENTRY_SIZE;
   104  #endif
   105  }
   106  
   107  static void cover_mmap(cover_t* cov)
   108  {
   109  	if (cov->mmap_alloc_ptr != NULL)
   110  		fail("cover_mmap invoked on an already mmapped cover_t object");
   111  	cov->mmap_alloc_size = cov->data_size;
   112  	void* mmap_ptr = mmap(NULL, cov->mmap_alloc_size, PROT_READ | PROT_WRITE,
   113  			      MAP_SHARED, cov->fd, 0);
   114  	if (mmap_ptr == MAP_FAILED)
   115  		fail("cover mmap failed");
   116  	cov->mmap_alloc_ptr = (char*)mmap_ptr;
   117  	cov->data = (char*)mmap_ptr;
   118  	cov->data_end = cov->data + cov->mmap_alloc_size;
   119  	cov->data_offset = is_kernel_64_bit ? sizeof(uint64_t) : sizeof(uint32_t);
   120  	cov->pc_offset = 0;
   121  }
   122  
   123  static void cover_protect(cover_t* cov)
   124  {
   125  	if (cov->mmap_alloc_ptr == NULL)
   126  		fail("cover_protect invoked on an unmapped cover_t object");
   127  #if GOOS_freebsd
   128  	size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE;
   129  	long page_size = sysconf(_SC_PAGESIZE);
   130  	if (page_size > 0)
   131  		mprotect(cov->mmap_alloc_ptr + page_size, mmap_alloc_size - page_size,
   132  			 PROT_READ);
   133  #elif GOOS_openbsd
   134  	int mib[2], page_size;
   135  	size_t mmap_alloc_size = kCoverSize * sizeof(uintptr_t);
   136  	mib[0] = CTL_HW;
   137  	mib[1] = HW_PAGESIZE;
   138  	size_t len = sizeof(page_size);
   139  	if (sysctl(mib, ARRAY_SIZE(mib), &page_size, &len, NULL, 0) != -1)
   140  		mprotect(cov->mmap_alloc_ptr + page_size, mmap_alloc_size - page_size, PROT_READ);
   141  #endif
   142  }
   143  
   144  static void cover_unprotect(cover_t* cov)
   145  {
   146  	if (cov->mmap_alloc_ptr == NULL)
   147  		fail("cover_unprotect invoked on an unmapped cover_t object");
   148  #if GOOS_freebsd
   149  	size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE;
   150  	mprotect(cov->mmap_alloc_ptr, mmap_alloc_size, PROT_READ | PROT_WRITE);
   151  #elif GOOS_openbsd
   152  	size_t mmap_alloc_size = kCoverSize * sizeof(uintptr_t);
   153  	mprotect(cov->mmap_alloc_ptr, mmap_alloc_size, PROT_READ | PROT_WRITE);
   154  #endif
   155  }
   156  
   157  static void cover_enable(cover_t* cov, bool collect_comps, bool extra)
   158  {
   159  	int kcov_mode = collect_comps ? KCOV_MODE_TRACE_CMP : KCOV_MODE_TRACE_PC;
   160  #if GOOS_freebsd
   161  	// FreeBSD uses an int as the third argument.
   162  	if (ioctl(cov->fd, KIOENABLE, kcov_mode))
   163  		exitf("cover enable write trace failed, mode=%d", kcov_mode);
   164  #elif GOOS_openbsd
   165  	// OpenBSD uses an pointer to an int as the third argument.
   166  	// Whether it is a regular coverage or an extra coverage, the enable
   167  	// ioctl is the same.
   168  	if (ioctl(cov->fd, KIOENABLE, &kcov_mode))
   169  		exitf("cover enable write trace failed, mode=%d", kcov_mode);
   170  #elif GOOS_netbsd
   171  	// Whether it is a regular coverage or a USB coverage, the enable
   172  	// ioctl is the same.
   173  	if (ioctl(cov->fd, KCOV_IOC_ENABLE, &kcov_mode))
   174  		exitf("cover enable write trace failed, mode=%d", kcov_mode);
   175  #endif
   176  }
   177  
   178  static void cover_reset(cover_t* cov)
   179  {
   180  	*(uint64*)cov->data = 0;
   181  }
   182  
   183  static void cover_collect(cover_t* cov)
   184  {
   185  	cov->size = *(uint64*)cov->data;
   186  }
   187  
   188  #if GOOS_netbsd
   189  #define SYZ_HAVE_FEATURES 1
   190  static feature_t features[] = {
   191      {rpc::Feature::USBEmulation, setup_usb},
   192      {rpc::Feature::Fault, setup_fault},
   193  };
   194  
   195  static void setup_sysctl(void)
   196  {
   197  }
   198  
   199  static void setup_cgroups(void)
   200  {
   201  }
   202  #endif