CoreML (apple/coremltools) MILBlob Span::Slice integer-overflow bounds-check bypass -> OOB read
PoC package for a heap/mapped out-of-bounds read in the CoreML weight-blob parser
(MILBlob) shipped in apple/coremltools. A crafted .mlmodelc / weights.bin
weight-blob file with attacker-controlled blob_metadata.offset and
blob_metadata.sizeInBytes (both uint64) triggers a 64-bit integer overflow in
the bounds check inside Span<T>::Slice(index, size), returning a Span whose
Size() is enormous (here 2^64 - 50). A consumer reading the weight bytes then
reads far past the mapped file.
Files
weights.bin- the malicious CoreML weight-blob file (384 bytes)craft.py- script that generatesweights.bin(documents the byte layout)harness.cpp- ASan harness that drives the REALStorageReader/MMapFileReader/Spanbuild.sh- build command (clang++ + ASan)asan_crash.txt- the captured AddressSanitizer crash trace
Reproduce
git clone --filter=blob:none --sparse https://github.com/apple/coremltools.git
cd coremltools
git sparse-checkout set mlmodel/src/MILBlob
# commit pinned for this PoC: 3fb88603a6af3d12bf72ca2dcc0a3724a631317b
clang++ -std=c++17 -fsanitize=address -g -O0 -Imlmodel/src \
harness.cpp \
mlmodel/src/MILBlob/Blob/StorageReader.cpp \
mlmodel/src/MILBlob/Blob/MMapFileReader.cpp \
mlmodel/src/MILBlob/Blob/MMapFileReaderFactory.cpp \
-o harness
python3 craft.py
ASAN_OPTIONS=detect_leaks=0 ./harness weights.bin
Root cause
mlmodel/src/MILBlob/Util/Span.hpp (~line 325):
Span<T> Slice(size_t index, size_t size) const
{
MILVerifyIsTrue(size > 0 && index < Size() && index + size <= Size(), std::range_error, "index out of bounds");
return Span<T>(Data() + index, size);
}
index + size is an unchecked 64-bit addition. With index = 100 and
size = 2^64 - 50, index + size wraps to 50, which is <= Size(), so the
check passes and an oversized Span is returned.
Both operands are attacker-controlled: StorageReader::Impl::GetRawDataView
(StorageReader.cpp) calls m_reader->ReadData(metadata.offset, metadata.sizeInBytes),
and MMapFileReader::ReadData (MMapFileReader.cpp) forwards straight to
m_dataSpan.Slice(offset, length). The only validation on the metadata is the
sentinel == 0xDEADBEEF check in GetMetadata - offset and sizeInBytes are
never range-checked before reaching Slice.
Source availability
100% open source, BSD-3-Clause: https://github.com/apple/coremltools
Vulnerable file: mlmodel/src/MILBlob/Util/Span.hpp.