forked from lix-project/lix
Compare commits
11 commits
f4dd67e436
...
7685a0f5e0
Author | SHA1 | Date | |
---|---|---|---|
|
7685a0f5e0 | ||
|
4e6306e130 | ||
|
ce6cb14995 | ||
|
3d155fc509 | ||
|
b43a2e84c4 | ||
|
a9949f4760 | ||
|
39a1e248c9 | ||
|
f80d95e36d | ||
|
12f5d27363 | ||
|
fb8553f63c | ||
|
e09cc60df9 |
|
@ -164,6 +164,7 @@
|
||||||
nixUnstable = prev.nixUnstable;
|
nixUnstable = prev.nixUnstable;
|
||||||
|
|
||||||
check-headers = final.buildPackages.callPackage ./maintainers/check-headers.nix { };
|
check-headers = final.buildPackages.callPackage ./maintainers/check-headers.nix { };
|
||||||
|
check-syscalls = final.buildPackages.callPackage ./maintainers/check-syscalls.nix { };
|
||||||
|
|
||||||
default-busybox-sandbox-shell = final.busybox.override {
|
default-busybox-sandbox-shell = final.busybox.override {
|
||||||
useMusl = true;
|
useMusl = true;
|
||||||
|
|
16
maintainers/check-syscalls.nix
Normal file
16
maintainers/check-syscalls.nix
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
runCommandNoCC,
|
||||||
|
lib,
|
||||||
|
libseccomp,
|
||||||
|
writeShellScriptBin,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
syscalls-csv = runCommandNoCC "syscalls.csv" { } ''
|
||||||
|
echo ${lib.escapeShellArg libseccomp.src}
|
||||||
|
tar -xf ${lib.escapeShellArg libseccomp.src} --strip-components=2 ${libseccomp.name}/src/syscalls.csv
|
||||||
|
mv syscalls.csv "$out"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
writeShellScriptBin "check-syscalls" ''
|
||||||
|
${./check-syscalls.sh} ${syscalls-csv}
|
||||||
|
''
|
7
maintainers/check-syscalls.sh
Executable file
7
maintainers/check-syscalls.sh
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
diff -u <(awk < src/libstore/build/local-derivation-goal.cc '/BEGIN extract-syscalls/ { extracting = 1; next }
|
||||||
|
match($0, /allowSyscall\(ctx, SCMP_SYS\(([^)]*)\)\);|\/\/ skip ([^ ]*)/, result) { print result[1] result[2] }
|
||||||
|
/END extract-syscalls/ { extracting = 0; next }') <(tail -n+2 "$1" | cut -d, -f 1)
|
|
@ -402,6 +402,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
# Lix specific packages
|
# Lix specific packages
|
||||||
pre-commit-checks,
|
pre-commit-checks,
|
||||||
contribNotice,
|
contribNotice,
|
||||||
|
check-syscalls,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
glibcFix = lib.optionalAttrs (buildPlatform.isLinux && glibcLocales != null) {
|
glibcFix = lib.optionalAttrs (buildPlatform.isLinux && glibcLocales != null) {
|
||||||
|
@ -455,6 +456,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
pythonEnv
|
pythonEnv
|
||||||
# docker image tool
|
# docker image tool
|
||||||
skopeo
|
skopeo
|
||||||
|
check-syscalls
|
||||||
just
|
just
|
||||||
nixfmt
|
nixfmt
|
||||||
# Load-bearing order. Must come before clang-unwrapped below, but after clang_tools above.
|
# Load-bearing order. Must come before clang-unwrapped below, but after clang_tools above.
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "primops.hh"
|
#include "primops.hh"
|
||||||
#include "print-options.hh"
|
#include "print-options.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
|
#include "suggestions.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
@ -1426,11 +1427,13 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
|
||||||
|
|
||||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vTmp;
|
Value vFirst;
|
||||||
PosIdx pos2;
|
|
||||||
Value * vAttrs = &vTmp;
|
|
||||||
|
|
||||||
e->eval(state, env, vTmp);
|
// Pointer to the current attrset Value in this select chain.
|
||||||
|
Value * vCurrent = &vFirst;
|
||||||
|
// Position for the current attrset Value in this select chain.
|
||||||
|
PosIdx posCurrent;
|
||||||
|
e->eval(state, env, vFirst);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto dts = state.debugRepl
|
auto dts = state.debugRepl
|
||||||
|
@ -1443,48 +1446,75 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
showAttrPath(state, env, attrPath))
|
showAttrPath(state, env, attrPath))
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
|
||||||
for (auto & i : attrPath) {
|
for (auto const & currentAttrName : attrPath) {
|
||||||
state.nrLookups++;
|
state.nrLookups++;
|
||||||
Bindings::iterator j;
|
|
||||||
auto name = getName(i, state, env);
|
Symbol const name = getName(currentAttrName, state, env);
|
||||||
if (def) {
|
|
||||||
state.forceValue(*vAttrs, pos);
|
state.forceValue(*vCurrent, pos);
|
||||||
if (vAttrs->type() != nAttrs ||
|
|
||||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
if (vCurrent->type() != nAttrs) {
|
||||||
{
|
|
||||||
def->eval(state, env, v);
|
// If we have an `or` provided default,
|
||||||
|
// then this is allowed to not be an attrset.
|
||||||
|
if (def != nullptr) {
|
||||||
|
this->def->eval(state, env, v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
|
// Otherwise, we must type error.
|
||||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
state.error<TypeError>(
|
||||||
std::set<std::string> allAttrNames;
|
"expected a set but found %s: %s",
|
||||||
for (auto & attr : *vAttrs->attrs)
|
showType(*vCurrent),
|
||||||
allAttrNames.insert(state.symbols[attr.name]);
|
ValuePrinter(state, *vCurrent, errorPrintOptions)
|
||||||
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
).withTrace(pos, "while selecting an attribute").debugThrow();
|
||||||
state.error<EvalError>("attribute '%1%' missing", state.symbols[name])
|
|
||||||
.atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vAttrs = j->value;
|
|
||||||
pos2 = j->pos;
|
// Now that we know this is actually an attrset, try to find an attr
|
||||||
if (state.countCalls) state.attrSelects[pos2]++;
|
// with the selected name.
|
||||||
|
Bindings::iterator attrIt = vCurrent->attrs->find(name);
|
||||||
|
if (attrIt == vCurrent->attrs->end()) {
|
||||||
|
|
||||||
|
// If we have an `or` provided default, then we'll use that.
|
||||||
|
if (def != nullptr) {
|
||||||
|
this->def->eval(state, env, v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, missing attr error.
|
||||||
|
std::set<std::string> allAttrNames;
|
||||||
|
for (auto const & attr : *vCurrent->attrs) {
|
||||||
|
allAttrNames.insert(state.symbols[attr.name]);
|
||||||
|
}
|
||||||
|
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
||||||
|
state.error<EvalError>("attribute '%s' missing", state.symbols[name])
|
||||||
|
.atPos(pos)
|
||||||
|
.withSuggestions(suggestions)
|
||||||
|
.withFrame(env, *this)
|
||||||
|
.debugThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're here, then we successfully found the attribute.
|
||||||
|
// Set our currently operated-on attrset to this one, and keep going.
|
||||||
|
vCurrent = attrIt->value;
|
||||||
|
posCurrent = attrIt->pos;
|
||||||
|
if (state.countCalls) state.attrSelects[posCurrent]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
|
state.forceValue(*vCurrent, (posCurrent ? posCurrent : this->pos));
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (pos2) {
|
if (posCurrent) {
|
||||||
auto pos2r = state.positions[pos2];
|
auto pos2r = state.positions[posCurrent];
|
||||||
auto origin = std::get_if<SourcePath>(&pos2r.origin);
|
auto origin = std::get_if<SourcePath>(&pos2r.origin);
|
||||||
if (!(origin && *origin == state.derivationInternal))
|
if (!(origin && *origin == state.derivationInternal))
|
||||||
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
|
state.addErrorTrace(e, posCurrent, "while evaluating the attribute '%1%'",
|
||||||
showAttrPath(state, env, attrPath));
|
showAttrPath(state, env, attrPath));
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
v = *vAttrs;
|
v = *vCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -157,8 +157,18 @@ struct ExprInheritFrom : ExprVar
|
||||||
struct ExprSelect : Expr
|
struct ExprSelect : Expr
|
||||||
{
|
{
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
std::unique_ptr<Expr> e, def;
|
|
||||||
|
/** The expression attributes are being selected on. e.g. `foo` in `foo.bar.baz`. */
|
||||||
|
std::unique_ptr<Expr> e;
|
||||||
|
|
||||||
|
/** A default value specified with `or`, if the selected attr doesn't exist.
|
||||||
|
* e.g. `bix` in `foo.bar.baz or bix`
|
||||||
|
*/
|
||||||
|
std::unique_ptr<Expr> def;
|
||||||
|
|
||||||
|
/** The path of attributes being selected. e.g. `bar.baz` in `foo.bar.baz.` */
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
|
|
||||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath, std::unique_ptr<Expr> def) : pos(pos), e(std::move(e)), def(std::move(def)), attrPath(std::move(attrPath)) { };
|
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath, std::unique_ptr<Expr> def) : pos(pos), e(std::move(e)), def(std::move(def)), attrPath(std::move(attrPath)) { };
|
||||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, Symbol name) : pos(pos), e(std::move(e)) { attrPath.push_back(AttrName(name)); };
|
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, Symbol name) : pos(pos), e(std::move(e)) { attrPath.push_back(AttrName(name)); };
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
|
|
|
@ -378,7 +378,7 @@ RunPager::RunPager()
|
||||||
RunPager::~RunPager()
|
RunPager::~RunPager()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (pid != -1) {
|
if (pid) {
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
dup2(std_out, STDOUT_FILENO);
|
dup2(std_out, STDOUT_FILENO);
|
||||||
pid.wait();
|
pid.wait();
|
||||||
|
|
|
@ -79,7 +79,7 @@ HookInstance::~HookInstance()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
toHook.writeSide.reset();
|
toHook.writeSide.reset();
|
||||||
if (pid != -1) pid.kill();
|
if (pid) pid.kill();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,16 +44,12 @@
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#if HAVE_SECCOMP
|
#if HAVE_SECCOMP
|
||||||
#include "linux/fchmodat2-compat.hh"
|
|
||||||
#include <seccomp.h>
|
#include <seccomp.h>
|
||||||
#endif
|
#endif
|
||||||
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
|
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
#include <spawn.h>
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
|
|
||||||
/* This definition is undocumented but depended upon by all major browsers. */
|
/* This definition is undocumented but depended upon by all major browsers. */
|
||||||
extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
|
extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
|
||||||
#endif
|
#endif
|
||||||
|
@ -140,7 +136,7 @@ LocalStore & LocalDerivationGoal::getLocalStore()
|
||||||
|
|
||||||
void LocalDerivationGoal::killChild()
|
void LocalDerivationGoal::killChild()
|
||||||
{
|
{
|
||||||
if (pid != -1) {
|
if (pid) {
|
||||||
worker.childTerminated(this);
|
worker.childTerminated(this);
|
||||||
|
|
||||||
/* If we're using a build user, then there is a tricky race
|
/* If we're using a build user, then there is a tricky race
|
||||||
|
@ -148,7 +144,7 @@ void LocalDerivationGoal::killChild()
|
||||||
done its setuid() to the build user uid, then it won't be
|
done its setuid() to the build user uid, then it won't be
|
||||||
killed, and we'll potentially lock up in pid.wait(). So
|
killed, and we'll potentially lock up in pid.wait(). So
|
||||||
also send a conventional kill to the child. */
|
also send a conventional kill to the child. */
|
||||||
::kill(-pid, SIGKILL); /* ignore the result */
|
::kill(-pid.get(), SIGKILL); /* ignore the result */
|
||||||
|
|
||||||
killSandbox(true);
|
killSandbox(true);
|
||||||
|
|
||||||
|
@ -161,19 +157,7 @@ void LocalDerivationGoal::killChild()
|
||||||
|
|
||||||
void LocalDerivationGoal::killSandbox(bool getStats)
|
void LocalDerivationGoal::killSandbox(bool getStats)
|
||||||
{
|
{
|
||||||
if (cgroup) {
|
if (buildUser) {
|
||||||
#if __linux__
|
|
||||||
auto stats = destroyCgroup(*cgroup);
|
|
||||||
if (getStats) {
|
|
||||||
buildResult.cpuUser = stats.cpuUser;
|
|
||||||
buildResult.cpuSystem = stats.cpuSystem;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
abort();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (buildUser) {
|
|
||||||
auto uid = buildUser->getUID();
|
auto uid = buildUser->getUID();
|
||||||
assert(uid != 0);
|
assert(uid != 0);
|
||||||
killUser(uid);
|
killUser(uid);
|
||||||
|
@ -945,7 +929,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
if (usingUserNamespace)
|
if (usingUserNamespace)
|
||||||
options.cloneFlags |= CLONE_NEWUSER;
|
options.cloneFlags |= CLONE_NEWUSER;
|
||||||
|
|
||||||
pid_t child = startProcess([&]() { runChild(); }, options);
|
pid_t child = startProcess([&]() { runChild(); }, options).release();
|
||||||
|
|
||||||
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
|
@ -966,7 +950,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
||||||
assert(ss.size() == 1);
|
assert(ss.size() == 1);
|
||||||
pid = string2Int<pid_t>(ss[0]).value();
|
pid = Pid{string2Int<pid_t>(ss[0]).value()};
|
||||||
|
|
||||||
if (usingUserNamespace) {
|
if (usingUserNamespace) {
|
||||||
/* Set the UID/GID mapping of the builder's user namespace
|
/* Set the UID/GID mapping of the builder's user namespace
|
||||||
|
@ -976,13 +960,13 @@ void LocalDerivationGoal::startBuilder()
|
||||||
uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
|
uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
|
||||||
uid_t nrIds = buildUser ? buildUser->getUIDCount() : 1;
|
uid_t nrIds = buildUser ? buildUser->getUIDCount() : 1;
|
||||||
|
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/uid_map",
|
writeFile("/proc/" + std::to_string(pid.get()) + "/uid_map",
|
||||||
fmt("%d %d %d", sandboxUid(), hostUid, nrIds));
|
fmt("%d %d %d", sandboxUid(), hostUid, nrIds));
|
||||||
|
|
||||||
if (!buildUser || buildUser->getUIDCount() == 1)
|
if (!buildUser || buildUser->getUIDCount() == 1)
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny");
|
writeFile("/proc/" + std::to_string(pid.get()) + "/setgroups", "deny");
|
||||||
|
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/gid_map",
|
writeFile("/proc/" + std::to_string(pid.get()) + "/gid_map",
|
||||||
fmt("%d %d %d", sandboxGid(), hostGid, nrIds));
|
fmt("%d %d %d", sandboxGid(), hostGid, nrIds));
|
||||||
} else {
|
} else {
|
||||||
debug("note: not using a user namespace");
|
debug("note: not using a user namespace");
|
||||||
|
@ -1005,19 +989,19 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
/* Save the mount- and user namespace of the child. We have to do this
|
/* Save the mount- and user namespace of the child. We have to do this
|
||||||
*before* the child does a chroot. */
|
*before* the child does a chroot. */
|
||||||
sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY)};
|
sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", pid.get()).c_str(), O_RDONLY)};
|
||||||
if (sandboxMountNamespace.get() == -1)
|
if (sandboxMountNamespace.get() == -1)
|
||||||
throw SysError("getting sandbox mount namespace");
|
throw SysError("getting sandbox mount namespace");
|
||||||
|
|
||||||
if (usingUserNamespace) {
|
if (usingUserNamespace) {
|
||||||
sandboxUserNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY)};
|
sandboxUserNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/user", pid.get()).c_str(), O_RDONLY)};
|
||||||
if (sandboxUserNamespace.get() == -1)
|
if (sandboxUserNamespace.get() == -1)
|
||||||
throw SysError("getting sandbox user namespace");
|
throw SysError("getting sandbox user namespace");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move the child into its own cgroup. */
|
/* Move the child into its own cgroup. */
|
||||||
if (cgroup)
|
if (cgroup)
|
||||||
writeFile(*cgroup + "/cgroup.procs", fmt("%d", (pid_t) pid));
|
writeFile(*cgroup + "/cgroup.procs", fmt("%d", pid.get()));
|
||||||
|
|
||||||
/* Signal the builder that we've updated its user namespace. */
|
/* Signal the builder that we've updated its user namespace. */
|
||||||
writeFull(userNamespaceSync.writeSide.get(), "1");
|
writeFull(userNamespaceSync.writeSide.get(), "1");
|
||||||
|
@ -1585,7 +1569,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path)
|
||||||
entering its mount namespace, which is not possible
|
entering its mount namespace, which is not possible
|
||||||
in multithreaded programs. So we do this in a
|
in multithreaded programs. So we do this in a
|
||||||
child process.*/
|
child process.*/
|
||||||
Pid child(startProcess([&]() {
|
Pid child = startProcess([&]() {
|
||||||
|
|
||||||
if (usingUserNamespace && (setns(sandboxUserNamespace.get(), 0) == -1))
|
if (usingUserNamespace && (setns(sandboxUserNamespace.get(), 0) == -1))
|
||||||
throw SysError("entering sandbox user namespace");
|
throw SysError("entering sandbox user namespace");
|
||||||
|
@ -1596,7 +1580,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path)
|
||||||
doBind(source, target);
|
doBind(source, target);
|
||||||
|
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}));
|
});
|
||||||
|
|
||||||
int status = child.wait();
|
int status = child.wait();
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
|
@ -1617,6 +1601,12 @@ void LocalDerivationGoal::chownToBuilder(const Path & path)
|
||||||
throw SysError("cannot change ownership of '%1%'", path);
|
throw SysError("cannot change ownership of '%1%'", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_SECCOMP
|
||||||
|
void allowSyscall(scmp_filter_ctx ctx, int syscall) {
|
||||||
|
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 0) != 0)
|
||||||
|
throw SysError("unable to add seccomp rule");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void setupSeccomp()
|
void setupSeccomp()
|
||||||
{
|
{
|
||||||
|
@ -1624,7 +1614,9 @@ void setupSeccomp()
|
||||||
#if HAVE_SECCOMP
|
#if HAVE_SECCOMP
|
||||||
scmp_filter_ctx ctx;
|
scmp_filter_ctx ctx;
|
||||||
|
|
||||||
if (!(ctx = seccomp_init(SCMP_ACT_ALLOW)))
|
// Pretend that syscalls we don't yet know about don't exist.
|
||||||
|
// This is the best option for compatibility: after all, they did in fact not exist not too long ago.
|
||||||
|
if (!(ctx = seccomp_init(SCMP_ACT_ERRNO(ENOSYS))))
|
||||||
throw SysError("unable to initialize seccomp mode 2");
|
throw SysError("unable to initialize seccomp mode 2");
|
||||||
|
|
||||||
Finally cleanup([&]() {
|
Finally cleanup([&]() {
|
||||||
|
@ -1659,28 +1651,520 @@ void setupSeccomp()
|
||||||
seccomp_arch_add(ctx, SCMP_ARCH_MIPSEL64N32) != 0)
|
seccomp_arch_add(ctx, SCMP_ARCH_MIPSEL64N32) != 0)
|
||||||
printError("unable to add mips64el-*abin32 seccomp architecture");
|
printError("unable to add mips64el-*abin32 seccomp architecture");
|
||||||
|
|
||||||
/* Prevent builders from creating setuid/setgid binaries. */
|
// This list is intended for machine consumption.
|
||||||
for (int perm : { S_ISUID, S_ISGID }) {
|
// Please keep its format, order and BEGIN/END markers.
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1,
|
//
|
||||||
SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
// Currently, it is up to date with libseccomp 2.5.5 and glibc 2.38.
|
||||||
throw SysError("unable to add seccomp rule");
|
// Run check-syscalls to determine which new syscalls should be added.
|
||||||
|
// New syscalls must be audited and handled in a way that blocks the following dangerous operations:
|
||||||
|
// * Creation of non-empty setuid/setgid files
|
||||||
|
// * Creation of extended attributes (including ACLs)
|
||||||
|
//
|
||||||
|
// BEGIN extract-syscalls
|
||||||
|
allowSyscall(ctx, SCMP_SYS(accept));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(accept4));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(access));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(acct));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(add_key));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(adjtimex));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(afs_syscall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(alarm));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(arch_prctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(arm_fadvise64_64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(arm_sync_file_range));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(bdflush));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(bind));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(bpf));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(break));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(breakpoint));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(brk));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(cachectl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(cacheflush));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(cachestat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(capget));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(capset));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(chdir));
|
||||||
|
// skip chmod (dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(chown));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(chown32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(chroot));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_adjtime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_adjtime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_getres));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_getres_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_gettime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_gettime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_nanosleep));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_nanosleep_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_settime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clock_settime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clone));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(clone3));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(close));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(close_range));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(connect));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(copy_file_range));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(creat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(create_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(delete_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(dup));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(dup2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(dup3));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_create1));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_ctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_ctl_old));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_pwait));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_pwait2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_wait));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(epoll_wait_old));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(eventfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(eventfd2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(execve));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(execveat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(exit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(exit_group));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(faccessat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(faccessat2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fadvise64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fadvise64_64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fallocate));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fanotify_init));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fanotify_mark));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fchdir));
|
||||||
|
// skip fchmod (dangerous)
|
||||||
|
// skip fchmodat (dangerous)
|
||||||
|
// skip fchmodat2 (requires glibc 2.39, dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fchown));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fchown32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fchownat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fcntl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fcntl64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fdatasync));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fgetxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(finit_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(flistxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(flock));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fork));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fremovexattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fsconfig));
|
||||||
|
// skip fsetxattr (dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fsmount));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fsopen));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fspick));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstat64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstatat64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstatfs));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fstatfs64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(fsync));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ftime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ftruncate));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ftruncate64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(futex));
|
||||||
|
// skip futex_requeue (requires glibc 2.39)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(futex_time64));
|
||||||
|
// skip futex_wait (requires glibc 2.39)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(futex_waitv));
|
||||||
|
// skip futex_wake (requires glibc 2.39)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(futimesat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getcpu));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getcwd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getdents));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getdents64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getegid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getegid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(geteuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(geteuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getgroups));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getgroups32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getitimer));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_kernel_syms));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_mempolicy));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpeername));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpgrp));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getppid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getpriority));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getrandom));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getresgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getresgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getresuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getresuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getrlimit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_robust_list));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getrusage));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getsid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getsockname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getsockopt));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_thread_area));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(gettid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(gettimeofday));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(get_tls));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(getxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(gtty));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(idle));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(init_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(inotify_add_watch));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(inotify_init));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(inotify_init1));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(inotify_rm_watch));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_cancel));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ioctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_destroy));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_getevents));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ioperm));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_pgetevents));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_pgetevents_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(iopl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ioprio_get));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ioprio_set));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_setup));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_submit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_uring_enter));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_uring_register));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(io_uring_setup));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ipc));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(kcmp));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(kexec_file_load));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(kexec_load));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(keyctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(kill));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(landlock_add_rule));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(landlock_create_ruleset));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(landlock_restrict_self));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lchown));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lchown32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lgetxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(link));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(linkat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(listen));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(listxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(llistxattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(_llseek));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lock));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lookup_dcookie));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lremovexattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lseek));
|
||||||
|
// skip lsetxattr (dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(lstat64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(madvise));
|
||||||
|
// skip map_shadow_stack (requires glibc 2.39)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mbind));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(membarrier));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(memfd_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(memfd_secret));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(migrate_pages));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mincore));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mkdir));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mkdirat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mknod));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mknodat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mlock));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mlock2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mlockall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mmap));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mmap2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(modify_ldt));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mount));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mount_setattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(move_mount));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(move_pages));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mprotect));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mpx));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_getsetattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_notify));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_open));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_timedreceive));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_timedreceive_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_timedsend));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_timedsend_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mq_unlink));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(mremap));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msgctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msgget));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msgrcv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msgsnd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(msync));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(multiplexer));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(munlock));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(munlockall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(munmap));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(name_to_handle_at));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(nanosleep));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(newfstatat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(_newselect));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(nfsservctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(nice));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(oldfstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(oldlstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(oldolduname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(oldstat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(olduname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(open));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(openat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(openat2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(open_by_handle_at));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(open_tree));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pause));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pciconfig_iobase));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pciconfig_read));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pciconfig_write));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(perf_event_open));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(personality));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pidfd_getfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pidfd_open));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pidfd_send_signal));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pipe));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pipe2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pivot_root));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pkey_alloc));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pkey_free));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pkey_mprotect));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(poll));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ppoll));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ppoll_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(prctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pread64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(preadv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(preadv2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(prlimit64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(process_madvise));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(process_mrelease));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(process_vm_readv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(process_vm_writev));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(prof));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(profil));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pselect6));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pselect6_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ptrace));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(putpmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pwrite64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pwritev));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(pwritev2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(query_module));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(quotactl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(quotactl_fd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(read));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readahead));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readdir));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readlink));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readlinkat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(readv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(reboot));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recv));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recvfrom));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recvmmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recvmmsg_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(recvmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(remap_file_pages));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(removexattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rename));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(renameat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(renameat2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(request_key));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(restart_syscall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(riscv_flush_icache));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rmdir));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rseq));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rtas));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigaction));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigpending));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigprocmask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigqueueinfo));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigreturn));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigsuspend));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigtimedwait));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_sigtimedwait_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(rt_tgsigqueueinfo));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_guarded_storage));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_pci_mmio_read));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_pci_mmio_write));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_runtime_instr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(s390_sthyi));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_getaffinity));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_getattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_getparam));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_get_priority_max));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_get_priority_min));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_getscheduler));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_rr_get_interval));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_rr_get_interval_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_setaffinity));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_setattr));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_setparam));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_setscheduler));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sched_yield));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(seccomp));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(security));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(select));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semget));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semop));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semtimedop));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(semtimedop_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(send));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendfile));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendfile64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendmmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendmsg));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sendto));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setdomainname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setfsgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setfsgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setfsuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setfsuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setgroups));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setgroups32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sethostname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setitimer));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_mempolicy));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_mempolicy_home_node));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setns));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setpgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setpriority));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setregid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setregid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setresgid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setresgid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setresuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setresuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setreuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setreuid32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setrlimit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_robust_list));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setsid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setsockopt));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_thread_area));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_tid_address));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(settimeofday));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(set_tls));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setuid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(setuid32));
|
||||||
|
// skip setxattr (dangerous)
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sgetmask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shmat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shmctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shmdt));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shmget));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(shutdown));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigaction));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigaltstack));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(signal));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(signalfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(signalfd4));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigpending));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigprocmask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigreturn));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sigsuspend));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(socket));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(socketcall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(socketpair));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(splice));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(spu_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(spu_run));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ssetmask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(stat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(stat64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(statfs));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(statfs64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(statx));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(stime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(stty));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(subpage_prot));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(swapcontext));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(swapoff));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(swapon));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(switch_endian));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(symlink));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(symlinkat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sync));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sync_file_range));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sync_file_range2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(syncfs));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(syscall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(_sysctl));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sys_debug_setcontext));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sysfs));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sysinfo));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(syslog));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(sysmips));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(tee));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(tgkill));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(time));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_delete));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_create));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_gettime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_gettime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_settime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timerfd_settime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_getoverrun));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_gettime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_gettime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_settime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(timer_settime64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(times));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(tkill));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(truncate));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(truncate64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(tuxcall));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ugetrlimit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ulimit));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(umask));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(umount));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(umount2));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(uname));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(unlink));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(unlinkat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(unshare));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(uselib));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(userfaultfd));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(usr26));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(usr32));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(ustat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(utime));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(utimensat));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(utimensat_time64));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(utimes));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vfork));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vhangup));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vm86));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vm86old));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vmsplice));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(vserver));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(wait4));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(waitid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(waitpid));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(write));
|
||||||
|
allowSyscall(ctx, SCMP_SYS(writev));
|
||||||
|
// END extract-syscalls
|
||||||
|
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1,
|
// chmod family: prevent adding setuid/setgid bits to existing files.
|
||||||
SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
// The Nix store does not support setuid/setgid, and even their temporary creation can weaken the security of the sandbox.
|
||||||
throw SysError("unable to add seccomp rule");
|
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID | S_ISGID, 0)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID | S_ISGID, 0)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchmodat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISUID | S_ISGID, 0)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) != 0 ||
|
||||||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) != 0)
|
||||||
|
throw SysError("unable to add seccomp rule");
|
||||||
|
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1,
|
// setxattr family: prevent creation of extended attributes or ACLs.
|
||||||
SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
// Not all filesystems support them, and they're incompatible with the NAR format.
|
||||||
throw SysError("unable to add seccomp rule");
|
|
||||||
|
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), NIX_SYSCALL_FCHMODAT2, 1,
|
|
||||||
SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
|
||||||
throw SysError("unable to add seccomp rule");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prevent builders from creating EAs or ACLs. Not all filesystems
|
|
||||||
support these, and they're not allowed in the Nix store because
|
|
||||||
they're not representable in the NAR serialisation. */
|
|
||||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 ||
|
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 ||
|
||||||
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 ||
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 ||
|
||||||
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0)
|
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0)
|
||||||
|
@ -1714,11 +2198,7 @@ void LocalDerivationGoal::runChild()
|
||||||
|
|
||||||
commonChildInit();
|
commonChildInit();
|
||||||
|
|
||||||
try {
|
setupSeccomp();
|
||||||
setupSeccomp();
|
|
||||||
} catch (...) {
|
|
||||||
if (buildUser) throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setUser = true;
|
bool setUser = true;
|
||||||
|
|
||||||
|
@ -2177,31 +2657,8 @@ void LocalDerivationGoal::runChild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __APPLE__
|
execBuilder(drv->builder, args, envStrs);
|
||||||
posix_spawnattr_t attrp;
|
// execBuilder should not return
|
||||||
|
|
||||||
if (posix_spawnattr_init(&attrp))
|
|
||||||
throw SysError("failed to initialize builder");
|
|
||||||
|
|
||||||
if (posix_spawnattr_setflags(&attrp, POSIX_SPAWN_SETEXEC))
|
|
||||||
throw SysError("failed to initialize builder");
|
|
||||||
|
|
||||||
if (drv->platform == "aarch64-darwin") {
|
|
||||||
// Unset kern.curproc_arch_affinity so we can escape Rosetta
|
|
||||||
int affinity = 0;
|
|
||||||
sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, sizeof(affinity));
|
|
||||||
|
|
||||||
cpu_type_t cpu = CPU_TYPE_ARM64;
|
|
||||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
|
||||||
} else if (drv->platform == "x86_64-darwin") {
|
|
||||||
cpu_type_t cpu = CPU_TYPE_X86_64;
|
|
||||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
|
||||||
#else
|
|
||||||
execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
throw SysError("executing '%1%'", drv->builder);
|
throw SysError("executing '%1%'", drv->builder);
|
||||||
|
|
||||||
|
@ -2217,6 +2674,11 @@ void LocalDerivationGoal::runChild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocalDerivationGoal::execBuilder(std::string builder, Strings args, Strings envStrs)
|
||||||
|
{
|
||||||
|
execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
{
|
{
|
||||||
|
|
|
@ -178,7 +178,28 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
|
|
||||||
friend struct RestrictedStore;
|
friend struct RestrictedStore;
|
||||||
|
|
||||||
using DerivationGoal::DerivationGoal;
|
/**
|
||||||
|
* Create a LocalDerivationGoal without an on-disk .drv file,
|
||||||
|
* possibly a platform-specific subclass
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<LocalDerivationGoal> makeLocalDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
Worker & worker,
|
||||||
|
BuildMode buildMode
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a LocalDerivationGoal for an on-disk .drv file,
|
||||||
|
* possibly a platform-specific subclass
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<LocalDerivationGoal> makeLocalDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const BasicDerivation & drv,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
Worker & worker,
|
||||||
|
BuildMode buildMode
|
||||||
|
);
|
||||||
|
|
||||||
virtual ~LocalDerivationGoal() noexcept(false) override;
|
virtual ~LocalDerivationGoal() noexcept(false) override;
|
||||||
|
|
||||||
|
@ -282,7 +303,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
* Kill any processes running under the build user UID or in the
|
* Kill any processes running under the build user UID or in the
|
||||||
* cgroup of the build.
|
* cgroup of the build.
|
||||||
*/
|
*/
|
||||||
void killSandbox(bool getStats);
|
virtual void killSandbox(bool getStats);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create alternative path calculated from but distinct from the
|
* Create alternative path calculated from but distinct from the
|
||||||
|
@ -299,6 +320,16 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
* rewrites caught everything
|
* rewrites caught everything
|
||||||
*/
|
*/
|
||||||
StorePath makeFallbackPath(OutputNameView outputName);
|
StorePath makeFallbackPath(OutputNameView outputName);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using DerivationGoal::DerivationGoal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the builder, replacing the current process.
|
||||||
|
* Generally this means an `execve` call.
|
||||||
|
*/
|
||||||
|
virtual void execBuilder(std::string builder, Strings args, Strings envStrs);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,6 +217,7 @@ void PathSubstitutionGoal::tryToRun()
|
||||||
promise = std::promise<void>();
|
promise = std::promise<void>();
|
||||||
|
|
||||||
thr = std::thread([this]() {
|
thr = std::thread([this]() {
|
||||||
|
auto & fetchPath = subPath ? *subPath : storePath;
|
||||||
try {
|
try {
|
||||||
ReceiveInterrupts receiveInterrupts;
|
ReceiveInterrupts receiveInterrupts;
|
||||||
|
|
||||||
|
@ -226,10 +227,17 @@ void PathSubstitutionGoal::tryToRun()
|
||||||
Activity act(*logger, actSubstitute, Logger::Fields{worker.store.printStorePath(storePath), sub->getUri()});
|
Activity act(*logger, actSubstitute, Logger::Fields{worker.store.printStorePath(storePath), sub->getUri()});
|
||||||
PushActivity pact(act.id);
|
PushActivity pact(act.id);
|
||||||
|
|
||||||
copyStorePath(*sub, worker.store,
|
copyStorePath(
|
||||||
subPath ? *subPath : storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs);
|
*sub, worker.store, fetchPath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs
|
||||||
|
);
|
||||||
|
|
||||||
promise.set_value();
|
promise.set_value();
|
||||||
|
} catch (const EndOfFile &) {
|
||||||
|
promise.set_exception(std::make_exception_ptr(EndOfFile(
|
||||||
|
"NAR for '%s' fetched from '%s' is incomplete",
|
||||||
|
sub->printStorePath(fetchPath),
|
||||||
|
sub->getUri()
|
||||||
|
)));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
promise.set_exception(std::current_exception());
|
promise.set_exception(std::current_exception());
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,8 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drv
|
||||||
{
|
{
|
||||||
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
||||||
return !dynamic_cast<LocalStore *>(&store)
|
return !dynamic_cast<LocalStore *>(&store)
|
||||||
? std::make_shared</* */DerivationGoal>(drvPath, wantedOutputs, *this, buildMode)
|
? std::make_shared<DerivationGoal>(drvPath, wantedOutputs, *this, buildMode)
|
||||||
: std::make_shared<LocalDerivationGoal>(drvPath, wantedOutputs, *this, buildMode);
|
: LocalDerivationGoal::makeLocalDerivationGoal(drvPath, wantedOutputs, *this, buildMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,8 +76,8 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath
|
||||||
{
|
{
|
||||||
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
||||||
return !dynamic_cast<LocalStore *>(&store)
|
return !dynamic_cast<LocalStore *>(&store)
|
||||||
? std::make_shared</* */DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode)
|
? std::make_shared<DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode)
|
||||||
: std::make_shared<LocalDerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
|
: LocalDerivationGoal::makeLocalDerivationGoal(drvPath, drv, wantedOutputs, *this, buildMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Determine the syscall number for `fchmodat2`.
|
|
||||||
*
|
|
||||||
* On most platforms this is 452. Exceptions can be found on
|
|
||||||
* a glibc git checkout via `rg --pcre2 'define __NR_fchmodat2 (?!452)'`.
|
|
||||||
*
|
|
||||||
* The problem is that glibc 2.39 and libseccomp 2.5.5 are needed to
|
|
||||||
* get the syscall number. However, a Lix built against nixpkgs 23.11
|
|
||||||
* (glibc 2.38) should still have the issue fixed without depending
|
|
||||||
* on the build environment.
|
|
||||||
*
|
|
||||||
* To achieve that, the macros below try to determine the platform and
|
|
||||||
* set the syscall number which is platform-specific, but
|
|
||||||
* in most cases 452.
|
|
||||||
*
|
|
||||||
* TODO: remove this when 23.11 is EOL and the entire (supported) ecosystem
|
|
||||||
* is on glibc 2.39.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
///@file
|
|
||||||
|
|
||||||
#if defined(__alpha__)
|
|
||||||
# define NIX_SYSCALL_FCHMODAT2 562
|
|
||||||
#elif defined(__x86_64__) && SIZE_MAX == 0xFFFFFFFF // x32
|
|
||||||
# define NIX_SYSCALL_FCHMODAT2 1073742276
|
|
||||||
#elif defined(__mips__) && defined(__mips64) && defined(_ABIN64) // mips64/n64
|
|
||||||
# define NIX_SYSCALL_FCHMODAT2 5452
|
|
||||||
#elif defined(__mips__) && defined(__mips64) && defined(_ABIN32) // mips64/n32
|
|
||||||
# define NIX_SYSCALL_FCHMODAT2 6452
|
|
||||||
#elif defined(__mips__) && defined(_ABIO32) // mips32
|
|
||||||
# define NIX_SYSCALL_FCHMODAT2 4452
|
|
||||||
#else
|
|
||||||
# define NIX_SYSCALL_FCHMODAT2 452
|
|
||||||
#endif
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
#include "build/local-derivation-goal.hh"
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
#include "platform/linux.hh"
|
#include "platform/linux.hh"
|
||||||
|
@ -19,4 +20,43 @@ std::shared_ptr<LocalStore> LocalStore::makeLocalStore(const Params & params)
|
||||||
return std::shared_ptr<LocalStore>(new FallbackLocalStore(params));
|
return std::shared_ptr<LocalStore>(new FallbackLocalStore(params));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<LocalDerivationGoal> LocalDerivationGoal::makeLocalDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
Worker & worker,
|
||||||
|
BuildMode buildMode
|
||||||
|
)
|
||||||
|
{
|
||||||
|
#if __linux__
|
||||||
|
return std::make_shared<LinuxLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||||
|
#elif __APPLE__
|
||||||
|
return std::make_shared<DarwinLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||||
|
#else
|
||||||
|
return std::make_shared<FallbackLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<LocalDerivationGoal> LocalDerivationGoal::makeLocalDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const BasicDerivation & drv,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
Worker & worker,
|
||||||
|
BuildMode buildMode
|
||||||
|
)
|
||||||
|
{
|
||||||
|
#if __linux__
|
||||||
|
return std::make_shared<LinuxLocalDerivationGoal>(
|
||||||
|
drvPath, drv, wantedOutputs, worker, buildMode
|
||||||
|
);
|
||||||
|
#elif __APPLE__
|
||||||
|
return std::make_shared<DarwinLocalDerivationGoal>(
|
||||||
|
drvPath, drv, wantedOutputs, worker, buildMode
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
return std::make_shared<FallbackLocalDerivationGoal>(
|
||||||
|
drvPath, drv, wantedOutputs, worker, buildMode
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <sys/proc_info.h>
|
#include <sys/proc_info.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <libproc.h>
|
#include <libproc.h>
|
||||||
|
#include <spawn.h>
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
@ -220,4 +221,29 @@ void DarwinLocalStore::findPlatformRoots(UncheckedRoots & unchecked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DarwinLocalDerivationGoal::execBuilder(std::string builder, Strings args, Strings envStrs)
|
||||||
|
{
|
||||||
|
posix_spawnattr_t attrp;
|
||||||
|
|
||||||
|
if (posix_spawnattr_init(&attrp))
|
||||||
|
throw SysError("failed to initialize builder");
|
||||||
|
|
||||||
|
if (posix_spawnattr_setflags(&attrp, POSIX_SPAWN_SETEXEC))
|
||||||
|
throw SysError("failed to initialize builder");
|
||||||
|
|
||||||
|
if (drv->platform == "aarch64-darwin") {
|
||||||
|
// Unset kern.curproc_arch_affinity so we can escape Rosetta
|
||||||
|
int affinity = 0;
|
||||||
|
sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, sizeof(affinity));
|
||||||
|
|
||||||
|
cpu_type_t cpu = CPU_TYPE_ARM64;
|
||||||
|
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
||||||
|
} else if (drv->platform == "x86_64-darwin") {
|
||||||
|
cpu_type_t cpu = CPU_TYPE_X86_64;
|
||||||
|
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include "build/local-derivation-goal.hh"
|
||||||
#include "gc-store.hh"
|
#include "gc-store.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
|
@ -32,4 +33,19 @@ private:
|
||||||
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Darwin-specific implementation of LocalDerivationGoal
|
||||||
|
*/
|
||||||
|
class DarwinLocalDerivationGoal : public LocalDerivationGoal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using LocalDerivationGoal::LocalDerivationGoal;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Set process flags to enter or leave rosetta, then execute the builder
|
||||||
|
*/
|
||||||
|
void execBuilder(std::string builder, Strings args, Strings envStrs) override;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include "build/local-derivation-goal.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -28,4 +29,14 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback platform implementation of LocalDerivationGoal
|
||||||
|
* Exists so we can make LocalDerivationGoal constructor protected
|
||||||
|
*/
|
||||||
|
class FallbackLocalDerivationGoal : public LocalDerivationGoal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using LocalDerivationGoal::LocalDerivationGoal;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "cgroup.hh"
|
||||||
#include "gc-store.hh"
|
#include "gc-store.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "platform/linux.hh"
|
#include "platform/linux.hh"
|
||||||
|
@ -114,4 +115,17 @@ void LinuxLocalStore::findPlatformRoots(UncheckedRoots & unchecked)
|
||||||
readFileRoots("/proc/sys/kernel/fbsplash", unchecked);
|
readFileRoots("/proc/sys/kernel/fbsplash", unchecked);
|
||||||
readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked);
|
readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LinuxLocalDerivationGoal::killSandbox(bool getStats)
|
||||||
|
{
|
||||||
|
if (cgroup) {
|
||||||
|
auto stats = destroyCgroup(*cgroup);
|
||||||
|
if (getStats) {
|
||||||
|
buildResult.cpuUser = stats.cpuUser;
|
||||||
|
buildResult.cpuSystem = stats.cpuSystem;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LocalDerivationGoal::killSandbox(getStats);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include "build/local-derivation-goal.hh"
|
||||||
#include "gc-store.hh"
|
#include "gc-store.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
|
@ -32,4 +33,16 @@ private:
|
||||||
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linux-specific implementation of LocalDerivationGoal
|
||||||
|
*/
|
||||||
|
class LinuxLocalDerivationGoal : public LocalDerivationGoal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using LocalDerivationGoal::LocalDerivationGoal;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void killSandbox(bool getStatus) override;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ Path SSHMaster::startMaster()
|
||||||
|
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
|
|
||||||
if (state->sshMaster != -1) return state->socketPath;
|
if (state->sshMaster) return state->socketPath;
|
||||||
|
|
||||||
state->socketPath = (Path) *state->tmpDir + "/ssh.sock";
|
state->socketPath = (Path) *state->tmpDir + "/ssh.sock";
|
||||||
|
|
||||||
|
|
|
@ -1072,8 +1072,6 @@ void copyStorePath(
|
||||||
});
|
});
|
||||||
TeeSink tee { sink, progressSink };
|
TeeSink tee { sink, progressSink };
|
||||||
srcStore.narFromPath(storePath, tee);
|
srcStore.narFromPath(storePath, tee);
|
||||||
}, [&]() {
|
|
||||||
throw EndOfFile("NAR for '%s' fetched from '%s' is incomplete", srcStore.printStorePath(storePath), srcStore.getUri());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dstStore.addToStore(*info, *source, repair, checkSigs);
|
dstStore.addToStore(*info, *source, repair, checkSigs);
|
||||||
|
|
|
@ -94,12 +94,7 @@ bool userNamespacesSupported()
|
||||||
static auto res = [&]() -> bool
|
static auto res = [&]() -> bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Pid pid = startProcess([&]()
|
Pid pid = startProcess([&]() { _exit(0); }, {.cloneFlags = CLONE_NEWUSER});
|
||||||
{
|
|
||||||
_exit(0);
|
|
||||||
}, {
|
|
||||||
.cloneFlags = CLONE_NEWUSER
|
|
||||||
});
|
|
||||||
|
|
||||||
auto r = pid.wait();
|
auto r = pid.wait();
|
||||||
assert(!r);
|
assert(!r);
|
||||||
|
@ -120,8 +115,7 @@ bool mountAndPidNamespacesSupported()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Pid pid = startProcess([&]()
|
Pid pid = startProcess([&]() {
|
||||||
{
|
|
||||||
/* Make sure we don't remount the parent's /proc. */
|
/* Make sure we don't remount the parent's /proc. */
|
||||||
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
|
|
|
@ -35,9 +35,19 @@ Pid::Pid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pid::Pid(pid_t pid)
|
Pid::Pid(Pid && other) : pid(other.pid), separatePG(other.separatePG), killSignal(other.killSignal)
|
||||||
: pid(pid)
|
|
||||||
{
|
{
|
||||||
|
other.pid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pid & Pid::operator=(Pid && other)
|
||||||
|
{
|
||||||
|
Pid tmp(std::move(other));
|
||||||
|
std::swap(pid, tmp.pid);
|
||||||
|
std::swap(separatePG, tmp.separatePG);
|
||||||
|
std::swap(killSignal, tmp.killSignal);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,20 +57,6 @@ Pid::~Pid() noexcept(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Pid::operator =(pid_t pid)
|
|
||||||
{
|
|
||||||
if (this->pid != -1 && this->pid != pid) kill();
|
|
||||||
this->pid = pid;
|
|
||||||
killSignal = SIGKILL; // reset signal to default
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Pid::operator pid_t()
|
|
||||||
{
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Pid::kill()
|
int Pid::kill()
|
||||||
{
|
{
|
||||||
assert(pid != -1);
|
assert(pid != -1);
|
||||||
|
@ -131,7 +127,7 @@ void killUser(uid_t uid)
|
||||||
users to which the current process can send signals. So we
|
users to which the current process can send signals. So we
|
||||||
fork a process, switch to uid, and send a mass kill. */
|
fork a process, switch to uid, and send a mass kill. */
|
||||||
|
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid{startProcess([&]() {
|
||||||
|
|
||||||
if (setuid(uid) == -1)
|
if (setuid(uid) == -1)
|
||||||
throw SysError("setting uid");
|
throw SysError("setting uid");
|
||||||
|
@ -153,7 +149,7 @@ void killUser(uid_t uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit(0);
|
_exit(0);
|
||||||
});
|
})};
|
||||||
|
|
||||||
int status = pid.wait();
|
int status = pid.wait();
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
|
@ -187,7 +183,7 @@ static int childEntry(void * arg)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
Pid startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||||
{
|
{
|
||||||
std::function<void()> wrapper = [&]() {
|
std::function<void()> wrapper = [&]() {
|
||||||
logger = makeSimpleLogger();
|
logger = makeSimpleLogger();
|
||||||
|
@ -231,7 +227,7 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||||
|
|
||||||
if (pid == -1) throw SysError("unable to fork");
|
if (pid == -1) throw SysError("unable to fork");
|
||||||
|
|
||||||
return pid;
|
return Pid{pid};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string runProgram(Path program, bool searchPath, const Strings & args,
|
std::string runProgram(Path program, bool searchPath, const Strings & args,
|
||||||
|
@ -294,7 +290,7 @@ void runProgram2(const RunOptions & options)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fork. */
|
/* Fork. */
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid{startProcess([&]() {
|
||||||
if (options.environment)
|
if (options.environment)
|
||||||
replaceEnv(*options.environment);
|
replaceEnv(*options.environment);
|
||||||
if (options.standardOut && dup2(out.writeSide.get(), STDOUT_FILENO) == -1)
|
if (options.standardOut && dup2(out.writeSide.get(), STDOUT_FILENO) == -1)
|
||||||
|
@ -328,7 +324,7 @@ void runProgram2(const RunOptions & options)
|
||||||
execv(options.program.c_str(), stringsToCharPtrs(args_).data());
|
execv(options.program.c_str(), stringsToCharPtrs(args_).data());
|
||||||
|
|
||||||
throw SysError("executing '%1%'", options.program);
|
throw SysError("executing '%1%'", options.program);
|
||||||
}, processOptions);
|
}, processOptions)};
|
||||||
|
|
||||||
out.writeSide.close();
|
out.writeSide.close();
|
||||||
|
|
||||||
|
|
|
@ -26,16 +26,18 @@ class Pid
|
||||||
int killSignal = SIGKILL;
|
int killSignal = SIGKILL;
|
||||||
public:
|
public:
|
||||||
Pid();
|
Pid();
|
||||||
Pid(pid_t pid);
|
explicit Pid(pid_t pid): pid(pid) {}
|
||||||
|
Pid(Pid && other);
|
||||||
|
Pid & operator=(Pid && other);
|
||||||
~Pid() noexcept(false);
|
~Pid() noexcept(false);
|
||||||
void operator =(pid_t pid);
|
explicit operator bool() const { return pid != -1; }
|
||||||
operator pid_t();
|
|
||||||
int kill();
|
int kill();
|
||||||
int wait();
|
int wait();
|
||||||
|
|
||||||
void setSeparatePG(bool separatePG);
|
void setSeparatePG(bool separatePG);
|
||||||
void setKillSignal(int signal);
|
void setKillSignal(int signal);
|
||||||
pid_t release();
|
pid_t release();
|
||||||
|
pid_t get() const { return pid; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,7 +62,8 @@ struct ProcessOptions
|
||||||
int cloneFlags = 0;
|
int cloneFlags = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
[[nodiscard]]
|
||||||
|
Pid startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -266,20 +266,17 @@ std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Source> sinkToSource(
|
std::unique_ptr<Source> sinkToSource(std::function<void(Sink &)> fun)
|
||||||
std::function<void(Sink &)> fun,
|
|
||||||
std::function<void()> eof)
|
|
||||||
{
|
{
|
||||||
struct SinkToSource : Source
|
struct SinkToSource : Source
|
||||||
{
|
{
|
||||||
typedef boost::coroutines2::coroutine<std::string> coro_t;
|
typedef boost::coroutines2::coroutine<std::string> coro_t;
|
||||||
|
|
||||||
std::function<void(Sink &)> fun;
|
std::function<void(Sink &)> fun;
|
||||||
std::function<void()> eof;
|
|
||||||
std::optional<coro_t::pull_type> coro;
|
std::optional<coro_t::pull_type> coro;
|
||||||
|
|
||||||
SinkToSource(std::function<void(Sink &)> fun, std::function<void()> eof)
|
SinkToSource(std::function<void(Sink &)> fun)
|
||||||
: fun(fun), eof(eof)
|
: fun(fun)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +295,9 @@ std::unique_ptr<Source> sinkToSource(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*coro) { eof(); abort(); }
|
if (!*coro) {
|
||||||
|
throw EndOfFile("coroutine has finished");
|
||||||
|
}
|
||||||
|
|
||||||
if (pos == cur.size()) {
|
if (pos == cur.size()) {
|
||||||
if (!cur.empty()) {
|
if (!cur.empty()) {
|
||||||
|
@ -317,7 +316,7 @@ std::unique_ptr<Source> sinkToSource(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return std::make_unique<SinkToSource>(fun, eof);
|
return std::make_unique<SinkToSource>(fun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -338,11 +338,7 @@ std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun);
|
||||||
* Convert a function that feeds data into a Sink into a Source. The
|
* Convert a function that feeds data into a Sink into a Source. The
|
||||||
* Source executes the function as a coroutine.
|
* Source executes the function as a coroutine.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<Source> sinkToSource(
|
std::unique_ptr<Source> sinkToSource(std::function<void(Sink &)> fun);
|
||||||
std::function<void(Sink &)> fun,
|
|
||||||
std::function<void()> eof = []() {
|
|
||||||
throw EndOfFile("coroutine has finished");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
void writePadding(size_t len, Sink & sink);
|
void writePadding(size_t len, Sink & sink);
|
||||||
|
|
|
@ -65,7 +65,7 @@ static void bindConnectProcHelper(
|
||||||
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||||
Pipe pipe;
|
Pipe pipe;
|
||||||
pipe.create();
|
pipe.create();
|
||||||
Pid pid = startProcess([&] {
|
Pid pid{startProcess([&] {
|
||||||
try {
|
try {
|
||||||
pipe.readSide.close();
|
pipe.readSide.close();
|
||||||
Path dir = dirOf(path);
|
Path dir = dirOf(path);
|
||||||
|
@ -83,7 +83,7 @@ static void bindConnectProcHelper(
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
writeFull(pipe.writeSide.get(), "-1\n");
|
writeFull(pipe.writeSide.get(), "-1\n");
|
||||||
}
|
}
|
||||||
});
|
})};
|
||||||
pipe.writeSide.close();
|
pipe.writeSide.close();
|
||||||
auto errNo = string2Int<int>(chomp(drainFD(pipe.readSide.get())));
|
auto errNo = string2Int<int>(chomp(drainFD(pipe.readSide.get())));
|
||||||
if (!errNo || *errNo == -1)
|
if (!errNo || *errNo == -1)
|
||||||
|
|
|
@ -369,7 +369,7 @@ static void daemonLoop(std::optional<TrustedFlag> forceTrustClientOpt)
|
||||||
processConnection(openUncachedStore(), from, to, trusted, NotRecursive);
|
processConnection(openUncachedStore(), from, to, trusted, NotRecursive);
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}, options);
|
}, options).release();
|
||||||
|
|
||||||
} catch (Interrupted & e) {
|
} catch (Interrupted & e) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -182,6 +182,7 @@ functional_tests_scripts = [
|
||||||
'debugger.sh',
|
'debugger.sh',
|
||||||
'test-libstoreconsumer.sh',
|
'test-libstoreconsumer.sh',
|
||||||
'extra-sandbox-profile.sh',
|
'extra-sandbox-profile.sh',
|
||||||
|
'substitute-truncated-nar.sh',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Plugin tests require shared libraries support.
|
# Plugin tests require shared libraries support.
|
||||||
|
|
|
@ -22,7 +22,7 @@ RunningProcess RunningProcess::start(std::string executable, Strings args)
|
||||||
procStdout.create();
|
procStdout.create();
|
||||||
|
|
||||||
// This is separate from runProgram2 because we have different IO requirements
|
// This is separate from runProgram2 because we have different IO requirements
|
||||||
pid_t pid = startProcess([&]() {
|
auto pid = startProcess([&]() {
|
||||||
if (dup2(procStdout.writeSide.get(), STDOUT_FILENO) == -1) {
|
if (dup2(procStdout.writeSide.get(), STDOUT_FILENO) == -1) {
|
||||||
throw SysError("dupping stdout");
|
throw SysError("dupping stdout");
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ RunningProcess RunningProcess::start(std::string executable, Strings args)
|
||||||
procStdin.readSide.close();
|
procStdin.readSide.close();
|
||||||
|
|
||||||
return RunningProcess{
|
return RunningProcess{
|
||||||
.pid = pid,
|
.pid = std::move(pid),
|
||||||
.procStdin = std::move(procStdin),
|
.procStdin = std::move(procStdin),
|
||||||
.procStdout = std::move(procStdout),
|
.procStdout = std::move(procStdout),
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,13 +7,14 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "file-descriptor.hh"
|
#include "file-descriptor.hh"
|
||||||
|
#include "processes.hh"
|
||||||
#include "tests/terminal-code-eater.hh"
|
#include "tests/terminal-code-eater.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct RunningProcess
|
struct RunningProcess
|
||||||
{
|
{
|
||||||
pid_t pid;
|
Pid pid;
|
||||||
Pipe procStdin;
|
Pipe procStdin;
|
||||||
Pipe procStdout;
|
Pipe procStdout;
|
||||||
|
|
||||||
|
|
29
tests/functional/substitute-truncated-nar.sh
Normal file
29
tests/functional/substitute-truncated-nar.sh
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
BINARY_CACHE=file://$cacheDir
|
||||||
|
|
||||||
|
build() {
|
||||||
|
nix-build --no-out-link "$@" --expr 'derivation {
|
||||||
|
name = "text";
|
||||||
|
system = builtins.currentSystem;
|
||||||
|
builder = "/bin/sh";
|
||||||
|
args = [ "-c" "echo some text to make the nar less empty > $out" ];
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
path=$(build)
|
||||||
|
nix copy --to "$BINARY_CACHE" "$path"
|
||||||
|
nix-collect-garbage >/dev/null 2>&1
|
||||||
|
|
||||||
|
nar=0bylmx35yjy2b1b4k7gjsl7i4vc03cpmryb41grfb1mp40n3hifl.nar.xz
|
||||||
|
|
||||||
|
[ -e $cacheDir/nar/$nar ] || fail "long nar missing?"
|
||||||
|
|
||||||
|
xzcat $cacheDir/nar/$nar > $TEST_HOME/tmp
|
||||||
|
truncate -s $(( $(stat -c %s $TEST_HOME/tmp) - 10 )) $TEST_HOME/tmp
|
||||||
|
xz < $TEST_HOME/tmp > $cacheDir/nar/$nar
|
||||||
|
|
||||||
|
# Copying back '$path' from the binary cache. This should fail as it is truncated
|
||||||
|
if build --option substituters "$BINARY_CACHE" --option require-sigs false -j0; then
|
||||||
|
fail "Importing a truncated nar should fail"
|
||||||
|
fi
|
|
@ -1,21 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
char *name = getenv("out");
|
|
||||||
FILE *fd = fopen(name, "w");
|
|
||||||
fprintf(fd, "henlo :3");
|
|
||||||
fclose(fd);
|
|
||||||
|
|
||||||
// FIXME use something nicer here that's less
|
|
||||||
// platform-dependent as soon as we go to 24.05
|
|
||||||
// and the glibc is new enough to support fchmodat2
|
|
||||||
long rs = syscall(452, NULL, name, S_ISUID, 0);
|
|
||||||
assert(rs == -1);
|
|
||||||
assert(errno == EPERM);
|
|
||||||
}
|
|
|
@ -4,17 +4,6 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
||||||
|
|
||||||
fchmodat2-builder = pkgs.runCommandCC "fchmodat2-suid" {
|
|
||||||
passAsFile = [ "code" ];
|
|
||||||
code = builtins.readFile ./fchmodat2-suid.c;
|
|
||||||
# Doesn't work with -O0, shuts up the warning about that.
|
|
||||||
hardeningDisable = [ "fortify" ];
|
|
||||||
} ''
|
|
||||||
mkdir -p $out/bin/
|
|
||||||
$CC -x c "$codePath" -O0 -g -o $out/bin/fchmodat2-suid
|
|
||||||
'';
|
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
name = "setuid";
|
name = "setuid";
|
||||||
|
@ -27,26 +16,13 @@ in
|
||||||
virtualisation.additionalPaths = [
|
virtualisation.additionalPaths = [
|
||||||
pkgs.stdenvNoCC
|
pkgs.stdenvNoCC
|
||||||
pkgs.pkgsi686Linux.stdenvNoCC
|
pkgs.pkgsi686Linux.stdenvNoCC
|
||||||
fchmodat2-builder
|
|
||||||
];
|
];
|
||||||
# need at least 6.6 to test for fchmodat2
|
|
||||||
boot.kernelPackages = pkgs.linuxKernel.packages.linux_6_6;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript = { nodes }: ''
|
testScript = { nodes }: ''
|
||||||
# fmt: off
|
# fmt: off
|
||||||
start_all()
|
start_all()
|
||||||
|
|
||||||
with subtest("fchmodat2 suid regression test"):
|
|
||||||
machine.succeed("""
|
|
||||||
nix-build -E '(with import <nixpkgs> {}; runCommand "fchmodat2-suid" {
|
|
||||||
BUILDER = builtins.storePath ${fchmodat2-builder};
|
|
||||||
} "
|
|
||||||
exec \\"$BUILDER\\"/bin/fchmodat2-suid
|
|
||||||
")'
|
|
||||||
""")
|
|
||||||
|
|
||||||
# Copying to /tmp should succeed.
|
# Copying to /tmp should succeed.
|
||||||
machine.succeed(r"""
|
machine.succeed(r"""
|
||||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
|
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
|
||||||
|
|
166
tests/unit/libutil/serialise.cc
Normal file
166
tests/unit/libutil/serialise.cc
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
#include "serialise.hh"
|
||||||
|
#include "error.hh"
|
||||||
|
#include "fmt.hh"
|
||||||
|
#include "pos-table.hh"
|
||||||
|
#include "ref.hh"
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
TEST(Sink, uint64_t)
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
s << 42;
|
||||||
|
ASSERT_EQ(s.s, std::string({42, 0, 0, 0, 0, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Sink, string_view)
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
s << "";
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data (omitted)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
s = {};
|
||||||
|
s << "test";
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
4, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data
|
||||||
|
't', 'e', 's', 't',
|
||||||
|
// padding
|
||||||
|
0, 0, 0, 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
s = {};
|
||||||
|
s << "longer string";
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
13, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data
|
||||||
|
'l', 'o', 'n', 'g', 'e', 'r', ' ', 's', 't', 'r', 'i', 'n', 'g',
|
||||||
|
// padding
|
||||||
|
0, 0, 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Sink, StringSet)
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
s << StringSet{};
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data (omitted)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
s = {};
|
||||||
|
s << StringSet{"a", ""};
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
2, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data ""
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data "a"
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0, 'a', 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Sink, Strings)
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
s << Strings{};
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data (omitted)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
s = {};
|
||||||
|
s << Strings{"a", ""};
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
2, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data "a"
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0, 'a', 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data ""
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Sink, Error)
|
||||||
|
{
|
||||||
|
PosTable pt;
|
||||||
|
auto o = pt.addOrigin(Pos::String{make_ref<std::string>("test")}, 4);
|
||||||
|
|
||||||
|
StringSink s;
|
||||||
|
s << Error{ErrorInfo{
|
||||||
|
.level = lvlInfo,
|
||||||
|
.msg = HintFmt("foo"),
|
||||||
|
.pos = pt[pt.add(o, 1)],
|
||||||
|
.traces = {{.pos = pt[pt.add(o, 2)], .hint = HintFmt("b %1%", "foo")}},
|
||||||
|
}};
|
||||||
|
// NOTE position of the error and all traces are ignored
|
||||||
|
// by the wire format
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
5, 0, 0, 0, 0, 0, 0, 0, 'E', 'r', 'r', 'o', 'r', 0, 0, 0,
|
||||||
|
3, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
5, 0, 0, 0, 0, 0, 0, 0, 'E', 'r', 'r', 'o', 'r', 0, 0, 0,
|
||||||
|
3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o', 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
16, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
'b', ' ', '\x1b', '[', '3', '5', ';', '1', 'm', 'f', 'o', 'o', '\x1b', '[', '0', 'm',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ libutil_tests_sources = files(
|
||||||
'libutil/paths-setting.cc',
|
'libutil/paths-setting.cc',
|
||||||
'libutil/pool.cc',
|
'libutil/pool.cc',
|
||||||
'libutil/references.cc',
|
'libutil/references.cc',
|
||||||
|
'libutil/serialise.cc',
|
||||||
'libutil/suggestions.cc',
|
'libutil/suggestions.cc',
|
||||||
'libutil/tests.cc',
|
'libutil/tests.cc',
|
||||||
'libutil/url.cc',
|
'libutil/url.cc',
|
||||||
|
|
Loading…
Reference in a new issue