Browse Source

urlapi: skip the extra dedotdot alloc if no dot in path

Saves an allocation for many/most URLs.

Updates test 1395 accordingly

Closes #10403
Daniel Stenberg 1 year ago
parent
commit
63c53ea627
2 changed files with 38 additions and 23 deletions
  1. 21 14
      lib/urlapi.c
  2. 17 9
      tests/unit/unit1395.c

+ 21 - 14
lib/urlapi.c

@@ -783,25 +783,28 @@ static CURLUcode decode_host(struct dynbuf *host)
  *
  * RETURNS
  *
- * an allocated dedotdotified output string
+ * Zero for success and 'out' set to an allocated dedotdotified string.
  */
-UNITTEST char *dedotdotify(const char *input, size_t clen);
-UNITTEST char *dedotdotify(const char *input, size_t clen)
+UNITTEST int dedotdotify(const char *input, size_t clen, char **outp);
+UNITTEST int dedotdotify(const char *input, size_t clen, char **outp)
 {
-  char *out = malloc(clen + 1);
   char *outptr;
   const char *orginput = input;
   char *queryp;
+  char *out;
+
+  *outp = NULL;
+  /* the path always starts with a slash, and a slash has not dot */
+  if((clen < 2) || !memchr(input, '.', clen))
+    return 0;
+
+  out = malloc(clen + 1);
   if(!out)
-    return NULL; /* out of memory */
+    return 1; /* out of memory */
 
   *out = 0; /* null-terminates, for inputs like "./" */
   outptr = out;
 
-  if(!*input)
-    /* zero length input string, return that */
-    return out;
-
   /*
    * To handle query-parts properly, we must find it and remove it during the
    * dotdot-operation and then append it again at the end to the output
@@ -906,7 +909,8 @@ UNITTEST char *dedotdotify(const char *input, size_t clen)
     memcpy(outptr, &orginput[oindex], qlen + 1); /* include zero byte */
   }
 
-  return out;
+  *outp = out;
+  return 0; /* success */
 }
 
 static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
@@ -1226,13 +1230,16 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
 
     if(!(flags & CURLU_PATH_AS_IS)) {
       /* remove ../ and ./ sequences according to RFC3986 */
-      char *newp = dedotdotify((char *)path, pathlen);
-      if(!newp) {
+      char *dedot;
+      int err = dedotdotify((char *)path, pathlen, &dedot);
+      if(err) {
         result = CURLUE_OUT_OF_MEMORY;
         goto fail;
       }
-      free(u->path);
-      u->path = newp;
+      if(dedot) {
+        free(u->path);
+        u->path = dedot;
+      }
     }
   }
 

+ 17 - 9
tests/unit/unit1395.c

@@ -24,7 +24,7 @@
 #include "curlcheck.h"
 
 /* copied from urlapi.c */
-extern char *dedotdotify(const char *input, size_t clen);
+extern int dedotdotify(const char *input, size_t clen, char **out);
 
 #include "memdebug.h"
 
@@ -58,35 +58,43 @@ UNITTEST_START
     { "/1/./..", "/" },
     { "/1/./../2", "/2" },
     { "/hello/1/./../2", "/hello/2" },
-    { "test/this", "test/this" },
+    { "test/this", NULL },
     { "test/this/../now", "test/now" },
     { "/1../moo../foo", "/1../moo../foo"},
     { "/../../moo", "/moo"},
     { "/../../moo?andnot/../yay", "/moo?andnot/../yay"},
     { "/123?foo=/./&bar=/../", "/123?foo=/./&bar=/../"},
     { "/../moo/..?what", "/?what" },
-    { "/", "/" },
-    { "", "" },
+    { "/", NULL },
+    { "", NULL },
     { "/.../", "/.../" },
     { "./moo", "moo" },
     { "../moo", "moo" },
     { "/.", "/" },
     { "/..", "/" },
     { "/moo/..", "/" },
-    { "..", "" },
-    { ".", "" },
+    { "/..", "/" },
+    { "/.", "/" },
   };
 
   for(i = 0; i < sizeof(pairs)/sizeof(pairs[0]); i++) {
-    char *out = dedotdotify(pairs[i].input, strlen(pairs[i].input));
-    abort_unless(out != NULL, "returned NULL!");
+    char *out;
+    int err = dedotdotify(pairs[i].input, strlen(pairs[i].input), &out);
+    abort_unless(err == 0, "returned error");
+    abort_if(err && out, "returned error with output");
 
-    if(strcmp(out, pairs[i].output)) {
+    if(out && strcmp(out, pairs[i].output)) {
       fprintf(stderr, "Test %u: '%s' gave '%s' instead of '%s'\n",
               i, pairs[i].input, out, pairs[i].output);
       fail("Test case output mismatched");
       fails++;
     }
+    else if(!out && pairs[i].output) {
+      fprintf(stderr, "Test %u: '%s' gave '%s' instead of NULL\n",
+              i, pairs[i].input, out);
+      fail("Test case output mismatched");
+      fails++;
+    }
     else
       fprintf(stderr, "Test %u: OK\n", i);
     free(out);