Fuzz4All

难得睡个好觉

Fuzz4All

fuzz.py

命令行参数

  • config:配置文件路径,在config路径下
  • folder:结果存储路径,在outputs路径下
  • cpu:字面意思
  • batch_size:字面意思
  • model_name:使用的模型名称
  • target:目标编译器路径
    • gcc
    • g++
    • go
    • javac
    • cvc5:一个约束求解器
    • qiskit:量子计算

配置文件参数

  • fuzzing:整个fuzz流程的配置

    • target_name:测试目标路径,可由–target指定
    • output_folder:结果文件夹,可由–folder指定
    • num:fuzz迭代轮数
    • total_time:总计fuzz时间,单位hour
    • log_level:
      • 1:info
      • 2:trace
      • 3:verbose
    • otf:即时验证,在每次生成后验证output
    • resume:是否是恢复模式
    • evaluate:是否是评测模式
    • use_hand_written_prompt:是否使用hand written prompt
    • no_input_prompt:不需要输入prompt
    • prompt_strategy:prompt策略
      • 0:只使用trigger_to_generate_input进行生成
      • 1:变异已有代码
      • 2:语义等价已有代码
      • 3:结合前两个生成的代码
  • target:目标编译器配置

    • language:语言

    • path_documentation:目标语言特性文档路径

    • path_example_code:目标语言特性示例代码

    • trigger_to_generate_input:例子:

      1
      "/* Please create a short program which uses new C features in a complex way */"
    • input_hint:例子:

      1
      "#include <stdlib.h>"
    • path_hand_written_prompt:一些写好的prompt

    • target_string:需要测试的目标名称

  • llm:模型配置

    • temperature:模型的temperature(选择时的冒险程度或稳定程度)
    • batch_size:字面意思,可用–batch_size指定
    • device:字面意思,可用–cpu指定为cpu
    • model_name:模型名,可用–model_name指定
    • max_length:output的最大长度

fuzz loop主要流程

  • 生成初始prompt

    • temperature=0,gpt生成greedy_prompt,config:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      config = {
      "model": "gpt-3.5-turbo",
      "max_token": 500,
      "temperature": 0.0, # risk
      "message": [
      {"role": "system", "content": self.AP_SYSTEM_MESSAGE},
      {"role": "user", "content": message + '\n' + self.AP_INSTRUCTION} # message是文档
      ]
      }
    • 验证打分

    • 重复三次生成:

      • temperature=1,gpt生成prompt,config同上
      • 验证打分

    选择分数最高的prompt作为initial_prompt

  • 进入loop:

    • generate
    • 选择变异策略,更新prompt
      • 选择最后一个合法的output作为new_code
      • 三种变异策略:
        • new_code+separator
        • 变异new_code
        • 语法等价new_code
        • 结合new_code和prev_example

cli

–config加载配置文件

main_with_config

  • 解析配置文件

  • 调用make_target_with_config创建Target实例

  • 如果不是evaluate模式,则开始fuzz

    • 如果不是resume模式则新建工作文件夹
    1
    2
    3
    4
    5
    6
    7
    8
    fuzz(
    target=target,
    number_of_iterations=fuzzing["num"],
    total_time=fuzzing["total_time"],
    output_folder=folder,
    resume=fuzzing["resume"],
    otf=fuzzing["otf"],
    )
  • 如果是evaluate模式则调用evaluate_all进行evaluate

    1
    evaluate_all(target)

    就是分别验证(编译或运行)每个output

fuzz

  • initialize target

    1
    target.initialize()

    initialize见target/target.py

  • 进入fuzz loop,迭代次数number_of_iterations

    • 使用self.prompt生成outputs

    • 对于每个output:

      • 写入文件
      • 如果设置了otf,则:
      • 调用validate_individual验证output(编译/运行)
      • 调用parse_validation_message解析验证结果信息,记录进log
    • 调用update更新target

      update见target/target.py

make_model.py

make_model

  • kwargs_for_model:参数

    • model_name
    • eos
    • device
    • max_length
  • 创建StarCoder实例

    1
    model_obj = StarCoder(**kwargs_for_model)
  • 返回model_obj

输出:

1
2
3
4
5
6
7
8
=== Model Config ===
model_name: bigcode/starcoderbase-1b
model_name: bigcode/starcoderbase-1b
eos: ['/* Please create a short program which uses new C features in a complex way */', '<eom>', '/* Please create a semantically equivalent program to the previous generation */', '/* Please create a mutated program that modifies the previous generation */', '/* Please combine the two previous programs into a single program */']
device: cuda
max_length: 1024
model_obj (class name): StarCoder
====================

class StarCoder

__init__

model_name,device,eos,max_length

  • device

  • tokenizer

  • model

  • eos:eos + EOF_STRINGS

    1
    EOF_STRINGS = ["<|endoftext|>", "###"]
  • prefix_token:

    1
    <fim_prefix>
  • suffix_token:

    1
    <fim_suffix><fim_middle>
  • skip_special_tokens:False

generate

prompt生成代码

  • inference_mode关闭一堆东西提高速度

    1
    @torch.inference_mode()
  • 给prompt添加前后缀

  • 用tokenizer获取input_tokens

  • EndOfFunctionCriteria用于实时检测生成是否已结束(出现eos),是则记录去除eos后的输出长度

    1
    2
    3
    4
    5
    6
    7
    8
    9
    scores = StoppingCriteriaList(
    [
    EndOfFunctionCriteria(
    start_length=len(input_tokens[0]),
    eos=self.eos,
    tokenizer=self.tokenizer,
    )
    ]
    )
  • 生成

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    raw_outputs = self.model.generate(
    input_tokens,
    max_length=min(self.max_length, len(input_tokens[0]) + max_length),
    do_sample=True,
    top_p=1.0,
    temperature=max(temperature, 1e-2),
    num_return_sequences=batch_size,
    stopping_criteria=scores,
    output_scores=True,
    return_dict_in_generate=True,
    repetition_penalty=1.0,
    pad_token_id=self.tokenizer.eos_token_id,
    )
    • do_sample:是否在生成文本时使用采样策略,如果设置为False,则使用贪心策略进行解码(总是选择概率最高的下一个token)
    • top_p:保留累计概率达到p的最小集合,然后从这个集合中进行选择
    • num_return_sequences:设置生成几个文本
    • repetition_penalty:防止文本重复
    • stopping_criteria:停止条件
    • output_scores:让模型同时返回输出和对应的分数
    • return_dict_in_generate:方法会返回一个字典,其中包含生成的文本和其他相关信息,如attention masks等
  • 去除每个输出的prompt部分

  • 去除每个输出的eos

make_target.py

make_target_with_config

  • target_compat_dict:一个压缩过的config_dict

    • language
    • folder:output_folder
    • bs:batch_size
    • temperature
    • device
    • model_name
    • max_length
    • use_hw:use_hand_written_prompt
    • no_input_prompt
    • prompt_strategy
    • level:log_level
    • template:fuzzing_with_config_file
    • config_dict:完整的config_dict,解析自配置文件
    • target_name
  • 根据语言创建对应Target实例,并返回Target实例:

    • cpp:CPPTarget
    • c:CTarget
    • qiskit:QiskitTarget
    • smt2:SMTTarget
    • go:GOTarget
    • java:JAVATarget

    Target见target/target.py,子类见子文件夹

target

target.py

class Target

参数是make_target_with_config中的target_compat_dict

__init__

language,timeout,folder,

  • language

  • timeout

  • folder

  • CURRENT_TIME:获取现在时间

  • batch_size

  • temperature

  • max_length

  • device

  • model_name

  • model:None

  • g_logger:结果目录下的log_generation.txt文件,格式:

    1
    f"[{level.name}] {msg}"

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [VERBOSE] #include <stdlib.h>

    #include <stdio.h>
    #include <inttypes.h>


    int main(void) {
    unsigned i = 1l;
    #ifdef UINTPTR_MAX
    printf("Unsigned integers are %zu bytes.\n", sizeof(i));
    #else
    printf("Unsigned integers are %zd bytes.\n", sizeof(i)*CHAR_BIT);
    #endif
    i++;
    printf("unsigned i is now %zd bytes long.\n", sizeof(i))*CHAR_BIT;
    printf("unsigned i is now %" PRIu64 "\n", i);
    return 0;
    }
  • v_logger:结果目录下的log_validation.txt文件,例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [TRACE] Validating outputs/full_run/gcc/0.fuzz ...
    [VERBOSE] outputs/full_run/gcc/0.fuzz failed validation with error message: outputs/full_run/gcc/0.fuzz: In function main:
    outputs/full_run/gcc/0.fuzz:15:62: error: CHAR_BIT undeclared (first use in this function)
    15 | printf("unsigned i is now %zd bytes long.\n", sizeof(i))*CHAR_BIT;
    | ^~~~~~~~
    outputs/full_run/gcc/0.fuzz:5:1: note: CHAR_BIT is defined in header <limits.h>; did you forget to #include <limits.h>?
    4 | #include <inttypes.h>
    +++ |+#include <limits.h>
    5 |
    outputs/full_run/gcc/0.fuzz:15:62: note: each undeclared identifier is reported only once for each function it appears in
    15 | printf("unsigned i is now %zd bytes long.\n", sizeof(i))*CHAR_BIT;
    | ^~~~~~~~
  • m_logger:结果目录下的log.txt文件,例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [INFO] Loading model ...
    [INFO] Initializing ... this may take a while ...
    [INFO] Loading model ...
    [INFO] Model Loaded
    [INFO] Use auto-prompting prompt ...
    [INFO] Initializing ... this may take a while ...
    [INFO] Loading model ...
    [INFO] Model Loaded
    [INFO] Use auto-prompting prompt ...
    [INFO] Done
  • SYSTEM_MESSAGE:子类设置

  • AP_SYSTEM_MESSAGE:系统提示prompt

    1
    "You are an auto-prompting tool"
  • AP_INSTRUCTION:生成命令prompt

    1
    2
    "Please summarize the above documentation in a concise manner to describe the usage and "
    "functionality of the target "
  • hw:use_hand_written_prompt

  • no_input_prompt

  • prompt_used:子类设置

  • prompt:None

  • initial_prompt:None

  • prev_example:None

  • se_prompt:语法等价替换变异策略的prompt

    1
    2
    3
    4
    self.se_prompt = self.wrap_in_comment(
    "Please create a semantically equivalent program to the previous "
    "generation"
    )
  • m_prompt:变异之前的代码变异策略的prompt

    1
    2
    3
    self.m_prompt = self.wrap_in_comment(
    "Please create a mutated program that modifies the previous generation"
    )
  • c_prompt:结合之前两段代码变异策略的prompt

    1
    2
    3
    self.c_prompt = self.wrap_in_comment(
    "Please combine the two previous programs into a single program"
    )
  • p_strategy:prompt_strategy

  • special_eos:None

  • target_name:target

_create_prompt_from_config

从config获取各种prompt

  • target:配置文件的target,提供

    • path_documentation
    • path_example_code
    • trigger_to_generate_input
    • input_hint
    • path_hand_written_prompt
    • target_string
  • trigger_to_generate_input:见配置文件

  • documentation:读取自path_documentation

  • example_code:读取自path_example_code

  • input_hint:见配置文件

  • hand_written_prompt:读取自path_hand_written_prompt

  • target_string:见配置文件

  • dict_compat:

    1
    2
    3
    4
    5
    6
    7
    8
    dict_compat = {
    "docstring": documentation,
    "example_code": example_code,
    "separator": trigger_to_generate_input,
    "begin": input_hint,
    "hw_prompt": hand_written_prompt,
    "target_api": target_string,
    }

返回dict_compat

validate_prompt

给prompt打分

  • 调用generate使用prompt生成

    1
    2
    3
    4
    5
    6
    fos = self.model.generate(
    prompt,
    batch_size=self.batch_size,
    temperature=self.temperature,
    max_length=self.max_length,
    )

    generate见model.py

  • 对于每个output:

    • 补充begin,为了编译或运行之后会去掉

    • 将output写回文件

    • 调用validate_individual验证输出,就是编译或运行

    • 如果output:

      • 验证为SAFE
      • 包含目标api,filter清除begin
      • clean_code清除注释和空行后不在set里

      则放入set,score+1

  • 返回score

auto_prompt

生成初始prompt

  • 工作路径下创建prompt文件夹

  • 如果已经有prompt,则直接使用

  • 如果设置了no_input_prompt,则直接使用prompt_used中的separator和begin作为best_prompt

  • 如果设置了use_hand_written_prompt,则使用hand written prompt

  • 否则自动生成prompt

  • 调用create_config生成config,temperature为0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    config = {
    "model": "gpt-3.5-turbo",
    "max_token": 500,
    "temperature": 0.0, # risk
    "message": [
    {"role": "system", "content": self.AP_SYSTEM_MESSAGE},
    {"role": "user", "content": message + '\n' + self.AP_INSTRUCTION} # message是文档
    ]
    }
  • 调用request_engine获取gpt的response

    1
    response = request_engine(config)
  • 选取第一个message作为greedy_prompt

  • best_prompt初始化为greedy_prompt,调用validate_prompt给greedy_prompt打分,score表示有效输出的个数,具体为包含目标api的、不重复的output数量

  • 重复三次request_engine(temperature为1)和validate_prompt选取分数最高的prompt作为best_prompt

initialize

target初始化,加载模型,初始化prompt

  • 初始化一个eos列表,这些prompt根据需要作为语句结尾

    1
    2
    3
    4
    5
    6
    7
    [
    trigger_to_generate_input,
    "<eom>", # for codegen2
    se_prompt,
    m_prompt,
    c_prompt
    ]
  • 添加special_eos,go和smt有这个

  • 调用make_model加载模型

    1
    2
    3
    4
    5
    6
    self.model = make_model(
    eos=eos,
    model_name=model_name,
    device=self.device,
    max_length=self.max_length,
    )

    make_model见model.py

  • 调用auto_prompt获取initial_prompt

    1
    2
    3
    4
    5
    6
    self.initial_prompt = self.auto_prompt(
    message=self.prompt_used["docstring"],
    hw_prompt=self.prompt_used["hw_prompt"] if self.hw else None,
    hw=self.hw,
    no_input_prompt=self.no_input_prompt,
    )

    参数:

    • message:文档
    • hw_prompt:一些写好的prompt
    • hw:是否使用hand written prompt
    • no_input_prompt:不需要输入prompt
update_strategy

选择一个变异策略,生成新的prompt

  • 0:new_code+trigger_to_generate_input

    1
    f"\n{new_code}\n{self.prompt_used['separator']}\n"
  • 1:变异new_code

    1
    f"\n{new_code}\n{self.m_prompt}\n"
  • 2:语法等价new_code

    1
    f"\n{new_code}\n{self.se_prompt}\n"
  • 3:结合前两个生成的代码:

    • prev_example
    • trigger_to_generate_input
    • input_hint
    • new_code
    • combine prompt
    1
    f"\n{self.prev_example}\n{self.prompt_used['separator']}\n{self.prompt_used['begin']}\n{new_code}\n{self.c_prompt}\n"
update

每轮fuzz loop后更新target

  • 选择最后一个filter合法、clean_code后不等于prev_example的code

  • 更新prompt为:

    1
    2
    3
    4
    5
    6
    self.prompt = (
    self.initial_prompt
    + self.update_strategy(new_code)
    + self.prompt_used["begin"]
    + "\n"
    )
  • 更新prev_example为code

C.py

class CTarget

__init__
  • SYSTEM_MESSAGE:

    1
    "You are a C Fuzzer"
  • config_dict

  • prompt_used:调用_create_prompt_from_config生成,包括

    • docstring:文档
    • example_code:示例代码
    • separator:trigger_to_generate_input,见配置文件
    • begin:input_hint,见配置文件
    • hw_prompt:写好的prompt
    • target_api:测试目标

    _create_prompt_from_config见target/target.py

wrap_prompt

prompt打包

1
2
3
/* prompt */
trigger_to_generate_input
input_hint

示例:

1
2
3
/* prompt */
/* Please create a short program which uses new C features in a complex way */
#include <stdlib.h>

Fuzz4All
http://akaieurus.github.io/2025/01/17/fuzz4all/
作者
Eurus
发布于
2025年1月17日
许可协议