Skip to content

Commit 803209c

Browse files
committed
Fix segfault for System.Posix.Env.ByteString.getEnvironment
(fixes #274)
1 parent 720debb commit 803209c

File tree

6 files changed

+62
-72
lines changed

6 files changed

+62
-72
lines changed

System/Posix/Env.hsc

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@ module System.Posix.Env (
2828

2929
#include "HsUnix.h"
3030

31+
import Foreign hiding (void)
3132
import Foreign.C.Error (throwErrnoIfMinus1_)
3233
import Foreign.C.Types
3334
import Foreign.C.String
34-
import Foreign.Marshal.Array
35-
import Foreign.Ptr
36-
import Foreign.Storable
3735
import Control.Monad
3836
import Data.Maybe (fromMaybe)
3937
import System.Posix.Internals
4038

39+
import qualified System.Posix.Env.Internal as Internal
40+
4141
-- |'getEnv' looks up a variable in the environment.
4242

4343
getEnv ::
@@ -63,28 +63,7 @@ foreign import ccall unsafe "getenv"
6363
c_getenv :: CString -> IO CString
6464

6565
getEnvironmentPrim :: IO [String]
66-
getEnvironmentPrim = do
67-
c_environ <- getCEnviron
68-
-- environ can be NULL
69-
if c_environ == nullPtr
70-
then return []
71-
else do
72-
arr <- peekArray0 nullPtr c_environ
73-
mapM peekFilePath arr
74-
75-
getCEnviron :: IO (Ptr CString)
76-
#if HAVE__NSGETENVIRON
77-
-- You should not access @char **environ@ directly on Darwin in a bundle/shared library.
78-
-- See #2458 and http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man7/environ.7.html
79-
getCEnviron = nsGetEnviron >>= peek
80-
81-
foreign import ccall unsafe "_NSGetEnviron"
82-
nsGetEnviron :: IO (Ptr (Ptr CString))
83-
#else
84-
getCEnviron = peek c_environ_p
85-
foreign import ccall unsafe "&environ"
86-
c_environ_p :: Ptr (Ptr CString)
87-
#endif
66+
getEnvironmentPrim = Internal.getEnvironmentPrim >>= mapM peekFilePath
8867

8968
-- |'getEnvironment' retrieves the entire environment as a
9069
-- list of @(key,value)@ pairs.
@@ -184,7 +163,7 @@ foreign import ccall unsafe "clearenv"
184163
#else
185164
-- Fallback to 'environ[0] = NULL'.
186165
clearEnv = do
187-
c_environ <- getCEnviron
166+
c_environ <- Internal.getCEnviron
188167
unless (c_environ == nullPtr) $
189168
poke c_environ nullPtr
190169
#endif

System/Posix/Env/ByteString.hsc

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import qualified Data.ByteString.Char8 as BC
4444
import Data.ByteString (ByteString)
4545
import Data.ByteString.Internal (ByteString (PS), memcpy)
4646

47+
import qualified System.Posix.Env.Internal as Internal
48+
4749
-- |'getEnv' looks up a variable in the environment.
4850

4951
getEnv ::
@@ -69,25 +71,7 @@ foreign import ccall unsafe "getenv"
6971
c_getenv :: CString -> IO CString
7072

7173
getEnvironmentPrim :: IO [ByteString]
72-
getEnvironmentPrim = do
73-
c_environ <- getCEnviron
74-
arr <- peekArray0 nullPtr c_environ
75-
mapM B.packCString arr
76-
77-
getCEnviron :: IO (Ptr CString)
78-
#if HAVE__NSGETENVIRON
79-
-- You should not access @char **environ@ directly on Darwin in a bundle/shared library.
80-
-- See #2458 and http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man7/environ.7.html
81-
getCEnviron = nsGetEnviron >>= peek
82-
83-
foreign import ccall unsafe "_NSGetEnviron"
84-
nsGetEnviron :: IO (Ptr (Ptr CString))
85-
#else
86-
getCEnviron = peek c_environ_p
87-
88-
foreign import ccall unsafe "&environ"
89-
c_environ_p :: Ptr (Ptr CString)
90-
#endif
74+
getEnvironmentPrim = Internal.getEnvironmentPrim >>= mapM B.packCString
9175

9276
-- |'getEnvironment' retrieves the entire environment as a
9377
-- list of @(key,value)@ pairs.

System/Posix/Env/Internal.hsc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module System.Posix.Env.Internal where
2+
3+
#include "HsUnix.h"
4+
5+
import Foreign
6+
import Foreign.C
7+
8+
getEnvironmentPrim :: IO [Ptr CChar]
9+
getEnvironmentPrim = do
10+
c_environ <- getCEnviron
11+
if c_environ == nullPtr
12+
then return []
13+
else do
14+
peekArray0 nullPtr c_environ
15+
16+
getCEnviron :: IO (Ptr CString)
17+
#if HAVE__NSGETENVIRON
18+
-- You should not access @char **environ@ directly on Darwin in a bundle/shared library.
19+
-- See #2458 and http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man7/environ.7.html
20+
getCEnviron = nsGetEnviron >>= peek
21+
22+
foreign import ccall unsafe "_NSGetEnviron"
23+
nsGetEnviron :: IO (Ptr (Ptr CString))
24+
#else
25+
getCEnviron = peek c_environ_p
26+
27+
foreign import ccall unsafe "&environ"
28+
c_environ_p :: Ptr (Ptr CString)
29+
#endif

System/Posix/Env/PosixString.hsc

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import System.OsString.Internal.Types
4545
import qualified System.OsPath.Data.ByteString.Short as B
4646
import Data.ByteString.Short.Internal ( copyToPtr )
4747

48+
import qualified System.Posix.Env.Internal as Internal
49+
4850
-- |'getEnv' looks up a variable in the environment.
4951

5052
getEnv ::
@@ -70,25 +72,7 @@ foreign import ccall unsafe "getenv"
7072
c_getenv :: CString -> IO CString
7173

7274
getEnvironmentPrim :: IO [PosixString]
73-
getEnvironmentPrim = do
74-
c_environ <- getCEnviron
75-
arr <- peekArray0 nullPtr c_environ
76-
mapM (fmap PS . B.packCString) arr
77-
78-
getCEnviron :: IO (Ptr CString)
79-
#if HAVE__NSGETENVIRON
80-
-- You should not access @char **environ@ directly on Darwin in a bundle/shared library.
81-
-- See #2458 and http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man7/environ.7.html
82-
getCEnviron = nsGetEnviron >>= peek
83-
84-
foreign import ccall unsafe "_NSGetEnviron"
85-
nsGetEnviron :: IO (Ptr (Ptr CString))
86-
#else
87-
getCEnviron = peek c_environ_p
88-
89-
foreign import ccall unsafe "&environ"
90-
c_environ_p :: Ptr (Ptr CString)
91-
#endif
75+
getEnvironmentPrim = Internal.getEnvironmentPrim >>= mapM (fmap PS . B.packCString)
9276

9377
-- |'getEnvironment' retrieves the entire environment as a
9478
-- list of @(key,value)@ pairs.

tests/Test.hs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Data.List (sort)
1313
import System.Exit
1414
import System.IO
1515
import System.Posix
16-
import qualified System.Posix.Env.ByteString
16+
import qualified System.Posix.Env.ByteString as ByteString
1717
import Test.Tasty
1818
import Test.Tasty.HUnit
1919

@@ -28,7 +28,7 @@ main = defaultMain $ testGroup "All"
2828
, fileStatus
2929
, fileStatusByteString
3030
, getEnvironment01
31-
, getEnvironment02
31+
, testSystemPosixEnvByteString
3232
, getGroupEntry
3333
, getUserEntry
3434
, processGroup001
@@ -69,11 +69,24 @@ getEnvironment01 = testCase "getEnvironment01" $ do
6969
not (null env)
7070
@? "environment should be non-empty"
7171

72-
getEnvironment02 :: TestTree
73-
getEnvironment02 = testCase "getEnvironment02" $ do
74-
env <- System.Posix.Env.ByteString.getEnvironment
75-
not (null env)
76-
@? "environment should be non-empty"
72+
protectEnvironment :: IO a -> IO a
73+
protectEnvironment action = E.bracket ByteString.getEnvironment ByteString.setEnvironment $ \ _ -> action
74+
75+
testSystemPosixEnvByteString :: TestTree
76+
testSystemPosixEnvByteString =
77+
testGroup "System.Posix.Env.ByteString" [
78+
testGroup "getEnvironment" [
79+
testCase "returns the environment" $ do
80+
env <- ByteString.getEnvironment
81+
not (null env)
82+
@? "environment should be non-empty"
83+
]
84+
, testGroup "clearEnv" [
85+
testCase "clears the environment" $ protectEnvironment $ do
86+
ByteString.clearEnv
87+
ByteString.getEnvironment >>= (@?= [])
88+
]
89+
]
7790

7891
getGroupEntry :: TestTree
7992
getGroupEntry = testCase "getGroupEntry" $ do
@@ -174,7 +187,7 @@ posix002 = testCase "posix002" $ do
174187
sort (lines actual) @?= ["ONE=1", "TWO=2"]
175188

176189
posix005 :: TestTree
177-
posix005 = testCase "posix005" $ do
190+
posix005 = testCase "posix005" $ protectEnvironment $ do
178191
hSetBuffering stdout NoBuffering
179192

180193
setEnvironment [("one","1"),("two","2")]

unix.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ library
141141
System.Posix.Process.Common
142142
System.Posix.Terminal.Common
143143
System.Posix.User.Common
144+
System.Posix.Env.Internal
144145

145146
ghc-options: -Wall
146147

0 commit comments

Comments
 (0)