corpus

更新时间:2022-12-26 21:06:05 阅读: 评论:0


2022年12月26日发(作者:英语国际音标发音视频)

libFuzzer

⽬录

1.概述

LibFuzzer是单进程的,覆盖引导的,进化的模糊引擎。

LibFuzzer与被测试的库链接,并通过特定的模糊⼊⼝点(也称为“⽬标函数”)将模糊输⼊提供给库;然后,模糊器跟踪到达代码的哪些

区域,并在输⼊数据的语料库中⽣成突变,以便最⼤化代码覆盖。libFuzzer的代码覆盖率信息由LLVM的SanitizerCoverage检测提供

2.版本

LibFuzzer正在积极开发中,因此您将需要Clang编译器的当前(或⾄少是最新版本)(请参阅从主⼲构建Clang)

rget

在库上使⽤libFuzzer的第⼀步是实现⼀个模糊⽬标-⼀个接受字节数组的函数,并使⽤被测API对这些字节做⼀些有趣的事情。像这样:

//fuzz_

extern"C"intLLVMFuzzerTestOneInput(constuint8_t*Data,size_tSize){

DoSomethingInterestingWithMyAPI(Data,Size);

return0;//Non-zeroreturnvaluesarerervedforfutureu.

}

请注意,这种fuzz⽬标不以任何⽅式依赖于libfuzzer,因此可能甚⾄需要将其与其他fuzz引擎(如AFL和/或RADAMSA)⼀起使⽤。

关于fuzz⽬标,需要记住的⼀些重要事项:

在同⼀过程中,fuzz引擎会多次执⾏不同输⼊的fuzz⽬标。

它必须允许任何类型的输⼊(空、⼤、畸形等)。

它不能在任何输⼊上exit()。

它可以使⽤线程,但理想情况下,所有线程都应该在函数的末尾连接。

它必须尽可能具有确定性。不确定性(例如不基于输⼊字节的随机决策)会使模糊效率低下。

⼀定很快。尝试避免⽴⽅或更⼤的复杂性、⽇志记录或过多的内存消耗。

理想情况下,它不应该修改任何全局状态(尽管这并不严格)。

通常,⽬标越窄越好。例如。如果您的⽬标可以解析多种数据格式,请将其拆分为多个⽬标,每种格式⼀个。

Usage

最新版本的Clang(从6.0开始)包括libFuzzer,⽆需额外安装。

要构建模糊化⼆进制⽂件,请在编译和链接期间使⽤-fsanitize=fuzzer标志。在⼤多数情况下,您可能希望将libFuzzer与

AddressSanitizer(ASAN),UndefinedBehaviorSanitizer(UBSAN)或两者结合使⽤。您也可以使⽤

MemorySanitizer(MSAN)构建,但⽀持是实验性的:

clang-g-O1-fsanitize=fuzzermytarget.c#Buildsthefuzztargetw/osanitizers

clang-g-O1-fsanitize=fuzzer,addressmytarget.c#BuildsthefuzztargetwithASAN

clang-g-O1-fsanitize=fuzzer,signed-integer-overflowmytarget.c#BuildsthefuzztargetwithapartofUBSAN

clang-g-O1-fsanitize=fuzzer,memorymytarget.c#BuildsthefuzztargetwithMSAN

这将执⾏必要的检测,以及与libFuzzer库的链接。请注意,libFuzzer的main()符号中的-fsanitize=fuzzer链接。

如果修改⼤型项⽬的CFLAGS,它也编译需要⾃⼰的主符号的可执⾏⽂件,则可能需要在不链接的情况下仅请求检测:

clang-fsanitize=fuzzer-no-linkmytarget.c

然后通过在链接阶段传⼊-fsanitize=fuzzer,可以将libFuzzer链接到所需的驱动程序。

像libFuzzer这样的覆盖引导模糊器依赖于被测代码的样本输⼊语料库。理想情况下,该语料库应该为被测代码提供各种有效和⽆效的输⼊;

例如,对于图形库,初始语料库可能包含各种不同的⼩PNG/JPG/GIF⽂件。模糊器基于当前语料库中的样本输⼊⽣成随机突变。如果突

变触发了测试代码中先前未覆盖的路径的执⾏,则该突变将保存到语料库中以供将来变更。

LibFuzzer将在没有任何初始种⼦的情况下⼯作,但如果受测试的库接受复杂的结构化输⼊,则效率会降低。

语料库还可以充当sanity/regression检查,以确认模糊测试⼊⼝点仍然有效,并且所有样本输⼊都通过测试中的代码运⾏⽽没有问题。

如果您有⼤型语料库(通过模糊测试⽣成或通过其他⽅式获取),您可能希望在保留完整覆盖范围的同时将其最⼩化。⼀种⽅法是使⽤-

merge=1标志:

mkdirNEW_CORPUS_DIR#Storeminimizedcorpushere.

./my_fuzzer-merge=1NEW_CORPUS_DIRFULL_CORPUS_DIR

您可以使⽤相同的标志向现有语料库添加更多有趣的项⽬。只有触发新覆盖的输⼊才会添加到第⼀个语料库中。

./my_fuzzer-merge=1CURRENT_CORPUS_DIRNEW_POTENTIALLY_INTERESTING_INPUTS_DIR

g

Torunthefuzzer,firstcreateadirectorythatholdstheinitial“ed”sampleinputs:

mkdirCORPUS_DIR

cp/some/input/samples/*CORPUS_DIR

然后在语料库⽬录上运⾏模糊器:

./my_fuzzerCORPUS_DIR#-max_len=1000-jobs=20...

当模糊器发现新的有趣测试⽤例(即通过被测代码触发新路径覆盖的测试⽤例)时,这些测试⽤例将被添加到语料库⽬录中。

默认情况下,模糊测试过程将⽆限期地继续-⾄少在发现错误之前。任何crashes或sanitizer故障都将照常报告,停⽌模糊测试过程,触

发错误的特定输⼊将写⼊磁盘(通常为crash-,leak-或timeout-)。

elFuzzing

每个libFuzzer进程都是单线程的,除⾮被测试的库启动⾃⼰的线程。但是,可以与共享语料库⽬录并⾏运⾏多个libFuzzer进程;这样做的

好处是,⼀个模糊进程找到的任何新输⼊都可⽤于其他模糊进程(除⾮使⽤-reload=0选项禁⽤此选项)。

这主要由-jobs=N选项控制,该选项指⽰N个模糊作业应该运⾏完成(即直到找到错误或达到时间/迭代限制)。这些作业将在⼀组⼯作进

程中运⾏,默认情况下使⽤⼀半的可⽤CPU核⼼;-workers=N选项可以覆盖⼯作进程的计数。例如,在12核计算机上使⽤-jobs=30运

⾏默认情况下将运⾏6个⼯作程序,每个⼯作程序在完成整个过程时平均会有5个错误。

de

实验模式-fork=N(其中N是并⾏作业的数量)使⽤单独的进程(使⽤fork-exec,⽽不仅仅是fork)启⽤oom-,timeout-和crash-

resistant-fuzzing。

顶级libFuzzer进程本⾝不会进⾏任何模糊测试,但会产⽣多达N个并发⼦进程,为它们提供语料库的⼩型随机⼦集。在孩⼦退出之后,顶

级过程将孩⼦⽣成的语料库合并回主语料库。

相关标志:

-ignore_ooms

默认为True。如果在其中⼀个⼦进程的模糊测试期间发⽣OOM,则复制器将保存在磁盘上,并继续进⾏模糊测试。

-ignore_timeouts

默认情况下为True,与-ignore_ooms相同,但是超时。

-ignore_crashes

默认情况下为Fal,与-ignore_ooms相同,但对于所有其他崩溃。

计划是最终⽤-fork=N替换-jobs=N和-workers=N.

ngmerge

合并⼤型语料库可能⾮常耗时,并且通常希望在可抢占的VM上执⾏此操作,其中该过程可能在任何时间被杀死。为了⽆缝地恢复合并,请

使⽤-merge_control_file标志并使⽤killall-SIGUSR1/path/to/fuzzer/binary来正常停⽌合并。例:

%rm-fSomeLocalPath

%./my_fuzzerCORPUS1CORPUS2-merge=1-merge_control_file=SomeLocalPath

...

MERGE-INNER:usingthecontrolfile'SomeLocalPath'

...

#Whilethisisrunning,do`killall-SIGUSR1my_fuzzer`inanotherconsole

==9015==INFO:libFuzzer:exitingasrequested

#ThiswillleavethefileSomeLocalPathwiththepartialstateofthemerge.

#Now,ge

#willcontinuefromwhereithasbeeninterrupted.

%./my_fuzzerCORPUS1CORPUS2-merge=1-merge_control_file=SomeLocalPath

...

MERGE-OUTER:non-emptycontrolfileprovided:'SomeLocalPath'

MERGE-OUTER:controlfileok,32filestotal,firstnotprocesdfile20

...

s

要运⾏模糊器,请将零个或多个语料库⽬录作为命令⾏参数传递。模糊器将读取每个语料库⽬录中的测试输⼊,并且⽣成的任何新测试输

⼊将被写回第⼀个语料库⽬录:

./fuzzer[-flag1=val1[-flag2=val2...]][dir1[dir2...]]

最重要的命令⾏选项是:

-help

打印帮助信息。

-ed

随机种⼦。如果为0(默认值),则⽣成种⼦。

-runs

单个测试运⾏的次数,-1(默认值)⽆限期运⾏。

-max_len

测试输⼊的最⼤长度。如果为0(默认值),则libFuzzer会尝试根据语料库猜测⼀个好的值(并报告它)。

len_control

⾸先尝试⽣成⼩输⼊,然后尝试更⼤的输⼊。指定长度限制增加的速率(更⼩==更快)。默认值为100.如果为0,则⽴即尝试输⼊⼤⼩为

max_len的输⼊。

-timeout

超时(以秒为单位),默认为1200.如果输⼊的时间超过此超时,则将该过程视为故障情况。

-rss_limit_mb

内存使⽤限制,单位为Mb,默认为2048.使⽤0禁⽤该限制。如果输⼊需要执⾏超过此数量的RSS内存,则该过程将被视为失败案例。每

秒在⼀个单独的线程中检查限制。如果运⾏没有ASAN/MSAN,您可以使⽤'ulimit-v'代替。

-malloc_limit_mb

如果⾮零,如果⽬标尝试使⽤⼀个malloc调⽤分配此数量的Mb,则模糊器将退出。如果应⽤零(默认)相同的限制,则应⽤

rss_limit_mb。

-timeout_exitcode

如果libFuzzer报告超时,则使⽤退出代码(默认为77)。

-error_exitcode

如果libFuzzer本⾝(不是清理程序)报告错误(泄漏,OOM等),则使⽤退出代码(默认为77)。

-max_total_time

如果为正,则表⽰运⾏模糊器的最长总时间(以秒为单位)。如果为0(默认值),则⽆限期运⾏。

-merge

如果设置为1,则触发新代码覆盖的第2,第3等语料库⽬录中的任何语料库输⼊将合并到第⼀个语料库⽬录中。默认为0.此标志可⽤于最⼩

化语料库。

-merge_control_file

指定⽤于合并进程的控制⽂件。如果合并进程被杀死,它会尝试将此⽂件保留在适合恢复合并的状态。默认情况下,将使⽤临时⽂件。

-minimize_crash

如果为1,则最⼩化提供的崩溃输⼊。与-runs=N或-max_total_time=N⼀起使⽤以限制尝试次数。

-reload

如果设置为1(默认值),则定期重新读取语料库⽬录以检查新输⼊;这允许检测由其他模糊测试过程发现的新输⼊。

-jobs

要运⾏完成的模糊测试作业的数量。默认值为0,运⾏单个模糊测试过程直到完成。如果值>=1,则在并⾏的单独⼯作进程的集合中运⾏执

⾏模糊测试的此数量的作业;每个这样的⼯作进程都将其stdout/stderr重定向到fuzz-.log。

-workers

运⾏模糊测试作业的同时⼯作进程数。如果为0(默认值),则使⽤min(jobs,NumberOfCpuCores()/2)。

-dict

提供输⼊关键字的字典;看字典。

-u_counters

使⽤覆盖计数器⽣成代码块被击中频率的近似计数;默认为1。

-reduce_inputs

尽量减少输⼊的⼤⼩,同时保留其完整的功能集;默认为1。

-u_value_profile

使⽤价值观来指导语料库的扩展;默认为0。

-only_ascii

如果为1,则仅⽣成ASCII(isprint``+``isspace)输⼊。默认为0。

-artifact_prefix

提供在将fuzzing⼯件(崩溃,超时或慢速输⼊)保存为$(artifact_prefix)⽂件时使⽤的前缀。默认为空。

-exact_artifact_path

如果为空则忽略(默认值)。如果⾮空,则将失败时写⼊的单个⼯件(崩溃,超时)写为$(exact_artifact_path)。这会覆盖-

artifact_prefix,并且不会在⽂件名中使⽤校验和。不要对多个并⾏进程使⽤相同的路径。

-print_pcs

如果为1,则打印出新覆盖的PC。默认为0。

-print_final_stats

如果为1,则退出时打印统计信息。默认为0。

-detect_leaks

如果为1(默认值)且启⽤了LeakSanitizer,则尝试在模糊测试期间(即不仅在关闭时)检测内存泄漏。

-clo_fd_mask

指⽰在启动时关闭的输出流。请注意,这将从⽬标代码中删除诊断输出(例如断⾔失败时的消息)。

0(默认值):既不关闭stdout也不关闭stderr

1:关闭stdout

2:关闭stderr

3:关闭stdout和stderr。

对于完整的标志列表,使⽤-help=1运⾏fuzzer⼆进制⽂件。

在操作期间,模糊器将信息打印到stderr,例如:

INFO:Seed:1523017872

INFO:Loaded1modules(16guards):[0x744e60,0x744ea0),

INFO:-max_lenisnotprovided,using64

INFO:Acorpusisnotprovided,startingfromanemptycorpus

#0READunits:1

#1INITEDcov:3ft:2corp:1/1bexec/s:0rss:24Mb

#3811NEWcov:4ft:3corp:2/2bexec/s:0rss:25MbL:1MS:5ChangeBit-ChangeByte-ChangeBit-ShuffleBytes-ChangeByte-

#3827NEWcov:5ft:4corp:3/4bexec/s:0rss:25MbL:2MS:1CopyPart-

#3963NEWcov:6ft:5corp:4/6bexec/s:0rss:25MbL:2MS:2ShuffleBytes-ChangeBit-

#4167NEWcov:7ft:6corp:5/9bexec/s:0rss:25MbL:3MS:1InrtByte-

...

输出的早期部分包括有关fuzzer选项和配置的信息,包括当前随机种⼦(在ed:line中;这可以⽤-ed=n标志覆盖)。

其他输出⾏具有事件代码和统计信息的形式。可能的事件代码为:

READ

Fuzzer已经从语料库⽬录中读取了所有提供的输⼊样本。

INITED

fuzzer已经完成初始化,包括通过被测代码运⾏每个初始输⼊样本。

NEW

fuzzer已经创建了⼀个测试输⼊,它覆盖了被测代码的新领域。此输⼊将保存到主语料库⽬录。

REDUCE

fuzzer发现了⼀个更好(更⼩)的输⼊,可以触发先前发现的特征(设置-reduce_inputs=0以禁⽤)。

pul

fuzzer已产⽣2N输⼊(定期产⽣,以保证⽤户fuzzer仍在⼯作)。

DONE

fuzzer已完成操作,因为它已达到指定的迭代限制(-runs)或时间限制(-max_total_time)。

RELOAD

fuzzer正在定期从corpus⽬录重新加载输⼊;这允许它发现由其他fuzzer进程发现的任何输⼊(请参见并⾏模糊)。

每个输出⾏还报告以下统计信息(⾮零时):

COV:

执⾏当前语料库所涵盖的代码块或边的总数。

FT:

libFuzzer使⽤不同的信号来评估代码覆盖率:边缘覆盖,边缘计数器,值配置⽂件,间接调⽤者/被调⽤者对等。这些组合的信号称为特征

(ft:)。

CORP:

当前内存中测试语料库中的条⽬数及其⼤⼩(以字节为单位)。

LIM:

语料库中新条⽬长度的当前限制。随着时间的推移逐渐增加,直到达到最⼤长度(-max_len)。

EXEC/S:

每秒的模糊器迭代次数。

RSS:

当前的内存消耗。

对于NEW事件,输出⾏还包括有关⽣成新输⼊的变异操作的信息:

L:

新输⼊的⼤⼩(以字节为单位)。

MS:<操作>

计数和⽤于⽣成输⼊的变异操作列表。

mple

⼀个简单的函数,如果收到输⼊“HI!”,它会做⼀些有趣的事情:

cat<test_

#include

#include

extern"C"intLLVMFuzzerTestOneInput(constuint8_t*data,size_tsize){

if(size>0&&data[0]=='H')

if(size>1&&data[1]=='I')

if(size>2&&data[2]=='!')

__builtin_trap();

return0;

}

EOF

#Buildtest_asanandlinkagainstlibFuzzer.

clang++-fsanitize=address,fuzzertest_

#Runthefuzzerwithnocorpus.

./

你应该很快得到⼀个错误

INFO:Seed:1523017872

INFO:Loaded1modules(16guards):[0x744e60,0x744ea0),

INFO:-max_lenisnotprovided,using64

INFO:Acorpusisnotprovided,startingfromanemptycorpus

#0READunits:1

#1INITEDcov:3ft:2corp:1/1bexec/s:0rss:24Mb

#3811NEWcov:4ft:3corp:2/2bexec/s:0rss:25MbL:1MS:5ChangeBit-ChangeByte-ChangeBit-ShuffleBytes-ChangeByte-

#3827NEWcov:5ft:4corp:3/4bexec/s:0rss:25MbL:2MS:1CopyPart-

#3963NEWcov:6ft:5corp:4/6bexec/s:0rss:25MbL:2MS:2ShuffleBytes-ChangeBit-

#4167NEWcov:7ft:6corp:5/9bexec/s:0rss:25MbL:3MS:1InrtByte-

==31511==ERROR:libFuzzer:deadlysignal

...

artifact_prefix='./';Testunitwrittento./crash-b13e8756b13a00cf1fb4b91fefbed

amples

naries

libfuzzer⽀持⽤户提供的带有输⼊语⾔关键字或其他有趣的字节序列(例如多字节magic值)的字典。使⽤-dict=DICTIONARY_FILE。对

于某些输⼊语⾔,使⽤字典可能会显著提⾼搜索速度。字典语法类似于AFL在其-x选项中使⽤的语法:

#Linesstartingwith'#'andemptylinesareignored.

#Adds"blah"(w/oquotes)tothedictionary.

kw1="blah"

#Uforbackslashand"forquotes.

kw2=""acdc""

#UxABforhexvalues

kw3="xF7xF8"

#thenameofthekeywordfollowedby'='maybeomitted:

"foox0Abar"

gCMPinstructions

使⽤额外的编译器标志-fsanitize-coverage=trace-cmp(默认情况下,作为-fsanitize=fuzzer的⼀部分,请参阅

SanitizerCoverageTraceDataFlow),libFuzzer将拦截CMP指令并根据截获的CMP指令的参数指导突变。这可能会减慢模糊测试速

度,但很可能会改善结果。

rofile

使⽤-fsanitizecoverage=trace-cmp(默认为-fsanitize=fuzzer)和额外的运⾏时标志-u_value_profile=1,fuzzer将收集⽐较指令

参数的值配置⽂件,并将⼀些新值视为新的覆盖。

⽬前的⼯作⼤致如下:

编译器使⽤接收两个CMP参数的回调来检测所有CMP指令。

回调计算(caller_pc&4095)/(popcnt(arg1^arg2)<<12)并使⽤该值在位集中设置⼀个位。

位集中的每个新观测位都被视为新的覆盖范围。

这个特性有可能发现许多有趣的输⼊,但有两个缺点。⾸先,额外的⽅法可能会带来2倍的额外减速。第⼆,语料库可能会增长⼏倍。

-friendlybuildmode

有时,测试中的代码不是模糊测试的。例⼦:

⽬标代码使⽤PRNG种⼦,例如:通过系统时间,因此即使最终结果相同,两个随后的调⽤也可能潜在地执⾏不同的代码路径。这将导致模

糊器将两个相似的输⼊视为显着不同,并且它会炸毁测试语料库。例如。libxml在其哈希表中使⽤rand()。

⽬标代码使⽤校验和来防⽌⽆效输⼊。例如。png检查每个块的CRC。

在许多情况下,构建⼀个特殊的模糊友好构建是有意义的,其中某些模糊不友好的功能被禁⽤。我们建议为所有这些情况使⽤公共构建宏

以保持⼀致性:FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION。

voidMyInitPRNG(){

#ifdefFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION

//Infuzzingmodethebehaviorofthecodeshouldbedeterministic.

srand(0);

#el

srand(time(0));

#endif

}

patibility

libfuzzer可以与afl⼀起在同⼀个测试语料库上使⽤。两个模糊器都期望测试语料库驻留在⼀个⽬录中,每个输⼊⼀个⽂件。你可以在同⼀

个语料库上运⾏两个fuzzer,⼀个接⼀个:

./afl-fuzz-itestca_dir-ofindings_dir/path/to/program@@

./llvm-fuzztestca_dirfindings_dir#Willwritenewteststotestca_dir

定期重新启动两个fuzzer,以便他们可以使⽤彼此的发现。⽬前,在共享同⼀个corpusdir时,没有简单的⽅法并⾏运⾏两个模糊引擎。

您也可以在⽬标函数llvmFuzzerteStoneInput上使⽤AFL:请参见下⾯的⽰例。

dismyfuzzer?

⼀旦实现了⽬标函数llvmfuzzertestoneinput并将其模糊到死亡,您将希望知道该函数或语料库是否可以进⼀步改进。当然,⼀个易于使⽤

的度量标准是代码覆盖率。

我们建议使⽤clang覆盖率来可视化和研究代码覆盖率(⽰例)。

-suppliedmu2tators

LibFuzzer允许使⽤⾃定义(⽤户提供的)突变,有关详细信息,请参阅Structure-AwareFuzzing。

pinitialization

如果需要初始化正在测试的库,则有⼏个选项。

最简单的⽅法是在LLVMFuzzerTestOneInput(或在全局范围内,如果它适⽤于您)中具有静态初始化的全局对象:

extern"C"intLLVMFuzzerTestOneInput(constuint8_t*Data,size_tSize){

staticboolInitialized=DoInitialization();

...

或者,您可以定义⼀个可选的init函数,它将接收您可以读取和修改的程序参数。只有在您确实需要访问argv/argc时才这样做。

extern"C"intLLVMFuzzerInitialize(int*argc,char***argv){

ReadAndMaybeModify(argc,argv);

return0;

}

使⽤AddressSanitizer或LeakSanitizer构建的⼆进制⽂件将尝试在进程关闭时检测内存泄漏。对于过程中模糊测试,这是不⽅便的,因为

⼀旦发现泄漏突变,模糊器需要⽤再现器报告泄漏。但是,在每次突变后运⾏完全泄漏检测是昂贵的。

默认情况下(-detect_leaks=1),libFuzzer将在执⾏每个突变时计算malloc和free调⽤的次数。如果数字不匹配(这本⾝并不意味着

存在泄漏),libFuzzer将调⽤更昂贵的LeakSanitizer传递,如果发现实际泄漏,它将与再现器⼀起报告,并且进程将退出。

如果您的⽬标有⼤量泄漏并且泄漏检测被禁⽤,则最终会耗尽RAM(请参阅-rss_limit_mb标志)。

pinglibFuzzer

默认情况下,LibFuzzer是作为LLVM项⽬的⼀部分构建在macos和Linux上的。其他操作系统的⽤户可以使⽤-DLIBFUZZER_ENABLE

=YES标志显式请求编译。使⽤来⾃构建⽬录的check-fuzzer⽬标运⾏测试,该⽬录使⽤-DLIBFUZZER_ENABLE_TESTS=ON标志进

⾏配置。

本文发布于:2022-12-26 21:06:05,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/36054.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:wwoof
下一篇:ablaze
标签:corpus
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图