Perl_delimcpy(): handle backslash as last char
authorDavid Mitchell <[email protected]>
Thu, 25 Aug 2016 16:48:34 +0000 (17:48 +0100)
committerDavid Mitchell <[email protected]>
Wed, 7 Sep 2016 20:00:16 +0000 (21:00 +0100)
[perl #129064] heap-buffer-overflow S_scan_heredoc
[perl #129176] Conditional jump depends on uninitialized values in
               S_scan_heredoc

Perl_delimcpy() is supposed to copy a delimited string to another buffer;
it handles \-<delimiter> escapes, but if the backslash is the last
character in the src buffer, it could overrun the end of the buffer
slightly.

Also document a bit better what this function is supposed to do.

t/op/heredoc.t
util.c

index f47f7ce..13d1074 100644 (file)
@@ -7,7 +7,7 @@ BEGIN {
 }
 
 use strict;
-plan(tests => 42);
+plan(tests => 43);
 
 
 # heredoc without newline (#65838)
@@ -115,4 +115,14 @@ HEREDOC
        {},
        "Don't assert parsing a here-doc if we hit EOF early"
     );
+
+    # [perl #129064] heap-buffer-overflow S_scan_heredoc
+    fresh_perl_like(
+        qq(<<`\\),
+        # valgrind and asan reports an error between these two lines
+        qr/^Unterminated delimiter for here document/,
+        {},
+        "delimcpy(): handle last char being backslash properly"
+    );
+
 }
diff --git a/util.c b/util.c
index 0f5533e..8d1f870 100644 (file)
--- a/util.c
+++ b/util.c
@@ -522,7 +522,14 @@ Free_t   Perl_mfree (Malloc_t where)
 
 #endif
 
-/* copy a string up to some (non-backslashed) delimiter, if any */
+/* copy a string up to some (non-backslashed) delimiter, if any.
+ * With allow_escape, converts \<delimiter> to <delimiter>, while leaves
+ * \<non-delimiter> as-is.
+ * Returns the position in the src string of the closing delimiter, if
+ * any, or returns fromend otherwise.
+ * This is the internal implementation for Perl_delimcpy and
+ * Perl_delimcpy_no_escape.
+ */
 
 static char *
 S_delimcpy(char *to, const char *toend, const char *from,
@@ -534,7 +541,7 @@ S_delimcpy(char *to, const char *toend, const char *from,
     PERL_ARGS_ASSERT_DELIMCPY;
 
     for (tolen = 0; from < fromend; from++, tolen++) {
-       if (allow_escape && *from == '\\') {
+       if (allow_escape && *from == '\\' && from + 1 < fromend) {
            if (from[1] != delim) {
                if (to < toend)
                    *to++ = *from;