1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-11 13:06:01 +01:00

Open slave pseudoterminal before CLONE_NEWUSER

Otherwise, when running as root and user namespaces are enabled,
opening the slave fails with EPERM.

Fixes "opening pseudoterminal slave: Permission denied" followed by a
hang (https://hydra.nixos.org/build/213104244), and "error: getting
sandbox mount namespace: No such file or directory" (#8072), which
happens when the child fails very quickly and consequently reading
/proc/<child>/ns fails.

(cherry picked from commit 16db8dc96f)
This commit is contained in:
Eelco Dolstra 2023-03-20 17:04:57 +01:00 committed by John Ericson
parent 94eba8a85a
commit 5fca88973f

View file

@ -454,7 +454,7 @@ static void commonChildInit(int stderrFd)
throw SysError(format("creating a new session")); throw SysError(format("creating a new session"));
/* Dup the write side of the logger pipe into stderr. */ /* Dup the write side of the logger pipe into stderr. */
if (dup2(stderrFd, STDERR_FILENO) == -1) if (stderrFd != -1 && dup2(stderrFd, STDERR_FILENO) == -1)
throw SysError("cannot pipe standard error into log file"); throw SysError("cannot pipe standard error into log file");
/* Dup stderr to stdout. */ /* Dup stderr to stdout. */
@ -2264,6 +2264,27 @@ void DerivationGoal::startBuilder()
if (unlockpt(builderOut.get())) if (unlockpt(builderOut.get()))
throw SysError("unlocking pseudoterminal"); throw SysError("unlocking pseudoterminal");
/* Open the slave side of the pseudoterminal and use it as stderr. */
auto openSlave = [&]()
{
AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
if (!builderOut)
throw SysError("opening pseudoterminal slave");
// Put the pt into raw mode to prevent \n -> \r\n translation.
struct termios term;
if (tcgetattr(builderOut.get(), &term))
throw SysError("getting pseudoterminal attributes");
cfmakeraw(&term);
if (tcsetattr(builderOut.get(), TCSANOW, &term))
throw SysError("putting pseudoterminal into raw mode");
if (dup2(builderOut.get(), STDERR_FILENO) == -1)
throw SysError("cannot pipe standard error into log file");
};
result.startTime = time(0); result.startTime = time(0);
/* Fork a child to build the package. */ /* Fork a child to build the package. */
@ -2318,6 +2339,11 @@ void DerivationGoal::startBuilder()
Pid helper = startProcess([&]() { Pid helper = startProcess([&]() {
sendPid.readSide.close(); sendPid.readSide.close();
/* We need to open the slave early, before
CLONE_NEWUSER. Otherwise we get EPERM when running as
root. */
openSlave();
/* Drop additional groups here because we can't do it /* Drop additional groups here because we can't do it
after we've created the new user namespace. FIXME: after we've created the new user namespace. FIXME:
this means that if we're not root in the parent this means that if we're not root in the parent
@ -2410,6 +2436,7 @@ void DerivationGoal::startBuilder()
fallback: fallback:
options.allowVfork = !buildUser && !drv->isBuiltin(); options.allowVfork = !buildUser && !drv->isBuiltin();
pid = startProcess([&]() { pid = startProcess([&]() {
openSlave();
runChild(); runChild();
}, options); }, options);
} }
@ -2723,22 +2750,7 @@ void DerivationGoal::runChild()
try { /* child */ try { /* child */
/* Open the slave side of the pseudoterminal. */ commonChildInit(-1);
AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
if (!builderOut)
throw SysError("opening pseudoterminal slave");
// Put the pt into raw mode to prevent \n -> \r\n translation.
struct termios term;
if (tcgetattr(builderOut.get(), &term))
throw SysError("getting pseudoterminal attributes");
cfmakeraw(&term);
if (tcsetattr(builderOut.get(), TCSANOW, &term))
throw SysError("putting pseudoterminal into raw mode");
commonChildInit(builderOut.get());
try { try {
setupSeccomp(); setupSeccomp();