| Adrian Taylor | 6a886ec6 | 2023-10-25 23:45:27 | [diff] [blame] | 1 | # Getting started with libfuzzer in Chromium |
| 2 | |
| 3 | Our current best advice on how to start fuzzing is by using FuzzTest, which |
| 4 | has its own [getting started guide here]. If you're reading this page, it's |
| 5 | probably because you've run into limitations of FuzzTest and want to create |
| 6 | a libfuzzer fuzzer instead. This is a slightly older approach to fuzzing |
| 7 | Chrome, but it still works well - read on. |
| 8 | |
| 9 | This document walks you through the basic steps to start fuzzing and suggestions |
| 10 | for improving your fuzz targets. If you're looking for more advanced fuzzing |
| 11 | topics, see the [main page](README.md). |
| 12 | |
| 13 | [TOC] |
| 14 | |
| 15 | ## Getting started |
| 16 | |
| 17 | ### Simple Example |
| 18 | |
| 19 | Before writing any code let us look at a simple |
| 20 | example of a test that uses input fuzzing. The test is setup to exercise the |
| 21 | [`CreateFnmatchQuery`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/extensions/file_manager/search_by_pattern.h;drc=4bc4bcef0ab5581a5a27cea986296739582243a6) |
| 22 | function. The role of this function is to take a user query and produce |
| 23 | a case-insensitive pattern that matches file names containing the |
| 24 | query in them. For example, for a query "1abc" the function generates |
| 25 | "\*1[aA][bB][cC]\*". Unlike a traditional test, an input fuzzing test does not |
| 26 | care about the output of the tested function. Instead it verifies that no |
| 27 | matter what string the user enters `CreateFnmatchQuery` does not do something |
| 28 | unexpected, such as a crash, overriding a memory region, etc. The test |
| 29 | [create_fnmatch_query_fuzzer.cc](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/extensions/file_manager/create_fnmatch_query_fuzzer.cc;drc=1f5a5af3eb1bbdf9e4566c3e6d2051e68de112eb) |
| 30 | is shown below: |
| 31 | |
| 32 | ```cpp |
| 33 | #include <stddef.h> |
| 34 | #include <stdint.h> |
| 35 | |
| 36 | #include <string> |
| 37 | |
| 38 | #include "chrome/browser/ash/extensions/file_manager/search_by_pattern.h" |
| 39 | |
| 40 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| 41 | std::string str = std::string(reinterpret_cast<const char*>(data), size); |
| 42 | extensions::CreateFnmatchQuery(str); |
| 43 | return 0; |
| 44 | } |
| 45 | ``` |
| 46 | |
| 47 | The code starts by including `stddef.h` for `size_t` definition, `stdint.h` |
| 48 | for `uint8_t` definition, `string` for `std::string` definition and finally |
| 49 | the file where `extensions::CreateFnmatchQuery` function is defined. Next |
| 50 | it declares and defines the `LLVMFuzzerTestOneInput` function, which is |
| 51 | the function called by the testing framework. The function is supplied with two |
| 52 | arguments, a pointer to an array of bytes, and the size of the array. These |
| 53 | bytes are generated by the fuzzing test harness and their specific values |
| 54 | are irrelevant. The job of the test is to convert those bytes to input |
| 55 | parameters of the tested function. In our case bytes are converted |
| 56 | to a `std::string` and given to the `CreateFnmatchQuery` function. If |
| 57 | the function completes its job and the code successfully returns, the |
| 58 | `LLVMFuzzerTestOneInput` function returns 0, signaling a successful execution. |
| 59 | |
| 60 | The above pattern is typical to fuzzing tests. You create a |
| 61 | `LLVMFuzzerTestOneInput` function. You then write code that uses the provided |
| 62 | random bytes to form input parameters to the function you intend to test. Next, |
| 63 | you call the function, and if it successfully completes, return 0. |
| 64 | |
| 65 | To run this test we need to create a `fuzzer_test` target in the appropriate |
| 66 | `BUILD.gn` file. For the above example, the target is defined as |
| 67 | |
| 68 | ```python |
| 69 | fuzzer_test("create_fnmatch_query_fuzzer") { |
| 70 | sources = [ "extensions/file_manager/create_fnmatch_query_fuzzer.cc" ] |
| 71 | deps = [ |
| 72 | ":ash", |
| 73 | "//base", |
| 74 | "//chrome/browser", |
| 75 | "//components/exo/wayland:ui_controls_protocol", |
| 76 | ] |
| 77 | } |
| 78 | ``` |
| 79 | The source field typically specified just the file that contains the test. The |
| 80 | dependencies are specific to the tested function. Here we are listing them for |
| 81 | the completeness. In your test all but `//base` dependencies are unlikely to be |
| 82 | required. |
| 83 | |
| 84 | ### Creating your first fuzz target |
| 85 | |
| 86 | Having seen a concrete example, let us describe the generic flow of steps to |
| 87 | create a new fuzzing test. |
| 88 | |
| 89 | 1. In the same directory as the code you are going to fuzz (or next to the tests |
| 90 | for that code), create a new `<my_fuzzer>.cc` file. |
| 91 | |
| 92 | *** note |
| 93 | **Note:** Do not use the `testing/libfuzzer/fuzzers` directory. This |
| 94 | directory was used for initial sample fuzz targets but is no longer |
| 95 | recommended for landing new targets. |
| 96 | *** |
| 97 | |
| 98 | 2. In the new file, define a `LLVMFuzzerTestOneInput` function: |
| 99 | |
| 100 | ```cpp |
| 101 | #include <stddef.h> |
| 102 | #include <stdint.h> |
| 103 | |
| 104 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| 105 | // Put your fuzzing code here and use |data| and |size| as input. |
| 106 | return 0; |
| 107 | } |
| 108 | ``` |
| 109 | |
| 110 | 3. In `BUILD.gn` file, define a `fuzzer_test` GN target: |
| 111 | |
| 112 | ```python |
| 113 | import("//testing/libfuzzer/fuzzer_test.gni") |
| 114 | fuzzer_test("my_fuzzer") { |
| 115 | sources = [ "my_fuzzer.cc" ] |
| 116 | deps = [ ... ] |
| 117 | } |
| 118 | ``` |
| 119 | |
| 120 | *** note |
| 121 | **Note:** Most of the targets are small. They may perform one or a few API calls |
| 122 | using the data provided by the fuzzing engine as an argument. However, fuzz |
| 123 | targets may be more complex if a certain initialization procedure needs to be |
| Dustin J. Mitchell | 795c736 | 2024-01-18 16:50:52 | [diff] [blame] | 124 | performed. [quic_session_pool_fuzzer.cc] is a good example of a complex fuzz |
| Adrian Taylor | 6a886ec6 | 2023-10-25 23:45:27 | [diff] [blame] | 125 | target. |
| 126 | *** |
| 127 | |
| 128 | Once you created your first fuzz target, in order to run it, you must set up |
| 129 | your build environment. This is described next. |
| 130 | |
| 131 | ### Setting up your build environment |
| 132 | |
| 133 | Generate build files by using the `use_libfuzzer` [GN] argument together with a |
| 134 | sanitizer. Rather than generating a GN build configuration by hand, we recommend |
| 135 | that you run the meta-builder tool using [GN config] that corresponds to the |
| 136 | operating system of the DUT you're deploying to: |
| 137 | |
| 138 | ```bash |
| 139 | # AddressSanitizer is the default config we recommend testing with. |
| 140 | # Linux: |
| 141 | tools/mb/mb.py gen -m chromium.fuzz -b 'Libfuzzer Upload Linux ASan' out/libfuzzer |
| 142 | # Chrome OS: |
| 143 | tools/mb/mb.py gen -m chromium.fuzz -b 'Libfuzzer Upload Chrome OS ASan' out/libfuzzer |
| 144 | # Mac: |
| 145 | tools/mb/mb.py gen -m chromium.fuzz -b 'Libfuzzer Upload Mac ASan' out/libfuzzer |
| 146 | # Windows: |
| 147 | python tools\mb\mb.py gen -m chromium.fuzz -b "Libfuzzer Upload Windows ASan" out\libfuzzer |
| 148 | ``` |
| 149 | |
| 150 | If testing things locally these are the recommended configurations |
| 151 | |
| 152 | ```bash |
| 153 | # AddressSanitizer is the default config we recommend testing with. |
| 154 | # Linux: |
| 155 | tools/mb/mb.py gen -m chromium.fuzz -b 'Libfuzzer Local Linux ASan' out/libfuzzer |
| 156 | # Chrome OS: |
| 157 | tools/mb/mb.py gen -m chromium.fuzz -b 'Libfuzzer Local Chrome OS ASan' out/libfuzzer |
| 158 | # Mac: |
| 159 | tools/mb/mb.py gen -m chromium.fuzz -b 'Libfuzzer Local Mac ASan' out/libfuzzer |
| 160 | # Windows: |
| 161 | python tools\mb\mb.py gen -m chromium.fuzz -b "Libfuzzer Local Windows ASan" out\libfuzzer |
| 162 | ``` |
| 163 | |
| 164 | [`tools/mb/mb.py`](https://source.chromium.org/chromium/chromium/src/+/main:tools/mb/mb.py;drc=c771c017eca9a6a859d245be54c511acafdc9867) |
| 165 | is "a wrapper script for GN that [..] generate[s] build files for sets of |
| 166 | canned configurations." The `-m` flag selects the builder group, while the |
| 167 | `-b` flag selects a specific builder in the builder group. The `out/libfuzzer` |
| 168 | is the directory to which GN configuration is written. If you wish, you can |
| 169 | inspect the generated config by running `gn args out/libfuzzer`, once the |
| 170 | `mb.py` script is done. |
| 171 | |
| Adrian Taylor | 6a886ec6 | 2023-10-25 23:45:27 | [diff] [blame] | 172 | You can also invoke [AFL] by using the `use_afl` GN argument, but we |
| 173 | recommend libFuzzer for local development. Running libFuzzer locally doesn't |
| 174 | require any special configuration and gives quick, meaningful output for speed, |
| 175 | coverage, and other parameters. |
| 176 | *** |
| 177 | |
| 178 | It’s possible to run fuzz targets without sanitizers, but not recommended, as |
| 179 | sanitizers help to detect errors which may not result in a crash otherwise. |
| 180 | `use_libfuzzer` is supported in the following sanitizer configurations. |
| 181 | |
| 182 | | GN Argument | Description | Supported OS | |
| 183 | |-------------|-------------|--------------| |
| 184 | | `is_asan=true` | Enables [AddressSanitizer] to catch problems like buffer overruns. | Linux, Windows, Mac, Chrome OS | |
| 185 | | `is_msan=true` | Enables [MemorySanitizer] to catch problems like uninitialized reads<sup>\[[\*](reference.md#MSan)\]</sup>. | Linux | |
| 186 | | `is_ubsan_security=true` | Enables [UndefinedBehaviorSanitizer] to catch<sup>\[[\*](reference.md#UBSan)\]</sup> undefined behavior like integer overflow.| Linux | |
| 187 | |
| 188 | For more on builder and sanitizer configurations, see the [Integration |
| 189 | Reference] page. |
| 190 | |
| 191 | *** note |
| 192 | **Hint**: Fuzz targets are built with minimal symbols by default. You can adjust |
| 193 | the symbol level by setting the `symbol_level` attribute. |
| 194 | *** |
| 195 | |
| 196 | ### Running the fuzz target |
| 197 | |
| 198 | After you create your fuzz target, build it with autoninja and run it locally. |
| Yulun Zeng | 3cfbf40a | 2024-02-16 15:50:26 | [diff] [blame] | 199 | To make this example concrete, we are going to use the existing |
| Adrian Taylor | 6a886ec6 | 2023-10-25 23:45:27 | [diff] [blame] | 200 | `create_fnmatch_query_fuzzer` target. |
| 201 | |
| 202 | ```bash |
| 203 | # Build the fuzz target. |
| Yulun Zeng | 3cfbf40a | 2024-02-16 15:50:26 | [diff] [blame] | 204 | autoninja -C out/libfuzzer chrome/browser/ash:create_fnmatch_query_fuzzer |
| Adrian Taylor | 6a886ec6 | 2023-10-25 23:45:27 | [diff] [blame] | 205 | # Run the fuzz target. |
| 206 | ./out/libfuzzer/create_fnmatch_query_fuzzer |
| 207 | ``` |
| 208 | |
| 209 | Your fuzz target should produce output like this: |
| 210 | |
| 211 | ``` |
| 212 | INFO: Seed: 1511722356 |
| 213 | INFO: Loaded 2 modules (115485 guards): 22572 [0x7fe8acddf560, 0x7fe8acdf5610), 92913 [0xaa05d0, 0xafb194), |
| 214 | INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes |
| 215 | INFO: A corpus is not provided, starting from an empty corpus |
| 216 | #2 INITED cov: 961 ft: 48 corp: 1/1b exec/s: 0 rss: 48Mb |
| 217 | #3 NEW cov: 986 ft: 70 corp: 2/104b exec/s: 0 rss: 48Mb L: 103/103 MS: 1 InsertRepeatedBytes- |
| 218 | #4 NEW cov: 989 ft: 74 corp: 3/106b exec/s: 0 rss: 48Mb L: 2/103 MS: 1 InsertByte- |
| 219 | #6 NEW cov: 991 ft: 76 corp: 4/184b exec/s: 0 rss: 48Mb L: 78/103 MS: 2 CopyPart-InsertRepeatedBytes- |
| 220 | ``` |
| 221 | |
| 222 | A `... NEW ...` line appears when libFuzzer finds new and interesting inputs. If |
| 223 | your fuzz target is efficient, it will find a lot of them quickly. A `... pulse |
| 224 | ...` line appears periodically to show the current status. |
| 225 | |
| 226 | For more information about the output, see [libFuzzer's output documentation]. |
| 227 | |
| 228 | *** note |
| 229 | **Note:** If you observe an `odr-violation` error in the log, please try setting |
| 230 | the following environment variable: `ASAN_OPTIONS=detect_odr_violation=0` and |
| 231 | running the fuzz target again. |
| 232 | *** |
| 233 | |
| 234 | #### Symbolizing a stacktrace |
| 235 | |
| 236 | If your fuzz target crashes when running locally and you see non-symbolized |
| 237 | stacktrace, make sure you add the `third_party/llvm-build/Release+Asserts/bin/` |
| 238 | directory from Chromium’s Clang package in `$PATH`. This directory contains the |
| 239 | `llvm-symbolizer` binary. |
| 240 | |
| 241 | Alternatively, you can set an `external_symbolizer_path` via the `ASAN_OPTIONS` |
| 242 | environment variable: |
| 243 | |
| 244 | ```bash |
| 245 | ASAN_OPTIONS=external_symbolizer_path=/my/local/llvm/build/llvm-symbolizer \ |
| 246 | ./fuzzer ./crash-input |
| 247 | ``` |
| 248 | |
| 249 | The same approach works with other sanitizers via `MSAN_OPTIONS`, |
| 250 | `UBSAN_OPTIONS`, etc. |
| 251 | |
| 252 | ### Submitting your fuzz target |
| 253 | |
| 254 | ClusterFuzz and the build infrastructure automatically discover, build and |
| 255 | execute all `fuzzer_test` targets in the Chromium repository. Once you land your |
| 256 | fuzz target, ClusterFuzz will run it at scale. Check the [ClusterFuzz status] |
| 257 | page after a day or two. |
| 258 | |
| 259 | If you want to better understand and optimize your fuzz target’s performance, |
| 260 | see the [Efficient Fuzzing Guide]. |
| 261 | |
| 262 | *** note |
| 263 | **Note:** It’s important to run fuzzers at scale, not just in your own |
| 264 | environment, because local fuzzing will catch fewer issues. If you run fuzz |
| 265 | targets at scale continuously, you’ll catch regressions and improve code |
| 266 | coverage over time. |
| 267 | *** |
| 268 | |
| 269 | ## Optional improvements |
| 270 | |
| 271 | ### Common tricks |
| 272 | |
| 273 | Your fuzz target may immediately discover interesting (i.e. crashing) inputs. |
| 274 | You can make it more effective with several easy steps: |
| 275 | |
| 276 | * **Create a seed corpus**. You can guide the fuzzing engine to generate more |
| 277 | relevant inputs by adding the `seed_corpus = "src/fuzz-testcases/"` attribute |
| 278 | to your fuzz target and adding example files to the appropriate directory. For |
| 279 | more, see the [Seed Corpus] section of the [Efficient Fuzzing Guide]. |
| 280 | |
| 281 | *** note |
| 282 | **Note:** make sure your corpus files are appropriately licensed. |
| 283 | *** |
| 284 | |
| 285 | * **Create a mutation dictionary**. You can make mutations more effective by |
| 286 | providing the fuzzer with a `dict = "protocol.dict"` GN attribute and a |
| 287 | dictionary file that contains interesting strings / byte sequences for the |
| 288 | target API. For more, see the [Fuzzer Dictionary] section of the [Efficient |
| 289 | Fuzzer Guide]. |
| 290 | |
| 291 | * **Specify testcase length limits**. Long inputs can be problematic, because |
| 292 | they are more slowly processed by the fuzz target and increase the search |
| 293 | space. By default, libFuzzer uses `-max_len=4096` or takes the longest |
| 294 | testcase in the corpus if `-max_len` is not specified. |
| 295 | |
| 296 | ClusterFuzz uses different strategies for different fuzzing sessions, |
| 297 | including different random values. Also, ClusterFuzz uses different fuzzing |
| 298 | engines (e.g. AFL that doesn't have `-max_len` option). If your target has an |
| 299 | input length limit that you would like to *strictly enforce*, add a sanity |
| 300 | check to the beginning of your `LLVMFuzzerTestOneInput` function: |
| 301 | |
| 302 | ```cpp |
| 303 | if (size < kMinInputLength || size > kMaxInputLength) |
| 304 | return 0; |
| 305 | ``` |
| 306 | |
| 307 | * **Generate a [code coverage report]**. See which code the fuzzer covered in |
| 308 | recent runs, so you can gauge whether it hits the important code parts or not. |
| 309 | |
| 310 | **Note:** Since the code coverage of a fuzz target depends heavily on the |
| 311 | corpus provided when running the target, we recommend running the fuzz target |
| 312 | built with ASan locally for a little while (several minutes / hours) first. |
| 313 | This will produce some corpus, which should be used for generating a code |
| 314 | coverage report. |
| 315 | |
| 316 | #### Disabling noisy error message logging |
| 317 | |
| 318 | If the code you’re fuzzing generates a lot of error messages when encountering |
| 319 | incorrect or invalid data, the fuzz target will be slow and inefficient. |
| 320 | |
| 321 | If the target uses Chromium logging APIs, you can silence errors by overriding |
| 322 | the environment used for logging in your fuzz target: |
| 323 | |
| 324 | ```cpp |
| 325 | struct Environment { |
| 326 | Environment() { |
| Peter Boström | 8310f0f | 2024-01-05 15:57:39 | [diff] [blame] | 327 | logging::SetMinLogLevel(logging::LOGGING_FATAL); |
| Adrian Taylor | 6a886ec6 | 2023-10-25 23:45:27 | [diff] [blame] | 328 | } |
| 329 | }; |
| 330 | |
| 331 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| 332 | static Environment env; |
| 333 | |
| 334 | // Put your fuzzing code here and use data+size as input. |
| 335 | return 0; |
| 336 | } |
| 337 | ``` |
| 338 | |
| 339 | ### Mutating Multiple Inputs |
| 340 | |
| 341 | By default, a fuzzing engine such as libFuzzer mutates a single input (`uint8_t* |
| 342 | data, size_t size`). However, APIs often accept multiple arguments of various |
| 343 | types, rather than a single buffer. You can use three different methods to |
| 344 | mutate multiple inputs at once. |
| 345 | |
| 346 | #### libprotobuf-mutator (LPM) |
| 347 | |
| 348 | If you need to mutate multiple inputs of various types and length, see [Getting |
| 349 | Started with libprotobuf-mutator in Chromium]. |
| 350 | |
| 351 | *** note |
| 352 | **Note:** This method works with APIs and data structures of any complexity, but |
| 353 | requires extra effort. You would need to write a `.proto` definition (unless you |
| 354 | fuzz an existing protobuf) and C++ code to pass the proto message to the API you |
| 355 | are fuzzing (you'll have a fuzzed protobuf message instead of `data, size` |
| 356 | buffer). |
| 357 | *** |
| 358 | |
| 359 | #### FuzzedDataProvider (FDP) |
| 360 | |
| 361 | [FuzzedDataProvider] is a class useful for splitting a fuzz input into multiple |
| 362 | parts of various types. |
| 363 | |
| 364 | *** note |
| 365 | **Note:** FDP is much easier to use than LPM, but its downside is that format of |
| 366 | the corpus becomes inconsistent. This doesn't matter if you don't have [Seed |
| 367 | Corpus] (e.g. valid image files if you fuzz an image parser). FDP splits your |
| 368 | corpus files into several pieces to fuzz a broader range of input types, so it |
| 369 | can take longer to reach deeper code paths that surface more quickly if you fuzz |
| 370 | only a single input type. |
| 371 | *** |
| 372 | |
| 373 | To use FDP, add `#include <fuzzer/FuzzedDataProvider.h>` to your fuzz target |
| 374 | source file. |
| 375 | |
| 376 | To learn more about `FuzzedDataProvider`, check out the [upstream documentation] |
| 377 | on it. It gives an overview of the available methods and links to a few example |
| 378 | fuzz targets. |
| 379 | |
| 380 | #### Hash-based argument |
| 381 | |
| 382 | If your API accepts a buffer with data and some integer value (i.e., a bitwise |
| 383 | combination of flags), you can calculate a hash value from (`data, size`) and |
| 384 | use it to fuzz an additional integer argument. For example: |
| 385 | |
| 386 | ```cpp |
| 387 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| 388 | std::string str = std::string(reinterpret_cast<const char*>(data), size); |
| 389 | std::size_t data_hash = std::hash<std::string>()(str); |
| 390 | APIToBeFuzzed(data, size, data_hash); |
| 391 | return 0; |
| 392 | } |
| 393 | |
| 394 | ``` |
| 395 | |
| 396 | *** note |
| 397 | **Note:** The hash method doesn't have the corpus format issue mentioned in the |
| 398 | FDP section above, but it can lead to results that aren't as sophisticated as |
| 399 | LPM or FDP. The hash value derived from the data is a random value, rather than |
| 400 | a meaningful one controlled by the fuzzing engine. A single bit mutation might |
| 401 | lead to a new code coverage, but the next mutation would generate a new hash |
| 402 | value and trigger another code path, without providing any real guidance to the |
| 403 | fuzzing engine. |
| 404 | *** |
| 405 | |
| 406 | [AFL]: AFL_integration.md |
| 407 | [AddressSanitizer]: http://clang.llvm.org/docs/AddressSanitizer.html |
| 408 | [ClusterFuzz status]: libFuzzer_integration.md#Status-Links |
| 409 | [Efficient Fuzzing Guide]: efficient_fuzzing.md |
| 410 | [FuzzedDataProvider]: https://cs.chromium.org/chromium/src/third_party/re2/src/re2/fuzzing/compiler-rt/include/fuzzer/FuzzedDataProvider.h |
| 411 | [Fuzzer Dictionary]: efficient_fuzzing.md#Fuzzer-dictionary |
| 412 | [GN]: https://gn.googlesource.com/gn/+/master/README.md |
| 413 | [GN config]: https://cs.chromium.org/chromium/src/tools/mb/mb_config_expectations/chromium.fuzz.json |
| 414 | [Getting Started with libprotobuf-mutator in Chromium]: libprotobuf-mutator.md |
| 415 | [Integration Reference]: reference.md |
| 416 | [MemorySanitizer]: http://clang.llvm.org/docs/MemorySanitizer.html |
| 417 | [Seed Corpus]: efficient_fuzzing.md#Seed-corpus |
| 418 | [UndefinedBehaviorSanitizer]: http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html |
| 419 | [code coverage report]: efficient_fuzzing.md#Code-coverage |
| 420 | [upstream documentation]: https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#fuzzed-data-provider |
| 421 | [libFuzzer's output documentation]: http://llvm.org/docs/LibFuzzer.html#output |
| Dustin J. Mitchell | 795c736 | 2024-01-18 16:50:52 | [diff] [blame] | 422 | [quic_session_pool_fuzzer.cc]: https://cs.chromium.org/chromium/src/net/quic/quic_session_pool_fuzzer.cc |
| Adrian Taylor | 6a886ec6 | 2023-10-25 23:45:27 | [diff] [blame] | 423 | [getting started guide here]: getting_started.md |