Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions doc/manual/command-ref/conf-file.xml
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,34 @@ builtins.fetchurl {

</varlistentry>

<varlistentry xml:id="conf-enable-native-code"><term><literal>allow-unsafe-native-code-during-evaluation</literal></term>

<listitem>

<para>If set to <literal>true</literal>, extra functions that allow
nix expressions to extend the language are added to the <literal>
builtins</literal> set. Because these functions entail running arbitrary
code as the evaluating user, this option should only be enabled when
all evaluated nix expressions are fully trusted! Defaults to <literal>
false</literal>.</para>

</listitem>

</varlistentry>

<varlistentry xml:id="conf-extra-builtins-file"><term><literal>extra-builtins-file</literal></term>

<listitem>

<para>An absolute path to a Nix expression specifying
<link linkend="builtin-extraBuiltins">extraBuiltins</link>. See
the documentation of that builtin for more details. Defaults to
<filename>$NIX_CONF_DIR/extra-builtins.nix</filename>.</para>

</listitem>

</varlistentry>


</variablelist>

Expand Down
58 changes: 58 additions & 0 deletions doc/manual/expressions/builtins.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,65 @@ stdenv.mkDerivation (rec {

</varlistentry>

<varlistentry><term><function>builtins.exec</function>
<replaceable>args</replaceable></term>

<listitem>
<para>Run the command specified by the arguments given in
<varname>args</varname> and parse its <literal>stdout</literal> as
a nix expression which is parsed into the return value. The first argument
in the list is treated as a program name, and it is looked up in the
<envar>PATH</envar>. This builtin is only available when the
<literal>allow-unsafe-native-code-during-evaluation</literal>
option is set to <literal>true</literal>, as the executed program can
run arbitrary code as the user running the nix evaluation.</para>
</listitem>

</varlistentry>

<varlistentry><term><function>builtins.importNative</function>
<replaceable>path</replaceable>
<replaceable>symbol</replaceable></term>

<listitem>
<para>Run native code to initialize a nix value. The dynamic shared
object at <varname>path</varname> is dynamically opened and the
symbol at <varname>symbol</varname> is interpreted as a C++ function
taking a reference to a nix::EvalState representing the current evaluator
state and an uninitialized reference to a nix::Value that is expected
to be initialized by the function. That value is the result of the
call to <literal>importNative</literal>. This builtin is only
available when the <literal>allow-unsafe-native-code-during-evaluation</literal>
option is set to <literal>true</literal>, as the loaded C++ code can
run arbitrary code as the user running the nix evaluation.</para>

<para>As the DSO is loaded into a running nix process, it should not
be linked to any of the shared libraries nix is linked to and can
expect them to be available at runtime. Due to the lack of a stable
internal ABI, users should take care that the headers used to build
the DSO match those of the currently running nix evaluation.</para>
</listitem>

</varlistentry>

<varlistentry
xml:id='builtin-extraBuiltins'><term><varname>builtins.extraBuiltins</varname></term>

<listitem><para>The result of calling the function defined in the
expression specified by the <literal>extra-builtins-file</literal>
setting (default <filename>$NIX_CONF_DIR/extra-builtins.nix</filename>)
at a set containing at least the <literal>exec</literal> and
<literal>importNative</literal> builtins, even if the <literal>
allow-unsafe-native-code-during-evaluation</literal> option is set
to <literal>false</literal>. This can be used to give nix expressions
access to extra functions that rely on running native code during
evaluation, without requiring them to be built into nix or requiring
the user to arbitrarily trust any nix expressions they evaluate.</para>

<para>This defaults to <literal>null</literal> if the file doesn't
exist.</para></listitem>

</varlistentry>
</variablelist>


Expand Down
2 changes: 1 addition & 1 deletion src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");

if (settings.restrictEval || settings.pureEval) {
allowedPaths = PathSet();
allowedPaths = PathSet({absPath(settings.extraBuiltinsFile)});
for (auto & i : searchPath) {
auto r = resolveSearchPathElem(i);
if (!r.first) continue;
Expand Down
43 changes: 43 additions & 0 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@
#include <regex>
#include <dlfcn.h>

#if HAVE_BOEHMGC

#include <gc/gc.h>
#include <gc/gc_cpp.h>

#define NEW new (UseGC)

#else

#define NEW new

#endif



namespace nix {

Expand Down Expand Up @@ -2153,6 +2167,35 @@ void EvalState::createBaseEnv()
}
addConstant("__nixPath", v);

try {
auto path = absPath(settings.extraBuiltinsFile);
auto fun = allocValue();
evalFile(path, *fun);
Value * arg;
if (settings.enableNativeCode) {
arg = baseEnv.values[0];
} else {
arg = allocValue();
mkAttrs(*arg, 2);

auto sExec = symbols.create("exec");
auto vExec = allocAttr(*arg, sExec);
vExec->type = tPrimOp;
vExec->primOp = NEW PrimOp(prim_exec, 1, sExec);

auto sImportNative = symbols.create("importNative");
auto vImportNative = allocAttr(*arg, sImportNative);
vImportNative->type = tPrimOp;
vImportNative->primOp = NEW PrimOp(prim_importNative, 2, sImportNative);
}
mkApp(v, *fun, *arg);
} catch (SysError & e) {
if (e.errNo != ENOENT)
throw;
mkNull(v);
}
addConstant("__extraBuiltins", v);

if (RegisterPrimOp::primOps)
for (auto & primOp : *RegisterPrimOp::primOps)
addPrimOp(std::get<0>(primOp), std::get<1>(primOp), std::get<2>(primOp));
Expand Down
3 changes: 3 additions & 0 deletions src/libstore/globals.hh
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ public:

Setting<Strings> allowedUris{this, {}, "allowed-uris",
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};

Setting<Path> extraBuiltinsFile{this, fmt("%s/extra-builtins.nix", nixConfDir), "extra-builtins-file",
"The path to a nix expression defining extra expression-language level builtins."};
};


Expand Down
11 changes: 11 additions & 0 deletions tests/extra-builtins.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
source common.sh

set -o pipefail

e=$(nix eval --option extra-builtins-file extra-builtins/extra-builtins.nix '(builtins.extraBuiltins.true)')

[ "$e"x = "true"x ];

e=$(nix eval --option restrict-eval true --option allow-unsafe-native-code-during-evaluation true --option extra-builtins-file extra-builtins/extra-builtins.nix '(builtins.extraBuiltins.true)')

[ "$e"x = "true"x ];
2 changes: 2 additions & 0 deletions tests/extra-builtins/extra-builtins.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{ exec, importNative, ... }:
{ true = exec [ "bash" "-c" "echo true" ]; }
3 changes: 2 additions & 1 deletion tests/local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ nix_tests = \
run.sh \
brotli.sh \
pure-eval.sh \
check.sh
check.sh \
extra-builtins.sh
# parallel.sh

install-tests += $(foreach x, $(nix_tests), tests/$(x))
Expand Down