-
-
AFL二三事 -- 源码分析 2
-
发表于: 2021-9-27 16:52 31562
-
本文为《AFL二三事》-- 源码分析系列的第二篇,主要阅读AFL的另外一种插桩方式 ——llvm
模式。这主要是因为通过 afl-gcc
的方式进行插桩,在效率和性能上已经不能完美应对现代复杂的软件程序。随着llvm的成熟发展,AFL提供了更好的插桩方式 afl-clang-fast
,通过llvm pass来实现插桩,从而提升性能。
当别人都要快的时候,你要慢下来。
LLVM 主要为了解决编译时多种多样的前端和后端导致编译环境复杂、苛刻的问题,其核心为设计了一个称为 LLVM IR
的中间表示,并以库的形式提供一些列接口,以提供诸如操作 IR 、生成目标平台代码等等后端的功能。其整体架构如下所示:
不同的前端和后端使用统一的中间代码LLVM InterMediate Representation(LLVM IR)
,其结果就是如果需要支持一门新的编程语言,只需要实现一个新的前端;如果需要支持一款新的硬件设备,只需要实现一个新的后端;优化阶段为通用阶段,针对统一的 LLVM IR ,与新的编程语言和硬件设备无关。
GCC 的前后端耦合在一起,没有进行分离,所以GCC为了支持一门新的编程语言或一个新的硬件设备,需要重新开发前端到后端的完整过程。
Clang 是 LLVM 项目的一个子项目,它是 LLVM 架构下的 C/C++/Objective-C 的编译器,是 LLVM 前端的一部分。相较于GCC,具备编译速度快、占用内存少、模块化设计、诊断信息可读性强、设计清晰简单等优点。
最终从源码到机器码的流程如下(以 Clang 做编译器为例):
(LLVM Pass 是一些中间过程处理 IR 的可以用户自定义的内容,可以用来遍历、修改 IR 以达到插桩、优化、静态分析等目的。)
代码首先由编译器前端clang处理后得到中间代码IR,然后经过各 LLVM Pass 进行优化和转换,最终交给编译器后端生成机器码。
AFL的 llvm_mode
可以实现编译器级别的插桩,可以替代 afl-gcc
或 afl-clang
使用的比较“粗暴”的汇编级别的重写的方法,且具备如下几个优势:
在AFL的 llvm_mode
文件夹下包含3个文件: afl-clang-fast.c
,afl-llvm-pass.so.cc
, afl-llvm-rt.o.c
。
afl-llvm-rt.o.c
文件主要是重写了 afl-as.h
文件中的 main_payload
部分,方便调用;
afl-llvm-pass.so.cc
文件主要是当通过 afl-clang-fast
调用 clang 时,这个pass被插入到 LLVM 中,告诉编译器添加与 `afl-as.h
中大致等效的代码;
afl-clang-fast.c
文件本质上是 clang 的 wrapper,最终调用的还是 clang 。但是与 afl-gcc
一样,会进行一些参数处理。
llvm_mode
的插桩思路就是通过编写pass来实现信息记录,对每个基本块都插入探针,具体代码在 afl-llvm-pass.so.cc
文件中,初始化和forkserver操作通过链接完成。
main
函数的全部逻辑如下:
主要是对 find_obj(), edit_params(), execvp()
函数的调用,
其中主要有以下三个函数的调用:
这里后两个函数的作用与 afl-gcc.c
中的作用基本相同,只是对参数的处理过程存在不同,不同的主要是 find_obj()
函数。
find_obj()
函数的控制流逻辑如下:
函数的主要功能是在寻找AFL的路径以找到 afl-llvm-rt.o
文件,该文件即为要用到的运行时库。
该函数的主要作用仍然为编辑参数数组,其控制流程如下:
首先,判断执行的是否为 afl-clang-fast++
:
判断是否定义了 USE_TRACE_PC
宏,如果有,添加 -fsanitize-coverage=trace-pc-guard -mllvm(only Android) -sanitizer-coverage-block-threshold=0(only Android)
选项到参数数组;如果没有,依次将 -Xclang -load -Xclang obj_path/afl-llvm-pass.so -Qunused-arguments
选项添加到参数数组;(这里涉及到llvm_mode使用的2种插桩方式:默认使用的是传统模式,使用 afl-llvm-pass.so
注入来进行插桩,这种方式较为稳定;另外一种是处于实验阶段的方式——trace-pc-guard
模式,对于该模式的详细介绍可以参考llvm相关文档——tracing-pcs-with-guards)
遍历传递给 afl-clang-fast
的参数,进行一定的检查和设置,并添加到 cc_params
数组:
检查环境变量是否设置了 AFL_HARDEN
:
检查参数中是否存在 -fsanitize=memory
,即 asan_set
为0:
检查是否定义了 USE_TRACE_PC
宏,如果存在定义,检查是否存在环境变量 AFL_INST_RATIO
,如果存在,抛出异常AFL_INST_RATIO
无法在trace-pc时使用;
afl-llvm-pass.so.cc
文件实现了 LLVM-mode 下的一个插桩 LLVM Pass。
本文不过多关心如何实现一个LLVM Pass,重点分析该pass的实现逻辑。
该文件只有一个Transform pass:AFLCoverage
,继承自 ModulePass
,实现了一个 runOnModule
函数,这也是我们需要重点分析的函数。
对pass进行注册的部分源码如下:
其核心功能为向PassManager注册新的pass,每个pass相互独立。
对于pass注册的细节部分请读者自行研究llvm的相关内容。
该函数为该文件中的关键函数,其控制流程图如下:
首先,通过 getContext()
来获取 LLVMContext
,获取进程上下文:
设置插桩密度:读取环境变量 AFL_INST_RATIO
,并赋值给 inst_ratio
,其值默认为100,范围为 1~100,该值表示插桩概率;
获取只想共享内存shm的指针以及上一个基本块的随机ID:
进入插桩过程:
通过 for
循环遍历每个BB(基本块),寻找BB中适合插入桩代码的位置,然后通过初始化 IRBuilder
实例执行插入;
随机创建当前BB的ID,然后插入load指令,获取前一个BB的ID;
插入load指令,获取共享内存的地址,并调用 CreateGEP
函数获取共享内存中指定index的地址;
插入load指令,获取对应index地址的值;插入add指令加一,然后创建store指令写入新值,并更新共享内存;
右移 cur_loc
,插入store指令,更新 __afl_prev_loc
;
最后对插桩计数加1;
扫描下一个BB,根据设置是否为quiet模式等,并判断 inst_blocks
是否为0,如果为0则说明没有进行插桩;
整个插桩过程较为清晰,没有冗余动作和代码。
该文件主要实现了llvm_mode的3个特殊功能:deferred instrumentation, persistent mode,trace-pc-guard mode
。
AFL会尝试通过只执行一次目标二进制文件来提升性能,在 main()
之前暂停程序,然后克隆“主”进程获得一个稳定的可进行持续fuzz的目标。简言之,避免目标二进制文件的多次、重复的完整运行,而是采取了一种类似快照的机制。
虽然这种机制可以减少程序运行在操作系统、链接器和libc级别的消耗,但是在面对大型配置文件的解析时,优势并不明显。
在这种情况下,可以将 forkserver
的初始化放在大部分初始化工作完成之后、二进制文件解析之前来进行,这在某些情况下可以提升10倍以上的性能。我们把这种方式称为LLVM模式下的 deferred instrumentation
。
首先,在代码中找到可以进行延迟克隆的合适位置。 这需要极端小心地完成,以避免破坏二进制文件。 特别是,如果您在以下情况下选择一个位置,程序可能会出现故障:
首先,在代码中寻找可以进行延迟克隆的合适的、不会破坏原二进制文件的位置,然后添加如下代码:
以上代码插入,在 afl-clang-fast.c
文件中有说明:
__afl_manual_init()
函数实现如下:
首先,判断是否进行了初始化,没有则调用 __afl_map_shm()
函数进行共享内存初始化。 __afl_map_shm()
函数如下:
然后,调用 __afl_start_forkserver()
函数开始执行forkserver:
上述逻辑可以概括如下:
首先,设置 child_stopped = 0
,写入4字节到状态管道,通知fuzzer已准备完成;
进入 while
,开启fuzz循环:
如果 child_stopped
为0,则fork出一个子进程执行fuzz,关闭和控制管道和状态管道相关的fd,跳出fuzz循环;
如果 child_stopped
为1,在 persistent mode
下进行的特殊处理,此时子进程还活着,只是被暂停了,可以通过kill(child_pid, SIGCONT)
来简单的重启,然后设置child_stopped
为0;
persistent mode
并没有通过fork子进程的方式来执行fuzz。一些库中提供的API是无状态的,或者可以在处理不同输入文件之间进行重置,恢复到之前的状态。执行此类重置时,可以使用一个长期存活的进程来测试多个用例,以这种方式来减少重复的 fork()
调用和操作系统的开销。不得不说,这种思路真的很优秀。
一个基础的框架大概如下:
设置一个 while
循环,并指定循环次数。在每次循环内,首先读取数据,然后调用想fuzz的库代码,然后重置状态,继续循环。(本质上也是一种快照。)
对于循环次数的设置,循环次数控制了AFL从头重新启动过程之前的最大迭代次数,较小的循环次数可以降低内存泄漏类故障的影响,官方建议的数值为1000。(循环次数设置过高可能出现较多意料之外的问题,并不建议设置过高。)
一个 persistent mode
的样例程序如下:
宏定义 __AFL_LOOP
内部调用 __afl_persistent_loop
函数:
__afl_persistent_loop(unsigned int max_cnt)
的逻辑如下:
结合源码梳理一下其逻辑:
首先判读是否为第一次执行循环,如果是第一次:
如果不是第一次执行循环,在 persistent mode 下,且 --cycle_cnt
大于1:
设置 __afl_area_ptr[0]
为1,__afl_prev_loc
为0,然后直接返回1
如果 cycle_cnt
为0,设置__afl_area_ptr
指向数组 __afl_area_initial
。
最后返回0
重新总结一下上面的逻辑:
该功能的使用需要设置宏 AFL_TRACE_PC=1
,然后再执行 afl-clang-fast
时传入参数 -fsanitize-coverage=trace-pc-guard
。
该功能的主要特点是会在每个edge插入桩代码,函数 __sanitizer_cov_trace_pc_guard
会在每个edge进行调用,该函数利用函数参数 guard
指针所指向的 uint32
值来确定共享内存上所对应的地址:
guard
的初始化位于函数 __sanitizer_cov_trace_pc_guard_init
中:
其实在这里最主要的还是 persistent mode
中的逻辑,其他的两个模式对于实际的fuzz工作已经或尚且没有太大的指导意义。但是研究一下其源码实现,可以发现AFL的设计和开发真的很巧妙,其中很多思路值得开发人员和安全人员学习和借鉴。
namespace {
class
AFLCoverage : public ModulePass {
public:
static char
ID
;
AFLCoverage() : ModulePass(
ID
) { }
bool
runOnModule(Module &M) override;
/
/
StringRef getPassName() const override {
/
/
return
"American Fuzzy Lop Instrumentation"
;
/
/
}
};
}
namespace {
class
AFLCoverage : public ModulePass {
public:
static char
ID
;
AFLCoverage() : ModulePass(
ID
) { }
bool
runOnModule(Module &M) override;
/
/
StringRef getPassName() const override {
/
/
return
"American Fuzzy Lop Instrumentation"
;
/
/
}
};
}
static void registerAFLPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new AFLCoverage());
}
static RegisterStandardPasses RegisterAFLPass(
PassManagerBuilder::EP_ModuleOptimizerEarly, registerAFLPass);
static RegisterStandardPasses RegisterAFLPass0(
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
static void registerAFLPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new AFLCoverage());
}
static RegisterStandardPasses RegisterAFLPass(
PassManagerBuilder::EP_ModuleOptimizerEarly, registerAFLPass);
static RegisterStandardPasses RegisterAFLPass0(
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
LLVMContext &C
=
M.getContext();
IntegerType
*
Int8Ty
=
IntegerType::getInt8Ty(C);
IntegerType
*
Int32Ty
=
IntegerType::getInt32Ty(C);
LLVMContext &C
=
M.getContext();
IntegerType
*
Int8Ty
=
IntegerType::getInt8Ty(C);
IntegerType
*
Int32Ty
=
IntegerType::getInt32Ty(C);
GlobalVariable
*
AFLMapPtr
=
new GlobalVariable(M, PointerType::get(Int8Ty,
0
), false,
GlobalValue::ExternalLinkage,
0
,
"__afl_area_ptr"
);
GlobalVariable
*
AFLPrevLoc
=
new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage,
0
,
"__afl_prev_loc"
,
0
, GlobalVariable::GeneralDynamicTLSModel,
0
, false);
GlobalVariable
*
AFLMapPtr
=
new GlobalVariable(M, PointerType::get(Int8Ty,
0
), false,
GlobalValue::ExternalLinkage,
0
,
"__afl_area_ptr"
);
GlobalVariable
*
AFLPrevLoc
=
new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage,
0
,
"__afl_prev_loc"
,
0
, GlobalVariable::GeneralDynamicTLSModel,
0
, false);
BasicBlock::iterator IP
=
BB.getFirstInsertionPt();
IRBuilder<> IRB(&(
*
IP));
BasicBlock::iterator IP
=
BB.getFirstInsertionPt();
IRBuilder<> IRB(&(
*
IP));
if
(AFL_R(
100
) >
=
inst_ratio)
continue
;
/
/
如果大于插桩密度,进行随机插桩
/
*
Make up cur_loc
*
/
unsigned
int
cur_loc
=
AFL_R(MAP_SIZE);
ConstantInt
*
CurLoc
=
ConstantInt::get(Int32Ty, cur_loc);
/
/
随机创建当前基本块
ID
/
*
Load prev_loc
*
/
LoadInst
*
PrevLoc
=
IRB.CreateLoad(AFLPrevLoc);
PrevLoc
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
Value
*
PrevLocCasted
=
IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
/
/
获取上一个基本块的随机
ID
if
(AFL_R(
100
) >
=
inst_ratio)
continue
;
/
/
如果大于插桩密度,进行随机插桩
/
*
Make up cur_loc
*
/
unsigned
int
cur_loc
=
AFL_R(MAP_SIZE);
ConstantInt
*
CurLoc
=
ConstantInt::get(Int32Ty, cur_loc);
/
/
随机创建当前基本块
ID
/
*
Load prev_loc
*
/
LoadInst
*
PrevLoc
=
IRB.CreateLoad(AFLPrevLoc);
PrevLoc
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
Value
*
PrevLocCasted
=
IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
/
/
获取上一个基本块的随机
ID
/
*
Load SHM pointer
*
/
LoadInst
*
MapPtr
=
IRB.CreateLoad(AFLMapPtr);
MapPtr
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
Value
*
MapPtrIdx
=
IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
/
*
Load SHM pointer
*
/
LoadInst
*
MapPtr
=
IRB.CreateLoad(AFLMapPtr);
MapPtr
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
Value
*
MapPtrIdx
=
IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
/
*
Update bitmap
*
/
LoadInst
*
Counter
=
IRB.CreateLoad(MapPtrIdx);
Counter
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
Value
*
Incr
=
IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty,
1
));
IRB.CreateStore(Incr, MapPtrIdx)
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
/
*
Update bitmap
*
/
LoadInst
*
Counter
=
IRB.CreateLoad(MapPtrIdx);
Counter
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
Value
*
Incr
=
IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty,
1
));
IRB.CreateStore(Incr, MapPtrIdx)
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
/
*
Set
prev_loc to cur_loc >>
1
*
/
StoreInst
*
Store
=
IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >>
1
), AFLPrevLoc);
Store
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
/
*
Set
prev_loc to cur_loc >>
1
*
/
StoreInst
*
Store
=
IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >>
1
), AFLPrevLoc);
Store
-
>setMetadata(M.getMDKindID(
"nosanitize"
), MDNode::get(C,
None
));
if
(!be_quiet) {
if
(!inst_blocks) WARNF(
"No instrumentation targets found."
);
else
OKF(
"Instrumented %u locations (%s mode, ratio %u%%)."
,
inst_blocks, getenv(
"AFL_HARDEN"
) ?
"hardened"
:
((getenv(
"AFL_USE_ASAN"
) || getenv(
"AFL_USE_MSAN"
)) ?
"ASAN/MSAN"
:
"non-hardened"
), inst_ratio);
}
if
(!be_quiet) {
if
(!inst_blocks) WARNF(
"No instrumentation targets found."
);
else
OKF(
"Instrumented %u locations (%s mode, ratio %u%%)."
,
inst_blocks, getenv(
"AFL_HARDEN"
) ?
"hardened"
:
((getenv(
"AFL_USE_ASAN"
) || getenv(
"AFL_USE_MSAN"
)) ?
"ASAN/MSAN"
:
"non-hardened"
), inst_ratio);
}
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
cc_params[cc_par_cnt
+
+
]
=
"-D__AFL_INIT()="
"do { static volatile char *_A __attribute__((used)); "
" _A = (char*)\""
DEFER_SIG
"\"; "
#ifdef __APPLE__
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"___afl_manual_init\"); "
#else
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"__afl_manual_init\"); "
#endif /* ^__APPLE__ */
cc_params[cc_par_cnt
+
+
]
=
"-D__AFL_INIT()="
"do { static volatile char *_A __attribute__((used)); "
" _A = (char*)\""
DEFER_SIG
"\"; "
#ifdef __APPLE__
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"___afl_manual_init\"); "
#else
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"__afl_manual_init\"); "
#endif /* ^__APPLE__ */
/
*
This one can be called
from
user code when deferred forkserver mode
is
enabled.
*
/
void __afl_manual_init(void) {
static u8 init_done;
if
(!init_done) {
__afl_map_shm();
__afl_start_forkserver();
init_done
=
1
;
}
}
/
*
This one can be called
from
user code when deferred forkserver mode
is
enabled.
*
/
void __afl_manual_init(void) {
static u8 init_done;
if
(!init_done) {
__afl_map_shm();
__afl_start_forkserver();
init_done
=
1
;
}
}
/
*
SHM setup.
*
/
static void __afl_map_shm(void) {
u8
*
id_str
=
getenv(SHM_ENV_VAR);
/
/
读取环境变量 SHM_ENV_VAR 获取
id
if
(id_str) {
/
/
成功读取
id
u32 shm_id
=
atoi(id_str);
__afl_area_ptr
=
shmat(shm_id, NULL,
0
);
/
/
获取shm地址,赋给 __afl_area_ptr
/
*
Whooooops.
*
/
if
(__afl_area_ptr
=
=
(void
*
)
-
1
) _exit(
1
);
/
/
异常则退出
/
*
Write something into the bitmap so that even with low AFL_INST_RATIO,
our parent doesn't give up on us.
*
/
__afl_area_ptr[
0
]
=
1
;
/
/
进行设置
}
}
/
*
SHM setup.
*
/
static void __afl_map_shm(void) {
u8
*
id_str
=
getenv(SHM_ENV_VAR);
/
/
读取环境变量 SHM_ENV_VAR 获取
id
if
(id_str) {
/
/
成功读取
id
u32 shm_id
=
atoi(id_str);
__afl_area_ptr
=
shmat(shm_id, NULL,
0
);
/
/
获取shm地址,赋给 __afl_area_ptr
/
*
Whooooops.
*
/
if
(__afl_area_ptr
=
=
(void
*
)
-
1
) _exit(
1
);
/
/
异常则退出
/
*
Write something into the bitmap so that even with low AFL_INST_RATIO,
our parent doesn't give up on us.
*
/
__afl_area_ptr[
0
]
=
1
;
/
/
进行设置
}
}
/
*
Fork server logic.
*
/
static void __afl_start_forkserver(void) {
static u8 tmp[
4
];
s32 child_pid;
u8 child_stopped
=
0
;
/
*
Phone home
and
tell the parent that we
're OK. If parent isn'
t there,
assume we're
not
running
in
forkserver mode
and
just execute program.
*
/
if
(write(FORKSRV_FD
+
1
, tmp,
4
) !
=
4
)
return
;
/
/
写入
4
字节到状态管道,通知 fuzzer已准备完成
while
(
1
) {
u32 was_killed;
int
status;
/
*
Wait
for
parent by reading
from
the pipe. Abort
if
read fails.
*
/
if
(read(FORKSRV_FD, &was_killed,
4
) !
=
4
) _exit(
1
);
/
*
If we stopped the child
in
persistent mode, but there was a race
condition
and
afl
-
fuzz already issued SIGKILL, write off the old
process.
*
/
/
/
处于persistent mode且子进程已被killed
if
(child_stopped && was_killed) {
child_stopped
=
0
;
if
(waitpid(child_pid, &status,
0
) <
0
) _exit(
1
);
}
if
(!child_stopped) {
/
*
Once woken up, create a clone of our process.
*
/
child_pid
=
fork();
/
/
重新fork
if
(child_pid <
0
) _exit(
1
);
/
*
In child process: close fds, resume execution.
*
/
if
(!child_pid) {
close(FORKSRV_FD);
/
/
关闭fd,
close(FORKSRV_FD
+
1
);
return
;
}
}
else
{
/
*
Special handling
for
persistent mode:
if
the child
is
alive but
currently stopped, simply restart it with SIGCONT.
*
/
/
/
子进程只是暂停,则进行重启
kill(child_pid, SIGCONT);
child_stopped
=
0
;
}
/
*
In parent process: write PID to pipe, then wait
for
child.
*
/
if
(write(FORKSRV_FD
+
1
, &child_pid,
4
) !
=
4
) _exit(
1
);
if
(waitpid(child_pid, &status, is_persistent ? WUNTRACED :
0
) <
0
)
_exit(
1
);
/
*
In persistent mode, the child stops itself with SIGSTOP to indicate
a successful run. In this case, we want to wake it up without forking
again.
*
/
if
(WIFSTOPPED(status)) child_stopped
=
1
;
/
*
Relay wait status to pipe, then loop back.
*
/
if
(write(FORKSRV_FD
+
1
, &status,
4
) !
=
4
) _exit(
1
);
}
}
/
*
Fork server logic.
*
/
static void __afl_start_forkserver(void) {
static u8 tmp[
4
];
s32 child_pid;
u8 child_stopped
=
0
;
/
*
Phone home
and
tell the parent that we
're OK. If parent isn'
t there,
assume we're
not
running
in
forkserver mode
and
just execute program.
*
/
if
(write(FORKSRV_FD
+
1
, tmp,
4
) !
=
4
)
return
;
/
/
写入
4
字节到状态管道,通知 fuzzer已准备完成
while
(
1
) {
u32 was_killed;
int
status;
/
*
Wait
for
parent by reading
from
the pipe. Abort
if
read fails.
*
/
if
(read(FORKSRV_FD, &was_killed,
4
) !
=
4
) _exit(
1
);
/
*
If we stopped the child
in
persistent mode, but there was a race
condition
and
afl
-
fuzz already issued SIGKILL, write off the old
process.
*
/
/
/
处于persistent mode且子进程已被killed
if
(child_stopped && was_killed) {
child_stopped
=
0
;
if
(waitpid(child_pid, &status,
0
) <
0
) _exit(
1
);
}
if
(!child_stopped) {
/
*
Once woken up, create a clone of our process.
*
/
child_pid
=
fork();
/
/
重新fork
if
(child_pid <
0
) _exit(
1
);
/
*
In child process: close fds, resume execution.
*
/
if
(!child_pid) {
close(FORKSRV_FD);
/
/
关闭fd,
close(FORKSRV_FD
+
1
);
return
;
}
}
else
{
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [分享]出一点自己暂时不用的书 4746
- 玩ChatGPT有感 15725
- [注意] 2022看雪二进制漏洞小组成立通告 33233
- [分享]我和看雪的故事 -- by 有毒 31535
- [原创]多版本gcc/g++共存方案 23141