Bug 1556136 - Fix misuse of readline() leading to a minor memory leak in the JS shell. r=jwalden, a=jcristau
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 07 Jun 2019 20:55:18 +0000
changeset 536865 00a108b014dac0f3ee308bbc0a7cb097236ed79e
parent 536864 ca143ce6478199dca407a10754dc92a6970324b3
child 536866 34a9ba95e9085caa5dd46f7aa78b6a7c43897fcb
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden, jcristau
bugs1556136
milestone68.0
Bug 1556136 - Fix misuse of readline() leading to a minor memory leak in the JS shell. r=jwalden, a=jcristau Differential Revision: https://phabricator.services.mozilla.com/D33529
js/src/shell/js.cpp
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtrExtensions.h"  // UniqueFreePtr
 #include "mozilla/Unused.h"
 #include "mozilla/Utf8.h"
 #include "mozilla/Variant.h"
 
 #include <chrono>
 #ifdef JS_POSIX_NSPR
 #  include <dlfcn.h>
 #endif
@@ -670,85 +671,85 @@ static void TraceGrayRoots(JSTracer* trc
           JS_GetCompartmentPrivate(comp.get()));
       if (priv) {
         TraceNullableEdge(trc, &priv->grayRoot, "test gray root");
       }
     }
   }
 }
 
-static char* GetLine(FILE* file, const char* prompt) {
+static mozilla::UniqueFreePtr<char[]> GetLine(FILE* file, const char* prompt) {
 #ifdef EDITLINE
   /*
    * Use readline only if file is stdin, because there's no way to specify
    * another handle.  Are other filehandles interactive?
    */
   if (file == stdin) {
-    char* linep = readline(prompt);
+    mozilla::UniqueFreePtr<char[]> linep(readline(prompt));
     /*
      * We set it to zero to avoid complaining about inappropriate ioctl
      * for device in the case of EOF. Looks like errno == 251 if line is
      * finished with EOF and errno == 25 (EINVAL on Mac) if there is
      * nothing left to read.
      */
     if (errno == 251 || errno == 25 || errno == EINVAL) {
       errno = 0;
     }
     if (!linep) {
       return nullptr;
     }
     if (linep[0] != '\0') {
-      add_history(linep);
+      add_history(linep.get());
     }
     return linep;
   }
 #endif
 
   size_t len = 0;
   if (*prompt != '\0' && gOutFile->isOpen()) {
     fprintf(gOutFile->fp, "%s", prompt);
     fflush(gOutFile->fp);
   }
 
   size_t size = 80;
-  char* buffer = static_cast<char*>(malloc(size));
+  mozilla::UniqueFreePtr<char[]> buffer(static_cast<char*>(malloc(size)));
   if (!buffer) {
     return nullptr;
   }
 
-  char* current = buffer;
+  char* current = buffer.get();
   do {
     while (true) {
       if (fgets(current, size - len, file)) {
         break;
       }
       if (errno != EINTR) {
-        free(buffer);
         return nullptr;
       }
     }
 
     len += strlen(current);
-    char* t = buffer + len - 1;
+    char* t = buffer.get() + len - 1;
     if (*t == '\n') {
       /* Line was read. We remove '\n' and exit. */
       *t = '\0';
       break;
     }
 
     if (len + 1 == size) {
       size = size * 2;
-      char* tmp = static_cast<char*>(realloc(buffer, size));
+      char* raw = buffer.release();
+      char* tmp = static_cast<char*>(realloc(raw, size));
       if (!tmp) {
-        free(buffer);
+        free(raw);
         return nullptr;
       }
-      buffer = tmp;
-    }
-    current = buffer + len;
+      buffer.reset(tmp);
+    }
+    current = buffer.get() + len;
   } while (true);
   return buffer;
 }
 
 static bool ShellInterruptCallback(JSContext* cx) {
   ShellContext* sc = GetShellContext(cx);
   if (!sc->serviceInterrupt) {
     return true;
@@ -1353,31 +1354,33 @@ static MOZ_MUST_USE bool ReadEvalPrintLo
     typedef Vector<char, 32> CharBuffer;
     RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
     CharBuffer buffer(cx);
     do {
       ScheduleWatchdog(cx, -1);
       sc->serviceInterrupt = false;
       errno = 0;
 
-      char* line = GetLine(in, startline == lineno ? "js> " : "");
+      mozilla::UniqueFreePtr<char[]> line =
+          GetLine(in, startline == lineno ? "js> " : "");
       if (!line) {
         if (errno) {
           /*
            * Use Latin1 variant here because strerror(errno)'s
            * encoding depends on the user's C locale.
            */
           JS_ReportErrorLatin1(cx, "%s", strerror(errno));
           return false;
         }
         hitEOF = true;
         break;
       }
 
-      if (!buffer.append(line, strlen(line)) || !buffer.append('\n')) {
+      if (!buffer.append(line.get(), strlen(line.get())) ||
+          !buffer.append('\n')) {
         return false;
       }
 
       lineno++;
       if (!ScheduleWatchdog(cx, sc->timeoutInterval)) {
         hitEOF = true;
         break;
       }