Use case
Coverage-guided fuzzers such as JQF/Zest drive a generator from a mutable byte stream: the fuzzer mutates the raw bytes, the generator turns them into a structured object, and coverage feedback steers the next mutation. For this to work well, the generator has to consume the stream incrementally, so that a small byte change produces a small, local change in the object (one field, one list element) rather than a completely different graph.
Current state
Instancio derives all randomness from a single seed via withSeed(long). That is ideal for reproducibility, but coarse for a fuzzer: one seed determines the entire object graph, so flipping one byte of the fuzzer's input reshapes everything at once instead of one field. Coverage-guided search cannot map input regions onto structure.
There is no public way to supply a custom source of randomness:
InstancioApi exposes only withSeed(long) and withSettings(Settings).
org.instancio.spi.InstancioServiceProvider has providers for generators, type resolution, instantiation, setter resolution, and annotations — but none for randomness.
org.instancio.Random is a public interface, yet nothing lets a caller inject an implementation of it; the internal Random is always built from the seed.
I integrated Instancio as a pluggable generator provider in JQF using the seed approach, and documented the trade-off in the module README (linked below): Zest can only steer generation through the eight-byte seed.
What would help
org.instancio.Random already exposes exactly the right shape for a byte-driven implementation — intRange, longRange, stringOf, characterRange, oneOf, trueOrFalse, and so on. Each call naturally consumes a few bytes from a stream. The only missing piece is a way to inject a custom implementation. For example:
// Option A: a builder hook, parallel to withSeed/withSettings
public InstancioApi<T> withRandom(org.instancio.Random random);
// Option B: a provider in the existing SPI
interface InstancioServiceProvider {
default RandomProvider getRandomProvider() { return null; }
}
With either, a fuzzer supplies a Random backed by its byte stream, and Instancio's generation becomes byte-structured and steerable, while withSeed stays the default for everyone else. This is the property that lets a coverage-guided fuzzer exploit the generator, and it is the one thing keeping Instancio a coarser fit than a byte-structured library for this use case.
Links
Thanks for Instancio — the public Random interface is already most of the way there; a way to plug it in would make Instancio a first-class option for coverage-guided fuzzing.
Use case
Coverage-guided fuzzers such as JQF/Zest drive a generator from a mutable byte stream: the fuzzer mutates the raw bytes, the generator turns them into a structured object, and coverage feedback steers the next mutation. For this to work well, the generator has to consume the stream incrementally, so that a small byte change produces a small, local change in the object (one field, one list element) rather than a completely different graph.
Current state
Instancio derives all randomness from a single seed via
withSeed(long). That is ideal for reproducibility, but coarse for a fuzzer: one seed determines the entire object graph, so flipping one byte of the fuzzer's input reshapes everything at once instead of one field. Coverage-guided search cannot map input regions onto structure.There is no public way to supply a custom source of randomness:
InstancioApiexposes onlywithSeed(long)andwithSettings(Settings).org.instancio.spi.InstancioServiceProviderhas providers for generators, type resolution, instantiation, setter resolution, and annotations — but none for randomness.org.instancio.Randomis a public interface, yet nothing lets a caller inject an implementation of it; the internalRandomis always built from the seed.I integrated Instancio as a pluggable generator provider in JQF using the seed approach, and documented the trade-off in the module README (linked below): Zest can only steer generation through the eight-byte seed.
What would help
org.instancio.Randomalready exposes exactly the right shape for a byte-driven implementation —intRange,longRange,stringOf,characterRange,oneOf,trueOrFalse, and so on. Each call naturally consumes a few bytes from a stream. The only missing piece is a way to inject a custom implementation. For example:With either, a fuzzer supplies a
Randombacked by its byte stream, and Instancio's generation becomes byte-structured and steerable, whilewithSeedstays the default for everyone else. This is the property that lets a coverage-guided fuzzer exploit the generator, and it is the one thing keeping Instancio a coarser fit than a byte-structured library for this use case.Links
Thanks for Instancio — the public
Randominterface is already most of the way there; a way to plug it in would make Instancio a first-class option for coverage-guided fuzzing.