changeset 18463:c8637f9bd3d6

quotearg: never write beyond the returned length * lib/quotearg.c (quotearg_buffer_restyled): Switch to a read-only scan of the string when we initially encounter a single quote when shell quoting, so that if we then switch to a more concise quoting method we will not have written beyond that returned length. This is significant for sh-quote, which has separate routines to determine the length and do the actual quoting. * tests/test-quotearg.h: Reinstate the buffer bounds checking now that we never write more than the returned length.
author Pádraig Brady <pbrady@fb.com>
date Tue, 18 Oct 2016 13:00:07 -0700
parents b6c7fb1f9e0c
children 7481c59e7c35
files ChangeLog lib/quotearg.c tests/test-quotearg.h
diffstat 3 files changed, 42 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Oct 19 00:33:01 2016 +0200
+++ b/ChangeLog	Tue Oct 18 13:00:07 2016 -0700
@@ -1,3 +1,15 @@
+2016-10-19  Pádraig Brady  <P@draigBrady.com>
+
+	quotearg: never write beyond the returned length
+	* lib/quotearg.c (quotearg_buffer_restyled): Switch to a read-only
+	scan of the string when we initially encounter a single quote when
+	shell quoting, so that if we then switch to a more concise quoting method
+	we will not have written beyond that returned length.
+	This is significant for sh-quote, which has separate routines
+	to determine the length and do the actual quoting.
+	* tests/test-quotearg.h: Reinstate the buffer bounds checking
+	now that we never write more than the returned length.
+
 2016-10-18  Bruno Haible  <bruno@clisp.org>
 
 	getprogname tests: Avoid failure in packages that use libtool.
--- a/lib/quotearg.c	Wed Oct 19 00:33:01 2016 +0200
+++ b/lib/quotearg.c	Tue Oct 18 13:00:07 2016 -0700
@@ -252,6 +252,7 @@
 {
   size_t i;
   size_t len = 0;
+  size_t orig_buffersize = 0;
   char const *quote_string = 0;
   size_t quote_string_len = 0;
   bool backslash_escapes = false;
@@ -300,6 +301,8 @@
       } \
     while (0)
 
+ process_input:
+
   switch (quoting_style)
     {
     case c_maybe_quoting_style:
@@ -544,6 +547,16 @@
             {
               if (elide_outer_quotes)
                 goto force_outer_quoting_style;
+
+              if (buffersize && ! orig_buffersize)
+                {
+                  /* Just scan string to see if supports a more concise
+                     representation, rather than writing a longer string
+                     but returning the length of the more concise form.  */
+                  orig_buffersize = buffersize;
+                  buffersize = 0;
+                }
+
               STORE ('\'');
               STORE ('\\');
               STORE ('\'');
@@ -713,11 +726,21 @@
      better to use the apostrophe modifier "\u02BC" if possible, as that
      renders better and works with the word match regex \W+ etc.  */
   if (quoting_style == shell_always_quoting_style && ! elide_outer_quotes
-      && all_c_and_shell_quote_compat && encountered_single_quote)
-    return quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
-                                     c_quoting_style,
-                                     flags, quote_these_too,
-                                     left_quote, right_quote);
+      && encountered_single_quote)
+    {
+      if (all_c_and_shell_quote_compat)
+        return quotearg_buffer_restyled (buffer, orig_buffersize, arg, argsize,
+                                         c_quoting_style,
+                                         flags, quote_these_too,
+                                         left_quote, right_quote);
+      else if (! buffersize && orig_buffersize)
+        {
+          /* Disable read-only scan, and reprocess to write quoted string.  */
+          buffersize = orig_buffersize;
+          len = 0;
+          goto process_input;
+        }
+    }
 
   if (quote_string && !elide_outer_quotes)
     for (; *quote_string; quote_string++)
--- a/tests/test-quotearg.h	Wed Oct 19 00:33:01 2016 +0200
+++ b/tests/test-quotearg.h	Tue Oct 18 13:00:07 2016 -0700
@@ -104,10 +104,9 @@
   static char buf[100];
   size_t size;
   memset (buf, 0xa5, 100);
-  size = quotearg_buffer (buf, 50, str, *len, NULL);
+  size = quotearg_buffer (buf, 100, str, *len, NULL);
   *len = size;
-  ASSERT ((unsigned char) buf[size] == '\0');
-  ASSERT ((unsigned char) buf[50] == 0xa5);
+  ASSERT ((unsigned char) buf[size + 1] == 0xa5);
   return buf;
 }