-
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
Changes from all commits
e7ea1af
064b7db
7083d08
398a539
ae1b9e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| /* | ||
| * Copyright 2026 Code Intelligence GmbH | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.example; | ||
|
|
||
| import com.code_intelligence.jazzer.api.Jazzer; | ||
| import com.code_intelligence.jazzer.junit.FuzzTest; | ||
| import com.code_intelligence.jazzer.mutation.annotation.NotNull; | ||
|
|
||
| public class ReactorFuzzTest { | ||
|
|
||
| @FuzzTest | ||
| public void fuzz(@NotNull String input) { | ||
| for (char c : input.toCharArray()) { | ||
| if (c < 32 || c > 126) return; | ||
| } | ||
| controlReactor(input); | ||
| } | ||
|
|
||
| private void controlReactor(String commands) { | ||
| long temperature = 0; // Starts cold | ||
|
|
||
| for (char cmd : commands.toCharArray()) { | ||
| // Complex, chaotic feedback loop. | ||
| // It is hard to predict which character increases temperature | ||
| // because it depends on the CURRENT temperature. | ||
| if ((temperature ^ cmd) % 3 == 0) { | ||
| temperature += (cmd % 10); // Heat up slightly | ||
| } else if ((temperature ^ cmd) % 3 == 1) { | ||
| temperature -= (cmd % 8); // Cool down slightly | ||
| } else { | ||
| temperature += 1; // Tiny increase | ||
| } | ||
|
|
||
| // Prevent dropping below absolute zero for simulation sanity | ||
| if (temperature < 0) temperature = 0; | ||
| } | ||
| // THE GOAL: MAXIMIZATION | ||
| // We need to drive 'temperature' to an extreme value. | ||
| // Standard coverage is 100% constant here (it just loops). | ||
| Jazzer.maximize(temperature, 500, 4500); | ||
| if (temperature >= 4500) { | ||
| throw new RuntimeException("Meltdown! Temperature maximized."); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -33,13 +33,18 @@ public final class Jazzer { | |||||||||||||||
| private static final MethodHandle TRACE_MEMCMP; | ||||||||||||||||
| private static final MethodHandle TRACE_PC_INDIR; | ||||||||||||||||
|
|
||||||||||||||||
| private static final MethodHandle COUNTERS_TRACKER_ALLOCATE; | ||||||||||||||||
| private static final MethodHandle COUNTERS_TRACKER_SET_RANGE; | ||||||||||||||||
|
|
||||||||||||||||
| static { | ||||||||||||||||
| Class<?> jazzerInternal = null; | ||||||||||||||||
| MethodHandle onFuzzTargetReady = null; | ||||||||||||||||
| MethodHandle traceStrcmp = null; | ||||||||||||||||
| MethodHandle traceStrstr = null; | ||||||||||||||||
| MethodHandle traceMemcmp = null; | ||||||||||||||||
| MethodHandle tracePcIndir = null; | ||||||||||||||||
| MethodHandle countersTrackerAllocate = null; | ||||||||||||||||
| MethodHandle countersTrackerSetRange = null; | ||||||||||||||||
| try { | ||||||||||||||||
| jazzerInternal = Class.forName("com.code_intelligence.jazzer.runtime.JazzerInternal"); | ||||||||||||||||
| MethodType onFuzzTargetReadyType = MethodType.methodType(void.class, Runnable.class); | ||||||||||||||||
|
|
@@ -70,6 +75,16 @@ public final class Jazzer { | |||||||||||||||
| tracePcIndir = | ||||||||||||||||
| MethodHandles.publicLookup() | ||||||||||||||||
| .findStatic(traceDataFlowNativeCallbacks, "tracePcIndir", tracePcIndirType); | ||||||||||||||||
|
|
||||||||||||||||
| Class<?> countersTracker = | ||||||||||||||||
| Class.forName("com.code_intelligence.jazzer.runtime.CountersTracker"); | ||||||||||||||||
| MethodType allocateType = MethodType.methodType(void.class, int.class, int.class); | ||||||||||||||||
| countersTrackerAllocate = | ||||||||||||||||
| MethodHandles.publicLookup() | ||||||||||||||||
| .findStatic(countersTracker, "ensureCountersAllocated", allocateType); | ||||||||||||||||
| MethodType setRangeType = MethodType.methodType(void.class, int.class, int.class); | ||||||||||||||||
| countersTrackerSetRange = | ||||||||||||||||
| MethodHandles.publicLookup().findStatic(countersTracker, "setCounterRange", setRangeType); | ||||||||||||||||
| } catch (ClassNotFoundException ignore) { | ||||||||||||||||
| // Not running in the context of the agent. This is fine as long as no methods are called on | ||||||||||||||||
| // this class. | ||||||||||||||||
|
|
@@ -86,14 +101,16 @@ public final class Jazzer { | |||||||||||||||
| TRACE_STRSTR = traceStrstr; | ||||||||||||||||
| TRACE_MEMCMP = traceMemcmp; | ||||||||||||||||
| TRACE_PC_INDIR = tracePcIndir; | ||||||||||||||||
| COUNTERS_TRACKER_ALLOCATE = countersTrackerAllocate; | ||||||||||||||||
| COUNTERS_TRACKER_SET_RANGE = countersTrackerSetRange; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| private Jazzer() {} | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * A 32-bit random number that hooks can use to make pseudo-random choices between multiple | ||||||||||||||||
| * possible mutations they could guide the fuzzer towards. Hooks <b>must not</b> base the decision | ||||||||||||||||
| * whether or not to report a finding on this number as this will make findings non-reproducible. | ||||||||||||||||
| * whether to report a finding on this number as this will make findings non-reproducible. | ||||||||||||||||
| * | ||||||||||||||||
| * <p>This is the same number that libFuzzer uses as a seed internally, which makes it possible to | ||||||||||||||||
| * deterministically reproduce a previous fuzzing run by supplying the seed value printed by | ||||||||||||||||
|
|
@@ -230,6 +247,75 @@ public static void exploreState(byte state) { | |||||||||||||||
| // an automatically generated call-site id. Without instrumentation, this is a no-op. | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Hill-climbing API to maximize a value. For each observed value v in [minValue, maxValue], | ||||||||||||||||
| * provides feedback that all values in [minValue, v] are covered. | ||||||||||||||||
| * | ||||||||||||||||
| * <p>This enables corpus minimization to keep only the input resulting in the maximum value. | ||||||||||||||||
| * Values below minValue provide no signal. Values above maxValue are clamped to maxValue. | ||||||||||||||||
| * | ||||||||||||||||
| * <p><b>Important:</b> This allocates (maxValue - minValue + 1) coverage counters per unique ID. | ||||||||||||||||
| * For large value ranges, use a mapping function to reduce the range: | ||||||||||||||||
| * | ||||||||||||||||
| * <pre>{@code | ||||||||||||||||
| * // Map [0, 1_000_000] to [0, 1000] steps | ||||||||||||||||
| * long step = value < 0 ? 0 : Math.min(value / 1000, 1000); | ||||||||||||||||
| * Jazzer.maximize(step, id, 0, 1000); | ||||||||||||||||
| * }</pre> | ||||||||||||||||
| * | ||||||||||||||||
| * @param value The value to maximize (will be clamped to [minValue, maxValue]) | ||||||||||||||||
| * @param id A unique identifier for this call site (must be consistent across runs) | ||||||||||||||||
| * @param minValue The minimum value in the range (inclusive) | ||||||||||||||||
| * @param maxValue The maximum value in the range (inclusive) | ||||||||||||||||
| */ | ||||||||||||||||
| public static void maximize(long value, int id, long minValue, long maxValue) { | ||||||||||||||||
| if (maxValue < minValue) { | ||||||||||||||||
| throw new IllegalArgumentException("maxValue must be >= minValue"); | ||||||||||||||||
| } | ||||||||||||||||
| long range = maxValue - minValue; | ||||||||||||||||
| if (range < 0 || range > (long) Integer.MAX_VALUE - 1) { | ||||||||||||||||
| throw new IllegalArgumentException( | ||||||||||||||||
| "Range too large: (maxValue - minValue + 1) must be <= Integer.MAX_VALUE"); | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+272
to
+279
|
||||||||||||||||
|
|
||||||||||||||||
| if (COUNTERS_TRACKER_ALLOCATE == null) { | ||||||||||||||||
| return; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| int numCounters = (int) (range + 1); | ||||||||||||||||
|
|
||||||||||||||||
| try { | ||||||||||||||||
| // Allocate counters (idempotent, validates numCounters > 0 and consistency) | ||||||||||||||||
| COUNTERS_TRACKER_ALLOCATE.invokeExact(id, numCounters); | ||||||||||||||||
|
|
||||||||||||||||
| // Set counters if value provides signal | ||||||||||||||||
| if (value >= minValue) { | ||||||||||||||||
| int toOffset = (int) (Math.min(value, maxValue) - minValue); | ||||||||||||||||
|
||||||||||||||||
| 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; |
Uh oh!
There was an error while loading. Please reload this page.