microsoft / microsoft/openvmm

Add fuzzing coverage for the mesh stack

Open
#2,926 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement testing
Dominant language
Rust
Stars
1.9k
Forks
238
Avg merge
1d 15h
Merged PRs (30d)
100

Description

Add fuzzing coverage for the mesh stack

Summary

In sync, we landed on a staged fuzzing plan for mesh.

I want to build this on top of the fuzzing infrastructure that already exists in the repo instead of inventing something new. The priority order should be:

  • mesh_protobuf first
  • mesh_channel_core next
  • mesh_remote as a follow-on
  • mesh_node out of scope unless later work turns up a concrete reason to pull it in

That gives us a path that is both practical and high-value.


Why this is worth doing

The repo already has a real fuzzing story:

  • docs at Guide/src/dev_guide/tests/fuzzing.md
  • repo integration in xtask/src/tasks/fuzz/mod.rs
  • an existing mesh-adjacent example in support/mesh/mesh_rpc/fuzz/fuzz_mesh_ttrpc_server.rs

What we do not have today is dedicated fuzz coverage for the core mesh crates themselves.

That leaves a pretty obvious gap:

  • mesh_protobuf is the low-level serialization / deserialization layer and explicitly supports transferring resources
  • mesh_channel_core owns the core channel machinery, including the failure and lifecycle paths that unit tests usually under-cover
  • mesh_remote owns the cross-process transport layer and still contains platform-facing unsafe code
  • mesh_node looks lower-risk right now and should not block the rest

I don’t think this needs to be sold as “fuzz all of mesh at once.” The right framing is: start with the highest-value, easiest-to-land targets and stage the rest.


Proposed scope by crate

1. mesh_protobuf

This should be the first landing.

mesh_protobuf lives at support/mesh/mesh_protobuf/ and is the low-level encoding layer behind the mesh derive story. Its src/lib.rs explicitly describes it as the low-level serialization and deserialization layer for mesh messages, including external resources.

The direction here is to add fuzzing for as many generated type patterns as possible, not just one hand-written message type.

That means I want coverage for representative derived shapes such as:

  • scalars and scalar-heavy structs
  • enums / oneof-style patterns
  • nested structs
  • vectors and other collection-like shapes
  • resource-carrying messages
  • wrapper / transparent-style types

I also want both sides of the problem covered:

  • malformed input decode
  • encode/decode roundtrip behavior for representative generated types

This is the cleanest place to start because it has a well-defined boundary, it is already a serialization layer, and it gives us immediate value without needing to solve the harder transport story first.


2. mesh_channel_core

This should be the second landing.

mesh_channel_core lives at support/mesh/mesh_channel_core/ and is the core channel implementation underneath the higher-level mesh channel APIs.

The important part here is not “send some messages and see if they arrive.” We already know how to write happy-path unit tests for that. The value is in fuzzing the weird lifecycle and failure paths:

  • send / recv / close / drop sequencing
  • cancellation paths
  • closed-channel behavior
  • bounded / edge conditions
  • oneshot lifecycle edges
  • other state-machine paths that are easy to miss with example-based tests

This is where I want operation-sequence or state-machine-style harnesses rather than a single simple byte-to-parser fuzzer.

If mesh_protobuf is the cleanest serialization target, mesh_channel_core is the highest-value “make sure the failure paths are actually robust” target.


3. mesh_remote

This should be a follow-on, not the thing that blocks the first landing.

mesh_remote lives at support/mesh/mesh_remote/ and owns the cross-process transport side of mesh. That is also where the remaining platform-facing unsafe code is concentrated today, especially in the Unix and ALPC implementations.

I do want fuzzing here, but I think the right direction is functional fuzzing, not just hammering raw OS glue in place.

So the plan should be:

  • identify a good functional / protocol-level fuzz surface
  • peel the remaining unsafe transport-specific pieces into a smaller helper crate or boundary where that makes sense
  • then fuzz the higher-level remote behavior against malformed messages, bad sequencing, and transport edge cases

I do not want to block mesh_protobuf and mesh_channel_core on solving all of this first.

In other words: mesh_remote belongs in the plan, but it should be staged after the first two crates, and part of the work here is making the unsafe surface smaller and more explicit before we fuzz across it.


4. mesh_node

mesh_node lives at support/mesh/mesh_node/.

Right now this looks fine to leave out of the initial scope. I don’t want to block the work on a crate that does not look like the highest-payoff fuzz target today.

If later work turns up a specific parser-like surface, unsafe boundary, or failure mode that deserves fuzzing, we can open that up then. For now, I think “should be OK” is the right call.


Why I want this staged

I don’t want this to turn into an all-or-nothing fuzzing project.

A reasonable path is:

  1. land mesh_protobuf fuzzing first
  2. add mesh_channel_core fuzzing next
  3. tackle mesh_remote functional fuzzing after that, along with the unsafe-boundary cleanup that makes it tractable
  4. keep mesh_node out unless we find a reason to revisit it

That gives us real coverage quickly, keeps the issue actionable, and avoids making the transport cleanup a blocker for the first win.


Goals

  • Add real fuzz targets for the highest-value mesh crates using the existing repo fuzzing infrastructure
  • Cover as many representative generated-type patterns as practical in mesh_protobuf
  • Exercise failure, cancellation, close, and lifecycle paths in mesh_channel_core
  • Create a credible path to functional fuzzing in mesh_remote
  • Keep the work incremental enough that we can land it in stages instead of waiting for one giant change

Non-goals

  • Fuzz every mesh crate in the first pass
  • Block on mesh_node
  • Solve all mesh_remote unsafe refactoring before we land any mesh fuzzing at all
  • Turn this into a huge PR-time fuzzing requirement for every change

Validation

For this effort to be successful, I want the new targets to:

  • fit into the existing xtask fuzz / cargo-fuzz flow
  • build and run locally
  • be reproducible with saved repro inputs
  • come with seeded corpora for obvious edge cases
  • be in a shape that can later feed longer-running fuzz jobs

Concretely:

  • mesh_protobuf should tolerate arbitrary input and representative generated-type roundtrips without panics or other obvious unsound behavior
  • mesh_channel_core should survive weird lifecycle orderings and failure-path operation sequences without panics or other obvious unsound behavior
  • mesh_remote should eventually have at least one functional fuzzing surface after we tighten the unsafe boundary enough to make that a good investment

Rough implementation plan

  1. Add a new fuzz crate under support/mesh/mesh_protobuf/fuzz/, modeled after the existing in-tree fuzz crates.
  2. Add one or more mesh_protobuf targets that cover malformed input decode and representative generated type patterns.
  3. Add support/mesh/mesh_channel_core/fuzz/ with operation-sequence / state-machine harnesses for mpsc and oneshot behavior.
  4. Audit mesh_remote to identify the best functional fuzz surface and the unsafe transport-specific pieces that should move behind a smaller boundary.
  5. Add at least one mesh_remote functional fuzz target as a follow-on.
  6. Revisit mesh_node only if the earlier work gives us a concrete reason to do so.

Open questions

  • Do we want one broad mesh_protobuf target or several smaller targets by generated-pattern family?
  • How much concurrency do we want in the first mesh_channel_core harness versus a simpler single-threaded operation model?
  • What is the right boundary for shrinking mesh_remote unsafe code before fuzzing: helper module, separate crate, or just tighter isolation inside the existing crate?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with Guide/src/dev_guide/tests/fuzzing.md and xtask/src/tasks/fuzz/mod.rs, then inspect support/mesh/mesh_protobuf/ and the existing support/mesh/mesh_rpc/fuzz/fuzz_mesh_ttrpc_server.rs example. The initial milestone is fuzzing mesh_protobuf with malformed-input and representative roundtrip targets that build and run through the existing cargo-fuzz flow; mesh_channel_core and mesh_remote are staged follow-ons.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
distributed-systems, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.