You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

SurrealML (.surml) header-parser panic -> process abort (DoS)

Gated PoC repository for a huntr submission against github.com/surrealdb/surrealml (crate surrealml-core v0.1.9, HEAD 152ac2d).

Summary

SurMlFile::from_file / SurMlFile::from_bytes (the public .surml model-file loader in surrealml-core) calls Header::from_bytes, which in turn calls several header-field parsers that panic (instead of returning the Result::Err the API contracts promise) on malformed-but-well-formed- looking header content:

  • storage/header/input_dims.rs::InputDims::from_string - x.parse::<i32>().unwrap() panics on non-numeric input, and dims[1] indexes out of bounds (panics) when the field contains a single number with no comma.
  • storage/header/origin.rs::Origin::from_string - split.next().unwrap() panics when the origin field is non-empty but lacks the inner "=>" separator.
  • storage/header/output.rs::Output::from_string - NormaliserType::from_string(data).unwrap() panics when the output field contains "=>" but the trailing value is neither "none" nor a well-formed label(a,b) normaliser string.

These parsers are reached directly from the public loading API with zero sanitisation of the header string extracted from the file. Because the real-world entry point is the C FFI function c-wrapper/src/api/storage/load_model.rs::load_model, which is an extern "C" fn with no catch_unwind, an unwinding panic that crosses this FFI boundary is a "panic in a function that cannot unwind" in Rust, which forces abort() - i.e. this is not a recoverable per-request panic, it aborts the entire host process (confirmed below).

This is exactly the call path used by the official surrealml Python client: SurMlFile.load(path) -> RustAdapter.load(path) -> loader.lib.load_model(path) where loader.lib is the ctypes CDLL of the compiled libc_wrapper.so/.dylib/.dll.

Attacker input

A 55-62 byte .surml file is enough. Format: [4-byte BE header length][header string][arbitrary model bytes]. Any application, service, or pipeline that loads a .surml file supplied/uploaded by another party (a shared/downloaded model, a file passed through a multi-tenant ML pipeline, etc.) is affected.

Example header string that triggers the InputDims out-of-bounds panic:

//=>//=>//=>//=>//=>//=>//=>//=>//=>5//=>

Real evidence (unmodified upstream code)

  1. poc_panic.rs - a cargo run --example harness linked directly against the real surrealml-core crate (no source modified), using std::panic::catch_unwind to demonstrate 4 independent, reachable panics from Header::from_bytes on 4 different malformed inputs. All 4 produce a real Rust panic with a real message (index-out-of-bounds / ParseIntError / unwrap on None / unwrap on Err) instead of a graceful Result::Err.

  2. ctypes_harness.py - loads the real, release-compiled libc_wrapper.so produced from the unmodified c-wrapper crate (the exact artifact the official Python client loads) and calls its public load_model() FFI export on each malicious .surml file via Python ctypes, exactly as clients/python/surrealml/rust_adapter.py does in production.

Observed for all 4 crafted files (malicious_inputdims_oob.surml, malicious_inputdims_parse.surml, malicious_origin.surml, malicious_output.surml):

thread '<unnamed>' (...) panicked at modules/core/src/storage/header/input_dims.rs:36:33:
index out of bounds: the len is 1 but the index is 1

thread '<unnamed>' (...) panicked at .../panicking.rs:225:5:
panic in a function that cannot unwind
thread caused non-unwinding panic. aborting.

Process exit code: 134 (SIGABRT) for every case - the entire Python process embedding the library is killed, not just the current call.

Reproduce

# 1. build surrealml-core + c-wrapper from the real (unmodified) repo
cd surrealml/modules/core && cargo run --example poc_panic   # see poc_panic.rs
cd surrealml && cargo build -p c-wrapper --release

# 2. craft the malicious files and drive the real compiled library
python3 build_variants.py
python3 ctypes_harness.py malicious_inputdims_oob.surml   # -> SIGABRT (exit 134)
python3 ctypes_harness.py malicious_inputdims_parse.surml # -> SIGABRT (exit 134)
python3 ctypes_harness.py malicious_origin.surml          # -> SIGABRT (exit 134)
python3 ctypes_harness.py malicious_output.surml          # -> SIGABRT (exit 134)

Dedup note

GitHub PR #40 ("Index overflow", merged 2024-03-25) previously hardened only the outer 4-byte length-prefix check in SurMlFile::from_bytes/from_file (storage/surml_file.rs). It does not touch any of the four field parsers exploited here (input_dims.rs, origin.rs, output.rs), which remain unpatched on current HEAD 152ac2d. No GitHub Security Advisory exists for this repository at time of testing.

Impact

Denial of service: any process that loads an untrusted/shared/downloaded .surml file through the official Python client (or any consumer of libc_wrapper.so / surrealml-core::storage::surml_file::SurMlFile) crashes outright on a handful of crafted bytes, with no way for calling code to catch or recover from it (FFI-boundary panic forces abort()).

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support