fuzzing

作者: microsoft

运行、优化和调试OpenVMM模糊测试器。涵盖cargo-fuzz目标、崩溃复现、lldb调试、代码覆盖率分析、熵优化及…

npx skills add https://github.com/microsoft/openvmm --skill fuzzing

OpenVMM Fuzzer Guide

Prerequisites

  • Linux only — libfuzzer-sys doesn't support Windows.
  • cargo-fuzz: cargo install cargo-fuzz
  • lldb (for debugging): sudo apt-get install -y lldb
  • aarch64 RUSTFLAGS: On aarch64, set RUSTFLAGS="-Ctarget-feature=+lse,+neon" or builds fail with atomics errors. Not needed on x86_64.

Fuzz targets

Fuzz targets live in <crate>/fuzz/ directories alongside the crate they test. Each has a Cargo.toml with cargo-fuzz = true metadata and a fuzz_<name>.rs binary. Find all targets:

cargo xtask fuzz list

Running a fuzzer

# Continuous fuzzing (finds new crashes)
cargo xtask fuzz run fuzz_ide

# Reproduce a specific crash artifact
cargo xtask fuzz run fuzz_ide path/to/crash-artifact

The xtask wrapper sets XTASK_FUZZ_REPRO=1 automatically when an artifact path is provided, which enables init_tracing_if_repro() in the fuzz target.

Build without running

cargo xtask fuzz build fuzz_ide

The binary lands at target/<triple>/release/fuzz_ide (e.g., target/aarch64-unknown-linux-gnu/release/fuzz_ide or target/x86_64-unknown-linux-gnu/release/fuzz_ide).

Reproducing a crash (direct binary)

Running the binary directly is faster for iteration than going through xtask:

# Without tracing (fast, quiet)
./target/<triple>/release/fuzz_ide path/to/crash-artifact

# With tracing (verbose — shows device state at each poll)
XTASK_FUZZ_REPRO=1 ./target/<triple>/release/fuzz_ide path/to/crash-artifact

# With backtrace
RUST_BACKTRACE=full ./target/<triple>/release/fuzz_ide path/to/crash-artifact

Debugging with lldb

Key lessons

  1. Use batch mode with a command file — lldb's interactive prompt doesn't work well from automated terminals. Write commands to a file and use lldb -b -s <file>.
  2. Set auto-confirm true — prevents lldb from blocking on "Do you really want to quit?" prompts.
  3. Breakpoint on panic_fmt — rust_begin_unwind is often mangled and won't resolve. Use -r panic_fmt instead.
  4. Drop XTASK_FUZZ_REPRO when using lldb — the tracing output from repeated polls produces megabytes of spam that buries the debugger output.
  5. Release builds have limited variable info — frame variable may show nothing. For richer inspection, build with --dev (see below).

Command file template

Create a file (e.g., scratch/lldb_fuzz.cmd):

settings set auto-confirm true
breakpoint set -r panic_fmt
run
bt 20
frame select 1
source list
frame select 2
source list
frame variable
quit

Run it

lldb -b -s scratch/lldb_fuzz.cmd \
  -- ./target/<triple>/release/fuzz_ide \
  path/to/crash-artifact 2>&1 | tail -80

The | tail -80 trims libfuzzer startup noise. Adjust as needed.

Using rust-lldb

rust-lldb adds Rust pretty-printers for Vec, String, etc. It requires lldb to be installed. Usage is the same but replace lldb with rust-lldb:

rust-lldb -b -s scratch/lldb_fuzz.cmd \
  -- ./target/<triple>/release/fuzz_ide \
  path/to/crash-artifact 2>&1 | tail -80

Debug builds for variable inspection

Release builds have limited frame variable output. For full variable inspection, build with --dev:

cargo xtask fuzz build fuzz_ide -- --dev

The debug binary lands at target/<triple>/debug/fuzz_ide. It's slower but lldb can inspect all locals and struct fields.

Navigating Rust wrapper types in lldb

Reaching the actual data through Arc, Mutex, UnsafeCell etc. requires knowing the field path. Use type lookup to discover struct layouts:

(lldb) type lookup closeable_mutex::CloseableMutex<ide::IdeDevice>

Common wrapper-type traversal patterns:

Rust typelldb field path
Arc<T>var.ptr.pointer->data (through ArcInner)
CloseableMutex<T>.value.value (UnsafeCell inside Mutex)
UnsafeCell<T>.value
Option<T>.$variants$.$variant$.value.__0 (check $discr$ — 0 = None for most types)
Rust enum.$variants$.$variant$N.value.__0 (N = variant index)

Full path example for IDE device state through Arc<CloseableMutex<IdeDevice>>:

(lldb) expr ide_device.ptr.pointer->data.value.value.channels[0].state
(lldb) expr ide_device.ptr.pointer->data.value.value.channels[0].enlightened_write
(lldb) expr ide_device.ptr.pointer->data.value.value.channels[0].bus_master_state

For drive registers inside Option<DiskDrive> containing an enum variant:

(lldb) expr ide_device.ptr.pointer->data.value.value.channels[0].drives[0].$variants$.$variant$.value.__0.$variants$.$variant$.value.__0.state.regs

Setting breakpoint ignore counts

To skip the first N hits of a breakpoint (useful when a function is called hundreds of times during polling):

(lldb) breakpoint set -f lib.rs -l 1253
(lldb) breakpoint modify -i 500 1
(lldb) run

This skips the first 500 hits, then stops on hit 501.

Reading Rust bitfield status registers

Status register values appear as (__0 = 'X') where 'X' is the char at that byte value. Decode manually: 0x58 = bits 3,4,6 set = drq, dsc, drdy.

Minimizing crash inputs

cargo xtask fuzz tmin fuzz_ide path/to/crash-artifact

Corpus management

# Minimize the corpus (remove redundant inputs)
cargo xtask fuzz cmin fuzz_ide

Corpus files live in <crate>/fuzz/corpus/<target>/. Crash artifacts land in <crate>/fuzz/artifacts/<target>/.

Fuzzer optimization principles

Entropy efficiency

Every byte of fuzzer input should drive meaningful behavior. Common wastes:

  • Arbitrary setup parameters that don't affect coverage — e.g., guest memory size, buffer counts. Use fixed values.
  • u.ratio() calls for branching — consumes entropy for each decision. Use Arbitrary enums instead (1 byte per decision vs 8+ for ratio).
  • Redundant arbitrary_data() calls — generate composite values with one call instead of field-by-field. E.g., a Guid from [u8; 16] instead of 11 separate calls.

Simplification

  • Merge duplicated functions that only differ in a small parameter. Use an enum variant to select behavior.
  • Remove dead protocol negotiation — if a device rejects all interesting operations without init, always negotiate. Raw packet paths can still exercise init state machines through mutation.
  • Remove wrapper functions that add no value over calling the underlying API directly.

Coverage gaps

  • 0-item/empty configurations — fuzzers that always attach 1+ disks/devices miss the empty-controller code paths. Use 0..=N ranges.
  • Hardcoded setup parameters can hide code paths. E.g., fixed namespace IDs mean only one NVMe identify-namespace path is tested. Make setup params fuzzer-driven where they affect behavior.
  • TODOs mark legitimate gaps — don't remove TODOs unless you've verified the marked code path is either (a) implemented or (b) genuinely doesn't affect coverage.

Performance

  • Smaller backing stores — RAM disks used for fuzzing don't need to be large. 32KB is sufficient for most device fuzzers (was 4MB).
  • Fixed guest memory — varying guest memory size wastes entropy without affecting device logic.
  • Always negotiate protocols — skipping negotiation wastes 100% of remaining fuzz actions on a single error branch.
  • -fork=N for parallel fuzzing across CPUs. Allocate cores proportionally to fuzzer complexity.

Multi-target parallel fuzzing

Run multiple fuzzers across available CPUs for extended campaigns:

# 6-hour campaign across 112 cores, 7 fuzzers
# Each runs with -fork=N, survives crashes/timeouts/OOMs
nohup bash -c 'RUSTFLAGS="-Ctarget-feature=+lse,+neon" \
  cargo xtask fuzz run fuzz_storvsp -- -- \
  -fork=20 -max_total_time=21600 \
  -ignore_crashes=1 -ignore_timeouts=1 -ignore_ooms=1 \
  -print_final_stats=1' > /tmp/fuzz_storvsp.log 2>&1 &

Allocate cores based on fuzzer complexity:

  • Heavy fuzzers (storvsp, ide, nvme, nvram): 20 cores each
  • Medium fuzzers (cmos_rtc): 15 cores
  • Light fuzzers (diagnostics, scsi_buffers): 7–10 cores

Monitor progress: tail -1 /tmp/fuzz_*.log

After the run, collect coverage on each fuzzer for gap analysis.

Code coverage

Prerequisites

Everything from the main prerequisites, plus:

  • llvm-tools: rustup component add llvm-tools
  • lcov (for HTML reports): sudo apt-get install -y lcov

Collecting coverage

The xtask has built-in coverage support. It runs each corpus entry through a coverage-instrumented build and merges the results:

# Collect coverage data only
cargo xtask fuzz coverage fuzz_ide

# Collect + generate HTML report (requires lcov + genhtml)
cargo xtask fuzz coverage fuzz_ide --with-html-report

# Skip rebuild, just regenerate report from existing profdata
cargo xtask fuzz coverage fuzz_ide --with-html-report --only-report

On aarch64, set RUSTFLAGS="-Ctarget-feature=+lse,+neon" as usual.

Coverage artifacts land in:

ArtifactPath
profdata<crate>/fuzz/coverage/<target>/coverage.profdata
HTML reporttarget/<triple>/coverage/<triple>/release/lcov_html_<target>/index.html

Filtering coverage to relevant crates

The raw HTML report covers all compiled code including dependencies — the overall percentage is meaningless (typically 4–6%). What matters is coverage of the target crate itself and its immediate domain dependencies.

Use llvm-cov report with source path filtering:

# Find the llvm-cov binary from the toolchain
LLVM_COV=$(find $(rustc --print sysroot) -name "llvm-cov" -type f | head -1)

# Full per-file report, excluding third-party code
$LLVM_COV report \
  -instr-profile <crate>/fuzz/coverage/<target>/coverage.profdata \
  -object target/<triple>/coverage/<triple>/release/<target> \
  --ignore-filename-regex='(rustc|\.cargo|registry)' 2>&1 \
  | grep 'vm/devices/storage/ide/src/'

For lcov-based per-file analysis, export to lcov and extract per-file stats:

$LLVM_COV export \
  -instr-profile <crate>/fuzz/coverage/<target>/coverage.profdata \
  -object target/<triple>/coverage/<triple>/release/<target> \
  --ignore-filename-regex='(rustc|\.cargo|registry)' \
  -format=lcov > /tmp/coverage.lcov

# Per-file function/line summary from lcov
awk '/SF:.*ide\/src\/lib\.rs$/,/end_of_record/' /tmp/coverage.lcov \
  | grep -E '^(FNF|FNH|LF|LH):'
# FNF/FNH = functions found/hit, LF/LH = lines found/hit

# Find uncovered line numbers
awk '/SF:.*ide\/src\/lib\.rs$/,/end_of_record/' /tmp/coverage.lcov \
  | grep '^DA:' | grep ',0$' | cut -d: -f2 | cut -d, -f1

Coverage scope rules

Each fuzzer has a primary scope (the crate it exists to test) and secondary scope (dependencies it exercises incidentally). Coverage improvements should focus on primary scope first.

What coverage matters per fuzzer

FuzzerPrimary scopeSecondary scopeOut of scope
fuzz_ideide/src/scsidisk/src/scsidvd/, scsidisk/src/atapi_scsi.rs, disk_backend/, disklayer_ram/pci_core/, vmcore/, guestmem/ (infrastructure)
fuzz_storvspstorvsp/src/scsidisk/src/lib.rs, vmbus_ring/, vmbus_async/, disk_backend/vmbus_channel/, vmbus_core/ (infrastructure)
fuzz_nvme_drivernvme_driver/src/nvme/src/, nvme_spec/, user_driver/page_pool_alloc/, vmcore/
fuzz_firmware_uefi_nvramfirmware_uefi/src/service/nvram/uefi_nvram_specvars/crypto, openssl (infrastructure)
fuzz_firmware_uefi_diagnosticsfirmware_uefi/src/service/diagnostics/—guestmem/
fuzz_chipset_cmos_rtcchipset/src/cmos_rtc/chipset_device_fuzz/pal_async/, vmcore/
fuzz_scsi_buffersscsi_buffers/src/—guestmem/
fuzz_chipset_batterychipset/src/battery/chipset_device_fuzz/—

Rules:

  1. Primary scope is the goal. Line coverage of the target crate is the metric to optimize. The fuzzer exists to find bugs in this code.

  2. Secondary scope matters when it's a trust boundary. scsidisk parsing guest-supplied SCSI commands is a real attack surface — coverage there has value. But disklayer_ram just storing bytes is infrastructure.

  3. Out-of-scope code is noise. pci_core, vmcore, guestmem get exercised incidentally. Don't optimize the fuzzer to improve their coverage — those crates deserve their own fuzzers if coverage matters.

  4. Save/restore and resolver code is structurally unreachable. Functions like save_restore.rs, resolver.rs won't be hit by fuzzing because the fuzzer doesn't exercise serialization or resource resolution paths. Don't count them against coverage targets.

  5. Inspect (InspectMut) implementations are low-value. These debug/ diagnostics paths aren't part of the attack surface. Low coverage is acceptable.

Coverage-guided fuzzer improvement process

Step 1: Collect and filter

# Run coverage
cargo xtask fuzz coverage <target> --with-html-report

# Open the HTML report for visual inspection
# target/<triple>/coverage/<triple>/release/lcov_html_<target>/index.html

Step 2: Identify gaps in primary scope

Look at the HTML report or extract uncovered lines:

awk '/SF:.*<crate>\/src\/lib\.rs$/,/end_of_record/' /tmp/coverage.lcov \
  | grep '^DA:' | grep ',0$' | cut -d: -f2 | cut -d, -f1

Read the source at those lines. Classify each uncovered region:

ClassificationAction
Reachable via fuzzer input — the fuzzer could reach this code but hasn't found the right input sequenceInvestigate why. Is the path gated by specific byte patterns the fuzzer struggles to generate?
Structurally unreachable — the fuzzer harness doesn't wire up the code path (e.g., save/restore, PCI config reads the harness skips)Extend the harness: add new FuzzAction variants, expose more device interfaces to the fuzzer.
Error-handling / defensive code — unreachable!(), error paths for invalid hardware statesLow priority. These are worth testing but don't indicate a fuzzer deficiency.
Inspect / debug only — InspectMut, Display, Debug implsSkip entirely. Not attack surface.

Step 3: Improve the fuzzer

Improvements fall into two categories, in priority order:

A. Extend the fuzz harness

If an entire code path is structurally unreachable, the harness needs changes. Examples:

  • fuzz_ide doesn't exercise PCI config space reads → add PCI config read/write to the FuzzAction enum in chipset_device_fuzz
  • fuzz_storvsp doesn't test sub-channel operations → add corresponding VMBus channel actions

B. Improve Arbitrary implementations

If the fuzzer generates mostly one type of operation, check the Arbitrary impl. Common issues:

  • Uniform distribution across action types wastes entropy on less interesting paths. Weight toward rarer operations.
  • Overly constrained generation (e.g., only valid SCSI commands) misses malformed-input bugs.
  • Overly unconstrained generation (random bytes everywhere) wastes time on inputs that fail early validation and never reach deep code.

Step 4: Measure improvement

After making changes:

  1. Run the fuzzer for a fixed duration (e.g., 1 hour)
  2. Minimize the corpus: cargo xtask fuzz cmin <target>
  3. Collect coverage again
  4. Compare line counts against prior coverage runs

Track coverage changes over time. Ratchet up: if a change reduces primary scope coverage, investigate why.

Step 5: Iterate

Coverage-guided improvement is iterative. Each cycle:

  1. Collect → 2. Identify gaps → 3. Fix → 4. Fuzz → 5. Measure → repeat

Target ≥85% line coverage in primary scope as a practical goal. The remaining ~15% is typically error-handling code, debug impls, and paths requiring specific hardware/timing conditions the fuzzer can't easily produce.

来自 microsoft 的更多技能

oss-growth
microsoft
OSS增长黑客角色
agent-framework-azure-ai-py
microsoft
使用Microsoft Agent Framework Python SDK(agent-framework-azure-ai)构建Azure AI Foundry代理。在创建使用AzureAIAgentsProvider的持久化代理、使用托管工具(代码解释器、文件搜索、网络搜索)、集成MCP服务器、管理对话线程或实现流式响应时使用。涵盖函数工具、结构化输出和多工具代理。
development
airunway-aks-setup
microsoft
在AKS上设置AI Runway——从裸集群到运行模型。涵盖集群验证、控制器安装、GPU评估、提供商设置和首次部署。适用场景:“设置AI Runway”、“接入AKS集群”、“安装AI Runway”、“airunway设置”、“将模型部署到AKS”、“在AKS上进行GPU推理”、“在AKS上配置KAITO”、“在AKS上运行LLM”、“在AKS上使用vLLM”、“在AKS上设置模型服务”、“AI Runway控制器”。
devops
appinsights-instrumentation
microsoft
使用Azure Application Insights对Web应用进行插桩的指南。提供遥测模式、SDK设置和配置参考。适用场景:如何对应用进行插桩、App Insights SDK、遥测模式、什么是App Insights、Application Insights指南、插桩示例、APM最佳实践。
devops
applicationinsights-web-ts
microsoft
使用Application Insights JavaScript SDK(@microsoft/applicationinsights-web)为浏览器/Web应用添加检测。用于真实用户监控(RUM)——页面视图、点击、AJAX/fetch依赖项、异常、自定义事件,以及与后端OpenTelemetry追踪关联的浏览器端GenAI代理追踪。涵盖SDK加载器脚本和npm设置、框架扩展(React、React Native、Angular)、点击分析、遥测初始化器,以及从浏览器发出的代理/工具/模型跨度所遵循的OTel GenAI语义约定。
devops
azure-ai-anomalydetector-java
microsoft
使用适用于 Java 的 Azure AI 异常检测器 SDK 构建异常检测应用程序。在实现单变量/多变量异常检测、时间序列分析或 AI 驱动的监控时使用。
development
azure-ai-language-conversations-py
microsoft
使用azure-ai-language-conversations Python SDK实现对话语言理解(CLU)。当使用ConversationAnalysisClient分析对话意图和实体、构建NLP功能或将语言理解集成到应用程序中时使用。
development
azure-ai-ml-py
microsoft
Azure Machine Learning SDK v2 for Python。用于机器学习工作区、作业、模型、数据集、计算资源和管道。 触发词:“azure-ai-ml”、“MLClient”、“工作区”、“模型注册表”、“训练作业”、“数据集”。
development