voice_audio_multi/tests/unittests/test_safetensors_metadata_depth.cpp
Fabio c36510c21e
Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

98 lines
3.9 KiB
C++

// load_safetensors_index() accepts a non-object `__metadata__` value (MLX writes
// `null`) by handing it to skip_json_value(), which recurses once per nested
// array/object with no depth limit. The header length is read straight from the
// file and is not bounded either, so a header such as
//
// {"__metadata__":[[[[ ... ]]]]}
//
// recurses until the native stack is exhausted and the process dies with
// SIGSEGV instead of the loader reporting a malformed file. The reference
// safetensors loader parses the header with serde_json, which stops at a fixed
// recursion limit and returns an error.
//
// A malformed header must be rejected with an exception, like every other
// header defect load_safetensors_index() already reports.
#include "engine/framework/io/safetensors.h"
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
namespace {
void require(bool condition, const std::string & message) {
if (!condition) {
throw std::runtime_error(message);
}
}
// Minimal safetensors container: u64 little-endian header length, then the
// JSON header. No tensor data is needed to reach the metadata parser.
std::filesystem::path write_safetensors(const std::string & header_json) {
static int counter = 0;
const auto path = std::filesystem::temp_directory_path() /
("safetensors_metadata_depth_" + std::to_string(counter++) + ".safetensors");
std::ofstream out(path, std::ios::binary);
require(out.good(), "failed to open temp safetensors file");
const uint64_t header_len = header_json.size();
out.write(reinterpret_cast<const char *>(&header_len), sizeof(header_len));
out.write(header_json.data(), static_cast<std::streamsize>(header_json.size()));
require(out.good(), "failed to write temp safetensors file");
return path;
}
void test_deeply_nested_metadata_is_rejected() {
// 1M levels is a 2 MB header: far below any realistic file size, and deep
// enough to exhaust an 8 MB main-thread stack with one frame per level.
constexpr size_t kDepth = 1000000;
const std::string header =
"{\"__metadata__\":" + std::string(kDepth, '[') + std::string(kDepth, ']') + "}";
const auto path = write_safetensors(header);
bool threw = false;
try {
(void) engine::io::load_safetensors_index(path);
} catch (const std::exception &) {
threw = true;
}
std::filesystem::remove(path);
require(threw, "deeply nested __metadata__ must be rejected as a malformed header");
}
void test_null_metadata_still_parses() {
// The non-object path exists for MLX, which writes "__metadata__": null.
const auto path = write_safetensors(
R"({"__metadata__":null,"t":{"dtype":"F32","shape":[0],"data_offsets":[0,0]}})");
const auto index = engine::io::load_safetensors_index(path);
std::filesystem::remove(path);
require(index.tensors.size() == 1, "null __metadata__ must still be accepted");
}
void test_shallow_nested_metadata_still_parses() {
// Nesting well inside the limit is skipped as before.
constexpr size_t kDepth = 64;
const auto path = write_safetensors(
"{\"__metadata__\":" + std::string(kDepth, '[') + std::string(kDepth, ']') +
R"(,"t":{"dtype":"F32","shape":[0],"data_offsets":[0,0]}})");
const auto index = engine::io::load_safetensors_index(path);
std::filesystem::remove(path);
require(index.tensors.size() == 1, "shallow nested __metadata__ must still be accepted");
}
} // namespace
int main() {
try {
test_null_metadata_still_parses();
test_shallow_nested_metadata_still_parses();
test_deeply_nested_metadata_is_rejected();
} catch (const std::exception & error) {
std::cerr << "safetensors metadata depth test failed: " << error.what() << "\n";
return 1;
}
std::cout << "safetensors metadata depth test passed\n";
return 0;
}