# Copyright (c) 2026 The FreeBSD Foundation # # SPDX-License-Identifier: BSD-2-Clause # # This software was developed by Olivier Certner at # Kumacom SARL under sponsorship from the FreeBSD Foundation. SJ_JID_FILE=sj.jid atf_test_case concurrent_rules_exec_paths_changes concurrent_rules_exec_paths_changes_head() { atf_set descr "Consistency of rules and exec paths changes on same jail" } concurrent_rules_exec_paths_changes_body() { local rules exec_paths rules_es exec_paths_es for I in $(jot - 1 1000); do sysctl_set_and_check_rules "uid=$I>uid=1001" done & rules=$! for I in $(jot - 1 1000); do sysctl_set_and_check_exec_paths /nowhere/nonexistent$I done & exec_paths=$! wait $rules rules_es=$? wait $exec_paths exec_paths_es=$? # atf_check called in the asynchronous AND-OR lists above causes exit of the # subshells and also a write to the ATF result file. These writes are # concurrent and may cause the result file to be malformed. Consequently, # it is important that, once execution becomes sequential again, atf_fail() is # called again (and not just exit()). if [ $rules_es -ne 0 ] || [ $exec_paths_es -ne 0 ]; then atf_fail "Rules exit status: $rules_es, \ exec paths exit status: $exec_paths_es" fi } atf_test_case inheritance cleanup inheritance_head() { atf_set descr "Simple inheritance test (values propagated to child jail)" } inheritance_body() { local sj rules exec_paths # For the sake of not running the test under Kyua mac_do_ensure_disabled sj=$(launch_subjail) echo $sj > "${SJ_JID_FILE}" jail -m jid=$sj ${ROOT_JAIL_PARAM}=inherit JEXEC="jexec $sj" mac_do_check_disabled JEXEC= rules="uid=1001>uid=0" sysctl_set_and_check_rules $rules JEXEC="jexec $sj" sysctl_check_rules $rules JEXEC= rules="gid=1001>uid=0" sysctl_set_and_check_rules $rules JEXEC="jexec $sj" sysctl_check_rules $rules JEXEC= # Not really necessary, just to keep mac_do(4) disabled sysctl_set_and_check_rules "" exec_paths="/nowhere/nonexistent" sysctl_set_and_check_exec_paths $exec_paths JEXEC="jexec $sj" sysctl_check_exec_paths $exec_paths JEXEC= exec_paths="$MDO" sysctl_set_and_check_exec_paths $exec_paths JEXEC="jexec $sj" sysctl_check_exec_paths $exec_paths JEXEC= } inheritance_cleanup() { # We clean up our subjail manually just for the sake of launching this test # with atf-sh. Kyua is informed that these tests should run in a jail, and # kills it automatically after the test, which kills all subjails. It is # annoying that atf-sh does not offer a more practical way to pass # information from the body to the cleanup part than a file. jail -r $(cat "${SJ_JID_FILE}") rm -f "${SJ_JID_FILE}" } atf_test_case inheritance_relax_parent_jail cleanup inheritance_relax_parent_jail_head() { atf_set descr \ "Test sequential consistency in a \"relax parent rules\" scenario" } inheritance_relax_parent_jail_body() { local sj rules exec_paths subproc sj=$(launch_subjail) echo $sj > "${SJ_JID_FILE}" jail -m jid=$sj ${ROOT_JAIL_PARAM}=inherit rules="uid=1001>uid=0" sysctl_set_and_check_rules $rules # Additional inheritance sanity check JEXEC="jexec $sj" sysctl_check_rules $rules JEXEC= exec_paths="$MDO" sysctl_set_and_check_exec_paths $exec_paths # Additional inheritance sanity check JEXEC="jexec $sj" sysctl_check_exec_paths $exec_paths JEXEC= # Launch a process that tries to become 'root' from user 1002, and verify # that this always fails. { for I in $(jot - 1 1000); do jexec $sj "$MDO" -u 1002 -g 1002 -G 1002 "$MDO" -i true 2>/dev/null && exit 1 done; true; } & subproc=$! # Decouple the subjail from the parent jail, copying its parameters jail -m jid=$sj ${ROOT_JAIL_PARAM}=new # Allow user 1002 to become 'root' on the parent jail sysctl_set_and_check_rules "$rules;uid=1002>uid=0" JEXEC="jexec $sj" # Additional sanity check (that rules of the subjail are now independent) [ "$(sysctl_rules)" == $rules ] || atf_fail "Rules not copied" [ "$(sysctl_exec_paths)" == $exec_paths ] || atf_fail "Exec paths not copied" JEXEC= wait $subproc || atf_fail "A transition wrongly succeeded in the subjail!" } inheritance_relax_parent_jail_cleanup() { # See inheritance_cleanup() for explanations jail -r $(cat "${SJ_JID_FILE}") rm -f "${SJ_JID_FILE}" } atf_test_case same_knob_and_jail_parameter cleanup same_knob_and_jail_parameter_head() { atf_set descr \ "Corresponding sysctl knobs and jail parameters have same value" } same_knob_and_jail_parameter_body() { local sj rules exec_paths subproc sj=$(launch_subjail) echo $sj > "${SJ_JID_FILE}" # Set sysctl knobs, observe parameters rules="uid=19999>uid=21700" exec_paths="/improbable/path/he" JEXEC="jexec $sj" sysctl_set_and_check_rules $rules sysctl_set_and_check_exec_paths $exec_paths JEXEC= atf_check -o inline:"$rules\n" jls -j $sj ${RULES_JAIL_PARAM} atf_check -o inline:"${exec_paths}\n" jls -j $sj ${EXEC_PATHS_JAIL_PARAM} # Set parameters, observe knobs rules="uid=128000>uid=-1" exec_paths="/hello/i_ve/changed" jail -m jid=$sj ${RULES_JAIL_PARAM}=$rules \ ${EXEC_PATHS_JAIL_PARAM}=${exec_paths} JEXEC="jexec $sj" sysctl_check_rules $rules sysctl_check_exec_paths $exec_paths JEXEC= } same_knob_and_jail_parameter_cleanup() { # See inheritance_cleanup() for explanations jail -r $(cat "${SJ_JID_FILE}") rm -f "${SJ_JID_FILE}" } atf_init_test_cases() { . $(atf_get_srcdir)/common.sh atf_require_prog jot # Needs an absolute path for mdo(1), to set it in exec_paths atf_require_prog "$MDO" atf_add_test_case concurrent_rules_exec_paths_changes atf_add_test_case inheritance atf_add_test_case inheritance_relax_parent_jail atf_add_test_case same_knob_and_jail_parameter }