summaryrefslogtreecommitdiffstats
path: root/src/runtime/libfuzzer_amd64.s
blob: e30b768a05a49a9908d335c944ccc9db7865d510 (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
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build libfuzzer

#include "go_asm.h"
#include "go_tls.h"
#include "textflag.h"

// Based on race_amd64.s; see commentary there.

#ifdef GOOS_windows
#define RARG0 CX
#define RARG1 DX
#define RARG2 R8
#define RARG3 R9
#else
#define RARG0 DI
#define RARG1 SI
#define RARG2 DX
#define RARG3 CX
#endif

// void runtime·libfuzzerCall4(fn, hookId int, s1, s2 unsafe.Pointer, result uintptr)
// Calls C function fn from libFuzzer and passes 4 arguments to it.
TEXT	runtime·libfuzzerCall4(SB), NOSPLIT, $0-40
	MOVQ	fn+0(FP), AX
	MOVQ	hookId+8(FP), RARG0
	MOVQ	s1+16(FP), RARG1
	MOVQ	s2+24(FP), RARG2
	MOVQ	result+32(FP), RARG3

	get_tls(R12)
	MOVQ	g(R12), R14
	MOVQ	g_m(R14), R13

	// Switch to g0 stack.
	MOVQ	SP, R12		// callee-saved, preserved across the CALL
	MOVQ	m_g0(R13), R10
	CMPQ	R10, R14
	JE	call	// already on g0
	MOVQ	(g_sched+gobuf_sp)(R10), SP
call:
	ANDQ	$~15, SP	// alignment for gcc ABI
	CALL	AX
	MOVQ	R12, SP
	RET

// void runtime·libfuzzerCallTraceIntCmp(fn, arg0, arg1, fakePC uintptr)
// Calls C function fn from libFuzzer and passes 2 arguments to it after
// manipulating the return address so that libfuzzer's integer compare hooks
// work
// libFuzzer's compare hooks obtain the caller's address from the compiler
// builtin __builtin_return_address. Since we invoke the hooks always
// from the same native function, this builtin would always return the same
// value. Internally, the libFuzzer hooks call through to the always inlined
// HandleCmp and thus can't be mimicked without patching libFuzzer.
//
// We solve this problem via an inline assembly trampoline construction that
// translates a runtime argument `fake_pc` in the range [0, 512) into a call to
// a hook with a fake return address whose lower 9 bits are `fake_pc` up to a
// constant shift. This is achieved by pushing a return address pointing into
// 512 ret instructions at offset `fake_pc` onto the stack and then jumping
// directly to the address of the hook.
//
// Note: We only set the lowest 9 bits of the return address since only these
// bits are used by the libFuzzer value profiling mode for integer compares, see
// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L390
// as well as
// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerValueBitMap.h#L34
// ValueProfileMap.AddValue() truncates its argument to 16 bits and shifts the
// PC to the left by log_2(128)=7, which means that only the lowest 16 - 7 bits
// of the return address matter. String compare hooks use the lowest 12 bits,
// but take the return address as an argument and thus don't require the
// indirection through a trampoline.
// TODO: Remove the inline assembly trampoline once a PC argument has been added to libfuzzer's int compare hooks.
TEXT	runtime·libfuzzerCallTraceIntCmp(SB), NOSPLIT, $0-32
	MOVQ	fn+0(FP), AX
	MOVQ	arg0+8(FP), RARG0
	MOVQ	arg1+16(FP), RARG1
	MOVQ	fakePC+24(FP), R8

	get_tls(R12)
	MOVQ	g(R12), R14
	MOVQ	g_m(R14), R13

	// Switch to g0 stack.
	MOVQ	SP, R12		// callee-saved, preserved across the CALL
	MOVQ	m_g0(R13), R10
	CMPQ	R10, R14
	JE	call	// already on g0
	MOVQ	(g_sched+gobuf_sp)(R10), SP
call:
	ANDQ	$~15, SP	// alignment for gcc ABI
	SUBQ	$8, SP
	// Load the address of the end of the function and push it into the stack.
	// This address will be jumped to after executing the return instruction
	// from the return sled. There we reset the stack pointer and return.
	MOVQ    $end_of_function<>(SB), BX
	PUSHQ   BX
	// Load the starting address of the return sled into BX.
	MOVQ    $ret_sled<>(SB), BX
	// Load the address of the i'th return instruction from the return sled.
	// The index is given in the fakePC argument.
	ADDQ    R8, BX
	PUSHQ   BX
	// Call the original function with the fakePC return address on the stack.
	// Function arguments arg0 and arg1 are passed in the registers specified
	// by the x64 calling convention.
	JMP     AX
// This code will not be executed and is only there to satisfy assembler
// check of a balanced stack.
not_reachable:
	POPQ    BX
	POPQ    BX
	RET

TEXT end_of_function<>(SB), NOSPLIT, $0-0
	MOVQ	R12, SP
	RET

#define REPEAT_8(a) a \
  a \
  a \
  a \
  a \
  a \
  a \
  a

#define REPEAT_512(a) REPEAT_8(REPEAT_8(REPEAT_8(a)))

TEXT ret_sled<>(SB), NOSPLIT, $0-0
	REPEAT_512(RET)

// void runtime·libfuzzerCallWithTwoByteBuffers(fn, start, end *byte)
// Calls C function fn from libFuzzer and passes 2 arguments of type *byte to it.
TEXT	runtime·libfuzzerCallWithTwoByteBuffers(SB), NOSPLIT, $0-24
	MOVQ	fn+0(FP), AX
	MOVQ	start+8(FP), RARG0
	MOVQ	end+16(FP), RARG1

	get_tls(R12)
	MOVQ	g(R12), R14
	MOVQ	g_m(R14), R13

	// Switch to g0 stack.
	MOVQ	SP, R12		// callee-saved, preserved across the CALL
	MOVQ	m_g0(R13), R10
	CMPQ	R10, R14
	JE	call	// already on g0
	MOVQ	(g_sched+gobuf_sp)(R10), SP
call:
	ANDQ	$~15, SP	// alignment for gcc ABI
	CALL	AX
	MOVQ	R12, SP
	RET