aboutsummaryrefslogtreecommitdiffstats
path: root/executor/executor_test.h
blob: c2802dd2a39d5643b4c6db0b0c865a533539cc47 (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
// Copyright 2018 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 <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#ifdef __linux__
#include <sys/prctl.h>
#endif

static uint64 kernel_text_start = 0xc0dec0dec0000000;
static uint64 kernel_text_mask = 0xffffff;

static void os_init(int argc, char** argv, void* data, size_t data_size)
{
#ifdef __linux__
	prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
	// There's a risk that the parent has exited before we get to call prctl().
	// In that case, let's assume that the child must have been reassigned to PID=1.
	if (getppid() == 1)
		exitf("the parent process was killed");
#endif
	void* got = mmap(data, data_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
	if (data != got)
		failmsg("mmap of data segment failed", "want %p, got %p", data, got);
	is_kernel_64_bit = sizeof(unsigned long) == 8;
}

#ifdef __clang__
#define notrace
#else
#define notrace __attribute__((no_sanitize_coverage))
#endif

extern "C" notrace void __sanitizer_cov_trace_pc(void)
{
	unsigned long ip = (unsigned long)__builtin_return_address(0);
	// Convert to what is_kernel_pc will accept as valid coverage;
	ip = kernel_text_start | (ip & kernel_text_mask);
	if (current_thread == nullptr || current_thread->cov.data == nullptr || current_thread->cov.collect_comps)
		return;
	unsigned long* start = (unsigned long*)current_thread->cov.data;
	unsigned long* end = (unsigned long*)current_thread->cov.data_end;
	int pos = start[0];
	if (start + pos + 1 < end) {
		start[0] = pos + 1;
		start[pos + 1] = ip;
	}
}

static intptr_t execute_syscall(const call_t* c, intptr_t a[kMaxArgs])
{
	// Inject coverage PC even when built w/o coverage instrumentation.
	// This allows to pass machine check with coverage enabled.
	// pkg/fuzzer tests with coverage instrumentation shouldn't be distracted by the additional PC,
	// and syz_inject_cover overwrites the whole array so will remote it.
	__sanitizer_cov_trace_pc();
	return c->call(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
}

static void cover_open(cover_t* cov, bool extra)
{
	cov->mmap_alloc_size = kCoverSize * sizeof(unsigned long);
}

static void cover_enable(cover_t* cov, bool collect_comps, bool extra)
{
	cov->collect_comps = collect_comps;
}

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

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

static void cover_protect(cover_t* cov)
{
}

static void cover_mmap(cover_t* cov)
{
	if (cov->data != NULL)
		fail("cover_mmap invoked on an already mmapped cover_t object");
	if (cov->mmap_alloc_size == 0)
		fail("cover_t structure is corrupted");
	cov->data = (char*)mmap(NULL, cov->mmap_alloc_size,
				PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
	if (cov->data == MAP_FAILED)
		exitf("cover mmap failed");
	cov->data_end = cov->data + cov->mmap_alloc_size;
	cov->data_offset = sizeof(unsigned long);
	// We don't care about the specific PC values for now.
	// Once we do, we might want to consider ASLR here.
	cov->pc_offset = 0;
}

static void cover_unprotect(cover_t* cov)
{
}

static bool is_kernel_data(uint64 addr)
{
	return addr >= 0xda1a0000 && addr <= 0xda1a1000;
}

static int is_kernel_pc(uint64 pc)
{
	uint64 start = kernel_text_start;
	uint64 end = kernel_text_start | kernel_text_mask;
	if (!is_kernel_64_bit) {
		start = (uint32)start;
		end = (uint32)end;
	}
	return pc >= start && pc <= end ? 1 : -1;
}

static bool use_cover_edges(uint64 pc)
{
	return true;
}

static long syz_inject_cover(volatile long a, volatile long b, volatile long c)
{
	cover_t* cov = &current_thread->cov;
	if (cov->data == nullptr)
		return ENOENT;
	is_kernel_64_bit = a;
	cov->data_offset = is_kernel_64_bit ? sizeof(uint64_t) : sizeof(uint32_t);
	uint32 size = std::min((uint32)c, cov->mmap_alloc_size);
	memcpy(cov->data, (void*)b, size);
	memset(cov->data + size, 0xcd, std::min<uint64>(100, cov->mmap_alloc_size - size));
	return 0;
}

static const char* setup_fault()
{
	return nullptr;
}

static const char* setup_leak()
{
	return "leak detection is not supported";
}

// Test various ways how feature setup can fail.
// We don't care about these features for test OS,
// this is just to test the feature support detection code.
#define SYZ_HAVE_FEATURES 1
static feature_t features[] = {
    {rpc::Feature::Fault, setup_fault},
    {rpc::Feature::Leak, setup_leak},
};