| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| library fuchsia.fuzzer; |
| |
| using zx; |
| |
| /// Indicates the result of a long-running fuzzing workflow. |
| /// |
| /// Fuzzers have several "long-running" workflows; that is, the fuzzer may |
| /// perform actions for an indefinite period of time. Fuzzing itself is the most |
| /// notable example, as the fuzzing engine may continuously generate new inputs |
| /// until an error is found. This workflow is represented by the `Fuzz` method |
| /// below. Other long-running workflows include `TryOne`, `Cleanse`, `Minimize` |
| /// and `Merge`. |
| /// |
| /// These workflows continue executing after their associated call returns. Only |
| /// one workflow at a time may be executing. Callers can use `AddMonitor` to |
| /// monitor the status of the workflow, and `GetArtifact` to retrieve the |
| /// `Result` and optionally associated `Input` when it completes. |
| type Result = flexible enum : uint32 { |
| /// The associated input did not cause any errors to be detected when tried. |
| NO_ERRORS = 1; |
| |
| /// Trying the associated input triggered a memory allocation by an |
| /// instrumented process that exceeded the configured limit. Some fuzzing |
| /// engines, such as libFuzzer, may not distinguish between this result and |
| /// `OOM`. To differentiate, examine the fuzzer output. |
| BAD_MALLOC = 2; |
| |
| /// Trying the associated input triggered an exception in an instrumented |
| /// process. |
| CRASH = 3; |
| |
| /// Trying the associated input triggered an error in an instrumented |
| /// process that was detected by a sanitizer runtime. |
| DEATH = 4; |
| |
| /// Trying the associated input caused an instrumented process to exit |
| /// unexpectedly. Some fuzzing engines such as libFuzzer may combine this |
| /// result with `CRASH`. To differentiate, examine the fuzzer output. |
| EXIT = 5; |
| |
| /// Trying the associated input triggered a memory leak in an instrumented |
| /// process. |
| LEAK = 6; |
| |
| /// Trying the associated input caused an instrumented process's memory |
| /// usage to exceed the configured limit. |
| OOM = 7; |
| |
| /// Trying the associated input took longer than the configured limit. |
| TIMEOUT = 8; |
| |
| /// The associated input was produced by finding the shortest input that |
| /// produces the same error as an original input. See `Minimize`. |
| MINIMIZED = 9; |
| |
| /// The associated input was produced by replacing bytes in an original |
| /// input with PII-free values while preserving the error produced. See |
| /// `Cleanse`. |
| CLEANSED = 10; |
| |
| /// The fuzzer corpus has been compacted to only include the shortest inputs |
| /// that preserve overall coverage. This workflow does not have an |
| /// associated input. See `Merge`. |
| MERGED = 11; |
| }; |
| |
| /// Encapsulation of a fuzzer input transferred over a socket, e.g. between a |
| /// host and device via overnet. |
| type Input = resource struct { |
| /// Socket that can be used to read the data. |
| socket zx.Handle:SOCKET; |
| |
| /// The total amount of data to be transferred. |
| size uint64; |
| }; |
| |
| /// Encapsulates the result of a long-running fuzzing workflow and optionally |
| /// the input that caused it, depending on the workflow. |
| // |
| // TODO(https://fxbug.dev/42074956): Include workflow enum. |
| type Artifact = resource table { |
| /// Result of executing the long-running workflow. |
| 1: result Result; |
| |
| /// Optional fuzzer input produced by a specific long-running workflow. This |
| /// may be: |
| /// |
| /// * A discovered input in the case of `Fuzz` |
| /// * A constructed input in the cases of `Cleanse` and `Minimize`. |
| /// * Omitted in the cases of `TryOne` and `Merge`. |
| 2: input Input; |
| |
| /// An error generated during a long-running workflow. These errors are |
| /// distinct from those that result from validating workflow requests and |
| /// are documented for the workflow methods below. These errors result from |
| /// unexpected framework failure. For example, if libFuzzer fails due to an |
| /// internal error, the engine will report it via this field. |
| /// |
| /// The `result` and `input` fields are invalid if this field is present and |
| /// anything other than `ZX_OK`. |
| 3: error zx.Status; |
| }; |
| |
| /// Marker indicating the last message in a diagnostic output stream. |
| /// |
| /// In addition to an `Artifact`, long-running workflows may also produce the |
| /// standard output, standard error, and system logs. These fuzzer output |
| /// streams are provided by `fuchsia.test_manager/RunBuilder` and have no |
| /// guaranteed order with respect to FIDL responses. As a result, the streams |
| /// need to include an "in-band" signal that indicates when they are complete |
| /// for a particular workflow. Clients processing the fuzzer output may use this |
| /// marker to synchronize the streams and the FIDL response for `GetArtifact`. |
| /// |
| /// The string is intentionally somewhat long to mitigate the likelihood of it |
| /// being emitted by the code under test. |
| /// |
| const DONE_MARKER string = "DONE: A long-running `fuchsia.fuzzer.Controller` workflow is complete."; |
| |
| /// Provides the management interface for fuzzing. |
| /// |
| /// This protocol is implemented by the fuzzing engine. Clients for this |
| /// protocol are provided by `fuchsia.fuzzer/ControllerProvider.Connect`. |
| /// |
| /// The channel is closed when the fuzzer is no longer needed, and on error. |
| /// Clients should exit and not attempt to reconnect. |
| closed protocol Controller { |
| /// Sets various execution and error detection parameters. This may be |
| /// called multiple times; only the most recently received options are used. |
| /// If the `Options` parameter omits one or more fields, those parameters |
| /// are unchanged in the fuzzer. Until the initial call to this method, the |
| /// fuzzer should assume the default values for `Options`. |
| /// |
| /// + request `options` the execution and error detection parameters. |
| /// * error one of the following: |
| /// * error |
| /// * `ZX_ERR_BAD_STATE` if a long-running call such as `Execute`, |
| /// `Cleanse`, `Minimize`, `Fuzz`, or `Merge` is in progress. |
| /// * `ZX_ERR_NOT_SUPPORTED` if a value provided for an option is not |
| /// supported by the engine. Check the logs for additional details. |
| strict Configure(struct { |
| options Options; |
| }) -> () error zx.Status; |
| |
| /// Gets the current values for the various execution and error detection |
| /// parameters. |
| /// |
| /// - response `options` the execution and error detection parameters. |
| strict GetOptions() -> (struct { |
| options Options; |
| }); |
| |
| /// Writes the provided `input` to either the "seed" or "live" `corpus`. |
| /// Returns an error if transferring the input fails; see `Input` for |
| /// details. |
| /// |
| /// + request `corpus` the type of corpus to add to. |
| /// + request `input` the sequence of bytes to add as a test input. |
| /// * error one of the following: |
| /// * `ZX_ERR_INVALID_ARGS` if corpus type is invalid. |
| /// * A socket error if transferring the input fails. |
| strict AddToCorpus(resource struct { |
| corpus Corpus; |
| input Input; |
| }) -> () error zx.Status; |
| |
| /// Connects a `corpus_reader` for either the "seed" or "live" corpus. |
| /// |
| /// + request `corpus` the type of corpus to read from. |
| /// + request `corpus_reader` the client used to send test inputs. |
| strict ReadCorpus(resource struct { |
| corpus Corpus; |
| corpus_reader client_end:CorpusReader; |
| }) -> (); |
| |
| /// Parses and loads an AFL-style dictionary. Invalid entries are logged and |
| /// skipped. |
| /// |
| /// See also: |
| /// |
| /// * https://github.com/mirrorer/afl/blob/master/dictionaries/README.dictionaries |
| /// |
| /// + request `dictionary` the AFL-style dictionary |
| /// * error one of the following: |
| /// * A socket error if transferring the input fails. |
| /// *`ZX_ERR_INVALID_ARGS` if parsing the dictionary fails. |
| strict WriteDictionary(resource struct { |
| dictionary Input; |
| }) -> () error zx.Status; |
| |
| /// Returns the current dictionary as an Input, which may be empty. |
| /// |
| /// - response `dictionary` the current AFL-style dictionary. |
| strict ReadDictionary() -> (resource struct { |
| dictionary Input; |
| }); |
| |
| /// Installs a `Monitor` to push status. To pull status instead, use |
| /// `GetStatus`. |
| /// |
| /// + request `monitor` the client used to send status updates. |
| strict AddMonitor(resource struct { |
| monitor client_end:Monitor; |
| }) -> (); |
| |
| /// Runs the normal fuzzing feedback loop: |
| /// |
| /// 1. Selects an input from the corpus. |
| /// 2. Mutates the input. |
| /// 3. Uses the `TargetAdapter` to execute the input. |
| /// 4. Checks feedback from the `InstrumentedProcesses`. |
| /// 5. If the input produced useful feedback, adds it to the corpus. |
| /// |
| /// This call returns quickly, but the loop continues until one of three |
| /// conditions is met: |
| /// |
| /// 1. The configured, non-zero number of `runs` has been reached. |
| /// 2. The configured, non-zero `duration` elapses. |
| /// 3. An error is detected, and returned. |
| /// |
| /// See `Result` for more details on long running workflows such as this |
| /// one. |
| /// |
| /// * error `ZX_ERR_BAD_STATE` if another long-running workflow is in |
| /// progress. |
| strict Fuzz() -> () error zx.Status; |
| |
| /// Executes the target code with a single `test_input`. |
| /// |
| /// See `Result` for more details on long running workflows such as this |
| /// one. |
| /// |
| /// + request `input` the sequence of bytes to use with the fuzz target. |
| /// * error one of the following: |
| /// * `ZX_ERR_BAD_STATE` if another long-running workflow is in |
| /// progress. |
| /// * A socket error if transferring the input fails. |
| strict TryOne(resource struct { |
| test_input Input; |
| }) -> () error zx.Status; |
| |
| /// Attempts to find the smallest input that produces the same error as the |
| /// given `test_input`, |
| /// constrained by the configured `total_time` and/or `runs` options. |
| /// |
| /// See `Result` for more details on long running workflows such as this |
| /// one. |
| /// |
| /// + request `input` the sequence of bytes to minimize. |
| /// * error one of the following: |
| /// * `ZX_ERR_BAD_STATE` if another long-running workflow is in |
| /// progress. |
| /// * A socket error if transferring the input fails. |
| /// * `ZX_ERR_INVALID_ARGS` if the provided `test_input` does not cause |
| /// an error. |
| strict Minimize(resource struct { |
| test_input Input; |
| }) -> () error zx.Status; |
| |
| /// Attempts to replace bytes of the given `test_input` with "filler" bytes, |
| /// e.g. \x00, \xFF, without changing the error produced. |
| /// |
| /// See `Result` for more details on long running workflows such as this |
| /// one. |
| /// |
| /// To preserve backwards compatibility with libFuzzer and ClusterFuzz, an |
| /// input that produces no result is *not* considered an error, and is |
| /// returned as-is. |
| /// |
| /// + request `input` the sequence of bytes to cleanse. |
| /// * error one of the following: |
| /// * `ZX_ERR_BAD_STATE` if another long-running workflow is in |
| /// progress. |
| /// * A socket error if transferring the input fails. |
| strict Cleanse(resource struct { |
| test_input Input; |
| }) -> () error zx.Status; |
| |
| /// Attempts to shrink the corpora. Inputs from the seed corpus will be |
| /// preserved. All other inputs will be sorted by amount of feedback |
| /// produced and input size, and only those inputs that add new feedback not |
| /// seen earlier in the sequence will be kept. |
| /// |
| /// See `Result` for more details on long running workflows such as this |
| /// one. |
| /// |
| /// * error one of the following: |
| /// * `ZX_ERR_BAD_STATE` if another long-running workflow is in |
| /// progress. |
| /// * `ZX_ERR_INVALID_ARGS` if an input in the seed corpus causes an |
| /// error. Inputs in the live corpus that cause errors are skipped. |
| strict Merge() -> () error zx.Status; |
| |
| /// Returns various fuzzing metrics, e.g. total coverage, speed, etc. |
| /// |
| /// - response `status` the current value of fuzzing metrics. |
| strict GetStatus() -> (struct { |
| status Status; |
| }); |
| |
| /// Returns the results of a long-running workflow. |
| /// |
| /// This method uses the |
| /// ["hanging get" pattern](https://fuchsia.dev/fuchsia-src/development/api/fidl#hanging-get). |
| /// Upon the first call for a given connection, it will immediately return |
| /// the controller's current artifact. Upon subsequent calls, it will block |
| /// until the controller's artifact changes before returning. Clients should |
| /// use `GetStatus` to ensure the fuzzer is not idle before making a |
| /// blocking call to `WatchArtifact`. |
| /// |
| /// Combined with the `Status.running` value obtained from `GetStatus`, this |
| /// allows a (re-)connecting client to infer the state of the fuzzer: |
| /// |
| /// * If the fuzzer is idle and has an empty artifact, then it is |
| /// unchanged since the last connection, if any. |
| /// * If the fuzzer is running and has an empty artifact, then it is |
| /// performing a long-running workflow started by a previous connection. |
| /// The client may call `WatchArtifact` again to wait for the workflow |
| /// to complete. |
| /// * If the fuzzer is idle and has a non-empty artifact, then it has |
| /// completed a long-running workflow since the last connection. |
| /// |
| /// It is not an error for clients to disconnect while waiting for a |
| /// response from this method. This method is intended to allow clients to |
| /// resume waiting for a long-running workflow to complete after |
| /// intentionally or inadvertently being disconnected for an indefinite |
| /// amount of time. |
| /// |
| /// - response `artifact` the most recent result and/or error-causing input. |
| strict WatchArtifact() -> (resource struct { |
| artifact Artifact; |
| }); |
| }; |
| |
| /// Provides a `Controller` implementation. |
| /// |
| /// This protocol is implemented by the fuzzing engine. This capability is *not* |
| /// routed. Instead, the engine uses the `fuchsia.fuzzer/Registry` channel |
| /// provided by the fuzz-test-runner to send the client end of this interface to |
| /// the fuzz-registry. |
| /// |
| /// The fuzz-registry will close the channel upon error, or upon its own exit. |
| /// The fuzzer should exit and not attempt to reconnect when on channel close. |
| closed protocol ControllerProvider { |
| /// Connects a client to the fuzzer. |
| /// |
| /// Within the component fuzzing framework, the fuzz-manager forwards |
| /// `Controller` connection requests to the fuzz-registry, which uses |
| /// clients of this interface provided by the fuzzers themselves to perform |
| /// the connection. |
| /// |
| /// At most one client can be connected to a controller at any given time. |
| /// A subsequent call to `Connect` will preempt and replace the existing |
| /// connection. |
| /// |
| /// + request `controller` the connection from the client. |
| strict Connect(resource struct { |
| controller server_end:Controller; |
| }) -> (); |
| |
| /// Interrupt any current workflow, closes the channel, and exits the |
| /// fuzzing engine. |
| strict Stop(); |
| }; |