From 8802ed2dc596f1880c1856d0af3e10bcc5224884 Mon Sep 17 00:00:00 2001 From: replikeit <56370676+replikeit@users.noreply.github.com> Date: Sat, 1 Aug 2026 08:55:31 +0300 Subject: [PATCH] sampling : fix use-after-scope in grammar trigger_words path (#2221) llama_sampler_init_grammar_impl built the trigger_words pattern in a block-scoped std::string, stored a pointer into it (trigger_pattern_c), pointed trigger_patterns at that pointer, then let the block drop both locals before llama_grammar_init_impl dereferenced trigger_patterns -- a read of a dangling pointer into freed std::string storage (UB). Hoist trigger_pattern and trigger_pattern_c to function scope so both outlive the call. --- src/llama-sampling.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/llama-sampling.cpp b/src/llama-sampling.cpp index db0aa6b2..4e0f4911 100644 --- a/src/llama-sampling.cpp +++ b/src/llama-sampling.cpp @@ -1367,11 +1367,13 @@ struct llama_grammar* llama_sampler_init_grammar_impl( size_t num_trigger_patterns) { // Huh? this is not used and leaks. auto* ctx = new llama_sampler_grammar; struct llama_grammar* grammar; + std::string trigger_pattern; + const char * trigger_pattern_c = nullptr; if (grammar_str != nullptr && grammar_str[0] != '\0') { // TODO: remove trigger_words support. if (trigger_words != nullptr && num_trigger_words > 0) { GGML_ASSERT(trigger_patterns == nullptr && num_trigger_patterns == 0); - std::string trigger_pattern("[\\s\\S]*?("); + trigger_pattern = "[\\s\\S]*?("; for (size_t i = 0; i < num_trigger_words; ++i) { static const std::regex special_chars("[.^$|()*+?\\[\\]{}\\\\]"); if (i > 0) { @@ -1380,7 +1382,7 @@ struct llama_grammar* llama_sampler_init_grammar_impl( trigger_pattern += std::regex_replace(trigger_words[i], special_chars, "\\$0"); } trigger_pattern += ")[\\s\\S]*"; - auto trigger_pattern_c = trigger_pattern.c_str(); + trigger_pattern_c = trigger_pattern.c_str(); trigger_patterns = &trigger_pattern_c; num_trigger_patterns = 1; }