Skip to content

Conversation

@kyakdan
Copy link
Member

@kyakdan kyakdan commented Feb 3, 2026

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() API

// Guide fuzzer to maximize 'value' within [minValue, maxValue]
Jazzer.maximize(value, id, minValue, maxValue);

// Convenience overload with auto-generated call-site ID (requires instrumentation)
Jazzer.maximize(value, minValue, maxValue);

How 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.

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.
Copy link

Copilot AI left a 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 CountersTracker infrastructure (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.

return;
}

int numCounters = (int) (maxValue - minValue + 1);
Copy link

Copilot AI Feb 3, 2026

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.

Suggested change
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;

Copilot uses AI. Check for mistakes.

// Set counters if value provides signal
if (value >= minValue) {
int toOffset = (int) (Math.min(value, maxValue) - minValue);
Copy link

Copilot AI Feb 3, 2026

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.

Suggested change
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;

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +79
Jazzer.maximize(50, 5008, 0, 100);
Jazzer.maximize(50, 5008, 0, 200); // No-op without runtime
Copy link

Copilot AI Feb 3, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +110
long largeValue = 750_000;
long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000);
Jazzer.maximize(step, 5014, 0, 1000);
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test is always false.

Suggested change
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);
}

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +56
/** 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;

Copy link

Copilot AI Feb 3, 2026

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'.

Suggested change
/** 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;
}
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants