Skip to content

Commit 059fa1b

Browse files
authored
Fixing and customizing the enhancer prompt (#865)
In enhancer agent: 1. Teach LLM to write fuzz targets and build scripts in the same way as prototyper. Before this, it writes them within \`\`\`. 2. Make enhancer prompt independent and customizable. 3. More Flexible agent names in logs.
1 parent 63c707b commit 059fa1b

File tree

4 files changed

+169
-12
lines changed

4 files changed

+169
-12
lines changed

agent/enhancer.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616
"""
1717
import logger
1818
from agent.prototyper import Prototyper
19-
#from experiment.workdir import WorkDirs
20-
from llm_toolkit.prompt_builder import DefaultTemplateBuilder, JvmFixingBuilder
19+
from llm_toolkit.prompt_builder import EnhancerTemplateBuilder, JvmFixingBuilder
2120
from llm_toolkit.prompts import Prompt
22-
from results import AnalysisResult, Result
21+
from results import AnalysisResult, BuildResult, Result
2322

2423

2524
class Enhancer(Prototyper):
@@ -36,20 +35,29 @@ def _initial_prompt(self, results: list[Result]) -> Prompt:
3635
trial=self.trial)
3736
return Prompt()
3837

38+
last_build_result = None
39+
for result in results[::-1]:
40+
if isinstance(result, BuildResult):
41+
last_build_result = result
42+
break
43+
if not last_build_result:
44+
logger.error('Unable to find the last build result in Enhancer : %s',
45+
results,
46+
trial=self.trial)
47+
return Prompt()
48+
3949
if benchmark.language == 'jvm':
4050
# TODO: Do this in a separate agent for JVM coverage.
4151
builder = JvmFixingBuilder(self.llm, benchmark,
4252
last_result.run_result.fuzz_target_source, [])
4353
prompt = builder.build([], None, None)
4454
else:
4555
error_desc, errors = last_result.semantic_result.get_error_info()
46-
builder = DefaultTemplateBuilder(self.llm)
47-
prompt = builder.build_fixer_prompt(benchmark,
48-
last_result.fuzz_target_source,
49-
error_desc,
50-
errors,
51-
context='',
52-
instruction='')
56+
builder = EnhancerTemplateBuilder(self.llm, benchmark, last_build_result,
57+
error_desc, errors)
58+
prompt = builder.build(example_pair=[],
59+
tool_guides=self.inspect_tool.tutorial(),
60+
project_dir=self.inspect_tool.project_dir)
5361
# TODO: A different file name/dir.
5462
prompt.save(self.args.work_dirs.prompt)
5563

agent/prototyper.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,17 @@ def _generate_prompt_from_build_result(
217217
'Default /src/build.sh works perfectly, no need for a new '
218218
'buid script',
219219
trial=build_result.trial)
220-
logger.info('***** Prototyper succeded in %02d rounds *****',
220+
logger.info('***** %s succeeded in %02d rounds *****',
221+
self.name,
221222
cur_round,
222223
trial=build_result.trial)
223224
return build_result_alt, None
224225

225226
if build_result_ori and build_result_ori.success:
226227
# Preference 2: New fuzz target + new build.sh can compile, save
227228
# binary to expected path, and reference function-under-test.
228-
logger.info('***** Prototyper succeded in %02d rounds *****',
229+
logger.info('***** %s succeeded in %02d rounds *****',
230+
self.name,
229231
cur_round,
230232
trial=build_result.trial)
231233
return build_result_ori, None

llm_toolkit/prompt_builder.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,59 @@ def build(self,
643643
return self._prompt
644644

645645

646+
class EnhancerTemplateBuilder(PrototyperTemplateBuilder):
647+
"""Builder specifically targeted C (and excluding C++)."""
648+
649+
def __init__(self,
650+
model: models.LLM,
651+
benchmark: Benchmark,
652+
build_result: BuildResult,
653+
error_desc: str,
654+
errors: list[str],
655+
template_dir: str = DEFAULT_TEMPLATE_DIR,
656+
initial: Any = None):
657+
super().__init__(model, benchmark, template_dir, initial)
658+
# Load templates.
659+
self.priming_template_file = self._find_template(self.agent_templare_dir,
660+
'enhancer-priming.txt')
661+
self.build_result = build_result
662+
self.error_desc = error_desc
663+
self.errors = errors
664+
665+
def build(self,
666+
example_pair: list[list[str]],
667+
project_example_content: Optional[list[list[str]]] = None,
668+
project_context_content: Optional[dict] = None,
669+
tool_guides: str = '',
670+
project_dir: str = '') -> prompts.Prompt:
671+
"""Constructs a prompt using the templates in |self| and saves it."""
672+
del (example_pair, project_example_content, project_context_content)
673+
if not self.benchmark:
674+
return self._prompt
675+
676+
priming = self._get_template(self.priming_template_file)
677+
priming = priming.replace('{LANGUAGE}', self.benchmark.file_type.value)
678+
priming = priming.replace('{FUNCTION_SIGNATURE}',
679+
self.benchmark.function_signature)
680+
# TODO(dongge): Add build script to .
681+
priming = priming.replace('{PROJECT_DIR}', project_dir)
682+
if self.build_result.build_script_source:
683+
build_text = (f'<build script>\n{self.build_result.build_script_source}\n'
684+
'</build script>')
685+
else:
686+
build_text = 'Build script reuses `/src/build.bk.sh`.'
687+
priming = priming.replace('{BUILD_TEXT}', build_text)
688+
priming = priming.replace('{TOOL_GUIDES}', tool_guides)
689+
priming_weight = self._model.estimate_token_num(priming)
690+
691+
problem = self._format_fixer_problem(self.build_result.fuzz_target_source,
692+
self.error_desc, self.errors,
693+
priming_weight, '', '')
694+
695+
self._prepare_prompt(priming, problem)
696+
return self._prompt
697+
698+
646699
class DefaultJvmTemplateBuilder(PromptBuilder):
647700
"""Default builder for JVM projects."""
648701

prompts/agent/enhancer-priming.txt

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<system>
2+
As a security testing engineer, you must refine the following compilable {LANGUAGE} fuzz target to make it more suitable for fuzzing function {FUNCTION_SIGNATURE}, based on its current runtime errors description.
3+
Objective: Your task is to understand the runtime error, and refine the fuzz target (and build script if needed) accordingly. Note that the fuzz target can already compile.
4+
</system>
5+
6+
<steps>
7+
Follow these steps to refine the fuzz target:
8+
9+
Step 1. Determine the information you need to understand the runtime error of the fuzz target.
10+
This includes:
11+
* The existing compilable fuzz target provided below.
12+
* The existing build script provided below.
13+
* **Source code** of the function under test.
14+
* **Custom Types and Dependencies** definitions and implementations.
15+
* **Initialization and setup** requirements and steps.
16+
* **Build details** and integration steps.
17+
* Valid and edge-case input values.
18+
* Environmental and runtime dependencies.
19+
20+
Step 2. Collect information using the Bash tool.
21+
Use the bash tool (see <tool> section) and follow its rules to gather the necessary information. You can collect information from:
22+
* The project source code directory `{PROJECT_DIR}/` cloned from the project repository.
23+
* Documentation about the project, the function, and the variables/constants involved.
24+
* Environment variables.
25+
* Knowledge about OSS-Fuzz's build infrastructure: It will compile your fuzz target in the same way as the exiting human written fuzz target with the build script.
26+
27+
Step 3. Analyze the function and its parameters.
28+
Understand the function under test by analyzing its source code and documentation:
29+
* **Purpose and functionality** of the function.
30+
* **Input processing** and internal logic.
31+
* **Dependencies** on other functions or global variables.
32+
* **Error handling** and edge cases.
33+
34+
Step 4. Understand initialization requirements.
35+
Identify what is needed to properly initialize the function:
36+
* **Header files** and their relative paths used by include statements in the fuzz target.
37+
* **Complex input parameters or objects** initialization.
38+
* **Constructor functions** or initialization routines.
39+
* **Global state** or configuration needs to be set up.
40+
* **Mocking** external dependencies if necessary.
41+
42+
Step 5. Understand Constraints and edge cases.
43+
For each input parameter, understand:
44+
* Valid ranges and data types.
45+
* Invalid or edge-case values (e.g., zero, NULL, predefined constants, maximum values).
46+
* Special values that trigger different code paths.
47+
48+
Step 6: Plan Fuzz Target Implementation.
49+
Decide how to implement the refined fuzz target:
50+
* The fuzz target can compile so your can reuse most of the code as a scaffold.
51+
* Only modify the parts caused the runtime error, no more no less.
52+
* Prepare to output the FULL new fuzz target, do not leave out any code that is the same as before.
53+
* **Extract parameters** from the `data` and `size` variable of `LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)`.
54+
* Handle fixed-size versus variable-size data.
55+
* **Initialize function's parameters** by appropriately mapping the raw input bytes.
56+
* Ensure that the fuzz target remains deterministic and avoids side effects.
57+
* Avoid `goto` statements.
58+
59+
*
60+
Step 7 (Optional): **Modify** the Build Script.
61+
Modify the build script only if the existing one in this prompt is insufficient:
62+
* Decide if you need to modify the build script to successfully build the refined fuzz target.
63+
* If the build script needs to be modified, prepare to output the FULL new build script, do not leave out any code that is the same as before.
64+
* Leave it empty if no modification is needed.
65+
66+
Step 9: Providing Your Conclusion:
67+
* Provide your conclusion on the FULL new fuzz target and build script **ONLY AFTER** you have gathered all necessary information.
68+
* **DO NOT SEND** any other content (e.g., bash tool commands) in the conclusion message. ALWAYS send other commands individually and ONLY SEND conclusion after collecting all information.
69+
* Conclusion Format:
70+
* Overall Description:
71+
* Summarize the error, the root cause your found, and describe your fuzz target refinement.
72+
* Wrap this summary within <conclusion> and </conclusion> tags.
73+
* Modified Fuzz Target:
74+
* Provide the full code of the refined fuzz target.
75+
* Wrap the code within <fuzz target> and </fuzz target> tags.
76+
* Modified Build Script (if applicable):
77+
* If you need to modify the build script, provide the full code.
78+
* Wrap it within <build script> and </build script> tags.
79+
* Format Example:
80+
<conclusion>
81+
The fuzz target has runtime error ___, which is caused by ___.
82+
I will refined it by ___.
83+
Additionally, the build script requires modification to link against the necessary libraries.
84+
</conclusion>
85+
<fuzz target>
86+
[Your FULL fuzz target code here, do not omit existing code]
87+
</fuzz target>
88+
<build script>
89+
[Your FULL build script code here, do not omit existing code.]
90+
</build script>
91+
92+
</steps>
93+
94+
{TOOL_GUIDES}

0 commit comments

Comments
 (0)