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: uncaught panics on load (DoS)

Target: https://github.com/surrealdb/surrealml Format: .surml (SurrealML's custom model container format) Affected component: modules/core/src/storage/surml_file.rs (SurMlFile::from_file / from_bytes) and modules/core/src/storage/header/*.rs

Summary

A .surml file is [4-byte BE header length][header string][raw model bytes]. The header is a //=>-delimited string. Several of the header's sub-field parsers use unchecked unwrap() / unchecked indexing instead of the crate's own safe_eject! / safe_eject_option! error-propagation macros used everywhere else in the same file. A single malformed field causes an uncaught Rust panic while loading the file, with no way for the caller to catch it.

The only code path that reaches this parser is SurMlFile::from_file, which is called from the extern "C" fn load_model in modules/c-wrapper/src/api/storage/load_model.rs โ€” the FFI entry point used by the Python client (ctypes) and the TypeScript client, and by any host embedding this library (e.g. a database loading a user-supplied .surml model). That function has no catch_unwind wrapper. A Rust panic is not permitted to unwind across an extern "C" boundary, so the runtime aborts the entire host process instead of returning an error โ€” there is no exception for the Python/TS caller to catch.

Confirmed panic sites

All four were reproduced against the real, unmodified surrealml-core crate (compiled from the public repo, no source changes) by calling SurMlFile::from_file directly.

PoC file Location Trigger Panic
poc_1_input_dims_no_comma.surml modules/core/src/storage/header/input_dims.rs:36 input_dims field has no comma (e.g. "5") index out of bounds on dims[1]
poc_2_input_dims_non_numeric.surml modules/core/src/storage/header/input_dims.rs:34 input_dims field is non-numeric (e.g. "x,y") unwrap() on ParseIntError
poc_3_origin_no_arrow.surml modules/core/src/storage/header/origin.rs:114-115 origin field is non-empty with no => separator unwrap() on None
poc_4_output_unknown_normaliser.surml modules/core/src/storage/header/output.rs:73 output field names a normaliser type that isn't one of the 4 known types unwrap() on Err("Unknown normaliser type: ...")

Each PoC file is 62-97 bytes: an empty/placeholder model payload plus a header string where every field is empty except the one field needed to trigger that specific panic.

Reproduction

// Cargo.toml: surrealml-core = { path = "<repo>/modules/core", default-features = false }
use surrealml_core::storage::surml_file::SurMlFile;

fn main() {
    let path = std::env::args().nth(1).unwrap();
    let result = std::panic::catch_unwind(|| SurMlFile::from_file(&path));
    match result {
        Ok(Ok(_)) => println!("loaded ok"),
        Ok(Err(e)) => println!("clean error: {e}"),
        Err(_) => println!("PROCESS PANIC"),
    }
}

Running this against any of the four PoC files prints a panic originating at the exact file:line listed above, e.g.:

thread 'main' panicked at .../storage/header/output.rs:73:57:
called `Result::unwrap()` on an `Err` value: SurrealError { message: "Unknown normaliser type: totally_bogus_normaliser", ... }

Equivalently, from Python, SurMlFile.load("poc_1_input_dims_no_comma.surml", engine=...) aborts the interpreter process outright (SIGABRT) rather than raising a catchable RuntimeError.

Impact

  • Denial of Service. Any service that loads a .surml file supplied by another party โ€” a multi-tenant database accepting model uploads, a marketplace of shared models, a client library loading a downloaded file โ€” can be crashed by a file well under 100 bytes. Because the crash is a process abort at the FFI boundary, it cannot be caught or recovered from by the embedding application; the whole process goes down, not just the load call.
  • This is a single root cause repeated across independent header sub-parsers (input dims, origin, output/normaliser): missing input validation before unwrap()/indexing, in code that otherwise consistently uses the crate's own safe_eject! / safe_eject_option! macros for exactly this purpose elsewhere in the same files.

Suggested fix

Replace the bare unwrap() calls and unchecked indexing in input_dims.rs, origin.rs, and output.rs with the same safe_eject! / safe_eject_option! macros already used throughout modules/core/src/storage/header/, so malformed fields produce a SurrealError instead of a panic. For input_dims.rs specifically, validate dims.len() == 2 before indexing.

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