From 812fd042630b02689258bd09b995940b8270d65f Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 12 Oct 2026 16:19:03 -0400 Subject: [PATCH] Fix buffer length append check 0 --- src/buffer.c | 8 +++++++- src/prism.c | 8 ++++++++ test/prism/fuzzer_test.rb | 4 ++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/buffer.c b/src/buffer.c index cb3b9a4fe8..07e43455e6 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -52,10 +52,16 @@ pm_buffer_length(const pm_buffer_t *buffer) { } /** - * Append the given amount of space to the buffer. + * Append the given amount of space to the buffer. Returns false when there is + * nothing to write: either the allocation failed or the length is zero. A + * zero-initialized buffer has a NULL value until its first allocation, and + * passing NULL to memcpy/memset/memmove is undefined behavior even for zero + * bytes, so callers must not write in the zero case. */ static PRISM_INLINE bool pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { + if (length == 0) return false; + size_t next_length = buffer->length + length; const size_t original_capacity = buffer->capacity; diff --git a/src/prism.c b/src/prism.c index 04fe33a5c5..164354d340 100644 --- a/src/prism.c +++ b/src/prism.c @@ -21490,6 +21490,14 @@ parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *c const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding); if (PRISM_UNLIKELY(cursor != NULL)) { pm_named_capture_escape(parser, &unescaped, source, length, cursor, shared ? NULL : &call->receiver->location); + + /* + * Escapes like \u{} write nothing, so the unescaped name can be empty. + * An empty name is never a valid local, and the buffer is unallocated + * (NULL) in that case, so there is no slice to check. + */ + if (pm_buffer_length(&unescaped) == 0) return; + source = (const uint8_t *) pm_buffer_value(&unescaped); length = pm_buffer_length(&unescaped); } diff --git a/test/prism/fuzzer_test.rb b/test/prism/fuzzer_test.rb index b6a18ab51e..aa3c57761d 100644 --- a/test/prism/fuzzer_test.rb +++ b/test/prism/fuzzer_test.rb @@ -67,5 +67,9 @@ def self.snippet(name, source) snippet "escaped newline in char literal after heredoc opener", "</ =~ a" + snippet "named capture that starts with an escape", "/(?<\\<>/ =~ a" + snippet "named capture that is an empty unicode escape", "/(?<\\u{}>/ =~ a" + snippet "named capture with consecutive empty unicode escapes", "/(?<\\u{}\\u{}\\x41>/ =~ a" end end