aboutsummaryrefslogtreecommitdiffstats
path: root/executor/executor_bsd.h
blob: 88e1f46cb4c9a3d97d72667d8f1df6011e2423d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/kcov.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#if GOOS_openbsd
#include <sys/sysctl.h>
#endif

static void os_init(int argc, char** argv, void* data, size_t data_size)
{
#if GOOS_openbsd
	// W^X not allowed by default on OpenBSD.
	int prot = PROT_READ | PROT_WRITE;
#elif GOOS_netbsd
	// W^X not allowed by default on NetBSD (PaX MPROTECT).
	int prot = PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC);
#else
	int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
#endif

	int flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED_EXCLUSIVE;
#if GOOS_freebsd
	// Fail closed if the chosen data offset conflicts with an existing mapping.
	flags |= MAP_EXCL;
#endif

	void* got = mmap(data, data_size, prot, flags, -1, 0);
	if (data != got)
		failmsg("mmap of data segment failed", "want %p, got %p", data, got);

	// Makes sure the file descriptor limit is sufficient to map control pipes.
	struct rlimit rlim;
	rlim.rlim_cur = rlim.rlim_max = kMaxFd;
	setrlimit(RLIMIT_NOFILE, &rlim);

	// A SIGCHLD handler makes sleep in loop exit immediately return with EINTR with a child exits.
	struct sigaction act = {};
	act.sa_handler = [](int) {};
	sigaction(SIGCHLD, &act, nullptr);
}

static intptr_t execute_syscall(const call_t* c, intptr_t a[kMaxArgs])
{
	if (c->call)
		return c->call(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
#if GOOS_openbsd
	failmsg("no call", "missing target for %s", c->name);
#else
	return __syscall(c->sys_nr, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
#endif
}

static void cover_open(cover_t* cov, bool extra)
{
	int fd = open("/dev/kcov", O_RDWR);
	if (fd == -1)
		fail("open of /dev/kcov failed");
	if (dup2(fd, cov->fd) < 0)
		failmsg("failed to dup cover fd", "from=%d, to=%d", fd, cov->fd);
	close(fd);

#if GOOS_freebsd
	if (ioctl(cov->fd, KIOSETBUFSIZE, kCoverSize))
		fail("ioctl init trace write failed");
	cov->data_size = kCoverSize * KCOV_ENTRY_SIZE;
#elif GOOS_openbsd
	unsigned long cover_size = kCoverSize;
	if (ioctl(cov->fd, KIOSETBUFSIZE, &cover_size))
		fail("ioctl init trace write failed");
	if (extra) {
		struct kio_remote_attach args;
		args.subsystem = KCOV_REMOTE_COMMON;
		args.id = 0;
		if (ioctl(cov->fd, KIOREMOTEATTACH, &args))
			fail("ioctl remote attach failed");
	}
	cov->data_size = kCoverSize * (is_kernel_64_bit ? 8 : 4);
#elif GOOS_netbsd
	uint64_t cover_size;
	if (extra) {
		// USB coverage, the size is fixed to the maximum
		cover_size = (256 << 10); // maximum size
		struct kcov_ioc_remote_attach args;
		args.subsystem = KCOV_REMOTE_VHCI;
		args.id = KCOV_REMOTE_VHCI_ID(procid, 1); // first port
		if (ioctl(cov->fd, KCOV_IOC_REMOTE_ATTACH, &args))
			fail("ioctl remote attach failed");
	} else {
		// Normal coverage
		cover_size = kCoverSize;
		if (ioctl(cov->fd, KCOV_IOC_SETBUFSIZE, &cover_size))
			fail("ioctl init trace write failed");
	}
	cov->data_size = cover_size * KCOV_ENTRY_SIZE;
#endif
}

static void cover_mmap(cover_t* cov)
{
	if (cov->mmap_alloc_ptr != NULL)
		fail("cover_mmap invoked on an already mmapped cover_t object");
	cov->mmap_alloc_size = cov->data_size;
	void* mmap_ptr = mmap(NULL, cov->mmap_alloc_size, PROT_READ | PROT_WRITE,
			      MAP_SHARED, cov->fd, 0);
	if (mmap_ptr == MAP_FAILED)
		fail("cover mmap failed");
	cov->mmap_alloc_ptr = (char*)mmap_ptr;
	cov->data = (char*)mmap_ptr;
	cov->data_end = cov->data + cov->mmap_alloc_size;
	cov->data_offset = is_kernel_64_bit ? sizeof(uint64_t) : sizeof(uint32_t);
	cov->pc_offset = 0;
}

static void cover_protect(cover_t* cov)
{
	if (cov->mmap_alloc_ptr == NULL)
		fail("cover_protect invoked on an unmapped cover_t object");
#if GOOS_freebsd
	size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE;
	long page_size = sysconf(_SC_PAGESIZE);
	if (page_size > 0)
		mprotect(cov->mmap_alloc_ptr + page_size, mmap_alloc_size - page_size,
			 PROT_READ);
#elif GOOS_openbsd
	int mib[2], page_size;
	size_t mmap_alloc_size = kCoverSize * sizeof(uintptr_t);
	mib[0] = CTL_HW;
	mib[1] = HW_PAGESIZE;
	size_t len = sizeof(page_size);
	if (sysctl(mib, ARRAY_SIZE(mib), &page_size, &len, NULL, 0) != -1)
		mprotect(cov->mmap_alloc_ptr + page_size, mmap_alloc_size - page_size, PROT_READ);
#endif
}

static void cover_unprotect(cover_t* cov)
{
	if (cov->mmap_alloc_ptr == NULL)
		fail("cover_unprotect invoked on an unmapped cover_t object");
#if GOOS_freebsd
	size_t mmap_alloc_size = kCoverSize * KCOV_ENTRY_SIZE;
	mprotect(cov->mmap_alloc_ptr, mmap_alloc_size, PROT_READ | PROT_WRITE);
#elif GOOS_openbsd
	size_t mmap_alloc_size = kCoverSize * sizeof(uintptr_t);
	mprotect(cov->mmap_alloc_ptr, mmap_alloc_size, PROT_READ | PROT_WRITE);
#endif
}

static void cover_enable(cover_t* cov, bool collect_comps, bool extra)
{
	int kcov_mode = collect_comps ? KCOV_MODE_TRACE_CMP : KCOV_MODE_TRACE_PC;
#if GOOS_freebsd
	// FreeBSD uses an int as the third argument.
	if (ioctl(cov->fd, KIOENABLE, kcov_mode))
		exitf("cover enable write trace failed, mode=%d", kcov_mode);
#elif GOOS_openbsd
	// OpenBSD uses an pointer to an int as the third argument.
	// Whether it is a regular coverage or an extra coverage, the enable
	// ioctl is the same.
	if (ioctl(cov->fd, KIOENABLE, &kcov_mode))
		exitf("cover enable write trace failed, mode=%d", kcov_mode);
#elif GOOS_netbsd
	// Whether it is a regular coverage or a USB coverage, the enable
	// ioctl is the same.
	if (ioctl(cov->fd, KCOV_IOC_ENABLE, &kcov_mode))
		exitf("cover enable write trace failed, mode=%d", kcov_mode);
#endif
}

static void cover_reset(cover_t* cov)
{
	*(uint64*)cov->data = 0;
}

static void cover_collect(cover_t* cov)
{
	cov->size = *(uint64*)cov->data;
}

#if GOOS_netbsd
#define SYZ_HAVE_FEATURES 1
static feature_t features[] = {
    {rpc::Feature::USBEmulation, setup_usb},
    {rpc::Feature::Fault, setup_fault},
};

static void setup_sysctl(void)
{
}

static void setup_cgroups(void)
{
}
#endif