github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/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;
    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  
    46  static intptr_t execute_syscall(const call_t* c, intptr_t a[kMaxArgs])
    47  {
    48  	if (c->call)
    49  		return c->call(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
    50  #if GOOS_openbsd
    51  	failmsg("no call", "missing target for %s", c->name);
    52  #else
    53  	return __syscall(c->sys_nr, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
    54  #endif
    55  }
    56  
    57  static void cover_open(cover_t* cov, bool extra)
    58  {
    59  	int fd = open("/dev/kcov", O_RDWR);
    60  	if (fd == -1)
    61  		fail("open of /dev/kcov failed");
    62  	if (dup2(fd, cov->fd) < 0)
    63  		failmsg("failed to dup cover fd", "from=%d, to=%d", fd, cov->fd);
    64  	close(fd);
    65  
    66  #if GOOS_freebsd
    67  	if (ioctl(cov->fd, KIOSETBUFSIZE, kCoverSize))
    68  		fail("ioctl init trace write failed");
    69  	cov->mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE;
    70  #elif GOOS_openbsd
    71  	unsigned long cover_size = kCoverSize;
    72  	if (ioctl(cov->fd, KIOSETBUFSIZE, &cover_size))
    73  		fail("ioctl init trace write failed");
    74  	if (extra) {
    75  		struct kio_remote_attach args;
    76  		args.subsystem = KCOV_REMOTE_COMMON;
    77  		args.id = 0;
    78  		if (ioctl(cov->fd, KIOREMOTEATTACH, &args))
    79  			fail("ioctl remote attach failed");
    80  	}
    81  	cov->mmap_alloc_size = kCoverSize * (is_kernel_64_bit ? 8 : 4);
    82  #elif GOOS_netbsd
    83  	uint64_t cover_size;
    84  	if (extra) {
    85  		// USB coverage, the size is fixed to the maximum
    86  		cover_size = (256 << 10); // maximum size
    87  		struct kcov_ioc_remote_attach args;
    88  		args.subsystem = KCOV_REMOTE_VHCI;
    89  		args.id = KCOV_REMOTE_VHCI_ID(procid, 1); // first port
    90  		if (ioctl(cov->fd, KCOV_IOC_REMOTE_ATTACH, &args))
    91  			fail("ioctl remote attach failed");
    92  	} else {
    93  		// Normal coverage
    94  		cover_size = kCoverSize;
    95  		if (ioctl(cov->fd, KCOV_IOC_SETBUFSIZE, &cover_size))
    96  			fail("ioctl init trace write failed");
    97  	}
    98  	cov->mmap_alloc_size = cover_size * KCOV_ENTRY_SIZE;
    99  #endif
   100  }
   101  
   102  static void cover_mmap(cover_t* cov)
   103  {
   104  	if (cov->data != NULL)
   105  		fail("cover_mmap invoked on an already mmapped cover_t object");
   106  	void* mmap_ptr = mmap(NULL, cov->mmap_alloc_size, PROT_READ | PROT_WRITE,
   107  			      MAP_SHARED, cov->fd, 0);
   108  	if (mmap_ptr == MAP_FAILED)
   109  		fail("cover mmap failed");
   110  	cov->data = (char*)mmap_ptr;
   111  	cov->data_end = cov->data + cov->mmap_alloc_size;
   112  	cov->data_offset = is_kernel_64_bit ? sizeof(uint64_t) : sizeof(uint32_t);
   113  	cov->pc_offset = 0;
   114  }
   115  
   116  static void cover_protect(cover_t* cov)
   117  {
   118  #if GOOS_freebsd
   119  	size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE;
   120  	long page_size = sysconf(_SC_PAGESIZE);
   121  	if (page_size > 0)
   122  		mprotect(cov->data + page_size, mmap_alloc_size - page_size,
   123  			 PROT_READ);
   124  #elif GOOS_openbsd
   125  	int mib[2], page_size;
   126  	size_t mmap_alloc_size = kCoverSize * sizeof(uintptr_t);
   127  	mib[0] = CTL_HW;
   128  	mib[1] = HW_PAGESIZE;
   129  	size_t len = sizeof(page_size);
   130  	if (sysctl(mib, ARRAY_SIZE(mib), &page_size, &len, NULL, 0) != -1)
   131  		mprotect(cov->data + page_size, mmap_alloc_size - page_size, PROT_READ);
   132  #endif
   133  }
   134  
   135  static void cover_unprotect(cover_t* cov)
   136  {
   137  #if GOOS_freebsd
   138  	size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE;
   139  	mprotect(cov->data, mmap_alloc_size, PROT_READ | PROT_WRITE);
   140  #elif GOOS_openbsd
   141  	size_t mmap_alloc_size = kCoverSize * sizeof(uintptr_t);
   142  	mprotect(cov->data, mmap_alloc_size, PROT_READ | PROT_WRITE);
   143  #endif
   144  }
   145  
   146  static void cover_enable(cover_t* cov, bool collect_comps, bool extra)
   147  {
   148  	int kcov_mode = collect_comps ? KCOV_MODE_TRACE_CMP : KCOV_MODE_TRACE_PC;
   149  #if GOOS_freebsd
   150  	// FreeBSD uses an int as the third argument.
   151  	if (ioctl(cov->fd, KIOENABLE, kcov_mode))
   152  		exitf("cover enable write trace failed, mode=%d", kcov_mode);
   153  #elif GOOS_openbsd
   154  	// OpenBSD uses an pointer to an int as the third argument.
   155  	// Whether it is a regular coverage or an extra coverage, the enable
   156  	// ioctl is the same.
   157  	if (ioctl(cov->fd, KIOENABLE, &kcov_mode))
   158  		exitf("cover enable write trace failed, mode=%d", kcov_mode);
   159  #elif GOOS_netbsd
   160  	// Whether it is a regular coverage or a USB coverage, the enable
   161  	// ioctl is the same.
   162  	if (ioctl(cov->fd, KCOV_IOC_ENABLE, &kcov_mode))
   163  		exitf("cover enable write trace failed, mode=%d", kcov_mode);
   164  #endif
   165  }
   166  
   167  static void cover_reset(cover_t* cov)
   168  {
   169  	*(uint64*)cov->data = 0;
   170  }
   171  
   172  static void cover_collect(cover_t* cov)
   173  {
   174  	cov->size = *(uint64*)cov->data;
   175  }
   176  
   177  static bool use_cover_edges(uint64 pc)
   178  {
   179  	return true;
   180  }
   181  
   182  #if GOOS_netbsd
   183  #define SYZ_HAVE_FEATURES 1
   184  static feature_t features[] = {
   185      {rpc::Feature::USBEmulation, setup_usb},
   186      {rpc::Feature::Fault, setup_fault},
   187  };
   188  
   189  static void setup_sysctl(void)
   190  {
   191  }
   192  
   193  static void setup_cgroups(void)
   194  {
   195  }
   196  #endif