or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcore-fuzzing.mddata-provider.mdindex.mdinstrumentation.md

instrumentation.mddocs/

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.