Skip to content

Commit 91d8838

Browse files
edolstraEricson2314
authored andcommitted
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 16db8dc)
1 parent 99c690c commit 91d8838

File tree

1 file changed

+29
-17
lines changed

1 file changed

+29
-17
lines changed

src/libstore/build.cc

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ static void commonChildInit(int stderrFd)
454454
throw SysError(format("creating a new session"));
455455

456456
/* Dup the write side of the logger pipe into stderr. */
457-
if (dup2(stderrFd, STDERR_FILENO) == -1)
457+
if (stderrFd != -1 && dup2(stderrFd, STDERR_FILENO) == -1)
458458
throw SysError("cannot pipe standard error into log file");
459459

460460
/* Dup stderr to stdout. */
@@ -2264,6 +2264,27 @@ void DerivationGoal::startBuilder()
22642264
if (unlockpt(builderOut.get()))
22652265
throw SysError("unlocking pseudoterminal");
22662266

2267+
/* Open the slave side of the pseudoterminal and use it as stderr. */
2268+
auto openSlave = [&]()
2269+
{
2270+
AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
2271+
if (!builderOut)
2272+
throw SysError("opening pseudoterminal slave");
2273+
2274+
// Put the pt into raw mode to prevent \n -> \r\n translation.
2275+
struct termios term;
2276+
if (tcgetattr(builderOut.get(), &term))
2277+
throw SysError("getting pseudoterminal attributes");
2278+
2279+
cfmakeraw(&term);
2280+
2281+
if (tcsetattr(builderOut.get(), TCSANOW, &term))
2282+
throw SysError("putting pseudoterminal into raw mode");
2283+
2284+
if (dup2(builderOut.get(), STDERR_FILENO) == -1)
2285+
throw SysError("cannot pipe standard error into log file");
2286+
};
2287+
22672288
result.startTime = time(0);
22682289

22692290
/* Fork a child to build the package. */
@@ -2318,6 +2339,11 @@ void DerivationGoal::startBuilder()
23182339
Pid helper = startProcess([&]() {
23192340
sendPid.readSide.close();
23202341

2342+
/* We need to open the slave early, before
2343+
CLONE_NEWUSER. Otherwise we get EPERM when running as
2344+
root. */
2345+
openSlave();
2346+
23212347
/* Drop additional groups here because we can't do it
23222348
after we've created the new user namespace. FIXME:
23232349
this means that if we're not root in the parent
@@ -2410,6 +2436,7 @@ void DerivationGoal::startBuilder()
24102436
fallback:
24112437
options.allowVfork = !buildUser && !drv->isBuiltin();
24122438
pid = startProcess([&]() {
2439+
openSlave();
24132440
runChild();
24142441
}, options);
24152442
}
@@ -2723,22 +2750,7 @@ void DerivationGoal::runChild()
27232750

27242751
try { /* child */
27252752

2726-
/* Open the slave side of the pseudoterminal. */
2727-
AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
2728-
if (!builderOut)
2729-
throw SysError("opening pseudoterminal slave");
2730-
2731-
// Put the pt into raw mode to prevent \n -> \r\n translation.
2732-
struct termios term;
2733-
if (tcgetattr(builderOut.get(), &term))
2734-
throw SysError("getting pseudoterminal attributes");
2735-
2736-
cfmakeraw(&term);
2737-
2738-
if (tcsetattr(builderOut.get(), TCSANOW, &term))
2739-
throw SysError("putting pseudoterminal into raw mode");
2740-
2741-
commonChildInit(builderOut.get());
2753+
commonChildInit(-1);
27422754

27432755
try {
27442756
setupSeccomp();

0 commit comments

Comments
 (0)