From a0ef21262f4d5652bfb65cfacaec01d89c475a93 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 13 Nov 2018 16:15:30 +0100 Subject: [PATCH] Restore parent mount namespace before executing a child process This ensures that they can't write to /nix/store. Fixes #2535. --- src/libstore/local-store.cc | 2 ++ src/libstore/ssh.cc | 3 +++ src/libutil/util.cc | 23 +++++++++++++++++++++++ src/libutil/util.hh | 9 +++++++++ src/nix-build/nix-build.cc | 4 ++-- src/nix/edit.cc | 5 +++++ src/nix/repl.cc | 2 ++ src/nix/run.cc | 4 ++-- 8 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 216f3417c..e1cb423d1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -366,6 +366,8 @@ void LocalStore::makeStoreWritable() throw SysError("getting info about the Nix store mount point"); if (stat.f_flag & ST_RDONLY) { + saveMountNamespace(); + if (unshare(CLONE_NEWNS) == -1) throw SysError("setting up a private mount namespace"); diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 5e0e44935..cf133b57c 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -1,4 +1,5 @@ #include "ssh.hh" +#include "affinity.hh" namespace nix { @@ -34,7 +35,9 @@ std::unique_ptr SSHMaster::startCommand(const std::string auto conn = std::make_unique(); conn->sshPid = startProcess([&]() { + restoreAffinity(); restoreSignals(); + restoreMountNamespace(); close(in.writeSide.get()); close(out.readSide.get()); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 259eaf0a0..6e4536e6e 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -936,6 +936,7 @@ pid_t startProcess(std::function fun, const ProcessOptions & options) throw SysError("setting death signal"); #endif restoreAffinity(); + restoreMountNamespace(); fun(); } catch (std::exception & e) { try { @@ -1504,4 +1505,26 @@ std::unique_ptr createInterruptCallback(std::function return std::unique_ptr(res.release()); } +static AutoCloseFD fdSavedMountNamespace; + +void saveMountNamespace() +{ +#if __linux__ + std::once_flag done; + std::call_once(done, []() { + fdSavedMountNamespace = open("/proc/self/ns/mnt", O_RDONLY); + if (!fdSavedMountNamespace) + throw SysError("saving parent mount namespace"); + }); +#endif +} + +void restoreMountNamespace() +{ +#if __linux__ + if (fdSavedMountNamespace && setns(fdSavedMountNamespace.get(), CLONE_NEWNS) == -1) + throw SysError("restoring parent mount namespace"); +#endif +} + } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index bda87bee4..2689cbd8b 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -514,4 +514,13 @@ typedef std::function PathFilter; extern PathFilter defaultPathFilter; +/* Save the current mount namespace. Ignored if called more than + once. */ +void saveMountNamespace(); + +/* Restore the mount namespace saved by saveMountNamespace(). Ignored + if saveMountNamespace() was never called. */ +void restoreMountNamespace(); + + } diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 618895d38..11ea3b1f7 100755 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -401,8 +401,6 @@ static void _main(int argc, char * * argv) } else env[var.first] = var.second; - restoreAffinity(); - /* Run a shell using the derivation's environment. For convenience, source $stdenv/setup to setup additional environment variables and shell functions. Also don't @@ -446,7 +444,9 @@ static void _main(int argc, char * * argv) auto argPtrs = stringsToCharPtrs(args); + restoreAffinity(); restoreSignals(); + restoreMountNamespace(); execvp(shell.c_str(), argPtrs.data()); diff --git a/src/nix/edit.cc b/src/nix/edit.cc index c9671f76d..d8d5895bd 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -3,6 +3,7 @@ #include "eval.hh" #include "attr-path.hh" #include "progress-bar.hh" +#include "affinity.hh" #include @@ -72,6 +73,10 @@ struct CmdEdit : InstallableCommand stopProgressBar(); + restoreAffinity(); + restoreSignals(); + restoreMountNamespace(); + execvp(args.front().c_str(), stringsToCharPtrs(args).data()); throw SysError("cannot run editor '%s'", editor); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 1bbe256b2..77898c632 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -285,6 +285,8 @@ static int runProgram(const string & program, const Strings & args) if (pid == -1) throw SysError("forking"); if (pid == 0) { restoreAffinity(); + restoreSignals(); + restoreMountNamespace(); execvp(program.c_str(), stringsToCharPtrs(args2).data()); _exit(1); } diff --git a/src/nix/run.cc b/src/nix/run.cc index 35b763345..129707298 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -153,9 +153,9 @@ struct CmdRun : InstallablesCommand stopProgressBar(); - restoreSignals(); - restoreAffinity(); + restoreSignals(); + restoreMountNamespace(); /* If this is a diverted store (i.e. its "logical" location (typically /nix/store) differs from its "physical" location