-
Notifications
You must be signed in to change notification settings - Fork 164
Maximize API #1036
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Maximize API #1036
Conversation
CountersTracker provides a flexible API for mapping program state to coverage counters, enabling incremental progress feedback to libFuzzer. Key features: - ensureCountersAllocated(id, numCounters): allocate counter range - setCounter/setCounterRange: set counter values by ID and offset - Thread-safe allocation via ConcurrentHashMap - Separate memory region from main coverage map This lays the foundation for the maximize() hill-climbing API.
Add Jazzer.maximize(value, id, minValue, maxValue) for guiding the fuzzer to maximize a value over time. For each observed value v in [minValue, maxValue], sets counters [0, v-minValue] to signal progress. Features: - Enables corpus minimization (only max-value input retained) - Convenience overload without explicit ID (uses instrumentation hook) - Delegates to CountersTracker for counter management - No state in Jazzer.java - all managed by CountersTracker
Example shows how maximize() helps fuzz a chaotic feedback system where standard coverage provides no guidance. The fuzzer is guided to increase "temperature" through complex state-dependent logic.
97db30c to
390836b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a maximize() API to Jazzer that enables hill-climbing fuzzing scenarios where standard code coverage is insufficient. The API guides the fuzzer to maximize a value by setting coverage counters for all values from the minimum up to the observed value, creating incremental progress feedback.
Changes:
- Added
CountersTrackerinfrastructure (Java and C++) to manage extra coverage counters separate from regular code coverage - Added
Jazzer.maximize()API with automatic call-site ID generation via instrumentation hooks - Added comprehensive test coverage for the new APIs
- Added ReactorFuzzTest example demonstrating the maximize API on a chaotic feedback system
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.h | Refactored header from CoverageTracker to CountersTracker, adding support for separate extra counters region |
| src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.cpp | New implementation managing both coverage and extra counters with libFuzzer registration |
| src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel | Updated build dependencies to reference counters_tracker instead of coverage_tracker |
| src/main/java/com/code_intelligence/jazzer/runtime/CountersTracker.java | New Java class providing thread-safe counter allocation and management API |
| src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel | Added CountersTracker build target and dependencies |
| src/main/java/com/code_intelligence/jazzer/runtime/JazzerApiHooks.java | Added instrumentation hook to auto-generate call-site IDs for maximize() calls |
| src/main/java/com/code_intelligence/jazzer/api/Jazzer.java | Added maximize() API methods with documentation |
| src/test/java/com/code_intelligence/jazzer/runtime/CountersTrackerTest.java | Comprehensive unit tests for CountersTracker including concurrency tests |
| src/test/java/com/code_intelligence/jazzer/api/MaximizeTest.java | Unit tests for the maximize() API covering edge cases |
| examples/junit/src/test/java/com/example/ReactorFuzzTest.java | Example demonstrating maximize() on a temperature maximization problem |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.cpp
Show resolved
Hide resolved
| return; | ||
| } | ||
|
|
||
| int numCounters = (int) (maxValue - minValue + 1); |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Integer overflow vulnerability: When (maxValue - minValue + 1) exceeds Integer.MAX_VALUE, the cast to int will silently overflow, leading to incorrect behavior. This could result in negative numCounters values or other unexpected results. The code should validate that the range fits within an int before the cast, and provide a clear error message if it doesn't. For example: if (maxValue - minValue + 1 > Integer.MAX_VALUE) throw an appropriate exception.
| int numCounters = (int) (maxValue - minValue + 1); | |
| final long range; | |
| try { | |
| range = Math.addExact(Math.subtractExact(maxValue, minValue), 1L); | |
| } catch (ArithmeticException e) { | |
| throw new IllegalArgumentException( | |
| "maxValue - minValue + 1 must fit within a 32-bit signed integer range", e); | |
| } | |
| if (range <= 0L || range > Integer.MAX_VALUE) { | |
| throw new IllegalArgumentException( | |
| "maxValue - minValue + 1 must be in the range [1, " + Integer.MAX_VALUE + "], but was " | |
| + range); | |
| } | |
| int numCounters = (int) range; |
|
|
||
| // Set counters if value provides signal | ||
| if (value >= minValue) { | ||
| int toOffset = (int) (Math.min(value, maxValue) - minValue); |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Integer overflow in toOffset calculation: Similar to the numCounters calculation above, when (Math.min(value, maxValue) - minValue) exceeds Integer.MAX_VALUE, the cast to int will silently overflow. This could cause incorrect counter ranges to be set. Add validation before the cast.
| int toOffset = (int) (Math.min(value, maxValue) - minValue); | |
| long toOffsetLong = Math.min(value, maxValue) - minValue; | |
| if (toOffsetLong > Integer.MAX_VALUE) { | |
| // Avoid integer overflow when converting to int; cannot represent this offset safely. | |
| return; | |
| } | |
| int toOffset = (int) toOffsetLong; |
| Jazzer.maximize(50, 5008, 0, 100); | ||
| Jazzer.maximize(50, 5008, 0, 200); // No-op without runtime |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test issue: This test expects different behavior with and without runtime but doesn't verify either behavior. With runtime, calling maximize with different ranges for the same ID should throw an IllegalArgumentException (as stated in the comment on line 76), but the test doesn't verify this. Consider using an appropriate assertion or expectation to verify the exception is thrown when runtime is available.
| long largeValue = 750_000; | ||
| long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000); | ||
| Jazzer.maximize(step, 5014, 0, 1000); |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test is always false.
| long largeValue = 750_000; | |
| long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000); | |
| Jazzer.maximize(step, 5014, 0, 1000); | |
| for (long largeValue : new long[] {-750_000L, 750_000L}) { | |
| long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000); | |
| Jazzer.maximize(step, 5014, 0, 1000); | |
| } |
| /** Maximum number of counters available (default 1M, configurable via environment variable). */ | ||
| private static final int MAX_COUNTERS = | ||
| System.getenv(ENV_MAX_COUNTERS) != null | ||
| ? Integer.parseInt(System.getenv(ENV_MAX_COUNTERS)) | ||
| : 1 << 20; | ||
|
|
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential uncaught 'java.lang.NumberFormatException'.
| /** Maximum number of counters available (default 1M, configurable via environment variable). */ | |
| private static final int MAX_COUNTERS = | |
| System.getenv(ENV_MAX_COUNTERS) != null | |
| ? Integer.parseInt(System.getenv(ENV_MAX_COUNTERS)) | |
| : 1 << 20; | |
| private static final int DEFAULT_MAX_COUNTERS = 1 << 20; | |
| /** Maximum number of counters available (default 1M, configurable via environment variable). */ | |
| private static final int MAX_COUNTERS = initMaxCounters(); | |
| private static int initMaxCounters() { | |
| String value = System.getenv(ENV_MAX_COUNTERS); | |
| if (value == null || value.isEmpty()) { | |
| return DEFAULT_MAX_COUNTERS; | |
| } | |
| try { | |
| return Integer.parseInt(value.trim()); | |
| } catch (NumberFormatException e) { | |
| return DEFAULT_MAX_COUNTERS; | |
| } | |
| } |
Summary
Add a hill-climbing
maximize()API to Jazzer that guides the fuzzer toward maximizing a value over time. This enables fuzzing scenarios where standard code coverage provides insufficient guidance, such as finding inputs that maximize some computed metric.Changes
Jazzer.maximize()APIHow it works: For each observed value v, sets coverage counters [0, v-minValue] to 1. This creates incremental progress feedback - higher values trigger more "coverage," guiding the fuzzer toward the maximum. Corpus minimization naturally retains only the input producing the highest value.
Example
Added ReactorFuzzTest demonstrating the API on a chaotic feedback system where standard coverage is constant but the fuzzer needs to maximize a computed temperature value.