0
# Code Instrumentation
1
2
Atheris provides multiple methods for adding coverage instrumentation to Python code. Instrumentation is essential for effective fuzzing as it provides feedback to the fuzzer about which code paths have been executed, enabling guided exploration of the program's behavior.
3
4
## Capabilities
5
6
### Import-Time Instrumentation
7
8
Automatically instrument Python modules as they are imported.
9
10
```python { .api }
11
def instrument_imports(include=None, exclude=None, enable_loader_override=True):
12
"""
13
Context manager that instruments Python modules imported within the context.
14
15
This is the recommended approach for most fuzzing scenarios as it automatically
16
instruments libraries without requiring manual intervention.
17
18
Args:
19
include (list, optional): List of fully-qualified module names to instrument.
20
If provided, only these modules will be instrumented.
21
exclude (list, optional): List of fully-qualified module names to exclude
22
from instrumentation.
23
enable_loader_override (bool): Whether to enable experimental feature of
24
instrumenting custom loaders (default: True).
25
26
Returns:
27
Context manager: Use with 'with' statement to enable instrumentation
28
"""
29
```
30
31
**Usage Examples:**
32
33
```python
34
import atheris
35
36
# Basic usage - instrument all imported modules
37
with atheris.instrument_imports():
38
import json
39
import urllib.parse
40
from xml.etree import ElementTree
41
42
def TestOneInput(data):
43
# All the imported modules are now instrumented
44
parsed = json.loads(data.decode('utf-8', errors='ignore'))
45
# ...
46
```
47
48
```python
49
# Selective instrumentation
50
with atheris.instrument_imports(include=['mypackage', 'mypackage.submodule']):
51
import mypackage # Will be instrumented
52
import os # Will NOT be instrumented
53
54
# Exclusion-based instrumentation
55
with atheris.instrument_imports(exclude=['requests.sessions']):
56
import requests # Most of requests will be instrumented
57
# except requests.sessions which is excluded
58
```
59
60
**Important Notes:**
61
- Only modules imported within the context will be instrumented
62
- Previously imported modules cannot be instrumented with this method
63
- Use `instrument_all()` for already-imported modules
64
65
### Function-Level Instrumentation
66
67
Instrument individual functions with a decorator or direct call.
68
69
```python { .api }
70
def instrument_func(func):
71
"""
72
Instrument a specific Python function for coverage tracking.
73
74
Can be used as a decorator or called directly on a function object.
75
The function is instrumented in-place, affecting all call sites.
76
77
Args:
78
func (callable): Function to instrument
79
80
Returns:
81
callable: The same function, now instrumented
82
"""
83
```
84
85
**Usage Examples:**
86
87
```python
88
import atheris
89
90
# As a decorator
91
@atheris.instrument_func
92
def my_function(x, y):
93
if x > y:
94
return x * 2
95
else:
96
return y * 2
97
98
# Direct instrumentation
99
def another_function(data):
100
return process_data(data)
101
102
atheris.instrument_func(another_function)
103
104
# Instrumenting the test function itself (recommended)
105
@atheris.instrument_func
106
def TestOneInput(data):
107
my_function(data[0], data[1])
108
another_function(data[2:])
109
```
110
111
**When to Use Function-Level Instrumentation:**
112
- For specific functions you want to ensure are instrumented
113
- When you need instrumentation but can't use `instrument_imports()`
114
- For the `TestOneInput` function itself to avoid "no interesting inputs" errors
115
116
### Global Instrumentation
117
118
Instrument all currently loaded Python functions.
119
120
```python { .api }
121
def instrument_all():
122
"""
123
Instrument all currently loaded Python functions.
124
125
This scans the entire Python interpreter and instruments every function
126
it finds, including core Python functions and previously imported modules.
127
This operation can be slow but provides comprehensive coverage.
128
129
Note: This is an experimental feature.
130
"""
131
```
132
133
**Usage Example:**
134
135
```python
136
import atheris
137
import json
138
import sys
139
140
# Import modules first
141
import target_module
142
143
def TestOneInput(data):
144
target_module.process(data)
145
146
# Instrument everything that's currently loaded
147
atheris.instrument_all()
148
149
atheris.Setup(sys.argv, TestOneInput)
150
atheris.Fuzz()
151
```
152
153
**When to Use Global Instrumentation:**
154
- When you need to instrument modules that were imported before you could use `instrument_imports()`
155
- For comprehensive coverage including Python standard library functions
156
- When troubleshooting coverage issues
157
158
### Low-Level Bytecode Instrumentation
159
160
Direct bytecode manipulation for advanced use cases.
161
162
```python { .api }
163
def patch_code(code, trace_dataflow, nested=False):
164
"""
165
Patch Python bytecode with instrumentation for coverage tracking.
166
167
This is a low-level function used internally by other instrumentation methods.
168
Most users should use the higher-level functions instead.
169
170
Args:
171
code (types.CodeType): Python code object to patch
172
trace_dataflow (bool): Whether to trace dataflow (comparisons)
173
nested (bool): Whether this is a nested code object (default: False)
174
175
Returns:
176
types.CodeType: New code object with instrumentation added
177
"""
178
```
179
180
### Instrumentation Best Practices
181
182
**Recommended Pattern:**
183
184
```python
185
#!/usr/bin/python3
186
187
import atheris
188
import sys
189
190
# Import system modules before instrumentation
191
import os
192
import sys
193
194
# Import and instrument target modules
195
with atheris.instrument_imports():
196
import target_library
197
from mypackage import mymodule
198
199
@atheris.instrument_func
200
def TestOneInput(data):
201
# Instrumented test function
202
target_library.parse(data)
203
mymodule.process(data)
204
205
atheris.Setup(sys.argv, TestOneInput)
206
atheris.Fuzz()
207
```
208
209
**Handling "No Interesting Inputs" Error:**
210
211
If you see this error, it means the first few executions didn't generate coverage:
212
213
```
214
ERROR: no interesting inputs were found. Is the code instrumented for coverage? Exiting.
215
```
216
217
Solutions:
218
1. Add `@atheris.instrument_func` to your `TestOneInput` function
219
2. Use `atheris.instrument_all()` if import-time instrumentation isn't sufficient
220
3. Ensure your test function actually calls instrumented code in the first few executions
221
222
**Example Fix:**
223
224
```python
225
# This might cause "no interesting inputs" error
226
def TestOneInput(data):
227
if len(data) < 10:
228
return # Early return with no instrumented code
229
target_function(data)
230
231
# Better approach
232
@atheris.instrument_func # Instrument the test function itself
233
def TestOneInput(data):
234
if len(data) < 10:
235
return # Now this branch is instrumented
236
target_function(data)
237
```
238
239
### Performance Considerations
240
241
- **Import-time instrumentation** has minimal runtime overhead
242
- **Global instrumentation** can be slow during the instrumentation phase but has similar runtime performance
243
- **Function-level instrumentation** provides fine-grained control with minimal overhead
244
245
### Module Compatibility
246
247
Some modules may not work well with instrumentation:
248
249
```python
250
# Skip problematic modules
251
with atheris.instrument_imports(exclude=['numpy.core', 'matplotlib._internal']):
252
import numpy
253
import matplotlib.pyplot as plt
254
```
255
256
Common modules to exclude:
257
- Native extension internals (e.g., `numpy.core`)
258
- Modules that use dynamic code generation
259
- Modules with C extensions that don't interact well with bytecode instrumentation
260
261
### Testing Instrumentation
262
263
Verify that instrumentation is working:
264
265
```python
266
import atheris
267
import sys
268
269
with atheris.instrument_imports():
270
import target_module
271
272
def TestOneInput(data):
273
print(f"Testing with {len(data)} bytes")
274
target_module.function(data)
275
276
# Run with limited iterations to verify coverage
277
# Command: python fuzzer.py -atheris_runs=10
278
atheris.Setup(sys.argv, TestOneInput)
279
atheris.Fuzz()
280
```
281
282
If instrumentation is working, you should see coverage feedback in the fuzzer output.