[commit: ghc] master: Fix memory leak from #12664 (e41b9c6)

git at git.haskell.org git at git.haskell.org
Fri Oct 7 10:07:36 UTC 2016


Repository : ssh://git@git.haskell.org/ghc

On branch  : master
Link       : http://ghc.haskell.org/trac/ghc/changeset/e41b9c614984b63c4660018cecde682453e083e5/ghc

>---------------------------------------------------------------

commit e41b9c614984b63c4660018cecde682453e083e5
Author: Bartosz Nitka <niteria at gmail.com>
Date:   Thu Oct 6 05:40:24 2016 -0700

    Fix memory leak from #12664
    
    This fixes the leak with `setProgArgv`. The problem was
    that `setProgArgv` would not free the objects pointed
    to by `prog_argc`, `prog_argv` when the globals were
    changed resulting in a leak.
    
    The only strictly necessary change is in `rts/RtsFlags.c`, but
    the code in `System.Environment` was a bit confusing and not
    exception safe, so I refactored it.
    
    Test Plan: ./validate
    
    Reviewers: simonmar, ezyang, austin, hvr, bgamari, erikd
    
    Subscribers: thomie
    
    Differential Revision: https://phabricator.haskell.org/D2576
    
    GHC Trac Issues: #12664


>---------------------------------------------------------------

e41b9c614984b63c4660018cecde682453e083e5
 libraries/base/GHC/Foreign.hs                 | 18 ++++++++++++++++
 libraries/base/System/Environment.hs          | 30 +++++++++++----------------
 libraries/base/tests/IO/environment001.hs     |  4 ++++
 libraries/base/tests/IO/environment001.stdout |  2 ++
 rts/RtsFlags.c                                |  1 +
 5 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/libraries/base/GHC/Foreign.hs b/libraries/base/GHC/Foreign.hs
index e8553d8..7d2f915 100644
--- a/libraries/base/GHC/Foreign.hs
+++ b/libraries/base/GHC/Foreign.hs
@@ -32,6 +32,7 @@ module GHC.Foreign (
     --
     withCString,
     withCStringLen,
+    withCStringsLen,
 
     charIsRepresentable,
   ) where
@@ -134,6 +135,23 @@ withCString enc s act = withEncodedCString enc True s $ \(cp, _sz) -> act cp
 withCStringLen         :: TextEncoding -> String -> (CStringLen -> IO a) -> IO a
 withCStringLen enc = withEncodedCString enc False
 
+-- | Marshal a list of Haskell strings into an array of NUL terminated C strings
+-- using temporary storage.
+--
+-- * the Haskell strings may /not/ contain any NUL characters
+--
+-- * the memory is freed when the subcomputation terminates (either
+--   normally or via an exception), so the pointer to the temporary
+--   storage must /not/ be used after this.
+--
+withCStringsLen :: TextEncoding
+                -> [String]
+                -> (Int -> Ptr CString -> IO a)
+                -> IO a
+withCStringsLen enc strs f = go [] strs
+  where
+  go cs (s:ss) = withCString enc s $ \c -> go (c:cs) ss
+  go cs [] = withArrayLen (reverse cs) f
 
 -- | Determines whether a character can be accurately encoded in a 'CString'.
 --
diff --git a/libraries/base/System/Environment.hs b/libraries/base/System/Environment.hs
index 242845a..d8b3e03 100644
--- a/libraries/base/System/Environment.hs
+++ b/libraries/base/System/Environment.hs
@@ -32,12 +32,14 @@ module System.Environment
 import Foreign
 import Foreign.C
 import System.IO.Error (mkIOError)
-import Control.Exception.Base (bracket, throwIO)
+import Control.Exception.Base (bracket_, throwIO)
+#ifdef mingw32_HOST_OS
+import Control.Exception.Base (bracket)
+#endif
 -- import GHC.IO
 import GHC.IO.Exception
 import GHC.IO.Encoding (getFileSystemEncoding)
 import qualified GHC.Foreign as GHC
-import Data.List
 import Control.Monad
 #ifdef mingw32_HOST_OS
 import GHC.Environment
@@ -369,25 +371,17 @@ withProgArgv :: [String] -> IO a -> IO a
 withProgArgv new_args act = do
   pName <- System.Environment.getProgName
   existing_args <- System.Environment.getArgs
-  bracket (setProgArgv new_args)
-          (\argv -> do _ <- setProgArgv (pName:existing_args)
-                       freeProgArgv argv)
-          (const act)
-
-freeProgArgv :: Ptr CString -> IO ()
-freeProgArgv argv = do
-  size <- lengthArray0 nullPtr argv
-  sequence_ [ peek (argv `advancePtr` i) >>= free
-            | i <- [size - 1, size - 2 .. 0]]
-  free argv
-
-setProgArgv :: [String] -> IO (Ptr CString)
+  bracket_ (setProgArgv new_args)
+           (setProgArgv (pName:existing_args))
+           act
+
+setProgArgv :: [String] -> IO ()
 setProgArgv argv = do
   enc <- getFileSystemEncoding
-  vs <- mapM (GHC.newCString enc) argv >>= newArray0 nullPtr
-  c_setProgArgv (genericLength argv) vs
-  return vs
+  GHC.withCStringsLen enc argv $ \len css ->
+    c_setProgArgv (fromIntegral len) css
 
+-- setProgArgv copies the arguments
 foreign import ccall unsafe "setProgArgv"
   c_setProgArgv  :: CInt -> Ptr CString -> IO ()
 
diff --git a/libraries/base/tests/IO/environment001.hs b/libraries/base/tests/IO/environment001.hs
index 11d7912..1d7a5c1 100644
--- a/libraries/base/tests/IO/environment001.hs
+++ b/libraries/base/tests/IO/environment001.hs
@@ -14,3 +14,7 @@ main = do
     [arg1] <- withArgs ["你好!"] getArgs
     putStrLn arg1
     putStrLn ("Test 3: " ++ show (length arg1))
+
+    args2 <- withArgs ["a", "b"] getArgs
+    print args2
+    putStrLn ("Test 4: " ++ show (length args2))
diff --git a/libraries/base/tests/IO/environment001.stdout b/libraries/base/tests/IO/environment001.stdout
index 2434d0c..2d32a83 100644
--- a/libraries/base/tests/IO/environment001.stdout
+++ b/libraries/base/tests/IO/environment001.stdout
@@ -4,3 +4,5 @@ Test 1: 3
 Test 2: 1
 你好!
 Test 3: 3
+["a","b"]
+Test 4: 2
diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c
index c994a0c..4bd544e 100644
--- a/rts/RtsFlags.c
+++ b/rts/RtsFlags.c
@@ -1943,6 +1943,7 @@ getProgArgv(int *argc, char **argv[])
 void
 setProgArgv(int argc, char *argv[])
 {
+    freeArgv(prog_argc,prog_argv);
     prog_argc = argc;
     prog_argv = copyArgv(argc,argv);
     setProgName(prog_argv);



More information about the ghc-commits mailing list