mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-17 12:28:07 +00:00
libfuzzer-dotnet integration (#535)
This commit is contained in:
@ -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:
|
||||
|
24
src/integration-tests/libfuzzer-dotnet/Makefile
Normal file
24
src/integration-tests/libfuzzer-dotnet/Makefile
Normal 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
|
1
src/integration-tests/libfuzzer-dotnet/inputs/hi.txt
Normal file
1
src/integration-tests/libfuzzer-dotnet/inputs/hi.txt
Normal file
@ -0,0 +1 @@
|
||||
hi
|
15
src/integration-tests/libfuzzer-dotnet/problems/problems.cs
Normal file
15
src/integration-tests/libfuzzer-dotnet/problems/problems.cs
Normal 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"); }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
@ -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); });
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
Reference in New Issue
Block a user