Skip to content

Commit 9e530c6

Browse files
committed
Merge bitcoin/bitcoin#20487: Add syscall sandboxing using seccomp-bpf (Linux secure computing mode)
4747da3 Add syscall sandboxing (seccomp-bpf) (practicalswift) Pull request description: Add experimental syscall sandboxing using seccomp-bpf (Linux secure computing mode). Enable filtering of system calls using seccomp-bpf: allow only explicitly allowlisted (expected) syscalls to be called. The syscall sandboxing implemented in this PR is an experimental feature currently available only under Linux x86-64. To enable the experimental syscall sandbox the `-sandbox=<mode>` option must be passed to `bitcoind`: ``` -sandbox=<mode> Use the experimental syscall sandbox in the specified mode (-sandbox=log-and-abort or -sandbox=abort). Allow only expected syscalls to be used by bitcoind. Note that this is an experimental new feature that may cause bitcoind to exit or crash unexpectedly: use with caution. In the "log-and-abort" mode the invocation of an unexpected syscall results in a debug handler being invoked which will log the incident and terminate the program (without executing the unexpected syscall). In the "abort" mode the invocation of an unexpected syscall results in the entire process being killed immediately by the kernel without executing the unexpected syscall. ``` The allowed syscalls are defined on a per thread basis. I've used this feature since summer 2020 and I find it to be a helpful testing/debugging addition which makes it much easier to reason about the actual capabilities required of each type of thread in Bitcoin Core. --- Quick start guide: ``` $ ./configure $ src/bitcoind -regtest -debug=util -sandbox=log-and-abort … 2021-06-09T12:34:56Z Experimental syscall sandbox enabled (-sandbox=log-and-abort): bitcoind will terminate if an unexpected (not allowlisted) syscall is invoked. … 2021-06-09T12:34:56Z Syscall filter installed for thread "addcon" 2021-06-09T12:34:56Z Syscall filter installed for thread "dnsseed" 2021-06-09T12:34:56Z Syscall filter installed for thread "net" 2021-06-09T12:34:56Z Syscall filter installed for thread "msghand" 2021-06-09T12:34:56Z Syscall filter installed for thread "opencon" 2021-06-09T12:34:56Z Syscall filter installed for thread "init" … # A simulated execve call to show the sandbox in action: 2021-06-09T12:34:56Z ERROR: The syscall "execve" (syscall number 59) is not allowed by the syscall sandbox in thread "msghand". Please report. … Aborted (core dumped) $ ``` --- [About seccomp and seccomp-bpf](https://en.wikipedia.org/wiki/Seccomp): > In computer security, seccomp (short for secure computing mode) is a facility in the Linux kernel. seccomp allows a process to make a one-way transition into a "secure" state where it cannot make any system calls except exit(), sigreturn(), and read() and write() to already-open file descriptors. Should it attempt any other system calls, the kernel will terminate the process with SIGKILL or SIGSYS. In this sense, it does not virtualize the system's resources but isolates the process from them entirely. > > […] > > seccomp-bpf is an extension to seccomp that allows filtering of system calls using a configurable policy implemented using Berkeley Packet Filter rules. It is used by OpenSSH and vsftpd as well as the Google Chrome/Chromium web browsers on Chrome OS and Linux. (In this regard seccomp-bpf achieves similar functionality, but with more flexibility and higher performance, to the older systrace—which seems to be no longer supported for Linux.) ACKs for top commit: laanwj: Code review and lightly tested ACK 4747da3 Tree-SHA512: e1c28e323eb4409a46157b7cc0fc29a057ba58d1ee2de268962e2ade28ebd4421b5c2536c64a3af6e9bd3f54016600fec88d016adb49864b63edea51ad838e17
2 parents 42fedb4 + 4747da3 commit 9e530c6

27 files changed

+1125
-1
lines changed

ci/test/00_setup_env_i686_multiprocess.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ export DEP_OPTS="DEBUG=1 MULTIPROCESS=1"
1414
export GOAL="install"
1515
export BITCOIN_CONFIG="--enable-debug CC='clang -m32' CXX='clang++ -m32' LDFLAGS='--rtlib=compiler-rt -lgcc_s'"
1616
export TEST_RUNNER_ENV="BITCOIND=bitcoin-node"
17+
export TEST_RUNNER_EXTRA="--nosandbox"
1718
export PIP_PACKAGES="lief"

configure.ac

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ case $host in
7171
;;
7272
esac
7373

74+
AC_ARG_WITH([seccomp],
75+
[AS_HELP_STRING([--with-seccomp],
76+
[enable experimental syscall sandbox feature (-sandbox), default is yes if seccomp-bpf is detected under Linux x86_64])],
77+
[seccomp_found=$withval],
78+
[seccomp_found=auto])
79+
7480
dnl Require C++17 compiler (no GNU extensions)
7581
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
7682

@@ -1432,6 +1438,36 @@ if test "x$use_external_signer" != xno; then
14321438
fi
14331439
AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "x$use_external_signer" = "xyes"])
14341440

1441+
dnl Do not compile with syscall sandbox support when compiling under the sanitizers.
1442+
dnl The sanitizers introduce use of syscalls that are not typically used in bitcoind
1443+
dnl (such as execve when the sanitizers execute llvm-symbolizer).
1444+
if test x$use_sanitizers != x; then
1445+
AC_MSG_WARN(Specifying --with-sanitizers forces --without-seccomp since the sanitizers introduce use of syscalls not allowed by the bitcoind syscall sandbox (-sandbox=<mode>).)
1446+
seccomp_found=no
1447+
fi
1448+
if test "x$seccomp_found" != "xno"; then
1449+
AC_MSG_CHECKING([for seccomp-bpf (Linux x86-64)])
1450+
AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[
1451+
@%:@include <linux/seccomp.h>
1452+
]], [[
1453+
#if !defined(__x86_64__)
1454+
# error Syscall sandbox is an experimental feature currently available only under Linux x86-64.
1455+
#endif
1456+
]])],[
1457+
AC_MSG_RESULT(yes)
1458+
seccomp_found="yes"
1459+
AC_DEFINE(USE_SYSCALL_SANDBOX, 1, [Define this symbol to build with syscall sandbox support.])
1460+
],[
1461+
AC_MSG_RESULT(no)
1462+
seccomp_found="no"
1463+
])
1464+
fi
1465+
dnl Currently only enable -sandbox=<mode> feature if seccomp is found.
1466+
dnl In the future, sandboxing could be also be supported with other
1467+
dnl sandboxing mechanisms besides seccomp.
1468+
use_syscall_sandbox=$seccomp_found
1469+
AM_CONDITIONAL([ENABLE_SYSCALL_SANDBOX], [test "x$use_syscall_sandbox" != "xno"])
1470+
14351471
dnl Check for reduced exports
14361472
if test x$use_reduce_exports = xyes; then
14371473
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[CXXFLAGS="$CXXFLAGS -fvisibility=hidden"],
@@ -1921,6 +1957,7 @@ echo
19211957
echo "Options used to compile and link:"
19221958
echo " external signer = $use_external_signer"
19231959
echo " multiprocess = $build_multiprocess"
1960+
echo " with experimental syscall sandbox support = $use_syscall_sandbox"
19241961
echo " with libs = $build_bitcoin_libs"
19251962
echo " with wallet = $enable_wallet"
19261963
if test "x$enable_wallet" != "xno"; then

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ BITCOIN_CORE_H = \
261261
util/sock.h \
262262
util/spanparsing.h \
263263
util/string.h \
264+
util/syscall_sandbox.h \
264265
util/system.h \
265266
util/thread.h \
266267
util/threadnames.h \
@@ -611,6 +612,7 @@ libbitcoin_util_a_SOURCES = \
611612
util/spanparsing.cpp \
612613
util/strencodings.cpp \
613614
util/string.cpp \
615+
util/syscall_sandbox.cpp \
614616
util/time.cpp \
615617
util/tokenpipe.cpp \
616618
$(BITCOIN_CORE_H)

src/bitcoind.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <shutdown.h>
2020
#include <util/check.h>
2121
#include <util/strencodings.h>
22+
#include <util/syscall_sandbox.h>
2223
#include <util/system.h>
2324
#include <util/threadnames.h>
2425
#include <util/tokenpipe.h>
@@ -238,6 +239,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
238239
daemon_ep.Close();
239240
}
240241
#endif
242+
SetSyscallSandboxPolicy(SyscallSandboxPolicy::SHUTOFF);
241243
if (fRet) {
242244
WaitForShutdown();
243245
}

src/checkqueue.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <sync.h>
99
#include <tinyformat.h>
10+
#include <util/syscall_sandbox.h>
1011
#include <util/threadnames.h>
1112

1213
#include <algorithm>
@@ -151,6 +152,7 @@ class CCheckQueue
151152
for (int n = 0; n < threads_num; ++n) {
152153
m_worker_threads.emplace_back([this, n]() {
153154
util::ThreadRename(strprintf("scriptch.%i", n));
155+
SetSyscallSandboxPolicy(SyscallSandboxPolicy::VALIDATION_SCRIPT_CHECK);
154156
Loop(false /* worker thread */);
155157
});
156158
}

src/httpserver.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <shutdown.h>
1313
#include <sync.h>
1414
#include <util/strencodings.h>
15+
#include <util/syscall_sandbox.h>
1516
#include <util/system.h>
1617
#include <util/threadnames.h>
1718
#include <util/translation.h>
@@ -279,6 +280,7 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
279280
static bool ThreadHTTP(struct event_base* base)
280281
{
281282
util::ThreadRename("http");
283+
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_HTTP_SERVER);
282284
LogPrint(BCLog::HTTP, "Entering http event loop\n");
283285
event_base_dispatch(base);
284286
// Event loop will be interrupted by InterruptHTTPServer()
@@ -332,6 +334,7 @@ static bool HTTPBindAddresses(struct evhttp* http)
332334
static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
333335
{
334336
util::ThreadRename(strprintf("httpworker.%i", worker_num));
337+
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_HTTP_SERVER_WORKER);
335338
queue->Run();
336339
}
337340

src/index/base.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <node/ui_interface.h>
99
#include <shutdown.h>
1010
#include <tinyformat.h>
11+
#include <util/syscall_sandbox.h>
1112
#include <util/thread.h>
1213
#include <util/translation.h>
1314
#include <validation.h> // For g_chainman
@@ -123,6 +124,7 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain&
123124

124125
void BaseIndex::ThreadSync()
125126
{
127+
SetSyscallSandboxPolicy(SyscallSandboxPolicy::TX_INDEX);
126128
const CBlockIndex* pindex = m_best_block_index.load();
127129
if (!m_synced) {
128130
auto& consensus_params = Params().GetConsensus();

src/init.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include <util/check.h>
6161
#include <util/moneystr.h>
6262
#include <util/string.h>
63+
#include <util/syscall_sandbox.h>
6364
#include <util/system.h>
6465
#include <util/thread.h>
6566
#include <util/threadnames.h>
@@ -562,6 +563,10 @@ void SetupServerArgs(ArgsManager& argsman)
562563
hidden_args.emplace_back("-daemonwait");
563564
#endif
564565

566+
#if defined(USE_SYSCALL_SANDBOX)
567+
argsman.AddArg("-sandbox=<mode>", "Use the experimental syscall sandbox in the specified mode (-sandbox=log-and-abort or -sandbox=abort). Allow only expected syscalls to be used by bitcoind. Note that this is an experimental new feature that may cause bitcoind to exit or crash unexpectedly: use with caution. In the \"log-and-abort\" mode the invocation of an unexpected syscall results in a debug handler being invoked which will log the incident and terminate the program (without executing the unexpected syscall). In the \"abort\" mode the invocation of an unexpected syscall results in the entire process being killed immediately by the kernel without executing the unexpected syscall.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
568+
#endif // USE_SYSCALL_SANDBOX
569+
565570
// Add the hidden options
566571
argsman.AddHiddenArgs(hidden_args);
567572
}
@@ -1018,6 +1023,37 @@ bool AppInitParameterInteraction(const ArgsManager& args)
10181023
return InitError(_("No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
10191024
}
10201025

1026+
#if defined(USE_SYSCALL_SANDBOX)
1027+
if (args.IsArgSet("-sandbox") && !args.IsArgNegated("-sandbox")) {
1028+
const std::string sandbox_arg{args.GetArg("-sandbox", "")};
1029+
bool log_syscall_violation_before_terminating{false};
1030+
if (sandbox_arg == "log-and-abort") {
1031+
log_syscall_violation_before_terminating = true;
1032+
} else if (sandbox_arg == "abort") {
1033+
// log_syscall_violation_before_terminating is false by default.
1034+
} else {
1035+
return InitError(Untranslated("Unknown syscall sandbox mode (-sandbox=<mode>). Available modes are \"log-and-abort\" and \"abort\"."));
1036+
}
1037+
// execve(...) is not allowed by the syscall sandbox.
1038+
const std::vector<std::string> features_using_execve{
1039+
"-alertnotify",
1040+
"-blocknotify",
1041+
"-signer",
1042+
"-startupnotify",
1043+
"-walletnotify",
1044+
};
1045+
for (const std::string& feature_using_execve : features_using_execve) {
1046+
if (!args.GetArg(feature_using_execve, "").empty()) {
1047+
return InitError(Untranslated(strprintf("The experimental syscall sandbox feature (-sandbox=<mode>) is incompatible with %s (which uses execve).", feature_using_execve)));
1048+
}
1049+
}
1050+
if (!SetupSyscallSandbox(log_syscall_violation_before_terminating)) {
1051+
return InitError(Untranslated("Installation of the syscall sandbox failed."));
1052+
}
1053+
LogPrintf("Experimental syscall sandbox enabled (-sandbox=%s): bitcoind will terminate if an unexpected (not allowlisted) syscall is invoked.\n", sandbox_arg);
1054+
}
1055+
#endif // USE_SYSCALL_SANDBOX
1056+
10211057
return true;
10221058
}
10231059

src/logging.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ const CLogCategoryDesc LogCategories[] =
160160
{BCLog::I2P, "i2p"},
161161
{BCLog::IPC, "ipc"},
162162
{BCLog::LOCK, "lock"},
163+
{BCLog::UTIL, "util"},
163164
{BCLog::ALL, "1"},
164165
{BCLog::ALL, "all"},
165166
};

src/logging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ namespace BCLog {
6060
I2P = (1 << 22),
6161
IPC = (1 << 23),
6262
LOCK = (1 << 24),
63+
UTIL = (1 << 25),
6364
ALL = ~(uint32_t)0,
6465
};
6566

0 commit comments

Comments
 (0)