Skip to content

Commit

Permalink
test: fix dynamic modules loading on MacOS
Browse files Browse the repository at this point in the history
This patch resolves the issue with running the tests with auxiliary
dynamically loaded modules in case of out-of-source build.

The first issue relates to the way modules to be loaded at runtime are
built on MacOS. Since the auxiliary libraries are built as a dynamically
loaded modules on MacOS instead of shared libraries as it is done on
Linux and BSD, another environment variable should be used to guide
<ffi.load> while searching the extension. Hence the values collected in
scope of <BuildTestCLib> macro need to be set to DYLD_LIBRARY_PATH
variable instead of LD_LIBRARY_PATH on MacOS.

Unfortunately, this rather small change doesn't resolve the problem at
all and the root cause lies much deeper than it seems at the beginning.

Apple tries their best to "protect their users from malicious software".
As a result SIP[1] has been designed and released. Now, Apple developers
are *so protected*, that they can load nothing being not installed in
the system, since some programs sanitize the environment before they
start child processes. Specifically, environment variables starting with
DYLD_ and LD_ are unset for child process started by system programs[2].

That which does not kill us makes us stronger: fortunately, these
environment variables are used by FFI machinery to find the proper
shared library, hence we can still tweak testing environment before
calling <ffi.load>. However, the value can't be passed via the standard
environment variable, so we prepend TEST_ prefix to its name to get
around SIP magic tricks. Finally, to set the variable required by FFI
machinery the introduced <utils.tweakenv> routine is used. PROFIT!
Your move, Cupertino geniuses.

[1]: https://support.apple.com/en-us/HT204899
[2]: https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/RuntimeProtections/RuntimeProtections.html

Resolves tarantool/tarantool#5959
Follows up tarantool/tarantool#4862

Co-authored-by: Sergey Ostanevich <sergos@tarantool.org>
Co-authored-by: Mons Anderson <mons@cpan.org>
Reviewed-by: Sergey Kaplun <skaplun@tarantool.org>
Reviewed-by: Sergey Ostanevich <sergos@tarantool.org>
Signed-off-by: Igor Munkin <imun@tarantool.org>
  • Loading branch information
3 people committed Apr 7, 2021
1 parent 09f6635 commit 4f9108f
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 9 deletions.
38 changes: 35 additions & 3 deletions test/tarantool-tests/CMakeLists.txt
Expand Up @@ -69,11 +69,45 @@ set(LUA_PATH
)
set(LUA_TEST_SUFFIX .test.lua)
set(LUA_TEST_FLAGS --failures --shuffle)
set(LUA_TEST_ENV
"LUA_PATH=\"${LUA_PATH}\;\;\""
"LUA_CPATH=\"${LUA_CPATH}\;\;\""
)

if(CMAKE_VERBOSE_MAKEFILE)
list(APPEND LUA_TEST_FLAGS --verbose)
endif()

# XXX: Since the auxiliary libraries are built as a dynamically
# loaded modules on MacOS instead of shared libraries as it is
# done on Linux and BSD, another environment variable should be
# used to guide <ffi.load> while searching the extension.
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# XXX: Apple tries their best to "protect their users from
# malware". As a result SIP (see the link[1] below) has been
# designed and released. Now, Apple developers are so protected,
# that they can load nothing being not installed in the system,
# since some programs sanitize the environment before they start
# child processes. Specifically, environment variables starting
# with DYLD_ and LD_ are unset for child process started by
# system programs (like /usr/bin/env used for preparing testing
# environment). For more info, see the docs[2] below.
#
# These environment variables are used by FFI machinery to find
# the proper shared library, hence we can still tweak testing
# environment before calling <ffi.load>. However, the value
# can't be passed via the standard environment variable, so we
# prepend TEST_ prefix to its name to get around SIP magic
# tricks. Finally, to set the variable required by FFI machinery
# the introduced <utils.tweakenv> routine is used.
#
# [1]: https://support.apple.com/en-us/HT204899
# [2]: https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/RuntimeProtections/RuntimeProtections.html
list(APPEND LUA_TEST_ENV TEST_DYLD_LIBRARY_PATH="${LD_LIBRARY_PATH}")
else()
list(APPEND LUA_TEST_ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}")
endif()

# LUA_CPATH and LD_LIBRARY_PATH variables and also TESTLIBS list
# with dependecies are set in scope of BuildTestLib macro.
add_custom_target(tarantool-tests
Expand All @@ -83,9 +117,7 @@ add_custom_command(TARGET tarantool-tests
COMMENT "Running Tarantool tests"
COMMAND
env
LUA_PATH="${LUA_PATH}\;\;"
LUA_CPATH="${LUA_CPATH}\;\;"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
${LUA_TEST_ENV}
${PROVE} ${CMAKE_CURRENT_SOURCE_DIR}
--exec '${LUAJIT_TEST_COMMAND}'
--ext ${LUA_TEST_SUFFIX}
Expand Down
4 changes: 4 additions & 0 deletions test/tarantool-tests/gh-4427-ffi-sandwich.test.lua
Expand Up @@ -3,6 +3,10 @@ local utils = require('utils')
-- Disabled on *BSD due to #4819.
utils.skipcond(jit.os == 'BSD', 'Disabled due to #4819')

-- XXX: Tweak the process environment to get around SIP.
-- See the comment in suite CMakeLists.txt for more info.
utils.tweakenv(jit.os == 'OSX', 'DYLD_LIBRARY_PATH')

utils.selfrun(arg, {
{
arg = {
Expand Down
4 changes: 4 additions & 0 deletions test/tarantool-tests/lj-flush-on-trace.test.lua
Expand Up @@ -3,6 +3,10 @@ local utils = require('utils')
-- Disabled on *BSD due to #4819.
utils.skipcond(jit.os == 'BSD', 'Disabled due to #4819')

-- XXX: Tweak the process environment to get around SIP.
-- See the comment in suite CMakeLists.txt for more info.
utils.tweakenv(jit.os == 'OSX', 'DYLD_LIBRARY_PATH')

utils.selfrun(arg, {
{
arg = {
Expand Down
39 changes: 33 additions & 6 deletions test/tarantool-tests/utils.lua
@@ -1,7 +1,12 @@
local M = {}

local ffi = require('ffi')
local tap = require('tap')

ffi.cdef([[
int setenv(const char *name, const char *value, int overwrite);
]])

local function luacmd(args)
-- arg[-1] is guaranteed to be not nil.
local idx = -2
Expand All @@ -13,6 +18,12 @@ local function luacmd(args)
return table.concat(args, ' ', idx + 1, -1)
end

local function unshiftenv(variable, value, sep)
local envvar = os.getenv(variable)
return ('%s="%s%s"'):format(variable, value,
envvar and ('%s%s'):format(sep, envvar) or '')
end

function M.selfrun(arg, checks)
-- If TEST_SELFRUN is set, it means the test has been run via
-- <io.popen>, so just return from this routine and proceed
Expand All @@ -28,18 +39,22 @@ function M.selfrun(arg, checks)

test:plan(#checks)

local libext = package.cpath:match('?.(%a+);')
local vars = {
LUABIN = luacmd(arg),
SCRIPT = arg[0],
PATH = arg[0]:gsub('%.test%.lua', ''),
SUFFIX = package.cpath:match('?.(%a+);'),
SUFFIX = libext,
ENV = table.concat({
unshiftenv('LUA_PATH', '<PATH>/?.lua', ';'),
unshiftenv('LUA_CPATH', '<PATH>/?.<SUFFIX>', ';'),
unshiftenv((libext == 'dylib' and 'DYLD' or 'LD') .. '_LIBRARY_PATH',
'<PATH>', ':'),
'TEST_SELFRUN=1',
}, ' '),
}

local cmd = string.gsub('LUA_PATH="<PATH>/?.lua;$LUA_PATH" ' ..
'LUA_CPATH="<PATH>/?.<SUFFIX>;$LUA_CPATH" ' ..
'LD_LIBRARY_PATH=<PATH>:$LD_LIBRARY_PATH ' ..
'TEST_SELFRUN=1' ..
'<LUABIN> 2>&1 <SCRIPT>', '%<(%w+)>', vars)
local cmd = string.gsub('<ENV> <LUABIN> 2>&1 <SCRIPT>', '%<(%w+)>', vars)

for _, ch in pairs(checks) do
local testf = test[ch.test]
Expand All @@ -62,4 +77,16 @@ function M.skipcond(condition, message)
os.exit(test:check() and 0 or 1)
end

function M.tweakenv(condition, variable)
if not condition or os.getenv(variable) then return end
local testvar = assert(os.getenv('TEST_' .. variable),
('Neither %s nor auxiliary TEST_%s variables are set')
:format(variable, variable))
-- XXX: The third argument of setenv(3) is set to zero to forbid
-- overwriting the <variable>. Since there is the check above
-- whether this <variable> is set in the process environment, it
-- just makes this solution foolproof.
ffi.C.setenv(variable, testvar, 0)
end

return M

0 comments on commit 4f9108f

Please sign in to comment.