Skip to content

Commit

Permalink
Take care with special characters in paths to configure scripts.
Browse files Browse the repository at this point in the history
On all platforms, warn about their presence. On Windows, we should use
slashes (as opposed to backslashes) where possible, to avoid causing
things like #5386.

Closes #5386.
  • Loading branch information
quasicomputational committed Jun 20, 2018
1 parent 9df6d54 commit 48d7b60
Show file tree
Hide file tree
Showing 21 changed files with 5,524 additions and 4 deletions.
5 changes: 5 additions & 0 deletions Cabal/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
* Recognize `powerpc64le` as architecture PPC64.
* Cabal now deduplicates more `-I` and `-L` and flags to avoid `E2BIG`
([#5356](https://github.com/haskell/cabal/issues/5356)).
* With `build-type: configure`, avoid using backslashes to delimit
path components on Windows and warn about other unsafe characters
in the path to the source directory on all platforms
([#5386](https://github.com/haskell/cabal/issues/5386)).

----

## 2.2.0.1 (current 2.2 development version)
Expand Down
49 changes: 47 additions & 2 deletions Cabal/Distribution/Simple.hs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ import System.Environment (getArgs, getProgName)
import System.Directory (removeFile, doesFileExist
,doesDirectoryExist, removeDirectoryRecursive)
import System.Exit (exitWith,ExitCode(..))
import System.FilePath (searchPathSeparator, takeDirectory, (</>))
import System.FilePath (searchPathSeparator, takeDirectory, (</>), splitDirectories, dropDrive)
import Distribution.Compat.Directory (makeAbsolute)
import Distribution.Compat.Environment (getEnvironment)
import Distribution.Compat.GetShortPathName (getShortPathName)
Expand Down Expand Up @@ -726,6 +726,27 @@ runConfigureScript verbosity backwardsCompatHack flags lbi = do
-- a way to pass its flags too
configureFile <- makeAbsolute $
fromMaybe "." (takeDirectory <$> cabalFilePath lbi) </> "configure"
-- autoconf is fussy about filenames, and has a set of forbidden
-- characters that can't appear in the build directory, etc:
-- https://www.gnu.org/software/autoconf/manual/autoconf.html#File-System-Conventions
--
-- This has caused hard-to-debug failures in the past (#5368), so we
-- detect some cases early and warn with a clear message. Windows's
-- use of backslashes is problematic here, so we'll switch to
-- slashes, but we do still want to fail on backslashes in POSIX
-- paths.
--
-- TODO: We don't check for colons, tildes or leading dashes. We
-- also should check the builddir's path, destdir, and all other
-- paths as well.
let configureFile' = intercalate "/" $ splitDirectories configureFile
for_ badAutoconfCharacters $ \(c, cname) ->
when (c `elem` dropDrive configureFile') $
warn verbosity $
"The path to the './configure' script, '" ++ configureFile'
++ "', contains the character '" ++ [c] ++ "' (" ++ cname ++ ")."
++ " This may cause the script to fail with an obscure error, or for"
++ " building the package to fail later."
let extraPath = fromNubList $ configProgramPathExtra flags
let cflagsEnv = maybe (unwords ccFlags) (++ (" " ++ unwords ccFlags))
$ lookup "CFLAGS" env
Expand All @@ -734,7 +755,7 @@ runConfigureScript verbosity backwardsCompatHack flags lbi = do
((intercalate spSep extraPath ++ spSep)++) $ lookup "PATH" env
overEnv = ("CFLAGS", Just cflagsEnv) :
[("PATH", Just pathEnv) | not (null extraPath)]
args' = configureFile:args ++ ["CC=" ++ ccProgShort]
args' = configureFile':args ++ ["CC=" ++ ccProgShort]
shProg = simpleProgram "sh"
progDb = modifyProgramSearchPath
(\p -> map ProgramSearchPathDir extraPath ++ p) emptyProgramDb
Expand All @@ -749,6 +770,30 @@ runConfigureScript verbosity backwardsCompatHack flags lbi = do
where
args = configureArgs backwardsCompatHack flags

badAutoconfCharacters =
[ (' ', "space")
, ('\t', "tab")
, ('\n', "newline")
, ('\0', "null")
, ('"', "double quote")
, ('#', "hash")
, ('$', "dollar sign")
, ('&', "ampersand")
, ('\'', "single quote")
, ('(', "left bracket")
, (')', "right bracket")
, ('*', "star")
, (';', "semicolon")
, ('<', "less-than sign")
, ('=', "equals sign")
, ('>', "greater-than sign")
, ('?', "question mark")
, ('[', "left square bracket")
, ('\\', "backslash")
, ('`', "backtick")
, ('|', "pipe")
]

notFoundMsg = "The package has a './configure' script. "
++ "If you are on Windows, This requires a "
++ "Unix compatibility toolchain such as MinGW+MSYS or Cygwin. "
Expand Down
9 changes: 9 additions & 0 deletions Cabal/doc/developing-packages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,15 @@ management. The need to remain compatible with automatic package
management means that Cabal's conditional dependencies system is a bit
less flexible than with the "./configure" approach.

.. note::
`GNU autoconf places restrictions on paths, including the
path that the user builds a package from.
<https://www.gnu.org/software/autoconf/manual/autoconf.html#File-System-Conventions>`_
Package authors using ``build-type: configure`` should be aware of
these restrictions; because users may be unexpectedly constrained and
face mysterious errors, it is recommended that ``build-type: configure``
is only used where strictly necessary.

Portability
-----------

Expand Down
7 changes: 7 additions & 0 deletions Cabal/doc/installing-packages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,13 @@ passed the :option:`--with-hc-pkg`, :option:`--prefix`, :option:`--bindir`,
:option:`--with-compiler` option is passed in a :option:`--with-hc-pkg` option
and all options specified with :option:`--configure-option` are passed on.

.. note::
`GNU autoconf places restrictions on paths, including the directory
that the package is built from.
<https://www.gnu.org/software/autoconf/manual/autoconf.html#File-System-Conventions>`_
The errors produced when this happens can be obscure; Cabal attempts to
detect and warn in this situation, but it is not perfect.

In Cabal 2.0, support for a single positional argument was added to
``setup configure`` This makes Cabal configure a the specific component
to be configured. Specified names can be qualified with ``lib:`` or
Expand Down
6 changes: 6 additions & 0 deletions cabal-testsuite/PackageTests/AutoconfBadPaths/Setup.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Main (main) where

import Distribution.Simple

main :: IO ()
main = defaultMainWithHooks autoconfUserHooks
82 changes: 82 additions & 0 deletions cabal-testsuite/PackageTests/AutoconfBadPaths/cabal.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo bar/configure', contains the character ' ' (space). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo bar/configure', contains the character ' ' (tab). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo
bar/configure', contains the character '
' (newline). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo"bar/configure', contains the character '"' (double quote). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo#bar/configure', contains the character '#' (hash). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo$bar/configure', contains the character '$' (dollar sign). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo&bar/configure', contains the character '&' (ampersand). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo'bar/configure', contains the character ''' (single quote). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo(bar/configure', contains the character '(' (left bracket). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo)bar/configure', contains the character ')' (right bracket). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo*bar/configure', contains the character '*' (star). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo;bar/configure', contains the character ';' (semicolon). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo<bar/configure', contains the character '<' (less-than sign). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo=bar/configure', contains the character '=' (equals sign). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo>bar/configure', contains the character '>' (greater-than sign). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo?bar/configure', contains the character '?' (question mark). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo[bar/configure', contains the character '[' (left square bracket). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo/bar/configure', contains the character '/' (backslash). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo`bar/configure', contains the character '`' (backtick). This may cause the script to fail with an obscure error, or for building the package to fail later.
# cabal v1-configure
Resolving dependencies...
Configuring test-0...
Warning: The path to the './configure' script, '/<ROOT>/cabal.dist/foo|bar/configure', contains the character '|' (pipe). This may cause the script to fail with an obscure error, or for building the package to fail later.
53 changes: 53 additions & 0 deletions cabal-testsuite/PackageTests/AutoconfBadPaths/cabal.test.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Test.Cabal.Prelude
import Data.Foldable (traverse_)
main = cabalTest $ do
-- Test the forbidden characters except NUL. Reference:
-- https://www.gnu.org/software/autoconf/manual/autoconf.html#File-System-Conventions
-- Most of these are magic on Windows, so don't bother testing there.
--
-- Note: we bundle the configure script so no need to autoreconf
-- while building
skipIf =<< isWindows
traverse_ check
[ "foo bar"
, "foo\tbar"
, "foo\nbar"
, "foo\"bar"
, "foo#bar"
, "foo$bar"
, "foo&bar"
, "foo'bar"
, "foo(bar"
, "foo)bar"
, "foo*bar"
, "foo;bar"
, "foo<bar"
, "foo=bar"
, "foo>bar"
, "foo?bar"
, "foo[bar"
, "foo\\bar"
, "foo`bar"
, "foo|bar"
]
where
-- 'cabal' from the prelude requires the command to succeed; we
-- don't mind if it fails, so long as we get the warning. This is
-- an inlined+specialised version of 'cabal' for v1-configure.
check dir = withSourceCopyDir dir $
defaultRecordMode RecordMarked $ do
recordHeader ["cabal", "v1-configure"]
env <- getTestEnv
let args =
[ "v1-configure"
, "-vverbose +markoutput +nowrap"
, "--builddir"
, testDistDir env
]
configured_prog <- requireProgramM cabalProgram
r <- liftIO $ run (testVerbosity env)
(Just (testCurrentDir env))
(testEnvironment env)
(programPath configured_prog)
args
recordLog r
Loading

0 comments on commit 48d7b60

Please sign in to comment.