// Minimal reproducer for Gemma3 integer division-by-zero // Mirrors the vulnerable code in src/models/gemma3.cpp:32 // and src/llama-model.cpp:1147-1171 // // Compile: g++ -o reproducer reproducer.cpp -fsanitize=undefined -fno-sanitize-recover=all // Run: ./reproducer #include #include #include #include #include #include #include #define LLAMA_MAX_LAYERS 512 // Mirrors llama_hparams (simplified) struct llama_hparams { uint32_t n_embd = 3072; // from gemma3.embedding_length in GGUF uint32_t n_layer_all = 62; // from gemma3.block_count = 62 → LLM_TYPE_27B uint32_t n_embd_head_k_full = 0; std::array n_head_arr = {}; // all zeros — key missing from GGUF uint32_t n_head(uint32_t il = 0) const { return n_head_arr[il]; // returns 0 when key is absent } }; enum llm_type { LLM_TYPE_UNKNOWN, LLM_TYPE_1B, LLM_TYPE_4B, LLM_TYPE_8B, LLM_TYPE_12B, LLM_TYPE_27B }; int main() { llama_hparams hparams; // --- Mirrors llama-model.cpp:1147 (general hparams loader) --- // When n_head() == 0, n_embd_head_k_full is set to 0 if (hparams.n_head() > 0) { hparams.n_embd_head_k_full = hparams.n_embd / hparams.n_head(); } else { hparams.n_embd_head_k_full = 0; } // --- Mirrors gemma3.cpp:20-32 (load_arch_hparams) --- llm_type type = LLM_TYPE_UNKNOWN; switch (hparams.n_layer_all) { case 18: type = LLM_TYPE_UNKNOWN; break; // 270M case 26: type = LLM_TYPE_1B; break; case 32: type = LLM_TYPE_8B; break; case 34: type = LLM_TYPE_4B; break; case 48: type = LLM_TYPE_12B; break; case 62: type = LLM_TYPE_27B; break; // <-- block_count=62 triggers this default: type = LLM_TYPE_UNKNOWN; break; } printf("block_count = %u → type = %s\n", hparams.n_layer_all, type == LLM_TYPE_27B ? "LLM_TYPE_27B" : "other"); printf("n_head(0) = %u (key absent from GGUF → stays 0)\n", hparams.n_head(0)); printf("n_embd = %u\n", hparams.n_embd); printf("\nExecuting vulnerable line (gemma3.cpp:32):\n"); printf(" hparams.n_embd / hparams.n_head(0) = %u / %u\n", hparams.n_embd, hparams.n_head(0)); // THE VULNERABLE COMPUTATION — mirrors gemma3.cpp:32 exactly // On x86_64: SIGFPE (exit 136) // On ARM64: silent UB (SDIV returns 0), UBSan aborts with "division by zero" float f_attention_scale = (type == LLM_TYPE_27B) ? 1.0f / std::sqrt(float(hparams.n_embd / hparams.n_head(0))) // INTEGER DIV BY ZERO : 1.0f / std::sqrt(float(hparams.n_embd_head_k_full)); // Should never reach here on x86_64 printf("f_attention_scale = %f (should not reach here on x86_64)\n", f_attention_scale); return 0; }