libfuzzer-dotnet integration (#535)

This commit is contained in:
bmc-msft
2021-02-11 17:30:24 -05:00
committed by GitHub
parent 360693e8a4
commit 933fe6850c
10 changed files with 451 additions and 7 deletions

View File

@ -339,6 +339,10 @@ jobs:
(cd trivial-crash ; make )
cp -r trivial-crash/fuzz.exe trivial-crash/seeds artifacts/linux-trivial-crash
mkdir -p artifacts/linux-libfuzzer-dotnet
(cd libfuzzer-dotnet; make)
cp -r libfuzzer-dotnet/my-fuzzer libfuzzer-dotnet/inputs artifacts/linux-libfuzzer-dotnet/
mkdir -p artifacts/linux-trivial-crash-asan
(cd trivial-crash ; make clean; make CFLAGS='-fsanitize=address -fno-omit-frame-pointer')
cp -r trivial-crash/fuzz.exe trivial-crash/seeds artifacts/linux-trivial-crash-asan

View File

@ -0,0 +1,228 @@
# Fuzzing .Net using libfuzzer-dotnet
Fuzzing .Net using libFuzzer makes use of three Open Source capabilities.
1. [SharpFuzz](https://github.com/Metalnem/sharpfuzz), which injects coverage tracking into .Net assembly
2. [libFuzzer](https://www.llvm.org/docs/LibFuzzer.html), which performs the fuzzing
3. [libfuzzer-dotnet](https://github.com/Metalnem/libfuzzer-dotnet), which bridges the SharpFuzz instrumentation into libFuzzer
When using libFuzzer in C, developers provide a function
`LLVMFuzzerTestOneInput` which takes a pointer to a read-only buffer of bytes,
and the length of said buffer. ([Tutorial using libFuzzer in
C](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md))
With libfuzzer-dotnet, developers provide an application that within `Main` calls the method `Fuzzer.LibFuzzer.Run`, with a callback that passes a read only byte-stream their function of interest.
> NOTE: libfuzzer-dotnet only works on Linux at this time.
TL/DR: check out our [libfuzzer-dotnet example](../../src/integration-tests/libfuzzer-dotnet/)
## Supported versions
OneFuzz supports net45 framework or any version that support least
netstandard1.6. Refer to [.Net
Standard](https://dotnet.microsoft.com/platform/dotnet-standard) check if your
framework version is supported.
## Issues using libfuzzer-dotnet in OneFuzz
* The `libfuzzer_coverage` task does not support the coverage features used by libfuzzer-dotnet. (Work item: [#536](https://github.com/microsoft/onefuzz/issues/536))
* The `libfuzzer_crash_report` does not support extracting unique output during analysis, making the crash de-duplication and reporting ineffective. (Work item: [#538]https://github.com/microsoft/onefuzz/issues/538))
As such, a libfuzzer-dotnet template is available, which only uses the `libfuzzer_fuzz` tasks. As these issues are resolve, the template will be updated to include the additional tasks.
## Example
Let's fuzz the `Func` function of our example library named [problems](../../src/integration-tests/libfuzzer-dotnet/problems/).
1. Make sure sharpfuzz and a recent version of clang are installed. We'll need these later.
```
dotnet tool install --global SharpFuzz.CommandLine
sudo apt-get install -y clang
```
2. We need to build an application that uses `Fuzzer.LibFuzzer.Run` that calls our function `Func`. For this example, let's call this [wrapper](../../src/integration-tests/libfuzzer-dotnet/wrapper/)
The [wrapper/wrapper.csproj](../../src/integration-tests/libfuzzer-dotnet/wrapper/wrapper.csproj) project file uses SharpFuzz 1.6.1 and refers to our [problems](../../src/integration-tests/libfuzzer-dotnet/problems/) library locally.
```xml
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\problems\problems.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SharpFuzz" Version="1.6.1" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>
```
For our example [problems](../../src/integration-tests/libfuzzer-dotnet/problems/) library, our callback for `Fuzzer.LibFuzzer.Run` is straight forwards. `Func` already takes a `ReadOnlySpan<byte>`. If your functions takes strings, this would be the place to convert the span of bytes to strings.
[wrapper/program.cs](../../src/integration-tests/libfuzzer-dotnet/wrapper/program.cs)
```C#
using SharpFuzz;
namespace Wrapper {
public class Program {
public static void Main(string[] args) {
Fuzzer.LibFuzzer.Run(stream => { Problems.Problems.Func(stream); });
}
}
}
```
3. Build our [wrapper](../../src/integration-tests/libfuzzer-dotnet/wrapper/)
```
dotnet publish ./wrapper/wrapper.csproj -c release -r linux-x64 -o my-fuzzer
```
> NOTE: Specifying the runtime `linux-x64` is important such that we make a self-contained deployment.
4. Then we need to ensure our [problems](../../src/integration-tests/libfuzzer-dotnet/problems/) library is instrumented:
```
sharpfuzz ./my-fuzzer/problems.dll
```
5. The last thing we need to build before we can start fuzzing is the [libfuzzer-dotnet](https://github.com/Metalnem/libfuzzer-dotnet) harness.
```
curl -o libfuzzer-dotnet.cc https://raw.githubusercontent.com/Metalnem/libfuzzer-dotnet/master/libfuzzer-dotnet.cc
clang -fsanitize=fuzzer libfuzzer-dotnet.cc -o my-fuzzer/libfuzzer-dotnet
```
6. We should provide some sample inputs for our fuzzing. For this example, a basic file will do. However, this should include reasonable known-good inputs for your function. If you're fuzzing PNGs, use a selection of valid PNGs.
```
mkdir -p inputs
echo hi > inputs/hi.txt
```
7. Now we can fuzz!
Execute `libfuzzer-dotnet` with our `wrapper` program and our `inputs` directory:
```
./my-fuzzer/libfuzzer-dotnet --target_path=./my-fuzzer/wrapper ./inputs/
```
In a few seconds, you'll see output that looks something like this:
```
INFO: libFuzzer ignores flags that start with '--'
INFO: Seed: 2909502334
INFO: Loaded 1 modules (58 inline 8-bit counters): 58 [0x4f9090, 0x4f90ca),
INFO: Loaded 1 PC tables (58 PCs): 58 [0x4bfae8,0x4bfe88),
INFO: 65536 Extra Counters
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 8 ft: 10 corp: 1/1b exec/s: 0 rss: 24Mb
#3 NEW cov: 8 ft: 14 corp: 2/5b lim: 4 exec/s: 0 rss: 24Mb L: 4/4 MS: 1 CrossOver-
#36 NEW cov: 8 ft: 16 corp: 3/9b lim: 4 exec/s: 0 rss: 24Mb L: 4/4 MS: 3 EraseBytes-CopyPart-CrossOver-
#337 NEW cov: 8 ft: 18 corp: 4/13b lim: 6 exec/s: 0 rss: 24Mb L: 4/4 MS: 1 CMP- DE: "\x01\x00"-
System.Exception: this is bad
at Problems.Problems.Func(ReadOnlySpan`1 data)
at Wrapper.Program.<>c.<Main>b__0_0(ReadOnlySpan`1 stream) in /home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/wrapper/program.cs:line 5
at SharpFuzz.Fuzzer.LibFuzzer.Run(ReadOnlySpanAction action)
==7346== ERROR: libFuzzer: deadly signal
#0 0x4adf50 in __sanitizer_print_stack_trace (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x4adf50)
#1 0x45a258 in fuzzer::PrintStackTrace() (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x45a258)
#2 0x43f3a3 in fuzzer::Fuzzer::CrashCallback() (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x43f3a3)
#3 0x7fd6c323f3bf (/lib/x86_64-linux-gnu/libpthread.so.0+0x153bf)
#4 0x4aef35 in LLVMFuzzerTestOneInput (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x4aef35)
#5 0x440a61 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x440a61)
#6 0x4401a5 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x4401a5)
#7 0x442447 in fuzzer::Fuzzer::MutateAndTestOne() (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x442447)
#8 0x443145 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x443145)
#9 0x431afe in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x431afe)
#10 0x45a942 in main (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x45a942)
#11 0x7fd6c2ee20b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
#12 0x40689d in _start (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x40689d)
NOTE: libFuzzer has rudimentary signal handlers.
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
MS: 4 ChangeBit-CopyPart-ShuffleBytes-PersAutoDict- DE: "\x01\x00"-; base unit: ae8444de02705346dae4f4c67d0c710b833c14e1
0x0,0x1,0x0,0x0,0xe,0x0,
\x00\x01\x00\x00\x0e\x00
artifact_prefix='./'; Test unit written to ./crash-ad81c382bc24cb4edb13f5ab12ce1ee454600a69
Base64: AAEAAA4A
```
As shown in the output, our fuzzing run generated the file `crash-ad81c382bc24cb4edb13f5ab12ce1ee454600a69`. If we provide this file on the command line, we can reproduce the identified crash:
```
$ ./my-fuzzer/libfuzzer-dotnet --target_path=./my-fuzzer/wrapper ./crash-ad81c382bc24cb4edb13f5ab12ce1ee454600a69
INFO: libFuzzer ignores flags that start with '--'
INFO: Seed: 3044788143
INFO: Loaded 1 modules (58 inline 8-bit counters): 58 [0x4f9090, 0x4f90ca),
INFO: Loaded 1 PC tables (58 PCs): 58 [0x4bfae8,0x4bfe88),
INFO: 65536 Extra Counters
./my-fuzzer/libfuzzer-dotnet: Running 1 inputs 1 time(s) each.
Running: ./crash-ad81c382bc24cb4edb13f5ab12ce1ee454600a69
System.Exception: this is bad
at Problems.Problems.Func(ReadOnlySpan`1 data)
at Wrapper.Program.<>c.<Main>b__0_0(ReadOnlySpan`1 stream) in /home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/wrapper/program.cs:line 5
at SharpFuzz.Fuzzer.LibFuzzer.Run(ReadOnlySpanAction action)
==7882== ERROR: libFuzzer: deadly signal
#0 0x4adf50 in __sanitizer_print_stack_trace (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x4adf50)
#1 0x45a258 in fuzzer::PrintStackTrace() (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x45a258)
#2 0x43f3a3 in fuzzer::Fuzzer::CrashCallback() (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x43f3a3)
#3 0x7f1681d223bf (/lib/x86_64-linux-gnu/libpthread.so.0+0x153bf)
#4 0x4aef35 in LLVMFuzzerTestOneInput (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x4aef35)
#5 0x440a61 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x440a61)
#6 0x42c1d2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x42c1d2)
#7 0x431c86 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x431c86)
#8 0x45a942 in main (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x45a942)
#9 0x7f16819c50b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
#10 0x40689d in _start (/home/bcaswell/projects/onefuzz/onefuzz/src/integration-tests/libfuzzer-dotnet/my-fuzzer/libfuzzer-dotnet+0x40689d)
NOTE: libFuzzer has rudimentary signal handlers.
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
```
> NOTE: The stack shown here is from `libfuzzer-dotnet`, which isn't useful in figuring out the bug in `Problems`. However, fuzzing found a reproducable crash in our `Problems` library. We can use this crashing input file using traditional debug tooling to figure out the underlying problem.
## Launching our example in OneFuzz
These commands launches the a libfuzzer-dotnet focused fuzzing task in OneFuzz. Note, we've added the arguments `--wait_for_running --wait_for_files inputs` such that we can monitor our job until we've seen at least one new input found via fuzzing.
```bash
TARGET_PROJECT=Problems
TARGET_NAME=Func
TARGET_BUILD=1
FUZZ_POOL=linux
onefuzz template libfuzzer dotnet ${TARGET_PROJECT} ${TARGET_NAME} ${TARGET_BUILD} ${FUZZ_POOL} ./my-fuzzer/ wrapper --wait_for_running --wait_for_files inputs
```
When we run this, we'll see output similar to:
```
INFO:onefuzz:creating job (runtime: 24 hours)
INFO:onefuzz:created job: 62d666d3-3373-4094-adbe-e705d722d698
INFO:onefuzz:using container: oft-setup-b8c2890353235ab497b14913fe2ee204
INFO:onefuzz:using container: oft-inputs-68e4cb24b37855f0b0abab9b533d5fc1
INFO:onefuzz:using container: oft-crashes-68e4cb24b37855f0b0abab9b533d5fc1
INFO:onefuzz:uploading setup dir `./my-fuzzer/`
INFO:onefuzz:done creating tasks
\ waiting on: libfuzzer_fuzz:init
| waiting on: libfuzzer_fuzz:scheduled
- waiting on: libfuzzer_fuzz:setting_up
INFO:onefuzz:tasks started
\ waiting for new files: oft-inputs-68e4cb24b37855f0b0abab9b533d5fc1
INFO:onefuzz:new files found
{
"config": {
"build": "1",
"duration": 24,
"name": "Func",
"project": "Problems"
},
"end_time": "2021-02-12T01:52:45+00:00",
"job_id": "62d666d3-3373-4094-adbe-e705d722d698",
"state": "enabled",
"task_info": [
{
"state": "running",
"task_id": "e951e3ee-5097-46be-832d-08fff05507e4",
"type": "libfuzzer_fuzz"
}
],
"user_info": {
"application_id": "5e400594-28e9-4e26-97d8-8ea5bc2ff306",
"object_id": "33041cd0-ad34-4168-a98d-c8e6b4b666cb",
"upn": "example@contoso.com"
}
}
```

View File

@ -3,9 +3,10 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
import os
from typing import Dict, List, Optional
from onefuzztypes.enums import ContainerType, TaskDebugFlag, TaskType
from onefuzztypes.enums import OS, ContainerType, TaskDebugFlag, TaskType
from onefuzztypes.models import Job, NotificationConfig
from onefuzztypes.primitives import Container, Directory, File, PoolName
@ -376,3 +377,114 @@ class Libfuzzer(Command):
self.logger.info("done creating tasks")
helper.wait()
return helper.job
def dotnet(
self,
project: str,
name: str,
build: str,
pool_name: PoolName,
*,
setup_dir: Directory,
target_harness: str,
vm_count: int = 1,
inputs: Optional[Directory] = None,
reboot_after_setup: bool = False,
duration: int = 24,
target_workers: Optional[int] = None,
target_options: Optional[List[str]] = None,
target_env: Optional[Dict[str, str]] = None,
tags: Optional[Dict[str, str]] = None,
wait_for_running: bool = False,
wait_for_files: Optional[List[ContainerType]] = None,
existing_inputs: Optional[Container] = None,
debug: Optional[List[TaskDebugFlag]] = None,
ensemble_sync_delay: Optional[int] = None,
check_fuzzer_help: bool = True,
expect_crash_on_failure: bool = True,
) -> Optional[Job]:
"""
libfuzzer-dotnet task
"""
harness = "libfuzzer-dotnet"
pool = self.onefuzz.pools.get(pool_name)
if pool.os != OS.linux:
raise Exception("libfuzzer-dotnet jobs are only compatable on linux")
target_exe = File(os.path.join(setup_dir, harness))
if not os.path.exists(target_exe):
raise Exception(f"missing harness: {target_exe}")
assembly_path = os.path.join(setup_dir, target_harness)
if not os.path.exists(assembly_path):
raise Exception(f"missing assembly: {assembly_path}")
self._check_is_libfuzzer(target_exe)
if target_options is None:
target_options = []
target_options = [
"--target_path={setup_dir}/" f"{target_harness}"
] + target_options
helper = JobHelper(
self.onefuzz,
self.logger,
project,
name,
build,
duration,
pool_name=pool_name,
target_exe=target_exe,
)
helper.add_tags(tags)
helper.define_containers(
ContainerType.setup,
ContainerType.inputs,
ContainerType.crashes,
)
if existing_inputs:
self.onefuzz.containers.get(existing_inputs)
helper.containers[ContainerType.inputs] = existing_inputs
else:
helper.define_containers(ContainerType.inputs)
fuzzer_containers = [
(ContainerType.setup, helper.containers[ContainerType.setup]),
(ContainerType.crashes, helper.containers[ContainerType.crashes]),
(ContainerType.inputs, helper.containers[ContainerType.inputs]),
]
helper.create_containers()
helper.upload_setup(setup_dir, target_exe)
if inputs:
helper.upload_inputs(inputs)
helper.wait_on(wait_for_files, wait_for_running)
self.onefuzz.tasks.create(
helper.job.job_id,
TaskType.libfuzzer_fuzz,
harness,
fuzzer_containers,
pool_name=pool_name,
reboot_after_setup=reboot_after_setup,
duration=duration,
vm_count=vm_count,
target_options=target_options,
target_env=target_env,
target_workers=target_workers,
tags=tags,
debug=debug,
ensemble_sync_delay=ensemble_sync_delay,
check_fuzzer_help=check_fuzzer_help,
expect_crash_on_failure=expect_crash_on_failure,
)
self.logger.info("done creating tasks")
helper.wait()
return helper.job

View File

@ -31,7 +31,7 @@ from onefuzz.backend import ContainerWrapper, wait
from onefuzz.cli import execute_api
from onefuzztypes.enums import OS, ContainerType, TaskState, VmState
from onefuzztypes.models import Job, Pool, Repro, Scaleset
from onefuzztypes.primitives import Directory, File
from onefuzztypes.primitives import Container, Directory, File, PoolName, Region
from pydantic import BaseModel, Field
LINUX_POOL = "linux-test"
@ -41,6 +41,7 @@ BUILD = "0"
class TemplateType(Enum):
libfuzzer = "libfuzzer"
libfuzzer_dotnet = "libfuzzer_dotnet"
afl = "afl"
radamsa = "radamsa"
@ -51,6 +52,7 @@ class Integration(BaseModel):
target_exe: str
inputs: Optional[str]
use_setup: bool = Field(default=False)
nested_setup_dir: Optional[str]
wait_for_files: List[ContainerType]
check_asan_log: Optional[bool] = Field(default=False)
disable_check_debugger: Optional[bool] = Field(default=False)
@ -73,6 +75,15 @@ TARGETS: Dict[str, Integration] = {
wait_for_files=[ContainerType.unique_reports, ContainerType.coverage],
reboot_after_setup=True,
),
"linux-libfuzzer-dotnet": Integration(
template=TemplateType.libfuzzer_dotnet,
os=OS.linux,
target_exe="wrapper",
nested_setup_dir="my-fuzzer",
inputs="inputs",
use_setup=True,
wait_for_files=[ContainerType.inputs, ContainerType.crashes],
),
"linux-libfuzzer-rust": Integration(
template=TemplateType.libfuzzer,
os=OS.linux,
@ -159,7 +170,7 @@ class TestOnefuzz:
def setup(
self,
*,
region: Optional[str] = None,
region: Optional[Region] = None,
user_pools: Optional[Dict[str, str]] = None,
) -> None:
for entry in self.os:
@ -169,7 +180,7 @@ class TestOnefuzz:
)
self.pools[entry] = self.of.pools.get(user_pools[entry.name])
else:
name = "pool-%s-%s" % (self.project, entry.name)
name = PoolName("pool-%s-%s" % (self.project, entry.name))
self.logger.info("creating pool: %s:%s", entry.name, name)
self.pools[entry] = self.of.pools.create(name, entry)
self.logger.info("creating scaleset for pool: %s", name)
@ -195,6 +206,9 @@ class TestOnefuzz:
else None
)
if setup and config.nested_setup_dir:
setup = Directory(os.path.join(setup, config.nested_setup_dir))
job: Optional[Job] = None
if config.template == TemplateType.libfuzzer:
job = self.of.template.libfuzzer.basic(
@ -207,7 +221,21 @@ class TestOnefuzz:
setup_dir=setup,
duration=1,
vm_count=1,
reboot_after_setup=config.reboot_after_setup,
reboot_after_setup=config.reboot_after_setup or False,
)
elif config.template == TemplateType.libfuzzer_dotnet:
if setup is None:
raise Exception("setup required for libfuzzer_dotnet")
job = self.of.template.libfuzzer.dotnet(
self.project,
target,
BUILD,
self.pools[config.os].name,
target_harness=config.target_exe,
inputs=inputs,
setup_dir=setup,
duration=1,
vm_count=1,
)
elif config.template == TemplateType.radamsa:
job = self.of.template.radamsa.basic(
@ -362,7 +390,7 @@ class TestOnefuzz:
self.logger.info("checking jobs")
return wait(self.check_jobs_impl)
def get_job_crash(self, job_id: UUID) -> Optional[Tuple[str, str]]:
def get_job_crash(self, job_id: UUID) -> Optional[Tuple[Container, str]]:
# get the crash container for a given job
for task in self.of.tasks.list(job_id=job_id, state=None):
@ -396,6 +424,7 @@ class TestOnefuzz:
self.logger.info("launching repro: %s", self.target_jobs[job_id])
report = self.get_job_crash(job_id)
if report is None:
self.logger.warning("target does not include crash reports: %s", self.target_jobs[job_id])
return
(container, path) = report
self.repros[job_id] = self.of.repro.create(container, path, duration=1)
@ -555,7 +584,7 @@ class Run(Command):
endpoint: Optional[str] = None,
user_pools: Optional[Dict[str, str]] = None,
pool_size: int = 10,
region: Optional[str] = None,
region: Optional[Region] = None,
os_list: List[OS] = [OS.linux, OS.windows],
targets: List[str] = list(TARGETS.keys()),
skip_repro: bool = False,
@ -580,6 +609,7 @@ class Run(Command):
if skip_repro:
self.logger.warning("not testing crash repro")
else:
self.logger.info("launching crash repro tests")
tester.launch_repro()
tester.check_repro()
except Exception as e:

View File

@ -0,0 +1,24 @@
all: check
libfuzzer-dotnet:
mkdir -p my-fuzzer
# direct url to a known-good instance of libfuzzer-dotnet.cc
curl -o libfuzzer-dotnet.cc https://raw.githubusercontent.com/Metalnem/libfuzzer-dotnet/543b170a67bdd39e9ba260fe54bc93c77b877c24/libfuzzer-dotnet.cc
clang -fsanitize=fuzzer libfuzzer-dotnet.cc -o my-fuzzer/libfuzzer-dotnet
rm -f libfuzzer-dotnet.cc
build-harness: libfuzzer-dotnet
dotnet tool install --global SharpFuzz.CommandLine || echo already installed
dotnet publish ./wrapper/wrapper.csproj -c release -o my-fuzzer -r linux-x64
sharpfuzz my-fuzzer/problems.dll || echo already instrumented
check: build-harness
./my-fuzzer/libfuzzer-dotnet --target_path=./my-fuzzer/wrapper -runs=1
fuzz: check
./my-fuzzer/libfuzzer-dotnet --target_path=./my-fuzzer/wrapper
.PHONY: clean
clean:
rm -rf fuzz.exe libfuzzer-dotnet my-fuzzer wrapper/bin wrapper/obj problems/bin problems/obj

View File

@ -0,0 +1 @@
hi

View File

@ -0,0 +1,15 @@
using System;
namespace Problems {
public static class Problems {
public static void Func(ReadOnlySpan<byte> data) {
var count = 0;
if (data.Length < 4) {
return;
}
if (data[0] == 0) { count++; }
if (data[1] == 1) { count++; }
if (data[2] == 2) { count++; }
if (count >= 3) { throw new Exception("this is bad"); }
}
}
}

View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,8 @@
using SharpFuzz;
namespace Wrapper {
public class Program {
public static void Main(string[] args) {
Fuzzer.LibFuzzer.Run(stream => { Problems.Problems.Func(stream); });
}
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\problems\problems.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SharpFuzz" Version="1.6.1" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>