mte/unikernel/duniverse/dune_/test/blackbox-tests/test-cases/sandbox/sandboxing.t

385 lines
10 KiB
Perl
Raw Normal View History

2025-11-11 02:07:51 +01:00
If an action does not respect the dependency specification, it results in a broken
build. Dune fails to detect that:
$ echo '(lang dune 1.12)' > dune-project
$ true > dune
$ echo '(rule (target a) (deps) (action (bash "echo a | tee a > b")))' >> dune
$ echo '(rule (target b) (deps) (action (bash "echo b | tee a > b")))' >> dune
$ echo '(rule (target c) (deps a b) (action (bash "cat a b > c")))' >> dune
$ dune build c
$ cat _build/default/c
b
b
$ true > dune
$ echo '(rule (target a) (deps) (action (bash "echo a > a")))' >> dune
$ echo '(rule (target b) (deps) (action (bash "echo b > b")))' >> dune
$ echo '(rule (target c) (deps a b) (action (bash "cat a b > c")))' >> dune
$ dune build c
$ cat _build/default/c
b
b
(it's not obvious what the correct result is on the first invocation, but the second
invocation is clearly broken (it uses a wrongly cached result))
These rules clearly depend on sandboxing. Specifying that makes the build
well-behaved:
$ rm -rf _build
$ true > dune
$ echo '(rule (target a) (deps (sandbox always)) (action (bash "echo a | tee a > b")))' >> dune
$ echo '(rule (target b) (deps (sandbox always)) (action (bash "echo b | tee a > b")))' >> dune
$ echo '(rule (target c) (deps a b) (action (bash "cat a b > c")))' >> dune
$ dune build c
$ cat _build/default/c
a
b
Some errors:
$ rm -rf _build
$ true > dune
$ echo '(rule (target a) (deps (sandbox always none)) (action (bash "echo a > a")))' >> dune
$ dune build a
File "dune", line 1, characters 32-43:
1 | (rule (target a) (deps (sandbox always none)) (action (bash "echo a > a")))
^^^^^^^^^^^
Error: Inconsistent sandboxing configuration. Sandboxing mode none is both
allowed and disallowed
[1]
When we don't pass [preserve_file_kind], the rules can observe the file kind
changing based on sandbox mode chosen:
$ rm -rf _build
$ echo text-file > text-file
$ true > dune
$ echo '(rule (deps text-file) (target t) (action (with-stdout-to %{target} (run file -h text-file))))' >> dune
$ dune build t --sandbox symlink
$ cat _build/default/t | grep -Eo 'link|ASCII'
link
$ dune build t --sandbox none
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox copy
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox hardlink
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
When we pass [preserve_file_kind], the file type seen by the rule is preserved:
$ true > dune
$ echo '(rule (target t) (deps text-file (sandbox preserve_file_kind)) (action (with-stdout-to %{target} (run file -h text-file))))' >> dune
$ dune build t --sandbox symlink
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox none
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox copy
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox hardlink
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
If rule fails to generate targets, we give a good error message, even with sandboxing:
$ true > dune
$ echo '(rule (target t) (deps (sandbox always)) (action (run true)))' >> dune
$ dune build t
File "dune", line 1, characters 0-61:
1 | (rule (target t) (deps (sandbox always)) (action (run true)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Rule failed to generate the following targets:
- t
[1]
If rule is configured to require sandboxing, but clearly needs none, we sandbox
anyway.
$ true > dune
$ cat >dune <<EOF
> (rule
> (target t)
> (deps (sandbox always))
> (action (write-file t "")))
> EOF
$ dune build t
If an action [chdir]s to a non-existing directory, it is created.
$ echo '(lang dune 2.6)' > dune-project
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox none))
> (action
> (chdir dir
> (progn
> (no-infer (write-file file hi))
> (write-file ../t hi)))))
> EOF
$ dune build t
$ cat _build/default/dir/file
hi
$ cat _build/default/t
hi
Now the same but with sandboxing. The action succeeds but the directory is not
re-created in the build directory.
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox always))
> (action
> (chdir dir
> (progn
> (run true)
> (no-infer (write-file file hi))
> (write-file ../t hi)))))
> EOF
$ dune build t
$ cat _build/default/dir/file
cat: _build/default/dir/file: No such file or directory
[1]
$ cat _build/default/t
hi
Show errors when [chdir]ing outside of the build directory.
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox none))
> (action
> (chdir /dir
> (progn
> (no-infer (write-file file hi))
> (write-file ../t hi)))))
> EOF
$ dune build t
File "dune", line 5, characters 9-13:
5 | (chdir /dir
^^^^
Error: Directory /dir is outside the build directory. This is not allowed.
[1]
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox none))
> (action (chdir ../../dir (progn (no-infer (write-file file hi)) (write-file ../t hi)))))
> EOF
$ dune build t
File "dune", line 4, characters 16-25:
4 | (action (chdir ../../dir (progn (no-infer (write-file file hi)) (write-file ../t hi)))))
^^^^^^^^^
Error: Directory
$TESTCASE_ROOT/../../dir
is outside the build directory. This is not allowed.
[1]
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox none))
> (action
> (chdir ../dir
> (progn
> (no-infer (write-file file hi))
> (write-file ../t hi)))))
> EOF
$ dune build t
File "dune", line 5, characters 9-15:
5 | (chdir ../dir
^^^^^^
Error: Directory
$TESTCASE_ROOT/../dir
is outside the build directory. This is not allowed.
[1]
Sandboxing with hard links correctly resolves (follows) symbolic links.
$ echo hi > file
$ ln -s file link
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps link)
> (action (bash "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
The action observed [link] as a regular file with the right contents.
$ dune build t --sandbox hardlink
regular file
$ cat _build/default/t
hi
# CR-someday amokhov: Apparently, Dune turns [link] into a regular file when
# copying it into the build directory. This seems wrong. Fix this or document
# why this is the right thing to do.
$ dune_cmd stat kind _build/default/link
regular file
Now let's test what happens with external files.
$ mkdir test; cd test
$ echo '(lang dune 2.9)' > dune-project
$ echo '(lang dune 2.9)' > dune-workspace
Now [../link] is an external file from the point of view of Dune, so it will not
be copied to the build directory.
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps ../link)
> (action (bash "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
# CR-someday amokhov: The action observe a symlink. It should not be the case
# since the symlink will surely not point to the right place.
$ cat ../link
hi
$ dune build t --sandbox hardlink
symbolic link
Using the absolute path [$PWD/../link].
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps $PWD/../link)
> (action (bash "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
Sandboxed actions can observe external dependencies as symbolic links.
$ dune build t --sandbox hardlink
$ cat _build/default/t
hi
Now let's see what happens if the the symbolic link is produced by a rule.
$ cat > dune <<EOF
> (rule
> (deps file)
> (targets link)
> (action (run ln -s file link)))
> (rule
> (targets t)
> (deps link)
> (action (system "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
$ echo hi again > file
The action observed [link] as a regular file with the right contents.
$ dune build t --sandbox hardlink
regular file
$ cat _build/default/t
hi again
Test that [link] is in fact a symbolic link.
$ dune_cmd stat kind _build/default/link
symbolic link
Test how Dune handles a loop with symbolic links.
$ cat > dune <<EOF
> (rule
> (targets link)
> (action (bash "ln -s link link")))
> (rule
> (targets t)
> (deps link)
> (action (bash "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
This loop is caught immediately after running the rule that creates it.
$ dune build t --sandbox hardlink
File "dune", lines 1-3, characters 0-57:
1 | (rule
2 | (targets link)
3 | (action (bash "ln -s link link")))
Error: Error trying to read targets after a rule was run:
- link: Cyclic symbolic link
[1]
Let's try to create it in another way. We'll link to the external [../link],
which is circular.
$ cat > dune <<EOF
> (rule
> (deps $PWD/../link)
> (targets link)
> (action (run ln -s $PWD/../link link)))
> (rule
> (targets t)
> (deps link)
> (action (system "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
$ (cd ..; rm link; ln -s link link)
Still doesn't work: now the loop is detected when running [stat] on the
dependency.
$ dune build t --sandbox hardlink 2>&1 | grep -v "lines 1"
1 | (rule
2 | (deps $TESTCASE_ROOT/test/../link)
3 | (targets link)
4 | (action (run ln -s $TESTCASE_ROOT/test/../link link)))
Error: File unavailable:
$TESTCASE_ROOT/test/../link
Cyclic symbolic link
So, it seems like we must play dirty to create a symbolic link loop.
$ cat > dune <<EOF
> (rule
> (targets link)
> (action (run ln -s $PWD/../link link)))
> (rule
> (deps link (sandbox none))
> (targets dirty-rule)
> (action (system "(cd $PWD/..; rm link; ln -s link link); touch dirty-rule")))
> (rule
> (targets t)
> (deps link dirty-rule)
> (action (system "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
$ (cd ..; rm link; ln -s file link)
Finally, we get to see the error message printed out at sandbox creation.
$ dune build t --sandbox hardlink
File "dune", lines 8-11, characters 0-105:
8 | (rule
9 | (targets t)
10 | (deps link dirty-rule)
11 | (action (system "cp %{deps} t; dune_cmd stat kind %{deps}")))
Error: Sandbox creation error: cannot resolve symbolic link
"_build/default/link".
Reason: Too many indirections; is this a cyclic symbolic link?
[1]