Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
8 changes: 8 additions & 0 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
4 changes: 4 additions & 0 deletions test/prism/fuzzer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,9 @@ def self.snippet(name, source)
snippet "escaped newline in char literal after heredoc opener", "<<i=?\\\n\n\n"
snippet "escaped newline in char literal after heredoc opener with operator", "<<T^?\\\n\n\n"
snippet "control escape consuming newline after heredoc in interpolation", "%\n\#{<<e}\\c\n\n"
snippet "named capture that is a single backslash", "/(?<\\>/ =~ 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
Loading