Repository navigation
Conversation
Adds an OSS-Fuzz project for the safetensors Rust crate, which is widely used to load model-weight files across the ML ecosystem and is not currently fuzzed. A libFuzzer target exercises SafeTensors::deserialize, the untrusted-input entry point that parses the 8-byte little-endian header length, the JSON metadata header, and each tensor's (dtype, shape, offset) descriptors against the backing buffer. Built and run locally with infra/helper.py (build_fuzzers --sanitizer address; run_fuzzer smoke test).
|
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. |
|
ahkarl13 is integrating a new project: |
DavidKorczynski
left a comment
There was a problem hiding this comment.
- Are you a maintainer? If not, please do a full coordination with maintainers.
- Please land the fuzzers in the upstream repository
Per review: the fuzz targets live in safetensors/safetensors (safetensors/fuzz), so drop the copy staged in this directory and build whatever that crate declares via cargo fuzz list. Also point at the repository's current location (it moved from huggingface/ to the safetensors/ org) and turn on debug assertions, since the slicing code this parser exposes is arithmetic-heavy and an underflow otherwise wraps silently.
|
Thanks for the review — both points addressed, and you were right on both counts. Am I a maintainer? No. I'm an outside contributor. Coordination with maintainers. I've opened three things upstream and pointed them at this PR:
The same target also independently rediscovered safetensors/safetensors#843 (an existing report, fix pending in #844) from a random input, which is a reasonable sign the integration would earn its keep. Landing the fuzzers upstream. They're already there — cd "$SRC/safetensors/safetensors"
cargo fuzz build -O --debug-assertions
FUZZ_RELEASE="fuzz/target/x86_64-unknown-linux-gnu/release"
for target in $(cargo fuzz list); do
cp "$FUZZ_RELEASE/$target" "$OUT/"
doneThat way it picks up whatever upstream has, today and after #857 lands, without this directory needing to track target names. Two other corrections while I was in here: the repository moved from Verified locally against the current upstream Outside the OSS-Fuzz image, with #856's fix and #844's guard applied, the two targets from #857 run 44.7M and 14.6M executions clean. Happy to change any of this — including waiting on #857 before this merges, or switching |
Adds an OSS-Fuzz integration for safetensors, the Rust crate used across the ML ecosystem to load model-weight files. It is not currently in OSS-Fuzz.
What it fuzzes
The crate's untrusted-input surface:
SafeTensors::deserializeparses an 8-byte little-endian header length, a JSON metadata header, and each tensor's(dtype, shape, [begin, end))offset descriptor against the backing buffer, then hands outTensorViews that index into that buffer. The header-length and offset math is the classic out-of-bounds seam for memory-format deserializers, and the slicing layer on top of it (TensorView::sliced_data) does further index arithmetic against a shape the file itself controls.How it builds
The fuzz targets live upstream in
safetensors/fuzz, sobuild.shbuilds whatever that crate declares rather than pinning target names here:That picks up
fuzz_target_1today and the two targets in safetensors/safetensors#857 once they land, without this directory having to track names.--debug-assertionsis deliberate: the arithmetic inslice_byte_rangesis where the bugs found so far live, and without it an underflow wraps silently instead of trapping.Verified locally against current upstream
mainBugs the targets have already found upstream
[0:0:2]) returns the whole tensor instead of nothing safetensors/safetensors#855 — an empty strided selection ([0:0:2]) returns the whole tensor while reportingnewshape = [0]. Affects released 0.8.0. Fix in fix: an empty strided slice must yield nothing, not the whole tensor safetensors/safetensors#856.Details
primary_contact/auto_ccs: ahkarl13@gmail.com — I am not a maintainer, and I'm glad to hand this to one of the safetensors maintainers if they'd rather own the crash reports.