
Reproduce el desbordamiento de enteros CVE-2026-70638 en llama.cpp Android JNI con una demostración segura de aritmética, un generador de GGUF malicioso y un hook de Frida para investigación de seguridad autorizada.
PoC complementario para el artículo de Hunt-Benito "Una multiplicación de más: CVE-2026-70638 — Desbordamiento de enteros en la asignación de montón JNI de Android de llama.cpp" → https://www.hunt-benito.com/blog/one-multiply-too-many-cve-2026-70638-llama-cpp-android-jni-integer-overflow/
Java_android_llama_cpp_LLamaAndroid_new_1batch() en
examples/llama.android/llama/src/main/cpp/llama-android.cpp (las compilaciones de llama.cpp
b1886–b7445) es una copia manual de llama_batch_init() que realiza varios
cálculos no verificados de malloc(sizeof(T) * count). Con un multiplicador
controlado por el atacante (n_seq_max, n_tokens o embd), el tamaño se desborda,
el bloque de montón es de tamaño insuficiente y las escrituras posteriores del llamador lo desbordan
(CWE-190 → CWE-122). NVD CVSS 3.1 7.8 Alto
(AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H).
El núcleo vulnerable de la función:
batch->seq_id[i] = (llama_seq_id *) malloc(sizeof(llama_seq_id) * n_seq_max);
# 1. Arithmetic demonstrator (the core of the article's PoC)
cc -O2 -o overflow_demo overflow_demo.c
./overflow_demo
# 2. Malicious model file
python3 craft_gguf.py 0x40000001 malicious.gguf # -> 416-byte GGUF
xxd malicious.gguf | head # verify magic 'GGUF' + key
# 3. Live hook (rooted/emulator device with frida-server, authorized target only)
frida -U -l hook_new_batch.js -f <package> --no-pause
[2] Malicious: n_seq_max = 0x40000000 (2^30). 4 * 2^30 = 2^32 -> wraps:
n_seq_max (attacker) = 1073741824 (0x40000000)
malloc size, arm64 = 4294967296 bytes (4.00 GiB)
malloc size, armeabi-v7a = 0 bytes
caller believes it got = 4294967296 bytes
>>> 32-bit WRAP: 4294967296-byte write into 0-byte heap block (CWE-122)
armeabi-v7a, que aún se distribuye). En 64 bits (arm64-v8a), la misma expresión no verificada solicita en su lugar un bloque de varios GiB que malloc no puede asignar (NULL → caída posterior / DoS); la corrupción del montón requiere la ruta de 32 bits.overflow_demo.c no realiza ninguna escritura fuera de los límites: solo demuestra que la aritmética se desborda. craft_gguf.py produce un modelo no ejecutable (sin tensores), por lo que no puede utilizarse como arma tal cual; demuestra que el multiplicador controlado por el atacante proviene de metadatos no confiables.Actualice llama.cpp a b7446 o posterior (la ruta JNI new_1batch fue eliminada por la reescritura del enlace de Android). Si debe permanecer en una compilación afectada o en un fork derivado, aplique el parche de validación de Cyera desde
https://github.com/Vladimir-tokarev-cyera/llama-cpp-security-patches
(la protección batch-init de CVE-2026-43627 es la corrección canónica para esta clase de fallo).
SOLO PARA INVESTIGACIÓN DE SEGURIDAD AUTORIZADA Y USO EDUCATIVO.
| Archivo | Propósito |
|---|
overflow_demo.c | Reproductor independiente de la aritmética de tamaño con desbordamiento en size_t de 32 bits (armeabi-v7a) frente a 64 bits (arm64-v8a). Sin escritura corrupta: seguro de ejecutar en cualquier lugar. |
craft_gguf.py | Construye un GGUF mínimo y estructuralmente válido con llama.embedding_length establecido en un valor del atacante, mostrando el vector de entrega del archivo de modelo (embd se lee directamente de metadatos no confiables). |
hook_new_batch.js | Hook de Frida que registra/sobreescribe los tres multiplicandos en el límite JNI en vivo en un dispositivo de investigación. |