1// Copyright 2019 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45//go:build libfuzzer67#include "go_asm.h"8#include "go_tls.h"9#include "textflag.h"1011// Based on race_amd64.s; see commentary there.1213#ifdef GOOS_windows14#define RARG0 CX15#define RARG1 DX16#define RARG2 R817#define RARG3 R918#else19#define RARG0 DI20#define RARG1 SI21#define RARG2 DX22#define RARG3 CX23#endif2425// void runtime·libfuzzerCall4(fn, hookId int, s1, s2 unsafe.Pointer, result uintptr)26// Calls C function fn from libFuzzer and passes 4 arguments to it.27TEXT runtime·libfuzzerCall4(SB), NOSPLIT, $0-4028 MOVQ fn+0(FP), AX29 MOVQ hookId+8(FP), RARG030 MOVQ s1+16(FP), RARG131 MOVQ s2+24(FP), RARG232 MOVQ result+32(FP), RARG33334 get_tls(R12)35 MOVQ g(R12), R1436 MOVQ g_m(R14), R133738 // Switch to g0 stack.39 MOVQ SP, R12 // callee-saved, preserved across the CALL40 MOVQ m_g0(R13), R1041 CMPQ R10, R1442 JE call // already on g043 MOVQ (g_sched+gobuf_sp)(R10), SP44call:45 ANDQ $~15, SP // alignment for gcc ABI46 CALL AX47 MOVQ R12, SP48 RET4950// void runtime·libfuzzerCallTraceIntCmp(fn, arg0, arg1, fakePC uintptr)51// Calls C function fn from libFuzzer and passes 2 arguments to it after52// manipulating the return address so that libfuzzer's integer compare hooks53// work54// libFuzzer's compare hooks obtain the caller's address from the compiler55// builtin __builtin_return_address. Since we invoke the hooks always56// from the same native function, this builtin would always return the same57// value. Internally, the libFuzzer hooks call through to the always inlined58// HandleCmp and thus can't be mimicked without patching libFuzzer.59//60// We solve this problem via an inline assembly trampoline construction that61// translates a runtime argument `fake_pc` in the range [0, 512) into a call to62// a hook with a fake return address whose lower 9 bits are `fake_pc` up to a63// constant shift. This is achieved by pushing a return address pointing into64// 512 ret instructions at offset `fake_pc` onto the stack and then jumping65// directly to the address of the hook.66//67// Note: We only set the lowest 9 bits of the return address since only these68// bits are used by the libFuzzer value profiling mode for integer compares, see69// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L39070// as well as71// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerValueBitMap.h#L3472// ValueProfileMap.AddValue() truncates its argument to 16 bits and shifts the73// PC to the left by log_2(128)=7, which means that only the lowest 16 - 7 bits74// of the return address matter. String compare hooks use the lowest 12 bits,75// but take the return address as an argument and thus don't require the76// indirection through a trampoline.77// TODO: Remove the inline assembly trampoline once a PC argument has been added to libfuzzer's int compare hooks.78TEXT runtime·libfuzzerCallTraceIntCmp(SB), NOSPLIT, $0-3279 MOVQ fn+0(FP), AX80 MOVQ arg0+8(FP), RARG081 MOVQ arg1+16(FP), RARG182 MOVQ fakePC+24(FP), R88384 get_tls(R12)85 MOVQ g(R12), R1486 MOVQ g_m(R14), R138788 // Switch to g0 stack.89 MOVQ SP, R12 // callee-saved, preserved across the CALL90 MOVQ m_g0(R13), R1091 CMPQ R10, R1492 JE call // already on g093 MOVQ (g_sched+gobuf_sp)(R10), SP94call:95 ANDQ $~15, SP // alignment for gcc ABI96 SUBQ $8, SP97 // Load the address of the end of the function and push it into the stack.98 // This address will be jumped to after executing the return instruction99 // from the return sled. There we reset the stack pointer and return.100 MOVQ $end_of_function<>(SB), BX101 PUSHQ BX102 // Load the starting address of the return sled into BX.103 MOVQ $ret_sled<>(SB), BX104 // Load the address of the i'th return instruction from the return sled.105 // The index is given in the fakePC argument.106 ADDQ R8, BX107 PUSHQ BX108 // Call the original function with the fakePC return address on the stack.109 // Function arguments arg0 and arg1 are passed in the registers specified110 // by the x64 calling convention.111 JMP AX112// This code will not be executed and is only there to satisfy assembler113// check of a balanced stack.114not_reachable:115 POPQ BX116 POPQ BX117 RET118119TEXT end_of_function<>(SB), NOSPLIT, $0-0120 MOVQ R12, SP121 RET122123#define REPEAT_8(a) a \124 a \125 a \126 a \127 a \128 a \129 a \130 a131132#define REPEAT_512(a) REPEAT_8(REPEAT_8(REPEAT_8(a)))133134TEXT ret_sled<>(SB), NOSPLIT, $0-0135 REPEAT_512(RET)136137// void runtime·libfuzzerCallWithTwoByteBuffers(fn, start, end *byte)138// Calls C function fn from libFuzzer and passes 2 arguments of type *byte to it.139TEXT runtime·libfuzzerCallWithTwoByteBuffers(SB), NOSPLIT, $0-24140 MOVQ fn+0(FP), AX141 MOVQ start+8(FP), RARG0142 MOVQ end+16(FP), RARG1143144 get_tls(R12)145 MOVQ g(R12), R14146 MOVQ g_m(R14), R13147148 // Switch to g0 stack.149 MOVQ SP, R12 // callee-saved, preserved across the CALL150 MOVQ m_g0(R13), R10151 CMPQ R10, R14152 JE call // already on g0153 MOVQ (g_sched+gobuf_sp)(R10), SP154call:155 ANDQ $~15, SP // alignment for gcc ABI156 CALL AX157 MOVQ R12, SP158 RET
Findings
✓ No findings reported for this file.