Commit e28462de authored by Austin Seipp's avatar Austin Seipp

base: fix #10298 & #7695

Summary:
This applies a patch from Reid Barton and Sylvain Henry, which fix a
disasterous infinite loop when iconv fails to load locale files, as
specified in #10298.

The fix is a bit of a hack but should be fine - for the actual reasoning
behind it, see `Note [Disaster and iconv]` for more info.

In addition to this fix, we also patch up the IO Encoding utilities to
recognize several variations of the 'ASCII' encoding (including its
aliases) directly so that GHC can do conversions without iconv. This
allows a static binary to sit in an initramfs.
Authored-by: rwbarton's avatarReid Barton <rwbarton@gmail.com>
Authored-by: default avatarSylvain Henry <hsyl20@gmail.com>
Signed-off-by: default avatarAustin Seipp <austin@well-typed.com>

Test Plan: Eyeballed it.

Reviewers: rwbarton, hvr

Subscribers: bgamari, thomie

Differential Revision: https://phabricator.haskell.org/D898

GHC Trac Issues: #10298, #7695
parent 640fe142
...@@ -235,7 +235,14 @@ mkTextEncoding e = case mb_coding_failure_mode of ...@@ -235,7 +235,14 @@ mkTextEncoding e = case mb_coding_failure_mode of
_ -> Nothing _ -> Nothing
mkTextEncoding' :: CodingFailureMode -> String -> IO TextEncoding mkTextEncoding' :: CodingFailureMode -> String -> IO TextEncoding
mkTextEncoding' cfm enc = case [toUpper c | c <- enc, c /= '-'] of mkTextEncoding' cfm enc
-- First, specifically match on ASCII encodings directly using
-- several possible aliases (specified by RFC 1345 & co), which
-- allows us to handle ASCII conversions without iconv at all (see
-- trac #10298).
| any (== enc) ansiEncNames = return (UTF8.mkUTF8 cfm)
-- Otherwise, handle other encoding needs via iconv.
| otherwise = case [toUpper c | c <- enc, c /= '-'] of
"UTF8" -> return $ UTF8.mkUTF8 cfm "UTF8" -> return $ UTF8.mkUTF8 cfm
"UTF16" -> return $ UTF16.mkUTF16 cfm "UTF16" -> return $ UTF16.mkUTF16 cfm
"UTF16LE" -> return $ UTF16.mkUTF16le cfm "UTF16LE" -> return $ UTF16.mkUTF16le cfm
...@@ -249,6 +256,11 @@ mkTextEncoding' cfm enc = case [toUpper c | c <- enc, c /= '-'] of ...@@ -249,6 +256,11 @@ mkTextEncoding' cfm enc = case [toUpper c | c <- enc, c /= '-'] of
#else #else
_ -> Iconv.mkIconvEncoding cfm enc _ -> Iconv.mkIconvEncoding cfm enc
#endif #endif
where
ansiEncNames = -- ASCII aliases
[ "ANSI_X3.4-1968", "iso-ir-6", "ANSI_X3.4-1986", "ISO_646.irv:1991"
, "US-ASCII", "us", "IBM367", "cp367", "csASCII", "ASCII", "ISO646-US"
]
latin1_encode :: CharBuffer -> Buffer Word8 -> IO (CharBuffer, Buffer Word8) latin1_encode :: CharBuffer -> Buffer Word8 -> IO (CharBuffer, Buffer Word8)
latin1_encode input output = fmap (\(_why,input',output') -> (input',output')) $ Latin1.latin1_encode input output -- unchecked, used for char8 latin1_encode input output = fmap (\(_why,input',output') -> (input',output')) $ Latin1.latin1_encode input output -- unchecked, used for char8
......
...@@ -157,13 +157,40 @@ real_handler exit se = do ...@@ -157,13 +157,40 @@ real_handler exit se = do
Just (ExitFailure n) -> exit n Just (ExitFailure n) -> exit n
-- EPIPE errors received for stdout are ignored (#2699) -- EPIPE errors received for stdout are ignored (#2699)
_ -> case fromException se of _ -> catch (case fromException se of
Just IOError{ ioe_type = ResourceVanished, Just IOError{ ioe_type = ResourceVanished,
ioe_errno = Just ioe, ioe_errno = Just ioe,
ioe_handle = Just hdl } ioe_handle = Just hdl }
| Errno ioe == ePIPE, hdl == stdout -> exit 0 | Errno ioe == ePIPE, hdl == stdout -> exit 0
_ -> do reportError se _ -> do reportError se
exit 1 exit 1
) (disasterHandler exit) -- See Note [Disaster with iconv]
-- don't use errorBelch() directly, because we cannot call varargs functions
-- using the FFI.
foreign import ccall unsafe "HsBase.h errorBelch2"
errorBelch :: CString -> CString -> IO ()
disasterHandler :: (Int -> IO a) -> IOError -> IO a
disasterHandler exit _ =
withCAString "%s" $ \fmt ->
withCAString msgStr $ \msg ->
errorBelch fmt msg >> exit 1
where msgStr = "encountered an exception while trying to report an exception"
{- Note [Disaster with iconv]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using iconv, it's possible for things like iconv_open to fail in
restricted environments (like an initram or restricted container), but
when this happens the error raised inevitably calls `peekCString`,
which depends on the users locale, which depends on using
`iconv_open`... which causes an infinite loop.
This occurrence is also known as tickets #10298 and #7695. So to work
around it we just set _another_ error handler and bail directly by
calling the RTS, without iconv at all.
-}
-- try to flush stdout/stderr, but don't worry if we fail -- try to flush stdout/stderr, but don't worry if we fail
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment