Skip to content

Expose RegisterPostProcessor in the libprotobuf-mutator wrapper - #113

Open
ChrisJr404 wants to merge 1 commit into
google:masterfrom
ChrisJr404:lpm-register-post-processor
Open

Expose RegisterPostProcessor in the libprotobuf-mutator wrapper#113
ChrisJr404 wants to merge 1 commit into
google:masterfrom
ChrisJr404:lpm-register-post-processor

Conversation

@ChrisJr404

Copy link
Copy Markdown

Fixes #58. Exposes libprotobuf-mutator's RegisterPostProcessor through the wrapper so you can fix up a mutated proto before it reaches your test function.

The mutator flips fields without knowing anything about your message's invariants, so it happily produces protos that your target rejects early: a length field that no longer matches a repeated field, a stale checksum, an enum that's really a bitmask. libprotobuf-mutator already has a hook for exactly this (RegisterPostProcessor, see google/libprotobuf-mutator's mutator.cc), there just wasn't a way to reach it from Python.

New surface:

import atheris_libprotobuf_mutator as almo

def fixup(msg, seed):
    msg.checksum = crc32(msg.payload)   # edit in place
    # ...or build and `return` a new message of the same type

almo.RegisterPostProcessor(my_pb2.MyMessage, fixup)
# then almo.Setup(...) / atheris.Fuzz() as usual

The callback gets the freshly mutated message and the seed. You can mutate it in place or return a replacement; returning None keeps your in-place edits. Registration is global per message type (that's how libprotobuf-mutator keys it), so you call it once before Fuzz().

On the C++ side the binding pulls the descriptor off a prototype instance and hands libprotobuf-mutator a std::function that calls back into Python. Because the native proto caster hands Python a copy, the Python helper always returns the message and the binding copies it back into the one the mutator owns — so plain in-place edits actually take effect. It follows the same std::unique_ptr<protobuf::Message> bridging the existing CustomProtoMutator/LoadProtoInput bindings use. Purely additive, existing API is untouched.

Test: testPostProcessor in proto_fuzz_test.py registers a post-processor that rewrites every mutated StringValue to the solving value, so the existing comparison harness reports Solved on the first input instead of waiting for the mutator to find "abc" on its own. If the post-processor's edits didn't make it back into the bytes the fuzzer feeds the harness, that test would time out.

One note on building/verifying: I couldn't build the native extension in my environment (no bazel/protobuf-C++/pybind toolchain handy), so I verified the Python surface end to end against a stubbed _mutator that emulates the copy-in/copy-back the caster does, and checked the C++ against the libprotobuf-mutator v1.0 signature pinned in WORKSPACE. Worth a real build in CI before merge. I've signed the Google CLA.

@google-cla

google-cla Bot commented Aug 18, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support RegisterPostProcessor in lib protofuf mutator

1 participant