Skip to content

Commit c4bf20c

Browse files
edolstraEricson2314
authored andcommitted
Mount allowed paths on storeFS with pure eval
No `AllowListSourceAccessor` for pure eval --- not needed anymore!
1 parent 3bf1268 commit c4bf20c

File tree

2 files changed

+62
-39
lines changed

2 files changed

+62
-39
lines changed

src/libexpr/eval.cc

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -213,35 +213,54 @@ EvalState::EvalState(
213213
, settings{settings}
214214
, symbols(StaticEvalSymbols::staticSymbolTable())
215215
, repair(NoRepair)
216-
, storeFS(makeMountedSourceAccessor({
217-
{CanonPath::root, makeEmptySourceAccessor()},
218-
/* In the pure eval case, we can simply require
219-
valid paths. However, in the *impure* eval
220-
case this gets in the way of the union
221-
mechanism, because an invalid access in the
222-
upper layer will *not* be caught by the union
223-
source accessor, but instead abort the entire
224-
lookup.
225-
226-
This happens when the store dir in the
227-
ambient file system has a path (e.g. because
228-
another Nix store there), but the relocated
229-
store does not.
230-
231-
TODO make the various source accessors doing
232-
access control all throw the same type of
233-
exception, and make union source accessor
234-
catch it, so we don't need to do this hack.
235-
*/
236-
{CanonPath(store->storeDir), store->getFSAccessor(settings.pureEval)},
237-
}))
238-
, rootFS([&] {
239-
auto accessor = [&]() -> decltype(rootFS) {
240-
/* In pure eval mode, we provide a filesystem that only
241-
contains the Nix store. */
242-
if (settings.pureEval)
243-
return storeFS;
216+
, storeFS([&] {
217+
auto accessor = makeMountedSourceAccessor({{CanonPath::root, makeEmptySourceAccessor()}});
218+
219+
/* In the pure eval case, we can simply require
220+
valid paths. However, in the *impure* eval
221+
case this gets in the way of the union
222+
mechanism, because an invalid access in the
223+
upper layer will *not* be caught by the union
224+
source accessor, but instead abort the entire
225+
lookup.
226+
227+
This happens when the store dir in the
228+
ambient file system has a path (e.g. because
229+
another Nix store there), but the relocated
230+
store does not.
231+
232+
TODO make the various source accessors doing
233+
access control all throw the same type of
234+
exception, and make union source accessor
235+
catch it, so we don't need to do this hack.
236+
*/
237+
if (settings.pureEval) {
238+
/* This is just an overkill way to make sure other store
239+
paths get this error, and not the "doesn't exist" error
240+
that the mounted source accessor would do on its own. */
241+
accessor->mount(
242+
CanonPath::root,
243+
AllowListSourceAccessor::create(
244+
getFSSourceAccessor(), {}, {CanonPath::root, CanonPath(store->storeDir)}, [&](const CanonPath & path) -> RestrictedPathError {
245+
throw RestrictedPathError(
246+
"access to absolute path '%1%' is forbidden in pure evaluation mode (use '--impure' to override)",
247+
CanonPath(store->storeDir) / path);
248+
}));
249+
/* We don't want to list store paths */
250+
accessor->mount(CanonPath(store->storeDir), makeEmptySourceAccessor());
251+
} else {
252+
accessor->mount(CanonPath(store->storeDir), store->getFSAccessor(false));
253+
}
254+
255+
return accessor;
256+
}())
257+
, rootFS([&] -> decltype(rootFS) {
258+
/* In pure eval mode, we provide a filesystem that only
259+
contains the Nix store. */
260+
if (settings.pureEval)
261+
return storeFS;
244262

263+
auto makeImpureAccessor = [&]() -> decltype(rootFS) {
245264
/* If we have a chroot store and pure eval is not enabled,
246265
use a union accessor to make the chroot store available
247266
at its logical location while still having the underlying
@@ -253,18 +272,16 @@ EvalState::EvalState(
253272
return makeUnionSourceAccessor({getFSSourceAccessor(), storeFS});
254273

255274
return getFSSourceAccessor();
256-
}();
275+
};
257276

258277
/* Apply access control if needed. */
259-
if (settings.restrictEval || settings.pureEval)
260-
accessor = AllowListSourceAccessor::create(
261-
accessor, {}, {}, [&settings](const CanonPath & path) -> RestrictedPathError {
262-
auto modeInformation = settings.pureEval ? "in pure evaluation mode (use '--impure' to override)"
263-
: "in restricted mode";
264-
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
278+
if (settings.restrictEval)
279+
return AllowListSourceAccessor::create(
280+
makeImpureAccessor(), {}, {}, [](const CanonPath & path) -> RestrictedPathError {
281+
throw RestrictedPathError("access to absolute path '%1%' is forbidden in restricted mode", path);
265282
});
266283

267-
return accessor;
284+
return makeImpureAccessor();
268285
}())
269286
, corepkgsFS(make_ref<MemorySourceAccessor>())
270287
, internalFS(make_ref<MemorySourceAccessor>())
@@ -351,13 +368,19 @@ void EvalState::allowPathLegacy(const Path & path)
351368

352369
void EvalState::allowPath(const StorePath & storePath)
353370
{
354-
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
371+
if (settings.pureEval) {
372+
storeFS->mount(CanonPath(store->printStorePath(storePath)), ref{store->getFSAccessor(storePath)});
373+
}
374+
if (settings.restrictEval) {
375+
auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>();
376+
assert(rootFS2);
355377
rootFS2->allowPrefix(CanonPath(store->printStorePath(storePath)));
378+
}
356379
}
357380

358381
void EvalState::allowClosure(const StorePath & storePath)
359382
{
360-
if (!rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
383+
if (!settings.pureEval && !settings.restrictEval)
361384
return;
362385

363386
StorePathSet closure;

tests/functional/restricted.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ traverseDir="${_NIX_TEST_SOURCE_DIR}/restricted-traverse-me"
6565
ln -sfn "${_NIX_TEST_SOURCE_DIR}/restricted-secret" "${_NIX_TEST_SOURCE_DIR}/restricted-innocent"
6666
mkdir -p "$traverseDir"
6767
goUp="..$(echo "$traverseDir" | sed -e 's,[^/]\+,..,g')"
68-
output="$(nix eval --raw --restrict-eval -I "$traverseDir" \
68+
output="$(nix eval --raw --impure --restrict-eval -I "$traverseDir" \
6969
--expr "builtins.readFile \"$traverseDir/$goUp${_NIX_TEST_SOURCE_DIR}/restricted-innocent\"" \
7070
2>&1 || :)"
7171
echo "$output" | grep "is forbidden"

0 commit comments

Comments
 (0)