pax_global_header00006660000000000000000000000064152645155550014527gustar00rootroot0000000000000052 comment=8787d1a4fdd7c64791607a1122d2408b3b35ade4 minijail-2026.05.18+git20261016.8787d1a/000077500000000000000000000000001526451555500163335ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/.clang-format000066400000000000000000000003661526451555500207130ustar00rootroot00000000000000BasedOnStyle: LLVM IndentWidth: 8 UseTab: Always BreakBeforeBraces: Linux AlignConsecutiveMacros: true AllowShortEnumsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortFunctionsOnASingleLine: false IndentCaseLabels: false minijail-2026.05.18+git20261016.8787d1a/.github/000077500000000000000000000000001526451555500176735ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/.github/workflows/000077500000000000000000000000001526451555500217305ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/.github/workflows/build-test-ci.yml000066400000000000000000000017371526451555500251300ustar00rootroot00000000000000# GitHub actions workflow. # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions name: Build+Test CI on: push: branches: [master] tags: [linux-v*] schedule: # The GH mirroring from Google GoB does not trigger push actions. # Fire it every other day to provide some coverage. This will run ~8 AM PT. - cron: '39 3 */2 * *' # Allow for manual triggers from the web. workflow_dispatch: jobs: build-test: strategy: fail-fast: false matrix: os: [ubuntu-latest] cc: [gcc, clang] runs-on: ${{ matrix.os }} env: CC: ${{ matrix.cc }} steps: - uses: actions/checkout@v6 - name: Update distro sources run: sudo apt-get update - name: Install system packages run: sudo apt-get install libcap-dev - name: Install Googletest (gtest/gmock) run: ./get_googletest.sh - run: make VERBOSE=1 - run: make VERBOSE=1 check minijail-2026.05.18+git20261016.8787d1a/.github/workflows/close-pull-request.yml000066400000000000000000000012361526451555500262220ustar00rootroot00000000000000# GitHub actions workflow. # https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # https://github.com/superbrothers/close-pull-request name: Close Pull Request on: pull_request_target: types: [opened] jobs: run: runs-on: ubuntu-latest steps: - uses: superbrothers/close-pull-request@v3 with: comment: > Thanks for your contribution! Unfortunately, we don't use GitHub pull requests to manage code contributions to this repository. Instead, please see [README.md](../blob/HEAD/README.md) which provides full instructions on how to get involved. minijail-2026.05.18+git20261016.8787d1a/.github/workflows/coverity.yml000066400000000000000000000017041526451555500243210ustar00rootroot00000000000000# GitHub actions workflow. # https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # https://scan.coverity.com/projects/google-minijail name: Coverity Scan on: push: branches: [master] schedule: # The GH mirroring from Google GoB does not trigger push actions. # Fire it once a week to provide some coverage. - cron: '39 2 * * WED' # Allow for manual triggers from the web. workflow_dispatch: jobs: coverity: strategy: matrix: os: [ubuntu-latest] cc: [clang] runs-on: ${{ matrix.os }} env: CC: ${{ matrix.cc }} steps: - uses: actions/checkout@v6 - name: Update distro sources run: sudo apt-get update - name: Install system packages run: sudo apt-get install -y libcap-dev - uses: vapier/coverity-scan-action@v1 with: email: ${{ secrets.COVERITY_SCAN_EMAIL }} token: ${{ secrets.COVERITY_SCAN_TOKEN }} minijail-2026.05.18+git20261016.8787d1a/.gitignore000066400000000000000000000012171526451555500203240ustar00rootroot00000000000000*.d *.depends /*.gen.c *.o *.a /constants.json # Coverage-related files that appear when building with MODE=profiling. *.gcno *.gcda /lcov-coverage.info /lcov-html/ # libseccomp. /libseccomp/ # Python-related files. /tools/__pycache__/ *.pyc # Shared libraries when compiling in-tree. *.so # Executables when compiling in-tree. /dump_constants /minijail0 /parse_seccomp_policy /*_unittest # common.mk TEST(). *.test # Google Test. googletest-* # Cargo build targets. /rust/**/target /rust/**/*.rs.bk /rust/**/Cargo.lock # VSCode workspace. minijail.code-workspace # IntelliJ projects. .idea # Generated code: /rust/minijail-sys/libminijail.rs minijail-2026.05.18+git20261016.8787d1a/Android.bp000066400000000000000000000361621526451555500202460ustar00rootroot00000000000000// Copyright (C) 2015 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Common variables. // ========================================================= package { default_applicable_licenses: ["external_minijail_license"], } // Added automatically by a large-scale-change that took the approach of // 'apply every license found to every target'. While this makes sure we respect // every license restriction, it may not be entirely correct. // // e.g. GPL in an MIT project might only apply to the contrib/ directory. // // Please consider splitting the single license below into multiple licenses, // taking care not to lose any license_kind information, and overriding the // default license using the 'licenses: [...]' property on targets as needed. // // For unused files, consider creating a 'fileGroup' with "//visibility:private" // to attach the license to, and including a comment whether the files may be // used in the current project. // // large-scale-change included anything that looked like it might be a license // text as a license_text. e.g. LICENSE, NOTICE, COPYING etc. // // Please consider removing redundant or irrelevant files from 'license_text:'. // See: http://go/android-license-faq license { name: "external_minijail_license", visibility: [":__subpackages__"], license_kinds: [ "SPDX-license-identifier-Apache-2.0", "SPDX-license-identifier-BSD", ], license_text: [ "LICENSE", "NOTICE", ], } libminijailSrcFiles = [ "bpf.c", "landlock_util.c", "libminijail.c", "signal_handler.c", "syscall_filter.c", "syscall_wrapper.c", "system.c", "util.c", ] unittestSrcFiles = [ "testrunner.cc", "test_util.cc", ] minijailCommonLibraries = ["libcap"] cc_defaults { name: "libminijail_flags", cflags: [ "-D_FILE_OFFSET_BITS=64", "-DALLOW_DEBUG_LOGGING", "-DALLOW_DUPLICATE_SYSCALLS", "-DDEFAULT_PIVOT_ROOT=\"/var/empty\"", "-DBINDMOUNT_ALLOWED_PREFIXES=\"\"", "-Wall", "-Werror", ], target: { darwin: { enabled: false, }, }, } // Static library for generated code. // ========================================================= cc_object { name: "libminijail_gen_syscall_obj", vendor_available: true, product_available: true, recovery_available: true, srcs: ["gen_syscalls.c"], cflags: [ "-dD", "-E", "-Wall", "-Werror", ], apex_available: [ "//apex_available:platform", "com.android.adbd", "com.android.compos", "com.android.media.swcodec", "com.android.virt", ], min_sdk_version: "29", } cc_genrule { name: "libminijail_gen_syscall", vendor_available: true, product_available: true, recovery_available: true, tool_files: ["gen_syscalls.sh"], cmd: "$(location gen_syscalls.sh) $(in) $(out)", srcs: [":libminijail_gen_syscall_obj"], out: ["libsyscalls.c"], apex_available: [ "//apex_available:platform", "com.android.adbd", "com.android.compos", "com.android.media.swcodec", "com.android.virt", ], } cc_object { name: "libminijail_gen_constants_obj", vendor_available: true, product_available: true, recovery_available: true, srcs: ["gen_constants.c"], cflags: [ "-dD", "-E", "-Wall", "-Werror", ], apex_available: [ "//apex_available:platform", "com.android.adbd", "com.android.compos", "com.android.media.swcodec", "com.android.virt", ], min_sdk_version: "29", } cc_genrule { name: "libminijail_gen_constants", vendor_available: true, product_available: true, recovery_available: true, tool_files: ["gen_constants.sh"], cmd: "$(location gen_constants.sh) $(in) $(out)", srcs: [":libminijail_gen_constants_obj"], out: ["libconstants.c"], apex_available: [ "//apex_available:platform", "com.android.adbd", "com.android.compos", "com.android.media.swcodec", "com.android.virt", ], } cc_library_static { name: "libminijail_generated", vendor_available: true, product_available: true, recovery_available: true, defaults: ["libminijail_flags"], host_supported: true, target: { android: { generated_sources: [ "libminijail_gen_syscall", "libminijail_gen_constants", ], }, host: { srcs: [ "linux-x86/libconstants.gen.c", "linux-x86/libsyscalls.gen.c", ], }, }, apex_available: [ "//apex_available:platform", "com.android.adbd", "com.android.compos", "com.android.media.swcodec", "com.android.virt", ], min_sdk_version: "29", } cc_object { name: "libminijail_gen_constants_llvmir", vendor_available: true, product_available: true, recovery_available: true, host_supported: true, cflags: [ "-S", "-O0", "-emit-llvm", ], target: { android: { generated_sources: ["libminijail_gen_constants"], }, host: { srcs: ["linux-x86/libconstants.gen.c"], }, }, } cc_object { name: "libminijail_gen_syscall_llvmir", vendor_available: true, product_available: true, recovery_available: true, host_supported: true, cflags: [ "-S", "-O0", "-emit-llvm", ], target: { android: { generated_sources: ["libminijail_gen_syscall"], }, host: { srcs: ["linux-x86/libsyscalls.gen.c"], }, }, } // libminijail shared and static library for target. // ========================================================= cc_library { name: "libminijail", host_supported: true, vendor_available: true, product_available: true, recovery_available: true, vndk: { enabled: true, }, defaults: ["libminijail_flags"], srcs: libminijailSrcFiles, static: { whole_static_libs: ["libminijail_generated"] + minijailCommonLibraries, }, shared: { static_libs: ["libminijail_generated"], shared_libs: minijailCommonLibraries, }, export_include_dirs: ["."], target: { host: { cflags: [ "-DPRELOADPATH=\"/invalidminijailpreload.so\"", ], }, }, apex_available: [ "//apex_available:platform", "com.android.adbd", "com.android.compos", "com.android.media.swcodec", "com.android.virt", ], min_sdk_version: "29", } // Example ASan-ified libminijail shared library for target. // Commented out since it's only needed for local debugging. // ========================================================= //cc_library_shared { // name: "libminijail_asan", // defaults: ["libminijail_flags"], // // sanitize: { // address: true, // }, // relative_install_path: "asan", // srcs: libminijailSrcFiles, // // static_libs: ["libminijail_generated"], // shared_libs: minijailCommonLibraries, // export_include_dirs: ["."], //} // libminijail native unit tests using gtest. // // For a device, run with: // adb shell /data/nativetest/libminijail_unittest_gtest/libminijail_unittest_gtest // // For host, run with: // out/host/linux-x86/nativetest(64)/libminijail_unittest_gtest/libminijail_unittest_gtest // ========================================================= cc_test { name: "libminijail_unittest_gtest", defaults: ["libminijail_flags"], // TODO(b/31395668): Re-enable once the seccomp(2) syscall becomes available. //host_supported: true srcs: libminijailSrcFiles + ["libminijail_unittest.cc"] + unittestSrcFiles, static_libs: ["libminijail_generated"], shared_libs: minijailCommonLibraries, target: { android: { cflags: ["-Wno-writable-strings"], test_suites: ["device-tests"], }, host: { cflags: ["-DPRELOADPATH=\"/invalid\""], }, }, } // Syscall filtering native unit tests using gtest. // // For a device, run with: // adb shell /data/nativetest/syscall_filter_unittest_gtest/syscall_filter_unittest_gtest // // For host, run with: // out/host/linux-x86/nativetest(64)/syscall_filter_unittest_gtest/syscall_filter_unittest_gtest // ========================================================= cc_test { name: "syscall_filter_unittest_gtest", defaults: ["libminijail_flags"], host_supported: true, srcs: [ "bpf.c", "syscall_filter.c", "syscall_wrapper.c", "util.c", "syscall_filter_unittest.cc", ] + unittestSrcFiles, static_libs: ["libminijail_generated"], shared_libs: minijailCommonLibraries, target: { android: { test_suites: ["device-tests"], }, }, test_options: { unit_test: true, }, data: ["test/*"], } // System functionality unit tests using gtest. // // For a device, run with: // adb shell /data/nativetest/mj_system_unittest_gtest/mj_system_unittest_gtest // // For host, run with: // out/host/linux-x86/nativetest(64)/mj_system_unittest_gtest/mj_system_unittest_gtest // ========================================================= cc_test { name: "mj_system_unittest_gtest", defaults: ["libminijail_flags"], host_supported: true, srcs: [ "syscall_wrapper.c", "system.c", "util.c", "system_unittest.cc", ] + unittestSrcFiles, static_libs: ["libminijail_generated"], shared_libs: minijailCommonLibraries, target: { android: { test_suites: ["device-tests"], }, }, } // Utility functionality unit tests using gtest. // // For a device, run with: // adb shell /data/nativetest/mj_util_unittest_gtest/mj_util_unittest_gtest // // For host, run with: // out/host/linux-x86/nativetest(64)/mj_util_unittest_gtest/mj_util_unittest_gtest // ========================================================= cc_test { name: "mj_util_unittest_gtest", defaults: ["libminijail_flags"], host_supported: true, srcs: [ "util.c", "util_unittest.cc", ] + unittestSrcFiles, static_libs: ["libminijail_generated"], shared_libs: minijailCommonLibraries, target: { android: { test_suites: ["device-tests"], }, }, } // Utility functionality unit tests using gtest. // // For a device, run with: // adb shell /data/nativetest/minijail0_cli_unittest_gtest/minijail0_cli_unittest_gtest // // For host, run with: // out/host/linux-x86/nativetest(64)/minijail0_cli_unittest_gtest/minijail0_cli_unittest_gtest // ========================================================= cc_test { name: "minijail0_cli_unittest_gtest", defaults: ["libminijail_flags"], host_supported: true, cflags: [ "-DPRELOADPATH=\"/invalid\"", ], srcs: libminijailSrcFiles + [ "config_parser.c", "elfparse.c", "minijail0_cli.c", "minijail0_cli_unittest.cc", ] + unittestSrcFiles, static_libs: ["libminijail_generated"], shared_libs: minijailCommonLibraries, target: { android: { test_suites: ["device-tests"], }, }, data: ["test/*"], } // Configuration file parser functionality unit tests using gtest. // // For a device, run with: // adb shell /data/nativetest/config_parser_unittest_gtest/config_parser_unittest_gtest // // For host, run with: // out/host/linux-x86/nativetest(64)/config_parser_unittest_gtest/config_parser_unittest_gtest // ========================================================= cc_test { name: "config_parser_unittest_gtest", defaults: ["libminijail_flags"], host_supported: true, srcs: [ "config_parser.c", "util.c", "config_parser_unittest.cc", ] + unittestSrcFiles, static_libs: ["libminijail_generated"], shared_libs: minijailCommonLibraries, target: { android: { test_suites: ["device-tests"], }, }, test_options: { unit_test: true, }, data: ["test/*"], } // libminijail_test executable for brillo_Minijail test. // ========================================================= cc_test { name: "libminijail_test", defaults: ["libminijail_flags"], test_suites: ["device-tests"], gtest: false, srcs: ["test/libminijail_test.cpp"], shared_libs: [ "libbase", "libminijail", ], } // libminijail usage example. // ========================================================= cc_binary { name: "drop_privs", defaults: ["libminijail_flags"], // Don't build with ASan, but leave commented out for easy local debugging. // sanitize: { address: true, }, srcs: ["examples/drop_privs.cpp"], shared_libs: [ "libbase", "libminijail", ], } // minijail0 executable. // This is not currently used on Brillo/Android, // but it's convenient to be able to build it. // ========================================================= cc_binary { name: "minijail0", defaults: ["libminijail_flags"], host_supported: true, cflags: [ "-DPRELOADPATH=\"/invalidminijailpreload.so\"", ], srcs: [ "config_parser.c", "elfparse.c", "minijail0.c", "minijail0_cli.c", ], static_libs: ["libminijail_generated"], shared_libs: minijailCommonLibraries + ["libminijail"], } rust_defaults { name: "libminijail_rust_defaults", target: { darwin: { enabled: false, }, }, } // This target was generated by cargo2android.py --run --device, with some // manual fixes. rust_library { name: "libminijail_sys", defaults: ["libminijail_rust_defaults"], host_supported: true, crate_name: "minijail_sys", srcs: ["rust/minijail-sys/lib.rs"], edition: "2018", rustlibs: [ "liblibc", ], shared_libs: [ "libcap", "libminijail", ], apex_available: [ "//apex_available:platform", "com.android.compos", "com.android.virt", ], } // This target was generated by cargo2android.py --run --device, with some // manual fixes. rust_library { name: "libminijail_rust", defaults: ["libminijail_rust_defaults"], host_supported: true, crate_name: "minijail", srcs: ["rust/minijail/src/lib.rs"], edition: "2018", rustlibs: [ "liblibc", "libminijail_sys", ], apex_available: [ "//apex_available:platform", "com.android.compos", "com.android.virt", ], } minijail-2026.05.18+git20261016.8787d1a/CPPLINT.cfg000066400000000000000000000000211526451555500201160ustar00rootroot00000000000000exclude_files=.* minijail-2026.05.18+git20261016.8787d1a/CleanSpec.mk000066400000000000000000000043651526451555500205310ustar00rootroot00000000000000# Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # If you don't need to do a full clean build but would like to touch # a file or delete some intermediate files, add a clean step to the end # of the list. These steps will only be run once, if they haven't been # run before. # # E.g.: # $(call add-clean-step, touch -c external/sqlite/sqlite3.h) # $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) # # Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with # files that are missing or have been moved. # # Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. # Use $(OUT_DIR) to refer to the "out" directory. # # If you need to re-do something that's already mentioned, just copy # the command and add it to the bottom of the list. E.g., if a change # that you made last week required touching a file and a change you # made today requires touching the same file, just copy the old # touch step and add it to the end of the list. # # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ # For example: #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -exec rm -f {} \;) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/lib/libminijail.so) minijail-2026.05.18+git20261016.8787d1a/DIR_METADATA000066400000000000000000000022541526451555500201370ustar00rootroot00000000000000# Metadata information for this directory. # # For more information on DIR_METADATA files, see: # https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: # https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/proto/dir_metadata.proto buganizer { component_id: 1099158 # ChromeOS > Security > Minijail } monorail { component: "OS>Systems>Minijail" } team_email: "minijail@chromium.org" chromeos { cq { source_test_plans { test_plan_starlark_files { host: "chrome-internal.googlesource.com" project: "chromeos/config-internal" path: "test/plans/v2/ctpv1_compatible/legacy_default_tast_hw.star" } test_plan_starlark_files { host: "chrome-internal.googlesource.com" project: "chromeos/config-internal" path: "test/plans/v2/ctpv1_compatible/legacy_default_autotest_hw.star" } test_plan_starlark_files { host: "chrome-internal.googlesource.com" project: "chromeos/config-internal" path: "test/plans/v2/ctpv1_compatible/legacy_default_vm.star" } } } } minijail-2026.05.18+git20261016.8787d1a/HACKING.md000066400000000000000000000056421526451555500177300ustar00rootroot00000000000000# Hacking on Minijail ## Dependencies You'll need these to build the source: * [libcap] * Linux kernel headers You'll need to install the relevant packages from your distro. ## Building For local experimentation (using Minijail libraries from the source directory): ``` $ make LIBDIR=/lib64 $ sudo ./minijail0.sh -u ${USER} -g 5000 -- /usr/bin/id ``` For system-wide usage, install `libminijail.so` and `libminijailpreload.so` to `/lib64` and `minijail0` to a directory in your `PATH` (e.g. `/usr/bin`). ## Testing We use [Google Test] (i.e. `gtest` & `gmock`) for unit tests. You can download a suitable copy of Google Test using the [get_googletest.sh](./get_googletest.sh) script. ``` $ ./get_googletest.sh googletest-release-1.8.0/ ... $ make tests ``` Building the tests will automatically execute them. ## Code Review We use [Android Review] for Minijail code review. The easiest way to submit changes for review is using `repo upload` on a ChromiumOS or Android checkout. Go to [Android Review HTTP Credentials] to obtain credentials to push code. For more detailed instructions see the [Android source documentation] or the [ChromiumOS documentation]. ## Source Style * Minijail uses kernel coding style: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding-style.rst * Utility functions with no side-effects should go in `util.{h|c}`. * Functions with side effects or with dependencies on operating system details, but that don't take a `struct minijail` argument, should go in `system.{h|c}`. ## Documentation ### Markdown Minijail uses markdown for general/source documentation. We follow the [Google Markdown style guide]. ### Man Pages For users of Minijail (e.g. `minijail0`), we use man pages. For style guides, check out the [Linux man-pages project] for general guidance. It has a number of useful references for syntax and such. * [man-pages(7)](http://man7.org/linux/man-pages/man7/man-pages.7.html) * [groff-man(7)](http://man7.org/linux/man-pages/man7/groff_man.7.html) * [groff(7)](http://man7.org/linux/man-pages/man7/groff.7.html) [minijail0.1] documents the command line interface. Please keep it in sync with [minijail0_cli.c]. [minijail0.5] documents the syntax of config files (e.g. seccomp filters). [libcap]: https://git.kernel.org/pub/scm/linux/kernel/git/morgan/libcap.git/ [minijail0.1]: ./minijail0.1 [minijail0.5]: ./minijail0.5 [minijail0_cli.c]: ./minijail0_cli.c [Android Review]: https://android-review.googlesource.com/ [Android Review HTTP Credentials]: https://android-review.googlesource.com/settings/#HTTPCredentials [Android source documentation]: https://source.android.com/setup/start [ChromiumOS documentation]: https://chromium.googlesource.com/chromiumos/docs/+/HEAD/developer_guide.md [Google Markdown style guide]: https://github.com/google/styleguide/blob/gh-pages/docguide/style.md [Google Test]: https://github.com/google/googletest minijail-2026.05.18+git20261016.8787d1a/LICENSE000066400000000000000000000030111526451555500173330ustar00rootroot00000000000000/* Copyright 2012 The ChromiumOS Authors * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ minijail-2026.05.18+git20261016.8787d1a/METADATA000066400000000000000000000000471526451555500174370ustar00rootroot00000000000000third_party { license_type: NOTICE } minijail-2026.05.18+git20261016.8787d1a/MODULE_LICENSE_BSD000066400000000000000000000000001526451555500210630ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/Makefile000066400000000000000000000256321526451555500200030ustar00rootroot00000000000000# Copyright 2012 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. include common.mk LIBDIR ?= /lib PRELOADNAME = libminijailpreload.so PRELOADPATH = "$(LIBDIR)/$(PRELOADNAME)" CPPFLAGS += -DPRELOADPATH='$(PRELOADPATH)' # Paths to newuidmap/newgidmap helpers for unprivileged ID mapping fallback. NEWUIDMAP_PATH ?= /usr/bin/newuidmap NEWGIDMAP_PATH ?= /usr/bin/newgidmap CPPFLAGS += -DNEWUIDMAP_PATH='"$(NEWUIDMAP_PATH)"' CPPFLAGS += -DNEWGIDMAP_PATH='"$(NEWGIDMAP_PATH)"' # We don't build static libs by default. BUILD_STATIC_LIBS ?= no # Defines the pivot root path used by the minimalistic-mountns profile. DEFAULT_PIVOT_ROOT ?= /var/empty CPPFLAGS += -DDEFAULT_PIVOT_ROOT='"$(DEFAULT_PIVOT_ROOT)"' # These are configurable strictness settings. Not every use case for Minijail # has the same requirements. # Allow seccomp to fail without a warning. You probably don't want this. ifeq ($(USE_seccomp),no) CPPFLAGS += -DUSE_SECCOMP_SOFTFAIL endif # Prevent Minijail configuration files from residing in a noexec # filesystem. # # The rationale here is that a configuration file that controls how a program # executes should be subject to the same restrictions as the executable it # controls. In essence, a configuration file should be considered to have as # much power as an executable. Files can only be executed from filesystems *not* # mounted as noexec, so configuration files should not reside in noexec # filesystems. # # For example, on ChromeOS executable filesystems are mounted read-only. Noexec # filesystems are allowed to be mounted read-write. If a configuration file # were allowed to reside in a noexec filesystem, an attacker would be able to # influence how a program is executed by modifying the configuration file. BLOCK_NOEXEC_CONF ?= no ifeq ($(BLOCK_NOEXEC_CONF),yes) CPPFLAGS += -DBLOCK_NOEXEC_CONF endif # Prevent Minijail configuration files from residing in a partition different # from the partition mounted at /. This is primarily used in ChromeOS. ENFORCE_ROOTFS_CONF ?= no ifeq ($(ENFORCE_ROOTFS_CONF),yes) CPPFLAGS += -DENFORCE_ROOTFS_CONF endif # Allow people to use -L and related flags. ALLOW_DEBUG_LOGGING ?= yes ifeq ($(ALLOW_DEBUG_LOGGING),yes) CPPFLAGS += -DALLOW_DEBUG_LOGGING ifeq ($(SECCOMP_DEFAULT_RET_LOG),yes) CPPFLAGS += -DSECCOMP_DEFAULT_RET_LOG endif endif # Prevent Minijail from following symlinks when performing bind mounts. # BINDMOUNT_ALLOWED_PREFIXES allows some flexibility. This is especially useful # for directories that are not normally modifiable by non-root users. # If a process can modify these directories, they probably don't need to mess # with Minijail bind mounts to gain root privileges. BINDMOUNT_ALLOWED_PREFIXES ?= /dev,/sys CPPFLAGS += -DBINDMOUNT_ALLOWED_PREFIXES='"$(BINDMOUNT_ALLOWED_PREFIXES)"' BLOCK_SYMLINKS_IN_BINDMOUNT_PATHS ?= no ifeq ($(BLOCK_SYMLINKS_IN_BINDMOUNT_PATHS),yes) CPPFLAGS += -DBLOCK_SYMLINKS_IN_BINDMOUNT_PATHS endif # Prevents symlinks from being followed in the /tmp folder. # Symlinks could be followed to modify arbitrary files when a process # had access to the /tmp folder. BLOCK_SYMLINKS_IN_NONINIT_MOUNTNS_TMP ?= no ifeq ($(BLOCK_SYMLINKS_IN_NONINIT_MOUNTNS_TMP),yes) CPPFLAGS += -DBLOCK_SYMLINKS_IN_NONINIT_MOUNTNS_TMP endif # If specified, the `libc_compatibility_syscalls` list will be silently added to # all allowlists. Intended for use when upgrading core libraries (mainly libc), # which can lead to new syscalls being introduced in many places. USE_LIBC_COMPATIBILITY_ALLOWLIST ?= no ifeq ($(USE_LIBC_COMPATIBILITY_ALLOWLIST),yes) CPPFLAGS += -DALLOW_LIBC_COMPATIBILITY_SYSCALLS endif ifeq ($(USE_ASAN),yes) CPPFLAGS += -fsanitize=address -fno-omit-frame-pointer LDFLAGS += -fsanitize=address -fno-omit-frame-pointer USE_EXIT_ON_DIE = yes endif # Setting this flag can be useful for both AddressSanitizer builds and running # fuzzing tools, which do not expect crashes on gracefully-handled malformed # inputs. ifeq ($(USE_EXIT_ON_DIE),yes) CPPFLAGS += -DUSE_EXIT_ON_DIE endif # Setting this flag allows duplicate syscalls definitions for seccomp filters. ifeq ($(ALLOW_DUPLICATE_SYSCALLS),yes) CPPFLAGS += -DALLOW_DUPLICATE_SYSCALLS endif MJ_COMMON_FLAGS = -Wunused-parameter -Wextra -Wno-missing-field-initializers CFLAGS += $(MJ_COMMON_FLAGS) CXXFLAGS += $(MJ_COMMON_FLAGS) # Dependencies that all gtest based unittests should have. UNITTEST_LIBS := -lcap UNITTEST_DEPS := testrunner.o test_util.o USE_SYSTEM_GTEST ?= no ifeq ($(USE_SYSTEM_GTEST),no) GTEST_CXXFLAGS := -std=gnu++20 GTEST_LIBS := gtest.a UNITTEST_DEPS += $(GTEST_LIBS) else GTEST_CXXFLAGS := $(shell gtest-config --cxxflags 2>/dev/null || \ echo "-pthread") GTEST_LIBS := $(shell gtest-config --libs 2>/dev/null || \ echo "-lgtest -pthread -lpthread") endif GTEST_CXXFLAGS += -DGTEST_REMOVE_LEGACY_TEST_CASEAPI_ UNITTEST_LIBS += $(GTEST_LIBS) CORE_OBJECT_FILES := libminijail.o syscall_filter.o signal_handler.o \ bpf.o landlock_util.o util.o system.o syscall_wrapper.o \ config_parser.o libconstants.gen.o libsyscalls.gen.o UNITTEST_DEPS += $(CORE_OBJECT_FILES) all: CC_BINARY(minijail0) CC_LIBRARY(libminijail.so) \ CC_LIBRARY(libminijailpreload.so) parse_seccomp_policy: CXX_BINARY(parse_seccomp_policy) dump_constants: CXX_STATIC_BINARY(dump_constants) tests: TEST(CXX_BINARY(libminijail_unittest)) \ TEST(CXX_BINARY(minijail0_cli_unittest)) \ TEST(CXX_BINARY(syscall_filter_unittest)) \ TEST(CXX_BINARY(system_unittest)) \ TEST(CXX_BINARY(util_unittest)) \ TEST(CXX_BINARY(config_parser_unittest)) CC_BINARY(minijail0): LDLIBS += -lcap -ldl CC_BINARY(minijail0): $(CORE_OBJECT_FILES) \ elfparse.o minijail0.o minijail0_cli.o clean: CLEAN(minijail0) CC_LIBRARY(libminijail.so): LDLIBS += -lcap CC_LIBRARY(libminijail.so): $(CORE_OBJECT_FILES) clean: CLEAN(libminijail.so) CC_STATIC_LIBRARY(libminijail.pic.a): $(CORE_OBJECT_FILES) CC_STATIC_LIBRARY(libminijail.pie.a): $(CORE_OBJECT_FILES) clean: CLEAN(libminijail.*.a) ifeq ($(BUILD_STATIC_LIBS),yes) all: CC_STATIC_LIBRARY(libminijail.pic.a) CC_STATIC_LIBRARY(libminijail.pie.a) endif CXX_BINARY(libminijail_unittest): CXXFLAGS += -Wno-write-strings \ $(GTEST_CXXFLAGS) CXX_BINARY(libminijail_unittest): LDLIBS += $(UNITTEST_LIBS) CXX_BINARY(libminijail_unittest): $(UNITTEST_DEPS) libminijail_unittest.o clean: CLEAN(libminijail_unittest) TEST(CXX_BINARY(libminijail_unittest)): CC_LIBRARY(libminijailpreload.so) CC_LIBRARY(libminijailpreload.so): LDLIBS += -lcap -ldl CC_LIBRARY(libminijailpreload.so): libminijailpreload.o $(CORE_OBJECT_FILES) clean: CLEAN(libminijailpreload.so) CXX_BINARY(minijail0_cli_unittest): CXXFLAGS += $(GTEST_CXXFLAGS) CXX_BINARY(minijail0_cli_unittest): LDLIBS += $(UNITTEST_LIBS) CXX_BINARY(minijail0_cli_unittest): $(UNITTEST_DEPS) minijail0_cli_unittest.o \ minijail0_cli.o elfparse.o clean: CLEAN(minijail0_cli_unittest) CXX_BINARY(config_parser_unittest): CXXFLAGS += $(GTEST_CXXFLAGS) CXX_BINARY(config_parser_unittest): LDLIBS += $(UNITTEST_LIBS) CXX_BINARY(config_parser_unittest): $(UNITTEST_DEPS) config_parser_unittest.o clean: CLEAN(config_parser_unittest) CXX_BINARY(syscall_filter_unittest): CXXFLAGS += -Wno-write-strings \ $(GTEST_CXXFLAGS) CXX_BINARY(syscall_filter_unittest): LDLIBS += $(UNITTEST_LIBS) CXX_BINARY(syscall_filter_unittest): $(UNITTEST_DEPS) syscall_filter_unittest.o clean: CLEAN(syscall_filter_unittest) CXX_BINARY(system_unittest): CXXFLAGS += $(GTEST_CXXFLAGS) CXX_BINARY(system_unittest): LDLIBS += $(UNITTEST_LIBS) CXX_BINARY(system_unittest): $(UNITTEST_DEPS) system_unittest.o clean: CLEAN(system_unittest) CXX_BINARY(util_unittest): CXXFLAGS += $(GTEST_CXXFLAGS) CXX_BINARY(util_unittest): LDLIBS += $(UNITTEST_LIBS) CXX_BINARY(util_unittest): $(UNITTEST_DEPS) util_unittest.o clean: CLEAN(util_unittest) CXX_BINARY(parse_seccomp_policy): parse_seccomp_policy.o syscall_filter.o \ bpf.o landlock_util.o util.o libconstants.gen.o libsyscalls.gen.o clean: CLEAN(parse_seccomp_policy) # Compiling dump_constants as a static executable makes it easy to run under # qemu-user, which in turn simplifies cross-compiling bpf policies. CXX_STATIC_BINARY(dump_constants): dump_constants.o \ libconstants.gen.o libsyscalls.gen.o clean: CLEAN(dump_constants) constants.json: CXX_STATIC_BINARY(dump_constants) ./dump_constants > $@ clean: CLEANFILE(constants.json) libsyscalls.gen.o: CPPFLAGS += -I$(SRC) libsyscalls.gen.o.depends: libsyscalls.gen.c # Only regenerate libsyscalls.gen.c if the Makefile or header changes. # NOTE! This will not detect if the file is not appropriate for the target. libsyscalls.gen.c: $(SRC)/libsyscalls.h $(SRC)/Makefile @/bin/echo -e "GEN $(subst $(SRC)/,,$<) -> $@" $(QUIET)CC="$(CC)" $(SRC)/gen_syscalls.sh "$@" clean: CLEAN(libsyscalls.gen.c) $(eval $(call add_object_rules,libsyscalls.gen.o,CC,c,CFLAGS)) libconstants.gen.o: CPPFLAGS += -I$(SRC) libconstants.gen.o.depends: libconstants.gen.c # Only regenerate libconstants.gen.c if the Makefile or header changes. # NOTE! This will not detect if the file is not appropriate for the target. libconstants.gen.c: $(SRC)/libconstants.h $(SRC)/Makefile @/bin/echo -e "GEN $(subst $(SRC)/,,$<) -> $@" $(QUIET)CC="$(CC)" $(SRC)/gen_constants.sh "$@" clean: CLEAN(libconstants.gen.c) $(eval $(call add_object_rules,libconstants.gen.o,CC,c,CFLAGS)) ################################################################################ # Google Test ifeq ($(USE_SYSTEM_GTEST),no) # Points to the root of Google Test, relative to where this file is. # Remember to tweak this if you move this file. GTEST_DIR = googletest-1.16.0/googletest # Flags passed to the preprocessor. # Set Google Test's header directory as a system directory, such that # the compiler doesn't generate warnings in Google Test headers. CPPFLAGS += -isystem $(GTEST_DIR)/include # Flags passed to the C++ compiler. GTEST_CXXFLAGS += -pthread # All Google Test headers. Usually you shouldn't change this # definition. GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \ $(GTEST_DIR)/include/gtest/internal/*.h # House-keeping build targets. clean: clean_gtest clean_gtest: $(QUIET)rm -f gtest.a gtest_main.a *.o # Builds gtest.a and gtest_main.a. # Usually you shouldn't tweak such internal variables, indicated by a # trailing _. GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS) # For simplicity and to avoid depending on Google Test's # implementation details, the dependencies specified below are # conservative and not optimized. This is fine as Google Test # compiles fast and for ordinary users its source rarely changes. gtest-all.o : $(GTEST_SRCS_) $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) $(GTEST_CXXFLAGS) -c \ $(GTEST_DIR)/src/gtest-all.cc -o $@ gtest_main.o : $(GTEST_SRCS_) $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) $(GTEST_CXXFLAGS) -c \ $(GTEST_DIR)/src/gtest_main.cc -o $@ gtest.a : gtest-all.o $(AR) $(ARFLAGS) $@ $^ gtest_main.a : gtest-all.o gtest_main.o $(AR) $(ARFLAGS) $@ $^ endif ################################################################################ minijail-2026.05.18+git20261016.8787d1a/NOTICE000066400000000000000000000030021526451555500172320ustar00rootroot00000000000000// Copyright 2014 The ChromiumOS Authors // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. minijail-2026.05.18+git20261016.8787d1a/OWNERS000066400000000000000000000003501526451555500172710ustar00rootroot00000000000000set noparent include OWNERS_GENERAL # Robots. per-file common.mk=copybara-worker-blackhole@google.com #{LAST_RESORT_SUGGESTION} # Emeritus. drewry@google.com #{LAST_RESORT_SUGGESTION} keescook@google.com #{LAST_RESORT_SUGGESTION} minijail-2026.05.18+git20261016.8787d1a/OWNERS_GENERAL000066400000000000000000000001131526451555500204230ustar00rootroot00000000000000akhna@google.com allenwebb@google.com jorgelo@google.com vapier@google.com minijail-2026.05.18+git20261016.8787d1a/PRESUBMIT.cfg000066400000000000000000000015101526451555500203630ustar00rootroot00000000000000[Hook Overrides] # We are using Linux-style indentation with tabs tab_check: false # This is an AOSP project, but we still use a BSD license. cros_license_check: true aosp_license_check: false [Hook Overrides Options] cros_license_check: --exclude_regex=^test/ [Hook Scripts] cros format = cros format --check --include '*.py' --exclude 'linux-x86/*.gen.c' --exclude 'test_util.h' --exclude 'unittest_util.h' --include '*.[ch]' --include '*.policy' --include '*.conf' --include .gitignore --exclude '*' --commit ${PRESUBMIT_COMMIT} ${PRESUBMIT_FILES} cros lint = cros lint --include '*.py' --exclude '*.[ch]' --exclude '*.cpp' --exclude '*.cc' --exclude 'test/*.policy' --exclude 'policies/cros_default_v0.policy' --exclude 'examples/*.policy' --exclude 'rust/minijail/src/test_*.policy' --commit ${PRESUBMIT_COMMIT} ${PRESUBMIT_FILES} minijail-2026.05.18+git20261016.8787d1a/PREUPLOAD.cfg000066400000000000000000000002051526451555500203440ustar00rootroot00000000000000[Builtin Hooks] clang_format = true [Builtin Hooks Options] clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c minijail-2026.05.18+git20261016.8787d1a/README.md000066400000000000000000000124141526451555500176140ustar00rootroot00000000000000# Minijail The Minijail homepage is https://google.github.io/minijail/. The main source repo is https://chromium.googlesource.com/chromiumos/platform/minijail. There might be other copies floating around, but this is the official one! [TOC] ## What is it? Minijail is a sandboxing and containment tool used in ChromeOS and Android. It provides an executable that can be used to launch and sandbox other programs, and a library that can be used by code to sandbox itself. ## Purpose and threat model Minijail is intended for sandboxing known binaries on a system. It is intended to mitigate the risk if a service is compromised through a bug, or a confused-deputy scenario. It is not designed for safely running malicious code, including attacker-controlled binaries or binaries that may use attacker-controlled shared object libraries. ## Getting the code You're one `git clone` away from happiness. ``` $ git clone https://chromium.googlesource.com/chromiumos/platform/minijail $ cd minijail ``` Releases are tagged as `linux-vXX`: https://chromium.googlesource.com/chromiumos/platform/minijail/+refs ## Building See the [HACKING.md](./HACKING.md) document for more details. ## Release process See the [RELEASE.md](./RELEASE.md) document for more details. ## Additional tools See the [tools/README.md](./tools/README.md) document for more details. ## Contact We've got a couple of contact points. * [minijail@chromium.org]: Public user & developer mailing list. * [minijail-users@google.com]: Internal Google user mailing list. * [minijail-dev@google.com]: Internal Google developer mailing list. * [crbug.com/list]: Existing bug reports & feature requests. * [crbug.com/new]: File new bug reports & feature requests. * [Chromium Gerrit]: Code reviews. [minijail@chromium.org]: https://groups.google.com/a/chromium.org/forum/#!forum/minijail [minijail-users@google.com]: https://groups.google.com/a/google.com/forum/#!forum/minijail-users [minijail-dev@google.com]: https://groups.google.com/a/google.com/forum/#!forum/minijail-dev [crbug.com/list]: https://crbug.com/?q=component:OS>Systems>Minijail [crbug.com/new]: https://bugs.chromium.org/p/chromium/issues/entry?components=OS>Systems>Minijail [Chromium Gerrit]: https://chromium-review.googlesource.com/q/project:chromiumos/platform/minijail ## GitHub Pages homepage The https://google.github.io/minijail/ homepage is maintained in the `gh-pages` branch, not in the `main` branch. Changes to it can be sent via Gerrit, but requires a little extra work. ```shell # Make sure you have all the branches, and not only the "main" one. $ git fetch # Create a new local branch tracking the remote "gh-pages". # Git should automatically detect the remote and track it for you. $ git checkout gh-pages # If git can't auto-detect the remote, try one of these. $ git checkout -b gh-pages origin/gh-pages $ git checkout -b gh-pages cros/gh-pages # Make your changes like normal, then push them to Gerrit for review. # Here's a couple of different ways to post changes; you only need one! $ repo upload -D gh-pages $ git push origin HEAD:refs/for/gh-pages $ git push cros HEAD:refs/for/gh-pages # Now review your changes via Gerrit like normal. ``` Once a change merges into the `gh-pages` branch, there is no additional work for you other than waiting -- GitHub periodically syncs with our host, and then it will automatically regenerate the homepage when the `gh-pages` branch updates. ## Talks and presentations The following talk serves as a good introduction to Minijail and how it can be used: [slides](https://docs.google.com/presentation/d/e/2PACX-1vRBqpin5xR9sng6lIBPjG0XQtu-uWWgr0ds-M3zW13XpDO-bTcMERLwoHUEB9078p1yqr9L-su9n5dk/pub). ## Example usage The ChromiumOS project has a comprehensive [sandboxing](https://www.chromium.org/chromium-os/developer-library/guides/development/sandboxing) document that is largely based on Minijail. After you play with the simple examples below, you should check that out. ### Change root to any user ``` # id uid=0(root) gid=0(root) groups=0(root),128(pkcs11) # minijail0 -u jorgelo -g 5000 /usr/bin/id uid=72178(jorgelo) gid=5000(eng) groups=5000(eng) ``` ### Drop root while keeping some capabilities ``` # minijail0 -u jorgelo -c 3000 -- /bin/cat /proc/self/status Name: cat ... CapInh: 0000000000003000 CapPrm: 0000000000003000 CapEff: 0000000000003000 CapBnd: 0000000000003000 ``` ## Historical notes Q. "Why is it called minijail0?" A. It is minijail0 because it was a rewrite of an earlier program named minijail, which was considerably less mini, and in particular had a dependency on libchrome (the ChromeOS packaged version of Chromium's //base). We needed a new name to not collide with the deprecated one. We didn't want to call it minijail2 or something that would make people start using it before we were ready, and it was also concretely _less_ since it dropped libbase, etc. Technically, we needed to be able to fork/preload with minimal extra syscall noise which was too hard with libbase at the time (onexit handlers, etc that called syscalls we didn't want to allow). Also, Elly made a strong case that C would be the right choice for this for linking and ease of controlled surprise system call use. https://crrev.com/c/4585/ added the original implementation. Source: Conversations with original authors, ellyjones@ and wad@. minijail-2026.05.18+git20261016.8787d1a/RELEASE.md000066400000000000000000000010221526451555500177300ustar00rootroot00000000000000# Tagging Minijail releases * Choose a new-ish, stable-ish commit (i.e. not one that implements a completely new feature). * Find the latest tag: `minijail$ git tag -l "linux-v*"` * Tag the commit with the next version number: `minijail$ git tag -a linux-v ` * Commit message: ``` Minijail v. New in this release: * New feature (author@domain). * New feature 2 (author2@domain2). ``` * Push the tag: `minijail$ git push aosp linux-v` We will tag a new release ~monthly at the beginning of the month. minijail-2026.05.18+git20261016.8787d1a/TEST_MAPPING000066400000000000000000000007571526451555500202210ustar00rootroot00000000000000{ "presubmit": [ { "name": "libminijail_unittest_gtest" }, { "name": "mj_system_unittest_gtest" }, { "name": "mj_util_unittest_gtest" }, { "name": "syscall_filter_unittest_gtest" } ], "hwasan-postsubmit": [ { "name": "libminijail_unittest_gtest" }, { "name": "mj_system_unittest_gtest" }, { "name": "mj_util_unittest_gtest" }, { "name": "syscall_filter_unittest_gtest" } ] } minijail-2026.05.18+git20261016.8787d1a/arch.h000066400000000000000000000054311526451555500174240ustar00rootroot00000000000000/* arch.h * Copyright 2014 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * MINIJAIL_ARCH_NR #define's. */ #ifndef ARCH_H #define ARCH_H #include #include /* clang-format off */ #if defined(__i386__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_I386 # define MINIJAIL_ARCH_NAME "x86" #elif defined(__x86_64__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_X86_64 # define MINIJAIL_ARCH_NAME "x86_64" #elif defined(__arm__) /* * includes , which does not define EM_ARM. * only includes if we're in the kernel. */ # ifndef EM_ARM # define EM_ARM 40 # endif # define MINIJAIL_ARCH_NR AUDIT_ARCH_ARM # define MINIJAIL_ARCH_NAME "arm" #elif defined(__aarch64__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_AARCH64 # define MINIJAIL_ARCH_NAME "arm64" #elif defined(__hppa__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_PARISC # define MINIJAIL_ARCH_NAME "parisc" #elif defined(__ia64__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_IA64 # define MINIJAIL_ARCH_NAME "ia64" #elif defined(__loongarch64) # define MINIJAIL_ARCH_NR AUDIT_ARCH_LOONGARCH64 # define MINIJAIL_ARCH_NAME "loong64" #elif defined(__mips__) # if defined(__mips64) # if defined(__MIPSEB__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_MIPS64 # define MINIJAIL_ARCH_NAME "mips64" # else # define MINIJAIL_ARCH_NR AUDIT_ARCH_MIPSEL64 # define MINIJAIL_ARCH_NAME "mipsel64" # endif # else # if defined(__MIPSEB__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_MIPS # define MINIJAIL_ARCH_NAME "mips" # else # define MINIJAIL_ARCH_NR AUDIT_ARCH_MIPSEL # define MINIJAIL_ARCH_NAME "mipsel" # endif # endif #elif defined(__powerpc64__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_PPC64 # define MINIJAIL_ARCH_NAME "ppc64" #elif defined(__powerpc__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_PPC # define MINIJAIL_ARCH_NAME "ppc" #elif defined(__riscv) # if defined(__riscv_xlen) # if (__riscv_xlen == 64) # define MINIJAIL_ARCH_NR AUDIT_ARCH_RISCV64 # define MINIJAIL_ARCH_NAME "riscv64" # else # error "Only 64bit riscv is supported" # endif # else # error "AUDIT_ARCH value unavailable" # endif #elif defined(__s390x__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_S390X # define MINIJAIL_ARCH_NAME "s390x" #elif defined(__s390__) # define MINIJAIL_ARCH_NR AUDIT_ARCH_S390 # define MINIJAIL_ARCH_NAME "s390" #elif defined(__sparc__) # if defined(__arch64__) # define AUDIT_ARCH_SPARC64 # define MINIJAIL_ARCH_NAME "sparc64" # else # define AUDIT_ARCH_SPARC # define MINIJAIL_ARCH_NAME "sparc" # endif #else # error "AUDIT_ARCH value unavailable" #endif /* clang-format on */ #define MINIJAIL_ARCH_BITS sizeof(uintptr_t) * 8 #endif /* ARCH_H */ minijail-2026.05.18+git20261016.8787d1a/bpf.c000066400000000000000000000243601526451555500172530ustar00rootroot00000000000000/* Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include "bpf.h" #include "util.h" /* Architecture validation. */ size_t bpf_validate_arch(struct sock_filter *filter) { struct sock_filter *curr_block = filter; set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, arch_nr); set_bpf_jump(curr_block++, BPF_JMP + BPF_JEQ + BPF_K, MINIJAIL_ARCH_NR, SKIP, NEXT); set_bpf_ret_kill(curr_block++); return curr_block - filter; } /* Syscall number eval functions. */ size_t bpf_allow_syscall(struct sock_filter *filter, int nr) { struct sock_filter *curr_block = filter; set_bpf_jump(curr_block++, BPF_JMP + BPF_JEQ + BPF_K, nr, NEXT, SKIP); set_bpf_stmt(curr_block++, BPF_RET + BPF_K, SECCOMP_RET_ALLOW); return curr_block - filter; } size_t bpf_allow_syscall_args(struct sock_filter *filter, int nr, unsigned int id) { struct sock_filter *curr_block = filter; set_bpf_jump(curr_block++, BPF_JMP + BPF_JEQ + BPF_K, nr, NEXT, SKIP); set_bpf_jump_lbl(curr_block++, id); return curr_block - filter; } /* Size-aware arg loaders. */ #if defined(BITS32) size_t bpf_load_arg(struct sock_filter *filter, int argidx) { set_bpf_stmt(filter, BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx)); return 1U; } #elif defined(BITS64) size_t bpf_load_arg(struct sock_filter *filter, int argidx) { struct sock_filter *curr_block = filter; set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx)); set_bpf_stmt(curr_block++, BPF_ST, 0); /* lo -> M[0] */ set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, HI_ARG(argidx)); set_bpf_stmt(curr_block++, BPF_ST, 1); /* hi -> M[1] */ return curr_block - filter; } #endif /* Size-aware comparisons. */ size_t bpf_comp_jeq32(struct sock_filter *filter, unsigned long c, unsigned char jt, unsigned char jf) { unsigned int lo = (unsigned int)(c & 0xFFFFFFFF); set_bpf_jump(filter, BPF_JMP + BPF_JEQ + BPF_K, lo, jt, jf); return 1U; } /* * On 64 bits, we have to do two 32-bit comparisons. * We jump true when *both* comparisons are true. */ #if defined(BITS64) size_t bpf_comp_jeq64(struct sock_filter *filter, uint64_t c, unsigned char jt, unsigned char jf) { unsigned int lo = (unsigned int)(c & 0xFFFFFFFF); unsigned int hi = (unsigned int)(c >> 32); struct sock_filter *curr_block = filter; /* bpf_load_arg leaves |hi| in A */ curr_block += bpf_comp_jeq32(curr_block, hi, NEXT, SKIPN(2) + jf); set_bpf_stmt(curr_block++, BPF_LD + BPF_MEM, 0); /* swap in |lo| */ curr_block += bpf_comp_jeq32(curr_block, lo, jt, jf); return curr_block - filter; } #endif size_t bpf_comp_jgt32(struct sock_filter *filter, unsigned long c, unsigned char jt, unsigned char jf) { unsigned int lo = (unsigned int)(c & 0xFFFFFFFF); set_bpf_jump(filter, BPF_JMP + BPF_JGT + BPF_K, lo, jt, jf); return 1U; } size_t bpf_comp_jge32(struct sock_filter *filter, unsigned long c, unsigned char jt, unsigned char jf) { unsigned int lo = (unsigned int)(c & 0xFFFFFFFF); set_bpf_jump(filter, BPF_JMP + BPF_JGE + BPF_K, lo, jt, jf); return 1U; } /* * On 64 bits, we have to do two/three 32-bit comparisons. * We jump true when the |hi| comparison is true *or* |hi| is equal and the * |lo| comparison is true. */ #if defined(BITS64) size_t bpf_comp_jgt64(struct sock_filter *filter, uint64_t c, unsigned char jt, unsigned char jf) { unsigned int lo = (unsigned int)(c & 0xFFFFFFFF); unsigned int hi = (unsigned int)(c >> 32); struct sock_filter *curr_block = filter; /* bpf_load_arg leaves |hi| in A. */ if (hi == 0) { curr_block += bpf_comp_jgt32(curr_block, hi, SKIPN(2) + jt, NEXT); } else { curr_block += bpf_comp_jgt32(curr_block, hi, SKIPN(3) + jt, NEXT); curr_block += bpf_comp_jeq32(curr_block, hi, NEXT, SKIPN(2) + jf); } set_bpf_stmt(curr_block++, BPF_LD + BPF_MEM, 0); /* swap in |lo| */ curr_block += bpf_comp_jgt32(curr_block, lo, jt, jf); return curr_block - filter; } size_t bpf_comp_jge64(struct sock_filter *filter, uint64_t c, unsigned char jt, unsigned char jf) { unsigned int lo = (unsigned int)(c & 0xFFFFFFFF); unsigned int hi = (unsigned int)(c >> 32); struct sock_filter *curr_block = filter; /* bpf_load_arg leaves |hi| in A. */ if (hi == 0) { curr_block += bpf_comp_jgt32(curr_block, hi, SKIPN(2) + jt, NEXT); } else { curr_block += bpf_comp_jgt32(curr_block, hi, SKIPN(3) + jt, NEXT); curr_block += bpf_comp_jeq32(curr_block, hi, NEXT, SKIPN(2) + jf); } set_bpf_stmt(curr_block++, BPF_LD + BPF_MEM, 0); /* swap in |lo| */ curr_block += bpf_comp_jge32(curr_block, lo, jt, jf); return curr_block - filter; } #endif /* Size-aware bitwise AND. */ size_t bpf_comp_jset32(struct sock_filter *filter, unsigned long mask, unsigned char jt, unsigned char jf) { unsigned int mask_lo = (unsigned int)(mask & 0xFFFFFFFF); set_bpf_jump(filter, BPF_JMP + BPF_JSET + BPF_K, mask_lo, jt, jf); return 1U; } /* * On 64 bits, we have to do two 32-bit bitwise ANDs. * We jump true when *either* bitwise AND is true (non-zero). */ #if defined(BITS64) size_t bpf_comp_jset64(struct sock_filter *filter, uint64_t mask, unsigned char jt, unsigned char jf) { unsigned int mask_lo = (unsigned int)(mask & 0xFFFFFFFF); unsigned int mask_hi = (unsigned int)(mask >> 32); struct sock_filter *curr_block = filter; /* bpf_load_arg leaves |hi| in A */ curr_block += bpf_comp_jset32(curr_block, mask_hi, SKIPN(2) + jt, NEXT); set_bpf_stmt(curr_block++, BPF_LD + BPF_MEM, 0); /* swap in |lo| */ curr_block += bpf_comp_jset32(curr_block, mask_lo, jt, jf); return curr_block - filter; } #endif size_t bpf_comp_jin(struct sock_filter *filter, unsigned long mask, unsigned char jt, unsigned char jf) { unsigned long negative_mask = ~mask; /* * The mask is negated, so the comparison will be true when the argument * includes a flag that wasn't listed in the original (non-negated) * mask. This would be the failure case, so we switch |jt| and |jf|. */ return bpf_comp_jset(filter, negative_mask, jf, jt); } static size_t bpf_arg_comp_len(int op, unsigned long c attribute_unused) { /* The comparisons that use gt/ge internally may have extra opcodes. */ switch (op) { case LT: case LE: case GT: case GE: #if defined(BITS64) /* * |c| can only have a high 32-bit part when running on 64 bits. */ if ((c >> 32) == 0) return BPF_ARG_SHORT_GT_GE_COMP_LEN + 1; #endif return BPF_ARG_GT_GE_COMP_LEN + 1; default: return BPF_ARG_COMP_LEN + 1; } } size_t bpf_arg_comp(struct sock_filter **pfilter, int op, int argidx, unsigned long c, unsigned int label_id) { size_t filter_len = bpf_arg_comp_len(op, c); struct sock_filter *filter = calloc(filter_len, sizeof(struct sock_filter)); struct sock_filter *curr_block = filter; size_t (*comp_function)(struct sock_filter *filter, unsigned long k, unsigned char jt, unsigned char jf); int flip = 0; if (!filter) { *pfilter = NULL; return 0; } /* Load arg */ curr_block += bpf_load_arg(curr_block, argidx); /* Jump type */ switch (op) { case EQ: comp_function = bpf_comp_jeq; flip = 0; break; case NE: comp_function = bpf_comp_jeq; flip = 1; break; case LT: comp_function = bpf_comp_jge; flip = 1; break; case LE: comp_function = bpf_comp_jgt; flip = 1; break; case GT: comp_function = bpf_comp_jgt; flip = 0; break; case GE: comp_function = bpf_comp_jge; flip = 0; break; case SET: comp_function = bpf_comp_jset; flip = 0; break; case IN: comp_function = bpf_comp_jin; flip = 0; break; default: curr_block = filter; free(filter); *pfilter = NULL; return 0; } /* * It's easier for the rest of the code to have the true branch * skip and the false branch fall through. */ unsigned char jt = flip ? NEXT : SKIP; unsigned char jf = flip ? SKIP : NEXT; curr_block += comp_function(curr_block, c, jt, jf); curr_block += set_bpf_jump_lbl(curr_block, label_id); *pfilter = filter; return curr_block - filter; } int bpf_resolve_jumps(struct bpf_labels *labels, struct sock_filter *filter, size_t len) { struct sock_filter *instr; size_t i, offset; if (len > BPF_MAXINSNS) return -1; /* * Walk it once, backwards, to build the label table and do fixups. * Since backward jumps are disallowed by BPF, this is easy. */ for (i = 0; i < len; i++) { offset = len - i - 1; instr = &filter[offset]; if (instr->code != (BPF_JMP + BPF_JA)) continue; switch ((instr->jt << 8) | instr->jf) { case (JUMP_JT << 8) | JUMP_JF: if (instr->k >= labels->count) { warn("nonexistent label id: %u", instr->k); return -1; } if (labels->labels[instr->k].location == 0xffffffff) { warn("unresolved label: '%s'", labels->labels[instr->k].label); return -1; } instr->k = labels->labels[instr->k].location - (offset + 1); instr->jt = 0; instr->jf = 0; continue; case (LABEL_JT << 8) | LABEL_JF: if (labels->labels[instr->k].location != 0xffffffff) { warn("duplicate label: '%s'", labels->labels[instr->k].label); return -1; } labels->labels[instr->k].location = offset; instr->k = 0; /* Fall through. */ instr->jt = 0; instr->jf = 0; continue; } } return 0; } /* Simple lookup table for labels. */ int bpf_label_id(struct bpf_labels *labels, const char *label) { struct __bpf_label *begin = labels->labels, *end; int id; if (labels->count == 0) { begin->label = strndup(label, MAX_BPF_LABEL_LEN); if (!begin->label) { return -1; } begin->location = 0xffffffff; labels->count++; return 0; } end = begin + labels->count; for (id = 0; begin < end; ++begin, ++id) { if (streq(label, begin->label)) { return id; } } /* The label wasn't found. Insert it only if there's space. */ if (labels->count == BPF_LABELS_MAX) { return -1; } begin->label = strndup(label, MAX_BPF_LABEL_LEN); if (!begin->label) { return -1; } begin->location = 0xffffffff; labels->count++; return id; } void free_label_strings(struct bpf_labels *labels) { if (labels->count == 0) return; struct __bpf_label *begin = labels->labels, *end; end = begin + labels->count; for (; begin < end; ++begin) { if (begin->label) free((void *)(begin->label)); } labels->count = 0; } minijail-2026.05.18+git20261016.8787d1a/bpf.h000066400000000000000000000157231526451555500172630ustar00rootroot00000000000000/* bpf.h * Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Berkeley Packet Filter functions. */ #ifndef BPF_H #define BPF_H #include /* for __BITS_PER_LONG */ #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #include "arch.h" #if __BITS_PER_LONG == 32 || defined(__ILP32__) #define BITS32 #elif __BITS_PER_LONG == 64 #define BITS64 #endif /* Constants for comparison operators. */ #define MIN_OPERATOR 128 enum { EQ = MIN_OPERATOR, NE, LT, LE, GT, GE, SET, IN }; /* * BPF return values and data structures, * since they're not yet in the kernel. * TODO(crbug.com/1147037): Replace this with an #include. */ #define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the entire process */ #define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */ #define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD #define SECCOMP_RET_TRAP 0x00030000U /* return SIGSYS */ #define SECCOMP_RET_ERRNO 0x00050000U /* return -1 and set errno */ #define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */ #define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ #define SECCOMP_RET_DATA 0x0000ffffU /* mask for return value */ struct seccomp_data { int nr; __u32 arch; __u64 instruction_pointer; __u64 args[6]; }; #define syscall_nr (offsetof(struct seccomp_data, nr)) #define arch_nr (offsetof(struct seccomp_data, arch)) /* Size-dependent defines. */ #if defined(BITS32) /* * On 32 bits, comparisons take 2 instructions: 1 for loading the argument, * 1 for the actual comparison. */ #define BPF_LOAD_ARG_LEN 1U #define BPF_COMP_LEN 1U #define BPF_SHORT_GT_GE_COMP_LEN 1U #define BPF_GT_GE_COMP_LEN 1U #define BPF_ARG_COMP_LEN (BPF_LOAD_ARG_LEN + BPF_COMP_LEN) #define BPF_ARG_SHORT_GT_GE_COMP_LEN \ (BPF_LOAD_ARG_LEN + BPF_SHORT_GT_GE_COMP_LEN) #define BPF_ARG_GT_GE_COMP_LEN (BPF_LOAD_ARG_LEN + BPF_GT_GE_COMP_LEN) #define bpf_comp_jeq bpf_comp_jeq32 #define bpf_comp_jgt bpf_comp_jgt32 #define bpf_comp_jge bpf_comp_jge32 #define bpf_comp_jset bpf_comp_jset32 #define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) #elif defined(BITS64) /* * On 64 bits, comparisons take 7-8 instructions: 4 for loading the argument, * and 3-4 for the actual comparison. */ #define BPF_LOAD_ARG_LEN 4U #define BPF_COMP_LEN 3U #define BPF_SHORT_GT_GE_COMP_LEN 3U #define BPF_GT_GE_COMP_LEN 4U #define BPF_ARG_COMP_LEN (BPF_LOAD_ARG_LEN + BPF_COMP_LEN) #define BPF_ARG_SHORT_GT_GE_COMP_LEN \ (BPF_LOAD_ARG_LEN + BPF_SHORT_GT_GE_COMP_LEN) #define BPF_ARG_GT_GE_COMP_LEN (BPF_LOAD_ARG_LEN + BPF_GT_GE_COMP_LEN) #define bpf_comp_jeq bpf_comp_jeq64 #define bpf_comp_jgt bpf_comp_jgt64 #define bpf_comp_jge bpf_comp_jge64 #define bpf_comp_jset bpf_comp_jset64 /* Ensure that we load the logically correct offset. */ #if defined(__LITTLE_ENDIAN__) || __BYTE_ORDER == __LITTLE_ENDIAN #define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) #define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32) #else #error "Unsupported endianness" #endif #else #error "Unknown bit width" #endif /* Common jump targets. */ #define NEXT 0 #define SKIP 1 #define SKIPN(_n) (_n) /* Support for labels in BPF programs. */ #define JUMP_JT 0xff #define JUMP_JF 0xff #define LABEL_JT 0xfe #define LABEL_JF 0xfe #define MAX_BPF_LABEL_LEN 32 #define BPF_LABELS_MAX 512U /* Each syscall could have an argument block. */ struct bpf_labels { size_t count; struct __bpf_label { const char *label; unsigned int location; } labels[BPF_LABELS_MAX]; }; /* BPF instruction manipulation functions and macros. */ static inline size_t set_bpf_instr(struct sock_filter *instr, unsigned short code, unsigned int k, unsigned char jt, unsigned char jf) { instr->code = code; instr->k = k; instr->jt = jt; instr->jf = jf; return 1U; } #define set_bpf_stmt(_block, _code, _k) \ set_bpf_instr((_block), (_code), (_k), 0, 0) #define set_bpf_jump(_block, _code, _k, _jt, _jf) \ set_bpf_instr((_block), (_code), (_k), (_jt), (_jf)) #define set_bpf_lbl(_block, _lbl_id) \ set_bpf_jump((_block), BPF_JMP + BPF_JA, (_lbl_id), LABEL_JT, LABEL_JF) #define set_bpf_jump_lbl(_block, _lbl_id) \ set_bpf_jump((_block), BPF_JMP + BPF_JA, (_lbl_id), JUMP_JT, JUMP_JF) #define set_bpf_ret_kill(_block) \ set_bpf_stmt((_block), BPF_RET + BPF_K, SECCOMP_RET_KILL) #define set_bpf_ret_kill_process(_block) \ set_bpf_stmt((_block), BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS) #define set_bpf_ret_trap(_block) \ set_bpf_stmt((_block), BPF_RET + BPF_K, SECCOMP_RET_TRAP) #define set_bpf_ret_errno(_block, _errno) \ set_bpf_stmt((_block), BPF_RET + BPF_K, \ SECCOMP_RET_ERRNO | ((_errno) & SECCOMP_RET_DATA)) #define set_bpf_ret_log(_block) \ set_bpf_stmt((_block), BPF_RET + BPF_K, SECCOMP_RET_LOG) #define set_bpf_ret_allow(_block) \ set_bpf_stmt((_block), BPF_RET + BPF_K, SECCOMP_RET_ALLOW) #define bpf_load_syscall_nr(_filter) \ set_bpf_stmt((_filter), BPF_LD + BPF_W + BPF_ABS, syscall_nr) /* BPF label functions. */ int bpf_resolve_jumps(struct bpf_labels *labels, struct sock_filter *filter, size_t count); int bpf_label_id(struct bpf_labels *labels, const char *label); void free_label_strings(struct bpf_labels *labels); /* BPF helper functions. */ size_t bpf_load_arg(struct sock_filter *filter, int argidx); size_t bpf_comp_jeq(struct sock_filter *filter, unsigned long c, unsigned char jt, unsigned char jf); size_t bpf_comp_jgt(struct sock_filter *filter, unsigned long c, unsigned char jt, unsigned char jf); size_t bpf_comp_jge(struct sock_filter *filter, unsigned long c, unsigned char jt, unsigned char jf); size_t bpf_comp_jset(struct sock_filter *filter, unsigned long mask, unsigned char jt, unsigned char jf); size_t bpf_comp_jin(struct sock_filter *filter, unsigned long mask, unsigned char jt, unsigned char jf); /* Functions called by syscall_filter.c */ #define ARCH_VALIDATION_LEN 3U #define ALLOW_SYSCALL_LEN 2U size_t bpf_arg_comp(struct sock_filter **pfilter, int op, int argidx, unsigned long c, unsigned int label_id); size_t bpf_validate_arch(struct sock_filter *filter); size_t bpf_allow_syscall(struct sock_filter *filter, int nr); size_t bpf_allow_syscall_args(struct sock_filter *filter, int nr, unsigned int id); #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* BPF_H */ minijail-2026.05.18+git20261016.8787d1a/common.mk000066400000000000000000001023461526451555500201620ustar00rootroot00000000000000# Copyright 2012 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # If this file is part of another source distribution, it's license may be # stored in LICENSE.makefile or LICENSE.common.mk. # # NOTE NOTE NOTE # The authoritative common.mk is located in: # https://chromium.googlesource.com/chromiumos/platform2/+/HEAD/common-mk # Please make all changes there, then copy into place in other repos. # # The copybara service is used to automatically sync updates for projects # hosted on Chromium GoB hosts. Please see: # //google3/third_party/copybara-gsubtreed/chromiumos_platform2/copy.bara.sky # NOTE NOTE NOTE # # This file provides a common architecture for building C/C++ source trees. # It uses recursive makefile inclusion to create a single make process which # can be built in the source tree or with the build artifacts placed elsewhere. # # It is fully parallelizable for all targets, including static archives. # # To use: # 1. Place common.mk in your top source level # 2. In your top-level Makefile, place "include common.mk" at the top # 3. In all subdirectories, create a 'module.mk' file that starts with: # include common.mk # And then contains the remainder of your targets. # 4. All build targets should look like: # relative/path/target: relative/path/obj.o # # See existing makefiles for rule examples. # # Exported macros: # - cc_binary, cxx_binary provide standard compilation steps for binaries # - cxx_library, cc_library provide standard compilation steps for # shared objects. # All of the above optionally take an argument for extra flags. # - update_archive creates/updates a given .a target # # Instead of using the build macros, most users can just use wrapped targets: # - CXX_BINARY, CC_BINARY, CC_STATIC_BINARY, CXX_STATIC_BINARY # - CXX_LIBRARY, CC_LIBRARY, CC_STATIC_LIBRARY, CXX_STATIC_LIBRARY # - E.g., CXX_BINARY(mahbinary): foo.o # - object.depends targets may be used when a prerequisite is required for an # object file. Because object files result in multiple build artifacts to # handle PIC and PIE weirdness. E.g. # foo.o.depends: generated/dbus.h # - TEST(binary) or TEST(CXX_BINARY(binary)) may be used as a prerequisite # for the tests target to trigger an automated test run. # - CLEAN(file_or_dir) dependency can be added to 'clean'. # # If source code is being generated, rules will need to be registered for # compiling the objects. This can be done by adding one of the following # to the Makefile: # - For C source files # $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CC,c,CFLAGS)) # - For C++ source files # $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CXX,cc,CXXFLAGS)) # # Exported targets meant to have prerequisites added to: # - all - Your desired targets should be given # - tests - Any TEST(test_binary) targets should be given # - FORCE - force the given target to run regardless of changes # In most cases, using .PHONY is preferred. # # Possible command line variables: # - COLOR=[0|1] to set ANSI color output (default: 1) # - VERBOSE=[0|1] V=[0|1] to hide/show commands (default: 0) # - MODE=[opt|dbg|profiling] (default: opt) # opt - Enable optimizations for release builds # dbg - Turn down optimization for debugging # profiling - Turn off optimization and turn on profiling/coverage # support. # - ARCH=[x86|arm|supported qemu name] (default: from portage or uname -m) # - SPLITDEBUG=[0|1] splits debug info in target.debug (default: 0) # If NOSTRIP=1, SPLITDEBUG will never strip the final emitted objects. # - NOSTRIP=[0|1] determines if binaries are stripped. (default: 1) # NOSTRIP=0 and MODE=opt will also drop -g from the CFLAGS. # - VALGRIND=[0|1] runs tests under valgrind (default: 0) # - OUT=/path/to/builddir puts all output in given path (default: $PWD) # - VALGRIND_ARGS="" supplies extra memcheck arguments # # Per-target(-ish) variable: # - NEEDS_ROOT=[0|1] allows a TEST() target to run with root. # Default is 0 unless it is running under QEmu. # - NEEDS_MOUNTS=[0|1] allows a TEST() target running on QEmu to get # setup mounts in the $(SYSROOT) # # Caveats: # - Directories or files with spaces in them DO NOT get along with GNU Make. # If you need them, all uses of dir/notdir/etc will need to have magic # wrappers. Proceed at risk to your own sanity. # - External CXXFLAGS and CFLAGS should be passed via the environment since # this file does not use 'override' to control them. # - Our version of GNU Make doesn't seem to support the 'private' variable # annotation, so you can't tag a variable private on a wrapping target. # Behavior configuration variables SPLITDEBUG ?= 0 NOSTRIP ?= 1 VALGRIND ?= 0 COLOR ?= 1 V ?= 0 VERBOSE ?= $(V) MODE ?= opt CXXEXCEPTIONS ?= 0 RUN_TESTS ?= 1 ARCH ?= $(shell uname -m) # Put objects in a separate tree based on makefile locations # This means you can build a tree without touching it: # make -C $SRCDIR # will create ./build-$(MODE) # Or # make -C $SRCDIR OUT=$PWD # This variable is extended on subdir calls and doesn't need to be re-called. OUT ?= $(PWD)/ # Make OUT now so we can use realpath. $(shell mkdir -p "$(OUT)") # TODO(wad) Relative paths are resolved against SRC and not the calling dir. # Ensure a command-line supplied OUT has a slash override OUT := $(realpath $(OUT))/ # SRC is not meant to be set by the end user, but during make call relocation. # $(PWD) != $(CURDIR) all the time. export SRC ?= $(CURDIR) # Re-start in the $(OUT) directory if we're not there. # We may be invoked using -C or bare and we need to ensure behavior # is consistent so we check both PWD vs OUT and PWD vs CURDIR. override RELOCATE_BUILD := 0 ifneq (${PWD}/,${OUT}) override RELOCATE_BUILD := 1 endif # Make sure we're running with no builtin targets. They cause # leakage and mayhem! ifneq (${PWD},${CURDIR}) override RELOCATE_BUILD := 1 # If we're run from the build dir, don't let it get cleaned up later. ifeq (${PWD}/,${OUT}) $(shell touch "$(PWD)/.dont_delete_on_clean") endif endif # ifneq (${PWD},${CURDIR} # "Relocate" if we need to restart without implicit rules. ifeq ($(subst r,,$(MAKEFLAGS)),$(MAKEFLAGS)) override RELOCATE_BUILD := 1 endif ifeq (${RELOCATE_BUILD},1) # By default, silence build output. Reused below as well. QUIET = @ ifeq ($(VERBOSE),1) QUIET= endif # This target will override all targets, including prerequisites. To avoid # calling $(MAKE) once per prereq on the given CMDGOAL, we guard it with a local # variable. RUN_ONCE := 0 MAKECMDGOALS ?= all # Keep the rules split as newer make does not allow them to be declared # on the same line. But the way :: rules work, the _all here will also # invoke the %:: rule while retaining "_all" as the default. _all:: %:: $(if $(filter 0,$(RUN_ONCE)), \ cd "$(OUT)" && \ $(MAKE) -r -I "$(SRC)" -f "$(CURDIR)/Makefile" \ SRC="$(CURDIR)" OUT="$(OUT)" $(foreach g,$(MAKECMDGOALS),"$(g)"),) $(eval RUN_ONCE := 1) pass-to-subcall := 1 endif ifeq ($(pass-to-subcall),) # Only call MODULE if we're in a submodule MODULES_LIST := $(filter-out Makefile %.d,$(MAKEFILE_LIST)) ifeq ($(words $(filter-out Makefile common.mk %.d $(SRC)/Makefile \ $(SRC)/common.mk,$(MAKEFILE_LIST))),0) # All the top-level defines outside of module.mk. # # Helper macros # # Create the directory if it doesn't yet exist. define auto_mkdir $(if $(wildcard $(dir $1)),$2,$(QUIET)mkdir -p "$(dir $1)") endef # Creates the actual archive with an index. # The target $@ must end with .pic.a or .pie.a. define update_archive $(call auto_mkdir,$(TARGET_OR_MEMBER)) $(QUIET)# Create the archive in one step to avoid parallel use accessing it $(QUIET)# before all the symbols are present. @$(ECHO) "AR $(subst \ $(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) \ -> $(subst $(SRC)/,,$(TARGET_OR_MEMBER))" $(QUIET)$(AR) rcs $(TARGET_OR_MEMBER) \ $(subst $(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) endef # Default compile from objects using pre-requisites but filters out # subdirs and .d files. define cc_binary $(call COMPILE_BINARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) endef define cxx_binary $(call COMPILE_BINARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) endef # Default compile from objects using pre-requisites but filters out # subdirs and .d files. define cc_library $(call COMPILE_LIBRARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) endef define cxx_library $(call COMPILE_LIBRARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) endef # Deletes files silently if they exist. Meant for use in any local # clean targets. define silent_rm $(if $(wildcard $(1)), $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANFILE$(COLOR_RESET) ' && \ $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ $(RM) $(1) 2>/dev/null) || true,) endef define silent_rmdir $(if $(wildcard $(1)), $(if $(wildcard $(1)/*), $(QUIET)# $(1) not empty [$(wildcard $(1)/*)]. Not deleting., $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANDIR$(COLOR_RESET) ' && \ $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ $(RMDIR) $(1) 2>/dev/null) || true),) endef # # Default variable values # # Only override toolchain vars if they are from make. CROSS_COMPILE ?= define override_var ifneq ($(filter undefined default,$(origin $1)),) $1 = $(CROSS_COMPILE)$2 endif endef $(eval $(call override_var,AR,ar)) $(eval $(call override_var,CC,gcc)) $(eval $(call override_var,CXX,g++)) $(eval $(call override_var,OBJCOPY,objcopy)) $(eval $(call override_var,PKG_CONFIG,pkg-config)) $(eval $(call override_var,RANLIB,ranlib)) $(eval $(call override_var,STRIP,strip)) RMDIR ?= rmdir ECHO = /bin/echo -e ifeq ($(filter clang,$(subst -, ,$(notdir $(CC)))),clang) CDRIVER = clang else CDRIVER = gcc endif ifeq ($(filter clang++,$(subst -, ,$(notdir $(CXX)))),clang++) CXXDRIVER = clang else CXXDRIVER = gcc endif # Internal macro to support check_XXX macros below. # Usage: $(call check_compile, [code], [compiler], [code_type], [c_flags], # [extra_c_flags], [library_flags], [success_ret], [fail_ret]) # Return: [success_ret] if compile succeeded, otherwise [fail_ret] check_compile = $(shell printf '%b\n' $(1) | \ $($(2)) $($(4)) -x $(3) $(LDFLAGS) $(5) - $(6) -o /dev/null > /dev/null 2>&1 \ && echo "$(7)" || echo "$(8)") # Helper macro to check whether a test program will compile with the specified # compiler flags. # Usage: $(call check_compile_cc, [code], [flags], [alternate_flags]) # Return: [flags] if compile succeeded, otherwise [alternate_flags] check_compile_cc = $(call check_compile,$(1),CC,c,CFLAGS,$(2),,$(2),$(3)) check_compile_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,$(2),,$(2),$(3)) # Helper macro to check whether a test program will compile with the specified # libraries. # Usage: $(call check_compile_cc, [code], [library_flags], [alternate_flags]) # Return: [library_flags] if compile succeeded, otherwise [alternate_flags] check_libs_cc = $(call check_compile,$(1),CC,c,CFLAGS,,$(2),$(2),$(3)) check_libs_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,,$(2),$(2),$(3)) # Helper macro to check whether the compiler accepts the specified flags. # Usage: $(call check_compile_cc, [flags], [alternate_flags]) # Return: [flags] if compile succeeded, otherwise [alternate_flags] check_cc = $(call check_compile_cc,'int main() { return 0; }',$(1),$(2)) check_cxx = $(call check_compile_cxx,'int main() { return 0; }',$(1),$(2)) # Choose the stack protector flags based on whats supported by the compiler. SSP_CFLAGS := $(call check_cc,-fstack-protector-strong) ifeq ($(SSP_CFLAGS),) SSP_CFLAGS := $(call check_cc,-fstack-protector-all) endif # To update these from an including Makefile: # CXXFLAGS += -mahflag # Append to the list # CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list # CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value # The same goes for CFLAGS. COMMON_CFLAGS-gcc := -fvisibility=internal -ggdb3 -Wa,--noexecstack COMMON_CFLAGS-clang := -fvisibility=hidden -ggdb -Wimplicit-fallthrough \ -Wstring-plus-int # When a class is exported through __attribute__((visibility("default"))), we # still want to eliminate symbols from inline class member functions to reduce # symbol resolution overhead. Therefore, pass -fvisibility-inlines-hidden in # addition to -fvisibility=hidden. (go/cros-symbol-slimming) COMMON_CFLAGS := -Wall -Wunused -Wno-unused-parameter -Wunreachable-code \ -Wbool-operation -Wstring-compare $(call check_cc,-Wxor-used-as-pow) \ -Wint-in-bool-context -Wfree-nonheap-object \ -Werror -Wformat=2 -fno-strict-aliasing \ $(SSP_CFLAGS) -O1 CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER)) -std=gnu++23 \ -fvisibility-inlines-hidden CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER)) -std=gnu17 # We undefine _FORTIFY_SOURCE because some distros enable it by default in # their toolchains. This makes the compiler issue warnings about redefines # and our -Werror usage breaks it all. CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 # Enable large file support. CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE # Disable exceptions based on the CXXEXCEPTIONS setting. ifeq ($(CXXEXCEPTIONS),0) CXXFLAGS := $(CXXFLAGS) -fno-exceptions -fno-unwind-tables \ -fno-asynchronous-unwind-tables endif ifeq ($(MODE),opt) # Up the optimizations. CFLAGS := $(filter-out -O1,$(CFLAGS)) -O2 CXXFLAGS := $(filter-out -O1,$(CXXFLAGS)) -O2 # Only drop -g* if symbols aren't desired. ifeq ($(NOSTRIP),0) # TODO: do we want -fomit-frame-pointer on x86? CFLAGS := $(filter-out -ggdb3,$(CFLAGS)) CXXFLAGS := $(filter-out -ggdb3,$(CXXFLAGS)) endif endif ifeq ($(MODE),profiling) CFLAGS := $(CFLAGS) -O0 -g --coverage CXXFLAGS := $(CXXFLAGS) -O0 -g --coverage LDFLAGS := $(LDFLAGS) --coverage endif # Pass -Bsymbolic-non-weak which pre-binds symbols in the same DSO to improve # startup performance. We don't support interposing non-weak symbols. # (go/cros-symbol-slimming) LDFLAGS := $(LDFLAGS) \ -z relro \ -z noexecstack \ -z now \ $(call check_cc,-Xlinker -Bsymbolic-non-weak) # Fancy helpers for color if a prompt is defined ifeq ($(COLOR),1) COLOR_RESET = \x1b[0m COLOR_GREEN = \x1b[32;01m COLOR_RED = \x1b[31;01m COLOR_YELLOW = \x1b[33;01m endif # By default, silence build output. QUIET = @ ifeq ($(VERBOSE),1) QUIET= endif # # Implementation macros for compile helpers above # # Useful for dealing with pie-broken toolchains. # Call make with PIE=0 to disable default PIE use. OBJ_PIE_FLAG = -fPIE COMPILE_PIE_FLAG = -pie ifeq ($(PIE),0) OBJ_PIE_FLAG = COMPILE_PIE_FLAG = endif # Favor member targets first for CXX_BINARY(%) magic. # And strip out nested members if possible. LP := ( RP := ) TARGET_OR_MEMBER = $(lastword $(subst $(LP), ,$(subst $(RP),,$(or $%,$@)))) # Default compile from objects using pre-requisites but filters out # all non-.o files. define COMPILE_BINARY_implementation @$(ECHO) "LD$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" $(call auto_mkdir,$(TARGET_OR_MEMBER)) $(QUIET)$($(1)) $(COMPILE_PIE_FLAGS) -o $(TARGET_OR_MEMBER) \ $(2) $(LDFLAGS) \ $(filter %.o %.a,$(^:.o=.pie.o)) \ $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ $(LDLIBS) $(call conditional_strip) @$(ECHO) -n "BIN " @$(ECHO) "$(COLOR_GREEN)$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" endef # TODO: add version support extracted from PV environment variable #ifeq ($(PV),9999) #$(warning PV=$(PV). If shared object versions matter, please force PV=.) #endif # Then add -Wl,-soname,$@.$(PV) ? # Default compile from objects using pre-requisites but filters out # all non-.o values. (Remember to add -L$(OUT) -llib) COMMA := , define COMPILE_LIBRARY_implementation @$(ECHO) "SHARED$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" $(call auto_mkdir,$(TARGET_OR_MEMBER)) $(QUIET)$($(1)) -shared -Wl,-E -o $(TARGET_OR_MEMBER) \ $(2) $(LDFLAGS) \ $(if $(filter %.a,$^),-Wl$(COMMA)--whole-archive,) \ $(filter %.o ,$(^:.o=.pic.o)) \ $(foreach a,$(filter %.a,$^),-L$(dir $(a)) \ -l$(patsubst lib%,%,$(basename $(notdir $(a))))) \ $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ $(LDLIBS) $(call conditional_strip) @$(ECHO) -n "LIB $(COLOR_GREEN)" @$(ECHO) "$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" endef define conditional_strip $(if $(filter 0,$(NOSTRIP)),$(call strip_artifact)) endef define strip_artifact @$(ECHO) "STRIP $(subst $(OUT)/,,$(TARGET_OR_MEMBER))" $(if $(filter 1,$(SPLITDEBUG)), @$(ECHO) -n "DEBUG "; \ $(ECHO) "$(COLOR_YELLOW)\ $(subst $(OUT)/,,$(TARGET_OR_MEMBER)).debug$(COLOR_RESET)") $(if $(filter 1,$(SPLITDEBUG)), \ $(QUIET)$(OBJCOPY) --only-keep-debug "$(TARGET_OR_MEMBER)" \ "$(TARGET_OR_MEMBER).debug") $(if $(filter-out dbg,$(MODE)),$(QUIET)$(STRIP) --strip-unneeded \ "$(TARGET_OR_MEMBER)",) endef # # Global pattern rules # # Below, the archive member syntax is abused to create fancier # syntactic sugar for recipe authors that avoids needed to know # subcall options. The downside is that make attempts to look # into the phony archives for timestamps. This will cause the final # target to be rebuilt/linked on _every_ call to make even when nothing # has changed. Until a better way presents itself, we have helpers that # do the stat check on make's behalf. Dodgy but simple. define old_or_no_timestamp $(if $(realpath $%),,$(1)) $(if $(shell find $^ -cnewer "$%" 2>/dev/null),$(1)) endef define check_deps $(if $(filter 0,$(words $^)),\ $(error Missing dependencies or declaration of $@($%)),) endef # Build a cxx target magically CXX_BINARY(%): $(call check_deps) $(call old_or_no_timestamp,$(call cxx_binary)) clean: CLEAN(CXX_BINARY*) CC_BINARY(%): $(call check_deps) $(call old_or_no_timestamp,$(call cc_binary)) clean: CLEAN(CC_BINARY*) CXX_STATIC_BINARY(%): $(call check_deps) $(call old_or_no_timestamp,$(call cxx_binary,-static)) clean: CLEAN(CXX_STATIC_BINARY*) CC_STATIC_BINARY(%): $(call check_deps) $(call old_or_no_timestamp,$(call cc_binary,-static)) clean: CLEAN(CC_STATIC_BINARY*) CXX_LIBRARY(%): $(call check_deps) $(call old_or_no_timestamp,$(call cxx_library)) clean: CLEAN(CXX_LIBRARY*) CXX_LIBARY(%): $(error Typo alert! LIBARY != LIBRARY) CC_LIBRARY(%): $(call check_deps) $(call old_or_no_timestamp,$(call cc_library)) clean: CLEAN(CC_LIBRARY*) CC_LIBARY(%): $(error Typo alert! LIBARY != LIBRARY) CXX_STATIC_LIBRARY(%): $(call check_deps) $(call old_or_no_timestamp,$(call update_archive)) clean: CLEAN(CXX_STATIC_LIBRARY*) CXX_STATIC_LIBARY(%): $(error Typo alert! LIBARY != LIBRARY) CC_STATIC_LIBRARY(%): $(call check_deps) $(call old_or_no_timestamp,$(call update_archive)) clean: CLEAN(CC_STATIC_LIBRARY*) CC_STATIC_LIBARY(%): $(error Typo alert! LIBARY != LIBRARY) TEST(%): % $(call TEST_implementation) ifneq ($(RUN_TESTS),0) TEST(%): qemu_chroot_install endif .PHONY: TEST # multiple targets with a wildcard need to share an directory. # Don't use this directly it just makes sure the directory is removed _after_ # the files are. CLEANFILE(%): $(call silent_rm,$(TARGET_OR_MEMBER)) .PHONY: CLEANFILE CLEAN(%): CLEANFILE(%) $(QUIET)# CLEAN($%) meta-target called $(if $(filter-out $(PWD)/,$(dir $(abspath $(TARGET_OR_MEMBER)))), \ $(call silent_rmdir,$(dir $(abspath $(TARGET_OR_MEMBER)))),\ $(QUIET)# Not deleting $(dir $(abspath $(TARGET_OR_MEMBER))) yet.) .PHONY: CLEAN # # Top-level objects and pattern rules # # All objects for .c files at the top level C_OBJECTS = $(patsubst $(SRC)/%.c,%.o,$(wildcard $(SRC)/*.c)) # All objects for .cxx files at the top level CXX_OBJECTS = $(patsubst $(SRC)/%.cc,%.o,$(wildcard $(SRC)/*.cc)) # Note, the catch-all pattern rules don't work in subdirectories because # we're building from the $(OUT) directory. At the top-level (here) they will # work, but we go ahead and match using the module form. Then we can place a # generic pattern rule to capture leakage from the main Makefile. (Later in the # file.) # # The reason target specific pattern rules work well for modules, # MODULE_C_OBJECTS, is because it scopes the behavior to the given target which # ensures we get a relative directory offset from $(OUT) which otherwise would # not match without further magic on a per-subdirectory basis. # Creates object file rules. Call with eval. # $(1) list of .o files # $(2) source type (CC or CXX) # $(3) source suffix (cc or c) # $(4) compiler flag name (CFLAGS or CXXFLAGS) # $(5) source dir: _only_ if $(SRC). Leave blank for obj tree. define add_object_rules $(patsubst %.o,%.pie.o,$(1)): %.pie.o: $(5)%.$(3) %.o.depends $$(call auto_mkdir,$$@) $$(call OBJECT_PATTERN_implementation,$(2),\ $$(basename $$@),$$($(4)) $$(CPPFLAGS) $$(OBJ_PIE_FLAG)) $(patsubst %.o,%.pic.o,$(1)): %.pic.o: $(5)%.$(3) %.o.depends $$(call auto_mkdir,$$@) $$(call OBJECT_PATTERN_implementation,$(2),\ $$(basename $$@),$$($(4)) $$(CPPFLAGS) -fPIC) # Placeholder for depends $(patsubst %.o,%.o.depends,$(1)): $$(call auto_mkdir,$$@) $$(QUIET)touch "$$@" $(1): %.o: %.pic.o %.pie.o $$(call auto_mkdir,$$@) $$(QUIET)touch "$$@" endef # Wrap all the deps in $$(wildcard) so a missing header won't cause weirdness. # First we remove newlines and \, then wrap it. define OBJECT_PATTERN_implementation @$(ECHO) "$(1) $(subst $(SRC)/,,$<) -> $(2).o" $(call auto_mkdir,$@) $(QUIET)$($(1)) -c -MD -MF $(2).d $(3) -o $(2).o $< $(QUIET)sed -i -e :j -e '$$!N;s|\\\s*\n| |;tj' \ -e 's|^\(.*\s*:\s*\)\(.*\)$$|\1 $$\(wildcard \2\)|' $(2).d endef # Now actually register handlers for C(XX)_OBJECTS. $(eval $(call add_object_rules,$(C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) $(eval $(call add_object_rules,$(CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) # Disable default pattern rules to help avoid leakage. # These may already be handled by '-r', but let's keep it to be safe. %: %.o ; %.a: %.o ; %.o: %.c ; %.o: %.cc ; # NOTE: A specific rule for archive objects is avoided because parallel # update of the archive causes build flakiness. # Instead, just make the objects the prerequisites and use update_archive # To use the foo.a(obj.o) functionality, targets would need to specify the # explicit object they expect on the prerequisite line. # # Architecture detection and QEMU wrapping # HOST_ARCH ?= $(shell uname -m) override ARCH := $(strip $(ARCH)) override HOST_ARCH := $(strip $(HOST_ARCH)) # emake will supply "x86" or "arm" for ARCH, but # if uname -m runs and you get x86_64, then this subst # will break. ifeq ($(subst x86,i386,$(ARCH)),i386) QEMU_ARCH := $(subst x86,i386,$(ARCH)) # x86 -> i386 else ifeq ($(subst amd64,x86_64,$(ARCH)),x86_64) QEMU_ARCH := $(subst amd64,x86_64,$(ARCH)) # amd64 -> x86_64 else QEMU_ARCH = $(ARCH) endif override QEMU_ARCH := $(strip $(QEMU_ARCH)) # If we're cross-compiling, try to use qemu for running the tests. ifneq ($(QEMU_ARCH),$(HOST_ARCH)) ifeq ($(SYSROOT),) $(info SYSROOT not defined. qemu-based testing disabled) else # A SYSROOT is assumed for QEmu use. USE_QEMU ?= 1 # Allow 64-bit hosts to run 32-bit without qemu. ifeq ($(HOST_ARCH),x86_64) ifeq ($(QEMU_ARCH),i386) USE_QEMU = 0 endif endif endif else USE_QEMU ?= 0 endif # Normally we don't need to run as root or do bind mounts, so only # enable it by default when we're using QEMU. NEEDS_ROOT ?= $(USE_QEMU) NEEDS_MOUNTS ?= $(USE_QEMU) SYSROOT_OUT = $(OUT) ifneq ($(SYSROOT),) SYSROOT_OUT = $(subst $(SYSROOT),,$(OUT)) else # Default to / when all the empty-sysroot logic is done. SYSROOT = / endif QEMU_NAME = qemu-$(QEMU_ARCH) QEMU_PATH = /build/bin/$(QEMU_NAME) QEMU_SYSROOT_PATH = $(SYSROOT)$(QEMU_PATH) QEMU_SRC_PATH = /usr/bin/$(QEMU_NAME) QEMU_BINFMT_PATH = /proc/sys/fs/binfmt_misc/$(QEMU_NAME) QEMU_REGISTER_PATH = /proc/sys/fs/binfmt_misc/register QEMU_MAGIC_arm = ":$(QEMU_NAME):M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/build/bin/qemu-arm:" # # Output full configuration at top level # # Don't show on clean ifneq ($(MAKECMDGOALS),clean) $(info build configuration:) $(info - OUT=$(OUT)) $(info - SRC=$(SRC)) $(info - MODE=$(MODE)) $(info - SPLITDEBUG=$(SPLITDEBUG)) $(info - NOSTRIP=$(NOSTRIP)) $(info - VALGRIND=$(VALGRIND)) $(info - COLOR=$(COLOR)) $(info - CXXEXCEPTIONS=$(CXXEXCEPTIONS)) $(info - ARCH=$(ARCH)) $(info - QEMU_ARCH=$(QEMU_ARCH)) $(info - USE_QEMU=$(USE_QEMU)) $(info - NEEDS_ROOT=$(NEEDS_ROOT)) $(info - NEEDS_MOUNTS=$(NEEDS_MOUNTS)) $(info - SYSROOT=$(SYSROOT)) $(info ) endif # # Standard targets with detection for when they are improperly configured. # # all does not include tests by default all: $(QUIET)(test -z "$^" && \ $(ECHO) "You must add your targets as 'all' prerequisites") || true $(QUIET)test -n "$^" # Builds and runs tests for the target arch # Run them in parallel # After the test have completed, if profiling, run coverage analysis tests: ifeq ($(MODE),profiling) @$(ECHO) "COVERAGE [$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" $(QUIET)FILES=""; \ for GCNO in `find . -name "*.gcno"`; do \ GCDA="$${GCNO%.gcno}.gcda"; \ if [ -e $${GCDA} ]; then \ FILES="$${FILES} $${GCDA}"; \ fi \ done; \ if [ -n "$${FILES}" ]; then \ gcov -l $${FILES}; \ lcov --capture --directory . \ --output-file=lcov-coverage.info; \ genhtml lcov-coverage.info \ --output-directory lcov-html; \ fi @$(ECHO) "COVERAGE [$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" endif # Standard name everyone else uses. check: tests .PHONY: check tests qemu_chroot_install: ifeq ($(USE_QEMU),1) $(QUIET)$(ECHO) "QEMU Preparing $(QEMU_NAME)" @# Copying strategy @# Compare /usr/bin/qemu inode to /build/$board/build/bin/qemu, if different @# hard link to a temporary file, then rename temp to target. This should @# ensure that once $QEMU_SYSROOT_PATH exists it will always exist, regardless @# of simultaneous test setups. $(QUIET)if [[ ! -e $(QEMU_SYSROOT_PATH) || \ `stat -c %i $(QEMU_SRC_PATH)` != `stat -c %i $(QEMU_SYSROOT_PATH)` \ ]]; then \ $(ROOT_CMD) ln -Tf $(QEMU_SRC_PATH) $(QEMU_SYSROOT_PATH).$$$$; \ $(ROOT_CMD) mv -Tf $(QEMU_SYSROOT_PATH).$$$$ $(QEMU_SYSROOT_PATH); \ fi @# Prep the binfmt handler. First mount if needed, then unregister any bad @# mappings and then register our mapping. @# There may still be some race conditions here where one script de-registers @# and another script starts executing before it gets re-registered, however @# it should be rare. -$(QUIET)[[ -e $(QEMU_REGISTER_PATH) ]] || \ $(ROOT_CMD) mount binfmt_misc -t binfmt_misc \ /proc/sys/fs/binfmt_misc -$(QUIET)if [[ -e $(QEMU_BINFMT_PATH) && \ `awk '$$1 == "interpreter" {print $$NF}' $(QEMU_BINFMT_PATH)` != \ "$(QEMU_PATH)" ]]; then \ echo -1 | $(ROOT_CMD) tee $(QEMU_BINFMT_PATH) >/dev/null; \ fi -$(if $(QEMU_MAGIC_$(ARCH)),$(QUIET)[[ -e $(QEMU_BINFMT_PATH) ]] || \ echo $(QEMU_MAGIC_$(ARCH)) | $(ROOT_CMD) tee $(QEMU_REGISTER_PATH) \ >/dev/null) endif .PHONY: qemu_clean qemu_chroot_install # TODO(wad) Move to -L $(SYSROOT) and fakechroot when qemu-user # doesn't hang traversing /proc from SYSROOT. SUDO_CMD = sudo UNSHARE_CMD = unshare QEMU_CMD = ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),$(SUDO_CMD) , ) MOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) mount, \#) UMOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) umount, \#) QEMU_LDPATH = $(SYSROOT_LDPATH):/lib64:/lib:/usr/lib64:/usr/lib ROOT_CMD_LDPATH = $(SYSROOT_LDPATH):$(SYSROOT)/lib64: ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/lib:$(SYSROOT)/usr/lib64: ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/usr/lib ifeq ($(USE_QEMU),1) export QEMU_CMD = \ $(SUDO_CMD) chroot $(SYSROOT) $(QEMU_PATH) \ -drop-ld-preload \ -E LD_LIBRARY_PATH="$(QEMU_LDPATH):$(patsubst $(OUT),,$(LD_DIRS))" \ -E HOME="$(HOME)" -E SRC="$(SRC)" -- # USE_QEMU conditional function define if_qemu $(1) endef else ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo, ) \ LD_LIBRARY_PATH="$(ROOT_CMD_LDPATH):$(LD_DIRS)" define if_qemu $(2) endef endif VALGRIND_CMD = ifeq ($(VALGRIND),1) VALGRIND_CMD = /usr/bin/valgrind --tool=memcheck $(VALGRIND_ARGS) -- endif ifneq ($(RUN_TESTS),0) define TEST_implementation $(QUIET)$(call TEST_setup) $(QUIET)$(call TEST_run) $(QUIET)$(call TEST_teardown) $(QUIET)exit $$(cat $(OUT)$(TARGET_OR_MEMBER).status.test) endef else define TEST_implementation endef endif define TEST_setup @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " @$(ECHO) "[$(COLOR_YELLOW)SETUP$(COLOR_RESET)]" $(QUIET)# Setup a target-specific results file $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).setup.test) $(QUIET)(echo 1 > $(OUT)$(TARGET_OR_MEMBER).status.test) $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).cleanup.test) $(QUIET)# No setup if we are not using QEMU $(QUIET)# TODO(wad) this is racy until we use a vfs namespace $(call if_qemu,\ $(QUIET)(echo "mkdir -p '$(SYSROOT)/proc' '$(SYSROOT)/dev' \ '$(SYSROOT)/mnt/host/source'" \ >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) $(call if_qemu,\ $(QUIET)(echo "$(MOUNT_CMD) --bind /mnt/host/source \ '$(SYSROOT)/mnt/host/source'" \ >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) $(call if_qemu,\ $(QUIET)(echo "$(MOUNT_CMD) --bind /proc '$(SYSROOT)/proc'" \ >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) $(call if_qemu,\ $(QUIET)(echo "$(MOUNT_CMD) --bind /dev '$(SYSROOT)/dev'" \ >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) endef define TEST_teardown @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " @$(ECHO) "[$(COLOR_YELLOW)TEARDOWN$(COLOR_RESET)]" $(call if_qemu, $(QUIET)$(SHELL) "$(OUT)$(TARGET_OR_MEMBER).cleanup.test") endef # Use GTEST_ARGS.[arch] if defined. override GTEST_ARGS.real = \ $(call if_qemu,$(GTEST_ARGS.qemu.$(QEMU_ARCH)),$(GTEST_ARGS.host.$(HOST_ARCH))) define TEST_run @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " @$(ECHO) "[$(COLOR_GREEN)RUN$(COLOR_RESET)]" $(QUIET)(echo 1 > "$(OUT)$(TARGET_OR_MEMBER).status.test") $(QUIET)(echo $(ROOT_CMD) SRC="$(SRC)" $(QEMU_CMD) $(VALGRIND_CMD) \ "$(strip $(call if_qemu, $(SYSROOT_OUT),$(OUT))$(TARGET_OR_MEMBER))" \ $(if $(filter-out 0,$(words $(GTEST_ARGS.real))),$(GTEST_ARGS.real),\ $(GTEST_ARGS)) >> "$(OUT)$(TARGET_OR_MEMBER).setup.test") -$(QUIET)$(call if_qemu,$(SUDO_CMD) $(UNSHARE_CMD) -m) $(SHELL) \ $(OUT)$(TARGET_OR_MEMBER).setup.test \ && echo 0 > "$(OUT)$(TARGET_OR_MEMBER).status.test" endef # Recursive list reversal so that we get RMDIR_ON_CLEAN in reverse order. define reverse $(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) endef clean: qemu_clean clean: CLEAN($(OUT)*.d) CLEAN($(OUT)*.o) CLEAN($(OUT)*.debug) clean: CLEAN($(OUT)*.test) CLEAN($(OUT)*.depends) clean: CLEAN($(OUT)*.gcno) CLEAN($(OUT)*.gcda) CLEAN($(OUT)*.gcov) clean: CLEAN($(OUT)lcov-coverage.info) CLEAN($(OUT)lcov-html) clean: $(QUIET)# Always delete the containing directory last. $(call silent_rmdir,$(OUT)) FORCE: ; # Empty rule for use when no special targets are needed, like large_tests NONE: .PHONY: clean NONE valgrind NONE .DEFAULT_GOAL := all # Don't let make blow away "intermediates" .PRECIOUS: %.pic.o %.pie.o %.a %.pic.a %.pie.a %.test # Start accruing build info OUT_DIRS = $(OUT) LD_DIRS = $(OUT) SRC_DIRS = $(SRC) include $(wildcard $(OUT)*.d) SUBMODULE_DIRS = $(wildcard $(SRC)/*/module.mk) include $(SUBMODULE_DIRS) else ## In duplicate inclusions of common.mk # Get the current inclusion directory without a trailing slash MODULE := $(patsubst %/,%, \ $(dir $(lastword $(filter-out %common.mk,$(MAKEFILE_LIST))))) MODULE := $(subst $(SRC)/,,$(MODULE)) MODULE_NAME := $(subst /,_,$(MODULE)) #VPATH := $(MODULE):$(VPATH) # Depth first $(eval OUT_DIRS += $(OUT)$(MODULE)) $(eval SRC_DIRS += $(OUT)$(MODULE)) $(eval LD_DIRS := $(LD_DIRS):$(OUT)$(MODULE)) # Add the defaults from this dir to rm_clean clean: CLEAN($(OUT)$(MODULE)/*.d) CLEAN($(OUT)$(MODULE)/*.o) clean: CLEAN($(OUT)$(MODULE)/*.debug) CLEAN($(OUT)$(MODULE)/*.test) clean: CLEAN($(OUT)$(MODULE)/*.depends) clean: CLEAN($(OUT)$(MODULE)/*.gcno) CLEAN($(OUT)$(MODULE)/*.gcda) clean: CLEAN($(OUT)$(MODULE)/*.gcov) CLEAN($(OUT)lcov-coverage.info) clean: CLEAN($(OUT)lcov-html) $(info + submodule: $(MODULE_NAME)) # We must eval otherwise they may be dropped. MODULE_C_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.c,$(MODULE)/%.o,\ $(wildcard $(SRC)/$(MODULE)/*.c)) $(eval $(MODULE_NAME)_C_OBJECTS ?= $(MODULE_C_OBJECTS)) MODULE_CXX_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.cc,$(MODULE)/%.o,\ $(wildcard $(SRC)/$(MODULE)/*.cc)) $(eval $(MODULE_NAME)_CXX_OBJECTS ?= $(MODULE_CXX_OBJECTS)) # Note, $(MODULE) is implicit in the path to the %.c. # See $(C_OBJECTS) for more details. # Register rules for the module objects. $(eval $(call add_object_rules,$(MODULE_C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) $(eval $(call add_object_rules,$(MODULE_CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) # Continue recursive inclusion of module.mk files SUBMODULE_DIRS = $(wildcard $(SRC)/$(MODULE)/*/module.mk) include $(wildcard $(OUT)$(MODULE)/*.d) include $(SUBMODULE_DIRS) endif endif ## pass-to-subcall wrapper for relocating the call directory minijail-2026.05.18+git20261016.8787d1a/config_parser.c000066400000000000000000000076011526451555500213240ustar00rootroot00000000000000/* Copyright 2021 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include #include "config_parser.h" #include "util.h" #define LIST_DEFAULT_SIZE (100) struct config_entry_list *new_config_entry_list(void) { /* * There are <100 CLI options, configuration file will likely have * a similar number of config entries. */ struct config_entry *entries = calloc(LIST_DEFAULT_SIZE, sizeof(struct config_entry)); if (!entries) return NULL; struct config_entry_list *list = calloc(1, sizeof(struct config_entry_list)); if (!list) { free(entries); return NULL; } list->entries = entries; list->num_allocated_ = LIST_DEFAULT_SIZE; list->num_entries = 0; return list; } void clear_config_entry(struct config_entry *entry) { free((char *)entry->key); free((char *)entry->value); } void free_config_entry_list(struct config_entry_list *list) { if (!list) return; for (size_t i = 0; i < list->num_entries; i++) { clear_config_entry(&list->entries[i]); } free(list->entries); free(list); } bool parse_config_line(const char *config_line, struct config_entry *entry) { /* Parsing will modify |config_line| in place, so make a copy. */ attribute_cleanup_str char *line = strdup(config_line); if (!line) return false; char *value = line; /* After tokenize call, |value| will point to a substring after '='. * If there is no '=' in the string, |key| will contain the entire * string while |value| will be NULL. */ char *key = tokenize(&value, "="); if (key) key = strip(key); if (value) value = strip(value); if (!key || key[0] == '\0' || (value && value[0] == '\0')) { warn("unable to parse %s", config_line); return false; } entry->key = strdup(key); entry->value = value ? strdup(value) : NULL; if (!entry->key || (value && !entry->value)) { clear_config_entry(entry); return false; } return true; } static bool match_special_directive(const char *line) { return streq(line, "% minijail-config-file v0\n"); } bool parse_config_file(FILE *config_file, struct config_entry_list *list) { attribute_cleanup_str char *line = NULL; size_t len = 0; /* The first line must match the special directive */ if (getline(&line, &len, config_file) == -1 || !match_special_directive(line)) return false; while (getmultiline(&line, &len, config_file) != -1) { char *stripped_line = strip(line); /* * Skip blank lines and all comments. Comment lines start with * '#'. */ if (stripped_line[0] == '\0' || stripped_line[0] == '#') continue; /* * Check if the list is full, and reallocate with doubled * capacity if so. */ if (list->num_entries >= list->num_allocated_) { list->num_allocated_ = list->num_allocated_ * 2; list->entries = realloc( list->entries, list->num_allocated_ * sizeof(struct config_entry)); if (list->entries == NULL) { return false; } } struct config_entry *entry = &list->entries[list->num_entries]; if (!parse_config_line(stripped_line, entry)) { return false; } ++list->num_entries; } /* * getmultiline() behaves similarly with getline(3). It returns -1 * when read into EOF or an error occurs. Since errno is not cleared * on success or when EOF is reached, the value might be held over from * a different call, so we can't rely on it directly, at least not w/out * refactoring the loop above to clear+check it around the getline call. * Instead, let's just check if the file has been completely read. */ if (!feof(config_file)) { return false; } /* Shrink the list to save memory. */ if (list->num_entries < list->num_allocated_) { list->entries = realloc(list->entries, list->num_entries * sizeof(struct config_entry)); list->num_allocated_ = list->num_entries; } return true; } minijail-2026.05.18+git20261016.8787d1a/config_parser.h000066400000000000000000000023611526451555500213270ustar00rootroot00000000000000/* Copyright 2021 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef CONFIG_PARSER_H #define CONFIG_PARSER_H #include #include #include #ifdef __cplusplus extern "C" { #endif struct config_entry { const char *key; const char *value; }; struct config_entry_list { struct config_entry *entries; size_t num_entries; size_t num_allocated_; }; /* Allocate a new |config_entry_list| struct. */ struct config_entry_list *new_config_entry_list(void); /* Free allocated pointers in |config_entry|. */ void clear_config_entry(struct config_entry *entry); /* Free a |config_entry_list| struct. */ void free_config_entry_list(struct config_entry_list *list); /* * Parse one config line into a entry. * * Returns true for success, otherwise false for parsing failures. */ bool parse_config_line(const char *config_line, struct config_entry *entry); /* * Parse a minijail config file into a |config_entry_list|. * * Returns true for success, otherwise false for parsing failures. */ bool parse_config_file(FILE *config_file, struct config_entry_list *list); #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* CONFIG_PARSER_H */ minijail-2026.05.18+git20261016.8787d1a/config_parser_unittest.cc000066400000000000000000000074211526451555500234260ustar00rootroot00000000000000/* Copyright 2021 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test config_parser.c using gtest. */ #include #include #include "config_parser.h" #include "test_util.h" #include "util.h" namespace { class ConfigFileTest : public ::testing::Test { protected: virtual void SetUp() { list_ = new_config_entry_list(); ASSERT_NE(list_, nullptr); } virtual void TearDown() { free_config_entry_list(list_); } struct config_entry_list* list_; }; } // namespace TEST(ParsingConfigTest, valid_config_line) { ScopedConfigEntry entry( (config_entry*)calloc(1, sizeof(struct config_entry))); const std::vector valid_conf_lines = { "mount=none", "valueless_key" "binding = none", " xyz = abc ", }; for (const auto& conf_line : valid_conf_lines) { ASSERT_TRUE(parse_config_line(conf_line.c_str(), entry.get())); clear_config_entry(entry.get()); } } TEST(ParsingConfigTest, invalid_config_line) { ScopedConfigEntry entry( (config_entry*)calloc(1, sizeof(struct config_entry))); const std::vector invalid_conf_lines = { "= none", "", "empty_arg=", "empty_arg= ", }; for (const auto& conf_line : invalid_conf_lines) { ASSERT_FALSE(parse_config_line(conf_line.c_str(), entry.get())); } } TEST_F(ConfigFileTest, malformed_config_line) { std::string config = "% minijail-config-file v0\n" "=malformed"; ScopedFILE config_file(write_to_pipe(config)); ASSERT_NE(config_file.get(), nullptr); bool res = parse_config_file(config_file.get(), list_); // Policy is malformed, but process should not crash. ASSERT_FALSE(res); ASSERT_EQ(list_->num_entries, 0); } TEST_F(ConfigFileTest, bad_directive) { std::string config = "% bad-directive\n" "# comments"; ScopedFILE config_file(write_to_pipe(config)); ASSERT_NE(config_file.get(), nullptr); bool res = parse_config_file(config_file.get(), list_); // Policy is malformed, but process should not crash. ASSERT_FALSE(res); ASSERT_EQ(list_->num_entries, 0); } TEST_F(ConfigFileTest, wellformed_single_line) { std::string config = "% minijail-config-file v0\n" "# Comments \n" "\n" "uts\n" "mount= xyz\n" "binding = none,/tmp"; ScopedFILE config_file(write_to_pipe(config)); ASSERT_NE(config_file.get(), nullptr); bool res = parse_config_file(config_file.get(), list_); ASSERT_TRUE(res); ASSERT_EQ(list_->num_entries, 3); struct config_entry* first_entry = list_->entries; struct config_entry* second_entry = list_->entries + 1; struct config_entry* third_entry = list_->entries + 2; ASSERT_EQ(std::string(first_entry->key), "uts"); ASSERT_EQ(first_entry->value, nullptr); ASSERT_EQ(std::string(second_entry->key), "mount"); ASSERT_EQ(std::string(second_entry->value), "xyz"); ASSERT_EQ(std::string(third_entry->key), "binding"); ASSERT_EQ(std::string(third_entry->value), "none,/tmp"); } TEST_F(ConfigFileTest, wellformed_multi_line) { std::string config = "% minijail-config-file v0\n" "# Comments \n" "\n" "mount = \\\n" "none\n" "binding = none,\\\n" "/tmp"; ScopedFILE config_file(write_to_pipe(config)); ASSERT_NE(config_file.get(), nullptr); int res = parse_config_file(config_file.get(), list_); ASSERT_TRUE(res); ASSERT_EQ(list_->num_entries, 2); struct config_entry* first_entry = list_->entries; struct config_entry* second_entry = list_->entries + 1; ASSERT_EQ(std::string(first_entry->key), "mount"); ASSERT_EQ(std::string(first_entry->value), "none"); ASSERT_EQ(std::string(second_entry->key), "binding"); ASSERT_EQ(std::string(second_entry->value), "none, /tmp"); } minijail-2026.05.18+git20261016.8787d1a/dump_constants.cc000066400000000000000000000027271526451555500217130ustar00rootroot00000000000000/* Copyright 2018 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ // Generate a .json file with all the architecture-specific constants. #include #include #include #include #include "arch.h" #include "libconstants.h" #include "libsyscalls.h" int main() { // Numeric values are passed to std::cout via std::to_string() to avoid // the use of 'bextr' asm instruction (when compiled with -march=bdver4). std::cout << "{\n"; std::cout << " \"arch_nr\": " << std::to_string(MINIJAIL_ARCH_NR) << ",\n"; std::cout << " \"arch_name\": \"" << MINIJAIL_ARCH_NAME << "\",\n"; std::cout << " \"bits\": " << std::to_string(MINIJAIL_ARCH_BITS) << ",\n"; std::cout << " \"syscalls\": {\n"; bool first = true; for (const struct syscall_entry* entry = syscall_table; entry->name; ++entry) { if (first) first = false; else std::cout << ",\n"; std::cout << " \"" << entry->name << "\": " << std::to_string(entry->nr); } std::cout << "\n },\n"; std::cout << " \"constants\": {\n"; first = true; for (const struct constant_entry* entry = constant_table; entry->name; ++entry) { if (first) first = false; else std::cout << ",\n"; std::cout << " \"" << entry->name << "\": " << std::to_string(entry->value); } std::cout << "\n }\n"; std::cout << "}\n"; return 0; } minijail-2026.05.18+git20261016.8787d1a/elfparse.c000066400000000000000000000113311526451555500202770ustar00rootroot00000000000000/* Copyright 2014 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include #include #include "elfparse.h" #include "util.h" int is_elf_magic(const uint8_t *buf) { return (buf[EI_MAG0] == ELFMAG0) && (buf[EI_MAG1] == ELFMAG1) && (buf[EI_MAG2] == ELFMAG2) && (buf[EI_MAG3] == ELFMAG3); } #define parseElftemplate(bit) \ ElfType parseElf##bit(FILE *elf_file, uint8_t *pHead, \ int little_endian) \ { \ ElfType ret = ELFSTATIC; \ Minijail_Elf##bit##_Ehdr *pHeader = NULL; \ Minijail_Elf##bit##_Phdr pheader; \ uint32_t i = 0; \ \ if (!elf_file || !pHead) \ return ELFERROR; \ \ pHeader = (Minijail_Elf##bit##_Ehdr *)pHead; \ if (little_endian) { \ pHeader->e_phoff = le##bit##toh(pHeader->e_phoff); \ pHeader->e_phentsize = le16toh(pHeader->e_phentsize); \ pHeader->e_phnum = le16toh(pHeader->e_phnum); \ } else { \ pHeader->e_phoff = be##bit##toh(pHeader->e_phoff); \ pHeader->e_phentsize = be16toh(pHeader->e_phentsize); \ pHeader->e_phnum = be16toh(pHeader->e_phnum); \ } \ if (pHeader->e_phentsize != sizeof(Minijail_Elf##bit##_Phdr)) \ return ELFERROR; \ \ if (fseek(elf_file, pHeader->e_phoff, SEEK_SET) != 0) \ return ELFERROR; \ \ for (i = 0; i < pHeader->e_phnum; i++) { \ if (fread(&pheader, sizeof(pheader), 1, elf_file) == \ 1) { \ if (pheader.p_type == PT_INTERP) { \ ret = ELFDYNAMIC; \ break; \ } \ } else { \ ret = ELFERROR; \ break; \ } \ } \ return ret; \ } parseElftemplate(64) parseElftemplate(32) /* Public function to determine the linkage of an ELF. */ ElfType get_elf_linkage(const char *path) { ElfType ret = ELFERROR; attribute_cleanup_fp FILE *elf_file = NULL; uint8_t pHeader[HEADERSIZE] = ""; elf_file = fopen(path, "re"); if (elf_file) { if (fread(pHeader, 1, HEADERSIZE, elf_file) == HEADERSIZE) { if (is_elf_magic(pHeader)) { if ((pHeader[EI_DATA] == ELFDATA2LSB) && (pHeader[EI_CLASS] == ELFCLASS64)) { /* 64-bit little endian. */ ret = parseElf64(elf_file, pHeader, 1); } else if ((pHeader[EI_DATA] == ELFDATA2MSB) && (pHeader[EI_CLASS] == ELFCLASS64)) { /* 64-bit big endian. */ ret = parseElf64(elf_file, pHeader, 0); } else if ((pHeader[EI_DATA] == ELFDATA2LSB) && (pHeader[EI_CLASS] == ELFCLASS32)) { /* 32-bit little endian. */ ret = parseElf32(elf_file, pHeader, 1); } else if ((pHeader[EI_DATA] == ELFDATA2MSB) && (pHeader[EI_CLASS] == ELFCLASS32)) { /* 32-bit big endian. */ ret = parseElf32(elf_file, pHeader, 0); } } else { /* * The binary is not an ELF. We assume it's a * script. We should parse the #! line and * check the interpreter to guard against * static interpreters escaping the sandbox. * As Minijail is only called from the rootfs * it was deemed not necessary to check this. * So we will just let execve(2) decide if this * is valid. */ ret = ELFDYNAMIC; } } else { /* * The file is smaller than |HEADERSIZE| bytes. * We assume it's a short script. See above for * reasoning on scripts. */ ret = ELFDYNAMIC; } } return ret; } minijail-2026.05.18+git20261016.8787d1a/elfparse.h000066400000000000000000000067551526451555500203220ustar00rootroot00000000000000/* elfparse.h * Copyright 2014 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Elf parsing. */ #ifndef _ELFPARSE_H_ #define _ELFPARSE_H_ #include /* * These structs come from elf.h * The version in elf.h do not pack these structs so * portability could be an issue. * The compiler could mess with aligmment depending on arch * so I'm redefining them here and packing them to 1-byte alignment. */ #if !defined(EI_NIDENT) #define EI_NIDENT (16) #endif #pragma pack(push) #pragma pack(1) typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf32_Half e_type; /* Object file type */ Elf32_Half e_machine; /* Architecture */ Elf32_Word e_version; /* Object file version */ Elf32_Addr e_entry; /* Entry point virtual address */ Elf32_Off e_phoff; /* Program header table file offset */ Elf32_Off e_shoff; /* Section header table file offset */ Elf32_Word e_flags; /* Processor-specific flags */ Elf32_Half e_ehsize; /* ELF header size in bytes */ Elf32_Half e_phentsize; /* Program header table entry size */ Elf32_Half e_phnum; /* Program header table entry count */ Elf32_Half e_shentsize; /* Section header table entry size */ Elf32_Half e_shnum; /* Section header table entry count */ Elf32_Half e_shstrndx; /* Section header string table index */ } Minijail_Elf32_Ehdr; typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf64_Half e_type; /* Object file type */ Elf64_Half e_machine; /* Architecture */ Elf64_Word e_version; /* Object file version */ Elf64_Addr e_entry; /* Entry point virtual address */ Elf64_Off e_phoff; /* Program header table file offset */ Elf64_Off e_shoff; /* Section header table file offset */ Elf64_Word e_flags; /* Processor-specific flags */ Elf64_Half e_ehsize; /* ELF header size in bytes */ Elf64_Half e_phentsize; /* Program header table entry size */ Elf64_Half e_phnum; /* Program header table entry count */ Elf64_Half e_shentsize; /* Section header table entry size */ Elf64_Half e_shnum; /* Section header table entry count */ Elf64_Half e_shstrndx; /* Section header string table index */ } Minijail_Elf64_Ehdr; typedef struct { Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */ Elf32_Addr p_paddr; /* Segment physical address */ Elf32_Word p_filesz; /* Segment size in file */ Elf32_Word p_memsz; /* Segment size in memory */ Elf32_Word p_flags; /* Segment flags */ Elf32_Word p_align; /* Segment alignment */ } Minijail_Elf32_Phdr; typedef struct { Elf64_Word p_type; /* Segment type */ Elf64_Word p_flags; /* Segment flags */ Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ Elf64_Xword p_filesz; /* Segment size in file */ Elf64_Xword p_memsz; /* Segment size in memory */ Elf64_Xword p_align; /* Segment alignment */ } Minijail_Elf64_Phdr; #pragma pack(pop) /* End of definitions from elf.h */ enum ElfTypeEnum { ELFERROR = 0, ELFSTATIC = 1, ELFDYNAMIC = 2 }; typedef enum ElfTypeEnum ElfType; /* * This is the initial amount of the ELF file we try and read. * It is the same value that the kernel uses (BINPRM_BUF_SIZE). */ #define HEADERSIZE 128 ElfType get_elf_linkage(const char *path); #endif /* _ELFPARSE_H_ */ minijail-2026.05.18+git20261016.8787d1a/examples/000077500000000000000000000000001526451555500201515ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/examples/cat.policy000066400000000000000000000005271526451555500221450ustar00rootroot00000000000000# In this directory, test with: # make LIBDIR=. # ./minijail0 -n -S examples/cat.policy -- /bin/cat /proc/self/status # This policy only works on x86_64. read: 1 write: 1 restart_syscall: 1 rt_sigreturn: 1 exit_group: 1 open: 1 openat: 1 close: 1 fstat: 1 # Enforce W^X. mmap: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE mseal: 1 fadvise64: 1 minijail-2026.05.18+git20261016.8787d1a/examples/drop_privs.cpp000066400000000000000000000045011526451555500230440ustar00rootroot00000000000000// Copyright (C) 2015 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include gid_t groups[] = { 1001, 1002 }; void log_resugid() { uid_t ruid, euid, suid; gid_t rgid, egid, sgid; getresuid(&ruid, &euid, &suid); getresgid(&rgid, &egid, &sgid); LOG(INFO) << "ruid " << ruid << " euid " << euid << " suid " << suid; LOG(INFO) << "rgid " << rgid << " egid " << egid << " sgid " << sgid; int nsupp_groups = getgroups(0, NULL); if (nsupp_groups < 0) { PLOG(FATAL) << "getgroups(0)"; } if (nsupp_groups == 0) { LOG(INFO) << "no supplemental groups"; return; } gid_t *list = (gid_t*)calloc((size_t)nsupp_groups, sizeof(gid_t)); nsupp_groups = getgroups(nsupp_groups, list); if (nsupp_groups < 0) { PLOG(FATAL) << "getgroups(nsupp_groups)"; } for (size_t i = 0; i < (size_t)nsupp_groups; i++) { LOG(INFO) << "supp gid " << i + 1 << " " << list[i]; } free(list); } int main(void) { log_resugid(); minijail *j = minijail_new(); minijail_change_user(j, "system"); minijail_change_group(j, "system"); minijail_set_supplementary_gids(j, arraysize(groups), groups); // minijail_use_caps(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID)); // minijail_use_seccomp_filter(j); // minijail_log_seccomp_filter_failures(j); // minijail_parse_seccomp_filters(j, "/data/filter.policy"); minijail_enter(j); log_resugid(); minijail_destroy(j); // minijail *j2 = minijail_new(); // minijail_change_uid(j2, 5000); // minijail_change_gid(j2, 5000); // minijail_enter(j2); // log_resugid(); return 0; } minijail-2026.05.18+git20261016.8787d1a/gen_constants-inl.h000066400000000000000000000050371526451555500221360ustar00rootroot00000000000000/* Copyright 2014 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #if defined(__i386__) || defined(__x86_64__) #include #endif /* __i386__ || __x86_64__ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arch.h" /* These defines use C structures that are not defined in the same headers which * cause our CPP logic to fail w/undefined identifiers. Remove them to avoid * build errors on such broken systems. */ #undef BLKTRACESETUP #undef BLKTRACESETUP2 #undef FS_IOC_FIEMAP /* The old glibc bundled with the Android host toolchain is missing some ioctl * definitions used by minijail policy in crosvm and other projects. Locally * define them below. * This UAPI is taken from sanitized bionic headers. */ /* */ #if !defined(FS_IOC_FSGETXATTR) && !defined(FS_IOC_FSSETXATTR) struct fsxattr { __u32 fsx_xflags; __u32 fsx_extsize; __u32 fsx_nextents; __u32 fsx_projid; __u32 fsx_cowextsize; unsigned char fsx_pad[8]; }; #define FS_IOC_FSGETXATTR _IOR('X', 31, struct fsxattr) #define FS_IOC_FSSETXATTR _IOW('X', 32, struct fsxattr) #endif /* !FS_IOC_FSGETXATTR && !FS_IOC_FSSETXATTR */ /* */ #if !defined(FS_IOC_SET_ENCRYPTION_POLICY) && \ !defined(FS_IOC_GET_ENCRYPTION_POLICY) #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 struct fscrypt_policy_v1 { __u8 version; __u8 contents_encryption_mode; __u8 filenames_encryption_mode; __u8 flags; __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; }; #define fscrypt_policy fscrypt_policy_v1 #define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) #define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) #endif /* !FS_IOC_SET_ENCRYPTION_POLICY && !FS_IOC_GET_ENCRYPTION_POLICY */ #if !defined(FS_IOC_GET_ENCRYPTION_POLICY_EX) #define FS_IOC_GET_ENCRYPTION_POLICY_EX _IOWR('f', 22, __u8[9]) #endif #if !defined(MADV_FREE) #define MADV_FREE 8 #endif #if !defined(MADV_GUARD_INSTALL) #define MADV_GUARD_INSTALL 102 #endif #if !defined(MADV_GUARD_REMOVE) #define MADV_GUARD_REMOVE 103 #endif minijail-2026.05.18+git20261016.8787d1a/gen_constants.c000066400000000000000000000000371526451555500213440ustar00rootroot00000000000000#include "gen_constants-inl.h" minijail-2026.05.18+git20261016.8787d1a/gen_constants.sh000077500000000000000000000033561526451555500215460ustar00rootroot00000000000000#!/bin/sh # Copyright 2015 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Generates a header file with a named constant table made up of "name", value # entries by including several build target header files and emitting the list # of defines. Use of the preprocessor is needed to recursively include all # relevant headers. set -e if [ $# -ne 1 ] && [ $# -ne 2 ]; then echo "Usage: $(basename "$0") OUTFILE" echo "Usage: $(basename "$0") INFILE OUTFILE" exit 1 fi build() { ${CC:-cc} -dD "${SRC:-.}"/gen_constants.c -E "$@" } GEN_DEPS=1 if [ $# -eq 2 ]; then build() { cat "${CAT_FILE}" } CAT_FILE="$1" GEN_DEPS=0 shift fi OUTFILE="$1" if [ "${GEN_DEPS}" -eq 1 ]; then # Generate a dependency file which helps the build tool to see when it # should regenerate ${OUTFILE}. build -M -MF "${OUTFILE}.d" fi # sed expression which extracts constants and converts them from: # #define AT_FDWCD foo # to: # #ifdef AT_FDCWD # { "AT_FDWCD", AT_FDCWD }, # endif SED_MULTILINE='s@#define ([[:upper:]][[:upper:]0-9_]*).*@#ifdef \1\ { "\1", (unsigned long) \1 },\ #endif // \1@' # Passes the previous list of #includes to the C preprocessor and prints out # all #defines whose name is all-caps. Excludes a few symbols that are known # macro functions that don't evaluate to a constant. cat <<-EOF > "${OUTFILE}" /* GENERATED BY MAKEFILE */ #include "gen_constants-inl.h" #include "libconstants.h" const struct constant_entry constant_table[] = { $(build | \ grep -E '^#define [[:upper:]][[:upper:]0-9_]*[[:space:]]+[[:alnum:]_(]' | \ grep -Ev '(FLT_|HOST_|SIGRTMAX|SIGRTMIN|SIG_|NULL)' | \ sort -u | \ sed -Ee "${SED_MULTILINE}") { NULL, 0 }, }; EOF minijail-2026.05.18+git20261016.8787d1a/gen_syscalls-inl.h000066400000000000000000000043321526451555500217540ustar00rootroot00000000000000/* Copyright 2014 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include /* Ideally minijail is compiled against a modern libc, which has modern copies * of Linux uapi for ioctls, and unistd.h for syscalls. However, sometimes this * isn't possible - such as when building with the Android host toolchain - so * locally define the system calls in use in active seccomp policy files. * This UAPI is taken from sanitized bionic headers. */ #ifndef __NR_copy_file_range #ifdef __x86_64__ #define __NR_copy_file_range 326 #elif __i386__ #define __NR_copy_file_range 377 #elif __arm64__ #define __NR_copy_file_range 285 #endif #endif /* __NR_copy_file_range */ #ifndef __NR_getrandom #ifdef __x86_64__ #define __NR_getrandom 318 #elif __i386__ #define __NR_getrandom 355 #elif __arm64__ #define __NR_getrandom 278 #endif #endif /* __NR_getrandom */ #ifndef __NR_memfd_create #ifdef __x86_64__ #define __NR_memfd_create 319 #elif __i386__ #define __NR_memfd_create 356 #elif __arm64__ #define __NR_memfd_create 279 #endif #endif /* __NR_memfd_create */ #ifndef __NR_renameat2 #ifdef __x86_64__ #define __NR_renameat2 316 #elif __i386__ #define __NR_renameat2 353 #elif __arm64__ #define __NR_renameat2 276 #endif #endif /* __NR_renameat2 */ #ifndef __NR_statx #ifdef __x86_64__ #define __NR_statx 332 #elif __i386__ #define __NR_statx 383 #elif __arm64__ #define __NR_statx 291 #endif #endif /* __NR_statx */ #ifndef __NR_io_uring_enter #define __NR_io_uring_enter 426 #endif #ifndef __NR_io_uring_register #define __NR_io_uring_register 427 #endif #ifndef __NR_io_uring_setup #define __NR_io_uring_setup 425 #endif #ifndef __NR_faccessat2 #define __NR_faccessat2 439 #endif #ifndef __NR_rseq #ifdef __x86_64__ #define __NR_rseq 334 #elif __i386__ #define __NR_rseq 386 #elif __arm64__ #define __NR_rseq 293 #endif #endif /* __NR_rseq */ #ifndef __NR_clone3 #define __NR_clone3 435 #endif #ifndef __NR_userfaultfd #ifdef __x86_64__ #define __NR_userfaultfd 323 #elif __i386__ #define __NR_userfaultfd 374 #elif __arm64__ #define __NR_userfaultfd 282 #endif #endif /* __NR_userfaultfd */ #ifndef __NR_mseal #define __NR_mseal 462 #endif /* __NR_mseal */ minijail-2026.05.18+git20261016.8787d1a/gen_syscalls.c000066400000000000000000000000301526451555500211560ustar00rootroot00000000000000#include minijail-2026.05.18+git20261016.8787d1a/gen_syscalls.sh000077500000000000000000000031211526451555500213550ustar00rootroot00000000000000#!/bin/sh # Copyright 2012 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Generates a header file with a system call table made up of "name", # syscall_nr entries by including the build target and # emitting the list of defines. Use of the compiler is needed to # dereference the actual provider of syscall definitions. # E.g., asm/unistd_32.h or asm/unistd_64.h, etc. set -e if [ $# -ne 1 ] && [ $# -ne 2 ]; then echo "Usage: $(basename "$0") OUTFILE" echo "Usage: $(basename "$0") INFILE OUTFILE" exit 1 fi build() { ${CC:-cc} -dD "${SRC:-.}"/gen_syscalls.c -E "$@" } GEN_DEPS=1 if [ $# -eq 2 ]; then build() { cat "${CAT_FILE}" } CAT_FILE="$1" GEN_DEPS=0 shift fi OUTFILE="$1" if [ "${GEN_DEPS}" -eq 1 ]; then # Generate a dependency file which helps the build tool to see when it # should regenerate ${OUTFILE}. build -M -MF "${OUTFILE}.d" fi # sed expression which extracts system calls that are # defined via asm/unistd.h. It converts them from: # #define __NR_read foo # to: # #ifdef __NR_read # { "read", __NR_read }, # #endif SED_MULTILINE='s/#define __(ARM_)?(NR_)([[:lower:]0-9_]*) (.*)$/#ifdef __\1\2\3\ { "\1\3", __\1\2\3 },\ #endif/g p;' cat <<-EOF > "${OUTFILE}" /* GENERATED BY MAKEFILE */ #include #include "gen_syscalls-inl.h" #include "libsyscalls.h" const struct syscall_entry syscall_table[] = { $(build | sed -Ene "${SED_MULTILINE}") { NULL, -1 }, }; const size_t syscall_table_size = sizeof(syscall_table) / sizeof(syscall_table[0]); EOF minijail-2026.05.18+git20261016.8787d1a/get_googletest.sh000077500000000000000000000005211526451555500217030ustar00rootroot00000000000000#!/bin/bash # Copyright 2017 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. PV="1.16.0" wget -q -nc --secure-protocol=TLSv1 \ "https://github.com/google/googletest/archive/v${PV}.tar.gz" \ -O "googletest-${PV}.tar.gz" tar zxvf "googletest-${PV}.tar.gz" minijail-2026.05.18+git20261016.8787d1a/landlock.h000066400000000000000000000067561526451555500203110ustar00rootroot00000000000000/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* * Landlock system definitions. * * These definitions are based on . However, because we * can't guarantee that header will be available on all systems that need to * build Minijail, they are extracted here. */ #ifndef _LANDLOCK_H #define _LANDLOCK_H #include /** * struct landlock_ruleset_attr - Ruleset definition * * Argument of sys_landlock_create_ruleset(). This structure can grow in * future versions. */ struct minijail_landlock_ruleset_attr { /** * @handled_access_fs: Bitmask of actions (cf. `Filesystem flags`_) * that is handled by this ruleset and should then be forbidden if no * rule explicitly allow them. This is needed for backward * compatibility reasons. */ __u64 handled_access_fs; }; /* * sys_landlock_create_ruleset() flags: * * - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI * version. */ #ifndef LANDLOCK_CREATE_RULESET_VERSION #define LANDLOCK_CREATE_RULESET_VERSION (1U << 0) #endif /** * enum landlock_rule_type - Landlock rule type * * Argument of sys_landlock_add_rule(). */ enum minijail_landlock_rule_type { /** * @LANDLOCK_RULE_PATH_BENEATH: Type of a &struct * landlock_path_beneath_attr . */ LANDLOCK_RULE_PATH_BENEATH = 1, }; /** * struct landlock_path_beneath_attr - Path hierarchy definition * * Argument of sys_landlock_add_rule(). */ struct minijail_landlock_path_beneath_attr { /** * @allowed_access: Bitmask of allowed actions for this file hierarchy * (cf. `Filesystem flags`_). */ __u64 allowed_access; /** * @parent_fd: File descriptor, open with ``O_PATH``, which identifies * the parent directory of a file hierarchy, or just a file. */ __s32 parent_fd; /* * This struct is packed to avoid trailing reserved members. * Cf. security/landlock/syscalls.c:build_check_abi() */ } __attribute__((__packed__)); #ifndef LANDLOCK_ACCESS_FS_EXECUTE #define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0) #endif #ifndef LANDLOCK_ACCESS_FS_WRITE_FILE #define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1) #endif #ifndef LANDLOCK_ACCESS_FS_READ_FILE #define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2) #endif #ifndef LANDLOCK_ACCESS_FS_READ_DIR #define LANDLOCK_ACCESS_FS_READ_DIR (1ULL << 3) #endif #ifndef LANDLOCK_ACCESS_FS_REMOVE_DIR #define LANDLOCK_ACCESS_FS_REMOVE_DIR (1ULL << 4) #endif #ifndef LANDLOCK_ACCESS_FS_REMOVE_FILE #define LANDLOCK_ACCESS_FS_REMOVE_FILE (1ULL << 5) #endif #ifndef LANDLOCK_ACCESS_FS_MAKE_CHAR #define LANDLOCK_ACCESS_FS_MAKE_CHAR (1ULL << 6) #endif #ifndef LANDLOCK_ACCESS_FS_MAKE_DIR #define LANDLOCK_ACCESS_FS_MAKE_DIR (1ULL << 7) #endif #ifndef LANDLOCK_ACCESS_FS_MAKE_REG #define LANDLOCK_ACCESS_FS_MAKE_REG (1ULL << 8) #endif #ifndef LANDLOCK_ACCESS_FS_MAKE_SOCK #define LANDLOCK_ACCESS_FS_MAKE_SOCK (1ULL << 9) #endif #ifndef LANDLOCK_ACCESS_FS_MAKE_FIFO #define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10) #endif #ifndef LANDLOCK_ACCESS_FS_MAKE_BLOCK #define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11) #endif #ifndef LANDLOCK_ACCESS_FS_MAKE_SYM #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) #endif #ifndef LANDLOCK_ACCESS_FS_REFER #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) #endif #ifndef LANDLOCK_ACCESS_FS_TRUNCATE #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) #endif #ifndef LANDLOCK_ACCESS_FS_IOCTL_DEV #define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15) #endif #endif /* _LANDLOCK_H */ minijail-2026.05.18+git20261016.8787d1a/landlock_util.c000066400000000000000000000063661526451555500213360ustar00rootroot00000000000000/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* Define _GNU_SOURCE because we need O_PATH to resolve correctly. */ #define _GNU_SOURCE #include "landlock_util.h" #include #include #include #include "util.h" int landlock_create_ruleset( const struct minijail_landlock_ruleset_attr *const attr, const size_t size, const __u32 flags) { return syscall(__NR_landlock_create_ruleset, attr, size, flags); } int landlock_add_rule(const int ruleset_fd, const enum minijail_landlock_rule_type rule_type, const void *const rule_attr, const __u32 flags) { return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, flags); } int landlock_restrict_self(const int ruleset_fd, const __u32 flags) { return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); } uint64_t landlock_get_handled_fs_mask(const int abi) { static const uint64_t masks[] = { [0] = 0, [1] = HANDLED_ACCESS_TYPES, [2] = HANDLED_ACCESS_TYPES | LANDLOCK_ACCESS_FS_REFER, [3] = HANDLED_ACCESS_TYPES | LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_TRUNCATE, [4] = HANDLED_ACCESS_TYPES | LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_TRUNCATE, [5] = HANDLED_ACCESS_TYPES | LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV, }; const size_t max_supported_abi = sizeof(masks) / sizeof(masks[0]) - 1; if (abi < 0) { return 0; } size_t index = MIN((size_t)abi, max_supported_abi); return masks[index]; } uint64_t landlock_get_rw_mask(const int abi) { uint64_t mask = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_BASIC_WRITE; if (abi >= LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED) { mask |= LANDLOCK_ACCESS_FS_TRUNCATE; } return mask; } uint64_t landlock_get_advanced_rw_mask(const int abi) { uint64_t mask = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_FULL_WRITE; if (abi >= LANDLOCK_ABI_FS_REFER_SUPPORTED) { mask |= LANDLOCK_ACCESS_FS_REFER; } if (abi >= LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED) { mask |= LANDLOCK_ACCESS_FS_TRUNCATE; } if (abi >= LANDLOCK_ABI_FS_IOCTL_DEV_SUPPORTED) { mask |= LANDLOCK_ACCESS_FS_IOCTL_DEV; } return mask; } uint64_t landlock_get_edit_mask(const int abi) { uint64_t mask = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_EDIT; if (abi >= LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED) { mask |= LANDLOCK_ACCESS_FS_TRUNCATE; } return mask; } bool populate_ruleset_internal(const char *const path, const int ruleset_fd, const uint64_t allowed_access) { struct minijail_landlock_path_beneath_attr path_beneath = { .parent_fd = -1, }; struct stat statbuf; attribute_cleanup_fd int parent_fd = open(path, O_PATH | O_CLOEXEC); path_beneath.parent_fd = parent_fd; if (path_beneath.parent_fd < 0) { pwarn("Failed to open \"%s\"", path); return false; } if (fstat(path_beneath.parent_fd, &statbuf)) { return false; } path_beneath.allowed_access = allowed_access; if (!S_ISDIR(statbuf.st_mode)) { path_beneath.allowed_access &= ACCESS_FILE; } if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0)) { pwarn("Failed to update ruleset \"%s\"", path); return false; } return true; } minijail-2026.05.18+git20261016.8787d1a/landlock_util.h000066400000000000000000000102161526451555500213300ustar00rootroot00000000000000/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* * Landlock functions and constants. */ #ifndef _LANDLOCK_UTIL_H_ #define _LANDLOCK_UTIL_H_ #include #include #include #include #include "landlock.h" #ifdef __cplusplus extern "C" { #endif #ifndef __NR_landlock_create_ruleset #define __NR_landlock_create_ruleset 444 #endif #ifndef __NR_landlock_add_rule #define __NR_landlock_add_rule 445 #endif #ifndef __NR_landlock_restrict_self #define __NR_landlock_restrict_self 446 #endif #define ACCESS_FS_ROUGHLY_READ \ (LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR) #define ACCESS_FS_ROUGHLY_READ_EXECUTE \ (LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_READ_DIR) #define ACCESS_FS_ROUGHLY_BASIC_WRITE \ (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | \ LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_DIR | \ LANDLOCK_ACCESS_FS_MAKE_REG) #define ACCESS_FS_ROUGHLY_EDIT \ (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | \ LANDLOCK_ACCESS_FS_REMOVE_FILE) #define ACCESS_FS_ROUGHLY_FULL_WRITE \ (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | \ LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_CHAR | \ LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | \ LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | \ LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM) #define ACCESS_FILE \ (LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_TRUNCATE | \ LANDLOCK_ACCESS_FS_IOCTL_DEV) #define HANDLED_ACCESS_TYPES \ (ACCESS_FS_ROUGHLY_READ_EXECUTE | ACCESS_FS_ROUGHLY_FULL_WRITE) #define LANDLOCK_ABI_FS_REFER_SUPPORTED 2 #define LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED 3 #define LANDLOCK_ABI_FS_IOCTL_DEV_SUPPORTED 5 #define LANDLOCK_ABI_DEFAULT 2 /* * Performs Landlock create ruleset syscall. * * Returns the ruleset file descriptor on success, returns an error code * otherwise. */ extern int landlock_create_ruleset(const struct minijail_landlock_ruleset_attr *const attr, const size_t size, const __u32 flags); /* Performs Landlock add rule syscall. */ extern int landlock_add_rule(const int ruleset_fd, const enum minijail_landlock_rule_type rule_type, const void *const rule_attr, const __u32 flags); /* Performs Landlock restrict self syscall. */ extern int landlock_restrict_self(const int ruleset_fd, const __u32 flags); /* Returns the handled filesystem access mask for the given ABI version. */ extern uint64_t landlock_get_handled_fs_mask(const int abi); /* * Returns the filesystem access mask for RW paths for the given ABI version. */ extern uint64_t landlock_get_rw_mask(const int abi); /* * Returns the filesystem access mask for advanced RW paths for the given ABI * version. */ extern uint64_t landlock_get_advanced_rw_mask(const int abi); /* * Returns the filesystem access mask for edit paths for the given ABI version. */ extern uint64_t landlock_get_edit_mask(const int abi); /* * Populates the landlock ruleset for a path and any needed paths beneath. * * |allowed_access| should already be masked to only include flags supported by * the effective Landlock ABI. If |path| is not a directory, directory-only * flags will be filtered out using |ACCESS_FILE| (which contains all possible * file access rights, including those from newer ABI versions). */ extern bool populate_ruleset_internal(const char *const path, const int ruleset_fd, const uint64_t allowed_access); #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* _LANDLOCK_UTIL_H_ */ minijail-2026.05.18+git20261016.8787d1a/libconstants.h000066400000000000000000000005711526451555500212120ustar00rootroot00000000000000/* Copyright 2015 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef MINIJAIL_LIBCONSTANTS_H_ #define MINIJAIL_LIBCONSTANTS_H_ struct constant_entry { const char *name; unsigned long value; }; extern const struct constant_entry constant_table[]; #endif /* MINIJAIL_LIBCONSTANTS_H_ */ minijail-2026.05.18+git20261016.8787d1a/libminijail-private.h000066400000000000000000000056111526451555500224420ustar00rootroot00000000000000/* libminijail-private.h * Copyright 2011 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Values shared between libminijailpreload and libminijail, but not visible to * the outside world. */ #ifndef LIBMINIJAIL_PRIVATE_H #define LIBMINIJAIL_PRIVATE_H #include #ifdef __cplusplus extern "C" { #endif /* Explicitly declare exported functions so that -fvisibility tricks * can be used for testing and minimal symbol leakage occurs. */ #define API __attribute__((__visibility__("default"))) static const char kFdEnvVar[] = "__MINIJAIL_FD"; static const char kLdPreloadEnvVar[] = "LD_PRELOAD"; static const char kSeccompPolicyPathEnvVar[] = "SECCOMP_POLICY_PATH"; static const char kLoggingEnvVar[] = "__MINIJAIL_LOGGING"; static const char kLoggingEnvValueStderr[] = "stderr"; static const char kAltSyscallNamePlaceholder[] = ""; struct minijail; /* minijail_size: returns the size (in bytes) of @j if marshalled * @j jail to compute size of * * Returns 0 on error. */ extern size_t minijail_size(const struct minijail *j); /* minijail_marshal: serializes @j to @buf * @j minijail to serialize * @buf buffer to serialize to * @size size of @buf * * Returns 0 on success. * * Writes |j| to |buf| such that it can be reparsed by the same * library on the same architecture. This is meant to be used * by minijail0.c and libminijailpreload.c. minijail flags that * require minijail_run() will be excluded. * * The marshalled data is not robust to differences between the child * and parent process (personality, etc). */ extern int minijail_marshal(const struct minijail *j, char *buf, size_t size); /* minijail_unmarshal: initializes @j from @serialized * @j minijail to initialize * @serialized serialized jail buffer * @length length of buffer * * Returns 0 on success. */ extern int minijail_unmarshal(struct minijail *j, char *serialized, size_t length); /* minijail_from_fd: builds @j from @fd * @j minijail to initialize * @fd fd to initialize from * * Returns 0 on success. */ extern int minijail_from_fd(int fd, struct minijail *j); /* minijail_to_fd: sends @j over @fd * @j minijail to send * @fd fd to send over * * Returns 0 on success, or a negative error code on error. */ extern int minijail_to_fd(struct minijail *j, int fd); /* minijail_preexec: strips @j of all options handled by minijail_enter() * @j jail to strip */ extern void minijail_preexec(struct minijail *j); /* minijail_preenter: strips @j of all options handled by minijail_run() * @j jail to strip */ extern void minijail_preenter(struct minijail *j); /* minijail_fd_is_open: returns true if the specified file descriptor is open. */ extern int minijail_fd_is_open(int fd); #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* !LIBMINIJAIL_PRIVATE_H */ minijail-2026.05.18+git20261016.8787d1a/libminijail.c000066400000000000000000003432521526451555500207730ustar00rootroot00000000000000/* Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #define _BSD_SOURCE #define _DEFAULT_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "landlock_util.h" #include "libminijail-private.h" #include "libminijail.h" #include "signal_handler.h" #include "syscall_filter.h" #include "syscall_wrapper.h" #include "system.h" #include "util.h" /* Until these are reliably available in linux/prctl.h. */ #ifndef PR_ALT_SYSCALL #define PR_ALT_SYSCALL 0x43724f53 #endif /* New cgroup namespace might not be in linux-headers yet. */ #ifndef CLONE_NEWCGROUP #define CLONE_NEWCGROUP 0x02000000 #endif #define MAX_CGROUPS 10 /* 10 different controllers supported by Linux. */ #define MAX_RLIMITS 32 /* Currently there are 15 supported by Linux. */ #define MAX_PRESERVED_FDS 256U /* Paths to newuidmap/newgidmap helper binaries for unprivileged ID mapping. */ #ifndef NEWUIDMAP_PATH #define NEWUIDMAP_PATH "/usr/bin/newuidmap" #endif #ifndef NEWGIDMAP_PATH #define NEWGIDMAP_PATH "/usr/bin/newgidmap" #endif /* Keyctl commands. */ #define KEYCTL_JOIN_SESSION_KEYRING 1 /* * The userspace equivalent of MNT_USER_SETTABLE_MASK, which is the mask of all * flags that can be modified by MS_REMOUNT. */ #define MS_USER_SETTABLE_MASK \ (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME | MS_NODIRATIME | \ MS_RELATIME | MS_RDONLY) /* * Required for Android host glibc which is permanently stuck on 2.17. Causes * no harm for newer glibc versions. */ #ifndef MS_NOSYMFOLLOW /* Added locally in kernels 4.x+. */ #define MS_NOSYMFOLLOW 256 #endif struct minijail_rlimit { int type; rlim_t cur; rlim_t max; }; struct mountpoint { char *src; char *dest; char *type; char *data; int has_data; unsigned long flags; struct mountpoint *next; }; struct minijail_remount { unsigned long remount_mode; char *mount_name; struct minijail_remount *next; }; struct hook { minijail_hook_t hook; void *payload; minijail_hook_event_t event; struct hook *next; }; struct fs_rule { char *path; uint64_t landlock_flags; struct fs_rule *next; }; struct preserved_fd { int parent_fd; int child_fd; }; /* * minijail struct: new fields should either be marshaled/unmarshaled or have a * comment explaining why that's unnecessary. */ struct minijail { /* * WARNING: new bool flags should always be added to this struct, * unless you’re certain they don’t need to remain after marshaling. * If you add a flag here you need to make sure it's * accounted for in minijail_pre{enter|exec}() below. */ struct { bool uid : 1; bool gid : 1; bool inherit_suppl_gids : 1; bool set_suppl_gids : 1; bool keep_suppl_gids : 1; bool use_caps : 1; bool capbset_drop : 1; bool set_ambient_caps : 1; bool vfs : 1; bool enter_vfs : 1; bool pids : 1; bool ipc : 1; bool uts : 1; bool net : 1; bool net_loopback : 1; bool enter_net : 1; bool ns_cgroups : 1; bool userns : 1; bool disable_setgroups : 1; bool seccomp : 1; bool remount_proc_ro : 1; bool no_new_privs : 1; bool seccomp_filter : 1; bool seccomp_filter_tsync : 1; bool seccomp_filter_logging : 1; bool seccomp_filter_allow_speculation : 1; bool chroot : 1; bool pivot_root : 1; bool mount_dev : 1; bool mount_tmp : 1; bool do_init : 1; bool run_as_init : 1; bool pid_file : 1; bool cgroups : 1; bool alt_syscall : 1; bool reset_signal_mask : 1; bool reset_signal_handlers : 1; bool close_open_fds : 1; bool new_session_keyring : 1; bool forward_signals : 1; bool setsid : 1; bool using_minimalistic_mountns : 1; bool enable_fs_restrictions : 1; bool enable_profile_fs_restrictions : 1; bool enable_default_runtime : 1; bool enable_new_sessions : 1; } flags; uid_t uid; gid_t gid; gid_t usergid; char *user; size_t suppl_gid_count; gid_t *suppl_gid_list; uint64_t caps; uint64_t cap_bset; pid_t initpid; int mountns_fd; int netns_fd; int fs_rules_fd; int effective_landlock_abi; int target_landlock_abi; char *chrootdir; char *pid_file_path; char *uidmap; char *gidmap; char *hostname; char *preload_path; /* * Filename that will be executed, unless an ELF fd is used instead. * This field is only used for logs and isn't included in marshaling. */ char *filename; size_t filter_len; struct sock_fprog *filter_prog; char *alt_syscall_table; struct mountpoint *mounts_head; struct mountpoint *mounts_tail; size_t mounts_count; unsigned long remount_mode; struct minijail_remount *remounts_head; struct minijail_remount *remounts_tail; size_t tmpfs_size; struct fs_rule *fs_rules_head; struct fs_rule *fs_rules_tail; size_t fs_rules_count; char *cgroups[MAX_CGROUPS]; size_t cgroup_count; struct minijail_rlimit rlimits[MAX_RLIMITS]; size_t rlimit_count; uint64_t securebits_skip_mask; struct hook *hooks_head; struct hook *hooks_tail; struct preserved_fd preserved_fds[MAX_PRESERVED_FDS]; size_t preserved_fd_count; char *seccomp_policy_path; }; static void run_hooks_or_die(const struct minijail *j, minijail_hook_event_t event); static bool seccomp_is_logging_allowed(const struct minijail *j) { return seccomp_default_ret_log() || j->flags.seccomp_filter_logging; } static void free_mounts_list(struct minijail *j) { while (j->mounts_head) { struct mountpoint *m = j->mounts_head; j->mounts_head = j->mounts_head->next; free(m->data); free(m->type); free(m->dest); free(m->src); free(m); } // No need to clear mounts_head as we know it's NULL after the loop. j->mounts_tail = NULL; } static void free_remounts_list(struct minijail *j) { while (j->remounts_head) { struct minijail_remount *m = j->remounts_head; j->remounts_head = j->remounts_head->next; free(m->mount_name); free(m); } // No need to clear remounts_head as we know it's NULL after the loop. j->remounts_tail = NULL; } static void free_fs_rules_list(struct minijail *j) { while (j->fs_rules_head) { struct fs_rule *r = j->fs_rules_head; j->fs_rules_head = j->fs_rules_head->next; free(r->path); free(r); } j->fs_rules_tail = NULL; } /* * Writes exactly n bytes from buf to file descriptor fd. * Returns 0 on success or a negative error code on error. */ static int write_exactly(int fd, const void *buf, size_t n) { const char *p = buf; while (n > 0) { const ssize_t written = write(fd, p, n); if (written < 0) { if (errno == EINTR) continue; return -errno; } p += written; n -= written; } return 0; } /* * Reads exactly n bytes from file descriptor fd into buf. * Returns 0 on success or a negative error code on error. */ static int read_exactly(int fd, void *buf, size_t n) { char *p = buf; while (n > 0) { const ssize_t bytes = read(fd, p, n); if (bytes < 0) { if (errno == EINTR) continue; return -errno; } if (bytes == 0) { errno = EPIPE; return -EPIPE; } p += bytes; n -= bytes; } return 0; } /* Closes *pfd and sets it to -1. */ static void close_and_reset(int *pfd) { if (*pfd != -1) close(*pfd); *pfd = -1; } /* * Strip out flags meant for the parent. * We keep things that are not inherited across execve(2) (e.g. capabilities), * or are easier to set after execve(2) (e.g. seccomp filters). */ void minijail_preenter(struct minijail *j) { j->flags.vfs = 0; j->flags.enter_vfs = 0; j->flags.ns_cgroups = 0; j->flags.net = 0; j->flags.net_loopback = 0; j->flags.uts = 0; j->flags.remount_proc_ro = 0; j->flags.pids = 0; j->flags.do_init = 0; j->flags.run_as_init = 0; j->flags.pid_file = 0; j->flags.cgroups = 0; j->flags.forward_signals = 0; j->flags.setsid = 0; j->remount_mode = 0; j->flags.using_minimalistic_mountns = 0; j->flags.enable_profile_fs_restrictions = 0; j->flags.enable_default_runtime = 0; j->flags.enable_new_sessions = 0; free_remounts_list(j); } static int get_effective_landlock_abi(struct minijail *j) { if (j->effective_landlock_abi < 0) { const int abi = landlock_create_ruleset( NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); /* * If we have a valid ABI, save the result. Otherwise, use 0. */ int system_abi = abi > 0 ? abi : 0; int target = j->target_landlock_abi; if (target < 0) { target = LANDLOCK_ABI_DEFAULT; } j->effective_landlock_abi = MIN(system_abi, target); } return j->effective_landlock_abi; } /* Sets fs_rules_fd to an empty ruleset, if Landlock is available. */ static int setup_fs_rules_fd(struct minijail *j) { int effective_abi = get_effective_landlock_abi(j); struct minijail_landlock_ruleset_attr ruleset_attr = { .handled_access_fs = landlock_get_handled_fs_mask(effective_abi), }; j->fs_rules_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); if (j->fs_rules_fd < 0) { /* * As of Landlock ABI=3, the useful errors we expect here are * ENOSYS or EOPNOTSUPP. In both cases, Landlock is not * supported by the kernel and Minijail can silently ignore it. * TODO(b/300142205): log when we no longer have 5.4 kernels in * ChromeOS (~EoY 2024). */ return errno; } return 0; } /* Adds a rule for a given path to apply once minijail is entered. */ static int add_fs_restriction_path(struct minijail *j, const char *path, uint64_t landlock_flags) { struct fs_rule *r = calloc(1, sizeof(*r)); if (!r) return -ENOMEM; r->path = strdup(path); if (!r->path) { free(r); return -ENOMEM; } r->landlock_flags = landlock_flags; if (j->fs_rules_tail) { j->fs_rules_tail->next = r; j->fs_rules_tail = r; } else { j->fs_rules_head = r; j->fs_rules_tail = r; } /* * If this is our first rule, set up the rules FD early for API users. * * This is important for users calling minijail_enter() directly. * Otherise, this is handled later inside minijail_run_internal(). * * The reason for this is because setup_fs_rules_fd() needs to be * called from inside the process that applies Landlock rules. For * minijail_enter(), that's this process. For minijail_run_internal(), * that's the child process. */ if (j->fs_rules_count == 0) setup_fs_rules_fd(j); j->fs_rules_count++; return 0; } bool mount_has_bind_flag(struct mountpoint *m) { return !!(m->flags & MS_BIND); } bool mount_has_readonly_flag(struct mountpoint *m) { return !!(m->flags & MS_RDONLY); } bool mount_events_allowed(struct mountpoint *m) { return !!(m->flags & MS_SHARED) || !!(m->flags & MS_SLAVE); } /* * Strip out flags meant for the child. * We keep things that are inherited across execve(2). */ void minijail_preexec(struct minijail *j) { int vfs = j->flags.vfs; int enter_vfs = j->flags.enter_vfs; int ns_cgroups = j->flags.ns_cgroups; int net = j->flags.net; int net_loopback = j->flags.net_loopback; int uts = j->flags.uts; int remount_proc_ro = j->flags.remount_proc_ro; int userns = j->flags.userns; int using_minimalistic_mountns = j->flags.using_minimalistic_mountns; int enable_fs_restrictions = j->flags.enable_fs_restrictions; int enable_profile_fs_restrictions = j->flags.enable_profile_fs_restrictions; int enable_default_runtime = j->flags.enable_default_runtime; int enable_new_sessions = j->flags.enable_new_sessions; if (j->user) free(j->user); j->user = NULL; if (j->suppl_gid_list) free(j->suppl_gid_list); j->suppl_gid_list = NULL; if (j->preload_path) free(j->preload_path); j->preload_path = NULL; free_mounts_list(j); free_fs_rules_list(j); memset(&j->flags, 0, sizeof(j->flags)); /* Now restore anything we meant to keep. */ j->flags.vfs = vfs; j->flags.enter_vfs = enter_vfs; j->flags.ns_cgroups = ns_cgroups; j->flags.net = net; j->flags.net_loopback = net_loopback; j->flags.uts = uts; j->flags.remount_proc_ro = remount_proc_ro; j->flags.userns = userns; j->flags.using_minimalistic_mountns = using_minimalistic_mountns; j->flags.enable_fs_restrictions = enable_fs_restrictions; j->flags.enable_profile_fs_restrictions = enable_profile_fs_restrictions; j->flags.enable_default_runtime = enable_default_runtime; j->flags.enable_new_sessions = enable_new_sessions; /* Note, |pids| will already have been used before this call. */ } /* Minijail API. */ struct minijail API *minijail_new(void) { struct minijail *j = calloc(1, sizeof(struct minijail)); if (j) { j->remount_mode = MS_PRIVATE; j->fs_rules_fd = -1; j->effective_landlock_abi = -1; j->target_landlock_abi = LANDLOCK_ABI_DEFAULT; j->flags.using_minimalistic_mountns = false; j->flags.enable_fs_restrictions = true; j->flags.enable_profile_fs_restrictions = true; j->flags.enable_default_runtime = true; j->flags.enable_new_sessions = true; } return j; } void API minijail_set_landlock_abi(struct minijail *j, int abi) { j->target_landlock_abi = abi; j->effective_landlock_abi = -1; } void API minijail_change_uid(struct minijail *j, uid_t uid) { if (uid == 0) die("useless change to uid 0"); j->uid = uid; j->flags.uid = 1; } void API minijail_change_gid(struct minijail *j, gid_t gid) { if (gid == 0) die("useless change to gid 0"); j->gid = gid; j->flags.gid = 1; } void API minijail_set_supplementary_gids(struct minijail *j, size_t size, const gid_t *list) { size_t i; if (j->flags.inherit_suppl_gids) die("cannot inherit *and* set supplementary groups"); if (j->flags.keep_suppl_gids) die("cannot keep *and* set supplementary groups"); if (size == 0) { /* Clear supplementary groups. */ j->suppl_gid_list = NULL; j->suppl_gid_count = 0; j->flags.set_suppl_gids = 1; return; } /* Copy the gid_t array. */ j->suppl_gid_list = calloc(size, sizeof(gid_t)); if (!j->suppl_gid_list) { die("failed to allocate internal supplementary group array"); } for (i = 0; i < size; i++) { j->suppl_gid_list[i] = list[i]; } j->suppl_gid_count = size; j->flags.set_suppl_gids = 1; } void API minijail_keep_supplementary_gids(struct minijail *j) { j->flags.keep_suppl_gids = 1; } int API minijail_change_user(struct minijail *j, const char *user) { uid_t uid; gid_t gid; int rc = lookup_user(user, &uid, &gid); if (rc) return rc; minijail_change_uid(j, uid); j->user = strdup(user); if (!j->user) return -ENOMEM; j->usergid = gid; return 0; } int API minijail_change_group(struct minijail *j, const char *group) { gid_t gid; int rc = lookup_group(group, &gid); if (rc) return rc; minijail_change_gid(j, gid); return 0; } void API minijail_use_seccomp(struct minijail *j) { j->flags.seccomp = 1; } void API minijail_no_new_privs(struct minijail *j) { j->flags.no_new_privs = 1; } void API minijail_use_seccomp_filter(struct minijail *j) { j->flags.seccomp_filter = 1; } void API minijail_set_seccomp_filter_tsync(struct minijail *j) { if (j->filter_len > 0 && j->filter_prog != NULL) { die("minijail_set_seccomp_filter_tsync() must be called " "before minijail_parse_seccomp_filters()"); } if (seccomp_is_logging_allowed(j) && !seccomp_ret_log_available()) { /* * If SECCOMP_RET_LOG is not available, we don't want to use * SECCOMP_RET_TRAP to both kill the entire process and report * failing syscalls, since it will be brittle. Just bail. */ die("SECCOMP_RET_LOG not available, cannot use logging with " "thread sync at the same time"); } j->flags.seccomp_filter_tsync = 1; } void API minijail_set_seccomp_filter_allow_speculation(struct minijail *j) { if (j->filter_len > 0 && j->filter_prog != NULL) { die("minijail_set_seccomp_filter_allow_speculation() must be " "called before minijail_parse_seccomp_filters()"); } j->flags.seccomp_filter_allow_speculation = 1; } void API minijail_log_seccomp_filter_failures(struct minijail *j) { if (j->filter_len > 0 && j->filter_prog != NULL) { die("minijail_log_seccomp_filter_failures() must be called " "before minijail_parse_seccomp_filters()"); } if (j->flags.seccomp_filter_tsync && !seccomp_ret_log_available()) { /* * If SECCOMP_RET_LOG is not available, we don't want to use * SECCOMP_RET_TRAP to both kill the entire process and report * failing syscalls, since it will be brittle. Just bail. */ die("SECCOMP_RET_LOG not available, cannot use thread sync " "with logging at the same time"); } if (debug_logging_allowed()) { j->flags.seccomp_filter_logging = 1; } else { warn("non-debug build: ignoring request to enable seccomp " "logging"); } } void API minijail_set_using_minimalistic_mountns(struct minijail *j) { j->flags.using_minimalistic_mountns = true; } void API minijail_set_enable_new_sessions(struct minijail *j, bool enable_new_sessions) { j->flags.enable_new_sessions = enable_new_sessions; } void API minijail_set_enable_default_runtime(struct minijail *j, bool enable_default_runtime) { j->flags.enable_default_runtime = enable_default_runtime; } bool API minijail_get_enable_default_runtime(struct minijail *j) { return j->flags.enable_default_runtime; } bool API minijail_is_fs_restriction_available(void) { const int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); // ABI > 0 is considered supported. return abi > 0; } void API minijail_disable_fs_restrictions(struct minijail *j) { j->flags.enable_fs_restrictions = false; } void API minijail_set_enable_profile_fs_restrictions(struct minijail *j) { j->flags.enable_profile_fs_restrictions = true; } void API minijail_add_minimalistic_mountns_fs_rules(struct minijail *j) { struct mountpoint *m = j->mounts_head; bool landlock_enabled_by_profile = false; if (!j->flags.using_minimalistic_mountns || !j->flags.enable_profile_fs_restrictions) return; /* Apply Landlock rules. */ while (m) { landlock_enabled_by_profile = true; minijail_add_fs_restriction_rx(j, m->dest); /* * Allow rw if mounted as writable, or mount flags allow mount * events. */ if (!mount_has_readonly_flag(m) || mount_events_allowed(m)) minijail_add_fs_restriction_advanced_rw(j, m->dest); m = m->next; } if (landlock_enabled_by_profile) { minijail_enable_default_fs_restrictions(j); minijail_add_fs_restriction_edit(j, "/dev"); minijail_add_fs_restriction_ro(j, "/proc"); if (j->flags.vfs) minijail_add_fs_restriction_rw(j, "/tmp"); } } void API minijail_enable_default_fs_restrictions(struct minijail *j) { // Common library locations. minijail_add_fs_restriction_rx(j, "/lib"); minijail_add_fs_restriction_rx(j, "/lib64"); minijail_add_fs_restriction_rx(j, "/usr/lib"); minijail_add_fs_restriction_rx(j, "/usr/lib64"); // Common locations for services invoking Minijail. minijail_add_fs_restriction_rx(j, "/bin"); minijail_add_fs_restriction_rx(j, "/sbin"); minijail_add_fs_restriction_rx(j, "/usr/sbin"); minijail_add_fs_restriction_rx(j, "/usr/bin"); // Common /etc locations. minijail_add_fs_restriction_ro(j, "/etc/group"); minijail_add_fs_restriction_ro(j, "/etc/passwd"); } void API minijail_use_caps(struct minijail *j, uint64_t capmask) { /* * 'minijail_use_caps' configures a runtime-capabilities-only * environment, including a bounding set matching the thread's runtime * (permitted|inheritable|effective) sets. * Therefore, it will override any existing bounding set configurations * since the latter would allow gaining extra runtime capabilities from * file capabilities. */ if (j->flags.capbset_drop) { warn("overriding bounding set configuration"); j->cap_bset = 0; j->flags.capbset_drop = 0; } j->caps = capmask; j->flags.use_caps = 1; } void API minijail_capbset_drop(struct minijail *j, uint64_t capmask) { if (j->flags.use_caps) { /* * 'minijail_use_caps' will have already configured a capability * bounding set matching the (permitted|inheritable|effective) * sets. Abort if the user tries to configure a separate * bounding set. 'minijail_capbset_drop' and 'minijail_use_caps' * are mutually exclusive. */ die("runtime capabilities already configured, can't drop " "bounding set separately"); } j->cap_bset = capmask; j->flags.capbset_drop = 1; } void API minijail_set_ambient_caps(struct minijail *j) { j->flags.set_ambient_caps = 1; } void API minijail_reset_signal_mask(struct minijail *j) { j->flags.reset_signal_mask = 1; } void API minijail_reset_signal_handlers(struct minijail *j) { j->flags.reset_signal_handlers = 1; } void API minijail_namespace_vfs(struct minijail *j) { j->flags.vfs = 1; } void API minijail_namespace_enter_vfs(struct minijail *j, const char *ns_path) { /* Note: Do not use O_CLOEXEC here. We'll close it after we use it. */ int ns_fd = open(ns_path, O_RDONLY); if (ns_fd < 0) { pdie("failed to open namespace '%s'", ns_path); } j->mountns_fd = ns_fd; j->flags.enter_vfs = 1; } void API minijail_new_session_keyring(struct minijail *j) { j->flags.new_session_keyring = 1; } void API minijail_skip_setting_securebits(struct minijail *j, uint64_t securebits_skip_mask) { j->securebits_skip_mask = securebits_skip_mask; } void API minijail_remount_mode(struct minijail *j, unsigned long mode) { j->remount_mode = mode; } void API minijail_skip_remount_private(struct minijail *j) { j->remount_mode = 0; } void API minijail_namespace_pids(struct minijail *j) { j->flags.vfs = 1; j->flags.remount_proc_ro = 1; j->flags.pids = 1; j->flags.do_init = 1; } void API minijail_namespace_pids_rw_proc(struct minijail *j) { j->flags.vfs = 1; j->flags.pids = 1; j->flags.do_init = 1; } void API minijail_namespace_ipc(struct minijail *j) { j->flags.ipc = 1; } void API minijail_namespace_uts(struct minijail *j) { j->flags.uts = 1; } int API minijail_namespace_set_hostname(struct minijail *j, const char *name) { if (j->hostname) return -EINVAL; minijail_namespace_uts(j); j->hostname = strdup(name); if (!j->hostname) return -ENOMEM; return 0; } void API minijail_namespace_net_loopback(struct minijail *j, bool enable_loopback) { j->flags.net = 1; j->flags.net_loopback = enable_loopback; } void API minijail_namespace_net(struct minijail *j) { minijail_namespace_net_loopback(j, true); } void API minijail_namespace_enter_net(struct minijail *j, const char *ns_path) { /* Note: Do not use O_CLOEXEC here. We'll close it after we use it. */ int ns_fd = open(ns_path, O_RDONLY); if (ns_fd < 0) { pdie("failed to open namespace '%s'", ns_path); } j->netns_fd = ns_fd; j->flags.enter_net = 1; } void API minijail_namespace_cgroups(struct minijail *j) { j->flags.ns_cgroups = 1; } void API minijail_close_open_fds(struct minijail *j) { j->flags.close_open_fds = 1; } void API minijail_remount_proc_readonly(struct minijail *j) { j->flags.vfs = 1; j->flags.remount_proc_ro = 1; } void API minijail_namespace_user(struct minijail *j) { j->flags.userns = 1; } void API minijail_namespace_user_disable_setgroups(struct minijail *j) { j->flags.disable_setgroups = 1; } int API minijail_uidmap(struct minijail *j, const char *uidmap) { j->uidmap = strdup(uidmap); if (!j->uidmap) return -ENOMEM; char *ch; for (ch = j->uidmap; *ch; ch++) { if (*ch == ',') *ch = '\n'; } return 0; } int API minijail_gidmap(struct minijail *j, const char *gidmap) { j->gidmap = strdup(gidmap); if (!j->gidmap) return -ENOMEM; char *ch; for (ch = j->gidmap; *ch; ch++) { if (*ch == ',') *ch = '\n'; } return 0; } void API minijail_inherit_usergroups(struct minijail *j) { j->flags.inherit_suppl_gids = 1; } void API minijail_run_as_init(struct minijail *j) { /* * Since the jailed program will become 'init' in the new PID namespace, * Minijail does not need to fork an 'init' process. */ j->flags.run_as_init = 1; } int API minijail_enter_chroot(struct minijail *j, const char *dir) { if (j->chrootdir) return -EINVAL; j->chrootdir = strdup(dir); if (!j->chrootdir) return -ENOMEM; j->flags.chroot = 1; return 0; } int API minijail_enter_pivot_root(struct minijail *j, const char *dir) { if (j->chrootdir) return -EINVAL; j->chrootdir = strdup(dir); if (!j->chrootdir) return -ENOMEM; j->flags.pivot_root = 1; return 0; } char API *minijail_get_original_path(struct minijail *j, const char *path_inside_chroot) { struct mountpoint *b; b = j->mounts_head; while (b) { /* * If |path_inside_chroot| is the exact destination of a * mount, then the original path is exactly the source of * the mount. * for example: "-b /some/path/exe,/chroot/path/exe" * mount source = /some/path/exe, mount dest = * /chroot/path/exe Then when getting the original path of * "/chroot/path/exe", the source of that mount, * "/some/path/exe" is what should be returned. */ if (streq(b->dest, path_inside_chroot)) return strdup(b->src); /* * If |path_inside_chroot| is within the destination path of a * mount, take the suffix of the chroot path relative to the * mount destination path, and append it to the mount source * path. */ if (!strncmp(b->dest, path_inside_chroot, strlen(b->dest))) { const char *relative_path = path_inside_chroot + strlen(b->dest); return path_join(b->src, relative_path); } b = b->next; } /* If there is a chroot path, append |path_inside_chroot| to that. */ if (j->chrootdir) return path_join(j->chrootdir, path_inside_chroot); /* No chroot, so the path outside is the same as it is inside. */ return strdup(path_inside_chroot); } void API minijail_mount_dev(struct minijail *j) { j->flags.mount_dev = 1; } void API minijail_mount_tmp(struct minijail *j) { minijail_mount_tmp_size(j, 64 * 1024 * 1024); } void API minijail_mount_tmp_size(struct minijail *j, size_t size) { j->tmpfs_size = size; j->flags.mount_tmp = 1; } int API minijail_write_pid_file(struct minijail *j, const char *path) { j->pid_file_path = strdup(path); if (!j->pid_file_path) return -ENOMEM; j->flags.pid_file = 1; return 0; } int API minijail_add_to_cgroup(struct minijail *j, const char *path) { if (j->cgroup_count >= MAX_CGROUPS) return -ENOMEM; j->cgroups[j->cgroup_count] = strdup(path); if (!j->cgroups[j->cgroup_count]) return -ENOMEM; j->cgroup_count++; j->flags.cgroups = 1; return 0; } int API minijail_rlimit(struct minijail *j, int type, rlim_t cur, rlim_t max) { size_t i; if (j->rlimit_count >= MAX_RLIMITS) return -ENOMEM; /* It's an error if the caller sets the same rlimit multiple times. */ for (i = 0; i < j->rlimit_count; i++) { if (j->rlimits[i].type == type) return -EEXIST; } j->rlimits[j->rlimit_count].type = type; j->rlimits[j->rlimit_count].cur = cur; j->rlimits[j->rlimit_count].max = max; j->rlimit_count++; return 0; } int API minijail_forward_signals(struct minijail *j) { j->flags.forward_signals = 1; return 0; } int API minijail_create_session(struct minijail *j) { j->flags.setsid = 1; return 0; } int API minijail_add_fs_restriction_rx(struct minijail *j, const char *path) { return !add_fs_restriction_path(j, path, ACCESS_FS_ROUGHLY_READ_EXECUTE); } int API minijail_add_fs_restriction_ro(struct minijail *j, const char *path) { return !add_fs_restriction_path(j, path, ACCESS_FS_ROUGHLY_READ); } int API minijail_add_fs_restriction_rw(struct minijail *j, const char *path) { int effective_abi = get_effective_landlock_abi(j); return !add_fs_restriction_path(j, path, landlock_get_rw_mask(effective_abi)); } int API minijail_add_fs_restriction_advanced_rw(struct minijail *j, const char *path) { int effective_abi = get_effective_landlock_abi(j); return !add_fs_restriction_path( j, path, landlock_get_advanced_rw_mask(effective_abi)); } int API minijail_add_fs_restriction_edit(struct minijail *j, const char *path) { int effective_abi = get_effective_landlock_abi(j); return !add_fs_restriction_path(j, path, landlock_get_edit_mask(effective_abi)); } int API minijail_add_fs_restriction_access_rights(struct minijail *j, const char *path, uint16_t landlock_flags) { return !add_fs_restriction_path(j, path, landlock_flags); } bool API minijail_is_fs_restriction_ruleset_initialized(const struct minijail *j) { return j->fs_rules_fd >= 0; } static bool is_valid_bind_path(const char *path) { if (!block_symlinks_in_bindmount_paths()) { return true; } /* * tokenize() will modify both the |prefixes| pointer and the contents * of the string, so: * -Copy |BINDMOUNT_ALLOWED_PREFIXES| since it lives in .rodata. * -Save the original pointer for free()ing. */ char *prefixes = strdup(BINDMOUNT_ALLOWED_PREFIXES); attribute_cleanup_str char *orig_prefixes = prefixes; (void)orig_prefixes; char *prefix = NULL; bool found_prefix = false; if (!is_canonical_path(path)) { while ((prefix = tokenize(&prefixes, ",")) != NULL) { if (path_is_parent(prefix, path)) { found_prefix = true; break; } } if (!found_prefix) { /* * If the path does not include one of the allowed * prefixes, fail. */ warn("path '%s' is not a canonical path", path); return false; } } return true; } int API minijail_mount_with_data(struct minijail *j, const char *src, const char *dest, const char *type, unsigned long flags, const char *data) { struct mountpoint *m; if (*dest != '/') return -EINVAL; m = calloc(1, sizeof(*m)); if (!m) return -ENOMEM; m->dest = strdup(dest); if (!m->dest) goto error; m->src = strdup(src); if (!m->src) goto error; m->type = strdup(type); if (!m->type) goto error; if (!data || !data[0]) { /* * Set up secure defaults for certain filesystems. Adding this * fs-specific logic here kind of sucks, but considering how * people use these in practice, it's probably OK. If they want * the kernel defaults, they can pass data="" instead of NULL. */ if (streq(type, "tmpfs")) { /* tmpfs defaults to mode=1777 and size=50%. */ data = "mode=0755,size=10M"; } } if (data) { m->data = strdup(data); if (!m->data) goto error; m->has_data = 1; } /* If they don't specify any flags, default to secure ones. */ if (flags == 0) flags = MS_NODEV | MS_NOEXEC | MS_NOSUID; m->flags = flags; /* * Unless asked to enter an existing namespace, force vfs namespacing * so the mounts don't leak out into the containing vfs namespace. * If Minijail is being asked to enter the root vfs namespace this will * leak mounts, but it's unlikely that the user would ask to do that by * mistake. */ if (!j->flags.enter_vfs) minijail_namespace_vfs(j); if (j->mounts_tail) j->mounts_tail->next = m; else j->mounts_head = m; j->mounts_tail = m; j->mounts_count++; return 0; error: free(m->type); free(m->src); free(m->dest); free(m); return -ENOMEM; } int API minijail_mount(struct minijail *j, const char *src, const char *dest, const char *type, unsigned long flags) { return minijail_mount_with_data(j, src, dest, type, flags, NULL); } int API minijail_bind(struct minijail *j, const char *src, const char *dest, int writeable) { unsigned long flags = MS_BIND; /* * Check for symlinks in bind-mount source paths to warn the user early. * Minijail will perform one final check immediately before the mount() * call. */ if (!is_valid_bind_path(src)) { warn("src '%s' is not a valid bind mount path", src); return -ELOOP; } /* * Symlinks in |dest| are blocked by the ChromiumOS LSM: * /security/chromiumos/lsm.c#77 */ if (!writeable) flags |= MS_RDONLY; /* * |type| is ignored for bind mounts, use it to signal that this mount * came from minijail_bind(). * TODO(b/238362528): Implement a better way to signal this. */ return minijail_mount(j, src, dest, "minijail_bind", flags); } int API minijail_add_remount(struct minijail *j, const char *mount_name, unsigned long remount_mode) { struct minijail_remount *m; if (*mount_name != '/') return -EINVAL; m = calloc(1, sizeof(*m)); if (!m) return -ENOMEM; m->mount_name = strdup(mount_name); if (!m->mount_name) { free(m); return -ENOMEM; } m->remount_mode = remount_mode; if (j->remounts_tail) j->remounts_tail->next = m; else j->remounts_head = m; j->remounts_tail = m; return 0; } int API minijail_add_hook(struct minijail *j, minijail_hook_t hook, void *payload, minijail_hook_event_t event) { struct hook *c; if (event >= MINIJAIL_HOOK_EVENT_MAX) return -EINVAL; c = calloc(1, sizeof(*c)); if (!c) return -ENOMEM; c->hook = hook; c->payload = payload; c->event = event; if (j->hooks_tail) j->hooks_tail->next = c; else j->hooks_head = c; j->hooks_tail = c; return 0; } int API minijail_preserve_fd(struct minijail *j, int parent_fd, int child_fd) { if (parent_fd < 0 || child_fd < 0) return -EINVAL; if (j->preserved_fd_count >= MAX_PRESERVED_FDS) return -ENOMEM; j->preserved_fds[j->preserved_fd_count].parent_fd = parent_fd; j->preserved_fds[j->preserved_fd_count].child_fd = child_fd; j->preserved_fd_count++; return 0; } int API minijail_set_preload_path(struct minijail *j, const char *preload_path) { if (j->preload_path) return -EINVAL; j->preload_path = strdup(preload_path); if (!j->preload_path) return -ENOMEM; return 0; } static void clear_seccomp_options(struct minijail *j) { j->flags.seccomp_filter = 0; j->flags.seccomp_filter_tsync = 0; j->flags.seccomp_filter_logging = 0; j->flags.seccomp_filter_allow_speculation = 0; j->filter_len = 0; j->filter_prog = NULL; j->flags.no_new_privs = 0; if (j->seccomp_policy_path) { free(j->seccomp_policy_path); } j->seccomp_policy_path = NULL; } static int seccomp_should_use_filters(struct minijail *j) { if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL) == -1) { /* * |errno| will be set to EINVAL when seccomp has not been * compiled into the kernel. On certain platforms and kernel * versions this is not a fatal failure. In that case, and only * in that case, disable seccomp and skip loading the filters. */ if ((errno == EINVAL) && seccomp_can_softfail()) { warn("not loading seccomp filters, seccomp filter not " "supported"); clear_seccomp_options(j); return 0; } /* * If |errno| != EINVAL or seccomp_can_softfail() is false, * we can proceed. Worst case scenario minijail_enter() will * abort() if seccomp fails. */ } if (j->flags.seccomp_filter_tsync) { /* Are the seccomp(2) syscall and the TSYNC option supported? */ if (sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL) == -1) { int saved_errno = errno; if (saved_errno == ENOSYS && seccomp_can_softfail()) { warn("seccomp(2) syscall not supported"); clear_seccomp_options(j); return 0; } else if (saved_errno == EINVAL && seccomp_can_softfail()) { warn( "seccomp filter thread sync not supported"); clear_seccomp_options(j); return 0; } /* * Similar logic here. If seccomp_can_softfail() is * false, or |errno| != ENOSYS, or |errno| != EINVAL, * we can proceed. Worst case scenario minijail_enter() * will abort() if seccomp or TSYNC fail. */ } } if (j->flags.seccomp_filter_allow_speculation) { /* Is the SPEC_ALLOW flag supported? */ if (!seccomp_filter_flags_available( SECCOMP_FILTER_FLAG_SPEC_ALLOW)) { warn("allowing speculative execution on seccomp " "processes not supported"); j->flags.seccomp_filter_allow_speculation = 0; } } return 1; } static int set_seccomp_filters_internal(struct minijail *j, const struct sock_fprog *filter, bool owned) { struct sock_fprog *fprog; if (owned) { /* * If |owned| is true, it's OK to cast away the const-ness since * we'll own the pointer going forward. */ fprog = (struct sock_fprog *)filter; } else { fprog = malloc(sizeof(struct sock_fprog)); if (!fprog) return -ENOMEM; fprog->len = filter->len; fprog->filter = malloc(sizeof(struct sock_filter) * fprog->len); if (!fprog->filter) { free(fprog); return -ENOMEM; } memcpy(fprog->filter, filter->filter, sizeof(struct sock_filter) * fprog->len); } if (j->filter_prog) { free(j->filter_prog->filter); free(j->filter_prog); } j->filter_len = fprog->len; j->filter_prog = fprog; return 0; } static int parse_seccomp_filters(struct minijail *j, const char *filename, FILE *policy_file) { struct sock_fprog *fprog = malloc(sizeof(struct sock_fprog)); if (!fprog) return -ENOMEM; struct filter_options filteropts; /* * Figure out filter options. * Allow logging? */ filteropts.allow_logging = debug_logging_allowed() && seccomp_is_logging_allowed(j); /* What to do on a blocked system call? */ if (filteropts.allow_logging) { if (seccomp_ret_log_available()) filteropts.action = ACTION_RET_LOG; else filteropts.action = ACTION_RET_TRAP; } else { if (j->flags.seccomp_filter_tsync) { if (seccomp_ret_kill_process_available()) { filteropts.action = ACTION_RET_KILL_PROCESS; } else { filteropts.action = ACTION_RET_TRAP; } } else { filteropts.action = ACTION_RET_KILL; } } /* * When logging to the system log, if SECCOMP_RET_LOG is not available, * need to allow extra syscalls for logging. */ filteropts.allow_syscalls_for_logging = filteropts.allow_logging && !seccomp_ret_log_available() && logging_to_syslog(); /* Whether to also allow syscalls for libc compatibility. */ filteropts.include_libc_compatibility_allowlist = allow_libc_compatibility_syscalls(); /* Whether to fail on duplicate syscalls. */ filteropts.allow_duplicate_syscalls = allow_duplicate_syscalls(); if (compile_filter(filename, policy_file, fprog, &filteropts)) { free(fprog); return -1; } return set_seccomp_filters_internal(j, fprog, true /* owned */); } void API minijail_parse_seccomp_filters(struct minijail *j, const char *path) { if (!seccomp_should_use_filters(j)) return; attribute_cleanup_fp FILE *file = fopen(path, "re"); if (!file) { pdie("failed to open seccomp filter file '%s'", path); } if (parse_seccomp_filters(j, path, file) != 0) { die("failed to compile seccomp filter BPF program in '%s'", path); } if (j->seccomp_policy_path) { free(j->seccomp_policy_path); } j->seccomp_policy_path = strdup(path); } void API minijail_parse_seccomp_filters_from_fd(struct minijail *j, int fd) { char *fd_path, *path; attribute_cleanup_fp FILE *file = NULL; if (!seccomp_should_use_filters(j)) return; file = fdopen(fd, "r"); if (!file) { pdie("failed to associate stream with fd %d", fd); } if (asprintf(&fd_path, "/proc/self/fd/%d", fd) == -1) pdie("failed to create path for fd %d", fd); path = realpath(fd_path, NULL); if (path == NULL) pwarn("failed to get path of fd %d", fd); free(fd_path); if (parse_seccomp_filters(j, path ? path : "", file) != 0) { die("failed to compile seccomp filter BPF program from fd %d", fd); } if (j->seccomp_policy_path) { free(j->seccomp_policy_path); } j->seccomp_policy_path = path; } void API minijail_set_seccomp_filters(struct minijail *j, const struct sock_fprog *filter) { if (!seccomp_should_use_filters(j)) return; if (seccomp_is_logging_allowed(j)) { die("minijail_log_seccomp_filter_failures() is incompatible " "with minijail_set_seccomp_filters()"); } /* * set_seccomp_filters_internal() can only fail with ENOMEM. * Furthermore, since we won't own the incoming filter, it will not be * modified. */ if (set_seccomp_filters_internal(j, filter, false /* owned */) < 0) { die("failed to set seccomp filter"); } } int API minijail_use_alt_syscall(struct minijail *j, const char *table) { j->alt_syscall_table = strdup(table); if (!j->alt_syscall_table) return -ENOMEM; j->flags.alt_syscall = 1; return 0; } struct marshal_state { size_t available; size_t total; char *buf; }; static void marshal_state_init(struct marshal_state *state, char *buf, size_t available) { state->available = available; state->buf = buf; state->total = 0; } static void marshal_append(struct marshal_state *state, const void *src, size_t length) { size_t copy_len = MIN(state->available, length); /* Up to |available| will be written. */ if (copy_len) { memcpy(state->buf, src, copy_len); state->buf += copy_len; state->available -= copy_len; } /* |total| will contain the expected length. */ state->total += length; } static void marshal_append_string(struct marshal_state *state, const char *src) { marshal_append(state, src, strlen(src) + 1); } static void marshal_mount(struct marshal_state *state, const struct mountpoint *m) { marshal_append(state, m->src, strlen(m->src) + 1); marshal_append(state, m->dest, strlen(m->dest) + 1); marshal_append(state, m->type, strlen(m->type) + 1); marshal_append(state, (char *)&m->has_data, sizeof(m->has_data)); if (m->has_data) marshal_append(state, m->data, strlen(m->data) + 1); marshal_append(state, (char *)&m->flags, sizeof(m->flags)); } static void marshal_fs_rule(struct marshal_state *state, const struct fs_rule *r) { marshal_append(state, r->path, strlen(r->path) + 1); marshal_append(state, (char *)&r->landlock_flags, sizeof(r->landlock_flags)); } static void minijail_marshal_helper(struct marshal_state *state, const struct minijail *j) { struct mountpoint *m = NULL; struct fs_rule *r = NULL; size_t i; marshal_append(state, (char *)j, sizeof(*j)); if (j->user) marshal_append_string(state, j->user); if (j->suppl_gid_list) { marshal_append(state, j->suppl_gid_list, j->suppl_gid_count * sizeof(gid_t)); } if (j->chrootdir) marshal_append_string(state, j->chrootdir); if (j->hostname) marshal_append_string(state, j->hostname); if (j->alt_syscall_table) { marshal_append(state, j->alt_syscall_table, strlen(j->alt_syscall_table) + 1); } if (j->flags.seccomp_filter && j->filter_prog) { struct sock_fprog *fp = j->filter_prog; marshal_append(state, (char *)fp->filter, fp->len * sizeof(struct sock_filter)); } for (m = j->mounts_head; m; m = m->next) { marshal_mount(state, m); } for (i = 0; i < j->cgroup_count; ++i) marshal_append_string(state, j->cgroups[i]); for (r = j->fs_rules_head; r; r = r->next) marshal_fs_rule(state, r); marshal_append(state, (char *)&j->fs_rules_fd, sizeof(j->fs_rules_fd)); if (j->seccomp_policy_path) marshal_append_string(state, j->seccomp_policy_path); } size_t API minijail_size(const struct minijail *j) { struct marshal_state state; marshal_state_init(&state, NULL, 0); minijail_marshal_helper(&state, j); return state.total; } int minijail_marshal(const struct minijail *j, char *buf, size_t available) { struct marshal_state state; marshal_state_init(&state, buf, available); minijail_marshal_helper(&state, j); return (state.total > available); } int minijail_unmarshal(struct minijail *j, char *serialized, size_t length) { size_t i; size_t count; size_t fs_rules_count; int ret = -EINVAL; if (length < sizeof(*j)) goto out; memcpy((void *)j, serialized, sizeof(*j)); serialized += sizeof(*j); length -= sizeof(*j); /* Potentially stale pointers not used as signals. */ j->preload_path = NULL; j->filename = NULL; j->pid_file_path = NULL; j->uidmap = NULL; j->gidmap = NULL; j->mounts_head = NULL; j->mounts_tail = NULL; j->remounts_head = NULL; j->remounts_tail = NULL; j->filter_prog = NULL; j->hooks_head = NULL; j->hooks_tail = NULL; j->fs_rules_head = NULL; j->fs_rules_tail = NULL; j->effective_landlock_abi = -1; if (j->user) { /* stale pointer */ char *user = consumestr(&serialized, &length); if (!user) goto clear_pointers; j->user = strdup(user); if (!j->user) goto clear_pointers; } if (j->suppl_gid_list) { /* stale pointer */ if (j->suppl_gid_count > NGROUPS_MAX) { goto bad_gid_list; } size_t gid_list_size = j->suppl_gid_count * sizeof(gid_t); void *gid_list_bytes = consumebytes(gid_list_size, &serialized, &length); if (!gid_list_bytes) goto bad_gid_list; j->suppl_gid_list = calloc(j->suppl_gid_count, sizeof(gid_t)); if (!j->suppl_gid_list) goto bad_gid_list; memcpy(j->suppl_gid_list, gid_list_bytes, gid_list_size); } if (j->chrootdir) { /* stale pointer */ char *chrootdir = consumestr(&serialized, &length); if (!chrootdir) goto bad_chrootdir; j->chrootdir = strdup(chrootdir); if (!j->chrootdir) goto bad_chrootdir; } if (j->hostname) { /* stale pointer */ char *hostname = consumestr(&serialized, &length); if (!hostname) goto bad_hostname; j->hostname = strdup(hostname); if (!j->hostname) goto bad_hostname; } if (j->alt_syscall_table) { /* stale pointer */ char *alt_syscall_table = consumestr(&serialized, &length); if (!alt_syscall_table) goto bad_syscall_table; j->alt_syscall_table = strdup(alt_syscall_table); if (!j->alt_syscall_table) goto bad_syscall_table; } if (j->flags.seccomp_filter && j->filter_len > 0) { size_t ninstrs = j->filter_len; if (ninstrs > (SIZE_MAX / sizeof(struct sock_filter)) || ninstrs > USHRT_MAX) goto bad_filters; size_t program_len = ninstrs * sizeof(struct sock_filter); void *program = consumebytes(program_len, &serialized, &length); if (!program) goto bad_filters; j->filter_prog = malloc(sizeof(struct sock_fprog)); if (!j->filter_prog) goto bad_filters; j->filter_prog->len = ninstrs; j->filter_prog->filter = malloc(program_len); if (!j->filter_prog->filter) goto bad_filter_prog_instrs; memcpy(j->filter_prog->filter, program, program_len); } count = j->mounts_count; j->mounts_count = 0; for (i = 0; i < count; ++i) { unsigned long *flags; int *has_data; const char *dest; const char *type; const char *data = NULL; const char *src = consumestr(&serialized, &length); if (!src) goto bad_mounts; dest = consumestr(&serialized, &length); if (!dest) goto bad_mounts; type = consumestr(&serialized, &length); if (!type) goto bad_mounts; has_data = consumebytes(sizeof(*has_data), &serialized, &length); if (!has_data) goto bad_mounts; if (*has_data) { data = consumestr(&serialized, &length); if (!data) goto bad_mounts; } flags = consumebytes(sizeof(*flags), &serialized, &length); if (!flags) goto bad_mounts; if (minijail_mount_with_data(j, src, dest, type, *flags, data)) goto bad_mounts; } count = j->cgroup_count; j->cgroup_count = 0; if (count > MAX_CGROUPS) { ret = -EINVAL; goto bad_cgroups; } for (i = 0; i < count; ++i) { char *cgroup = consumestr(&serialized, &length); if (!cgroup) goto bad_cgroups; j->cgroups[i] = strdup(cgroup); if (!j->cgroups[i]) goto bad_cgroups; ++j->cgroup_count; } /* Unmarshal fs_rules. */ fs_rules_count = j->fs_rules_count; j->fs_rules_count = 0; for (i = 0; i < fs_rules_count; ++i) { const char *path = consumestr(&serialized, &length); uint64_t landlock_flags; void *landlock_flags_bytes = consumebytes(sizeof(landlock_flags), &serialized, &length); if (!path) goto bad_fs_rules; memcpy(&landlock_flags, landlock_flags_bytes, sizeof(landlock_flags)); if (!landlock_flags) goto bad_fs_rules; if (add_fs_restriction_path(j, path, landlock_flags)) goto bad_fs_rules; } /* Unmarshal fs_rules_fd. */ void *fs_rules_fd_bytes = consumebytes(sizeof(j->fs_rules_fd), &serialized, &length); memcpy(&j->fs_rules_fd, fs_rules_fd_bytes, sizeof(j->fs_rules_fd)); if (!j->fs_rules_fd) goto bad_cgroups; if (j->seccomp_policy_path) { /* stale pointer */ char *seccomp_policy_path = consumestr(&serialized, &length); if (!seccomp_policy_path) goto bad_cgroups; j->seccomp_policy_path = strdup(seccomp_policy_path); if (!j->seccomp_policy_path) goto bad_cgroups; } return 0; /* * If more is added after j->seccomp_policy_path, then this is needed: * if (j->seccomp_policy_path) * free(j->seccomp_policy_path); */ bad_cgroups: free_mounts_list(j); free_remounts_list(j); for (i = 0; i < j->cgroup_count; ++i) free(j->cgroups[i]); bad_fs_rules: free_fs_rules_list(j); bad_mounts: if (j->filter_prog && j->filter_prog->filter) free(j->filter_prog->filter); bad_filter_prog_instrs: if (j->filter_prog) free(j->filter_prog); bad_filters: if (j->alt_syscall_table) free(j->alt_syscall_table); bad_syscall_table: if (j->hostname) free(j->hostname); bad_hostname: if (j->chrootdir) free(j->chrootdir); bad_chrootdir: if (j->suppl_gid_list) free(j->suppl_gid_list); bad_gid_list: if (j->user) free(j->user); clear_pointers: j->user = NULL; j->suppl_gid_list = NULL; j->chrootdir = NULL; j->hostname = NULL; j->alt_syscall_table = NULL; j->cgroup_count = 0; j->fs_rules_count = 0; j->seccomp_policy_path = NULL; out: return ret; } struct dev_spec { const char *name; mode_t mode; dev_t major, minor; }; // clang-format off static const struct dev_spec device_nodes[] = { { "null", S_IFCHR | 0666, 1, 3, }, { "zero", S_IFCHR | 0666, 1, 5, }, { "full", S_IFCHR | 0666, 1, 7, }, { "urandom", S_IFCHR | 0444, 1, 9, }, { "tty", S_IFCHR | 0666, 5, 0, }, }; // clang-format on struct dev_sym_spec { const char *source, *dest; }; static const struct dev_sym_spec device_symlinks[] = { { "ptmx", "pts/ptmx", }, { "fd", "/proc/self/fd", }, { "stdin", "fd/0", }, { "stdout", "fd/1", }, { "stderr", "fd/2", }, }; /* * Clean up the temporary dev path we had setup previously. In case of errors, * we don't want to go leaking empty tempdirs. */ static void mount_dev_cleanup(char *dev_path) { umount2(dev_path, MNT_DETACH); rmdir(dev_path); free(dev_path); } /* * Set up the pseudo /dev path at the temporary location. * See mount_dev_finalize for more details. */ static int mount_dev(char **dev_path_ret) { int ret; attribute_cleanup_fd int dev_fd = -1; size_t i; mode_t mask; char *dev_path; /* * Create a temp path for the /dev init. We'll relocate this to the * final location later on in the startup process. */ dev_path = *dev_path_ret = strdup("/tmp/minijail.dev.XXXXXX"); if (dev_path == NULL || mkdtemp(dev_path) == NULL) pdie("could not create temp path for /dev"); /* Set up the empty /dev mount point first. */ ret = mount("minijail-devfs", dev_path, "tmpfs", MS_NOEXEC | MS_NOSUID, "size=5M,mode=755"); if (ret) { rmdir(dev_path); return ret; } /* We want to set the mode directly from the spec. */ mask = umask(0); /* Get a handle to the temp dev path for *at funcs below. */ dev_fd = open(dev_path, O_DIRECTORY | O_PATH | O_CLOEXEC); if (dev_fd < 0) { ret = 1; goto done; } /* Create all the nodes in /dev. */ for (i = 0; i < ARRAY_SIZE(device_nodes); ++i) { const struct dev_spec *ds = &device_nodes[i]; ret = mknodat(dev_fd, ds->name, ds->mode, makedev(ds->major, ds->minor)); if (ret) goto done; } /* Create all the symlinks in /dev. */ for (i = 0; i < ARRAY_SIZE(device_symlinks); ++i) { const struct dev_sym_spec *ds = &device_symlinks[i]; ret = symlinkat(ds->dest, dev_fd, ds->source); if (ret) goto done; } /* Create empty dir for glibc shared mem APIs. */ ret = mkdirat(dev_fd, "shm", 01777); if (ret) goto done; /* Restore old mask. */ done: umask(mask); if (ret) mount_dev_cleanup(dev_path); return ret; } /* * Relocate the temporary /dev mount to its final /dev place. * We have to do this two step process so people can bind mount extra * /dev paths like /dev/log. */ static int mount_dev_finalize(const struct minijail *j, char *dev_path) { int ret = -1; char *dest = NULL; /* Unmount the /dev mount if possible. */ if (umount2("/dev", MNT_DETACH)) goto done; if (asprintf(&dest, "%s/dev", j->chrootdir ?: "") < 0) goto done; if (mount(dev_path, dest, NULL, MS_MOVE, NULL)) goto done; ret = 0; done: free(dest); mount_dev_cleanup(dev_path); return ret; } // Cleans |path| in-place by resolving . and .. components. // Ensures it does not go back past |limit|. static void clean_path_limit(char *path, const char *limit) { char *src = path; char *dst = path; // Preserve leading slash. if (*src == '/') { *dst++ = *src++; } while (*src) { // Skip consecutive slashes. if (*src == '/') { src++; continue; } // Check for "." component. if (src[0] == '.') { if (src[1] == '\0') { src += 1; // Skip "." at the end of the path. continue; } if (src[1] == '/') { src += 2; // Skip "./". continue; } } // Check for ".." component. if (src[0] == '.' && src[1] == '.') { bool is_double_dot = false; size_t skip_len = 0; if (src[2] == '\0') { is_double_dot = true; skip_len = 2; // Skip ".." at the end of the path. } else if (src[2] == '/') { is_double_dot = true; skip_len = 3; // Skip "../". } if (is_double_dot) { src += skip_len; // Pop the last component from dst, but do not // go back past limit. if (dst > limit) { dst--; // Move past the trailing slash // of the last component. // Move back until we hit the slash // before the last component. while (dst > limit && *(dst - 1) != '/') { dst--; } } continue; } } // Copy the current component. while (*src && *src != '/') { *dst++ = *src++; } // Copy the trailing slash of the component. if (*src == '/') { *dst++ = *src++; } } // Remove trailing slash if it's not the root directory. if (dst > path + 1 && *(dst - 1) == '/') { dst--; } *dst = '\0'; } // Resolves symlinks in |path| safely, ensuring they do not escape |chrootdir|. // |path| must be dynamically allocated (will be freed and updated if resolved). // Returns 0 on success, or negative error code. static int resolve_mount_destination(const char *chrootdir, char **path) { int symlink_loop_limit = 8; struct stat st; char link_target[PATH_MAX]; ssize_t target_len; char *current_path = *path; size_t chroot_len = chrootdir ? strlen(chrootdir) : 0; while (symlink_loop_limit--) { if (lstat(current_path, &st) < 0) { if (errno == ENOENT) { break; } return -errno; } if (!S_ISLNK(st.st_mode)) { break; } target_len = readlink(current_path, link_target, sizeof(link_target) - 1); if (target_len < 0) { return -errno; } link_target[target_len] = '\0'; char *next_path = NULL; if (link_target[0] == '/') { if (asprintf(&next_path, "%s%s", chrootdir ?: "", link_target) < 0) { return -ENOMEM; } } else { char *dir = strdup(current_path); if (!dir) { return -ENOMEM; } char *parent = "."; char *last_slash = strrchr(dir, '/'); if (last_slash) { if (last_slash == dir) { parent = "/"; } else { *last_slash = '\0'; parent = dir; } } if (asprintf(&next_path, "%s/%s", parent, link_target) < 0) { free(dir); return -ENOMEM; } free(dir); } // Calculate limit offset for next_path. size_t limit_offset = 1; if (chroot_len > 0) { limit_offset = chroot_len; if (next_path[limit_offset] == '/') { limit_offset++; } } clean_path_limit(next_path, next_path + limit_offset); // Verify it didn't escape (should start with chrootdir if set). if (chroot_len > 0) { size_t check_len = chroot_len; if (chrootdir[chroot_len - 1] == '/') { check_len--; } if (strncmp(next_path, chrootdir, check_len) != 0 || (next_path[check_len] != '\0' && next_path[check_len] != '/')) { free(next_path); return -EACCES; } } free(current_path); current_path = next_path; *path = current_path; } if (symlink_loop_limit < 0) { return -ELOOP; } return 0; } /* * mount_one: Applies mounts from @m for @j, recursing as needed. * @j Minijail these mounts are for * @m Head of list of mounts * * Returns 0 for success. */ static int mount_one(const struct minijail *j, struct mountpoint *m, const char *dev_path) { int ret; char *dest; bool do_remount = false; bool has_bind_flag = mount_has_bind_flag(m); bool has_remount_flag = !!(m->flags & MS_REMOUNT); unsigned long original_mnt_flags = 0; /* We assume |dest| has a leading "/". */ if (dev_path && strncmp("/dev/", m->dest, 5) == 0) { /* * Since the temp path is rooted at /dev, skip that dest part. */ if (asprintf(&dest, "%s%s", dev_path, m->dest + 4) < 0) return -ENOMEM; } else { if (asprintf(&dest, "%s%s", j->chrootdir ?: "", m->dest) < 0) return -ENOMEM; } ret = resolve_mount_destination(j->chrootdir, &dest); if (ret) { warn("cannot resolve mount target '%s'", dest); goto error; } ret = setup_mount_destination(m->src, dest, j->uid, j->gid, has_bind_flag); if (ret) { warn("cannot create mount target '%s'", dest); goto error; } /* * Remount bind mounts that: * - Come from the minijail_bind() API, and * - Add the 'ro' flag * since 'bind' and other flags can't both be specified in the same * mount(2) call. * Callers using minijail_mount() to perform bind mounts are expected to * know what they're doing and call minijail_mount() with MS_REMOUNT as * needed. * Therefore, if the caller is asking for a remount (using MS_REMOUNT), * there is no need to do an extra remount here. */ if (has_bind_flag && strcmp(m->type, "minijail_bind") == 0 && !has_remount_flag) { /* * Grab the mount flags of the source. These are used to figure * out whether the bind mount needs to be remounted read-only. */ if (get_mount_flags(m->src, &original_mnt_flags)) { warn("cannot get mount flags for '%s'", m->src); goto error; } if ((m->flags & MS_RDONLY) != (original_mnt_flags & MS_RDONLY)) { do_remount = 1; /* * Restrict the mount flags to those that are * user-settable in a MS_REMOUNT request, but excluding * MS_RDONLY. The user-requested mount flags will * dictate whether the remount will have that flag or * not. */ original_mnt_flags &= (MS_USER_SETTABLE_MASK & ~MS_RDONLY); } } /* * Do a final check for symlinks in |m->src|. * |m->src| will only contain a valid path when purely bind-mounting * (but not when remounting a bind mount). * * Short of having a version of mount(2) that can take fd's, this is the * smallest we can make the TOCTOU window. */ if (has_bind_flag && !has_remount_flag && !is_valid_bind_path(m->src)) { warn("src '%s' is not a valid bind mount path", m->src); goto error; } ret = mount(m->src, dest, m->type, m->flags, m->data); if (ret) { pwarn("cannot mount '%s' as '%s' with flags %#lx", m->src, dest, m->flags); goto error; } /* Remount *after* the initial mount. */ if (do_remount) { ret = mount(m->src, dest, NULL, m->flags | original_mnt_flags | MS_REMOUNT, m->data); if (ret) { pwarn( "cannot bind-remount '%s' as '%s' with flags %#lx", m->src, dest, m->flags | original_mnt_flags | MS_REMOUNT); goto error; } } free(dest); if (m->next) return mount_one(j, m->next, dev_path); return 0; error: free(dest); return ret; } static void process_mounts_or_die(const struct minijail *j) { /* * We have to mount /dev first in case there are bind mounts from * the original /dev into the new unique tmpfs one. */ char *dev_path = NULL; if (j->flags.mount_dev && mount_dev(&dev_path)) pdie("mount_dev failed"); if (j->mounts_head && mount_one(j, j->mounts_head, dev_path)) { warn("mount_one failed with /dev at '%s'", dev_path); if (dev_path) mount_dev_cleanup(dev_path); _exit(MINIJAIL_ERR_MOUNT); } /* * Once all bind mounts have been processed, move the temp dev to * its final /dev home. */ if (j->flags.mount_dev && mount_dev_finalize(j, dev_path)) pdie("mount_dev_finalize failed"); } static int enter_chroot(const struct minijail *j) { run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_CHROOT); if (chroot(j->chrootdir)) return -errno; if (chdir("/")) return -errno; return 0; } static int enter_pivot_root(const struct minijail *j) { attribute_cleanup_fd int oldroot = -1; attribute_cleanup_fd int newroot = -1; run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_CHROOT); /* * Keep the fd for both old and new root. * It will be used in fchdir(2) later. */ oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (oldroot < 0) pdie("failed to open / for fchdir"); newroot = open(j->chrootdir, O_DIRECTORY | O_RDONLY | O_CLOEXEC); if (newroot < 0) pdie("failed to open %s for fchdir", j->chrootdir); /* * To ensure j->chrootdir is the root of a filesystem, * do a self bind mount. */ if (mount(j->chrootdir, j->chrootdir, "bind", MS_BIND | MS_REC, "")) pdie("failed to bind mount '%s'", j->chrootdir); if (chdir(j->chrootdir)) return -errno; if (syscall(SYS_pivot_root, ".", ".")) pdie("pivot_root"); /* * Now the old root is mounted on top of the new root. Use fchdir(2) to * change to the old root and unmount it. */ if (fchdir(oldroot)) pdie("failed to fchdir to old /"); /* * If skip_remount_private was enabled for minijail_enter(), * there could be a shared mount point under |oldroot|. In that case, * mounts under this shared mount point will be unmounted below, and * this unmounting will propagate to the original mount namespace * (because the mount point is shared). To prevent this unexpected * unmounting, remove these mounts from their peer groups by recursively * remounting them as MS_PRIVATE. */ if (mount(NULL, ".", NULL, MS_REC | MS_PRIVATE, NULL)) pdie("failed to mount(/, private) before umount(/)"); /* The old root might be busy, so use lazy unmount. */ if (umount2(".", MNT_DETACH)) pdie("umount(/)"); /* Change back to the new root. */ if (fchdir(newroot)) return -errno; if (chroot("/")) return -errno; /* Set correct CWD for getcwd(3). */ if (chdir("/")) return -errno; return 0; } static int mount_tmp(const struct minijail *j) { const char fmt[] = "size=%zu,mode=1777"; /* Count for the user storing ULLONG_MAX literally + extra space. */ char data[sizeof(fmt) + sizeof("18446744073709551615ULL")]; int ret; ret = snprintf(data, sizeof(data), fmt, j->tmpfs_size); if (ret <= 0) pdie("tmpfs size spec error"); else if ((size_t)ret >= sizeof(data)) pdie("tmpfs size spec too large"); unsigned long flags = MS_NODEV | MS_NOEXEC | MS_NOSUID; if (block_symlinks_in_noninit_mountns_tmp()) { flags |= MS_NOSYMFOLLOW; } return mount("none", "/tmp", "tmpfs", flags, data); } static int remount_proc_readonly(const struct minijail *j) { const char *kProcPath = "/proc"; const unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID; /* * Right now, we're holding a reference to our parent's old mount of * /proc in our namespace, which means using MS_REMOUNT here would * mutate our parent's mount as well, even though we're in a VFS * namespace (!). Instead, remove their mount from our namespace lazily * (MNT_DETACH) and make our own. * * However, we skip this in the user namespace case because it will * invariably fail. Every mount namespace is "owned" by the * user namespace of the process that creates it. Mount namespace A is * "less privileged" than mount namespace B if A is created off of B, * and B is owned by a different user namespace. * When a less privileged mount namespace is created, the mounts used to * initialize it (coming from the more privileged mount namespace) come * as a unit, and are locked together. This means that code running in * the new mount (and user) namespace cannot piecemeal unmount * individual mounts inherited from a more privileged mount namespace. * See https://man7.org/linux/man-pages/man7/mount_namespaces.7.html, * "Restrictions on mount namespaces" for details. * * This happens in our use case because we first enter a new user * namespace (on clone(2)) and then we unshare(2) a new mount namespace, * which means the new mount namespace is less privileged than its * parent mount namespace. This would also happen if we entered a new * mount namespace on clone(2), since the user namespace is created * first. * In all other non-user-namespace cases the new mount namespace is * similarly privileged as the parent mount namespace so unmounting a * single mount is allowed. * * We still remount /proc as read-only in the user namespace case * because while a process with CAP_SYS_ADMIN in the new user namespace * can unmount the RO mount and get at the RW mount, an attacker with * access only to a write primitive will not be able to modify /proc. */ if (!j->flags.userns && umount2(kProcPath, MNT_DETACH)) return -errno; if (mount("proc", kProcPath, "proc", kSafeFlags | MS_RDONLY, "")) return -errno; return 0; } static void kill_child_and_die(const struct minijail *j, const char *msg) { kill(j->initpid, SIGKILL); die("%s", msg); } static void write_pid_file_or_die(const struct minijail *j) { if (write_pid_to_path(j->initpid, j->pid_file_path)) kill_child_and_die(j, "failed to write pid file"); } static void add_to_cgroups_or_die(const struct minijail *j) { size_t i; for (i = 0; i < j->cgroup_count; ++i) { if (write_pid_to_path(j->initpid, j->cgroups[i])) kill_child_and_die(j, "failed to add to cgroups"); } } static void set_rlimits_or_die(const struct minijail *j) { size_t i; for (i = 0; i < j->rlimit_count; ++i) { struct rlimit limit; limit.rlim_cur = j->rlimits[i].cur; limit.rlim_max = j->rlimits[i].max; if (prlimit(j->initpid, j->rlimits[i].type, &limit, NULL)) kill_child_and_die(j, "failed to set rlimit"); } } static void write_ugid_maps_or_die(const struct minijail *j) { if (j->uidmap) { int ret = write_proc_file(j->initpid, j->uidmap, "uid_map"); if (ret == -EPERM) { warn("direct uid_map write failed (EPERM), " "falling back to newuidmap helper"); ret = run_id_map_helper(j->initpid, j->uidmap, NEWUIDMAP_PATH); } if (ret != 0) kill_child_and_die(j, "failed to write uid_map"); } if (j->gidmap && j->flags.disable_setgroups) { /* * Older kernels might not have the /proc//setgroups files. */ int ret = write_proc_file(j->initpid, "deny", "setgroups"); if (ret != 0) { if (ret == -ENOENT) { /* * See * http://man7.org/linux/man-pages/man7/user_namespaces.7.html. */ warn("could not disable setgroups(2)"); } else kill_child_and_die( j, "failed to disable setgroups(2)"); } } if (j->gidmap) { int ret = write_proc_file(j->initpid, j->gidmap, "gid_map"); if (ret == -EPERM) { warn("direct gid_map write failed (EPERM), " "falling back to newgidmap helper"); ret = run_id_map_helper(j->initpid, j->gidmap, NEWGIDMAP_PATH); } if (ret != 0) kill_child_and_die(j, "failed to write gid_map"); } } static void enter_user_namespace(const struct minijail *j) { int uid = j->flags.uid ? j->uid : 0; int gid = j->flags.gid ? j->gid : 0; if (j->gidmap && setresgid(gid, gid, gid)) { pdie("user_namespaces: setresgid(%d, %d, %d) failed", gid, gid, gid); } if (j->uidmap && setresuid(uid, uid, uid)) { pdie("user_namespaces: setresuid(%d, %d, %d) failed", uid, uid, uid); } } static void parent_setup_complete(int *pipe_fds) { close_and_reset(&pipe_fds[0]); close_and_reset(&pipe_fds[1]); } /* * wait_for_parent_setup: Called by the child process to wait for any * further parent-side setup to complete before continuing. */ static void wait_for_parent_setup(int *pipe_fds) { char buf; close_and_reset(&pipe_fds[1]); /* Wait for parent to complete setup and close the pipe. */ if (read(pipe_fds[0], &buf, 1) != 0) die("failed to sync with parent"); close_and_reset(&pipe_fds[0]); } static void drop_ugid(const struct minijail *j) { if (j->flags.inherit_suppl_gids + j->flags.keep_suppl_gids + j->flags.set_suppl_gids > 1) { die("can only do one of inherit, keep, or set supplementary " "groups"); } if (j->flags.inherit_suppl_gids) { if (initgroups(j->user, j->usergid)) pdie("initgroups(%s, %d) failed", j->user, j->usergid); } else if (j->flags.set_suppl_gids) { if (setgroups(j->suppl_gid_count, j->suppl_gid_list)) pdie("setgroups(suppl_gids) failed"); } else if (!j->flags.keep_suppl_gids && !j->flags.disable_setgroups) { /* * Only attempt to clear supplementary groups if we are changing * users or groups, and if the caller did not request to disable * setgroups (used when entering a user namespace as a * non-privileged user). */ if ((j->flags.uid || j->flags.gid) && setgroups(0, NULL)) pdie("setgroups(0, NULL) failed"); } if (j->flags.gid && setresgid(j->gid, j->gid, j->gid)) pdie("setresgid(%d, %d, %d) failed", j->gid, j->gid, j->gid); if (j->flags.uid && setresuid(j->uid, j->uid, j->uid)) pdie("setresuid(%d, %d, %d) failed", j->uid, j->uid, j->uid); } static void drop_capbset(uint64_t keep_mask, unsigned int last_valid_cap) { const uint64_t one = 1; unsigned int i; for (i = 0; i < sizeof(keep_mask) * 8 && i <= last_valid_cap; ++i) { if (keep_mask & (one << i)) continue; if (prctl(PR_CAPBSET_DROP, i)) pdie("could not drop capability from bounding set"); } } static void drop_caps(const struct minijail *j, unsigned int last_valid_cap) { if (!j->flags.use_caps) return; cap_t caps = cap_get_proc(); cap_value_t flag[1]; const size_t ncaps = sizeof(j->caps) * 8; const uint64_t one = 1; unsigned int i; if (!caps) die("can't get process caps"); if (cap_clear(caps)) die("can't clear caps"); for (i = 0; i < ncaps && i <= last_valid_cap; ++i) { /* Keep CAP_SETPCAP for dropping bounding set bits. */ if (i != CAP_SETPCAP && !(j->caps & (one << i))) continue; flag[0] = i; if (cap_set_flag(caps, CAP_EFFECTIVE, 1, flag, CAP_SET)) die("can't add effective cap"); if (cap_set_flag(caps, CAP_PERMITTED, 1, flag, CAP_SET)) die("can't add permitted cap"); if (cap_set_flag(caps, CAP_INHERITABLE, 1, flag, CAP_SET)) die("can't add inheritable cap"); } if (cap_set_proc(caps)) die("can't apply initial cleaned capset"); /* * Instead of dropping the bounding set first, do it here in case * the caller had a more permissive bounding set which could * have been used above to raise a capability that wasn't already * present. This requires CAP_SETPCAP, so we raised/kept it above. * * However, if we're asked to skip setting *and* locking the * SECURE_NOROOT securebit, also skip dropping the bounding set. * If the caller wants to regain all capabilities when executing a * set-user-ID-root program, allow them to do so. The default behavior * (i.e. the behavior without |securebits_skip_mask| set) will still put * the jailed process tree in a capabilities-only environment. * * We check the negated skip mask for SECURE_NOROOT and * SECURE_NOROOT_LOCKED. If the bits are set in the negated mask they * will *not* be skipped in lock_securebits(), and therefore we should * drop the bounding set. */ if (secure_noroot_set_and_locked(~j->securebits_skip_mask)) { drop_capbset(j->caps, last_valid_cap); } else { warn("SECURE_NOROOT not set, not dropping bounding set"); } /* If CAP_SETPCAP wasn't specifically requested, now we remove it. */ if ((j->caps & (one << CAP_SETPCAP)) == 0) { flag[0] = CAP_SETPCAP; if (cap_set_flag(caps, CAP_EFFECTIVE, 1, flag, CAP_CLEAR)) die("can't clear effective cap"); if (cap_set_flag(caps, CAP_PERMITTED, 1, flag, CAP_CLEAR)) die("can't clear permitted cap"); if (cap_set_flag(caps, CAP_INHERITABLE, 1, flag, CAP_CLEAR)) die("can't clear inheritable cap"); } if (cap_set_proc(caps)) die("can't apply final cleaned capset"); /* * If ambient capabilities are supported, clear all capabilities first, * then raise the requested ones. */ if (j->flags.set_ambient_caps) { if (!cap_ambient_supported()) { pdie("ambient capabilities not supported"); } if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) != 0) { pdie("can't clear ambient capabilities"); } for (i = 0; i < ncaps && i <= last_valid_cap; ++i) { if (!(j->caps & (one << i))) continue; if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) != 0) { pdie("prctl(PR_CAP_AMBIENT, " "PR_CAP_AMBIENT_RAISE, %u) failed", i); } } } cap_free(caps); } /* Calls landlock_restrict_self(), based on current inodes. */ static void apply_landlock_restrictions(const struct minijail *j) { struct fs_rule *r = j->fs_rules_head; /* The ruleset_fd needs to be mutable so use a stack copy from now on. */ int ruleset_fd = j->fs_rules_fd; if (!j->flags.enable_fs_restrictions || !r) { return; } if (minijail_is_fs_restriction_available()) { while (r) { populate_ruleset_internal(r->path, ruleset_fd, r->landlock_flags); r = r->next; } } if (ruleset_fd >= 0) { if (j->filename != NULL) { info("applying Landlock to process %s", j->filename); } if (landlock_restrict_self(ruleset_fd, 0)) { pdie("failed to enforce ruleset"); } close(ruleset_fd); } } static void set_no_new_privs(const struct minijail *j) { if (j->flags.no_new_privs) { if (!sys_set_no_new_privs()) { die("set_no_new_privs() failed"); } } } static void set_seccomp_filter(const struct minijail *j) { /* * Code running with ASan * (https://github.com/google/sanitizers/wiki/AddressSanitizer) * will make system calls not included in the syscall filter policy, * which will likely crash the program. Skip setting seccomp filter in * that case. * 'running_with_asan()' has no inputs and is completely defined at * build time, so this cannot be used by an attacker to skip setting * seccomp filter. */ if (j->flags.seccomp_filter && running_with_asan()) { warn("running with (HW)ASan, not setting seccomp filter"); return; } if (j->flags.seccomp_filter) { if (seccomp_is_logging_allowed(j)) { warn("logging seccomp filter failures"); if (!seccomp_ret_log_available()) { /* * If SECCOMP_RET_LOG is not available, * install the SIGSYS handler first. */ if (install_sigsys_handler()) pdie( "failed to install SIGSYS handler"); } } else if (j->flags.seccomp_filter_tsync) { /* * If setting thread sync, * reset the SIGSYS signal handler so that * the entire thread group is killed. */ if (signal(SIGSYS, SIG_DFL) == SIG_ERR) pdie("failed to reset SIGSYS disposition"); } } /* * Install the syscall filter. */ if (j->flags.seccomp_filter) { if (j->flags.seccomp_filter_tsync || j->flags.seccomp_filter_allow_speculation) { int filter_flags = (j->flags.seccomp_filter_tsync ? SECCOMP_FILTER_FLAG_TSYNC : 0) | (j->flags.seccomp_filter_allow_speculation ? SECCOMP_FILTER_FLAG_SPEC_ALLOW : 0); if (sys_seccomp(SECCOMP_SET_MODE_FILTER, filter_flags, j->filter_prog)) { pdie("seccomp(tsync) failed"); } } else { if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, j->filter_prog)) { pdie("prctl(seccomp_filter) failed"); } } } } static pid_t forward_pid = -1; static void forward_signal(int sig, siginfo_t *siginfo attribute_unused, void *void_context attribute_unused) { if (forward_pid != -1) { kill(forward_pid, sig); } } static void install_signal_handlers(void) { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_sigaction = &forward_signal; act.sa_flags = SA_SIGINFO | SA_RESTART; /* Handle all signals, except SIGCHLD. */ for (int sig = 1; sig < NSIG; sig++) { /* * We don't care if we get EINVAL: that just means that we * can't handle this signal, so let's skip it and continue. */ sigaction(sig, &act, NULL); } /* Reset SIGCHLD's handler. */ signal(SIGCHLD, SIG_DFL); /* Handle real-time signals. */ for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++) { sigaction(sig, &act, NULL); } } static const char *lookup_hook_name(minijail_hook_event_t event) { switch (event) { case MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS: return "pre-drop-caps"; case MINIJAIL_HOOK_EVENT_PRE_EXECVE: return "pre-execve"; case MINIJAIL_HOOK_EVENT_PRE_CHROOT: return "pre-chroot"; case MINIJAIL_HOOK_EVENT_MAX: /* * Adding this in favor of a default case to force the * compiler to error out if a new enum value is added. */ break; } return "unknown"; } static void run_hooks_or_die(const struct minijail *j, minijail_hook_event_t event) { int rc; int hook_index = 0; for (struct hook *c = j->hooks_head; c; c = c->next) { if (c->event != event) continue; rc = c->hook(c->payload); if (rc != 0) { errno = -rc; pdie("%s hook (index %d) failed", lookup_hook_name(event), hook_index); } /* Only increase the index within the same hook event type. */ ++hook_index; } } void API minijail_enter(const struct minijail *j) { /* * If we're dropping caps, get the last valid cap from /proc now, * since /proc can be unmounted before drop_caps() is called. */ unsigned int last_valid_cap = 0; if (j->flags.capbset_drop || j->flags.use_caps) last_valid_cap = get_last_valid_cap(); if (j->flags.pids) die("tried to enter a pid-namespaced jail;" " try minijail_run()?"); if (j->flags.inherit_suppl_gids && !j->user) die("cannot inherit supplementary groups without setting a " "username"); /* * We can't recover from failures if we've dropped privileges partially, * so we don't even try. If any of our operations fail, we abort() the * entire process. */ if (j->flags.enter_vfs) { if (setns(j->mountns_fd, CLONE_NEWNS)) pdie("setns(CLONE_NEWNS) failed"); close(j->mountns_fd); } if (j->flags.vfs) { if (unshare(CLONE_NEWNS)) pdie("unshare(CLONE_NEWNS) failed"); /* * By default, remount all filesystems as private, unless * - Passed a specific remount mode, in which case remount with * that, * - Asked not to remount at all, in which case skip the * mount(2) call. * https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt */ if (j->remount_mode) { if (mount(NULL, "/", NULL, MS_REC | j->remount_mode, NULL)) pdie("mount(NULL, /, NULL, " "MS_REC | j->remount_mode, NULL) failed"); struct minijail_remount *temp = j->remounts_head; while (temp) { if (temp->remount_mode < j->remount_mode) die("cannot remount %s as stricter " "than the root dir", temp->mount_name); if (mount(NULL, temp->mount_name, NULL, MS_REC | temp->remount_mode, NULL)) pdie("mount(NULL, %s, NULL, " "MS_REC | temp->remount_mode, " "NULL) failed", temp->mount_name); temp = temp->next; } } } if (j->flags.ipc && unshare(CLONE_NEWIPC)) { pdie("unshare(CLONE_NEWIPC) failed"); } if (j->flags.uts) { if (unshare(CLONE_NEWUTS)) pdie("unshare(CLONE_NEWUTS) failed"); if (j->hostname && sethostname(j->hostname, strlen(j->hostname))) pdie("sethostname(%s) failed", j->hostname); } if (j->flags.enter_net) { if (setns(j->netns_fd, CLONE_NEWNET)) pdie("setns(CLONE_NEWNET) failed"); close(j->netns_fd); } else if (j->flags.net) { if (unshare(CLONE_NEWNET)) pdie("unshare(CLONE_NEWNET) failed"); if (j->flags.net_loopback) config_net_loopback(); } if (j->flags.ns_cgroups && unshare(CLONE_NEWCGROUP)) pdie("unshare(CLONE_NEWCGROUP) failed"); if (j->flags.new_session_keyring) { if (syscall(SYS_keyctl, KEYCTL_JOIN_SESSION_KEYRING, NULL) < 0) pdie("keyctl(KEYCTL_JOIN_SESSION_KEYRING) failed"); } /* We have to process all the mounts before we chroot/pivot_root. */ process_mounts_or_die(j); if (j->flags.chroot && enter_chroot(j)) pdie("chroot"); if (j->flags.pivot_root && enter_pivot_root(j)) pdie("pivot_root"); if (j->flags.mount_tmp && mount_tmp(j)) pdie("mount_tmp"); if (j->flags.remount_proc_ro && remount_proc_readonly(j)) pdie("remount"); run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS); /* * If we're only dropping capabilities from the bounding set, but not * from the thread's (permitted|inheritable|effective) sets, do it now. */ if (j->flags.capbset_drop) { drop_capbset(j->cap_bset, last_valid_cap); } /* * POSIX capabilities are a bit tricky. We must set SECBIT_KEEP_CAPS * before drop_ugid() below as the latter would otherwise drop all * capabilities. */ if (j->flags.use_caps) { /* * When using ambient capabilities, CAP_SET{GID,UID} can be * inherited across execve(2), so SECBIT_KEEP_CAPS is not * strictly needed. */ bool require_keep_caps = !j->flags.set_ambient_caps; if (lock_securebits(j->securebits_skip_mask, require_keep_caps) < 0) { pdie("locking securebits failed"); } } if (j->flags.no_new_privs) { /* * If we're setting no_new_privs, we can drop privileges * before setting seccomp filter. This way filter policies * don't need to allow privilege-dropping syscalls. */ drop_ugid(j); drop_caps(j, last_valid_cap); /* * Landlock is applied as late as possible. If no_new_privs is * requested, then we need to set that first because the * landlock_restrict_self() syscall has a seccomp(2) like check * for that. See: * https://elixir.bootlin.com/linux/v5.15.74/source/security/landlock/syscalls.c#L409 */ set_no_new_privs(j); apply_landlock_restrictions(j); set_seccomp_filter(j); } else { apply_landlock_restrictions(j); /* * If we're not setting no_new_privs, * we need to set seccomp filter *before* dropping privileges. * WARNING: this means that filter policies *must* allow * setgroups()/setresgid()/setresuid() for dropping root and * capget()/capset()/prctl() for dropping caps. */ set_seccomp_filter(j); drop_ugid(j); drop_caps(j, last_valid_cap); } /* * Select the specified alternate syscall table. The table must not * block prctl(2) if we're using seccomp as well. */ if (j->flags.alt_syscall) { if (prctl(PR_ALT_SYSCALL, 1, j->alt_syscall_table)) pdie("prctl(PR_ALT_SYSCALL) failed"); } /* * seccomp has to come last since it cuts off all the other * privilege-dropping syscalls :) */ if (j->flags.seccomp && prctl(PR_SET_SECCOMP, 1)) { if ((errno == EINVAL) && seccomp_can_softfail()) { warn("seccomp not supported"); return; } pdie("prctl(PR_SET_SECCOMP) failed"); } } /* TODO(wad): will visibility affect this variable? */ static int init_exitstatus = 0; static void init_term(int sig attribute_unused) { _exit(init_exitstatus); } static void init(pid_t rootpid) { pid_t pid; int status; /* So that we exit with the right status. */ signal(SIGTERM, init_term); /* TODO(wad): self jail with seccomp filters here. */ while ((pid = wait(&status)) > 0) { /* * This loop will only end when either there are no processes * left inside our pid namespace or we get a signal. */ if (pid == rootpid) init_exitstatus = status; } if (!WIFEXITED(init_exitstatus)) _exit(MINIJAIL_ERR_INIT); _exit(WEXITSTATUS(init_exitstatus)); } int API minijail_from_fd(int fd, struct minijail *j) { size_t sz = 0; int err = read_exactly(fd, &sz, sizeof(sz)); attribute_cleanup_str char *buf = NULL; if (err) { pwarn("failed to read marshalled minijail size"); return err; } if (sz > USHRT_MAX) /* arbitrary check */ return -E2BIG; buf = malloc(sz); if (!buf) return -ENOMEM; err = read_exactly(fd, buf, sz); if (err) { pwarn("failed to read marshalled minijail payload"); return err; } return minijail_unmarshal(j, buf, sz); } int API minijail_to_fd(struct minijail *j, int fd) { size_t sz = minijail_size(j); if (!sz) return -EINVAL; attribute_cleanup_str char *buf = malloc(sz); if (!buf) return -ENOMEM; int err = minijail_marshal(j, buf, sz); if (err) return err; /* Sends [size][minijail]. */ err = write_exactly(fd, &sz, sizeof(sz)); if (err) return err; return write_exactly(fd, buf, sz); } int API minijail_copy_jail(const struct minijail *from, struct minijail *out) { size_t sz = minijail_size(from); if (!sz) return -EINVAL; attribute_cleanup_str char *buf = malloc(sz); if (!buf) return -ENOMEM; int err = minijail_marshal(from, buf, sz); if (err) return err; return minijail_unmarshal(out, buf, sz); } static int setup_preload(const struct minijail *j attribute_unused, char ***child_env attribute_unused) { #if defined(__ANDROID__) /* Don't use LDPRELOAD on Android. */ return 0; #else const char *preload_path = j->preload_path ?: PRELOADPATH; char *newenv = NULL; int ret = 0; const char *oldenv = minijail_getenv(*child_env, kLdPreloadEnvVar); if (!oldenv) oldenv = ""; /* Only insert a separating space if we have something to separate... */ if (asprintf(&newenv, "%s%s%s", oldenv, oldenv[0] != '\0' ? " " : "", preload_path) < 0) { return -1; } ret = minijail_setenv(child_env, kLdPreloadEnvVar, newenv, 1); free(newenv); return ret; #endif } /* * This is for logging purposes and does not change the enforced seccomp * filter. */ static int setup_seccomp_policy_path(const struct minijail *j, char ***child_env) { return minijail_setenv(child_env, kSeccompPolicyPathEnvVar, j->seccomp_policy_path ? j->seccomp_policy_path : "NO-LABEL", 1 /* overwrite */); } static int setup_pipe(char ***child_env, int fds[2]) { int r = pipe(fds); char fd_buf[11]; if (r) return r; r = snprintf(fd_buf, sizeof(fd_buf), "%d", fds[0]); if (r <= 0) return -EINVAL; return minijail_setenv(child_env, kFdEnvVar, fd_buf, 1); } static int close_open_fds(int *inheritable_fds, size_t size) { const char *kFdPath = "/proc/self/fd"; DIR *d = opendir(kFdPath); struct dirent *dir_entry; if (d == NULL) return -1; int dir_fd = dirfd(d); while ((dir_entry = readdir(d)) != NULL) { size_t i; char *end; bool should_close = true; const int fd = strtol(dir_entry->d_name, &end, 10); if ((*end) != '\0') { continue; } /* * We might have set up some pipes that we want to share with * the parent process, and should not be closed. */ for (i = 0; i < size; ++i) { if (fd == inheritable_fds[i]) { should_close = false; break; } } /* Also avoid closing the directory fd. */ if (should_close && fd != dir_fd) close(fd); } closedir(d); return 0; } /* Return true if the specified file descriptor is already open. */ int minijail_fd_is_open(int fd) { return fcntl(fd, F_GETFD) != -1 || errno != EBADF; } /* * Returns true if |check_fd| is one of j->preserved_fds[:max_index].child_fd. */ static bool is_preserved_child_fd(struct minijail *j, int check_fd, size_t max_index) { max_index = MIN(max_index, j->preserved_fd_count); for (size_t i = 0; i < max_index; i++) { if (j->preserved_fds[i].child_fd == check_fd) { return true; } } return false; } /* If parent_fd will be used by a child fd, move it to an unused fd. */ static int ensure_no_fd_conflict(struct minijail *j, int child_fd, int *parent_fd, size_t max_index) { if (!is_preserved_child_fd(j, *parent_fd, max_index)) { return 0; } /* * If no other parent_fd matches the child_fd then use it instead of a * temporary. */ int fd = child_fd; if (fd == -1 || minijail_fd_is_open(fd)) { fd = 1023; while (is_preserved_child_fd(j, fd, j->preserved_fd_count) || minijail_fd_is_open(fd)) { --fd; if (fd < 0) { die("failed to find an unused fd"); } } } int ret = dup2(*parent_fd, fd); /* * warn() opens a file descriptor so it needs to happen after dup2 to * avoid unintended side effects. This can be avoided by reordering the * mapping requests so that the source fds with overlap are mapped * first (unless there are cycles). */ warn("mapped fd overlap: moving %d to %d", *parent_fd, fd); if (ret == -1) { return -1; } *parent_fd = fd; return 0; } /* * Check for contradictory mappings and create temporaries for parent file * descriptors that would otherwise be overwritten during redirect_fds(). */ static int prepare_preserved_fds(struct minijail *j) { /* Relocate parent_fds that would be replaced by a child_fd. */ for (size_t i = 0; i < j->preserved_fd_count; i++) { int child_fd = j->preserved_fds[i].child_fd; if (is_preserved_child_fd(j, child_fd, i)) { die("fd %d is mapped more than once", child_fd); } int *parent_fd = &j->preserved_fds[i].parent_fd; if (ensure_no_fd_conflict(j, child_fd, parent_fd, i) == -1) { return -1; } } return 0; } /* * Structure holding resources and state created when running a minijail. */ struct minijail_run_state { pid_t child_pid; int pipe_fds[2]; int stdin_fds[2]; int stdout_fds[2]; int stderr_fds[2]; int child_sync_pipe_fds[2]; char **child_env; }; /* * Move pipe_fds if they conflict with a child_fd. */ static int avoid_pipe_conflicts(struct minijail *j, struct minijail_run_state *state) { int *pipe_fds[] = { state->pipe_fds, state->child_sync_pipe_fds, state->stdin_fds, state->stdout_fds, state->stderr_fds, }; for (size_t i = 0; i < ARRAY_SIZE(pipe_fds); ++i) { if (pipe_fds[i][0] != -1 && ensure_no_fd_conflict(j, -1, &pipe_fds[i][0], j->preserved_fd_count) == -1) { return -1; } if (pipe_fds[i][1] != -1 && ensure_no_fd_conflict(j, -1, &pipe_fds[i][1], j->preserved_fd_count) == -1) { return -1; } } return 0; } /* * Redirect j->preserved_fds from the parent_fd to the child_fd. * * NOTE: This will clear FD_CLOEXEC since otherwise the child_fd would not be * inherited after the exec call. */ static int redirect_fds(struct minijail *j) { for (size_t i = 0; i < j->preserved_fd_count; i++) { if (j->preserved_fds[i].parent_fd == j->preserved_fds[i].child_fd) { // Clear CLOEXEC if it is set so the FD will be // inherited by the child. int flags = fcntl(j->preserved_fds[i].child_fd, F_GETFD); if (flags == -1 || (flags & FD_CLOEXEC) == 0) { continue; } // Currently FD_CLOEXEC is cleared without being // restored. It may make sense to track when this // happens and restore FD_CLOEXEC in the child process. flags &= ~FD_CLOEXEC; if (fcntl(j->preserved_fds[i].child_fd, F_SETFD, flags) == -1) { pwarn("failed to clear CLOEXEC for %d", j->preserved_fds[i].parent_fd); } continue; } if (dup2(j->preserved_fds[i].parent_fd, j->preserved_fds[i].child_fd) == -1) { return -1; } } /* * After all fds have been duped, we are now free to close all parent * fds that are *not* child fds. */ for (size_t i = 0; i < j->preserved_fd_count; i++) { int parent_fd = j->preserved_fds[i].parent_fd; if (!is_preserved_child_fd(j, parent_fd, j->preserved_fd_count)) { close(parent_fd); } } return 0; } static void minijail_free_run_state(struct minijail_run_state *state) { state->child_pid = -1; int *fd_pairs[] = {state->pipe_fds, state->stdin_fds, state->stdout_fds, state->stderr_fds, state->child_sync_pipe_fds}; for (size_t i = 0; i < ARRAY_SIZE(fd_pairs); ++i) { close_and_reset(&fd_pairs[i][0]); close_and_reset(&fd_pairs[i][1]); } minijail_free_env(state->child_env); state->child_env = NULL; } /* Set up stdin/stdout/stderr file descriptors in the child. */ static void setup_child_std_fds(struct minijail *j, struct minijail_run_state *state) { struct { const char *name; int from; int to; } fd_map[] = { {"stdin", state->stdin_fds[0], STDIN_FILENO}, {"stdout", state->stdout_fds[1], STDOUT_FILENO}, {"stderr", state->stderr_fds[1], STDERR_FILENO}, }; for (size_t i = 0; i < ARRAY_SIZE(fd_map); ++i) { if (fd_map[i].from == -1 || fd_map[i].from == fd_map[i].to) continue; if (dup2(fd_map[i].from, fd_map[i].to) == -1) die("failed to set up %s pipe", fd_map[i].name); } /* Close temporary pipe file descriptors. */ int *std_pipes[] = {state->stdin_fds, state->stdout_fds, state->stderr_fds}; for (size_t i = 0; i < ARRAY_SIZE(std_pipes); ++i) { close_and_reset(&std_pipes[i][0]); close_and_reset(&std_pipes[i][1]); } /* Make sure we're not trying to skip setsid() with a PID namespace. */ if (!j->flags.enable_new_sessions && j->flags.pids) { die("cannot skip setsid() with PID namespace"); } /* * If new sessions are enabled and any of stdin, stdout, or stderr are * TTYs, or setsid flag is set, create a new session. This prevents * the jailed process from using the TIOCSTI ioctl to push characters * into the parent process terminal's input buffer, therefore escaping * the jail. * * Since it has just forked, the child will not be a process group * leader, and this call to setsid() should always succeed. */ if (j->flags.enable_new_sessions && (j->flags.setsid || isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO))) { if (setsid() < 0) { pdie("setsid() failed"); } if (isatty(STDIN_FILENO)) { ioctl(STDIN_FILENO, TIOCSCTTY, 0); } } } /* * Structure that specifies how to start a minijail. * * filename - The program to exec in the child. Should be NULL if elf_fd is set. * elf_fd - A fd to be used with fexecve. Should be -1 if filename is set. * NOTE: either filename or elf_fd is required if |exec_in_child| = 1. * argv - Arguments for the child program. Required if |exec_in_child| = 1. * envp - Environment for the child program. Available if |exec_in_child| = 1. * use_preload - If true use LD_PRELOAD. * exec_in_child - If true, run |filename|. Otherwise, the child will return to * the caller. * pstdin_fd - Filled with stdin pipe if non-NULL. * pstdout_fd - Filled with stdout pipe if non-NULL. * pstderr_fd - Filled with stderr pipe if non-NULL. * pchild_pid - Filled with the pid of the child process if non-NULL. */ struct minijail_run_config { const char *filename; int elf_fd; char *const *argv; char *const *envp; int use_preload; int exec_in_child; int *pstdin_fd; int *pstdout_fd; int *pstderr_fd; pid_t *pchild_pid; }; static int minijail_run_config_internal(struct minijail *j, const struct minijail_run_config *config); int API minijail_run(struct minijail *j, const char *filename, char *const argv[]) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = NULL, .use_preload = true, .exec_in_child = true, }; return minijail_run_config_internal(j, &config); } int API minijail_run_env(struct minijail *j, const char *filename, char *const argv[], char *const envp[]) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = envp, .use_preload = true, .exec_in_child = true, }; return minijail_run_config_internal(j, &config); } int API minijail_run_pid(struct minijail *j, const char *filename, char *const argv[], pid_t *pchild_pid) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = NULL, .use_preload = true, .exec_in_child = true, .pchild_pid = pchild_pid, }; return minijail_run_config_internal(j, &config); } int API minijail_run_pipe(struct minijail *j, const char *filename, char *const argv[], int *pstdin_fd) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = NULL, .use_preload = true, .exec_in_child = true, .pstdin_fd = pstdin_fd, }; return minijail_run_config_internal(j, &config); } int API minijail_run_pid_pipes(struct minijail *j, const char *filename, char *const argv[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = NULL, .use_preload = true, .exec_in_child = true, .pstdin_fd = pstdin_fd, .pstdout_fd = pstdout_fd, .pstderr_fd = pstderr_fd, .pchild_pid = pchild_pid, }; return minijail_run_config_internal(j, &config); } int API minijail_run_env_pid_pipes(struct minijail *j, const char *filename, char *const argv[], char *const envp[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = envp, .use_preload = true, .exec_in_child = true, .pstdin_fd = pstdin_fd, .pstdout_fd = pstdout_fd, .pstderr_fd = pstderr_fd, .pchild_pid = pchild_pid, }; return minijail_run_config_internal(j, &config); } int API minijail_run_fd_env_pid_pipes(struct minijail *j, int elf_fd, char *const argv[], char *const envp[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) { struct minijail_run_config config = { .filename = NULL, .elf_fd = elf_fd, .argv = argv, .envp = envp, .use_preload = true, .exec_in_child = true, .pstdin_fd = pstdin_fd, .pstdout_fd = pstdout_fd, .pstderr_fd = pstderr_fd, .pchild_pid = pchild_pid, }; return minijail_run_config_internal(j, &config); } int API minijail_run_no_preload(struct minijail *j, const char *filename, char *const argv[]) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = NULL, .use_preload = false, .exec_in_child = true, }; return minijail_run_config_internal(j, &config); } int API minijail_run_pid_pipes_no_preload(struct minijail *j, const char *filename, char *const argv[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = NULL, .use_preload = false, .exec_in_child = true, .pstdin_fd = pstdin_fd, .pstdout_fd = pstdout_fd, .pstderr_fd = pstderr_fd, .pchild_pid = pchild_pid, }; return minijail_run_config_internal(j, &config); } int API minijail_run_env_pid_pipes_no_preload(struct minijail *j, const char *filename, char *const argv[], char *const envp[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) { struct minijail_run_config config = { .filename = filename, .elf_fd = -1, .argv = argv, .envp = envp, .use_preload = false, .exec_in_child = true, .pstdin_fd = pstdin_fd, .pstdout_fd = pstdout_fd, .pstderr_fd = pstderr_fd, .pchild_pid = pchild_pid, }; return minijail_run_config_internal(j, &config); } pid_t API minijail_fork(struct minijail *j) { struct minijail_run_config config = { .elf_fd = -1, }; return minijail_run_config_internal(j, &config); } static int minijail_run_internal(struct minijail *j, const struct minijail_run_config *config, struct minijail_run_state *state_out) { int sync_child = 0; int ret; /* We need to remember this across the minijail_preexec() call. */ int pid_namespace = j->flags.pids; /* * Create an init process if we are entering a pid namespace, unless the * user has explicitly opted out by calling minijail_run_as_init(). */ int do_init = j->flags.do_init && !j->flags.run_as_init; int use_preload = config->use_preload; if (config->filename != NULL && config->elf_fd != -1) { die("filename and elf_fd cannot be set at the same time"); } if (config->filename != NULL) { j->filename = strdup(config->filename); } /* * Only copy the environment if we need to modify it. If this is done * unconditionally, it triggers odd behavior in the ARC container. */ if (use_preload || j->seccomp_policy_path) { state_out->child_env = minijail_copy_env(config->envp ? config->envp : environ); if (!state_out->child_env) return ENOMEM; } if (j->seccomp_policy_path && setup_seccomp_policy_path(j, &state_out->child_env)) return -EFAULT; if (use_preload) { if (j->hooks_head != NULL) die("Minijail hooks are not supported with LD_PRELOAD"); if (!config->exec_in_child) die("minijail_fork is not supported with LD_PRELOAD"); /* * Before we fork(2) and execve(2) the child process, we need * to open a pipe(2) to send the minijail configuration over. */ if (setup_preload(j, &state_out->child_env) || setup_pipe(&state_out->child_env, state_out->pipe_fds)) return -EFAULT; } else { if (j->flags.use_caps && j->caps != 0 && !j->flags.set_ambient_caps) { die("non-empty, non-ambient capabilities are not " "supported without LD_PRELOAD"); } } /* Create pipes for stdin/stdout/stderr as requested by caller. */ struct { bool requested; int *pipe_fds; } pipe_fd_req[] = { {config->pstdin_fd != NULL, state_out->stdin_fds}, {config->pstdout_fd != NULL, state_out->stdout_fds}, {config->pstderr_fd != NULL, state_out->stderr_fds}, }; for (size_t i = 0; i < ARRAY_SIZE(pipe_fd_req); ++i) { if (pipe_fd_req[i].requested && pipe(pipe_fd_req[i].pipe_fds) == -1) return EFAULT; } /* * If the parent process needs to configure the child's runtime * environment after forking, create a pipe(2) to block the child until * configuration is done. */ if (j->flags.forward_signals || j->flags.pid_file || j->flags.cgroups || j->rlimit_count || j->flags.userns) { sync_child = 1; if (pipe(state_out->child_sync_pipe_fds)) return -EFAULT; } /* * Use sys_clone() if and only if we're creating a pid namespace. * * tl;dr: WARNING: do not mix pid namespaces and multithreading. * * In multithreaded programs, there are a bunch of locks inside libc, * some of which may be held by other threads at the time that we call * minijail_run_pid(). If we call fork(), glibc does its level best to * ensure that we hold all of these locks before it calls clone() * internally and drop them after clone() returns, but when we call * sys_clone(2) directly, all that gets bypassed and we end up with a * child address space where some of libc's important locks are held by * other threads (which did not get cloned, and hence will never release * those locks). This is okay so long as we call exec() immediately * after, but a bunch of seemingly-innocent libc functions like setenv() * take locks. * * Hence, only call sys_clone() if we need to, in order to get at pid * namespacing. If we follow this path, the child's address space might * have broken locks; you may only call functions that do not acquire * any locks. * * Unfortunately, fork() acquires every lock it can get its hands on, as * previously detailed, so this function is highly likely to deadlock * later on (see "deadlock here") if we're multithreaded. * * We might hack around this by having the clone()d child (init of the * pid namespace) return directly, rather than leaving the clone()d * process hanging around to be init for the new namespace (and having * its fork()ed child return in turn), but that process would be * crippled with its libc locks potentially broken. We might try * fork()ing in the parent before we clone() to ensure that we own all * the locks, but then we have to have the forked child hanging around * consuming resources (and possibly having file descriptors / shared * memory regions / etc attached). We'd need to keep the child around to * avoid having its children get reparented to init. * * TODO(b/317404364): figure out if the "forked child hanging around" * problem is fixable or not. It would be nice if we worked in this * case. */ pid_t child_pid; if (pid_namespace) { unsigned long clone_flags = CLONE_NEWPID | SIGCHLD; if (j->flags.userns) clone_flags |= CLONE_NEWUSER; child_pid = syscall(SYS_clone, clone_flags, NULL, 0L, 0L, 0L); if (child_pid < 0) { if (errno == EPERM) pdie("clone(CLONE_NEWPID | ...) failed with " "EPERM; is this process missing " "CAP_SYS_ADMIN?"); pdie("clone(CLONE_NEWPID | ...) failed"); } } else { if (j->flags.userns) die("user namespaces in Minijail require a PID " "namespace"); child_pid = fork(); if (child_pid < 0) pdie("fork failed"); } /* * setup_fs_rules_fd() needs to be called before close_open_fds(), and * before logic for the child process. */ if (j->fs_rules_head) { setup_fs_rules_fd(j); minijail_preserve_fd(j, j->fs_rules_fd, j->fs_rules_fd); } state_out->child_pid = child_pid; if (child_pid) { j->initpid = child_pid; if (j->flags.forward_signals) { forward_pid = child_pid; install_signal_handlers(); } if (j->flags.pid_file) write_pid_file_or_die(j); if (j->flags.cgroups) add_to_cgroups_or_die(j); if (j->rlimit_count) set_rlimits_or_die(j); if (j->flags.userns) write_ugid_maps_or_die(j); if (j->flags.enter_vfs) close(j->mountns_fd); if (j->flags.enter_net) close(j->netns_fd); if (sync_child) parent_setup_complete(state_out->child_sync_pipe_fds); if (use_preload) { /* * Add SIGPIPE to the signal mask to avoid getting * killed if the child process finishes or closes its * end of the pipe prematurely. * * TODO(crbug.com/1022170): Use pthread_sigmask instead * of sigprocmask if Minijail is used in multithreaded * programs. */ sigset_t to_block, to_restore; if (sigemptyset(&to_block) < 0) pdie("sigemptyset failed"); if (sigaddset(&to_block, SIGPIPE) < 0) pdie("sigaddset failed"); if (sigprocmask(SIG_BLOCK, &to_block, &to_restore) < 0) pdie("sigprocmask failed"); /* Send marshalled minijail. */ close_and_reset(&state_out->pipe_fds[0]); ret = minijail_to_fd(j, state_out->pipe_fds[1]); close_and_reset(&state_out->pipe_fds[1]); /* Accept any pending SIGPIPE. */ while (true) { const struct timespec zero_time = {0, 0}; const int sig = sigtimedwait(&to_block, NULL, &zero_time); if (sig < 0) { if (errno != EINTR) break; } else { if (sig != SIGPIPE) die("unexpected signal %d", sig); } } /* Restore the signal mask to its original state. */ if (sigprocmask(SIG_SETMASK, &to_restore, NULL) < 0) pdie("sigprocmask failed"); if (ret) { warn("failed to send marshalled minijail: %s", strerror(-ret)); kill(j->initpid, SIGKILL); } } return 0; } /* Child process. */ if (j->flags.reset_signal_mask) { sigset_t signal_mask; if (sigemptyset(&signal_mask) != 0) pdie("sigemptyset failed"); if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) pdie("sigprocmask failed"); } if (j->flags.reset_signal_handlers) { int signum; for (signum = 0; signum <= SIGRTMAX; signum++) { /* * Ignore EINVAL since some signal numbers in the range * might not be valid. */ if (signal(signum, SIG_DFL) == SIG_ERR && errno != EINVAL) { pdie("failed to reset signal %d disposition", signum); } } } if (j->flags.close_open_fds) { const size_t kMaxInheritableFdsSize = 11 + MAX_PRESERVED_FDS; int inheritable_fds[kMaxInheritableFdsSize]; size_t size = 0; int *pipe_fds[] = { state_out->pipe_fds, state_out->child_sync_pipe_fds, state_out->stdin_fds, state_out->stdout_fds, state_out->stderr_fds, }; for (size_t i = 0; i < ARRAY_SIZE(pipe_fds); ++i) { if (pipe_fds[i][0] != -1) { inheritable_fds[size++] = pipe_fds[i][0]; } if (pipe_fds[i][1] != -1) { inheritable_fds[size++] = pipe_fds[i][1]; } } /* * Preserve namespace file descriptors over the close_open_fds() * call. These are closed in minijail_enter() so they won't leak * into the child process. */ if (j->flags.enter_vfs) minijail_preserve_fd(j, j->mountns_fd, j->mountns_fd); if (j->flags.enter_net) minijail_preserve_fd(j, j->netns_fd, j->netns_fd); for (size_t i = 0; i < j->preserved_fd_count; i++) { /* * Preserve all parent_fds. They will be dup2(2)-ed in * the child later. */ inheritable_fds[size++] = j->preserved_fds[i].parent_fd; } if (config->elf_fd > -1) { inheritable_fds[size++] = config->elf_fd; } if (close_open_fds(inheritable_fds, size) < 0) die("failed to close open file descriptors"); } /* The set of fds will be replaced. */ if (prepare_preserved_fds(j)) die("failed to set up fd redirections"); if (avoid_pipe_conflicts(j, state_out)) die("failed to redirect conflicting pipes"); /* The elf_fd needs to be mutable so use a stack copy from now on. */ int elf_fd = config->elf_fd; if (elf_fd != -1 && ensure_no_fd_conflict(j, -1, &elf_fd, j->preserved_fd_count)) die("failed to redirect elf_fd"); if (redirect_fds(j)) die("failed to set up fd redirections"); if (sync_child) wait_for_parent_setup(state_out->child_sync_pipe_fds); if (j->flags.userns) enter_user_namespace(j); setup_child_std_fds(j, state_out); /* If running an init program, let it decide when/how to mount /proc. */ if (pid_namespace && !do_init) j->flags.remount_proc_ro = 0; if (use_preload) { /* Strip out flags that cannot be inherited across execve(2). */ minijail_preexec(j); } else { /* * If not using LD_PRELOAD, do all jailing before execve(2). * Note that PID namespaces can only be entered on fork(2), * so that flag is still cleared. */ j->flags.pids = 0; } /* * Jail this process. * If forking, return. * If not, execve(2) the target. */ minijail_enter(j); if (config->exec_in_child && pid_namespace && do_init) { /* * pid namespace: this process will become init inside the new * namespace. We don't want all programs we might exec to have * to know how to be init. Normally (do_init == 1) we fork off * a child to actually run the program. If |do_init == 0|, we * let the program keep pid 1 and be init. * * If we're multithreaded, we'll probably deadlock here. See * WARNING above. */ child_pid = fork(); if (child_pid < 0) { _exit(child_pid); } else if (child_pid > 0) { minijail_free_run_state(state_out); /* * Best effort. Don't bother checking the return value. */ prctl(PR_SET_NAME, "minijail-init"); init(child_pid); /* Never returns. */ } state_out->child_pid = child_pid; } run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_EXECVE); if (!config->exec_in_child) return 0; /* * We're going to execve(), so make sure any remaining resources are * freed. Exceptions are: * 1. The child environment. No need to worry about freeing it since * execve reinitializes the heap anyways. * 2. The read side of the LD_PRELOAD pipe, which we need to hand down * into the target in which the preloaded code will read from it and * then close it. */ state_out->pipe_fds[0] = -1; char *const *child_env = state_out->child_env; state_out->child_env = NULL; minijail_free_run_state(state_out); /* * If we aren't pid-namespaced, or the jailed program asked to be init: * calling process * -> execve()-ing process * If we are: * calling process * -> init()-ing process * -> execve()-ing process */ if (!child_env) child_env = config->envp ? config->envp : environ; if (elf_fd > -1) { fexecve(elf_fd, config->argv, child_env); pwarn("fexecve(%d) failed", config->elf_fd); } else { execve(config->filename, config->argv, child_env); pwarn("execve(%s) failed", config->filename); } ret = (errno == ENOENT ? MINIJAIL_ERR_NO_COMMAND : MINIJAIL_ERR_NO_ACCESS); _exit(ret); } static int minijail_run_config_internal(struct minijail *j, const struct minijail_run_config *config) { struct minijail_run_state state = { .child_pid = -1, .pipe_fds = {-1, -1}, .stdin_fds = {-1, -1}, .stdout_fds = {-1, -1}, .stderr_fds = {-1, -1}, .child_sync_pipe_fds = {-1, -1}, .child_env = NULL, }; int ret = minijail_run_internal(j, config, &state); if (ret == 0) { if (config->pchild_pid) *config->pchild_pid = state.child_pid; /* Grab stdin/stdout/stderr descriptors requested by caller. */ struct { int *pfd; int *psrc; } fd_map[] = { {config->pstdin_fd, &state.stdin_fds[1]}, {config->pstdout_fd, &state.stdout_fds[0]}, {config->pstderr_fd, &state.stderr_fds[0]}, }; for (size_t i = 0; i < ARRAY_SIZE(fd_map); ++i) { if (fd_map[i].pfd) { *fd_map[i].pfd = *fd_map[i].psrc; *fd_map[i].psrc = -1; } } if (!config->exec_in_child) ret = state.child_pid; } minijail_free_run_state(&state); return ret; } static int minijail_wait_internal(struct minijail *j, int expected_signal) { if (j->initpid <= 0) return -ECHILD; int st; while (true) { const int ret = waitpid(j->initpid, &st, 0); if (ret >= 0) break; if (errno != EINTR) return -errno; } if (!WIFEXITED(st)) { int error_status = st; if (!WIFSIGNALED(st)) { return error_status; } int signum = WTERMSIG(st); /* * We return MINIJAIL_ERR_SECCOMP_VIOLATION if the process * received SIGSYS, which happens when a syscall is blocked by * SECCOMP filters. * * If not, we do what bash(1) does: $? = 128 + signum */ if (signum == SIGSYS) { warn("child process %d had a policy violation (%s)", j->initpid, j->seccomp_policy_path ? j->seccomp_policy_path : "NO-LABEL"); error_status = MINIJAIL_ERR_SECCOMP_VIOLATION; } else { if (signum != expected_signal) { warn("child process %d received signal %d", j->initpid, signum); } error_status = MINIJAIL_ERR_SIG_BASE + signum; } return error_status; } int exit_status = WEXITSTATUS(st); if (exit_status != 0) info("child process %d exited with status %d", j->initpid, exit_status); return exit_status; } int API minijail_kill(struct minijail *j) { if (j->initpid <= 0) return -ECHILD; if (kill(j->initpid, SIGTERM)) return -errno; return minijail_wait_internal(j, SIGTERM); } int API minijail_wait(struct minijail *j) { return minijail_wait_internal(j, 0); } void API minijail_destroy(struct minijail *j) { size_t i; if (j->filter_prog) { free(j->filter_prog->filter); free(j->filter_prog); } free_mounts_list(j); free_remounts_list(j); while (j->hooks_head) { struct hook *c = j->hooks_head; j->hooks_head = c->next; free(c); } j->hooks_tail = NULL; free_fs_rules_list(j); if (j->user) free(j->user); if (j->suppl_gid_list) free(j->suppl_gid_list); if (j->chrootdir) free(j->chrootdir); if (j->pid_file_path) free(j->pid_file_path); if (j->uidmap) free(j->uidmap); if (j->gidmap) free(j->gidmap); if (j->hostname) free(j->hostname); if (j->preload_path) free(j->preload_path); if (j->filename) free(j->filename); if (j->alt_syscall_table) free(j->alt_syscall_table); for (i = 0; i < j->cgroup_count; ++i) free(j->cgroups[i]); if (j->seccomp_policy_path) free(j->seccomp_policy_path); free(j); } void API minijail_log_to_fd(int fd, int min_priority) { init_logging(LOG_TO_FD, fd, min_priority); } const char API *minijail_syscall_name(const struct minijail *j, long nr) { if (j && j->flags.alt_syscall) return kAltSyscallNamePlaceholder; return lookup_syscall_name(nr); } minijail-2026.05.18+git20261016.8787d1a/libminijail.h000066400000000000000000000661431526451555500210010ustar00rootroot00000000000000/* Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* * The general pattern of use here: * 1) Construct a minijail with minijail_new() * 2) Apply the desired restrictions to it * 3) Enter it, which locks the current process inside it, or: * 3) Run a process inside it * 4) Destroy it. */ #ifndef _LIBMINIJAIL_H_ #define _LIBMINIJAIL_H_ #include #include #include #include /* * Rust's bindgen needs the actual definition of sock_fprog in order to * generate usable bindings. */ #ifdef USE_BINDGEN #include #endif /* * The specified function arguments may not be NULL. Params starts counting * from 1, not 0. If no params are specified, then all function arguments are * marked as non-NULL. Thus, params should only be specified if a function * accepts NULL pointers for any of the arguments. * NB: Keep in sync with util.h style. */ #define MINIJAIL_ATTRIBUTE_NONNULL(params) __attribute__((__nonnull__ params)) #ifdef __cplusplus extern "C" { #endif /* Possible exit status codes returned by minijail_wait(). */ enum { /* Command can be found but cannot be run */ MINIJAIL_ERR_NO_ACCESS = 126, /* Command cannot be found */ MINIJAIL_ERR_NO_COMMAND = 127, /* * `MINIJAIL_ERR_SIG_BASE + n` if the process was killed by the signal * `n` other than `SIGSYS` */ MINIJAIL_ERR_SIG_BASE = 128, /* Cannot mount a file or folder in mount namespace */ MINIJAIL_ERR_MOUNT = 251, MINIJAIL_ERR_PRELOAD = 252, /* * Process killed by `SIGSYS`, which is the sign of a SECCOMP violation */ MINIJAIL_ERR_SECCOMP_VIOLATION = 253, MINIJAIL_ERR_JAIL = 253, MINIJAIL_ERR_INIT = 254, }; struct minijail; struct sock_fprog; /* * A hook that can be used to execute code at various events during minijail * setup in the forked process. These can only be used if the jailed process is * not going to be invoked with LD_PRELOAD. * * If the return value is non-zero, it will be interpreted as -errno and the * process will abort. */ typedef int (*minijail_hook_t)(void *context); /* * The events during minijail setup in which hooks can run. All the events are * run in the new process. */ typedef enum { /* The hook will run just before dropping capabilities. */ MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS, /* The hook will run just before calling execve(2). */ MINIJAIL_HOOK_EVENT_PRE_EXECVE, /* The hook will run just before calling chroot(2) / pivot_root(2). */ MINIJAIL_HOOK_EVENT_PRE_CHROOT, /* Sentinel for error checking. Must be last. */ MINIJAIL_HOOK_EVENT_MAX, } minijail_hook_event_t; /* Allocates a new minijail with no restrictions. */ struct minijail *minijail_new(void); /* * These functions add restrictions to the minijail. They are not applied until * minijail_enter() is called. See the documentation in minijail0.1 for * explanations in detail of what the restrictions do. */ void minijail_change_uid(struct minijail *j, uid_t uid) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_change_gid(struct minijail *j, gid_t gid) MINIJAIL_ATTRIBUTE_NONNULL(); /* Copies |list|. */ void minijail_set_supplementary_gids(struct minijail *j, size_t size, const gid_t *list) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_keep_supplementary_gids(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* Stores user to change to and copies |user| for internal consistency. */ int minijail_change_user(struct minijail *j, const char *user) MINIJAIL_ATTRIBUTE_NONNULL(); /* Does not take ownership of |group|. */ int minijail_change_group(struct minijail *j, const char *group) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_use_seccomp(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_no_new_privs(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_use_seccomp_filter(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_set_seccomp_filter_tsync(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* Sets using_minimalistic_mountns to true. */ void minijail_set_using_minimalistic_mountns(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_set_enable_default_runtime(struct minijail *j, bool enable_default_runtime) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_set_enable_new_sessions(struct minijail *j, bool enable_new_sessions) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Sets enable_profile_fs_restrictions to true. * * Exposed for unit tests and allowlisting services. */ bool minijail_get_enable_default_runtime(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_disable_fs_restrictions(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_set_enable_profile_fs_restrictions(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_add_minimalistic_mountns_fs_rules(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_enable_default_fs_restrictions(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Allow speculative execution features that may cause data leaks across * processes, by setting the SECCOMP_FILTER_FLAG_SPEC_ALLOW seccomp flag. * * WARNING: Enabling this may make the process vulnerable to speculative * execution attacks (Branch Target Injection, and Speculative Store Bypass). * This is only safe to use for processes that do not execute untrusted code. */ void minijail_set_seccomp_filter_allow_speculation(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* Does not take ownership of |filter|. */ void minijail_set_seccomp_filters(struct minijail *j, const struct sock_fprog *filter) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_parse_seccomp_filters(struct minijail *j, const char *path) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_parse_seccomp_filters_from_fd(struct minijail *j, int fd) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_log_seccomp_filter_failures(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* 'minijail_use_caps' and 'minijail_capbset_drop' are mutually exclusive. */ void minijail_use_caps(struct minijail *j, uint64_t capmask) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_capbset_drop(struct minijail *j, uint64_t capmask) MINIJAIL_ATTRIBUTE_NONNULL(); /* 'minijail_set_ambient_caps' requires 'minijail_use_caps'. */ void minijail_set_ambient_caps(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_reset_signal_mask(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_reset_signal_handlers(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_namespace_vfs(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_namespace_enter_vfs(struct minijail *j, const char *ns_path) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_new_session_keyring(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_skip_setting_securebits(struct minijail *j, uint64_t securebits_skip_mask) MINIJAIL_ATTRIBUTE_NONNULL(); /* * This option is *dangerous* as it negates most of the functionality of * minijail_namespace_vfs(). You very likely don't need this. */ void minijail_skip_remount_private(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_remount_mode(struct minijail *j, unsigned long mode) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_namespace_ipc(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_namespace_uts(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); int minijail_namespace_set_hostname(struct minijail *j, const char *name) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Starts a new network namespace with or without a loopback interface. * It is rare for jails to not bring up the loopback interface, and having it * available is not a security concern. Please double check you actually need to * disable it before using this API. * * @j minijail to apply restriction to. * @enable_loopback indicates whether the loopback interface should be enabled * in this new network namespace. */ void minijail_namespace_net_loopback(struct minijail *j, bool enable_loopback) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Starts a new network namespace featuring a loopback interface. * This has the same effect as `minijail_namespace_net_loopback(j, true)`. */ void minijail_namespace_net(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_namespace_enter_net(struct minijail *j, const char *ns_path) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_namespace_cgroups(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* Closes all open file descriptors after forking. */ void minijail_close_open_fds(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Implies namespace_vfs and remount_proc_readonly. * WARNING: this is NOT THREAD SAFE. See the block comment in . */ void minijail_namespace_pids(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Implies namespace_vfs. * WARNING: this is NOT THREAD SAFE. See the block comment in . * Minijail will by default remount /proc read-only when using a PID namespace. * Certain complex applications expect to be able to do their own sandboxing * which might require writing to /proc, so support a weaker version of PID * namespacing with a RW /proc. */ void minijail_namespace_pids_rw_proc(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_namespace_user(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_namespace_user_disable_setgroups(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); int minijail_uidmap(struct minijail *j, const char *uidmap) MINIJAIL_ATTRIBUTE_NONNULL(); int minijail_gidmap(struct minijail *j, const char *gidmap) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_remount_proc_readonly(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_run_as_init(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); int minijail_write_pid_file(struct minijail *j, const char *path) MINIJAIL_ATTRIBUTE_NONNULL(); void minijail_inherit_usergroups(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Changes the jailed process's syscall table to the alt_syscall table * named |table|. */ int minijail_use_alt_syscall(struct minijail *j, const char *table) MINIJAIL_ATTRIBUTE_NONNULL(); /* Sets the given runtime limit. See getrlimit(2). */ int minijail_rlimit(struct minijail *j, int type, rlim_t cur, rlim_t max) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Adds the jailed process to the cgroup given by |path|. |path| should be the * full path to the cgroups "tasks" file. * Example: /sys/fs/cgroup/cpu/jailed_procs/tasks adds to the "jailed_procs" cpu * cgroup. */ int minijail_add_to_cgroup(struct minijail *j, const char *path) MINIJAIL_ATTRIBUTE_NONNULL(); /* * These functions are used for filesystem restrictions. */ /* * Indicates whether minijail_add_fs_restriction_* functions are available. Even * if they are unavailable, minijail_add_fs_restriction_* functions would still * succeed, however without any actual effects. */ bool minijail_is_fs_restriction_available(void); void minijail_set_landlock_abi(struct minijail *j, int abi) MINIJAIL_ATTRIBUTE_NONNULL(); /* Adds a read-execute path. */ int minijail_add_fs_restriction_rx(struct minijail *j, const char *path) MINIJAIL_ATTRIBUTE_NONNULL(); /* Adds a read-only path. */ int minijail_add_fs_restriction_ro(struct minijail *j, const char *path) MINIJAIL_ATTRIBUTE_NONNULL(); /* Adds a path with read and basic write permissions. */ int minijail_add_fs_restriction_rw(struct minijail *j, const char *path) MINIJAIL_ATTRIBUTE_NONNULL(); /* Adds a path with read and advanced write permissions. */ int minijail_add_fs_restriction_advanced_rw(struct minijail *j, const char *path) MINIJAIL_ATTRIBUTE_NONNULL(); /* Adds a path with read and write permissions that exclude create. */ int minijail_add_fs_restriction_edit(struct minijail *j, const char *path) MINIJAIL_ATTRIBUTE_NONNULL(); /* Adds a path with permissions based on a bitmask of Landlock flags. */ int minijail_add_fs_restriction_access_rights(struct minijail *j, const char *path, uint16_t landlock_flags) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Indicates whether a filesystem restriction ruleset has been initialized. * * Exposed primarily for testing. * * This only indicates initialization status, not sandboxing status. */ bool minijail_is_fs_restriction_ruleset_initialized(const struct minijail *j); /* * Install signal handlers in the minijail process that forward received * signals to the jailed child process. */ int minijail_forward_signals(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* The jailed child process should call setsid() to create a new session. */ int minijail_create_session(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_enter_chroot: enables chroot() restriction for @j * @j minijail to apply restriction to * @dir directory to chroot() to. Owned by caller. * * Enters @dir, binding all bind mounts specified with minijail_bind() into * place. Requires @dir to contain all necessary directories for bind mounts * (i.e., if you have requested a bind mount at /etc, /etc must exist in @dir.) * * Returns 0 on success. */ int minijail_enter_chroot(struct minijail *j, const char *dir) MINIJAIL_ATTRIBUTE_NONNULL(); int minijail_enter_pivot_root(struct minijail *j, const char *dir) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_get_original_path: returns the path of a given file outside of the * chroot. * @j minijail to obtain the path from. * @chroot_path path inside of the chroot() to. * * When executing a binary in a chroot or pivot_root, return path to the binary * outside of the chroot. * * Returns a string containing the path. This must be freed by the caller. */ char *minijail_get_original_path(struct minijail *j, const char *chroot_path) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_mount_tmp: enables mounting of a 64M tmpfs filesystem on /tmp. * As be rules of bind mounts, /tmp must exist in chroot. */ void minijail_mount_tmp(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_mount_tmp_size: enables mounting of a tmpfs filesystem on /tmp. * As be rules of bind mounts, /tmp must exist in chroot. Size is in bytes. */ void minijail_mount_tmp_size(struct minijail *j, size_t size) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_mount_dev: enables mounting of a tmpfs filesystem on /dev. * It will then be seeded with a basic set of device nodes. For the exact * list, consult the minijail(0) man page. */ void minijail_mount_dev(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_mount_with_data: when entering minijail @j, * mounts @src at @dst with @flags and @data. * @j minijail to bind inside * @src source to bind * @dest location to bind (inside chroot) * @type type of filesystem * @flags flags passed to mount * @data data arguments passed to mount(2), e.g. "mode=755" * * This may be called multiple times; all mounts will be applied in the order * of minijail_mount() calls. * If @flags is 0, then MS_NODEV | MS_NOEXEC | MS_NOSUID will be used instead. * If @data is NULL or "", and @type is tmpfs, then "mode=0755,size=10M" will * be used instead. */ int minijail_mount_with_data(struct minijail *j, const char *src, const char *dest, const char *type, unsigned long flags, const char *data) MINIJAIL_ATTRIBUTE_NONNULL((1, 2, 3, 4)); /* * minijail_mount: when entering minijail @j, mounts @src at @dst with @flags * @j minijail to bind inside * @src source to bind * @dest location to bind (inside chroot) * @type type of filesystem * @flags flags passed to mount * * This may be called multiple times; all mounts will be applied in the order * of minijail_mount() calls. */ int minijail_mount(struct minijail *j, const char *src, const char *dest, const char *type, unsigned long flags) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_bind: bind-mounts @src into @j as @dest, optionally writeable * @j minijail to bind inside * @src source to bind * @dest location to bind (inside chroot) * @writeable 1 if the bind mount should be writeable * * This may be called multiple times; all bindings will be applied in the order * of minijail_bind() calls. */ int minijail_bind(struct minijail *j, const char *src, const char *dest, int writeable) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_add_remount: when entering minijail @j, remounts @mount_name and all * subdirectories as @remount_mode rather than the default MS_PRIVATE * @j minijail to bind inside * @mount_name mount to remount * @remount_mode remount mode to use * * This may be called multiple times; this overrides |j->remount_mode| for the * given mount. */ int minijail_add_remount(struct minijail *j, const char *mount_name, unsigned long remount_mode) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_add_hook: adds @hook to the list of hooks that will be * invoked when @event is reached during minijail setup. The caller is * responsible for the lifetime of @payload. * @j minijail to add the hook to * @hook the function that will be invoked * @payload an opaque pointer * @event the event that will trigger the hook */ int minijail_add_hook(struct minijail *j, minijail_hook_t hook, void *payload, minijail_hook_event_t event) MINIJAIL_ATTRIBUTE_NONNULL((1, 2)); /* * minijail_preserve_fd: preserves @parent_fd and makes it available as * @child_fd in the child process. @parent_fd will be closed if no other * redirect has claimed it as a @child_fd. This works even if * minijail_close_open_fds() is invoked. * @j minijail to add the fd to * @parent_fd the fd in the parent process * @child_fd the fd that will be available in the child process */ int minijail_preserve_fd(struct minijail *j, int parent_fd, int child_fd) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_set_preload_path: overrides the default path for * libminijailpreload.so. */ int minijail_set_preload_path(struct minijail *j, const char *preload_path) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Lock this process into the given minijail. Note that this procedure cannot * fail, since there is no way to undo privilege-dropping; therefore, if any * part of the privilege-drop fails, minijail_enter() will abort the entire * process. * * Some restrictions cannot be enabled this way (pid namespaces) and attempting * to do so will cause an abort. */ void minijail_enter(const struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Run the specified command in the given minijail, execve(2)-style. * Pass |envp| as the full environment for the child. */ int minijail_run_env(struct minijail *j, const char *filename, char *const argv[], char *const envp[]) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Run the specified command in the given minijail, execve(2)-style. * If minijail_namespace_pids() or minijail_namespace_user() are used, * this or minijail_fork() is required instead of minijail_enter(). */ int minijail_run(struct minijail *j, const char *filename, char *const argv[]) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Run the specified command in the given minijail, execve(2)-style. * Don't use LD_PRELOAD to do privilege dropping. This is useful when sandboxing * static binaries, or on systems without support for LD_PRELOAD. */ int minijail_run_no_preload(struct minijail *j, const char *filename, char *const argv[]) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Run the specified command in the given minijail, execve(2)-style. * Update |*pchild_pid| with the pid of the child. */ int minijail_run_pid(struct minijail *j, const char *filename, char *const argv[], pid_t *pchild_pid) MINIJAIL_ATTRIBUTE_NONNULL((1, 2, 3)); /* * Run the specified command in the given minijail, execve(2)-style. * Update |*pstdin_fd| with a fd that allows writing to the child's * standard input. */ int minijail_run_pipe(struct minijail *j, const char *filename, char *const argv[], int *pstdin_fd) MINIJAIL_ATTRIBUTE_NONNULL((1, 2, 3)); /* * Run the specified command in the given minijail, execve(2)-style. * Update |*pchild_pid| with the pid of the child. * Update |*pstdin_fd| with a fd that allows writing to the child's * standard input. * Update |*pstdout_fd| with a fd that allows reading from the child's * standard output. * Update |*pstderr_fd| with a fd that allows reading from the child's * standard error. */ int minijail_run_pid_pipes(struct minijail *j, const char *filename, char *const argv[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) MINIJAIL_ATTRIBUTE_NONNULL((1, 2, 3)); /* * Run the specified command in the given minijail, execve(2)-style. * Pass |envp| as the full environment for the child. * Update |*pchild_pid| with the pid of the child. * Update |*pstdin_fd| with a fd that allows writing to the child's * standard input. * Update |*pstdout_fd| with a fd that allows reading from the child's * standard output. * Update |*pstderr_fd| with a fd that allows reading from the child's * standard error. */ int minijail_run_env_pid_pipes(struct minijail *j, const char *filename, char *const argv[], char *const envp[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) MINIJAIL_ATTRIBUTE_NONNULL((1, 2, 3)); /* * Execute the specified file descriptor in the given minijail, * fexecve(3)-style. * Pass |envp| as the full environment for the child or NULL to inherit. * Update |*pchild_pid| with the pid of the child. * Update |*pstdin_fd| with a fd that allows writing to the child's * standard input. * Update |*pstdout_fd| with a fd that allows reading from the child's * standard output. * Update |*pstderr_fd| with a fd that allows reading from the child's * standard error. */ int minijail_run_fd_env_pid_pipes(struct minijail *j, int elf_fd, char *const argv[], char *const envp[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) MINIJAIL_ATTRIBUTE_NONNULL((1, 3)); /* * Run the specified command in the given minijail, execve(2)-style. * Update |*pchild_pid| with the pid of the child. * Update |*pstdin_fd| with a fd that allows writing to the child's * standard input. * Update |*pstdout_fd| with a fd that allows reading from the child's * standard output. * Update |*pstderr_fd| with a fd that allows reading from the child's * standard error. * Don't use LD_PRELOAD to do privilege dropping. This is useful when sandboxing * static binaries, or on systems without support for LD_PRELOAD. */ int minijail_run_pid_pipes_no_preload(struct minijail *j, const char *filename, char *const argv[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) MINIJAIL_ATTRIBUTE_NONNULL((1, 2, 3)); /* * Run the specified command in the given minijail, execve(2)-style. * Pass |envp| as the full environment for the child. * Update |*pchild_pid| with the pid of the child. * Update |*pstdin_fd| with a fd that allows writing to the child's * standard input. * Update |*pstdout_fd| with a fd that allows reading from the child's * standard output. * Update |*pstderr_fd| with a fd that allows reading from the child's * standard error. * Don't use LD_PRELOAD to do privilege dropping. This is useful when sandboxing * static binaries, or on systems without support for LD_PRELOAD. */ int minijail_run_env_pid_pipes_no_preload( struct minijail *j, const char *filename, char *const argv[], char *const envp[], pid_t *pchild_pid, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd) MINIJAIL_ATTRIBUTE_NONNULL((1, 2, 3)); /* * Fork, jail the child, and return. This behaves similar to fork(2), except it * puts the child process in a jail before returning. * `minijail_fork` returns in both the parent and the child. The pid of the * child is returned to the parent. Zero is returned in the child. LD_PRELOAD * is not supported. * If minijail_namespace_pids() or minijail_namespace_user() are used, * this or minijail_run*() is required instead of minijail_enter(). */ pid_t minijail_fork(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Send SIGTERM to the process in the minijail and wait for it to terminate. * * Return the same nonnegative exit status as minijail_wait(), or a negative * error code (eg -ESRCH if the process has already been waited for). * * This is most useful if the minijail has been created with PID namespacing * since, in this case, all processes inside it are atomically killed. */ int minijail_kill(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Wait for the first process spawned in the specified minijail to exit, and * return its exit status. A process can only be waited once. * * Return: * * A negative error code if the process cannot be waited for (eg `-ECHILD` * if no process has been started or if the process has already been waited * for). * * `MINIJAIL_ERR_NO_COMMAND` if the command cannot be found. * * `MINIJAIL_ERR_NO_ACCESS` if the command can be found but cannot be run. * * `MINIJAIL_ERR_SECCOMP_VIOLATION` if the process was killed by `SIGSYS`, * which is the sign of a SECCOMP violation. * * `MINIJAIL_ERR_SIG_BASE + n` if the process was killed by the signal `n` * other than `SIGSYS`. * * `n & 0xFF` if the process finished by returning the code `n`. */ int minijail_wait(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Frees the given minijail. It does not matter if the process is inside the * minijail or not. It will not kill the process, see minijail_kill() if that is * desired. */ void minijail_destroy(struct minijail *j) MINIJAIL_ATTRIBUTE_NONNULL(); /* * Deep copies the minijail in |from| to |out| providing two identical jails * that can be used to contain separate children created with minijail_fork(). * * Duplicating a jail is invalid after a jail has been passed to * minijail_fork(). Many minijail_*() calls will yield undefined * results when called on a jail duplicated post-fork. */ int minijail_copy_jail(const struct minijail *from, struct minijail *out) MINIJAIL_ATTRIBUTE_NONNULL(); /* * minijail_log_to_fd: redirects the module-wide logging to an FD instead of * syslog. * @fd FD to log to. Caller must ensure this is available after * jailing (e.g. with minijail_preserve_fd()). * @min_priority the minimum logging priority. Same as the priority argument * to syslog(2). */ void minijail_log_to_fd(int fd, int min_priority); /* * minijail_syscall_name: Returns the name of the provided system call on the * current architecture. This is exposed to make tasks like including the * system call name in crash reports possible. Note this does not support * alt-syscall or other supported ABIs such as x86 (32-bit) when this is built * for x86_64 (64-bit). * @j Optional minijail struct to check if features that affect * syscall tables. If NULL, the current syscall ABI is used. * @nr The system call number. * * Returns the system call name if found otherwise NULL. */ const char *minijail_syscall_name(const struct minijail *j, long nr); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !_LIBMINIJAIL_H_ */ minijail-2026.05.18+git20261016.8787d1a/libminijail.pc.in000066400000000000000000000002351526451555500215470ustar00rootroot00000000000000bslot=@BSLOT@ include_dir=@INCLUDE_DIR@ Name: libminijail Description: Minijail shared library. Version: ${bslot} CFlags: -I${include_dir} Libs: -lminijail minijail-2026.05.18+git20261016.8787d1a/libminijail_unittest.cc000066400000000000000000002257301526451555500230750ustar00rootroot00000000000000/* Copyright 2016 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test platform independent logic of Minijail using gtest. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "landlock_util.h" #include "libminijail-private.h" #include "libminijail.h" #include "scoped_minijail.h" #include "test_util.h" #include "unittest_util.h" #include "util.h" namespace { #if defined(__ANDROID__) #define ROOT_PREFIX "/system" #else #define ROOT_PREFIX "" #endif constexpr char kShellPath[] = ROOT_PREFIX "/bin/sh"; constexpr char kCatPath[] = ROOT_PREFIX "/bin/cat"; constexpr char kPreloadPath[] = "./libminijailpreload.so"; constexpr size_t kBufferSize = 128; constexpr bool kCompiledWithCoverage = #if defined(CROS_CODE_COVERAGE_ENABLED) true; #else false; #endif std::set GetProcessSubtreePids(pid_t root_pid) { std::set pids{root_pid}; bool progress = false; do { progress = false; DIR* d = opendir("/proc"); if (!d) pdie("opendir(\"/proc\")"); struct dirent* dir_entry; while ((dir_entry = readdir(d)) != nullptr) { if (dir_entry->d_type != DT_DIR) continue; char* end; const int pid = strtol(dir_entry->d_name, &end, 10); if (*end != '\0') continue; std::string path = "/proc/" + std::to_string(pid) + "/stat"; FILE* f = fopen(path.c_str(), "re"); if (!f) { if (errno == ENOENT) { // This loop is inherently racy, since PIDs can be reaped in the // middle of this. Not being able to find one /proc/PID/stat file is // completely normal. continue; } pdie("fopen(%s)", path.c_str()); } pid_t ppid; int ret = fscanf(f, "%*d (%*[^)]) %*c %d", &ppid); fclose(f); if (ret != 1) { continue; } if (pids.find(ppid) == pids.end()) continue; progress |= pids.insert(pid).second; } closedir(d); } while (progress); return pids; } std::map GetNamespaces( pid_t pid, const std::vector& namespace_names) { std::map namespaces; char buf[kBufferSize]; for (const auto& namespace_name : namespace_names) { std::string path = "/proc/" + std::to_string(pid) + "/ns/" + namespace_name; ssize_t len = readlink(path.c_str(), buf, sizeof(buf)); if (len == -1) pdie("readlink(\"%s\")", path.c_str()); namespaces.emplace(namespace_name, std::string(buf, len)); } return namespaces; } void set_preload_path(minijail* j) { // We need to get the absolute path because entering a new mntns will // implicitly chdir(/) for us. char* preload_path = realpath(kPreloadPath, nullptr); ASSERT_NE(preload_path, nullptr); minijail_set_preload_path(j, preload_path); free(preload_path); } } // namespace /* Silence unused variable warnings. */ TEST(silence, silence_unused) { EXPECT_STREQ(kLdPreloadEnvVar, kLdPreloadEnvVar); EXPECT_STREQ(kFdEnvVar, kFdEnvVar); EXPECT_STRNE(kFdEnvVar, kLdPreloadEnvVar); } TEST(consumebytes, zero) { char buf[1024]; size_t len = sizeof(buf); char* pos = &buf[0]; EXPECT_NE(nullptr, consumebytes(0, &pos, &len)); EXPECT_EQ(&buf[0], pos); EXPECT_EQ(sizeof(buf), len); } TEST(consumebytes, exact) { char buf[1024]; size_t len = sizeof(buf); char* pos = &buf[0]; /* One past the end since it consumes the whole buffer. */ char* end = &buf[sizeof(buf)]; EXPECT_NE(nullptr, consumebytes(len, &pos, &len)); EXPECT_EQ((size_t)0, len); EXPECT_EQ(end, pos); } TEST(consumebytes, half) { char buf[1024]; size_t len = sizeof(buf); char* pos = &buf[0]; /* One past the end since it consumes the whole buffer. */ char* end = &buf[sizeof(buf) / 2]; EXPECT_NE(nullptr, consumebytes(len / 2, &pos, &len)); EXPECT_EQ(sizeof(buf) / 2, len); EXPECT_EQ(end, pos); } TEST(consumebytes, toolong) { char buf[1024]; size_t len = sizeof(buf); char* pos = &buf[0]; /* One past the end since it consumes the whole buffer. */ EXPECT_EQ(nullptr, consumebytes(len + 1, &pos, &len)); EXPECT_EQ(sizeof(buf), len); EXPECT_EQ(&buf[0], pos); } TEST(consumestr, zero) { char buf[1024]; size_t len = 0; char* pos = &buf[0]; memset(buf, 0xff, sizeof(buf)); EXPECT_EQ(nullptr, consumestr(&pos, &len)); EXPECT_EQ((size_t)0, len); EXPECT_EQ(&buf[0], pos); } TEST(consumestr, nonul) { char buf[1024]; size_t len = sizeof(buf); char* pos = &buf[0]; memset(buf, 0xff, sizeof(buf)); EXPECT_EQ(nullptr, consumestr(&pos, &len)); EXPECT_EQ(sizeof(buf), len); EXPECT_EQ(&buf[0], pos); } TEST(consumestr, full) { char buf[1024]; size_t len = sizeof(buf); char* pos = &buf[0]; memset(buf, 0xff, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; EXPECT_EQ((void*)buf, consumestr(&pos, &len)); EXPECT_EQ((size_t)0, len); EXPECT_EQ(&buf[sizeof(buf)], pos); } TEST(consumestr, trailing_nul) { char buf[1024]; size_t len = sizeof(buf) - 1; char* pos = &buf[0]; memset(buf, 0xff, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; EXPECT_EQ(nullptr, consumestr(&pos, &len)); EXPECT_EQ(sizeof(buf) - 1, len); EXPECT_EQ(&buf[0], pos); } class MarshalTest : public ::testing::Test { protected: virtual void SetUp() { m_ = minijail_new(); j_ = minijail_new(); size_ = minijail_size(m_); } virtual void TearDown() { minijail_destroy(m_); minijail_destroy(j_); } char buf_[4096]; struct minijail* m_; struct minijail* j_; size_t size_; }; TEST_F(MarshalTest, empty) { ASSERT_EQ(0, minijail_marshal(m_, buf_, sizeof(buf_))); EXPECT_EQ(0, minijail_unmarshal(j_, buf_, size_)); } TEST_F(MarshalTest, 0xff) { memset(buf_, 0xff, sizeof(buf_)); /* Should fail on the first consumestr since a NUL will never be found. */ EXPECT_EQ(-EINVAL, minijail_unmarshal(j_, buf_, sizeof(buf_))); } TEST_F(MarshalTest, copy_empty) { ASSERT_EQ(0, minijail_copy_jail(m_, j_)); } TEST_F(MarshalTest, profile_flags) { minijail_bind(m_, "/var", "/var", false); minijail_set_using_minimalistic_mountns(m_); minijail_set_enable_profile_fs_restrictions(m_); minijail_add_minimalistic_mountns_fs_rules(m_); size_ = minijail_size(m_); for (size_t offset = 0; offset < 8; ++offset) { do_log(LOG_INFO, "offset: %zu", offset); ASSERT_EQ(0, minijail_marshal(m_, buf_ + offset, sizeof(buf_) - offset)); EXPECT_EQ(0, minijail_unmarshal(j_, buf_ + offset, size_)); } } TEST(KillTest, running_process) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "sleep 1000", nullptr}; EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); EXPECT_EQ(minijail_kill(j.get()), 128 + SIGTERM); EXPECT_EQ(minijail_kill(j.get()), -ESRCH); } TEST(KillTest, process_already_awaited) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "sleep 1; exit 42", nullptr}; set_preload_path(j.get()); EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); EXPECT_EQ(minijail_wait(j.get()), 42); EXPECT_EQ(minijail_kill(j.get()), -ESRCH); } TEST(KillTest, process_already_finished_but_not_awaited) { int fds[2]; const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "exit 42", nullptr}; set_preload_path(j.get()); ASSERT_EQ(pipe(fds), 0); EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); ASSERT_EQ(close(fds[1]), 0); // Wait for process to finish. char buf[PIPE_BUF]; EXPECT_EQ(read(fds[0], buf, PIPE_BUF), 0); EXPECT_EQ(minijail_kill(j.get()), 42); EXPECT_EQ(minijail_wait(j.get()), -ECHILD); } TEST(KillTest, process_not_started) { const ScopedMinijail j(minijail_new()); EXPECT_EQ(minijail_kill(j.get()), -ECHILD); } TEST(WaitTest, return_zero) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "exit 0", nullptr}; set_preload_path(j.get()); EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); EXPECT_EQ(minijail_wait(j.get()), 0); } TEST(WaitTest, return_max) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "exit 255", nullptr}; set_preload_path(j.get()); EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); EXPECT_EQ(minijail_wait(j.get()), 255); } TEST(WaitTest, return_modulo) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "exit 256", nullptr}; set_preload_path(j.get()); EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); EXPECT_EQ(minijail_wait(j.get()), 0); } TEST(WaitTest, killed_by_sigkill) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "kill -KILL $$; sleep 1000", nullptr}; set_preload_path(j.get()); EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_SIG_BASE + SIGKILL); } TEST(WaitTest, killed_by_sigsys) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "kill -SYS $$; sleep 1000", nullptr}; set_preload_path(j.get()); EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_SECCOMP_VIOLATION); } TEST(WaitTest, command_not_found) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"whatever", nullptr}; EXPECT_EQ(minijail_run(j.get(), "command that cannot be found", argv), 0); EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_NO_COMMAND); } TEST(WaitTest, command_not_run) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"whatever", nullptr}; EXPECT_EQ(minijail_run(j.get(), "/dev/null", argv), 0); EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_NO_ACCESS); } TEST(WaitTest, no_process) { const ScopedMinijail j(minijail_new()); EXPECT_EQ(minijail_wait(j.get()), -ECHILD); } TEST(WaitTest, can_wait_only_once) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "exit 0", nullptr}; set_preload_path(j.get()); EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); EXPECT_EQ(minijail_wait(j.get()), 0); EXPECT_EQ(minijail_wait(j.get()), -ECHILD); } TEST(Test, seccomp_logging_emits_warning_to_stderr_when_logging_to_fd) { if (!debug_logging_allowed()) GTEST_SKIP() << "seccomp logging disabled in this build"; std::string policy_path = source_path("test/seccomp.policy"); int pipefd[2]; ASSERT_EQ(pipe(pipefd), 0); pid_t pid = fork(); ASSERT_GE(pid, 0); if (pid == 0) { close(pipefd[0]); minijail_log_to_fd(pipefd[1], LOG_INFO); ScopedMinijail j(minijail_new()); minijail_no_new_privs(j.get()); minijail_use_seccomp_filter(j.get()); minijail_log_seccomp_filter_failures(j.get()); minijail_parse_seccomp_filters(j.get(), policy_path.c_str()); minijail_enter(j.get()); _exit(0); } close(pipefd[1]); std::string log_output; char buffer[256]; ssize_t read_ret; while ((read_ret = read(pipefd[0], buffer, sizeof(buffer))) > 0) { log_output.append(buffer, static_cast(read_ret)); } close(pipefd[0]); int status; ASSERT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); EXPECT_NE(log_output.find("logging seccomp filter failures"), std::string::npos); } TEST(Test, minijail_preserve_fd_no_leak) { const ScopedMinijail j(minijail_new()); char* const script = R"( echo Hi >&1; exec 1>&-; read line1; read line2; echo "$line1$line2 and Goodbye" >&2; exit 42; )"; char* const argv[] = {"sh", "-c", script, nullptr}; struct rlimit limit {}; ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit)); const int high_fd = static_cast(limit.rlim_cur) - 1; const int npipes = 4; int fds[npipes][2]; // Create pipes. for (int i = 0; i < npipes; ++i) { ASSERT_EQ(pipe(fds[i]), 0); } // (b/308042314) Move a pipe to > 1024 to check for a crash. ASSERT_FALSE(minijail_fd_is_open(high_fd)) << "high_fd is already in use"; ASSERT_EQ(dup2(fds[3][1], high_fd), high_fd) << strerror(errno); EXPECT_EQ(close(fds[3][1]), 0); fds[3][1] = high_fd; // All pipes are output pipes except for the first one which is used as // input pipe. std::swap(fds[0][0], fds[0][1]); for (int i = 0; i < npipes; ++i) { const int fd = fds[i][1]; minijail_preserve_fd(j.get(), fd, i); } minijail_close_open_fds(j.get()); EXPECT_EQ(minijail_run_no_preload(j.get(), kShellPath, argv), 0); // Close unused end of pipes. for (int i = 0; i < npipes; ++i) { const int fd = fds[i][1]; ASSERT_EQ(close(fd), 0); } const int in = fds[0][0]; const int out = fds[1][0]; const int err = fds[2][0]; char buf[PIPE_BUF]; ssize_t nbytes; // Check that stdout pipe works. nbytes = read(out, buf, PIPE_BUF); ASSERT_GT(nbytes, 0); EXPECT_EQ(std::string(buf, nbytes), "Hi\n"); // Check that the write end of stdout pipe got closed by the child process. If // the child process kept other file descriptors connected to stdout, then the // parent process wouldn't be able to detect that all write ends of this pipe // are closed and it would block here. EXPECT_EQ(read(out, buf, PIPE_BUF), 0); ASSERT_EQ(close(out), 0); // Check that stdin pipe works. const std::string s = "Greetings\n"; EXPECT_EQ(write(in, s.data(), s.size()), s.size()); // Close write end of pipe connected to child's stdin. If there was another // file descriptor connected to this write end, then the child process // wouldn't be able to detect that this write end is closed and it would // block. ASSERT_EQ(close(in), 0); // Check that child process continued and ended. nbytes = read(err, buf, PIPE_BUF); ASSERT_GT(nbytes, 0); EXPECT_EQ(std::string(buf, nbytes), "Greetings and Goodbye\n"); // Check that the write end of the stderr pipe is closed when the child // process finishes. EXPECT_EQ(read(err, buf, PIPE_BUF), 0); ASSERT_EQ(close(err), 0); // Check the child process termination status. EXPECT_EQ(minijail_wait(j.get()), 42); } TEST(Test, close_original_pipes_after_dup2) { // Pipe used by child process to signal that it continued after reading from // stdin. int to_wait[2]; ASSERT_EQ(pipe(to_wait), 0); const ScopedMinijail j(minijail_new()); // Ensure the child inherits a stable write fd for signaling. const int preserved_fd = 3; minijail_preserve_fd(j.get(), to_wait[1], preserved_fd); char* program; ASSERT_GT(asprintf(&program, R"( echo Hi >&1; echo There >&2; exec 1>&-; exec 2>&-; echo ready >&%d; # Signal parent that we closed fd 1 and 2 read line1; read line2; echo "$line1$line2 and Goodbye" >&%d; exit 42; )", preserved_fd, preserved_fd), 0); char* const argv[] = {"sh", "-c", program, nullptr}; pid_t child_pid = -1; int in = -1; int out = -1; int err = -1; EXPECT_EQ(minijail_run_pid_pipes_no_preload(j.get(), kShellPath, argv, &child_pid, &in, &out, &err), 0); free(program); // Close parent's copy of child's write end of signaling pipe. ASSERT_EQ(close(to_wait[1]), 0); EXPECT_GT(child_pid, 0); EXPECT_GT(in, 0); EXPECT_GT(out, 0); EXPECT_GT(err, 0); struct stat st; ASSERT_EQ(fstat(err, &st), 0); ino_t pipe_inode = st.st_ino; char buf[PIPE_BUF]; ssize_t n; // Check that stdout and stderr pipes work. n = read(out, buf, PIPE_BUF); ASSERT_GT(n, 0); EXPECT_EQ(std::string(buf, n), "Hi\n"); n = read(err, buf, PIPE_BUF); ASSERT_GT(n, 0); EXPECT_EQ(std::string(buf, n), "There\n"); // Check that the write end of stdout pipe got closed by the child. EXPECT_EQ(read(out, buf, PIPE_BUF), 0); ASSERT_EQ(close(out), 0); // Read "ready" signal from child. n = read(to_wait[0], buf, PIPE_BUF); ASSERT_GT(n, 0); EXPECT_EQ(std::string(buf, n), "ready\n"); // Now verify that fd 2 is closed in the child via /proc. // Note: we do not check read(err) == 0 here because mksh keeps the pipe // open on a high fd internally. auto is_fd_open_in_child = [](pid_t pid, int fd, ino_t inode) -> bool { char path[PATH_MAX]; snprintf(path, sizeof(path), "/proc/%d/fd/%d", pid, fd); char link_target[PATH_MAX]; ssize_t len = readlink(path, link_target, sizeof(link_target) - 1); if (len <= 0) { return false; } link_target[len] = '\0'; char expected_target[64]; snprintf(expected_target, sizeof(expected_target), "pipe:[%lu]", (unsigned long)inode); return strcmp(link_target, expected_target) == 0; }; EXPECT_FALSE(is_fd_open_in_child(child_pid, 2, pipe_inode)); ASSERT_EQ(close(err), 0); // Check that stdin pipe works. const std::string s = "Greetings\n"; EXPECT_EQ(write(in, s.data(), s.size()), s.size()); // Close write end of pipe connected to child's stdin. If there was another // file descriptor connected to this write end, then the child wouldn't be // able to detect that this write end is closed and it would block. ASSERT_EQ(close(in), 0); // Check that child process continued and ended. n = read(to_wait[0], buf, PIPE_BUF); ASSERT_GT(n, 0); EXPECT_EQ(std::string(buf, n), "Greetings and Goodbye\n"); ASSERT_EQ(close(to_wait[0]), 0); EXPECT_EQ(minijail_wait(j.get()), 42); } TEST(Test, minijail_no_clobber_pipe_fd) { const ScopedMinijail j(minijail_new()); char* const script = R"( echo Hi >&1; exec 1>&-; exec 4>&-; exec 7>&-; read line1; read line2; echo "$line1$line2 and Goodbye" >&2; exit 42; )"; char* const argv[] = {"sh", "-c", script, nullptr}; const int npipes = 3; int fds[npipes][2]; // Create pipes. for (int i = 0; i < npipes; ++i) { ASSERT_EQ(pipe(fds[i]), 0); } // All pipes are output pipes except for the first one which is used as // input pipe. std::swap(fds[0][0], fds[0][1]); // Generate a lot of mappings to try to clobber any file descriptors generated // by libminijail. for (int offset = 0; offset < npipes * 3; offset += npipes) { for (int i = 0; i < npipes; ++i) { const int fd = fds[i][1]; minijail_preserve_fd(j.get(), fd, i + offset); } } minijail_close_open_fds(j.get()); EXPECT_EQ(minijail_run_no_preload(j.get(), kShellPath, argv), 0); // Close unused end of pipes. for (int i = 0; i < npipes; ++i) { const int fd = fds[i][1]; ASSERT_EQ(close(fd), 0); } const int in = fds[0][0]; const int out = fds[1][0]; const int err = fds[2][0]; char buf[PIPE_BUF]; ssize_t nbytes; // Check that stdout pipe works. nbytes = read(out, buf, PIPE_BUF); ASSERT_GT(nbytes, 0); EXPECT_EQ(std::string(buf, nbytes), "Hi\n"); // Check that the write end of stdout pipe got closed by the child process. If // the child process kept other file descriptors connected to stdout, then the // parent process wouldn't be able to detect that all write ends of this pipe // are closed and it would block here. EXPECT_EQ(read(out, buf, PIPE_BUF), 0); ASSERT_EQ(close(out), 0); // Check that stdin pipe works. const std::string s = "Greetings\n"; EXPECT_EQ(write(in, s.data(), s.size()), s.size()); // Close write end of pipe connected to child's stdin. If there was another // file descriptor connected to this write end, then the child process // wouldn't be able to detect that this write end is closed and it would // block. ASSERT_EQ(close(in), 0); // Check that child process continued and ended. nbytes = read(err, buf, PIPE_BUF); ASSERT_GT(nbytes, 0); EXPECT_EQ(std::string(buf, nbytes), "Greetings and Goodbye\n"); // Check that the write end of the stderr pipe is closed when the child // process finishes. EXPECT_EQ(read(err, buf, PIPE_BUF), 0); ASSERT_EQ(close(err), 0); // Check the child process termination status. EXPECT_EQ(minijail_wait(j.get()), 42); } TEST(Test, minijail_run_env_pid_pipes) { // TODO(crbug.com/895875): The preload library interferes with ASan since they // both need to use LD_PRELOAD. // TODO(b/238743201): This test consistently breaks with code coverage // enabled. That should be fixed. if (kCompiledWithCoverage || running_with_asan()) GTEST_SKIP(); ScopedMinijail j(minijail_new()); set_preload_path(j.get()); char* argv[4]; argv[0] = const_cast(kCatPath); argv[1] = NULL; pid_t pid; int child_stdin, child_stdout; int mj_run_ret = minijail_run_pid_pipes(j.get(), argv[0], argv, &pid, &child_stdin, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); char teststr[] = "test\n"; const size_t teststr_len = strlen(teststr); ssize_t write_ret = write(child_stdin, teststr, teststr_len); EXPECT_EQ(write_ret, static_cast(teststr_len)); char buf[kBufferSize] = {}; ssize_t read_ret = read(child_stdout, buf, sizeof(buf) - 1); EXPECT_EQ(read_ret, static_cast(teststr_len)); EXPECT_STREQ(buf, teststr); int status; EXPECT_EQ(kill(pid, SIGTERM), 0); EXPECT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFSIGNALED(status)); EXPECT_EQ(WTERMSIG(status), SIGTERM); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\" >&2"; argv[3] = nullptr; char* envp[2]; envp[0] = "TEST_VAR=test"; envp[1] = NULL; // Set a canary env var in the parent that should not be present in the child. ASSERT_EQ(setenv("TEST_PARENT", "test", 1 /*overwrite*/), 0); int child_stderr; mj_run_ret = minijail_run_env_pid_pipes(j.get(), argv[0], argv, envp, &pid, &child_stdin, &child_stdout, &child_stderr); EXPECT_EQ(mj_run_ret, 0); memset(buf, 0, sizeof(buf)); read_ret = read(child_stderr, buf, sizeof(buf) - 1); EXPECT_GE(read_ret, 0); EXPECT_STREQ(buf, "|test\n"); EXPECT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); } TEST(Test, minijail_run_fd_env_pid_pipes) { // TODO(crbug.com/895875): The preload library interferes with ASan since they // both need to use LD_PRELOAD. // TODO(b/238743201): This test consistently breaks with code coverage // enabled. That should be fixed. if (kCompiledWithCoverage || running_with_asan()) GTEST_SKIP(); ScopedMinijail j(minijail_new()); set_preload_path(j.get()); char* argv[4]; argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\" >&2\n"; argv[3] = nullptr; char* envp[2]; envp[0] = "TEST_VAR=test"; envp[1] = nullptr; // Set a canary env var in the parent that should not be present in the child. ASSERT_EQ(setenv("TEST_PARENT", "test", 1 /*overwrite*/), 0); int elf_fd = open(const_cast(kShellPath), O_RDONLY | O_CLOEXEC); ASSERT_NE(elf_fd, -1); int dev_null = open("/dev/null", O_RDONLY); ASSERT_NE(dev_null, -1); // Create a mapping to dev_null that would clobber elf_fd if it is not // relocated. minijail_preserve_fd(j.get(), dev_null, elf_fd); pid_t pid; int child_stdin, child_stdout, child_stderr; int mj_run_ret = minijail_run_fd_env_pid_pipes(j.get(), elf_fd, argv, envp, &pid, &child_stdin, &child_stdout, &child_stderr); EXPECT_EQ(mj_run_ret, 0); close(dev_null); char buf[kBufferSize] = {}; ssize_t read_ret = read(child_stderr, buf, sizeof(buf) - 1); EXPECT_GE(read_ret, 0); EXPECT_STREQ(buf, "|test\n"); int status; EXPECT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); } TEST(Test, minijail_run_env_pid_pipes_with_local_preload) { // TODO(crbug.com/895875): The preload library interferes with ASan since they // both need to use LD_PRELOAD. // TODO(b/238743201): This test consistently breaks with code coverage // enabled. That should be fixed. if (kCompiledWithCoverage || running_with_asan()) GTEST_SKIP(); ScopedMinijail j(minijail_new()); // Use the preload library from this test build. set_preload_path(j.get()); char* argv[4]; argv[0] = const_cast(kCatPath); argv[1] = NULL; pid_t pid; int child_stdin, child_stdout; int mj_run_ret = minijail_run_pid_pipes(j.get(), argv[0], argv, &pid, &child_stdin, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); char teststr[] = "test\n"; const size_t teststr_len = strlen(teststr); ssize_t write_ret = write(child_stdin, teststr, teststr_len); EXPECT_EQ(write_ret, static_cast(teststr_len)); char buf[kBufferSize] = {}; ssize_t read_ret = read(child_stdout, buf, sizeof(buf) - 1); EXPECT_EQ(read_ret, static_cast(teststr_len)); EXPECT_STREQ(buf, teststr); int status; EXPECT_EQ(kill(pid, SIGTERM), 0); EXPECT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFSIGNALED(status)); EXPECT_EQ(WTERMSIG(status), SIGTERM); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\" >&2"; argv[3] = nullptr; char* envp[2]; envp[0] = "TEST_VAR=test"; envp[1] = NULL; // Set a canary env var in the parent that should not be present in the child. ASSERT_EQ(setenv("TEST_PARENT", "test", 1 /*overwrite*/), 0); int child_stderr; mj_run_ret = minijail_run_env_pid_pipes(j.get(), argv[0], argv, envp, &pid, &child_stdin, &child_stdout, &child_stderr); EXPECT_EQ(mj_run_ret, 0); memset(buf, 0, sizeof(buf)); read_ret = read(child_stderr, buf, sizeof(buf) - 1); EXPECT_GE(read_ret, 0); EXPECT_STREQ(buf, "|test\n"); EXPECT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); } TEST(Test, preload_clears_logging_env_var_for_child) { if (kCompiledWithCoverage || running_with_asan()) GTEST_SKIP(); ScopedMinijail j(minijail_new()); set_preload_path(j.get()); char* argv[4]; argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "env | grep '^__MINIJAIL_LOGGING=' || echo missing"; argv[3] = nullptr; char* envp[2]; envp[0] = "__MINIJAIL_LOGGING=stderr"; envp[1] = nullptr; pid_t pid; int child_stdin, child_stdout; int mj_run_ret = minijail_run_env_pid_pipes(j.get(), argv[0], argv, envp, &pid, &child_stdin, &child_stdout, nullptr); ASSERT_EQ(mj_run_ret, 0); ASSERT_EQ(close(child_stdin), 0); char buf[kBufferSize] = {}; ssize_t read_ret = read(child_stdout, buf, sizeof(buf) - 1); ASSERT_GT(read_ret, 0); EXPECT_STREQ(buf, "missing\n"); ASSERT_EQ(close(child_stdout), 0); int status; EXPECT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); } TEST(Test, test_minijail_no_clobber_fds) { int dev_null = open("/dev/null", O_RDONLY); ASSERT_NE(dev_null, -1); ScopedMinijail j(minijail_new()); // Keep stderr. minijail_preserve_fd(j.get(), 2, 2); // Create a lot of mappings to dev_null to possibly clobber libminijail.c fds. for (int i = 3; i < 15; ++i) { minijail_preserve_fd(j.get(), dev_null, i); } char* argv[4]; argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "echo Hello; read line1; echo \"${line1}\" >&2"; argv[3] = nullptr; pid_t pid; int child_stdin; int child_stdout; int child_stderr; int mj_run_ret = minijail_run_pid_pipes_no_preload( j.get(), argv[0], argv, &pid, &child_stdin, &child_stdout, &child_stderr); EXPECT_EQ(mj_run_ret, 0); char buf[kBufferSize]; ssize_t read_ret = read(child_stdout, buf, sizeof(buf)); EXPECT_GE(read_ret, 0); buf[read_ret] = '\0'; EXPECT_STREQ(buf, "Hello\n"); constexpr char to_write[] = "test in and err\n"; ssize_t write_ret = write(child_stdin, to_write, sizeof(to_write)); EXPECT_EQ(write_ret, sizeof(to_write)); read_ret = read(child_stderr, buf, sizeof(buf)); EXPECT_GE(read_ret, 0); buf[read_ret] = '\0'; EXPECT_STREQ(buf, to_write); int status; waitpid(pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); close(dev_null); } TEST(Test, test_minijail_no_fd_leaks) { pid_t pid; int child_stdout; int mj_run_ret; ssize_t read_ret; char buf[kBufferSize]; char script[kBufferSize]; int status; char* argv[4]; int dev_null = open("/dev/null", O_RDONLY); ASSERT_NE(dev_null, -1); snprintf(script, sizeof(script), "[ -e /proc/self/fd/%d ] && echo yes || echo no", dev_null); struct minijail* j = minijail_new(); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = script; argv[3] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); read_ret = read(child_stdout, buf, sizeof(buf)); EXPECT_GE(read_ret, 0); buf[read_ret] = '\0'; EXPECT_STREQ(buf, "yes\n"); waitpid(pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_close_open_fds(j); mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); read_ret = read(child_stdout, buf, sizeof(buf)); EXPECT_GE(read_ret, 0); buf[read_ret] = '\0'; EXPECT_STREQ(buf, "no\n"); waitpid(pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); close(dev_null); } TEST(Test, test_minijail_fork) { pid_t mj_fork_ret; int status; int pipe_fds[2]; ssize_t pid_size = sizeof(mj_fork_ret); ScopedMinijail j(minijail_new()); ASSERT_EQ(pipe(pipe_fds), 0); mj_fork_ret = minijail_fork(j.get()); ASSERT_GE(mj_fork_ret, 0); if (mj_fork_ret == 0) { pid_t pid_in_parent; // Wait for the parent to tell us the pid in the parent namespace. ASSERT_EQ(read(pipe_fds[0], &pid_in_parent, pid_size), pid_size); ASSERT_EQ(pid_in_parent, getpid()); exit(0); } EXPECT_EQ(write(pipe_fds[1], &mj_fork_ret, pid_size), pid_size); waitpid(mj_fork_ret, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); } static int early_exit(void* payload) { exit(static_cast(reinterpret_cast(payload))); } TEST(Test, test_minijail_callback) { pid_t pid; int mj_run_ret; int status; char* argv[2]; int exit_code = 42; struct minijail* j = minijail_new(); status = minijail_add_hook(j, &early_exit, reinterpret_cast(exit_code), MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS); EXPECT_EQ(status, 0); argv[0] = const_cast(kCatPath); argv[1] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL, NULL, NULL); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j); EXPECT_EQ(status, exit_code); minijail_destroy(j); } TEST(Test, test_minijail_preserve_fd) { int mj_run_ret; int status; char* argv[2]; char teststr[] = "test\n"; size_t teststr_len = strlen(teststr); int read_pipe[2]; int write_pipe[2]; char buf[1024]; struct minijail* j = minijail_new(); status = pipe(read_pipe); ASSERT_EQ(status, 0); status = pipe(write_pipe); ASSERT_EQ(status, 0); status = minijail_preserve_fd(j, write_pipe[0], STDIN_FILENO); ASSERT_EQ(status, 0); status = minijail_preserve_fd(j, read_pipe[1], STDOUT_FILENO); ASSERT_EQ(status, 0); minijail_close_open_fds(j); argv[0] = const_cast(kCatPath); argv[1] = NULL; mj_run_ret = minijail_run_no_preload(j, argv[0], argv); EXPECT_EQ(mj_run_ret, 0); close(write_pipe[0]); status = write(write_pipe[1], teststr, teststr_len); EXPECT_EQ(status, (int)teststr_len); close(write_pipe[1]); close(read_pipe[1]); status = read(read_pipe[0], buf, 8); EXPECT_EQ(status, (int)teststr_len); buf[teststr_len] = 0; EXPECT_STREQ(buf, teststr); status = minijail_wait(j); EXPECT_EQ(status, 0); minijail_destroy(j); } TEST(Test, test_minijail_reset_signal_mask) { struct minijail* j = minijail_new(); sigset_t original_signal_mask; { sigset_t signal_mask; ASSERT_EQ(0, sigemptyset(&signal_mask)); ASSERT_EQ(0, sigaddset(&signal_mask, SIGUSR1)); ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &signal_mask, &original_signal_mask)); } minijail_reset_signal_mask(j); pid_t mj_fork_ret = minijail_fork(j); ASSERT_GE(mj_fork_ret, 0); if (mj_fork_ret == 0) { sigset_t signal_mask; ASSERT_EQ(0, sigprocmask(SIG_SETMASK, NULL, &signal_mask)); ASSERT_EQ(0, sigismember(&signal_mask, SIGUSR1)); minijail_destroy(j); exit(0); } ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &original_signal_mask, NULL)); int status; waitpid(mj_fork_ret, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); } TEST(Test, test_minijail_reset_signal_handlers) { struct minijail* j = minijail_new(); ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_DFL)); ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_IGN)); ASSERT_EQ(SIG_IGN, signal(SIGUSR1, SIG_IGN)); minijail_reset_signal_handlers(j); pid_t mj_fork_ret = minijail_fork(j); ASSERT_GE(mj_fork_ret, 0); if (mj_fork_ret == 0) { ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_DFL)); minijail_destroy(j); exit(0); } ASSERT_NE(SIG_ERR, signal(SIGUSR1, SIG_DFL)); int status; waitpid(mj_fork_ret, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); } // Test that bind mounting onto a non-existing location works. TEST(Test, test_bind_mount_nonexistent_dest) { TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); // minijail_bind() expects absolute paths, but TemporaryDir::path can return // relative paths on Linux. std::string path = dir.path; if (!is_android()) { std::string cwd(getcwd(NULL, 0)); path = cwd + "/" + path; } std::string path_src = path + "/src"; std::string path_dest = path + "/dest"; EXPECT_EQ(mkdir(path_src.c_str(), 0700), 0); ScopedMinijail j(minijail_new()); int bind_res = minijail_bind(j.get(), path_src.c_str(), path_dest.c_str(), 0 /*writable*/); EXPECT_EQ(bind_res, 0); } // Test that bind mounting with a symlink behaves according to build-time // configuration. TEST(Test, test_bind_mount_symlink) { TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); // minijail_bind() expects absolute paths, but TemporaryDir::path can return // relative paths on Linux. std::string path = dir.path; if (!is_android()) { std::string cwd(getcwd(NULL, 0)); path = cwd + "/" + path; } std::string path_src = path + "/src"; std::string path_dest = path + "/dest"; std::string path_sym = path + "/symlink"; EXPECT_EQ(mkdir(path_src.c_str(), 0700), 0); EXPECT_EQ(mkdir(path_dest.c_str(), 0700), 0); EXPECT_EQ(symlink(path_src.c_str(), path_sym.c_str()), 0); ScopedMinijail j(minijail_new()); int bind_res = minijail_bind(j.get(), path_sym.c_str(), path_dest.c_str(), 0 /*writable*/); if (block_symlinks_in_bindmount_paths()) { EXPECT_NE(bind_res, 0); } else { EXPECT_EQ(bind_res, 0); } EXPECT_EQ(unlink(path_sym.c_str()), 0); } // Check for error when trying to enter a user namespace without a pid // namespace. TEST(Test, test_user_ns_without_pid_ns) { ScopedMinijail j(minijail_new()); minijail_namespace_user(j.get()); ASSERT_EXIT((void)minijail_fork(j.get()), ::testing::KilledBySignal(6), "user namespaces in Minijail require a PID namespace"); } namespace { // Tests that require userns access. // Android unit tests don't currently support entering user namespaces as // unprivileged users due to having an older kernel. ChromeOS unit tests // don't support it either due to being in a chroot environment (see man 2 // clone for more information about failure modes with the CLONE_NEWUSER flag). class NamespaceTest : public ::testing::Test { protected: static void SetUpTestSuite() { userns_supported_ = UsernsSupported(); } // Whether userns is supported. static bool userns_supported_; static bool UsernsSupported() { pid_t pid = fork(); if (pid == -1) pdie("could not fork"); // Check that unshare(CLONE_NEWUSER) works. if (pid == 0) _exit(unshare(CLONE_NEWUSER) == 0 ? 0 : 1); // Check that /proc/[pid]/uid_map can be opened. When pivot_root is used to // enter CrOS SDK chroot, unshare() works, but libminijail fails to open // /proc/[pid]/uid_map because its owner uid and gid are set to 0. char* filename = nullptr; if (asprintf(&filename, "/proc/%d/uid_map", pid) == -1) die("asprintf failed"); ScopedStr filename_deleter(filename); bool fd_is_valid = ScopedFD(open(filename, O_WRONLY | O_CLOEXEC)).get() != -1; int status; if (waitpid(pid, &status, 0) < 0) pdie("could not wait"); if (!WIFEXITED(status)) die("child did not exit properly: %#x", status); bool ret = WEXITSTATUS(status) == 0 && fd_is_valid; if (!ret) warn("Skipping userns related tests"); return ret; } }; bool NamespaceTest::userns_supported_; } // namespace TEST_F(NamespaceTest, test_tmpfs_userns) { int mj_run_ret; int status; char* argv[4]; char uidmap[kBufferSize], gidmap[kBufferSize]; constexpr uid_t kTargetUid = 1000; // Any non-zero value will do. constexpr gid_t kTargetGid = 1000; if (!userns_supported_) GTEST_SKIP(); struct minijail* j = minijail_new(); minijail_namespace_pids(j); minijail_namespace_vfs(j); minijail_mount_tmp(j); minijail_run_as_init(j); // Perform userns mapping. minijail_namespace_user(j); snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid()); snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid()); minijail_change_uid(j, kTargetUid); minijail_change_gid(j, kTargetGid); minijail_uidmap(j, uidmap); minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec touch /tmp/foo"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j, argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j); EXPECT_EQ(status, 0); minijail_destroy(j); } TEST_F(NamespaceTest, test_namespaces) { constexpr char teststr[] = "test\n"; // TODO(crbug.com/895875): The preload library interferes with ASan since they // both need to use LD_PRELOAD. if (!userns_supported_ || running_with_asan()) GTEST_SKIP(); std::string uidmap = "0 " + std::to_string(getuid()) + " 1"; std::string gidmap = "0 " + std::to_string(getgid()) + " 1"; const std::vector namespace_names = {"pid", "mnt", "user", "net", "cgroup", "uts"}; // Grab the set of namespaces outside the container. std::map init_namespaces = GetNamespaces(getpid(), namespace_names); std::function test_functions[] = { [](struct minijail* j attribute_unused) {}, [](struct minijail* j) { minijail_mount(j, "/", "/", "", MS_BIND | MS_REC | MS_RDONLY); minijail_enter_pivot_root(j, "/tmp"); }, [](struct minijail* j) { minijail_enter_chroot(j, "/"); }, }; // This test is run with and without the preload library. for (const auto& run_function : {minijail_run_pid_pipes, minijail_run_pid_pipes_no_preload}) { for (const auto& test_function : test_functions) { ScopedMinijail j(minijail_new()); set_preload_path(j.get()); // Enter all the namespaces we can. minijail_namespace_cgroups(j.get()); minijail_namespace_net(j.get()); minijail_namespace_pids(j.get()); minijail_namespace_user(j.get()); minijail_namespace_vfs(j.get()); minijail_namespace_uts(j.get()); // Set up the user namespace. minijail_uidmap(j.get(), uidmap.c_str()); minijail_gidmap(j.get(), gidmap.c_str()); minijail_namespace_user_disable_setgroups(j.get()); minijail_close_open_fds(j.get()); test_function(j.get()); char* const argv[] = {const_cast(kCatPath), nullptr}; pid_t container_pid; int child_stdin, child_stdout; int mj_run_ret = run_function(j.get(), argv[0], argv, &container_pid, &child_stdin, &child_stdout, nullptr); EXPECT_EQ(mj_run_ret, 0); // Send some data to stdin and read it back to ensure that the child // process is running. const size_t teststr_len = strlen(teststr); ssize_t write_ret = write(child_stdin, teststr, teststr_len); EXPECT_EQ(write_ret, static_cast(teststr_len)); char buf[kBufferSize]; ssize_t read_ret = read(child_stdout, buf, 8); EXPECT_EQ(read_ret, static_cast(teststr_len)); buf[teststr_len] = 0; EXPECT_STREQ(buf, teststr); // Grab the set of namespaces in every container process. They must not // match the ones in the init namespace, and they must all match each // other. std::map container_namespaces = GetNamespaces(container_pid, namespace_names); EXPECT_NE(container_namespaces, init_namespaces); for (pid_t pid : GetProcessSubtreePids(container_pid)) EXPECT_EQ(container_namespaces, GetNamespaces(pid, namespace_names)); EXPECT_EQ(0, close(child_stdin)); int status = minijail_wait(j.get()); EXPECT_EQ(status, 0); } } } TEST_F(NamespaceTest, test_bind_mount_symlink_dest) { if (!userns_supported_ || running_with_asan()) GTEST_SKIP(); TemporaryDir chroot_dir; ASSERT_TRUE(chroot_dir.is_valid()); std::string chroot_etc = chroot_dir.path + "/etc"; ASSERT_EQ(mkdir(chroot_etc.c_str(), 0755), 0); std::string symlink_path = chroot_etc + "/resolv.conf"; ASSERT_EQ(symlink("../run/shill/resolv.conf", symlink_path.c_str()), 0); std::string chroot_run = chroot_dir.path + "/run"; ASSERT_EQ(mkdir(chroot_run.c_str(), 0755), 0); TemporaryFile src_file; ASSERT_TRUE(src_file.is_valid()); const char test_content[] = "nameserver 8.8.8.8\n"; int fd = open(src_file.path.c_str(), O_WRONLY | O_CLOEXEC); ASSERT_GE(fd, 0); ASSERT_EQ(write(fd, test_content, strlen(test_content)), (ssize_t)strlen(test_content)); close(fd); std::string uidmap = "0 " + std::to_string(getuid()) + " 1"; std::string gidmap = "0 " + std::to_string(getgid()) + " 1"; ScopedMinijail j(minijail_new()); set_preload_path(j.get()); minijail_namespace_user(j.get()); minijail_namespace_vfs(j.get()); minijail_uidmap(j.get(), uidmap.c_str()); minijail_gidmap(j.get(), gidmap.c_str()); minijail_namespace_user_disable_setgroups(j.get()); minijail_enter_chroot(j.get(), chroot_dir.path.c_str()); ASSERT_EQ( minijail_bind(j.get(), src_file.path.c_str(), "/etc/resolv.conf", 0), 0); char* const argv[] = {const_cast("/bin/true"), nullptr}; pid_t pid; int mj_run_ret = minijail_run_pid_pipes_no_preload( j.get(), argv[0], argv, &pid, nullptr, nullptr, nullptr); ASSERT_EQ(mj_run_ret, 0); int status = minijail_wait(j.get()); EXPECT_EQ(status, 0); std::string expected_target_path = chroot_run + "/shill/resolv.conf"; struct stat st; EXPECT_EQ(stat(expected_target_path.c_str(), &st), 0); EXPECT_TRUE(S_ISREG(st.st_mode)); EXPECT_EQ(unlink(expected_target_path.c_str()), 0); EXPECT_EQ(rmdir((chroot_run + "/shill").c_str()), 0); } TEST_F(NamespaceTest, test_enter_ns) { char uidmap[kBufferSize], gidmap[kBufferSize]; if (!userns_supported_) GTEST_SKIP(); // We first create a child in a new userns so we have privs to run more tests. // We can't combine the steps as the kernel disallows many resource sharing // from outside the userns. struct minijail* j = minijail_new(); minijail_namespace_vfs(j); minijail_namespace_pids(j); minijail_run_as_init(j); // Perform userns mapping. minijail_namespace_user(j); snprintf(uidmap, sizeof(uidmap), "0 %d 1", getuid()); snprintf(gidmap, sizeof(gidmap), "0 %d 1", getgid()); minijail_uidmap(j, uidmap); minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); pid_t pid = minijail_fork(j); if (pid == 0) { // Child. minijail_destroy(j); // Create new namespaces inside this userns which we may enter. j = minijail_new(); minijail_namespace_net(j); minijail_namespace_vfs(j); pid = minijail_fork(j); if (pid == 0) { // Child. minijail_destroy(j); // Finally enter those namespaces. j = minijail_new(); set_preload_path(j); minijail_namespace_net(j); minijail_namespace_vfs(j); minijail_namespace_enter_net(j, "/proc/self/ns/net"); minijail_namespace_enter_vfs(j, "/proc/self/ns/mnt"); char* argv[] = {"/bin/true", nullptr}; EXPECT_EQ(0, minijail_run(j, argv[0], argv)); EXPECT_EQ(0, minijail_wait(j)); minijail_destroy(j); exit(0); } else { ASSERT_GT(pid, 0); EXPECT_EQ(0, minijail_wait(j)); minijail_destroy(j); exit(0); } } else { ASSERT_GT(pid, 0); EXPECT_EQ(0, minijail_wait(j)); minijail_destroy(j); } } TEST_F(NamespaceTest, test_remount_all_private) { pid_t pid; int child_stdout; int mj_run_ret; ssize_t read_ret; char buf[kBufferSize]; int status; char* argv[4]; char uidmap[kBufferSize], gidmap[kBufferSize]; constexpr uid_t kTargetUid = 1000; // Any non-zero value will do. constexpr gid_t kTargetGid = 1000; if (!userns_supported_) GTEST_SKIP(); struct minijail* j = minijail_new(); minijail_namespace_pids(j); minijail_namespace_vfs(j); minijail_run_as_init(j); // Perform userns mapping. minijail_namespace_user(j); snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid()); snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid()); minijail_change_uid(j, kTargetUid); minijail_change_gid(j, kTargetGid); minijail_uidmap(j, uidmap); minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); minijail_namespace_vfs(j); minijail_remount_mode(j, MS_PRIVATE); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "grep -E 'shared:|master:|propagate_from:|unbindable:' " "/proc/self/mountinfo"; argv[3] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); // There should be no output because all mounts should be remounted as // private. read_ret = read(child_stdout, buf, sizeof(buf)); EXPECT_EQ(read_ret, 0); // grep will exit with 1 if it does not find anything which is what we // expect. status = minijail_wait(j); EXPECT_EQ(status, 1); minijail_destroy(j); } TEST_F(NamespaceTest, test_fail_to_remount_one_private) { int status; char uidmap[kBufferSize], gidmap[kBufferSize]; constexpr uid_t kTargetUid = 1000; // Any non-zero value will do. constexpr gid_t kTargetGid = 1000; if (!userns_supported_) GTEST_SKIP(); struct minijail* j = minijail_new(); minijail_namespace_pids(j); minijail_namespace_vfs(j); minijail_mount_tmp(j); minijail_run_as_init(j); // Perform userns mapping. minijail_namespace_user(j); snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid()); snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid()); minijail_change_uid(j, kTargetUid); minijail_change_gid(j, kTargetGid); minijail_uidmap(j, uidmap); minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); minijail_namespace_vfs(j); minijail_remount_mode(j, MS_SHARED); minijail_add_remount(j, "/proc", MS_PRIVATE); char* argv[] = {"/bin/true", nullptr}; minijail_run(j, argv[0], argv); status = minijail_wait(j); EXPECT_GT(status, 0); minijail_destroy(j); } TEST_F(NamespaceTest, test_remount_one_shared) { pid_t pid; int child_stdout; int mj_run_ret; ssize_t read_ret; char buf[kBufferSize * 4]; int status; char* argv[4]; char uidmap[kBufferSize], gidmap[kBufferSize]; constexpr uid_t kTargetUid = 1000; // Any non-zero value will do. constexpr gid_t kTargetGid = 1000; if (!userns_supported_) GTEST_SKIP(); struct minijail* j = minijail_new(); minijail_namespace_pids(j); minijail_namespace_vfs(j); minijail_mount_tmp(j); minijail_run_as_init(j); // Perform userns mapping. minijail_namespace_user(j); snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid()); snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid()); minijail_change_uid(j, kTargetUid); minijail_change_gid(j, kTargetGid); minijail_uidmap(j, uidmap); minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); minijail_namespace_vfs(j); minijail_remount_mode(j, MS_PRIVATE); minijail_add_remount(j, "/proc", MS_SHARED); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "grep -E 'shared:' /proc/self/mountinfo"; argv[3] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); // There should be no output because all mounts should be remounted as // private. read_ret = read(child_stdout, buf, sizeof(buf)); EXPECT_GE(read_ret, 0); buf[read_ret] = '\0'; EXPECT_NE(std::string(buf).find("/proc"), std::string::npos); status = minijail_wait(j); EXPECT_EQ(status, 0); minijail_destroy(j); } // Test that using minijail_mount() for bind mounts works. TEST_F(NamespaceTest, test_remount_ro_using_mount) { int status; char uidmap[kBufferSize], gidmap[kBufferSize]; constexpr uid_t kTargetUid = 1000; // Any non-zero value will do. constexpr gid_t kTargetGid = 1000; if (!userns_supported_) GTEST_SKIP(); struct minijail* j = minijail_new(); minijail_namespace_pids(j); minijail_namespace_vfs(j); minijail_mount_tmp(j); minijail_run_as_init(j); // Perform userns mapping. minijail_namespace_user(j); snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid()); snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid()); minijail_change_uid(j, kTargetUid); minijail_change_gid(j, kTargetGid); minijail_uidmap(j, uidmap); minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); // Perform a RO remount using minijail_mount(). minijail_mount(j, "none", "/", "none", MS_REMOUNT | MS_BIND | MS_RDONLY); char* argv[] = {"/bin/true", nullptr}; minijail_run_no_preload(j, argv[0], argv); status = minijail_wait(j); EXPECT_EQ(status, 0); minijail_destroy(j); } namespace { constexpr char kInvokeDevIoctlCmd[] = R"(exec python3 -B -c ' import fcntl, termios, ctypes, sys try: fd = open("/dev/zero", "r") except OSError as e: sys.exit(100 + e.errno) try: fcntl.ioctl(fd, termios.FIONREAD, b" " * ctypes.sizeof(ctypes.c_int)) except OSError as e: if e.errno == 25: sys.exit(0) else: sys.exit(200 + e.errno) ')"; int RunIoctlTestProcess(struct minijail* j) { char* argv[4]; argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = const_cast(kInvokeDevIoctlCmd); argv[3] = NULL; int run_ret = minijail_run_no_preload(j, argv[0], argv); if (run_ret != 0) { return run_ret; } return minijail_wait(j); } // Tests that require Landlock support. // // These subclass NamespaceTest because they also require userns access. // TODO(akhna): ideally, Landlock unit tests should be able to run w/o // namespace_pids or namespace_user. class LandlockTest : public NamespaceTest { protected: static void SetUpTestSuite() { NamespaceTest::SetUpTestSuite(); run_landlock_tests_ = LandlockSupported() && userns_supported_; } // Whether Landlock tests should be run. static bool run_landlock_tests_; static bool LandlockSupported() { // Check the Landlock version w/o creating a ruleset file descriptor. const int landlock_version = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (landlock_version <= 0) { const int err = errno; warn("Skipping Landlock tests"); switch (err) { case ENOSYS: warn("Landlock not supported by the current kernel."); break; case EOPNOTSUPP: warn("Landlock is currently disabled."); break; } return false; } return true; } // Sets up a minijail to make Landlock syscalls and child processes. void SetupLandlockTestingNamespaces(struct minijail* j) { minijail_namespace_pids(j); minijail_namespace_user(j); } }; bool LandlockTest::run_landlock_tests_; // Constants used in Landlock tests. constexpr char kBinPath[] = "/bin"; constexpr char kEtcPath[] = "/etc"; constexpr char kLibPath[] = "/lib"; constexpr char kLib64Path[] = "/lib64"; constexpr char kTmpPath[] = "/tmp"; constexpr char kLsPath[] = "/bin/ls"; constexpr char kTestSymlinkScript[] = R"( unlink /tmp/test-sym-link-1; ln -s /bin /tmp/test-sym-link-1 )"; } // namespace TEST_F(LandlockTest, test_rule_rx_allow) { int mj_run_ret; int status; char* argv[3]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_add_fs_restriction_rx(j.get(), kEtcPath); minijail_add_fs_restriction_rx(j.get(), kLibPath); minijail_add_fs_restriction_rx(j.get(), kLib64Path); argv[0] = const_cast(kLsPath); argv[1] = const_cast(kCatPath); argv[2] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_EQ(status, 0); } TEST_F(LandlockTest, test_rule_rx_deny) { int mj_run_ret; int status; char* argv[3]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); // Add irrelevant Landlock rule. minijail_add_fs_restriction_rx(j.get(), "/var"); argv[0] = const_cast(kLsPath); argv[1] = const_cast(kCatPath); argv[2] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_NE(status, 0); } TEST_F(LandlockTest, test_rule_ro_allow) { int mj_run_ret; int status; char* argv[3]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_add_fs_restriction_rx(j.get(), kEtcPath); minijail_add_fs_restriction_rx(j.get(), kLibPath); minijail_add_fs_restriction_rx(j.get(), kLib64Path); // Add RO rule. minijail_add_fs_restriction_ro(j.get(), "/var"); argv[0] = const_cast(kLsPath); argv[1] = "/var"; argv[2] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_EQ(status, 0); } TEST_F(LandlockTest, test_rule_ro_deny) { int mj_run_ret; int status; char* argv[3]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_add_fs_restriction_rx(j.get(), kEtcPath); minijail_add_fs_restriction_rx(j.get(), kLibPath); minijail_add_fs_restriction_rx(j.get(), kLib64Path); // No RO rule for /var, because we want the cmd to fail. argv[0] = const_cast(kLsPath); argv[1] = "/var"; argv[2] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_NE(status, 0); } TEST_F(LandlockTest, test_rule_rw_allow) { int mj_run_ret; int status; char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_add_fs_restriction_rx(j.get(), kEtcPath); minijail_add_fs_restriction_rx(j.get(), kLibPath); minijail_add_fs_restriction_rx(j.get(), kLib64Path); // Add RW Landlock rule. minijail_add_fs_restriction_rw(j.get(), kTmpPath); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/baz"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_EQ(status, 0); } TEST_F(LandlockTest, test_rule_rw_deny) { int mj_run_ret; int status; char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_add_fs_restriction_rx(j.get(), kEtcPath); minijail_add_fs_restriction_rx(j.get(), kLibPath); minijail_add_fs_restriction_rx(j.get(), kLib64Path); // No RW rule, because we want the cmd to fail. argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/baz"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_NE(status, 0); } TEST_F(LandlockTest, test_deny_rule_with_close_open_fds) { int mj_run_ret; int status; char* argv[3]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); // Make sure Landlock still functions if fds are closed. minijail_close_open_fds(j.get()); // Add irrelevant Landlock rule. minijail_add_fs_restriction_rx(j.get(), "/var"); argv[0] = const_cast(kLsPath); argv[1] = const_cast(kCatPath); argv[2] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); // We should see 126 because /bin is not executable. EXPECT_EQ(status, 126); } TEST_F(LandlockTest, test_fs_rules_disabled) { int mj_run_ret; int status; char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_disable_fs_restrictions(j.get()); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/fs-rules-test"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); // Rules aren't applied, so cmd succeeds. EXPECT_EQ(status, 0); } TEST_F(LandlockTest, test_fs_rules_availability) { char* argv[4]; int status; bool landlock_available; // We always run regardless of LandlockSupported() because we would like to // test even if Landlock is unavailable. if (!UsernsSupported()) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/fs-rules-test"; argv[3] = NULL; EXPECT_EQ(minijail_run_no_preload(j.get(), argv[0], argv), 0); status = minijail_wait(j.get()); landlock_available = minijail_is_fs_restriction_available(); EXPECT_EQ(landlock_available, LandlockSupported()); if (landlock_available) { // Landlock is available, writing rule should fail. EXPECT_NE(status, 0); } else { // Rules aren't effective, so cmd succeeds. EXPECT_EQ(status, 0); } } TEST_F(LandlockTest, test_setup_fs_rules_fd) { // Test that the Landlock ruleset FD is set up, because this is important // behavior for API users calling minijail_enter() directly. if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); EXPECT_FALSE(minijail_is_fs_restriction_ruleset_initialized(j.get())); minijail_add_fs_restriction_rx(j.get(), kBinPath); EXPECT_TRUE(minijail_is_fs_restriction_ruleset_initialized(j.get())); } TEST_F(LandlockTest, test_rule_allow_symlinks_advanced_rw) { int mj_run_ret; int status; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_add_fs_restriction_rx(j.get(), kEtcPath); minijail_add_fs_restriction_rx(j.get(), kLibPath); minijail_add_fs_restriction_rx(j.get(), kLib64Path); minijail_add_fs_restriction_advanced_rw(j.get(), kTmpPath); char* const argv[] = {"sh", "-c", const_cast(kTestSymlinkScript), nullptr}; mj_run_ret = minijail_run_no_preload(j.get(), kShellPath, argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_EQ(status, 0); } TEST_F(LandlockTest, test_rule_deny_symlinks_basic_rw) { int mj_run_ret; int status; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_add_fs_restriction_rx(j.get(), kEtcPath); minijail_add_fs_restriction_rx(j.get(), kLibPath); minijail_add_fs_restriction_rx(j.get(), kLib64Path); minijail_add_fs_restriction_rw(j.get(), kTmpPath); char* const argv[] = {"sh", "-c", const_cast(kTestSymlinkScript), nullptr}; mj_run_ret = minijail_run_no_preload(j.get(), kShellPath, argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_NE(status, 0); } TEST_F(LandlockTest, test_rule_rx_cannot_write) { int mj_run_ret; int status; char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), kBinPath); minijail_add_fs_restriction_rx(j.get(), kEtcPath); minijail_add_fs_restriction_rx(j.get(), kLibPath); minijail_add_fs_restriction_rx(j.get(), kLib64Path); minijail_add_fs_restriction_rx(j.get(), kTmpPath); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/baz"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_NE(status, 0); } TEST_F(LandlockTest, test_rule_ro_cannot_wx) { int mj_run_ret; int status; char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_ro(j.get(), kBinPath); minijail_add_fs_restriction_ro(j.get(), kEtcPath); minijail_add_fs_restriction_ro(j.get(), kLibPath); minijail_add_fs_restriction_ro(j.get(), kLib64Path); minijail_add_fs_restriction_ro(j.get(), kTmpPath); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/baz"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_NE(status, 0); } TEST_F(LandlockTest, test_rule_rw_cannot_exec) { int mj_run_ret; int status; char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rw(j.get(), kBinPath); minijail_add_fs_restriction_rw(j.get(), kEtcPath); minijail_add_fs_restriction_rw(j.get(), kLibPath); minijail_add_fs_restriction_rw(j.get(), kLib64Path); minijail_add_fs_restriction_rw(j.get(), kTmpPath); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/baz"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_NE(status, 0); } TEST_F(LandlockTest, test_access_default_paths) { int mj_run_ret; int status; char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_enable_default_fs_restrictions(j.get()); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec cat /etc/group"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_EQ(status, 0); } TEST_F(LandlockTest, test_cannot_access_default_paths) { int mj_run_ret; int status; char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rw(j.get(), kBinPath); minijail_add_fs_restriction_rw(j.get(), kLibPath); minijail_add_fs_restriction_rw(j.get(), kLib64Path); // No call to minijail_enable_default_fs_restrictions(). argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec cat /etc/group"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j.get(), argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j.get()); EXPECT_NE(status, 0); } // Tests that LANDLOCK_ACCESS_FS_REFER is supported when the kernel supports // Landlock version ABI=2 (introduced in Linux 5.19). TEST_F(LandlockTest, test_refer_supported) { char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); const int landlock_version = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (landlock_version < LANDLOCK_ABI_FS_REFER_SUPPORTED) { GTEST_SKIP() << "Test requires Landlock ABI " << LANDLOCK_ABI_FS_REFER_SUPPORTED << ", but system only supports ABI " << landlock_version; } ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), "/"); // If LANDLOCK_ACCESS_FS_REFER isn’t part of the access rights handled by // minijail, none of the access rights in this call will be added. minijail_add_fs_restriction_access_rights( j.get(), "/tmp", ACCESS_FS_ROUGHLY_FULL_WRITE | LANDLOCK_ACCESS_FS_REFER); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/baz"; argv[3] = NULL; EXPECT_EQ(0, minijail_run_no_preload(j.get(), argv[0], argv)); EXPECT_EQ(0, minijail_wait(j.get())); } // Tests that LANDLOCK_ACCESS_FS_REFER is NOT supported when the kernel only // supports Landlock version ABI < 2 (ABI=1 was introduced in Linux 5.13). TEST_F(LandlockTest, test_refer_not_supported) { char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); const int landlock_version = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (landlock_version >= LANDLOCK_ABI_FS_REFER_SUPPORTED) { GTEST_SKIP() << "Test requires Landlock ABI < " << LANDLOCK_ABI_FS_REFER_SUPPORTED << ", but system supports ABI " << landlock_version; } ScopedMinijail j(minijail_new()); std::string link_name = std::tmpnam(NULL); std::string link_cmd = "exec /bin/ln -s /var " + link_name; SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), "/"); // This call shouldn't succeed, since we're adding an access right // that doesn't have kernel support. minijail_add_fs_restriction_access_rights( j.get(), "/tmp", ACCESS_FS_ROUGHLY_FULL_WRITE | LANDLOCK_ACCESS_FS_REFER); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "exec echo 'bar' > /tmp/baz"; argv[3] = NULL; EXPECT_EQ(0, minijail_run_no_preload(j.get(), argv[0], argv)); EXPECT_NE(0, minijail_wait(j.get())); } // Landlock version ABI=3 (Truncate, introduced in Linux 6.2) // - Default behavior (no restriction) TEST_F(LandlockTest, test_truncate_default) { char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); std::string file_path = dir.path + "/test_file"; FILE* f = fopen(file_path.c_str(), "w"); ASSERT_NE(f, nullptr); fprintf(f, "hello"); fclose(f); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), "/"); minijail_add_fs_restriction_rw(j.get(), dir.path.c_str()); std::string cmd = "exec echo -n > " + file_path; argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = const_cast(cmd.c_str()); argv[3] = NULL; EXPECT_EQ(0, minijail_run_no_preload(j.get(), argv[0], argv)); EXPECT_EQ(0, minijail_wait(j.get())); struct stat st; EXPECT_EQ(stat(file_path.c_str(), &st), 0); EXPECT_EQ(st.st_size, 0); } // Landlock version ABI=3 (Truncate, introduced in Linux 6.2) // - Restricted behavior (opt-in) TEST_F(LandlockTest, test_truncate_restricted) { char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); const int landlock_version = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (landlock_version < LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED) { GTEST_SKIP() << "Test requires Landlock ABI " << LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED << ", but system only supports ABI " << landlock_version; } TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); std::string file_path = dir.path + "/test_file"; FILE* f = fopen(file_path.c_str(), "w"); ASSERT_NE(f, nullptr); fprintf(f, "hello"); fclose(f); ScopedMinijail j(minijail_new()); minijail_set_landlock_abi(j.get(), LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), "/"); minijail_add_fs_restriction_access_rights( j.get(), dir.path.c_str(), ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_BASIC_WRITE); std::string cmd = "exec echo -n > " + file_path; argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = const_cast(cmd.c_str()); argv[3] = NULL; EXPECT_EQ(0, minijail_run_no_preload(j.get(), argv[0], argv)); EXPECT_NE(0, minijail_wait(j.get())); struct stat st; EXPECT_EQ(stat(file_path.c_str(), &st), 0); EXPECT_EQ(st.st_size, 5); } // Landlock version ABI=3 (Truncate, introduced in Linux 6.2) // - Allowed behavior (opt-in + explicitly allowed) TEST_F(LandlockTest, test_truncate_allowed) { char* argv[4]; if (!run_landlock_tests_) GTEST_SKIP(); const int landlock_version = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (landlock_version < LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED) { GTEST_SKIP() << "Test requires Landlock ABI " << LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED << ", but system only supports ABI " << landlock_version; } TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); std::string file_path = dir.path + "/test_file"; FILE* f = fopen(file_path.c_str(), "w"); ASSERT_NE(f, nullptr); fprintf(f, "hello"); fclose(f); ScopedMinijail j(minijail_new()); minijail_set_landlock_abi(j.get(), LANDLOCK_ABI_FS_TRUNCATE_SUPPORTED); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), "/"); minijail_add_fs_restriction_access_rights( j.get(), dir.path.c_str(), ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_BASIC_WRITE | LANDLOCK_ACCESS_FS_TRUNCATE); std::string cmd = "exec echo -n > " + file_path; argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = const_cast(cmd.c_str()); argv[3] = NULL; EXPECT_EQ(0, minijail_run_no_preload(j.get(), argv[0], argv)); EXPECT_EQ(0, minijail_wait(j.get())); struct stat st; EXPECT_EQ(stat(file_path.c_str(), &st), 0); EXPECT_EQ(st.st_size, 0); } // Landlock version ABI=5 (Ioctl, introduced in Linux 6.10) // - Default behavior (no restriction) TEST_F(LandlockTest, test_ioctl_default) { if (!run_landlock_tests_) GTEST_SKIP(); ScopedMinijail j(minijail_new()); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), "/"); minijail_add_fs_restriction_ro(j.get(), "/dev/zero"); EXPECT_EQ(0, RunIoctlTestProcess(j.get())); } // Landlock version ABI=5 (Ioctl, introduced in Linux 6.10) // - Restricted behavior (opt-in) TEST_F(LandlockTest, test_ioctl_restricted) { if (!run_landlock_tests_) GTEST_SKIP(); const int landlock_version = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (landlock_version < LANDLOCK_ABI_FS_IOCTL_DEV_SUPPORTED) { GTEST_SKIP() << "Test requires Landlock ABI " << LANDLOCK_ABI_FS_IOCTL_DEV_SUPPORTED << ", but system only supports ABI " << landlock_version; } ScopedMinijail j(minijail_new()); minijail_set_landlock_abi(j.get(), LANDLOCK_ABI_FS_IOCTL_DEV_SUPPORTED); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), "/"); minijail_add_fs_restriction_ro(j.get(), "/dev/zero"); EXPECT_EQ(200 + EACCES, RunIoctlTestProcess(j.get())); } // Landlock version ABI=5 (Ioctl, introduced in Linux 6.10) // - Allowed behavior (opt-in + explicitly allowed) TEST_F(LandlockTest, test_ioctl_allowed) { if (!run_landlock_tests_) GTEST_SKIP(); const int landlock_version = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (landlock_version < LANDLOCK_ABI_FS_IOCTL_DEV_SUPPORTED) { GTEST_SKIP() << "Test requires Landlock ABI " << LANDLOCK_ABI_FS_IOCTL_DEV_SUPPORTED << ", but system only supports ABI " << landlock_version; } ScopedMinijail j(minijail_new()); minijail_set_landlock_abi(j.get(), LANDLOCK_ABI_FS_IOCTL_DEV_SUPPORTED); SetupLandlockTestingNamespaces(j.get()); minijail_add_fs_restriction_rx(j.get(), "/"); minijail_add_fs_restriction_access_rights( j.get(), "/dev/zero", LANDLOCK_ACCESS_FS_IOCTL_DEV); EXPECT_EQ(0, RunIoctlTestProcess(j.get())); } void TestCreateSession(bool create_session) { int status; int pipe_fds[2]; pid_t child_pid; pid_t parent_sid = getsid(0); ssize_t pid_size = sizeof(pid_t); ScopedMinijail j(minijail_new()); // stdin/stdout/stderr might be attached to TTYs. Close them to avoid creating // a new session because of that. minijail_close_open_fds(j.get()); if (create_session) minijail_create_session(j.get()); ASSERT_EQ(pipe(pipe_fds), 0); minijail_preserve_fd(j.get(), pipe_fds[0], pipe_fds[0]); child_pid = minijail_fork(j.get()); ASSERT_GE(child_pid, 0); if (child_pid == 0) { pid_t sid_in_parent; ASSERT_EQ(read(pipe_fds[0], &sid_in_parent, pid_size), pid_size); if (create_session) ASSERT_NE(sid_in_parent, getsid(0)); else ASSERT_EQ(sid_in_parent, getsid(0)); exit(0); } EXPECT_EQ(write(pipe_fds[1], &parent_sid, pid_size), pid_size); waitpid(child_pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); } TEST(Test, default_no_new_session) { TestCreateSession(/*create_session=*/false); } TEST(Test, create_new_session) { TestCreateSession(/*create_session=*/true); } TEST(Test, syscall_name_altsyscall) { ScopedMinijail j(minijail_new()); // Use a placeholder since we don't need a valid table (yet). minijail_use_alt_syscall(j.get(), "placeholder"); EXPECT_EQ(std::string(minijail_syscall_name(j.get(), 1)), std::string(kAltSyscallNamePlaceholder)); } TEST(Test, syscall_name) { // With jail; Success. ScopedMinijail j(minijail_new()); EXPECT_STREQ(minijail_syscall_name(j.get(), SYS_read), "read"); // Without jail; Success. EXPECT_STREQ(minijail_syscall_name(nullptr, SYS_read), "read"); // With jail; Null. EXPECT_EQ(minijail_syscall_name(j.get(), -1), nullptr); // Without jail; Null. EXPECT_EQ(minijail_syscall_name(nullptr, -1), nullptr); } minijail-2026.05.18+git20261016.8787d1a/libminijailpreload.c000066400000000000000000000122001526451555500223240ustar00rootroot00000000000000/* libminijailpreload.c - preload hack library * Copyright 2011 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * This library is preloaded into every program launched by minijail_run(). * DO NOT EXPORT ANY SYMBOLS FROM THIS LIBRARY. They will replace other symbols * in the programs it is preloaded into and cause impossible-to-debug failures. * See the minijail0.1 for a design explanation. */ #include "libminijail-private.h" #include "libminijail.h" #include "util.h" #include #include #include #include #include #include static int (*real_main)(int, char **, char **); static void *libc_handle; static void maybe_init_stderr_logging(void) { const char *target = getenv(kLoggingEnvVar); if (target && streq(target, kLoggingEnvValueStderr)) { init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO); } } static void truncate_preload_env(char **envp, const char *name) { char *env_value = minijail_getenv(envp, name); if (env_value) { /* * if we have more than just libminijailpreload.so in * LD_PRELOAD, cut out libminijailpreload.so from it, * as it is guaranteed to always be last in the * LD_PRELOAD list. */ char *last_space = strrchr(env_value, ' '); if (last_space) { *last_space = '\0'; } else { /* Only our lib was in LD_PRELOAD, just unset it. */ minijail_unsetenv(envp, name); } } } /** @brief Fake main(), spliced in before the real call to main() by * __libc_start_main (see below). * We get serialized commands from our invoking process over an fd specified * by an environment variable (kFdEnvVar). The environment variable is a list * of key=value pairs (see move_commands_to_env); we use them to construct a * jail, then enter it. */ static int fake_main(int argc, char **argv, char **envp) { char *fd_name = getenv(kFdEnvVar); int fd = -1; struct minijail *j; if (geteuid() != getuid() || getegid() != getgid()) { /* * If we didn't do this check, an attacker could set kFdEnvVar * for any setuid program that uses libminijail to cause it to * get capabilities or a uid it did not expect. */ /* TODO(wad): why would libminijail interact here? */ return MINIJAIL_ERR_PRELOAD; } if (!fd_name) return MINIJAIL_ERR_PRELOAD; fd = atoi(fd_name); if (fd < 0) return MINIJAIL_ERR_PRELOAD; j = minijail_new(); if (!j) die("preload: out of memory"); if (minijail_from_fd(fd, j)) die("preload: failed to parse minijail from parent"); close(fd); minijail_unsetenv(envp, kFdEnvVar); minijail_unsetenv(envp, kLoggingEnvVar); truncate_preload_env(envp, kLdPreloadEnvVar); /* Strip out flags meant for the parent. */ minijail_preenter(j); minijail_enter(j); minijail_destroy(j); dlclose(libc_handle); return real_main(argc, argv, envp); } /** @brief LD_PRELOAD override of __libc_start_main. * * It is really best if you do not look too closely at this function. We need * to ensure that some of our code runs before the target program (see the * minijail0.1 file in this directory for high-level details about this), and * the only available place to hook is this function, which is normally * responsible for calling main(). Our LD_PRELOAD will overwrite the real * __libc_start_main with this one, so we have to look up the real one from * libc and invoke it with a pointer to the fake main() we'd like to run before * the real main(). We can't just run our setup code *here* because * __libc_start_main is responsible for setting up the C runtime environment, * so we can't rely on things like malloc() being available yet. */ int API __libc_start_main(int (*main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void(*stack_end)) { void *sym; /* * This hack is unfortunately required by C99 - casting directly from * void* to function pointers is left undefined. See POSIX.1-2003, the * Rationale for the specification of dlsym(), and dlsym(3). This * deliberately violates strict-aliasing rules, but gcc can't tell. */ union { int (*fn)(int (*main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void(*stack_end)); void *symval; } real_libc_start_main; /* * We hold this handle for the duration of the real __libc_start_main() * and drop it just before calling the real main(). */ maybe_init_stderr_logging(); libc_handle = dlopen("libc.so.6", RTLD_NOW); if (!libc_handle) { do_log(LOG_ERR, "can't dlopen() libc"); /* * We dare not use abort() here because it will run atexit() * handlers and try to flush stdio. */ _exit(1); } sym = dlsym(libc_handle, "__libc_start_main"); if (!sym) { do_log(LOG_ERR, "can't find the real __libc_start_main()"); _exit(1); } real_libc_start_main.symval = sym; real_main = main; /* * Note that we swap fake_main in for main - fake_main knows that it * should call real_main after it's done. */ return real_libc_start_main.fn(fake_main, argc, ubp_av, init, fini, rtld_fini, stack_end); } minijail-2026.05.18+git20261016.8787d1a/libsyscalls.h000066400000000000000000000006451526451555500210350ustar00rootroot00000000000000/* Copyright 2011 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef MINIJAIL_LIBSYSCALLS_H_ #define MINIJAIL_LIBSYSCALLS_H_ #include struct syscall_entry { const char *name; int nr; }; extern const struct syscall_entry syscall_table[]; extern const size_t syscall_table_size; #endif /* MINIJAIL_LIBSYSCALLS_H_ */ minijail-2026.05.18+git20261016.8787d1a/linux-x86/000077500000000000000000000000001526451555500201155ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/linux-x86/libconstants.gen.c000066400000000000000000006267201526451555500235510ustar00rootroot00000000000000/* GENERATED BY MAKEFILE */ #include "gen_constants-inl.h" #include "libconstants.h" const struct constant_entry constant_table[] = { #ifdef ADDRB { "ADDRB", (unsigned long) ADDRB }, #endif // ADDRB #ifdef AF_ALG { "AF_ALG", (unsigned long) AF_ALG }, #endif // AF_ALG #ifdef AF_APPLETALK { "AF_APPLETALK", (unsigned long) AF_APPLETALK }, #endif // AF_APPLETALK #ifdef AF_ASH { "AF_ASH", (unsigned long) AF_ASH }, #endif // AF_ASH #ifdef AF_ATMPVC { "AF_ATMPVC", (unsigned long) AF_ATMPVC }, #endif // AF_ATMPVC #ifdef AF_ATMSVC { "AF_ATMSVC", (unsigned long) AF_ATMSVC }, #endif // AF_ATMSVC #ifdef AF_AX25 { "AF_AX25", (unsigned long) AF_AX25 }, #endif // AF_AX25 #ifdef AF_BLUETOOTH { "AF_BLUETOOTH", (unsigned long) AF_BLUETOOTH }, #endif // AF_BLUETOOTH #ifdef AF_BRIDGE { "AF_BRIDGE", (unsigned long) AF_BRIDGE }, #endif // AF_BRIDGE #ifdef AF_CAIF { "AF_CAIF", (unsigned long) AF_CAIF }, #endif // AF_CAIF #ifdef AF_CAN { "AF_CAN", (unsigned long) AF_CAN }, #endif // AF_CAN #ifdef AF_ECONET { "AF_ECONET", (unsigned long) AF_ECONET }, #endif // AF_ECONET #ifdef AF_FILE { "AF_FILE", (unsigned long) AF_FILE }, #endif // AF_FILE #ifdef AF_IB { "AF_IB", (unsigned long) AF_IB }, #endif // AF_IB #ifdef AF_IEEE802154 { "AF_IEEE802154", (unsigned long) AF_IEEE802154 }, #endif // AF_IEEE802154 #ifdef AF_INET6 { "AF_INET6", (unsigned long) AF_INET6 }, #endif // AF_INET6 #ifdef AF_INET { "AF_INET", (unsigned long) AF_INET }, #endif // AF_INET #ifdef AF_IPX { "AF_IPX", (unsigned long) AF_IPX }, #endif // AF_IPX #ifdef AF_IRDA { "AF_IRDA", (unsigned long) AF_IRDA }, #endif // AF_IRDA #ifdef AF_ISDN { "AF_ISDN", (unsigned long) AF_ISDN }, #endif // AF_ISDN #ifdef AF_IUCV { "AF_IUCV", (unsigned long) AF_IUCV }, #endif // AF_IUCV #ifdef AF_KCM { "AF_KCM", (unsigned long) AF_KCM }, #endif // AF_KCM #ifdef AF_KEY { "AF_KEY", (unsigned long) AF_KEY }, #endif // AF_KEY #ifdef AF_LLC { "AF_LLC", (unsigned long) AF_LLC }, #endif // AF_LLC #ifdef AF_LOCAL { "AF_LOCAL", (unsigned long) AF_LOCAL }, #endif // AF_LOCAL #ifdef AF_MAX { "AF_MAX", (unsigned long) AF_MAX }, #endif // AF_MAX #ifdef AF_MCTP { "AF_MCTP", (unsigned long) AF_MCTP }, #endif // AF_MCTP #ifdef AF_MPLS { "AF_MPLS", (unsigned long) AF_MPLS }, #endif // AF_MPLS #ifdef AF_NETBEUI { "AF_NETBEUI", (unsigned long) AF_NETBEUI }, #endif // AF_NETBEUI #ifdef AF_NETLINK { "AF_NETLINK", (unsigned long) AF_NETLINK }, #endif // AF_NETLINK #ifdef AF_NETROM { "AF_NETROM", (unsigned long) AF_NETROM }, #endif // AF_NETROM #ifdef AF_NFC { "AF_NFC", (unsigned long) AF_NFC }, #endif // AF_NFC #ifdef AF_PACKET { "AF_PACKET", (unsigned long) AF_PACKET }, #endif // AF_PACKET #ifdef AF_PHONET { "AF_PHONET", (unsigned long) AF_PHONET }, #endif // AF_PHONET #ifdef AF_PPPOX { "AF_PPPOX", (unsigned long) AF_PPPOX }, #endif // AF_PPPOX #ifdef AF_QIPCRTR { "AF_QIPCRTR", (unsigned long) AF_QIPCRTR }, #endif // AF_QIPCRTR #ifdef AF_RDS { "AF_RDS", (unsigned long) AF_RDS }, #endif // AF_RDS #ifdef AF_ROSE { "AF_ROSE", (unsigned long) AF_ROSE }, #endif // AF_ROSE #ifdef AF_ROUTE { "AF_ROUTE", (unsigned long) AF_ROUTE }, #endif // AF_ROUTE #ifdef AF_RXRPC { "AF_RXRPC", (unsigned long) AF_RXRPC }, #endif // AF_RXRPC #ifdef AF_SECURITY { "AF_SECURITY", (unsigned long) AF_SECURITY }, #endif // AF_SECURITY #ifdef AF_SMC { "AF_SMC", (unsigned long) AF_SMC }, #endif // AF_SMC #ifdef AF_SNA { "AF_SNA", (unsigned long) AF_SNA }, #endif // AF_SNA #ifdef AF_TIPC { "AF_TIPC", (unsigned long) AF_TIPC }, #endif // AF_TIPC #ifdef AF_UNIX { "AF_UNIX", (unsigned long) AF_UNIX }, #endif // AF_UNIX #ifdef AF_UNSPEC { "AF_UNSPEC", (unsigned long) AF_UNSPEC }, #endif // AF_UNSPEC #ifdef AF_VSOCK { "AF_VSOCK", (unsigned long) AF_VSOCK }, #endif // AF_VSOCK #ifdef AF_WANPIPE { "AF_WANPIPE", (unsigned long) AF_WANPIPE }, #endif // AF_WANPIPE #ifdef AF_X25 { "AF_X25", (unsigned long) AF_X25 }, #endif // AF_X25 #ifdef AF_XDP { "AF_XDP", (unsigned long) AF_XDP }, #endif // AF_XDP #ifdef ARCH_ENABLE_TAGGED_ADDR { "ARCH_ENABLE_TAGGED_ADDR", (unsigned long) ARCH_ENABLE_TAGGED_ADDR }, #endif // ARCH_ENABLE_TAGGED_ADDR #ifdef ARCH_FORCE_TAGGED_SVA { "ARCH_FORCE_TAGGED_SVA", (unsigned long) ARCH_FORCE_TAGGED_SVA }, #endif // ARCH_FORCE_TAGGED_SVA #ifdef ARCH_GET_CPUID { "ARCH_GET_CPUID", (unsigned long) ARCH_GET_CPUID }, #endif // ARCH_GET_CPUID #ifdef ARCH_GET_FS { "ARCH_GET_FS", (unsigned long) ARCH_GET_FS }, #endif // ARCH_GET_FS #ifdef ARCH_GET_GS { "ARCH_GET_GS", (unsigned long) ARCH_GET_GS }, #endif // ARCH_GET_GS #ifdef ARCH_GET_MAX_TAG_BITS { "ARCH_GET_MAX_TAG_BITS", (unsigned long) ARCH_GET_MAX_TAG_BITS }, #endif // ARCH_GET_MAX_TAG_BITS #ifdef ARCH_GET_UNTAG_MASK { "ARCH_GET_UNTAG_MASK", (unsigned long) ARCH_GET_UNTAG_MASK }, #endif // ARCH_GET_UNTAG_MASK #ifdef ARCH_GET_XCOMP_GUEST_PERM { "ARCH_GET_XCOMP_GUEST_PERM", (unsigned long) ARCH_GET_XCOMP_GUEST_PERM }, #endif // ARCH_GET_XCOMP_GUEST_PERM #ifdef ARCH_GET_XCOMP_PERM { "ARCH_GET_XCOMP_PERM", (unsigned long) ARCH_GET_XCOMP_PERM }, #endif // ARCH_GET_XCOMP_PERM #ifdef ARCH_GET_XCOMP_SUPP { "ARCH_GET_XCOMP_SUPP", (unsigned long) ARCH_GET_XCOMP_SUPP }, #endif // ARCH_GET_XCOMP_SUPP #ifdef ARCH_MAP_VDSO_32 { "ARCH_MAP_VDSO_32", (unsigned long) ARCH_MAP_VDSO_32 }, #endif // ARCH_MAP_VDSO_32 #ifdef ARCH_MAP_VDSO_64 { "ARCH_MAP_VDSO_64", (unsigned long) ARCH_MAP_VDSO_64 }, #endif // ARCH_MAP_VDSO_64 #ifdef ARCH_MAP_VDSO_X32 { "ARCH_MAP_VDSO_X32", (unsigned long) ARCH_MAP_VDSO_X32 }, #endif // ARCH_MAP_VDSO_X32 #ifdef ARCH_REQ_XCOMP_GUEST_PERM { "ARCH_REQ_XCOMP_GUEST_PERM", (unsigned long) ARCH_REQ_XCOMP_GUEST_PERM }, #endif // ARCH_REQ_XCOMP_GUEST_PERM #ifdef ARCH_REQ_XCOMP_PERM { "ARCH_REQ_XCOMP_PERM", (unsigned long) ARCH_REQ_XCOMP_PERM }, #endif // ARCH_REQ_XCOMP_PERM #ifdef ARCH_SET_CPUID { "ARCH_SET_CPUID", (unsigned long) ARCH_SET_CPUID }, #endif // ARCH_SET_CPUID #ifdef ARCH_SET_FS { "ARCH_SET_FS", (unsigned long) ARCH_SET_FS }, #endif // ARCH_SET_FS #ifdef ARCH_SET_GS { "ARCH_SET_GS", (unsigned long) ARCH_SET_GS }, #endif // ARCH_SET_GS #ifdef ARCH_SHSTK_DISABLE { "ARCH_SHSTK_DISABLE", (unsigned long) ARCH_SHSTK_DISABLE }, #endif // ARCH_SHSTK_DISABLE #ifdef ARCH_SHSTK_ENABLE { "ARCH_SHSTK_ENABLE", (unsigned long) ARCH_SHSTK_ENABLE }, #endif // ARCH_SHSTK_ENABLE #ifdef ARCH_SHSTK_LOCK { "ARCH_SHSTK_LOCK", (unsigned long) ARCH_SHSTK_LOCK }, #endif // ARCH_SHSTK_LOCK #ifdef ARCH_SHSTK_STATUS { "ARCH_SHSTK_STATUS", (unsigned long) ARCH_SHSTK_STATUS }, #endif // ARCH_SHSTK_STATUS #ifdef ARCH_SHSTK_UNLOCK { "ARCH_SHSTK_UNLOCK", (unsigned long) ARCH_SHSTK_UNLOCK }, #endif // ARCH_SHSTK_UNLOCK #ifdef ARCH_XCOMP_TILECFG { "ARCH_XCOMP_TILECFG", (unsigned long) ARCH_XCOMP_TILECFG }, #endif // ARCH_XCOMP_TILECFG #ifdef ARCH_XCOMP_TILEDATA { "ARCH_XCOMP_TILEDATA", (unsigned long) ARCH_XCOMP_TILEDATA }, #endif // ARCH_XCOMP_TILEDATA #ifdef ARG_MAX { "ARG_MAX", (unsigned long) ARG_MAX }, #endif // ARG_MAX #ifdef ASYNCB_AUTO_IRQ { "ASYNCB_AUTO_IRQ", (unsigned long) ASYNCB_AUTO_IRQ }, #endif // ASYNCB_AUTO_IRQ #ifdef ASYNCB_AUTOPROBE { "ASYNCB_AUTOPROBE", (unsigned long) ASYNCB_AUTOPROBE }, #endif // ASYNCB_AUTOPROBE #ifdef ASYNCB_BOOT_AUTOCONF { "ASYNCB_BOOT_AUTOCONF", (unsigned long) ASYNCB_BOOT_AUTOCONF }, #endif // ASYNCB_BOOT_AUTOCONF #ifdef ASYNCB_BUGGY_UART { "ASYNCB_BUGGY_UART", (unsigned long) ASYNCB_BUGGY_UART }, #endif // ASYNCB_BUGGY_UART #ifdef ASYNCB_CALLOUT_NOHUP { "ASYNCB_CALLOUT_NOHUP", (unsigned long) ASYNCB_CALLOUT_NOHUP }, #endif // ASYNCB_CALLOUT_NOHUP #ifdef ASYNCB_CHECK_CD { "ASYNCB_CHECK_CD", (unsigned long) ASYNCB_CHECK_CD }, #endif // ASYNCB_CHECK_CD #ifdef ASYNCB_CLOSING { "ASYNCB_CLOSING", (unsigned long) ASYNCB_CLOSING }, #endif // ASYNCB_CLOSING #ifdef ASYNCB_CONS_FLOW { "ASYNCB_CONS_FLOW", (unsigned long) ASYNCB_CONS_FLOW }, #endif // ASYNCB_CONS_FLOW #ifdef ASYNCB_CTS_FLOW { "ASYNCB_CTS_FLOW", (unsigned long) ASYNCB_CTS_FLOW }, #endif // ASYNCB_CTS_FLOW #ifdef ASYNCB_FIRST_KERNEL { "ASYNCB_FIRST_KERNEL", (unsigned long) ASYNCB_FIRST_KERNEL }, #endif // ASYNCB_FIRST_KERNEL #ifdef ASYNCB_FOURPORT { "ASYNCB_FOURPORT", (unsigned long) ASYNCB_FOURPORT }, #endif // ASYNCB_FOURPORT #ifdef ASYNCB_HARDPPS_CD { "ASYNCB_HARDPPS_CD", (unsigned long) ASYNCB_HARDPPS_CD }, #endif // ASYNCB_HARDPPS_CD #ifdef ASYNCB_HUP_NOTIFY { "ASYNCB_HUP_NOTIFY", (unsigned long) ASYNCB_HUP_NOTIFY }, #endif // ASYNCB_HUP_NOTIFY #ifdef ASYNCB_INITIALIZED { "ASYNCB_INITIALIZED", (unsigned long) ASYNCB_INITIALIZED }, #endif // ASYNCB_INITIALIZED #ifdef ASYNCB_LAST_USER { "ASYNCB_LAST_USER", (unsigned long) ASYNCB_LAST_USER }, #endif // ASYNCB_LAST_USER #ifdef ASYNCB_LOW_LATENCY { "ASYNCB_LOW_LATENCY", (unsigned long) ASYNCB_LOW_LATENCY }, #endif // ASYNCB_LOW_LATENCY #ifdef ASYNCB_MAGIC_MULTIPLIER { "ASYNCB_MAGIC_MULTIPLIER", (unsigned long) ASYNCB_MAGIC_MULTIPLIER }, #endif // ASYNCB_MAGIC_MULTIPLIER #ifdef ASYNCB_NORMAL_ACTIVE { "ASYNCB_NORMAL_ACTIVE", (unsigned long) ASYNCB_NORMAL_ACTIVE }, #endif // ASYNCB_NORMAL_ACTIVE #ifdef ASYNCB_PGRP_LOCKOUT { "ASYNCB_PGRP_LOCKOUT", (unsigned long) ASYNCB_PGRP_LOCKOUT }, #endif // ASYNCB_PGRP_LOCKOUT #ifdef ASYNCB_SAK { "ASYNCB_SAK", (unsigned long) ASYNCB_SAK }, #endif // ASYNCB_SAK #ifdef ASYNCB_SESSION_LOCKOUT { "ASYNCB_SESSION_LOCKOUT", (unsigned long) ASYNCB_SESSION_LOCKOUT }, #endif // ASYNCB_SESSION_LOCKOUT #ifdef ASYNCB_SHARE_IRQ { "ASYNCB_SHARE_IRQ", (unsigned long) ASYNCB_SHARE_IRQ }, #endif // ASYNCB_SHARE_IRQ #ifdef ASYNCB_SKIP_TEST { "ASYNCB_SKIP_TEST", (unsigned long) ASYNCB_SKIP_TEST }, #endif // ASYNCB_SKIP_TEST #ifdef ASYNCB_SPD_HI { "ASYNCB_SPD_HI", (unsigned long) ASYNCB_SPD_HI }, #endif // ASYNCB_SPD_HI #ifdef ASYNCB_SPD_SHI { "ASYNCB_SPD_SHI", (unsigned long) ASYNCB_SPD_SHI }, #endif // ASYNCB_SPD_SHI #ifdef ASYNCB_SPD_VHI { "ASYNCB_SPD_VHI", (unsigned long) ASYNCB_SPD_VHI }, #endif // ASYNCB_SPD_VHI #ifdef ASYNCB_SPLIT_TERMIOS { "ASYNCB_SPLIT_TERMIOS", (unsigned long) ASYNCB_SPLIT_TERMIOS }, #endif // ASYNCB_SPLIT_TERMIOS #ifdef ASYNCB_SUSPENDED { "ASYNCB_SUSPENDED", (unsigned long) ASYNCB_SUSPENDED }, #endif // ASYNCB_SUSPENDED #ifdef ASYNC_CLOSING_WAIT_INF { "ASYNC_CLOSING_WAIT_INF", (unsigned long) ASYNC_CLOSING_WAIT_INF }, #endif // ASYNC_CLOSING_WAIT_INF #ifdef ASYNC_CLOSING_WAIT_NONE { "ASYNC_CLOSING_WAIT_NONE", (unsigned long) ASYNC_CLOSING_WAIT_NONE }, #endif // ASYNC_CLOSING_WAIT_NONE #ifdef AT_EACCESS { "AT_EACCESS", (unsigned long) AT_EACCESS }, #endif // AT_EACCESS #ifdef AT_REMOVEDIR { "AT_REMOVEDIR", (unsigned long) AT_REMOVEDIR }, #endif // AT_REMOVEDIR #ifdef AT_SYMLINK_FOLLOW { "AT_SYMLINK_FOLLOW", (unsigned long) AT_SYMLINK_FOLLOW }, #endif // AT_SYMLINK_FOLLOW #ifdef AT_SYMLINK_NOFOLLOW { "AT_SYMLINK_NOFOLLOW", (unsigned long) AT_SYMLINK_NOFOLLOW }, #endif // AT_SYMLINK_NOFOLLOW #ifdef AUDIT_ADD { "AUDIT_ADD", (unsigned long) AUDIT_ADD }, #endif // AUDIT_ADD #ifdef AUDIT_ADD_RULE { "AUDIT_ADD_RULE", (unsigned long) AUDIT_ADD_RULE }, #endif // AUDIT_ADD_RULE #ifdef AUDIT_ALWAYS { "AUDIT_ALWAYS", (unsigned long) AUDIT_ALWAYS }, #endif // AUDIT_ALWAYS #ifdef AUDIT_ANOM_ABEND { "AUDIT_ANOM_ABEND", (unsigned long) AUDIT_ANOM_ABEND }, #endif // AUDIT_ANOM_ABEND #ifdef AUDIT_ANOM_CREAT { "AUDIT_ANOM_CREAT", (unsigned long) AUDIT_ANOM_CREAT }, #endif // AUDIT_ANOM_CREAT #ifdef AUDIT_ANOM_LINK { "AUDIT_ANOM_LINK", (unsigned long) AUDIT_ANOM_LINK }, #endif // AUDIT_ANOM_LINK #ifdef AUDIT_ANOM_PROMISCUOUS { "AUDIT_ANOM_PROMISCUOUS", (unsigned long) AUDIT_ANOM_PROMISCUOUS }, #endif // AUDIT_ANOM_PROMISCUOUS #ifdef AUDIT_ARCH { "AUDIT_ARCH", (unsigned long) AUDIT_ARCH }, #endif // AUDIT_ARCH #ifdef AUDIT_ARG0 { "AUDIT_ARG0", (unsigned long) AUDIT_ARG0 }, #endif // AUDIT_ARG0 #ifdef AUDIT_AVC { "AUDIT_AVC", (unsigned long) AUDIT_AVC }, #endif // AUDIT_AVC #ifdef AUDIT_AVC_PATH { "AUDIT_AVC_PATH", (unsigned long) AUDIT_AVC_PATH }, #endif // AUDIT_AVC_PATH #ifdef AUDIT_BIT_MASK { "AUDIT_BIT_MASK", (unsigned long) AUDIT_BIT_MASK }, #endif // AUDIT_BIT_MASK #ifdef AUDIT_BITMASK_SIZE { "AUDIT_BITMASK_SIZE", (unsigned long) AUDIT_BITMASK_SIZE }, #endif // AUDIT_BITMASK_SIZE #ifdef AUDIT_BPF { "AUDIT_BPF", (unsigned long) AUDIT_BPF }, #endif // AUDIT_BPF #ifdef AUDIT_BPRM_FCAPS { "AUDIT_BPRM_FCAPS", (unsigned long) AUDIT_BPRM_FCAPS }, #endif // AUDIT_BPRM_FCAPS #ifdef AUDIT_CAPSET { "AUDIT_CAPSET", (unsigned long) AUDIT_CAPSET }, #endif // AUDIT_CAPSET #ifdef AUDIT_CLASS_CHATTR { "AUDIT_CLASS_CHATTR", (unsigned long) AUDIT_CLASS_CHATTR }, #endif // AUDIT_CLASS_CHATTR #ifdef AUDIT_CLASS_CHATTR_32 { "AUDIT_CLASS_CHATTR_32", (unsigned long) AUDIT_CLASS_CHATTR_32 }, #endif // AUDIT_CLASS_CHATTR_32 #ifdef AUDIT_CLASS_DIR_WRITE { "AUDIT_CLASS_DIR_WRITE", (unsigned long) AUDIT_CLASS_DIR_WRITE }, #endif // AUDIT_CLASS_DIR_WRITE #ifdef AUDIT_CLASS_DIR_WRITE_32 { "AUDIT_CLASS_DIR_WRITE_32", (unsigned long) AUDIT_CLASS_DIR_WRITE_32 }, #endif // AUDIT_CLASS_DIR_WRITE_32 #ifdef AUDIT_CLASS_READ_32 { "AUDIT_CLASS_READ_32", (unsigned long) AUDIT_CLASS_READ_32 }, #endif // AUDIT_CLASS_READ_32 #ifdef AUDIT_CLASS_READ { "AUDIT_CLASS_READ", (unsigned long) AUDIT_CLASS_READ }, #endif // AUDIT_CLASS_READ #ifdef AUDIT_CLASS_SIGNAL_32 { "AUDIT_CLASS_SIGNAL_32", (unsigned long) AUDIT_CLASS_SIGNAL_32 }, #endif // AUDIT_CLASS_SIGNAL_32 #ifdef AUDIT_CLASS_SIGNAL { "AUDIT_CLASS_SIGNAL", (unsigned long) AUDIT_CLASS_SIGNAL }, #endif // AUDIT_CLASS_SIGNAL #ifdef AUDIT_CLASS_WRITE_32 { "AUDIT_CLASS_WRITE_32", (unsigned long) AUDIT_CLASS_WRITE_32 }, #endif // AUDIT_CLASS_WRITE_32 #ifdef AUDIT_CLASS_WRITE { "AUDIT_CLASS_WRITE", (unsigned long) AUDIT_CLASS_WRITE }, #endif // AUDIT_CLASS_WRITE #ifdef AUDIT_COMPARE_AUID_TO_EUID { "AUDIT_COMPARE_AUID_TO_EUID", (unsigned long) AUDIT_COMPARE_AUID_TO_EUID }, #endif // AUDIT_COMPARE_AUID_TO_EUID #ifdef AUDIT_COMPARE_AUID_TO_FSUID { "AUDIT_COMPARE_AUID_TO_FSUID", (unsigned long) AUDIT_COMPARE_AUID_TO_FSUID }, #endif // AUDIT_COMPARE_AUID_TO_FSUID #ifdef AUDIT_COMPARE_AUID_TO_OBJ_UID { "AUDIT_COMPARE_AUID_TO_OBJ_UID", (unsigned long) AUDIT_COMPARE_AUID_TO_OBJ_UID }, #endif // AUDIT_COMPARE_AUID_TO_OBJ_UID #ifdef AUDIT_COMPARE_AUID_TO_SUID { "AUDIT_COMPARE_AUID_TO_SUID", (unsigned long) AUDIT_COMPARE_AUID_TO_SUID }, #endif // AUDIT_COMPARE_AUID_TO_SUID #ifdef AUDIT_COMPARE_EGID_TO_FSGID { "AUDIT_COMPARE_EGID_TO_FSGID", (unsigned long) AUDIT_COMPARE_EGID_TO_FSGID }, #endif // AUDIT_COMPARE_EGID_TO_FSGID #ifdef AUDIT_COMPARE_EGID_TO_OBJ_GID { "AUDIT_COMPARE_EGID_TO_OBJ_GID", (unsigned long) AUDIT_COMPARE_EGID_TO_OBJ_GID }, #endif // AUDIT_COMPARE_EGID_TO_OBJ_GID #ifdef AUDIT_COMPARE_EGID_TO_SGID { "AUDIT_COMPARE_EGID_TO_SGID", (unsigned long) AUDIT_COMPARE_EGID_TO_SGID }, #endif // AUDIT_COMPARE_EGID_TO_SGID #ifdef AUDIT_COMPARE_EUID_TO_FSUID { "AUDIT_COMPARE_EUID_TO_FSUID", (unsigned long) AUDIT_COMPARE_EUID_TO_FSUID }, #endif // AUDIT_COMPARE_EUID_TO_FSUID #ifdef AUDIT_COMPARE_EUID_TO_OBJ_UID { "AUDIT_COMPARE_EUID_TO_OBJ_UID", (unsigned long) AUDIT_COMPARE_EUID_TO_OBJ_UID }, #endif // AUDIT_COMPARE_EUID_TO_OBJ_UID #ifdef AUDIT_COMPARE_EUID_TO_SUID { "AUDIT_COMPARE_EUID_TO_SUID", (unsigned long) AUDIT_COMPARE_EUID_TO_SUID }, #endif // AUDIT_COMPARE_EUID_TO_SUID #ifdef AUDIT_COMPARE_FSGID_TO_OBJ_GID { "AUDIT_COMPARE_FSGID_TO_OBJ_GID", (unsigned long) AUDIT_COMPARE_FSGID_TO_OBJ_GID }, #endif // AUDIT_COMPARE_FSGID_TO_OBJ_GID #ifdef AUDIT_COMPARE_FSUID_TO_OBJ_UID { "AUDIT_COMPARE_FSUID_TO_OBJ_UID", (unsigned long) AUDIT_COMPARE_FSUID_TO_OBJ_UID }, #endif // AUDIT_COMPARE_FSUID_TO_OBJ_UID #ifdef AUDIT_COMPARE_GID_TO_EGID { "AUDIT_COMPARE_GID_TO_EGID", (unsigned long) AUDIT_COMPARE_GID_TO_EGID }, #endif // AUDIT_COMPARE_GID_TO_EGID #ifdef AUDIT_COMPARE_GID_TO_FSGID { "AUDIT_COMPARE_GID_TO_FSGID", (unsigned long) AUDIT_COMPARE_GID_TO_FSGID }, #endif // AUDIT_COMPARE_GID_TO_FSGID #ifdef AUDIT_COMPARE_GID_TO_OBJ_GID { "AUDIT_COMPARE_GID_TO_OBJ_GID", (unsigned long) AUDIT_COMPARE_GID_TO_OBJ_GID }, #endif // AUDIT_COMPARE_GID_TO_OBJ_GID #ifdef AUDIT_COMPARE_GID_TO_SGID { "AUDIT_COMPARE_GID_TO_SGID", (unsigned long) AUDIT_COMPARE_GID_TO_SGID }, #endif // AUDIT_COMPARE_GID_TO_SGID #ifdef AUDIT_COMPARE_SGID_TO_FSGID { "AUDIT_COMPARE_SGID_TO_FSGID", (unsigned long) AUDIT_COMPARE_SGID_TO_FSGID }, #endif // AUDIT_COMPARE_SGID_TO_FSGID #ifdef AUDIT_COMPARE_SGID_TO_OBJ_GID { "AUDIT_COMPARE_SGID_TO_OBJ_GID", (unsigned long) AUDIT_COMPARE_SGID_TO_OBJ_GID }, #endif // AUDIT_COMPARE_SGID_TO_OBJ_GID #ifdef AUDIT_COMPARE_SUID_TO_FSUID { "AUDIT_COMPARE_SUID_TO_FSUID", (unsigned long) AUDIT_COMPARE_SUID_TO_FSUID }, #endif // AUDIT_COMPARE_SUID_TO_FSUID #ifdef AUDIT_COMPARE_SUID_TO_OBJ_UID { "AUDIT_COMPARE_SUID_TO_OBJ_UID", (unsigned long) AUDIT_COMPARE_SUID_TO_OBJ_UID }, #endif // AUDIT_COMPARE_SUID_TO_OBJ_UID #ifdef AUDIT_COMPARE_UID_TO_AUID { "AUDIT_COMPARE_UID_TO_AUID", (unsigned long) AUDIT_COMPARE_UID_TO_AUID }, #endif // AUDIT_COMPARE_UID_TO_AUID #ifdef AUDIT_COMPARE_UID_TO_EUID { "AUDIT_COMPARE_UID_TO_EUID", (unsigned long) AUDIT_COMPARE_UID_TO_EUID }, #endif // AUDIT_COMPARE_UID_TO_EUID #ifdef AUDIT_COMPARE_UID_TO_FSUID { "AUDIT_COMPARE_UID_TO_FSUID", (unsigned long) AUDIT_COMPARE_UID_TO_FSUID }, #endif // AUDIT_COMPARE_UID_TO_FSUID #ifdef AUDIT_COMPARE_UID_TO_OBJ_UID { "AUDIT_COMPARE_UID_TO_OBJ_UID", (unsigned long) AUDIT_COMPARE_UID_TO_OBJ_UID }, #endif // AUDIT_COMPARE_UID_TO_OBJ_UID #ifdef AUDIT_COMPARE_UID_TO_SUID { "AUDIT_COMPARE_UID_TO_SUID", (unsigned long) AUDIT_COMPARE_UID_TO_SUID }, #endif // AUDIT_COMPARE_UID_TO_SUID #ifdef AUDIT_CONFIG_CHANGE { "AUDIT_CONFIG_CHANGE", (unsigned long) AUDIT_CONFIG_CHANGE }, #endif // AUDIT_CONFIG_CHANGE #ifdef AUDIT_CWD { "AUDIT_CWD", (unsigned long) AUDIT_CWD }, #endif // AUDIT_CWD #ifdef AUDIT_DAEMON_ABORT { "AUDIT_DAEMON_ABORT", (unsigned long) AUDIT_DAEMON_ABORT }, #endif // AUDIT_DAEMON_ABORT #ifdef AUDIT_DAEMON_CONFIG { "AUDIT_DAEMON_CONFIG", (unsigned long) AUDIT_DAEMON_CONFIG }, #endif // AUDIT_DAEMON_CONFIG #ifdef AUDIT_DAEMON_END { "AUDIT_DAEMON_END", (unsigned long) AUDIT_DAEMON_END }, #endif // AUDIT_DAEMON_END #ifdef AUDIT_DAEMON_START { "AUDIT_DAEMON_START", (unsigned long) AUDIT_DAEMON_START }, #endif // AUDIT_DAEMON_START #ifdef AUDIT_DEL { "AUDIT_DEL", (unsigned long) AUDIT_DEL }, #endif // AUDIT_DEL #ifdef AUDIT_DEL_RULE { "AUDIT_DEL_RULE", (unsigned long) AUDIT_DEL_RULE }, #endif // AUDIT_DEL_RULE #ifdef AUDIT_DEVMAJOR { "AUDIT_DEVMAJOR", (unsigned long) AUDIT_DEVMAJOR }, #endif // AUDIT_DEVMAJOR #ifdef AUDIT_DEVMINOR { "AUDIT_DEVMINOR", (unsigned long) AUDIT_DEVMINOR }, #endif // AUDIT_DEVMINOR #ifdef AUDIT_DIR { "AUDIT_DIR", (unsigned long) AUDIT_DIR }, #endif // AUDIT_DIR #ifdef AUDIT_DM_CTRL { "AUDIT_DM_CTRL", (unsigned long) AUDIT_DM_CTRL }, #endif // AUDIT_DM_CTRL #ifdef AUDIT_DM_EVENT { "AUDIT_DM_EVENT", (unsigned long) AUDIT_DM_EVENT }, #endif // AUDIT_DM_EVENT #ifdef AUDIT_EGID { "AUDIT_EGID", (unsigned long) AUDIT_EGID }, #endif // AUDIT_EGID #ifdef AUDIT_EOE { "AUDIT_EOE", (unsigned long) AUDIT_EOE }, #endif // AUDIT_EOE #ifdef AUDIT_EQUAL { "AUDIT_EQUAL", (unsigned long) AUDIT_EQUAL }, #endif // AUDIT_EQUAL #ifdef AUDIT_EUID { "AUDIT_EUID", (unsigned long) AUDIT_EUID }, #endif // AUDIT_EUID #ifdef AUDIT_EVENT_LISTENER { "AUDIT_EVENT_LISTENER", (unsigned long) AUDIT_EVENT_LISTENER }, #endif // AUDIT_EVENT_LISTENER #ifdef AUDIT_EXE { "AUDIT_EXE", (unsigned long) AUDIT_EXE }, #endif // AUDIT_EXE #ifdef AUDIT_EXECVE { "AUDIT_EXECVE", (unsigned long) AUDIT_EXECVE }, #endif // AUDIT_EXECVE #ifdef AUDIT_EXIT { "AUDIT_EXIT", (unsigned long) AUDIT_EXIT }, #endif // AUDIT_EXIT #ifdef AUDIT_FAIL_PANIC { "AUDIT_FAIL_PANIC", (unsigned long) AUDIT_FAIL_PANIC }, #endif // AUDIT_FAIL_PANIC #ifdef AUDIT_FAIL_PRINTK { "AUDIT_FAIL_PRINTK", (unsigned long) AUDIT_FAIL_PRINTK }, #endif // AUDIT_FAIL_PRINTK #ifdef AUDIT_FAIL_SILENT { "AUDIT_FAIL_SILENT", (unsigned long) AUDIT_FAIL_SILENT }, #endif // AUDIT_FAIL_SILENT #ifdef AUDIT_FANOTIFY { "AUDIT_FANOTIFY", (unsigned long) AUDIT_FANOTIFY }, #endif // AUDIT_FANOTIFY #ifdef AUDIT_FD_PAIR { "AUDIT_FD_PAIR", (unsigned long) AUDIT_FD_PAIR }, #endif // AUDIT_FD_PAIR #ifdef AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT { "AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT", (unsigned long) AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT }, #endif // AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT #ifdef AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME { "AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME", (unsigned long) AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME }, #endif // AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME #ifdef AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND { "AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND", (unsigned long) AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND }, #endif // AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND #ifdef AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH { "AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH", (unsigned long) AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH }, #endif // AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH #ifdef AUDIT_FEATURE_BITMAP_FILTER_FS { "AUDIT_FEATURE_BITMAP_FILTER_FS", (unsigned long) AUDIT_FEATURE_BITMAP_FILTER_FS }, #endif // AUDIT_FEATURE_BITMAP_FILTER_FS #ifdef AUDIT_FEATURE_BITMAP_LOST_RESET { "AUDIT_FEATURE_BITMAP_LOST_RESET", (unsigned long) AUDIT_FEATURE_BITMAP_LOST_RESET }, #endif // AUDIT_FEATURE_BITMAP_LOST_RESET #ifdef AUDIT_FEATURE_BITMAP_SESSIONID_FILTER { "AUDIT_FEATURE_BITMAP_SESSIONID_FILTER", (unsigned long) AUDIT_FEATURE_BITMAP_SESSIONID_FILTER }, #endif // AUDIT_FEATURE_BITMAP_SESSIONID_FILTER #ifdef AUDIT_FEATURE_CHANGE { "AUDIT_FEATURE_CHANGE", (unsigned long) AUDIT_FEATURE_CHANGE }, #endif // AUDIT_FEATURE_CHANGE #ifdef AUDIT_FEATURE_LOGINUID_IMMUTABLE { "AUDIT_FEATURE_LOGINUID_IMMUTABLE", (unsigned long) AUDIT_FEATURE_LOGINUID_IMMUTABLE }, #endif // AUDIT_FEATURE_LOGINUID_IMMUTABLE #ifdef AUDIT_FEATURE_ONLY_UNSET_LOGINUID { "AUDIT_FEATURE_ONLY_UNSET_LOGINUID", (unsigned long) AUDIT_FEATURE_ONLY_UNSET_LOGINUID }, #endif // AUDIT_FEATURE_ONLY_UNSET_LOGINUID #ifdef AUDIT_FEATURE_VERSION { "AUDIT_FEATURE_VERSION", (unsigned long) AUDIT_FEATURE_VERSION }, #endif // AUDIT_FEATURE_VERSION #ifdef AUDIT_FIELD_COMPARE { "AUDIT_FIELD_COMPARE", (unsigned long) AUDIT_FIELD_COMPARE }, #endif // AUDIT_FIELD_COMPARE #ifdef AUDIT_FILETYPE { "AUDIT_FILETYPE", (unsigned long) AUDIT_FILETYPE }, #endif // AUDIT_FILETYPE #ifdef AUDIT_FILTER_ENTRY { "AUDIT_FILTER_ENTRY", (unsigned long) AUDIT_FILTER_ENTRY }, #endif // AUDIT_FILTER_ENTRY #ifdef AUDIT_FILTER_EXCLUDE { "AUDIT_FILTER_EXCLUDE", (unsigned long) AUDIT_FILTER_EXCLUDE }, #endif // AUDIT_FILTER_EXCLUDE #ifdef AUDIT_FILTER_EXIT { "AUDIT_FILTER_EXIT", (unsigned long) AUDIT_FILTER_EXIT }, #endif // AUDIT_FILTER_EXIT #ifdef AUDIT_FILTER_FS { "AUDIT_FILTER_FS", (unsigned long) AUDIT_FILTER_FS }, #endif // AUDIT_FILTER_FS #ifdef AUDIT_FILTERKEY { "AUDIT_FILTERKEY", (unsigned long) AUDIT_FILTERKEY }, #endif // AUDIT_FILTERKEY #ifdef AUDIT_FILTER_PREPEND { "AUDIT_FILTER_PREPEND", (unsigned long) AUDIT_FILTER_PREPEND }, #endif // AUDIT_FILTER_PREPEND #ifdef AUDIT_FILTER_TASK { "AUDIT_FILTER_TASK", (unsigned long) AUDIT_FILTER_TASK }, #endif // AUDIT_FILTER_TASK #ifdef AUDIT_FILTER_TYPE { "AUDIT_FILTER_TYPE", (unsigned long) AUDIT_FILTER_TYPE }, #endif // AUDIT_FILTER_TYPE #ifdef AUDIT_FILTER_URING_EXIT { "AUDIT_FILTER_URING_EXIT", (unsigned long) AUDIT_FILTER_URING_EXIT }, #endif // AUDIT_FILTER_URING_EXIT #ifdef AUDIT_FILTER_USER { "AUDIT_FILTER_USER", (unsigned long) AUDIT_FILTER_USER }, #endif // AUDIT_FILTER_USER #ifdef AUDIT_FILTER_WATCH { "AUDIT_FILTER_WATCH", (unsigned long) AUDIT_FILTER_WATCH }, #endif // AUDIT_FILTER_WATCH #ifdef AUDIT_FIRST_KERN_ANOM_MSG { "AUDIT_FIRST_KERN_ANOM_MSG", (unsigned long) AUDIT_FIRST_KERN_ANOM_MSG }, #endif // AUDIT_FIRST_KERN_ANOM_MSG #ifdef AUDIT_FIRST_USER_MSG { "AUDIT_FIRST_USER_MSG", (unsigned long) AUDIT_FIRST_USER_MSG }, #endif // AUDIT_FIRST_USER_MSG #ifdef AUDIT_FIRST_USER_MSG2 { "AUDIT_FIRST_USER_MSG2", (unsigned long) AUDIT_FIRST_USER_MSG2 }, #endif // AUDIT_FIRST_USER_MSG2 #ifdef AUDIT_FSGID { "AUDIT_FSGID", (unsigned long) AUDIT_FSGID }, #endif // AUDIT_FSGID #ifdef AUDIT_FSTYPE { "AUDIT_FSTYPE", (unsigned long) AUDIT_FSTYPE }, #endif // AUDIT_FSTYPE #ifdef AUDIT_FSUID { "AUDIT_FSUID", (unsigned long) AUDIT_FSUID }, #endif // AUDIT_FSUID #ifdef AUDIT_GET { "AUDIT_GET", (unsigned long) AUDIT_GET }, #endif // AUDIT_GET #ifdef AUDIT_GET_FEATURE { "AUDIT_GET_FEATURE", (unsigned long) AUDIT_GET_FEATURE }, #endif // AUDIT_GET_FEATURE #ifdef AUDIT_GID { "AUDIT_GID", (unsigned long) AUDIT_GID }, #endif // AUDIT_GID #ifdef AUDIT_GREATER_THAN { "AUDIT_GREATER_THAN", (unsigned long) AUDIT_GREATER_THAN }, #endif // AUDIT_GREATER_THAN #ifdef AUDIT_INODE { "AUDIT_INODE", (unsigned long) AUDIT_INODE }, #endif // AUDIT_INODE #ifdef AUDIT_INTEGRITY_DATA { "AUDIT_INTEGRITY_DATA", (unsigned long) AUDIT_INTEGRITY_DATA }, #endif // AUDIT_INTEGRITY_DATA #ifdef AUDIT_INTEGRITY_EVM_XATTR { "AUDIT_INTEGRITY_EVM_XATTR", (unsigned long) AUDIT_INTEGRITY_EVM_XATTR }, #endif // AUDIT_INTEGRITY_EVM_XATTR #ifdef AUDIT_INTEGRITY_HASH { "AUDIT_INTEGRITY_HASH", (unsigned long) AUDIT_INTEGRITY_HASH }, #endif // AUDIT_INTEGRITY_HASH #ifdef AUDIT_INTEGRITY_METADATA { "AUDIT_INTEGRITY_METADATA", (unsigned long) AUDIT_INTEGRITY_METADATA }, #endif // AUDIT_INTEGRITY_METADATA #ifdef AUDIT_INTEGRITY_PCR { "AUDIT_INTEGRITY_PCR", (unsigned long) AUDIT_INTEGRITY_PCR }, #endif // AUDIT_INTEGRITY_PCR #ifdef AUDIT_INTEGRITY_POLICY_RULE { "AUDIT_INTEGRITY_POLICY_RULE", (unsigned long) AUDIT_INTEGRITY_POLICY_RULE }, #endif // AUDIT_INTEGRITY_POLICY_RULE #ifdef AUDIT_INTEGRITY_RULE { "AUDIT_INTEGRITY_RULE", (unsigned long) AUDIT_INTEGRITY_RULE }, #endif // AUDIT_INTEGRITY_RULE #ifdef AUDIT_INTEGRITY_STATUS { "AUDIT_INTEGRITY_STATUS", (unsigned long) AUDIT_INTEGRITY_STATUS }, #endif // AUDIT_INTEGRITY_STATUS #ifdef AUDIT_INTEGRITY_USERSPACE { "AUDIT_INTEGRITY_USERSPACE", (unsigned long) AUDIT_INTEGRITY_USERSPACE }, #endif // AUDIT_INTEGRITY_USERSPACE #ifdef AUDIT_IPC { "AUDIT_IPC", (unsigned long) AUDIT_IPC }, #endif // AUDIT_IPC #ifdef AUDIT_IPC_SET_PERM { "AUDIT_IPC_SET_PERM", (unsigned long) AUDIT_IPC_SET_PERM }, #endif // AUDIT_IPC_SET_PERM #ifdef AUDIT_IPE_ACCESS { "AUDIT_IPE_ACCESS", (unsigned long) AUDIT_IPE_ACCESS }, #endif // AUDIT_IPE_ACCESS #ifdef AUDIT_IPE_CONFIG_CHANGE { "AUDIT_IPE_CONFIG_CHANGE", (unsigned long) AUDIT_IPE_CONFIG_CHANGE }, #endif // AUDIT_IPE_CONFIG_CHANGE #ifdef AUDIT_IPE_POLICY_LOAD { "AUDIT_IPE_POLICY_LOAD", (unsigned long) AUDIT_IPE_POLICY_LOAD }, #endif // AUDIT_IPE_POLICY_LOAD #ifdef AUDIT_KERNEL { "AUDIT_KERNEL", (unsigned long) AUDIT_KERNEL }, #endif // AUDIT_KERNEL #ifdef AUDIT_KERNEL_OTHER { "AUDIT_KERNEL_OTHER", (unsigned long) AUDIT_KERNEL_OTHER }, #endif // AUDIT_KERNEL_OTHER #ifdef AUDIT_KERN_MODULE { "AUDIT_KERN_MODULE", (unsigned long) AUDIT_KERN_MODULE }, #endif // AUDIT_KERN_MODULE #ifdef AUDIT_LANDLOCK_ACCESS { "AUDIT_LANDLOCK_ACCESS", (unsigned long) AUDIT_LANDLOCK_ACCESS }, #endif // AUDIT_LANDLOCK_ACCESS #ifdef AUDIT_LANDLOCK_DOMAIN { "AUDIT_LANDLOCK_DOMAIN", (unsigned long) AUDIT_LANDLOCK_DOMAIN }, #endif // AUDIT_LANDLOCK_DOMAIN #ifdef AUDIT_LAST_FEATURE { "AUDIT_LAST_FEATURE", (unsigned long) AUDIT_LAST_FEATURE }, #endif // AUDIT_LAST_FEATURE #ifdef AUDIT_LAST_KERN_ANOM_MSG { "AUDIT_LAST_KERN_ANOM_MSG", (unsigned long) AUDIT_LAST_KERN_ANOM_MSG }, #endif // AUDIT_LAST_KERN_ANOM_MSG #ifdef AUDIT_LAST_USER_MSG { "AUDIT_LAST_USER_MSG", (unsigned long) AUDIT_LAST_USER_MSG }, #endif // AUDIT_LAST_USER_MSG #ifdef AUDIT_LAST_USER_MSG2 { "AUDIT_LAST_USER_MSG2", (unsigned long) AUDIT_LAST_USER_MSG2 }, #endif // AUDIT_LAST_USER_MSG2 #ifdef AUDIT_LESS_THAN { "AUDIT_LESS_THAN", (unsigned long) AUDIT_LESS_THAN }, #endif // AUDIT_LESS_THAN #ifdef AUDIT_LIST { "AUDIT_LIST", (unsigned long) AUDIT_LIST }, #endif // AUDIT_LIST #ifdef AUDIT_LIST_RULES { "AUDIT_LIST_RULES", (unsigned long) AUDIT_LIST_RULES }, #endif // AUDIT_LIST_RULES #ifdef AUDIT_LOGIN { "AUDIT_LOGIN", (unsigned long) AUDIT_LOGIN }, #endif // AUDIT_LOGIN #ifdef AUDIT_LOGINUID { "AUDIT_LOGINUID", (unsigned long) AUDIT_LOGINUID }, #endif // AUDIT_LOGINUID #ifdef AUDIT_LOGINUID_SET { "AUDIT_LOGINUID_SET", (unsigned long) AUDIT_LOGINUID_SET }, #endif // AUDIT_LOGINUID_SET #ifdef AUDIT_MAC_CALIPSO_ADD { "AUDIT_MAC_CALIPSO_ADD", (unsigned long) AUDIT_MAC_CALIPSO_ADD }, #endif // AUDIT_MAC_CALIPSO_ADD #ifdef AUDIT_MAC_CALIPSO_DEL { "AUDIT_MAC_CALIPSO_DEL", (unsigned long) AUDIT_MAC_CALIPSO_DEL }, #endif // AUDIT_MAC_CALIPSO_DEL #ifdef AUDIT_MAC_CIPSOV4_ADD { "AUDIT_MAC_CIPSOV4_ADD", (unsigned long) AUDIT_MAC_CIPSOV4_ADD }, #endif // AUDIT_MAC_CIPSOV4_ADD #ifdef AUDIT_MAC_CIPSOV4_DEL { "AUDIT_MAC_CIPSOV4_DEL", (unsigned long) AUDIT_MAC_CIPSOV4_DEL }, #endif // AUDIT_MAC_CIPSOV4_DEL #ifdef AUDIT_MAC_CONFIG_CHANGE { "AUDIT_MAC_CONFIG_CHANGE", (unsigned long) AUDIT_MAC_CONFIG_CHANGE }, #endif // AUDIT_MAC_CONFIG_CHANGE #ifdef AUDIT_MAC_IPSEC_ADDSA { "AUDIT_MAC_IPSEC_ADDSA", (unsigned long) AUDIT_MAC_IPSEC_ADDSA }, #endif // AUDIT_MAC_IPSEC_ADDSA #ifdef AUDIT_MAC_IPSEC_ADDSPD { "AUDIT_MAC_IPSEC_ADDSPD", (unsigned long) AUDIT_MAC_IPSEC_ADDSPD }, #endif // AUDIT_MAC_IPSEC_ADDSPD #ifdef AUDIT_MAC_IPSEC_DELSA { "AUDIT_MAC_IPSEC_DELSA", (unsigned long) AUDIT_MAC_IPSEC_DELSA }, #endif // AUDIT_MAC_IPSEC_DELSA #ifdef AUDIT_MAC_IPSEC_DELSPD { "AUDIT_MAC_IPSEC_DELSPD", (unsigned long) AUDIT_MAC_IPSEC_DELSPD }, #endif // AUDIT_MAC_IPSEC_DELSPD #ifdef AUDIT_MAC_IPSEC_EVENT { "AUDIT_MAC_IPSEC_EVENT", (unsigned long) AUDIT_MAC_IPSEC_EVENT }, #endif // AUDIT_MAC_IPSEC_EVENT #ifdef AUDIT_MAC_MAP_ADD { "AUDIT_MAC_MAP_ADD", (unsigned long) AUDIT_MAC_MAP_ADD }, #endif // AUDIT_MAC_MAP_ADD #ifdef AUDIT_MAC_MAP_DEL { "AUDIT_MAC_MAP_DEL", (unsigned long) AUDIT_MAC_MAP_DEL }, #endif // AUDIT_MAC_MAP_DEL #ifdef AUDIT_MAC_OBJ_CONTEXTS { "AUDIT_MAC_OBJ_CONTEXTS", (unsigned long) AUDIT_MAC_OBJ_CONTEXTS }, #endif // AUDIT_MAC_OBJ_CONTEXTS #ifdef AUDIT_MAC_POLICY_LOAD { "AUDIT_MAC_POLICY_LOAD", (unsigned long) AUDIT_MAC_POLICY_LOAD }, #endif // AUDIT_MAC_POLICY_LOAD #ifdef AUDIT_MAC_STATUS { "AUDIT_MAC_STATUS", (unsigned long) AUDIT_MAC_STATUS }, #endif // AUDIT_MAC_STATUS #ifdef AUDIT_MAC_TASK_CONTEXTS { "AUDIT_MAC_TASK_CONTEXTS", (unsigned long) AUDIT_MAC_TASK_CONTEXTS }, #endif // AUDIT_MAC_TASK_CONTEXTS #ifdef AUDIT_MAC_UNLBL_ALLOW { "AUDIT_MAC_UNLBL_ALLOW", (unsigned long) AUDIT_MAC_UNLBL_ALLOW }, #endif // AUDIT_MAC_UNLBL_ALLOW #ifdef AUDIT_MAC_UNLBL_STCADD { "AUDIT_MAC_UNLBL_STCADD", (unsigned long) AUDIT_MAC_UNLBL_STCADD }, #endif // AUDIT_MAC_UNLBL_STCADD #ifdef AUDIT_MAC_UNLBL_STCDEL { "AUDIT_MAC_UNLBL_STCDEL", (unsigned long) AUDIT_MAC_UNLBL_STCDEL }, #endif // AUDIT_MAC_UNLBL_STCDEL #ifdef AUDIT_MAKE_EQUIV { "AUDIT_MAKE_EQUIV", (unsigned long) AUDIT_MAKE_EQUIV }, #endif // AUDIT_MAKE_EQUIV #ifdef AUDIT_MAX_FIELD_COMPARE { "AUDIT_MAX_FIELD_COMPARE", (unsigned long) AUDIT_MAX_FIELD_COMPARE }, #endif // AUDIT_MAX_FIELD_COMPARE #ifdef AUDIT_MAX_FIELDS { "AUDIT_MAX_FIELDS", (unsigned long) AUDIT_MAX_FIELDS }, #endif // AUDIT_MAX_FIELDS #ifdef AUDIT_MAX_KEY_LEN { "AUDIT_MAX_KEY_LEN", (unsigned long) AUDIT_MAX_KEY_LEN }, #endif // AUDIT_MAX_KEY_LEN #ifdef AUDIT_MESSAGE_TEXT_MAX { "AUDIT_MESSAGE_TEXT_MAX", (unsigned long) AUDIT_MESSAGE_TEXT_MAX }, #endif // AUDIT_MESSAGE_TEXT_MAX #ifdef AUDIT_MMAP { "AUDIT_MMAP", (unsigned long) AUDIT_MMAP }, #endif // AUDIT_MMAP #ifdef AUDIT_MQ_GETSETATTR { "AUDIT_MQ_GETSETATTR", (unsigned long) AUDIT_MQ_GETSETATTR }, #endif // AUDIT_MQ_GETSETATTR #ifdef AUDIT_MQ_NOTIFY { "AUDIT_MQ_NOTIFY", (unsigned long) AUDIT_MQ_NOTIFY }, #endif // AUDIT_MQ_NOTIFY #ifdef AUDIT_MQ_OPEN { "AUDIT_MQ_OPEN", (unsigned long) AUDIT_MQ_OPEN }, #endif // AUDIT_MQ_OPEN #ifdef AUDIT_MQ_SENDRECV { "AUDIT_MQ_SENDRECV", (unsigned long) AUDIT_MQ_SENDRECV }, #endif // AUDIT_MQ_SENDRECV #ifdef AUDIT_MSGTYPE { "AUDIT_MSGTYPE", (unsigned long) AUDIT_MSGTYPE }, #endif // AUDIT_MSGTYPE #ifdef AUDIT_NEGATE { "AUDIT_NEGATE", (unsigned long) AUDIT_NEGATE }, #endif // AUDIT_NEGATE #ifdef AUDIT_NETFILTER_CFG { "AUDIT_NETFILTER_CFG", (unsigned long) AUDIT_NETFILTER_CFG }, #endif // AUDIT_NETFILTER_CFG #ifdef AUDIT_NETFILTER_PKT { "AUDIT_NETFILTER_PKT", (unsigned long) AUDIT_NETFILTER_PKT }, #endif // AUDIT_NETFILTER_PKT #ifdef AUDIT_NEVER { "AUDIT_NEVER", (unsigned long) AUDIT_NEVER }, #endif // AUDIT_NEVER #ifdef AUDIT_NOT_EQUAL { "AUDIT_NOT_EQUAL", (unsigned long) AUDIT_NOT_EQUAL }, #endif // AUDIT_NOT_EQUAL #ifdef AUDIT_NR_FILTERS { "AUDIT_NR_FILTERS", (unsigned long) AUDIT_NR_FILTERS }, #endif // AUDIT_NR_FILTERS #ifdef AUDIT_OBJ_GID { "AUDIT_OBJ_GID", (unsigned long) AUDIT_OBJ_GID }, #endif // AUDIT_OBJ_GID #ifdef AUDIT_OBJ_LEV_HIGH { "AUDIT_OBJ_LEV_HIGH", (unsigned long) AUDIT_OBJ_LEV_HIGH }, #endif // AUDIT_OBJ_LEV_HIGH #ifdef AUDIT_OBJ_LEV_LOW { "AUDIT_OBJ_LEV_LOW", (unsigned long) AUDIT_OBJ_LEV_LOW }, #endif // AUDIT_OBJ_LEV_LOW #ifdef AUDIT_OBJ_PID { "AUDIT_OBJ_PID", (unsigned long) AUDIT_OBJ_PID }, #endif // AUDIT_OBJ_PID #ifdef AUDIT_OBJ_ROLE { "AUDIT_OBJ_ROLE", (unsigned long) AUDIT_OBJ_ROLE }, #endif // AUDIT_OBJ_ROLE #ifdef AUDIT_OBJ_TYPE { "AUDIT_OBJ_TYPE", (unsigned long) AUDIT_OBJ_TYPE }, #endif // AUDIT_OBJ_TYPE #ifdef AUDIT_OBJ_UID { "AUDIT_OBJ_UID", (unsigned long) AUDIT_OBJ_UID }, #endif // AUDIT_OBJ_UID #ifdef AUDIT_OBJ_USER { "AUDIT_OBJ_USER", (unsigned long) AUDIT_OBJ_USER }, #endif // AUDIT_OBJ_USER #ifdef AUDIT_OPENAT2 { "AUDIT_OPENAT2", (unsigned long) AUDIT_OPENAT2 }, #endif // AUDIT_OPENAT2 #ifdef AUDIT_PATH { "AUDIT_PATH", (unsigned long) AUDIT_PATH }, #endif // AUDIT_PATH #ifdef AUDIT_PERM { "AUDIT_PERM", (unsigned long) AUDIT_PERM }, #endif // AUDIT_PERM #ifdef AUDIT_PERM_ATTR { "AUDIT_PERM_ATTR", (unsigned long) AUDIT_PERM_ATTR }, #endif // AUDIT_PERM_ATTR #ifdef AUDIT_PERM_EXEC { "AUDIT_PERM_EXEC", (unsigned long) AUDIT_PERM_EXEC }, #endif // AUDIT_PERM_EXEC #ifdef AUDIT_PERM_READ { "AUDIT_PERM_READ", (unsigned long) AUDIT_PERM_READ }, #endif // AUDIT_PERM_READ #ifdef AUDIT_PERM_WRITE { "AUDIT_PERM_WRITE", (unsigned long) AUDIT_PERM_WRITE }, #endif // AUDIT_PERM_WRITE #ifdef AUDIT_PERS { "AUDIT_PERS", (unsigned long) AUDIT_PERS }, #endif // AUDIT_PERS #ifdef AUDIT_PID { "AUDIT_PID", (unsigned long) AUDIT_PID }, #endif // AUDIT_PID #ifdef AUDIT_POSSIBLE { "AUDIT_POSSIBLE", (unsigned long) AUDIT_POSSIBLE }, #endif // AUDIT_POSSIBLE #ifdef AUDIT_PPID { "AUDIT_PPID", (unsigned long) AUDIT_PPID }, #endif // AUDIT_PPID #ifdef AUDIT_PROCTITLE { "AUDIT_PROCTITLE", (unsigned long) AUDIT_PROCTITLE }, #endif // AUDIT_PROCTITLE #ifdef AUDIT_REPLACE { "AUDIT_REPLACE", (unsigned long) AUDIT_REPLACE }, #endif // AUDIT_REPLACE #ifdef AUDIT_SADDR_FAM { "AUDIT_SADDR_FAM", (unsigned long) AUDIT_SADDR_FAM }, #endif // AUDIT_SADDR_FAM #ifdef AUDIT_SECCOMP { "AUDIT_SECCOMP", (unsigned long) AUDIT_SECCOMP }, #endif // AUDIT_SECCOMP #ifdef AUDIT_SELINUX_ERR { "AUDIT_SELINUX_ERR", (unsigned long) AUDIT_SELINUX_ERR }, #endif // AUDIT_SELINUX_ERR #ifdef AUDIT_SESSIONID { "AUDIT_SESSIONID", (unsigned long) AUDIT_SESSIONID }, #endif // AUDIT_SESSIONID #ifdef AUDIT_SET { "AUDIT_SET", (unsigned long) AUDIT_SET }, #endif // AUDIT_SET #ifdef AUDIT_SET_FEATURE { "AUDIT_SET_FEATURE", (unsigned long) AUDIT_SET_FEATURE }, #endif // AUDIT_SET_FEATURE #ifdef AUDIT_SGID { "AUDIT_SGID", (unsigned long) AUDIT_SGID }, #endif // AUDIT_SGID #ifdef AUDIT_SIGNAL_INFO { "AUDIT_SIGNAL_INFO", (unsigned long) AUDIT_SIGNAL_INFO }, #endif // AUDIT_SIGNAL_INFO #ifdef AUDIT_SOCKADDR { "AUDIT_SOCKADDR", (unsigned long) AUDIT_SOCKADDR }, #endif // AUDIT_SOCKADDR #ifdef AUDIT_SOCKETCALL { "AUDIT_SOCKETCALL", (unsigned long) AUDIT_SOCKETCALL }, #endif // AUDIT_SOCKETCALL #ifdef AUDIT_STATUS_BACKLOG_LIMIT { "AUDIT_STATUS_BACKLOG_LIMIT", (unsigned long) AUDIT_STATUS_BACKLOG_LIMIT }, #endif // AUDIT_STATUS_BACKLOG_LIMIT #ifdef AUDIT_STATUS_BACKLOG_WAIT_TIME { "AUDIT_STATUS_BACKLOG_WAIT_TIME", (unsigned long) AUDIT_STATUS_BACKLOG_WAIT_TIME }, #endif // AUDIT_STATUS_BACKLOG_WAIT_TIME #ifdef AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL { "AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL", (unsigned long) AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL }, #endif // AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL #ifdef AUDIT_STATUS_ENABLED { "AUDIT_STATUS_ENABLED", (unsigned long) AUDIT_STATUS_ENABLED }, #endif // AUDIT_STATUS_ENABLED #ifdef AUDIT_STATUS_FAILURE { "AUDIT_STATUS_FAILURE", (unsigned long) AUDIT_STATUS_FAILURE }, #endif // AUDIT_STATUS_FAILURE #ifdef AUDIT_STATUS_LOST { "AUDIT_STATUS_LOST", (unsigned long) AUDIT_STATUS_LOST }, #endif // AUDIT_STATUS_LOST #ifdef AUDIT_STATUS_PID { "AUDIT_STATUS_PID", (unsigned long) AUDIT_STATUS_PID }, #endif // AUDIT_STATUS_PID #ifdef AUDIT_STATUS_RATE_LIMIT { "AUDIT_STATUS_RATE_LIMIT", (unsigned long) AUDIT_STATUS_RATE_LIMIT }, #endif // AUDIT_STATUS_RATE_LIMIT #ifdef AUDIT_SUBJ_CLR { "AUDIT_SUBJ_CLR", (unsigned long) AUDIT_SUBJ_CLR }, #endif // AUDIT_SUBJ_CLR #ifdef AUDIT_SUBJ_ROLE { "AUDIT_SUBJ_ROLE", (unsigned long) AUDIT_SUBJ_ROLE }, #endif // AUDIT_SUBJ_ROLE #ifdef AUDIT_SUBJ_SEN { "AUDIT_SUBJ_SEN", (unsigned long) AUDIT_SUBJ_SEN }, #endif // AUDIT_SUBJ_SEN #ifdef AUDIT_SUBJ_TYPE { "AUDIT_SUBJ_TYPE", (unsigned long) AUDIT_SUBJ_TYPE }, #endif // AUDIT_SUBJ_TYPE #ifdef AUDIT_SUBJ_USER { "AUDIT_SUBJ_USER", (unsigned long) AUDIT_SUBJ_USER }, #endif // AUDIT_SUBJ_USER #ifdef AUDIT_SUCCESS { "AUDIT_SUCCESS", (unsigned long) AUDIT_SUCCESS }, #endif // AUDIT_SUCCESS #ifdef AUDIT_SUID { "AUDIT_SUID", (unsigned long) AUDIT_SUID }, #endif // AUDIT_SUID #ifdef AUDIT_SYSCALL { "AUDIT_SYSCALL", (unsigned long) AUDIT_SYSCALL }, #endif // AUDIT_SYSCALL #ifdef AUDIT_SYSCALL_CLASSES { "AUDIT_SYSCALL_CLASSES", (unsigned long) AUDIT_SYSCALL_CLASSES }, #endif // AUDIT_SYSCALL_CLASSES #ifdef AUDIT_TIME_ADJNTPVAL { "AUDIT_TIME_ADJNTPVAL", (unsigned long) AUDIT_TIME_ADJNTPVAL }, #endif // AUDIT_TIME_ADJNTPVAL #ifdef AUDIT_TIME_INJOFFSET { "AUDIT_TIME_INJOFFSET", (unsigned long) AUDIT_TIME_INJOFFSET }, #endif // AUDIT_TIME_INJOFFSET #ifdef AUDIT_TRIM { "AUDIT_TRIM", (unsigned long) AUDIT_TRIM }, #endif // AUDIT_TRIM #ifdef AUDIT_TTY { "AUDIT_TTY", (unsigned long) AUDIT_TTY }, #endif // AUDIT_TTY #ifdef AUDIT_TTY_GET { "AUDIT_TTY_GET", (unsigned long) AUDIT_TTY_GET }, #endif // AUDIT_TTY_GET #ifdef AUDIT_TTY_SET { "AUDIT_TTY_SET", (unsigned long) AUDIT_TTY_SET }, #endif // AUDIT_TTY_SET #ifdef AUDIT_UID { "AUDIT_UID", (unsigned long) AUDIT_UID }, #endif // AUDIT_UID #ifdef AUDIT_UNUSED_BITS { "AUDIT_UNUSED_BITS", (unsigned long) AUDIT_UNUSED_BITS }, #endif // AUDIT_UNUSED_BITS #ifdef AUDIT_URINGOP { "AUDIT_URINGOP", (unsigned long) AUDIT_URINGOP }, #endif // AUDIT_URINGOP #ifdef AUDIT_USER { "AUDIT_USER", (unsigned long) AUDIT_USER }, #endif // AUDIT_USER #ifdef AUDIT_USER_AVC { "AUDIT_USER_AVC", (unsigned long) AUDIT_USER_AVC }, #endif // AUDIT_USER_AVC #ifdef AUDIT_USER_TTY { "AUDIT_USER_TTY", (unsigned long) AUDIT_USER_TTY }, #endif // AUDIT_USER_TTY #ifdef AUDIT_VERSION_BACKLOG_LIMIT { "AUDIT_VERSION_BACKLOG_LIMIT", (unsigned long) AUDIT_VERSION_BACKLOG_LIMIT }, #endif // AUDIT_VERSION_BACKLOG_LIMIT #ifdef AUDIT_VERSION_BACKLOG_WAIT_TIME { "AUDIT_VERSION_BACKLOG_WAIT_TIME", (unsigned long) AUDIT_VERSION_BACKLOG_WAIT_TIME }, #endif // AUDIT_VERSION_BACKLOG_WAIT_TIME #ifdef AUDIT_VERSION_LATEST { "AUDIT_VERSION_LATEST", (unsigned long) AUDIT_VERSION_LATEST }, #endif // AUDIT_VERSION_LATEST #ifdef AUDIT_WATCH { "AUDIT_WATCH", (unsigned long) AUDIT_WATCH }, #endif // AUDIT_WATCH #ifdef AUDIT_WATCH_INS { "AUDIT_WATCH_INS", (unsigned long) AUDIT_WATCH_INS }, #endif // AUDIT_WATCH_INS #ifdef AUDIT_WATCH_LIST { "AUDIT_WATCH_LIST", (unsigned long) AUDIT_WATCH_LIST }, #endif // AUDIT_WATCH_LIST #ifdef AUDIT_WATCH_REM { "AUDIT_WATCH_REM", (unsigned long) AUDIT_WATCH_REM }, #endif // AUDIT_WATCH_REM #ifdef B0 { "B0", (unsigned long) B0 }, #endif // B0 #ifdef B1000000 { "B1000000", (unsigned long) B1000000 }, #endif // B1000000 #ifdef B110 { "B110", (unsigned long) B110 }, #endif // B110 #ifdef B1152000 { "B1152000", (unsigned long) B1152000 }, #endif // B1152000 #ifdef B115200 { "B115200", (unsigned long) B115200 }, #endif // B115200 #ifdef B1200 { "B1200", (unsigned long) B1200 }, #endif // B1200 #ifdef B134 { "B134", (unsigned long) B134 }, #endif // B134 #ifdef B1500000 { "B1500000", (unsigned long) B1500000 }, #endif // B1500000 #ifdef B150 { "B150", (unsigned long) B150 }, #endif // B150 #ifdef B1800 { "B1800", (unsigned long) B1800 }, #endif // B1800 #ifdef B19200 { "B19200", (unsigned long) B19200 }, #endif // B19200 #ifdef B2000000 { "B2000000", (unsigned long) B2000000 }, #endif // B2000000 #ifdef B200 { "B200", (unsigned long) B200 }, #endif // B200 #ifdef B230400 { "B230400", (unsigned long) B230400 }, #endif // B230400 #ifdef B2400 { "B2400", (unsigned long) B2400 }, #endif // B2400 #ifdef B2500000 { "B2500000", (unsigned long) B2500000 }, #endif // B2500000 #ifdef B3000000 { "B3000000", (unsigned long) B3000000 }, #endif // B3000000 #ifdef B300 { "B300", (unsigned long) B300 }, #endif // B300 #ifdef B3500000 { "B3500000", (unsigned long) B3500000 }, #endif // B3500000 #ifdef B38400 { "B38400", (unsigned long) B38400 }, #endif // B38400 #ifdef B4000000 { "B4000000", (unsigned long) B4000000 }, #endif // B4000000 #ifdef B460800 { "B460800", (unsigned long) B460800 }, #endif // B460800 #ifdef B4800 { "B4800", (unsigned long) B4800 }, #endif // B4800 #ifdef B500000 { "B500000", (unsigned long) B500000 }, #endif // B500000 #ifdef B50 { "B50", (unsigned long) B50 }, #endif // B50 #ifdef B576000 { "B576000", (unsigned long) B576000 }, #endif // B576000 #ifdef B57600 { "B57600", (unsigned long) B57600 }, #endif // B57600 #ifdef B600 { "B600", (unsigned long) B600 }, #endif // B600 #ifdef B75 { "B75", (unsigned long) B75 }, #endif // B75 #ifdef B921600 { "B921600", (unsigned long) B921600 }, #endif // B921600 #ifdef B9600 { "B9600", (unsigned long) B9600 }, #endif // B9600 #ifdef BIG_ENDIAN { "BIG_ENDIAN", (unsigned long) BIG_ENDIAN }, #endif // BIG_ENDIAN #ifdef BLKALIGNOFF { "BLKALIGNOFF", (unsigned long) BLKALIGNOFF }, #endif // BLKALIGNOFF #ifdef BLKBSZGET { "BLKBSZGET", (unsigned long) BLKBSZGET }, #endif // BLKBSZGET #ifdef BLKBSZSET { "BLKBSZSET", (unsigned long) BLKBSZSET }, #endif // BLKBSZSET #ifdef BLKDISCARD { "BLKDISCARD", (unsigned long) BLKDISCARD }, #endif // BLKDISCARD #ifdef BLKDISCARDZEROES { "BLKDISCARDZEROES", (unsigned long) BLKDISCARDZEROES }, #endif // BLKDISCARDZEROES #ifdef BLKFLSBUF { "BLKFLSBUF", (unsigned long) BLKFLSBUF }, #endif // BLKFLSBUF #ifdef BLKFRAGET { "BLKFRAGET", (unsigned long) BLKFRAGET }, #endif // BLKFRAGET #ifdef BLKFRASET { "BLKFRASET", (unsigned long) BLKFRASET }, #endif // BLKFRASET #ifdef BLKGETDISKSEQ { "BLKGETDISKSEQ", (unsigned long) BLKGETDISKSEQ }, #endif // BLKGETDISKSEQ #ifdef BLKGETSIZE64 { "BLKGETSIZE64", (unsigned long) BLKGETSIZE64 }, #endif // BLKGETSIZE64 #ifdef BLKGETSIZE { "BLKGETSIZE", (unsigned long) BLKGETSIZE }, #endif // BLKGETSIZE #ifdef BLKIOMIN { "BLKIOMIN", (unsigned long) BLKIOMIN }, #endif // BLKIOMIN #ifdef BLKIOOPT { "BLKIOOPT", (unsigned long) BLKIOOPT }, #endif // BLKIOOPT #ifdef BLKPBSZGET { "BLKPBSZGET", (unsigned long) BLKPBSZGET }, #endif // BLKPBSZGET #ifdef BLKRAGET { "BLKRAGET", (unsigned long) BLKRAGET }, #endif // BLKRAGET #ifdef BLKRASET { "BLKRASET", (unsigned long) BLKRASET }, #endif // BLKRASET #ifdef BLKROGET { "BLKROGET", (unsigned long) BLKROGET }, #endif // BLKROGET #ifdef BLKROSET { "BLKROSET", (unsigned long) BLKROSET }, #endif // BLKROSET #ifdef BLKROTATIONAL { "BLKROTATIONAL", (unsigned long) BLKROTATIONAL }, #endif // BLKROTATIONAL #ifdef BLKRRPART { "BLKRRPART", (unsigned long) BLKRRPART }, #endif // BLKRRPART #ifdef BLKSECDISCARD { "BLKSECDISCARD", (unsigned long) BLKSECDISCARD }, #endif // BLKSECDISCARD #ifdef BLKSECTGET { "BLKSECTGET", (unsigned long) BLKSECTGET }, #endif // BLKSECTGET #ifdef BLKSECTSET { "BLKSECTSET", (unsigned long) BLKSECTSET }, #endif // BLKSECTSET #ifdef BLKSSZGET { "BLKSSZGET", (unsigned long) BLKSSZGET }, #endif // BLKSSZGET #ifdef BLKTRACESETUP { "BLKTRACESETUP", (unsigned long) BLKTRACESETUP }, #endif // BLKTRACESETUP #ifdef BLKTRACESTART { "BLKTRACESTART", (unsigned long) BLKTRACESTART }, #endif // BLKTRACESTART #ifdef BLKTRACESTOP { "BLKTRACESTOP", (unsigned long) BLKTRACESTOP }, #endif // BLKTRACESTOP #ifdef BLKTRACETEARDOWN { "BLKTRACETEARDOWN", (unsigned long) BLKTRACETEARDOWN }, #endif // BLKTRACETEARDOWN #ifdef BLKZEROOUT { "BLKZEROOUT", (unsigned long) BLKZEROOUT }, #endif // BLKZEROOUT #ifdef BLOCK_SIZE_BITS { "BLOCK_SIZE_BITS", (unsigned long) BLOCK_SIZE_BITS }, #endif // BLOCK_SIZE_BITS #ifdef BMAP_IOCTL { "BMAP_IOCTL", (unsigned long) BMAP_IOCTL }, #endif // BMAP_IOCTL #ifdef BOTHER { "BOTHER", (unsigned long) BOTHER }, #endif // BOTHER #ifdef BRKINT { "BRKINT", (unsigned long) BRKINT }, #endif // BRKINT #ifdef BS0 { "BS0", (unsigned long) BS0 }, #endif // BS0 #ifdef BS1 { "BS1", (unsigned long) BS1 }, #endif // BS1 #ifdef BSDLY { "BSDLY", (unsigned long) BSDLY }, #endif // BSDLY #ifdef BUS_ADRALN { "BUS_ADRALN", (unsigned long) BUS_ADRALN }, #endif // BUS_ADRALN #ifdef BUS_ADRERR { "BUS_ADRERR", (unsigned long) BUS_ADRERR }, #endif // BUS_ADRERR #ifdef BUS_MCEERR_AO { "BUS_MCEERR_AO", (unsigned long) BUS_MCEERR_AO }, #endif // BUS_MCEERR_AO #ifdef BUS_MCEERR_AR { "BUS_MCEERR_AR", (unsigned long) BUS_MCEERR_AR }, #endif // BUS_MCEERR_AR #ifdef BUS_OBJERR { "BUS_OBJERR", (unsigned long) BUS_OBJERR }, #endif // BUS_OBJERR #ifdef BYTE_ORDER { "BYTE_ORDER", (unsigned long) BYTE_ORDER }, #endif // BYTE_ORDER #ifdef CBAUD { "CBAUD", (unsigned long) CBAUD }, #endif // CBAUD #ifdef CBAUDEX { "CBAUDEX", (unsigned long) CBAUDEX }, #endif // CBAUDEX #ifdef CIBAUD { "CIBAUD", (unsigned long) CIBAUD }, #endif // CIBAUD #ifdef CLD_CONTINUED { "CLD_CONTINUED", (unsigned long) CLD_CONTINUED }, #endif // CLD_CONTINUED #ifdef CLD_DUMPED { "CLD_DUMPED", (unsigned long) CLD_DUMPED }, #endif // CLD_DUMPED #ifdef CLD_EXITED { "CLD_EXITED", (unsigned long) CLD_EXITED }, #endif // CLD_EXITED #ifdef CLD_KILLED { "CLD_KILLED", (unsigned long) CLD_KILLED }, #endif // CLD_KILLED #ifdef CLD_STOPPED { "CLD_STOPPED", (unsigned long) CLD_STOPPED }, #endif // CLD_STOPPED #ifdef CLD_TRAPPED { "CLD_TRAPPED", (unsigned long) CLD_TRAPPED }, #endif // CLD_TRAPPED #ifdef CLOCAL { "CLOCAL", (unsigned long) CLOCAL }, #endif // CLOCAL #ifdef CLONE_ARGS_SIZE_VER0 { "CLONE_ARGS_SIZE_VER0", (unsigned long) CLONE_ARGS_SIZE_VER0 }, #endif // CLONE_ARGS_SIZE_VER0 #ifdef CLONE_ARGS_SIZE_VER1 { "CLONE_ARGS_SIZE_VER1", (unsigned long) CLONE_ARGS_SIZE_VER1 }, #endif // CLONE_ARGS_SIZE_VER1 #ifdef CLONE_ARGS_SIZE_VER2 { "CLONE_ARGS_SIZE_VER2", (unsigned long) CLONE_ARGS_SIZE_VER2 }, #endif // CLONE_ARGS_SIZE_VER2 #ifdef CLONE_CHILD_CLEARTID { "CLONE_CHILD_CLEARTID", (unsigned long) CLONE_CHILD_CLEARTID }, #endif // CLONE_CHILD_CLEARTID #ifdef CLONE_CHILD_SETTID { "CLONE_CHILD_SETTID", (unsigned long) CLONE_CHILD_SETTID }, #endif // CLONE_CHILD_SETTID #ifdef CLONE_CLEAR_SIGHAND { "CLONE_CLEAR_SIGHAND", (unsigned long) CLONE_CLEAR_SIGHAND }, #endif // CLONE_CLEAR_SIGHAND #ifdef CLONE_DETACHED { "CLONE_DETACHED", (unsigned long) CLONE_DETACHED }, #endif // CLONE_DETACHED #ifdef CLONE_FILES { "CLONE_FILES", (unsigned long) CLONE_FILES }, #endif // CLONE_FILES #ifdef CLONE_FS { "CLONE_FS", (unsigned long) CLONE_FS }, #endif // CLONE_FS #ifdef CLONE_INTO_CGROUP { "CLONE_INTO_CGROUP", (unsigned long) CLONE_INTO_CGROUP }, #endif // CLONE_INTO_CGROUP #ifdef CLONE_IO { "CLONE_IO", (unsigned long) CLONE_IO }, #endif // CLONE_IO #ifdef CLONE_NEWCGROUP { "CLONE_NEWCGROUP", (unsigned long) CLONE_NEWCGROUP }, #endif // CLONE_NEWCGROUP #ifdef CLONE_NEWIPC { "CLONE_NEWIPC", (unsigned long) CLONE_NEWIPC }, #endif // CLONE_NEWIPC #ifdef CLONE_NEWNET { "CLONE_NEWNET", (unsigned long) CLONE_NEWNET }, #endif // CLONE_NEWNET #ifdef CLONE_NEWNS { "CLONE_NEWNS", (unsigned long) CLONE_NEWNS }, #endif // CLONE_NEWNS #ifdef CLONE_NEWPID { "CLONE_NEWPID", (unsigned long) CLONE_NEWPID }, #endif // CLONE_NEWPID #ifdef CLONE_NEWTIME { "CLONE_NEWTIME", (unsigned long) CLONE_NEWTIME }, #endif // CLONE_NEWTIME #ifdef CLONE_NEWUSER { "CLONE_NEWUSER", (unsigned long) CLONE_NEWUSER }, #endif // CLONE_NEWUSER #ifdef CLONE_NEWUTS { "CLONE_NEWUTS", (unsigned long) CLONE_NEWUTS }, #endif // CLONE_NEWUTS #ifdef CLONE_PARENT { "CLONE_PARENT", (unsigned long) CLONE_PARENT }, #endif // CLONE_PARENT #ifdef CLONE_PARENT_SETTID { "CLONE_PARENT_SETTID", (unsigned long) CLONE_PARENT_SETTID }, #endif // CLONE_PARENT_SETTID #ifdef CLONE_PIDFD { "CLONE_PIDFD", (unsigned long) CLONE_PIDFD }, #endif // CLONE_PIDFD #ifdef CLONE_PTRACE { "CLONE_PTRACE", (unsigned long) CLONE_PTRACE }, #endif // CLONE_PTRACE #ifdef CLONE_SETTLS { "CLONE_SETTLS", (unsigned long) CLONE_SETTLS }, #endif // CLONE_SETTLS #ifdef CLONE_SIGHAND { "CLONE_SIGHAND", (unsigned long) CLONE_SIGHAND }, #endif // CLONE_SIGHAND #ifdef CLONE_SYSVSEM { "CLONE_SYSVSEM", (unsigned long) CLONE_SYSVSEM }, #endif // CLONE_SYSVSEM #ifdef CLONE_THREAD { "CLONE_THREAD", (unsigned long) CLONE_THREAD }, #endif // CLONE_THREAD #ifdef CLONE_UNTRACED { "CLONE_UNTRACED", (unsigned long) CLONE_UNTRACED }, #endif // CLONE_UNTRACED #ifdef CLONE_VFORK { "CLONE_VFORK", (unsigned long) CLONE_VFORK }, #endif // CLONE_VFORK #ifdef CLONE_VM { "CLONE_VM", (unsigned long) CLONE_VM }, #endif // CLONE_VM #ifdef CMSPAR { "CMSPAR", (unsigned long) CMSPAR }, #endif // CMSPAR #ifdef CR0 { "CR0", (unsigned long) CR0 }, #endif // CR0 #ifdef CR1 { "CR1", (unsigned long) CR1 }, #endif // CR1 #ifdef CR2 { "CR2", (unsigned long) CR2 }, #endif // CR2 #ifdef CR3 { "CR3", (unsigned long) CR3 }, #endif // CR3 #ifdef CRDLY { "CRDLY", (unsigned long) CRDLY }, #endif // CRDLY #ifdef CREAD { "CREAD", (unsigned long) CREAD }, #endif // CREAD #ifdef CRTSCTS { "CRTSCTS", (unsigned long) CRTSCTS }, #endif // CRTSCTS #ifdef CS5 { "CS5", (unsigned long) CS5 }, #endif // CS5 #ifdef CS6 { "CS6", (unsigned long) CS6 }, #endif // CS6 #ifdef CS7 { "CS7", (unsigned long) CS7 }, #endif // CS7 #ifdef CS8 { "CS8", (unsigned long) CS8 }, #endif // CS8 #ifdef CSIGNAL { "CSIGNAL", (unsigned long) CSIGNAL }, #endif // CSIGNAL #ifdef CSIZE { "CSIZE", (unsigned long) CSIZE }, #endif // CSIZE #ifdef CSTOPB { "CSTOPB", (unsigned long) CSTOPB }, #endif // CSTOPB #ifdef E2BIG { "E2BIG", (unsigned long) E2BIG }, #endif // E2BIG #ifdef EACCES { "EACCES", (unsigned long) EACCES }, #endif // EACCES #ifdef EADDRINUSE { "EADDRINUSE", (unsigned long) EADDRINUSE }, #endif // EADDRINUSE #ifdef EADDRNOTAVAIL { "EADDRNOTAVAIL", (unsigned long) EADDRNOTAVAIL }, #endif // EADDRNOTAVAIL #ifdef EADV { "EADV", (unsigned long) EADV }, #endif // EADV #ifdef EAFNOSUPPORT { "EAFNOSUPPORT", (unsigned long) EAFNOSUPPORT }, #endif // EAFNOSUPPORT #ifdef EAGAIN { "EAGAIN", (unsigned long) EAGAIN }, #endif // EAGAIN #ifdef EALREADY { "EALREADY", (unsigned long) EALREADY }, #endif // EALREADY #ifdef EBADE { "EBADE", (unsigned long) EBADE }, #endif // EBADE #ifdef EBADF { "EBADF", (unsigned long) EBADF }, #endif // EBADF #ifdef EBADFD { "EBADFD", (unsigned long) EBADFD }, #endif // EBADFD #ifdef EBADMSG { "EBADMSG", (unsigned long) EBADMSG }, #endif // EBADMSG #ifdef EBADR { "EBADR", (unsigned long) EBADR }, #endif // EBADR #ifdef EBADRQC { "EBADRQC", (unsigned long) EBADRQC }, #endif // EBADRQC #ifdef EBADSLT { "EBADSLT", (unsigned long) EBADSLT }, #endif // EBADSLT #ifdef EBFONT { "EBFONT", (unsigned long) EBFONT }, #endif // EBFONT #ifdef EBUSY { "EBUSY", (unsigned long) EBUSY }, #endif // EBUSY #ifdef ECANCELED { "ECANCELED", (unsigned long) ECANCELED }, #endif // ECANCELED #ifdef ECHILD { "ECHILD", (unsigned long) ECHILD }, #endif // ECHILD #ifdef ECHO { "ECHO", (unsigned long) ECHO }, #endif // ECHO #ifdef ECHOCTL { "ECHOCTL", (unsigned long) ECHOCTL }, #endif // ECHOCTL #ifdef ECHOE { "ECHOE", (unsigned long) ECHOE }, #endif // ECHOE #ifdef ECHOK { "ECHOK", (unsigned long) ECHOK }, #endif // ECHOK #ifdef ECHOKE { "ECHOKE", (unsigned long) ECHOKE }, #endif // ECHOKE #ifdef ECHONL { "ECHONL", (unsigned long) ECHONL }, #endif // ECHONL #ifdef ECHOPRT { "ECHOPRT", (unsigned long) ECHOPRT }, #endif // ECHOPRT #ifdef ECHRNG { "ECHRNG", (unsigned long) ECHRNG }, #endif // ECHRNG #ifdef ECOMM { "ECOMM", (unsigned long) ECOMM }, #endif // ECOMM #ifdef ECONNABORTED { "ECONNABORTED", (unsigned long) ECONNABORTED }, #endif // ECONNABORTED #ifdef ECONNREFUSED { "ECONNREFUSED", (unsigned long) ECONNREFUSED }, #endif // ECONNREFUSED #ifdef ECONNRESET { "ECONNRESET", (unsigned long) ECONNRESET }, #endif // ECONNRESET #ifdef EDEADLK { "EDEADLK", (unsigned long) EDEADLK }, #endif // EDEADLK #ifdef EDEADLOCK { "EDEADLOCK", (unsigned long) EDEADLOCK }, #endif // EDEADLOCK #ifdef EDESTADDRREQ { "EDESTADDRREQ", (unsigned long) EDESTADDRREQ }, #endif // EDESTADDRREQ #ifdef EDOM { "EDOM", (unsigned long) EDOM }, #endif // EDOM #ifdef EDOTDOT { "EDOTDOT", (unsigned long) EDOTDOT }, #endif // EDOTDOT #ifdef EDQUOT { "EDQUOT", (unsigned long) EDQUOT }, #endif // EDQUOT #ifdef EEXIST { "EEXIST", (unsigned long) EEXIST }, #endif // EEXIST #ifdef EFAULT { "EFAULT", (unsigned long) EFAULT }, #endif // EFAULT #ifdef EFBIG { "EFBIG", (unsigned long) EFBIG }, #endif // EFBIG #ifdef EHOSTDOWN { "EHOSTDOWN", (unsigned long) EHOSTDOWN }, #endif // EHOSTDOWN #ifdef EHOSTUNREACH { "EHOSTUNREACH", (unsigned long) EHOSTUNREACH }, #endif // EHOSTUNREACH #ifdef EHWPOISON { "EHWPOISON", (unsigned long) EHWPOISON }, #endif // EHWPOISON #ifdef EIDRM { "EIDRM", (unsigned long) EIDRM }, #endif // EIDRM #ifdef EILSEQ { "EILSEQ", (unsigned long) EILSEQ }, #endif // EILSEQ #ifdef EINPROGRESS { "EINPROGRESS", (unsigned long) EINPROGRESS }, #endif // EINPROGRESS #ifdef EINTR { "EINTR", (unsigned long) EINTR }, #endif // EINTR #ifdef EINVAL { "EINVAL", (unsigned long) EINVAL }, #endif // EINVAL #ifdef EIO { "EIO", (unsigned long) EIO }, #endif // EIO #ifdef EISCONN { "EISCONN", (unsigned long) EISCONN }, #endif // EISCONN #ifdef EISDIR { "EISDIR", (unsigned long) EISDIR }, #endif // EISDIR #ifdef EISNAM { "EISNAM", (unsigned long) EISNAM }, #endif // EISNAM #ifdef EKEYEXPIRED { "EKEYEXPIRED", (unsigned long) EKEYEXPIRED }, #endif // EKEYEXPIRED #ifdef EKEYREJECTED { "EKEYREJECTED", (unsigned long) EKEYREJECTED }, #endif // EKEYREJECTED #ifdef EKEYREVOKED { "EKEYREVOKED", (unsigned long) EKEYREVOKED }, #endif // EKEYREVOKED #ifdef EL2HLT { "EL2HLT", (unsigned long) EL2HLT }, #endif // EL2HLT #ifdef EL2NSYNC { "EL2NSYNC", (unsigned long) EL2NSYNC }, #endif // EL2NSYNC #ifdef EL3HLT { "EL3HLT", (unsigned long) EL3HLT }, #endif // EL3HLT #ifdef EL3RST { "EL3RST", (unsigned long) EL3RST }, #endif // EL3RST #ifdef ELIBACC { "ELIBACC", (unsigned long) ELIBACC }, #endif // ELIBACC #ifdef ELIBBAD { "ELIBBAD", (unsigned long) ELIBBAD }, #endif // ELIBBAD #ifdef ELIBEXEC { "ELIBEXEC", (unsigned long) ELIBEXEC }, #endif // ELIBEXEC #ifdef ELIBMAX { "ELIBMAX", (unsigned long) ELIBMAX }, #endif // ELIBMAX #ifdef ELIBSCN { "ELIBSCN", (unsigned long) ELIBSCN }, #endif // ELIBSCN #ifdef ELNRNG { "ELNRNG", (unsigned long) ELNRNG }, #endif // ELNRNG #ifdef ELOOP { "ELOOP", (unsigned long) ELOOP }, #endif // ELOOP #ifdef EM_386 { "EM_386", (unsigned long) EM_386 }, #endif // EM_386 #ifdef EM_486 { "EM_486", (unsigned long) EM_486 }, #endif // EM_486 #ifdef EM_68K { "EM_68K", (unsigned long) EM_68K }, #endif // EM_68K #ifdef EM_860 { "EM_860", (unsigned long) EM_860 }, #endif // EM_860 #ifdef EM_88K { "EM_88K", (unsigned long) EM_88K }, #endif // EM_88K #ifdef EM_AARCH64 { "EM_AARCH64", (unsigned long) EM_AARCH64 }, #endif // EM_AARCH64 #ifdef EM_ALPHA { "EM_ALPHA", (unsigned long) EM_ALPHA }, #endif // EM_ALPHA #ifdef EM_ALTERA_NIOS2 { "EM_ALTERA_NIOS2", (unsigned long) EM_ALTERA_NIOS2 }, #endif // EM_ALTERA_NIOS2 #ifdef EM_ARCOMPACT { "EM_ARCOMPACT", (unsigned long) EM_ARCOMPACT }, #endif // EM_ARCOMPACT #ifdef EM_ARCV2 { "EM_ARCV2", (unsigned long) EM_ARCV2 }, #endif // EM_ARCV2 #ifdef EM_ARM { "EM_ARM", (unsigned long) EM_ARM }, #endif // EM_ARM #ifdef EM_BLACKFIN { "EM_BLACKFIN", (unsigned long) EM_BLACKFIN }, #endif // EM_BLACKFIN #ifdef EM_BPF { "EM_BPF", (unsigned long) EM_BPF }, #endif // EM_BPF #ifdef EM_CRIS { "EM_CRIS", (unsigned long) EM_CRIS }, #endif // EM_CRIS #ifdef EM_CSKY { "EM_CSKY", (unsigned long) EM_CSKY }, #endif // EM_CSKY #ifdef EM_CYGNUS_M32R { "EM_CYGNUS_M32R", (unsigned long) EM_CYGNUS_M32R }, #endif // EM_CYGNUS_M32R #ifdef EM_CYGNUS_MN10300 { "EM_CYGNUS_MN10300", (unsigned long) EM_CYGNUS_MN10300 }, #endif // EM_CYGNUS_MN10300 #ifdef EMEDIUMTYPE { "EMEDIUMTYPE", (unsigned long) EMEDIUMTYPE }, #endif // EMEDIUMTYPE #ifdef EMFILE { "EMFILE", (unsigned long) EMFILE }, #endif // EMFILE #ifdef EM_FRV { "EM_FRV", (unsigned long) EM_FRV }, #endif // EM_FRV #ifdef EM_H8_300 { "EM_H8_300", (unsigned long) EM_H8_300 }, #endif // EM_H8_300 #ifdef EM_HEXAGON { "EM_HEXAGON", (unsigned long) EM_HEXAGON }, #endif // EM_HEXAGON #ifdef EM_IA_64 { "EM_IA_64", (unsigned long) EM_IA_64 }, #endif // EM_IA_64 #ifdef EMLINK { "EMLINK", (unsigned long) EMLINK }, #endif // EMLINK #ifdef EM_LOONGARCH { "EM_LOONGARCH", (unsigned long) EM_LOONGARCH }, #endif // EM_LOONGARCH #ifdef EM_M32 { "EM_M32", (unsigned long) EM_M32 }, #endif // EM_M32 #ifdef EM_M32R { "EM_M32R", (unsigned long) EM_M32R }, #endif // EM_M32R #ifdef EM_MICROBLAZE { "EM_MICROBLAZE", (unsigned long) EM_MICROBLAZE }, #endif // EM_MICROBLAZE #ifdef EM_MIPS { "EM_MIPS", (unsigned long) EM_MIPS }, #endif // EM_MIPS #ifdef EM_MIPS_RS3_LE { "EM_MIPS_RS3_LE", (unsigned long) EM_MIPS_RS3_LE }, #endif // EM_MIPS_RS3_LE #ifdef EM_MIPS_RS4_BE { "EM_MIPS_RS4_BE", (unsigned long) EM_MIPS_RS4_BE }, #endif // EM_MIPS_RS4_BE #ifdef EM_MN10300 { "EM_MN10300", (unsigned long) EM_MN10300 }, #endif // EM_MN10300 #ifdef EM_NDS32 { "EM_NDS32", (unsigned long) EM_NDS32 }, #endif // EM_NDS32 #ifdef EM_NONE { "EM_NONE", (unsigned long) EM_NONE }, #endif // EM_NONE #ifdef EM_OPENRISC { "EM_OPENRISC", (unsigned long) EM_OPENRISC }, #endif // EM_OPENRISC #ifdef EM_PARISC { "EM_PARISC", (unsigned long) EM_PARISC }, #endif // EM_PARISC #ifdef EM_PPC { "EM_PPC", (unsigned long) EM_PPC }, #endif // EM_PPC #ifdef EM_PPC64 { "EM_PPC64", (unsigned long) EM_PPC64 }, #endif // EM_PPC64 #ifdef EM_RISCV { "EM_RISCV", (unsigned long) EM_RISCV }, #endif // EM_RISCV #ifdef EM_S390 { "EM_S390", (unsigned long) EM_S390 }, #endif // EM_S390 #ifdef EM_S390_OLD { "EM_S390_OLD", (unsigned long) EM_S390_OLD }, #endif // EM_S390_OLD #ifdef EMSGSIZE { "EMSGSIZE", (unsigned long) EMSGSIZE }, #endif // EMSGSIZE #ifdef EM_SH { "EM_SH", (unsigned long) EM_SH }, #endif // EM_SH #ifdef EM_SPARC { "EM_SPARC", (unsigned long) EM_SPARC }, #endif // EM_SPARC #ifdef EM_SPARC32PLUS { "EM_SPARC32PLUS", (unsigned long) EM_SPARC32PLUS }, #endif // EM_SPARC32PLUS #ifdef EM_SPARCV9 { "EM_SPARCV9", (unsigned long) EM_SPARCV9 }, #endif // EM_SPARCV9 #ifdef EM_SPU { "EM_SPU", (unsigned long) EM_SPU }, #endif // EM_SPU #ifdef EM_TI_C6000 { "EM_TI_C6000", (unsigned long) EM_TI_C6000 }, #endif // EM_TI_C6000 #ifdef EM_TILEGX { "EM_TILEGX", (unsigned long) EM_TILEGX }, #endif // EM_TILEGX #ifdef EM_TILEPRO { "EM_TILEPRO", (unsigned long) EM_TILEPRO }, #endif // EM_TILEPRO #ifdef EMULTIHOP { "EMULTIHOP", (unsigned long) EMULTIHOP }, #endif // EMULTIHOP #ifdef EM_UNICORE { "EM_UNICORE", (unsigned long) EM_UNICORE }, #endif // EM_UNICORE #ifdef EM_X86_64 { "EM_X86_64", (unsigned long) EM_X86_64 }, #endif // EM_X86_64 #ifdef EM_XTENSA { "EM_XTENSA", (unsigned long) EM_XTENSA }, #endif // EM_XTENSA #ifdef ENAMETOOLONG { "ENAMETOOLONG", (unsigned long) ENAMETOOLONG }, #endif // ENAMETOOLONG #ifdef ENAVAIL { "ENAVAIL", (unsigned long) ENAVAIL }, #endif // ENAVAIL #ifdef ENETDOWN { "ENETDOWN", (unsigned long) ENETDOWN }, #endif // ENETDOWN #ifdef ENETRESET { "ENETRESET", (unsigned long) ENETRESET }, #endif // ENETRESET #ifdef ENETUNREACH { "ENETUNREACH", (unsigned long) ENETUNREACH }, #endif // ENETUNREACH #ifdef ENFILE { "ENFILE", (unsigned long) ENFILE }, #endif // ENFILE #ifdef ENOANO { "ENOANO", (unsigned long) ENOANO }, #endif // ENOANO #ifdef ENOBUFS { "ENOBUFS", (unsigned long) ENOBUFS }, #endif // ENOBUFS #ifdef ENOCSI { "ENOCSI", (unsigned long) ENOCSI }, #endif // ENOCSI #ifdef ENODATA { "ENODATA", (unsigned long) ENODATA }, #endif // ENODATA #ifdef ENODEV { "ENODEV", (unsigned long) ENODEV }, #endif // ENODEV #ifdef ENOENT { "ENOENT", (unsigned long) ENOENT }, #endif // ENOENT #ifdef ENOEXEC { "ENOEXEC", (unsigned long) ENOEXEC }, #endif // ENOEXEC #ifdef ENOKEY { "ENOKEY", (unsigned long) ENOKEY }, #endif // ENOKEY #ifdef ENOLCK { "ENOLCK", (unsigned long) ENOLCK }, #endif // ENOLCK #ifdef ENOLINK { "ENOLINK", (unsigned long) ENOLINK }, #endif // ENOLINK #ifdef ENOMEDIUM { "ENOMEDIUM", (unsigned long) ENOMEDIUM }, #endif // ENOMEDIUM #ifdef ENOMEM { "ENOMEM", (unsigned long) ENOMEM }, #endif // ENOMEM #ifdef ENOMSG { "ENOMSG", (unsigned long) ENOMSG }, #endif // ENOMSG #ifdef ENONET { "ENONET", (unsigned long) ENONET }, #endif // ENONET #ifdef ENOPKG { "ENOPKG", (unsigned long) ENOPKG }, #endif // ENOPKG #ifdef ENOPROTOOPT { "ENOPROTOOPT", (unsigned long) ENOPROTOOPT }, #endif // ENOPROTOOPT #ifdef ENOSPC { "ENOSPC", (unsigned long) ENOSPC }, #endif // ENOSPC #ifdef ENOSR { "ENOSR", (unsigned long) ENOSR }, #endif // ENOSR #ifdef ENOSTR { "ENOSTR", (unsigned long) ENOSTR }, #endif // ENOSTR #ifdef ENOSYS { "ENOSYS", (unsigned long) ENOSYS }, #endif // ENOSYS #ifdef ENOTBLK { "ENOTBLK", (unsigned long) ENOTBLK }, #endif // ENOTBLK #ifdef ENOTCONN { "ENOTCONN", (unsigned long) ENOTCONN }, #endif // ENOTCONN #ifdef ENOTDIR { "ENOTDIR", (unsigned long) ENOTDIR }, #endif // ENOTDIR #ifdef ENOTEMPTY { "ENOTEMPTY", (unsigned long) ENOTEMPTY }, #endif // ENOTEMPTY #ifdef ENOTNAM { "ENOTNAM", (unsigned long) ENOTNAM }, #endif // ENOTNAM #ifdef ENOTRECOVERABLE { "ENOTRECOVERABLE", (unsigned long) ENOTRECOVERABLE }, #endif // ENOTRECOVERABLE #ifdef ENOTSOCK { "ENOTSOCK", (unsigned long) ENOTSOCK }, #endif // ENOTSOCK #ifdef ENOTSUP { "ENOTSUP", (unsigned long) ENOTSUP }, #endif // ENOTSUP #ifdef ENOTTY { "ENOTTY", (unsigned long) ENOTTY }, #endif // ENOTTY #ifdef ENOTUNIQ { "ENOTUNIQ", (unsigned long) ENOTUNIQ }, #endif // ENOTUNIQ #ifdef ENXIO { "ENXIO", (unsigned long) ENXIO }, #endif // ENXIO #ifdef EOPNOTSUPP { "EOPNOTSUPP", (unsigned long) EOPNOTSUPP }, #endif // EOPNOTSUPP #ifdef EOVERFLOW { "EOVERFLOW", (unsigned long) EOVERFLOW }, #endif // EOVERFLOW #ifdef EOWNERDEAD { "EOWNERDEAD", (unsigned long) EOWNERDEAD }, #endif // EOWNERDEAD #ifdef EPERM { "EPERM", (unsigned long) EPERM }, #endif // EPERM #ifdef EPFNOSUPPORT { "EPFNOSUPPORT", (unsigned long) EPFNOSUPPORT }, #endif // EPFNOSUPPORT #ifdef EPIPE { "EPIPE", (unsigned long) EPIPE }, #endif // EPIPE #ifdef EPROTO { "EPROTO", (unsigned long) EPROTO }, #endif // EPROTO #ifdef EPROTONOSUPPORT { "EPROTONOSUPPORT", (unsigned long) EPROTONOSUPPORT }, #endif // EPROTONOSUPPORT #ifdef EPROTOTYPE { "EPROTOTYPE", (unsigned long) EPROTOTYPE }, #endif // EPROTOTYPE #ifdef ERANGE { "ERANGE", (unsigned long) ERANGE }, #endif // ERANGE #ifdef EREMCHG { "EREMCHG", (unsigned long) EREMCHG }, #endif // EREMCHG #ifdef EREMOTE { "EREMOTE", (unsigned long) EREMOTE }, #endif // EREMOTE #ifdef EREMOTEIO { "EREMOTEIO", (unsigned long) EREMOTEIO }, #endif // EREMOTEIO #ifdef ERESTART { "ERESTART", (unsigned long) ERESTART }, #endif // ERESTART #ifdef ERFKILL { "ERFKILL", (unsigned long) ERFKILL }, #endif // ERFKILL #ifdef EROFS { "EROFS", (unsigned long) EROFS }, #endif // EROFS #ifdef ESHUTDOWN { "ESHUTDOWN", (unsigned long) ESHUTDOWN }, #endif // ESHUTDOWN #ifdef ESOCKTNOSUPPORT { "ESOCKTNOSUPPORT", (unsigned long) ESOCKTNOSUPPORT }, #endif // ESOCKTNOSUPPORT #ifdef ESPIPE { "ESPIPE", (unsigned long) ESPIPE }, #endif // ESPIPE #ifdef ESRCH { "ESRCH", (unsigned long) ESRCH }, #endif // ESRCH #ifdef ESRMNT { "ESRMNT", (unsigned long) ESRMNT }, #endif // ESRMNT #ifdef ESTALE { "ESTALE", (unsigned long) ESTALE }, #endif // ESTALE #ifdef ESTRPIPE { "ESTRPIPE", (unsigned long) ESTRPIPE }, #endif // ESTRPIPE #ifdef ETIME { "ETIME", (unsigned long) ETIME }, #endif // ETIME #ifdef ETIMEDOUT { "ETIMEDOUT", (unsigned long) ETIMEDOUT }, #endif // ETIMEDOUT #ifdef ETOOMANYREFS { "ETOOMANYREFS", (unsigned long) ETOOMANYREFS }, #endif // ETOOMANYREFS #ifdef ETXTBSY { "ETXTBSY", (unsigned long) ETXTBSY }, #endif // ETXTBSY #ifdef EUCLEAN { "EUCLEAN", (unsigned long) EUCLEAN }, #endif // EUCLEAN #ifdef EUNATCH { "EUNATCH", (unsigned long) EUNATCH }, #endif // EUNATCH #ifdef EUSERS { "EUSERS", (unsigned long) EUSERS }, #endif // EUSERS #ifdef EWOULDBLOCK { "EWOULDBLOCK", (unsigned long) EWOULDBLOCK }, #endif // EWOULDBLOCK #ifdef EXDEV { "EXDEV", (unsigned long) EXDEV }, #endif // EXDEV #ifdef EXFULL { "EXFULL", (unsigned long) EXFULL }, #endif // EXFULL #ifdef EXTA { "EXTA", (unsigned long) EXTA }, #endif // EXTA #ifdef EXTB { "EXTB", (unsigned long) EXTB }, #endif // EXTB #ifdef EXTPROC { "EXTPROC", (unsigned long) EXTPROC }, #endif // EXTPROC #ifdef FAPPEND { "FAPPEND", (unsigned long) FAPPEND }, #endif // FAPPEND #ifdef FASYNC { "FASYNC", (unsigned long) FASYNC }, #endif // FASYNC #ifdef FD_2M { "FD_2M", (unsigned long) FD_2M }, #endif // FD_2M #ifdef FD_AUTODETECT_SIZE { "FD_AUTODETECT_SIZE", (unsigned long) FD_AUTODETECT_SIZE }, #endif // FD_AUTODETECT_SIZE #ifdef FD_BROKEN_DCL { "FD_BROKEN_DCL", (unsigned long) FD_BROKEN_DCL }, #endif // FD_BROKEN_DCL #ifdef FD_CLOEXEC { "FD_CLOEXEC", (unsigned long) FD_CLOEXEC }, #endif // FD_CLOEXEC #ifdef FDCLRPRM { "FDCLRPRM", (unsigned long) FDCLRPRM }, #endif // FDCLRPRM #ifdef FD_DEBUG { "FD_DEBUG", (unsigned long) FD_DEBUG }, #endif // FD_DEBUG #ifdef FDDEFMEDIAPRM { "FDDEFMEDIAPRM", (unsigned long) FDDEFMEDIAPRM }, #endif // FDDEFMEDIAPRM #ifdef FDDEFPRM { "FDDEFPRM", (unsigned long) FDDEFPRM }, #endif // FDDEFPRM #ifdef FD_DRIVER_VERSION { "FD_DRIVER_VERSION", (unsigned long) FD_DRIVER_VERSION }, #endif // FD_DRIVER_VERSION #ifdef FDEJECT { "FDEJECT", (unsigned long) FDEJECT }, #endif // FDEJECT #ifdef FD_FILL_BYTE { "FD_FILL_BYTE", (unsigned long) FD_FILL_BYTE }, #endif // FD_FILL_BYTE #ifdef FDFLUSH { "FDFLUSH", (unsigned long) FDFLUSH }, #endif // FDFLUSH #ifdef FDFMTBEG { "FDFMTBEG", (unsigned long) FDFMTBEG }, #endif // FDFMTBEG #ifdef FDFMTEND { "FDFMTEND", (unsigned long) FDFMTEND }, #endif // FDFMTEND #ifdef FDFMTTRK { "FDFMTTRK", (unsigned long) FDFMTTRK }, #endif // FDFMTTRK #ifdef FDGETDRVPRM { "FDGETDRVPRM", (unsigned long) FDGETDRVPRM }, #endif // FDGETDRVPRM #ifdef FDGETDRVSTAT { "FDGETDRVSTAT", (unsigned long) FDGETDRVSTAT }, #endif // FDGETDRVSTAT #ifdef FDGETDRVTYP { "FDGETDRVTYP", (unsigned long) FDGETDRVTYP }, #endif // FDGETDRVTYP #ifdef FDGETFDCSTAT { "FDGETFDCSTAT", (unsigned long) FDGETFDCSTAT }, #endif // FDGETFDCSTAT #ifdef FDGETMAXERRS { "FDGETMAXERRS", (unsigned long) FDGETMAXERRS }, #endif // FDGETMAXERRS #ifdef FDGETMEDIAPRM { "FDGETMEDIAPRM", (unsigned long) FDGETMEDIAPRM }, #endif // FDGETMEDIAPRM #ifdef FDGETPRM { "FDGETPRM", (unsigned long) FDGETPRM }, #endif // FDGETPRM #ifdef FD_INVERTED_DCL { "FD_INVERTED_DCL", (unsigned long) FD_INVERTED_DCL }, #endif // FD_INVERTED_DCL #ifdef FDMSGOFF { "FDMSGOFF", (unsigned long) FDMSGOFF }, #endif // FDMSGOFF #ifdef FDMSGON { "FDMSGON", (unsigned long) FDMSGON }, #endif // FDMSGON #ifdef FD_PERP { "FD_PERP", (unsigned long) FD_PERP }, #endif // FD_PERP #ifdef FDPOLLDRVSTAT { "FDPOLLDRVSTAT", (unsigned long) FDPOLLDRVSTAT }, #endif // FDPOLLDRVSTAT #ifdef FDRAWCMD { "FDRAWCMD", (unsigned long) FDRAWCMD }, #endif // FDRAWCMD #ifdef FD_RAW_CMD_SIZE { "FD_RAW_CMD_SIZE", (unsigned long) FD_RAW_CMD_SIZE }, #endif // FD_RAW_CMD_SIZE #ifdef FD_RAW_DISK_CHANGE { "FD_RAW_DISK_CHANGE", (unsigned long) FD_RAW_DISK_CHANGE }, #endif // FD_RAW_DISK_CHANGE #ifdef FD_RAW_FAILURE { "FD_RAW_FAILURE", (unsigned long) FD_RAW_FAILURE }, #endif // FD_RAW_FAILURE #ifdef FD_RAW_HARDFAILURE { "FD_RAW_HARDFAILURE", (unsigned long) FD_RAW_HARDFAILURE }, #endif // FD_RAW_HARDFAILURE #ifdef FD_RAW_INTR { "FD_RAW_INTR", (unsigned long) FD_RAW_INTR }, #endif // FD_RAW_INTR #ifdef FD_RAW_MORE { "FD_RAW_MORE", (unsigned long) FD_RAW_MORE }, #endif // FD_RAW_MORE #ifdef FD_RAW_NEED_DISK { "FD_RAW_NEED_DISK", (unsigned long) FD_RAW_NEED_DISK }, #endif // FD_RAW_NEED_DISK #ifdef FD_RAW_NEED_SEEK { "FD_RAW_NEED_SEEK", (unsigned long) FD_RAW_NEED_SEEK }, #endif // FD_RAW_NEED_SEEK #ifdef FD_RAW_NO_MOTOR { "FD_RAW_NO_MOTOR", (unsigned long) FD_RAW_NO_MOTOR }, #endif // FD_RAW_NO_MOTOR #ifdef FD_RAW_NO_MOTOR_AFTER { "FD_RAW_NO_MOTOR_AFTER", (unsigned long) FD_RAW_NO_MOTOR_AFTER }, #endif // FD_RAW_NO_MOTOR_AFTER #ifdef FD_RAW_READ { "FD_RAW_READ", (unsigned long) FD_RAW_READ }, #endif // FD_RAW_READ #ifdef FD_RAW_REPLY_SIZE { "FD_RAW_REPLY_SIZE", (unsigned long) FD_RAW_REPLY_SIZE }, #endif // FD_RAW_REPLY_SIZE #ifdef FD_RAW_SOFTFAILURE { "FD_RAW_SOFTFAILURE", (unsigned long) FD_RAW_SOFTFAILURE }, #endif // FD_RAW_SOFTFAILURE #ifdef FD_RAW_SPIN { "FD_RAW_SPIN", (unsigned long) FD_RAW_SPIN }, #endif // FD_RAW_SPIN #ifdef FD_RAW_STOP_IF_FAILURE { "FD_RAW_STOP_IF_FAILURE", (unsigned long) FD_RAW_STOP_IF_FAILURE }, #endif // FD_RAW_STOP_IF_FAILURE #ifdef FD_RAW_STOP_IF_SUCCESS { "FD_RAW_STOP_IF_SUCCESS", (unsigned long) FD_RAW_STOP_IF_SUCCESS }, #endif // FD_RAW_STOP_IF_SUCCESS #ifdef FD_RAW_WRITE { "FD_RAW_WRITE", (unsigned long) FD_RAW_WRITE }, #endif // FD_RAW_WRITE #ifdef FDRESET { "FDRESET", (unsigned long) FDRESET }, #endif // FDRESET #ifdef FD_SECTBASEMASK { "FD_SECTBASEMASK", (unsigned long) FD_SECTBASEMASK }, #endif // FD_SECTBASEMASK #ifdef FDSETDRVPRM { "FDSETDRVPRM", (unsigned long) FDSETDRVPRM }, #endif // FDSETDRVPRM #ifdef FDSETEMSGTRESH { "FDSETEMSGTRESH", (unsigned long) FDSETEMSGTRESH }, #endif // FDSETEMSGTRESH #ifdef FDSETMAXERRS { "FDSETMAXERRS", (unsigned long) FDSETMAXERRS }, #endif // FDSETMAXERRS #ifdef FDSETMEDIAPRM { "FDSETMEDIAPRM", (unsigned long) FDSETMEDIAPRM }, #endif // FDSETMEDIAPRM #ifdef FDSETPRM { "FDSETPRM", (unsigned long) FDSETPRM }, #endif // FDSETPRM #ifdef FD_SETSIZE { "FD_SETSIZE", (unsigned long) FD_SETSIZE }, #endif // FD_SETSIZE #ifdef FD_SILENT_DCL_CLEAR { "FD_SILENT_DCL_CLEAR", (unsigned long) FD_SILENT_DCL_CLEAR }, #endif // FD_SILENT_DCL_CLEAR #ifdef FD_SIZECODEMASK { "FD_SIZECODEMASK", (unsigned long) FD_SIZECODEMASK }, #endif // FD_SIZECODEMASK #ifdef FD_STRETCH { "FD_STRETCH", (unsigned long) FD_STRETCH }, #endif // FD_STRETCH #ifdef FD_SWAPSIDES { "FD_SWAPSIDES", (unsigned long) FD_SWAPSIDES }, #endif // FD_SWAPSIDES #ifdef FDTWADDLE { "FDTWADDLE", (unsigned long) FDTWADDLE }, #endif // FDTWADDLE #ifdef F_DUPFD { "F_DUPFD", (unsigned long) F_DUPFD }, #endif // F_DUPFD #ifdef F_DUPFD_CLOEXEC { "F_DUPFD_CLOEXEC", (unsigned long) F_DUPFD_CLOEXEC }, #endif // F_DUPFD_CLOEXEC #ifdef FDWERRORCLR { "FDWERRORCLR", (unsigned long) FDWERRORCLR }, #endif // FDWERRORCLR #ifdef FDWERRORGET { "FDWERRORGET", (unsigned long) FDWERRORGET }, #endif // FDWERRORGET #ifdef FD_ZEROBASED { "FD_ZEROBASED", (unsigned long) FD_ZEROBASED }, #endif // FD_ZEROBASED #ifdef F_EXLCK { "F_EXLCK", (unsigned long) F_EXLCK }, #endif // F_EXLCK #ifdef FF0 { "FF0", (unsigned long) FF0 }, #endif // FF0 #ifdef FF1 { "FF1", (unsigned long) FF1 }, #endif // FF1 #ifdef FFDLY { "FFDLY", (unsigned long) FFDLY }, #endif // FFDLY #ifdef FFSYNC { "FFSYNC", (unsigned long) FFSYNC }, #endif // FFSYNC #ifdef F_GETFD { "F_GETFD", (unsigned long) F_GETFD }, #endif // F_GETFD #ifdef F_GETFL { "F_GETFL", (unsigned long) F_GETFL }, #endif // F_GETFL #ifdef F_GETLK64 { "F_GETLK64", (unsigned long) F_GETLK64 }, #endif // F_GETLK64 #ifdef F_GETLK { "F_GETLK", (unsigned long) F_GETLK }, #endif // F_GETLK #ifdef F_GETOWN { "F_GETOWN", (unsigned long) F_GETOWN }, #endif // F_GETOWN #ifdef FIBMAP { "FIBMAP", (unsigned long) FIBMAP }, #endif // FIBMAP #ifdef FICLONE { "FICLONE", (unsigned long) FICLONE }, #endif // FICLONE #ifdef FICLONERANGE { "FICLONERANGE", (unsigned long) FICLONERANGE }, #endif // FICLONERANGE #ifdef FIDEDUPERANGE { "FIDEDUPERANGE", (unsigned long) FIDEDUPERANGE }, #endif // FIDEDUPERANGE #ifdef FIFREEZE { "FIFREEZE", (unsigned long) FIFREEZE }, #endif // FIFREEZE #ifdef FIGETBSZ { "FIGETBSZ", (unsigned long) FIGETBSZ }, #endif // FIGETBSZ #ifdef FILE_ATTR_SIZE_LATEST { "FILE_ATTR_SIZE_LATEST", (unsigned long) FILE_ATTR_SIZE_LATEST }, #endif // FILE_ATTR_SIZE_LATEST #ifdef FILE_ATTR_SIZE_VER0 { "FILE_ATTR_SIZE_VER0", (unsigned long) FILE_ATTR_SIZE_VER0 }, #endif // FILE_ATTR_SIZE_VER0 #ifdef FILE_DEDUPE_RANGE_DIFFERS { "FILE_DEDUPE_RANGE_DIFFERS", (unsigned long) FILE_DEDUPE_RANGE_DIFFERS }, #endif // FILE_DEDUPE_RANGE_DIFFERS #ifdef FILE_DEDUPE_RANGE_SAME { "FILE_DEDUPE_RANGE_SAME", (unsigned long) FILE_DEDUPE_RANGE_SAME }, #endif // FILE_DEDUPE_RANGE_SAME #ifdef FIOASYNC { "FIOASYNC", (unsigned long) FIOASYNC }, #endif // FIOASYNC #ifdef FIOCLEX { "FIOCLEX", (unsigned long) FIOCLEX }, #endif // FIOCLEX #ifdef FIOGETOWN { "FIOGETOWN", (unsigned long) FIOGETOWN }, #endif // FIOGETOWN #ifdef FIONBIO { "FIONBIO", (unsigned long) FIONBIO }, #endif // FIONBIO #ifdef FIONCLEX { "FIONCLEX", (unsigned long) FIONCLEX }, #endif // FIONCLEX #ifdef FIONREAD { "FIONREAD", (unsigned long) FIONREAD }, #endif // FIONREAD #ifdef FIOQSIZE { "FIOQSIZE", (unsigned long) FIOQSIZE }, #endif // FIOQSIZE #ifdef FIOSETOWN { "FIOSETOWN", (unsigned long) FIOSETOWN }, #endif // FIOSETOWN #ifdef FITHAW { "FITHAW", (unsigned long) FITHAW }, #endif // FITHAW #ifdef FITRIM { "FITRIM", (unsigned long) FITRIM }, #endif // FITRIM #ifdef F_LOCK { "F_LOCK", (unsigned long) F_LOCK }, #endif // F_LOCK #ifdef FLUSHO { "FLUSHO", (unsigned long) FLUSHO }, #endif // FLUSHO #ifdef FNDELAY { "FNDELAY", (unsigned long) FNDELAY }, #endif // FNDELAY #ifdef FNONBLOCK { "FNONBLOCK", (unsigned long) FNONBLOCK }, #endif // FNONBLOCK #ifdef F_OK { "F_OK", (unsigned long) F_OK }, #endif // F_OK #ifdef FPE_CONDTRAP { "FPE_CONDTRAP", (unsigned long) FPE_CONDTRAP }, #endif // FPE_CONDTRAP #ifdef FPE_FLTDIV { "FPE_FLTDIV", (unsigned long) FPE_FLTDIV }, #endif // FPE_FLTDIV #ifdef FPE_FLTINV { "FPE_FLTINV", (unsigned long) FPE_FLTINV }, #endif // FPE_FLTINV #ifdef FPE_FLTOVF { "FPE_FLTOVF", (unsigned long) FPE_FLTOVF }, #endif // FPE_FLTOVF #ifdef FPE_FLTRES { "FPE_FLTRES", (unsigned long) FPE_FLTRES }, #endif // FPE_FLTRES #ifdef FPE_FLTSUB { "FPE_FLTSUB", (unsigned long) FPE_FLTSUB }, #endif // FPE_FLTSUB #ifdef FPE_FLTUND { "FPE_FLTUND", (unsigned long) FPE_FLTUND }, #endif // FPE_FLTUND #ifdef FPE_FLTUNK { "FPE_FLTUNK", (unsigned long) FPE_FLTUNK }, #endif // FPE_FLTUNK #ifdef FPE_INTDIV { "FPE_INTDIV", (unsigned long) FPE_INTDIV }, #endif // FPE_INTDIV #ifdef FPE_INTOVF { "FPE_INTOVF", (unsigned long) FPE_INTOVF }, #endif // FPE_INTOVF #ifdef FP_XSTATE_MAGIC1 { "FP_XSTATE_MAGIC1", (unsigned long) FP_XSTATE_MAGIC1 }, #endif // FP_XSTATE_MAGIC1 #ifdef FP_XSTATE_MAGIC2 { "FP_XSTATE_MAGIC2", (unsigned long) FP_XSTATE_MAGIC2 }, #endif // FP_XSTATE_MAGIC2 #ifdef FP_XSTATE_MAGIC2_SIZE { "FP_XSTATE_MAGIC2_SIZE", (unsigned long) FP_XSTATE_MAGIC2_SIZE }, #endif // FP_XSTATE_MAGIC2_SIZE #ifdef F_RDLCK { "F_RDLCK", (unsigned long) F_RDLCK }, #endif // F_RDLCK #ifdef FS_APPEND_FL { "FS_APPEND_FL", (unsigned long) FS_APPEND_FL }, #endif // FS_APPEND_FL #ifdef FS_BTREE_FL { "FS_BTREE_FL", (unsigned long) FS_BTREE_FL }, #endif // FS_BTREE_FL #ifdef FS_CASEFOLD_FL { "FS_CASEFOLD_FL", (unsigned long) FS_CASEFOLD_FL }, #endif // FS_CASEFOLD_FL #ifdef FS_COMPRBLK_FL { "FS_COMPRBLK_FL", (unsigned long) FS_COMPRBLK_FL }, #endif // FS_COMPRBLK_FL #ifdef FS_COMPR_FL { "FS_COMPR_FL", (unsigned long) FS_COMPR_FL }, #endif // FS_COMPR_FL #ifdef FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED { "FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED", (unsigned long) FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED }, #endif // FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED #ifdef FSCRYPT_KEY_DESC_PREFIX_SIZE { "FSCRYPT_KEY_DESC_PREFIX_SIZE", (unsigned long) FSCRYPT_KEY_DESC_PREFIX_SIZE }, #endif // FSCRYPT_KEY_DESC_PREFIX_SIZE #ifdef FSCRYPT_KEY_DESCRIPTOR_SIZE { "FSCRYPT_KEY_DESCRIPTOR_SIZE", (unsigned long) FSCRYPT_KEY_DESCRIPTOR_SIZE }, #endif // FSCRYPT_KEY_DESCRIPTOR_SIZE #ifdef FSCRYPT_KEY_IDENTIFIER_SIZE { "FSCRYPT_KEY_IDENTIFIER_SIZE", (unsigned long) FSCRYPT_KEY_IDENTIFIER_SIZE }, #endif // FSCRYPT_KEY_IDENTIFIER_SIZE #ifdef FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY { "FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY", (unsigned long) FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY }, #endif // FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY #ifdef FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS { "FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS", (unsigned long) FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS }, #endif // FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS #ifdef FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR { "FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR", (unsigned long) FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR }, #endif // FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR #ifdef FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER { "FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER", (unsigned long) FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER }, #endif // FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER #ifdef FSCRYPT_KEY_STATUS_ABSENT { "FSCRYPT_KEY_STATUS_ABSENT", (unsigned long) FSCRYPT_KEY_STATUS_ABSENT }, #endif // FSCRYPT_KEY_STATUS_ABSENT #ifdef FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF { "FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF", (unsigned long) FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF }, #endif // FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF #ifdef FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED { "FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED", (unsigned long) FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED }, #endif // FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED #ifdef FSCRYPT_KEY_STATUS_PRESENT { "FSCRYPT_KEY_STATUS_PRESENT", (unsigned long) FSCRYPT_KEY_STATUS_PRESENT }, #endif // FSCRYPT_KEY_STATUS_PRESENT #ifdef FSCRYPT_MAX_KEY_SIZE { "FSCRYPT_MAX_KEY_SIZE", (unsigned long) FSCRYPT_MAX_KEY_SIZE }, #endif // FSCRYPT_MAX_KEY_SIZE #ifdef FSCRYPT_MODE_ADIANTUM { "FSCRYPT_MODE_ADIANTUM", (unsigned long) FSCRYPT_MODE_ADIANTUM }, #endif // FSCRYPT_MODE_ADIANTUM #ifdef FSCRYPT_MODE_AES_128_CBC { "FSCRYPT_MODE_AES_128_CBC", (unsigned long) FSCRYPT_MODE_AES_128_CBC }, #endif // FSCRYPT_MODE_AES_128_CBC #ifdef FSCRYPT_MODE_AES_128_CTS { "FSCRYPT_MODE_AES_128_CTS", (unsigned long) FSCRYPT_MODE_AES_128_CTS }, #endif // FSCRYPT_MODE_AES_128_CTS #ifdef FSCRYPT_MODE_AES_256_CTS { "FSCRYPT_MODE_AES_256_CTS", (unsigned long) FSCRYPT_MODE_AES_256_CTS }, #endif // FSCRYPT_MODE_AES_256_CTS #ifdef FSCRYPT_MODE_AES_256_HCTR2 { "FSCRYPT_MODE_AES_256_HCTR2", (unsigned long) FSCRYPT_MODE_AES_256_HCTR2 }, #endif // FSCRYPT_MODE_AES_256_HCTR2 #ifdef FSCRYPT_MODE_AES_256_XTS { "FSCRYPT_MODE_AES_256_XTS", (unsigned long) FSCRYPT_MODE_AES_256_XTS }, #endif // FSCRYPT_MODE_AES_256_XTS #ifdef FSCRYPT_MODE_SM4_CTS { "FSCRYPT_MODE_SM4_CTS", (unsigned long) FSCRYPT_MODE_SM4_CTS }, #endif // FSCRYPT_MODE_SM4_CTS #ifdef FSCRYPT_MODE_SM4_XTS { "FSCRYPT_MODE_SM4_XTS", (unsigned long) FSCRYPT_MODE_SM4_XTS }, #endif // FSCRYPT_MODE_SM4_XTS #ifdef FSCRYPT_POLICY_FLAG_DIRECT_KEY { "FSCRYPT_POLICY_FLAG_DIRECT_KEY", (unsigned long) FSCRYPT_POLICY_FLAG_DIRECT_KEY }, #endif // FSCRYPT_POLICY_FLAG_DIRECT_KEY #ifdef FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 { "FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32", (unsigned long) FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 }, #endif // FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 #ifdef FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 { "FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64", (unsigned long) FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 }, #endif // FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 #ifdef FSCRYPT_POLICY_FLAGS_PAD_16 { "FSCRYPT_POLICY_FLAGS_PAD_16", (unsigned long) FSCRYPT_POLICY_FLAGS_PAD_16 }, #endif // FSCRYPT_POLICY_FLAGS_PAD_16 #ifdef FSCRYPT_POLICY_FLAGS_PAD_32 { "FSCRYPT_POLICY_FLAGS_PAD_32", (unsigned long) FSCRYPT_POLICY_FLAGS_PAD_32 }, #endif // FSCRYPT_POLICY_FLAGS_PAD_32 #ifdef FSCRYPT_POLICY_FLAGS_PAD_4 { "FSCRYPT_POLICY_FLAGS_PAD_4", (unsigned long) FSCRYPT_POLICY_FLAGS_PAD_4 }, #endif // FSCRYPT_POLICY_FLAGS_PAD_4 #ifdef FSCRYPT_POLICY_FLAGS_PAD_8 { "FSCRYPT_POLICY_FLAGS_PAD_8", (unsigned long) FSCRYPT_POLICY_FLAGS_PAD_8 }, #endif // FSCRYPT_POLICY_FLAGS_PAD_8 #ifdef FSCRYPT_POLICY_FLAGS_PAD_MASK { "FSCRYPT_POLICY_FLAGS_PAD_MASK", (unsigned long) FSCRYPT_POLICY_FLAGS_PAD_MASK }, #endif // FSCRYPT_POLICY_FLAGS_PAD_MASK #ifdef FSCRYPT_POLICY_V1 { "FSCRYPT_POLICY_V1", (unsigned long) FSCRYPT_POLICY_V1 }, #endif // FSCRYPT_POLICY_V1 #ifdef FSCRYPT_POLICY_V2 { "FSCRYPT_POLICY_V2", (unsigned long) FSCRYPT_POLICY_V2 }, #endif // FSCRYPT_POLICY_V2 #ifdef FS_DAX_FL { "FS_DAX_FL", (unsigned long) FS_DAX_FL }, #endif // FS_DAX_FL #ifdef FS_DIRSYNC_FL { "FS_DIRSYNC_FL", (unsigned long) FS_DIRSYNC_FL }, #endif // FS_DIRSYNC_FL #ifdef FS_DIRTY_FL { "FS_DIRTY_FL", (unsigned long) FS_DIRTY_FL }, #endif // FS_DIRTY_FL #ifdef FS_EA_INODE_FL { "FS_EA_INODE_FL", (unsigned long) FS_EA_INODE_FL }, #endif // FS_EA_INODE_FL #ifdef FS_ENCRYPT_FL { "FS_ENCRYPT_FL", (unsigned long) FS_ENCRYPT_FL }, #endif // FS_ENCRYPT_FL #ifdef FS_ENCRYPTION_MODE_ADIANTUM { "FS_ENCRYPTION_MODE_ADIANTUM", (unsigned long) FS_ENCRYPTION_MODE_ADIANTUM }, #endif // FS_ENCRYPTION_MODE_ADIANTUM #ifdef FS_ENCRYPTION_MODE_AES_128_CBC { "FS_ENCRYPTION_MODE_AES_128_CBC", (unsigned long) FS_ENCRYPTION_MODE_AES_128_CBC }, #endif // FS_ENCRYPTION_MODE_AES_128_CBC #ifdef FS_ENCRYPTION_MODE_AES_128_CTS { "FS_ENCRYPTION_MODE_AES_128_CTS", (unsigned long) FS_ENCRYPTION_MODE_AES_128_CTS }, #endif // FS_ENCRYPTION_MODE_AES_128_CTS #ifdef FS_ENCRYPTION_MODE_AES_256_CBC { "FS_ENCRYPTION_MODE_AES_256_CBC", (unsigned long) FS_ENCRYPTION_MODE_AES_256_CBC }, #endif // FS_ENCRYPTION_MODE_AES_256_CBC #ifdef FS_ENCRYPTION_MODE_AES_256_CTS { "FS_ENCRYPTION_MODE_AES_256_CTS", (unsigned long) FS_ENCRYPTION_MODE_AES_256_CTS }, #endif // FS_ENCRYPTION_MODE_AES_256_CTS #ifdef FS_ENCRYPTION_MODE_AES_256_GCM { "FS_ENCRYPTION_MODE_AES_256_GCM", (unsigned long) FS_ENCRYPTION_MODE_AES_256_GCM }, #endif // FS_ENCRYPTION_MODE_AES_256_GCM #ifdef FS_ENCRYPTION_MODE_AES_256_XTS { "FS_ENCRYPTION_MODE_AES_256_XTS", (unsigned long) FS_ENCRYPTION_MODE_AES_256_XTS }, #endif // FS_ENCRYPTION_MODE_AES_256_XTS #ifdef FS_ENCRYPTION_MODE_INVALID { "FS_ENCRYPTION_MODE_INVALID", (unsigned long) FS_ENCRYPTION_MODE_INVALID }, #endif // FS_ENCRYPTION_MODE_INVALID #ifdef FS_EOFBLOCKS_FL { "FS_EOFBLOCKS_FL", (unsigned long) FS_EOFBLOCKS_FL }, #endif // FS_EOFBLOCKS_FL #ifdef F_SETFD { "F_SETFD", (unsigned long) F_SETFD }, #endif // F_SETFD #ifdef F_SETFL { "F_SETFL", (unsigned long) F_SETFL }, #endif // F_SETFL #ifdef F_SETLK64 { "F_SETLK64", (unsigned long) F_SETLK64 }, #endif // F_SETLK64 #ifdef F_SETLK { "F_SETLK", (unsigned long) F_SETLK }, #endif // F_SETLK #ifdef F_SETLKW64 { "F_SETLKW64", (unsigned long) F_SETLKW64 }, #endif // F_SETLKW64 #ifdef F_SETLKW { "F_SETLKW", (unsigned long) F_SETLKW }, #endif // F_SETLKW #ifdef F_SETOWN { "F_SETOWN", (unsigned long) F_SETOWN }, #endif // F_SETOWN #ifdef FS_EXTENT_FL { "FS_EXTENT_FL", (unsigned long) FS_EXTENT_FL }, #endif // FS_EXTENT_FL #ifdef FS_FL_USER_MODIFIABLE { "FS_FL_USER_MODIFIABLE", (unsigned long) FS_FL_USER_MODIFIABLE }, #endif // FS_FL_USER_MODIFIABLE #ifdef FS_FL_USER_VISIBLE { "FS_FL_USER_VISIBLE", (unsigned long) FS_FL_USER_VISIBLE }, #endif // FS_FL_USER_VISIBLE #ifdef F_SHLCK { "F_SHLCK", (unsigned long) F_SHLCK }, #endif // F_SHLCK #ifdef FS_HUGE_FILE_FL { "FS_HUGE_FILE_FL", (unsigned long) FS_HUGE_FILE_FL }, #endif // FS_HUGE_FILE_FL #ifdef FS_IMAGIC_FL { "FS_IMAGIC_FL", (unsigned long) FS_IMAGIC_FL }, #endif // FS_IMAGIC_FL #ifdef FS_IMMUTABLE_FL { "FS_IMMUTABLE_FL", (unsigned long) FS_IMMUTABLE_FL }, #endif // FS_IMMUTABLE_FL #ifdef FS_INDEX_FL { "FS_INDEX_FL", (unsigned long) FS_INDEX_FL }, #endif // FS_INDEX_FL #ifdef FS_INLINE_DATA_FL { "FS_INLINE_DATA_FL", (unsigned long) FS_INLINE_DATA_FL }, #endif // FS_INLINE_DATA_FL #ifdef FS_IOC32_GETFLAGS { "FS_IOC32_GETFLAGS", (unsigned long) FS_IOC32_GETFLAGS }, #endif // FS_IOC32_GETFLAGS #ifdef FS_IOC32_GETVERSION { "FS_IOC32_GETVERSION", (unsigned long) FS_IOC32_GETVERSION }, #endif // FS_IOC32_GETVERSION #ifdef FS_IOC32_SETFLAGS { "FS_IOC32_SETFLAGS", (unsigned long) FS_IOC32_SETFLAGS }, #endif // FS_IOC32_SETFLAGS #ifdef FS_IOC32_SETVERSION { "FS_IOC32_SETVERSION", (unsigned long) FS_IOC32_SETVERSION }, #endif // FS_IOC32_SETVERSION #ifdef FS_IOC_ADD_ENCRYPTION_KEY { "FS_IOC_ADD_ENCRYPTION_KEY", (unsigned long) FS_IOC_ADD_ENCRYPTION_KEY }, #endif // FS_IOC_ADD_ENCRYPTION_KEY #ifdef FS_IOC_FIEMAP { "FS_IOC_FIEMAP", (unsigned long) FS_IOC_FIEMAP }, #endif // FS_IOC_FIEMAP #ifdef FS_IOC_FSGETXATTR { "FS_IOC_FSGETXATTR", (unsigned long) FS_IOC_FSGETXATTR }, #endif // FS_IOC_FSGETXATTR #ifdef FS_IOC_FSSETXATTR { "FS_IOC_FSSETXATTR", (unsigned long) FS_IOC_FSSETXATTR }, #endif // FS_IOC_FSSETXATTR #ifdef FS_IOC_GET_ENCRYPTION_KEY_STATUS { "FS_IOC_GET_ENCRYPTION_KEY_STATUS", (unsigned long) FS_IOC_GET_ENCRYPTION_KEY_STATUS }, #endif // FS_IOC_GET_ENCRYPTION_KEY_STATUS #ifdef FS_IOC_GET_ENCRYPTION_NONCE { "FS_IOC_GET_ENCRYPTION_NONCE", (unsigned long) FS_IOC_GET_ENCRYPTION_NONCE }, #endif // FS_IOC_GET_ENCRYPTION_NONCE #ifdef FS_IOC_GET_ENCRYPTION_POLICY_EX { "FS_IOC_GET_ENCRYPTION_POLICY_EX", (unsigned long) FS_IOC_GET_ENCRYPTION_POLICY_EX }, #endif // FS_IOC_GET_ENCRYPTION_POLICY_EX #ifdef FS_IOC_GET_ENCRYPTION_POLICY { "FS_IOC_GET_ENCRYPTION_POLICY", (unsigned long) FS_IOC_GET_ENCRYPTION_POLICY }, #endif // FS_IOC_GET_ENCRYPTION_POLICY #ifdef FS_IOC_GET_ENCRYPTION_PWSALT { "FS_IOC_GET_ENCRYPTION_PWSALT", (unsigned long) FS_IOC_GET_ENCRYPTION_PWSALT }, #endif // FS_IOC_GET_ENCRYPTION_PWSALT #ifdef FS_IOC_GETFLAGS { "FS_IOC_GETFLAGS", (unsigned long) FS_IOC_GETFLAGS }, #endif // FS_IOC_GETFLAGS #ifdef FS_IOC_GETFSLABEL { "FS_IOC_GETFSLABEL", (unsigned long) FS_IOC_GETFSLABEL }, #endif // FS_IOC_GETFSLABEL #ifdef FS_IOC_GETFSSYSFSPATH { "FS_IOC_GETFSSYSFSPATH", (unsigned long) FS_IOC_GETFSSYSFSPATH }, #endif // FS_IOC_GETFSSYSFSPATH #ifdef FS_IOC_GETFSUUID { "FS_IOC_GETFSUUID", (unsigned long) FS_IOC_GETFSUUID }, #endif // FS_IOC_GETFSUUID #ifdef FS_IOC_GETLBMD_CAP { "FS_IOC_GETLBMD_CAP", (unsigned long) FS_IOC_GETLBMD_CAP }, #endif // FS_IOC_GETLBMD_CAP #ifdef FS_IOC_GETVERSION { "FS_IOC_GETVERSION", (unsigned long) FS_IOC_GETVERSION }, #endif // FS_IOC_GETVERSION #ifdef FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS { "FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS", (unsigned long) FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS }, #endif // FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS #ifdef FS_IOC_REMOVE_ENCRYPTION_KEY { "FS_IOC_REMOVE_ENCRYPTION_KEY", (unsigned long) FS_IOC_REMOVE_ENCRYPTION_KEY }, #endif // FS_IOC_REMOVE_ENCRYPTION_KEY #ifdef FS_IOC_SET_ENCRYPTION_POLICY { "FS_IOC_SET_ENCRYPTION_POLICY", (unsigned long) FS_IOC_SET_ENCRYPTION_POLICY }, #endif // FS_IOC_SET_ENCRYPTION_POLICY #ifdef FS_IOC_SETFLAGS { "FS_IOC_SETFLAGS", (unsigned long) FS_IOC_SETFLAGS }, #endif // FS_IOC_SETFLAGS #ifdef FS_IOC_SETFSLABEL { "FS_IOC_SETFSLABEL", (unsigned long) FS_IOC_SETFSLABEL }, #endif // FS_IOC_SETFSLABEL #ifdef FS_IOC_SETVERSION { "FS_IOC_SETVERSION", (unsigned long) FS_IOC_SETVERSION }, #endif // FS_IOC_SETVERSION #ifdef FS_JOURNAL_DATA_FL { "FS_JOURNAL_DATA_FL", (unsigned long) FS_JOURNAL_DATA_FL }, #endif // FS_JOURNAL_DATA_FL #ifdef FS_KEY_DESC_PREFIX { "FS_KEY_DESC_PREFIX", (unsigned long) FS_KEY_DESC_PREFIX }, #endif // FS_KEY_DESC_PREFIX #ifdef FS_KEY_DESC_PREFIX_SIZE { "FS_KEY_DESC_PREFIX_SIZE", (unsigned long) FS_KEY_DESC_PREFIX_SIZE }, #endif // FS_KEY_DESC_PREFIX_SIZE #ifdef FS_KEY_DESCRIPTOR_SIZE { "FS_KEY_DESCRIPTOR_SIZE", (unsigned long) FS_KEY_DESCRIPTOR_SIZE }, #endif // FS_KEY_DESCRIPTOR_SIZE #ifdef FSLABEL_MAX { "FSLABEL_MAX", (unsigned long) FSLABEL_MAX }, #endif // FSLABEL_MAX #ifdef FS_MAX_KEY_SIZE { "FS_MAX_KEY_SIZE", (unsigned long) FS_MAX_KEY_SIZE }, #endif // FS_MAX_KEY_SIZE #ifdef FSMOUNT_CLOEXEC { "FSMOUNT_CLOEXEC", (unsigned long) FSMOUNT_CLOEXEC }, #endif // FSMOUNT_CLOEXEC #ifdef FS_NOATIME_FL { "FS_NOATIME_FL", (unsigned long) FS_NOATIME_FL }, #endif // FS_NOATIME_FL #ifdef FS_NOCOMP_FL { "FS_NOCOMP_FL", (unsigned long) FS_NOCOMP_FL }, #endif // FS_NOCOMP_FL #ifdef FS_NOCOW_FL { "FS_NOCOW_FL", (unsigned long) FS_NOCOW_FL }, #endif // FS_NOCOW_FL #ifdef FS_NODUMP_FL { "FS_NODUMP_FL", (unsigned long) FS_NODUMP_FL }, #endif // FS_NODUMP_FL #ifdef FS_NOTAIL_FL { "FS_NOTAIL_FL", (unsigned long) FS_NOTAIL_FL }, #endif // FS_NOTAIL_FL #ifdef FSOPEN_CLOEXEC { "FSOPEN_CLOEXEC", (unsigned long) FSOPEN_CLOEXEC }, #endif // FSOPEN_CLOEXEC #ifdef FSPICK_CLOEXEC { "FSPICK_CLOEXEC", (unsigned long) FSPICK_CLOEXEC }, #endif // FSPICK_CLOEXEC #ifdef FSPICK_EMPTY_PATH { "FSPICK_EMPTY_PATH", (unsigned long) FSPICK_EMPTY_PATH }, #endif // FSPICK_EMPTY_PATH #ifdef FSPICK_NO_AUTOMOUNT { "FSPICK_NO_AUTOMOUNT", (unsigned long) FSPICK_NO_AUTOMOUNT }, #endif // FSPICK_NO_AUTOMOUNT #ifdef FSPICK_SYMLINK_NOFOLLOW { "FSPICK_SYMLINK_NOFOLLOW", (unsigned long) FSPICK_SYMLINK_NOFOLLOW }, #endif // FSPICK_SYMLINK_NOFOLLOW #ifdef FS_POLICY_FLAG_DIRECT_KEY { "FS_POLICY_FLAG_DIRECT_KEY", (unsigned long) FS_POLICY_FLAG_DIRECT_KEY }, #endif // FS_POLICY_FLAG_DIRECT_KEY #ifdef FS_POLICY_FLAGS_PAD_16 { "FS_POLICY_FLAGS_PAD_16", (unsigned long) FS_POLICY_FLAGS_PAD_16 }, #endif // FS_POLICY_FLAGS_PAD_16 #ifdef FS_POLICY_FLAGS_PAD_32 { "FS_POLICY_FLAGS_PAD_32", (unsigned long) FS_POLICY_FLAGS_PAD_32 }, #endif // FS_POLICY_FLAGS_PAD_32 #ifdef FS_POLICY_FLAGS_PAD_4 { "FS_POLICY_FLAGS_PAD_4", (unsigned long) FS_POLICY_FLAGS_PAD_4 }, #endif // FS_POLICY_FLAGS_PAD_4 #ifdef FS_POLICY_FLAGS_PAD_8 { "FS_POLICY_FLAGS_PAD_8", (unsigned long) FS_POLICY_FLAGS_PAD_8 }, #endif // FS_POLICY_FLAGS_PAD_8 #ifdef FS_POLICY_FLAGS_PAD_MASK { "FS_POLICY_FLAGS_PAD_MASK", (unsigned long) FS_POLICY_FLAGS_PAD_MASK }, #endif // FS_POLICY_FLAGS_PAD_MASK #ifdef FS_POLICY_FLAGS_VALID { "FS_POLICY_FLAGS_VALID", (unsigned long) FS_POLICY_FLAGS_VALID }, #endif // FS_POLICY_FLAGS_VALID #ifdef FS_PROJINHERIT_FL { "FS_PROJINHERIT_FL", (unsigned long) FS_PROJINHERIT_FL }, #endif // FS_PROJINHERIT_FL #ifdef FS_RESERVED_FL { "FS_RESERVED_FL", (unsigned long) FS_RESERVED_FL }, #endif // FS_RESERVED_FL #ifdef FS_SECRM_FL { "FS_SECRM_FL", (unsigned long) FS_SECRM_FL }, #endif // FS_SECRM_FL #ifdef FS_SYNC_FL { "FS_SYNC_FL", (unsigned long) FS_SYNC_FL }, #endif // FS_SYNC_FL #ifdef FS_TOPDIR_FL { "FS_TOPDIR_FL", (unsigned long) FS_TOPDIR_FL }, #endif // FS_TOPDIR_FL #ifdef FS_UNRM_FL { "FS_UNRM_FL", (unsigned long) FS_UNRM_FL }, #endif // FS_UNRM_FL #ifdef FS_VERITY_FL { "FS_VERITY_FL", (unsigned long) FS_VERITY_FL }, #endif // FS_VERITY_FL #ifdef FS_XFLAG_APPEND { "FS_XFLAG_APPEND", (unsigned long) FS_XFLAG_APPEND }, #endif // FS_XFLAG_APPEND #ifdef FS_XFLAG_COWEXTSIZE { "FS_XFLAG_COWEXTSIZE", (unsigned long) FS_XFLAG_COWEXTSIZE }, #endif // FS_XFLAG_COWEXTSIZE #ifdef FS_XFLAG_DAX { "FS_XFLAG_DAX", (unsigned long) FS_XFLAG_DAX }, #endif // FS_XFLAG_DAX #ifdef FS_XFLAG_EXTSIZE { "FS_XFLAG_EXTSIZE", (unsigned long) FS_XFLAG_EXTSIZE }, #endif // FS_XFLAG_EXTSIZE #ifdef FS_XFLAG_EXTSZINHERIT { "FS_XFLAG_EXTSZINHERIT", (unsigned long) FS_XFLAG_EXTSZINHERIT }, #endif // FS_XFLAG_EXTSZINHERIT #ifdef FS_XFLAG_FILESTREAM { "FS_XFLAG_FILESTREAM", (unsigned long) FS_XFLAG_FILESTREAM }, #endif // FS_XFLAG_FILESTREAM #ifdef FS_XFLAG_HASATTR { "FS_XFLAG_HASATTR", (unsigned long) FS_XFLAG_HASATTR }, #endif // FS_XFLAG_HASATTR #ifdef FS_XFLAG_IMMUTABLE { "FS_XFLAG_IMMUTABLE", (unsigned long) FS_XFLAG_IMMUTABLE }, #endif // FS_XFLAG_IMMUTABLE #ifdef FS_XFLAG_NOATIME { "FS_XFLAG_NOATIME", (unsigned long) FS_XFLAG_NOATIME }, #endif // FS_XFLAG_NOATIME #ifdef FS_XFLAG_NODEFRAG { "FS_XFLAG_NODEFRAG", (unsigned long) FS_XFLAG_NODEFRAG }, #endif // FS_XFLAG_NODEFRAG #ifdef FS_XFLAG_NODUMP { "FS_XFLAG_NODUMP", (unsigned long) FS_XFLAG_NODUMP }, #endif // FS_XFLAG_NODUMP #ifdef FS_XFLAG_NOSYMLINKS { "FS_XFLAG_NOSYMLINKS", (unsigned long) FS_XFLAG_NOSYMLINKS }, #endif // FS_XFLAG_NOSYMLINKS #ifdef FS_XFLAG_PREALLOC { "FS_XFLAG_PREALLOC", (unsigned long) FS_XFLAG_PREALLOC }, #endif // FS_XFLAG_PREALLOC #ifdef FS_XFLAG_PROJINHERIT { "FS_XFLAG_PROJINHERIT", (unsigned long) FS_XFLAG_PROJINHERIT }, #endif // FS_XFLAG_PROJINHERIT #ifdef FS_XFLAG_REALTIME { "FS_XFLAG_REALTIME", (unsigned long) FS_XFLAG_REALTIME }, #endif // FS_XFLAG_REALTIME #ifdef FS_XFLAG_RTINHERIT { "FS_XFLAG_RTINHERIT", (unsigned long) FS_XFLAG_RTINHERIT }, #endif // FS_XFLAG_RTINHERIT #ifdef FS_XFLAG_SYNC { "FS_XFLAG_SYNC", (unsigned long) FS_XFLAG_SYNC }, #endif // FS_XFLAG_SYNC #ifdef FTD_MSG { "FTD_MSG", (unsigned long) FTD_MSG }, #endif // FTD_MSG #ifdef F_TEST { "F_TEST", (unsigned long) F_TEST }, #endif // F_TEST #ifdef F_TLOCK { "F_TLOCK", (unsigned long) F_TLOCK }, #endif // F_TLOCK #ifdef F_ULOCK { "F_ULOCK", (unsigned long) F_ULOCK }, #endif // F_ULOCK #ifdef F_UNLCK { "F_UNLCK", (unsigned long) F_UNLCK }, #endif // F_UNLCK #ifdef F_WRLCK { "F_WRLCK", (unsigned long) F_WRLCK }, #endif // F_WRLCK #ifdef GRND_INSECURE { "GRND_INSECURE", (unsigned long) GRND_INSECURE }, #endif // GRND_INSECURE #ifdef GRND_NONBLOCK { "GRND_NONBLOCK", (unsigned long) GRND_NONBLOCK }, #endif // GRND_NONBLOCK #ifdef GRND_RANDOM { "GRND_RANDOM", (unsigned long) GRND_RANDOM }, #endif // GRND_RANDOM #ifdef HUGETLB_FLAG_ENCODE_MASK { "HUGETLB_FLAG_ENCODE_MASK", (unsigned long) HUGETLB_FLAG_ENCODE_MASK }, #endif // HUGETLB_FLAG_ENCODE_MASK #ifdef HUGETLB_FLAG_ENCODE_SHIFT { "HUGETLB_FLAG_ENCODE_SHIFT", (unsigned long) HUGETLB_FLAG_ENCODE_SHIFT }, #endif // HUGETLB_FLAG_ENCODE_SHIFT #ifdef HUPCL { "HUPCL", (unsigned long) HUPCL }, #endif // HUPCL #ifdef IBSHIFT { "IBSHIFT", (unsigned long) IBSHIFT }, #endif // IBSHIFT #ifdef ICANON { "ICANON", (unsigned long) ICANON }, #endif // ICANON #ifdef ICRNL { "ICRNL", (unsigned long) ICRNL }, #endif // ICRNL #ifdef IEXTEN { "IEXTEN", (unsigned long) IEXTEN }, #endif // IEXTEN #ifdef IGNBRK { "IGNBRK", (unsigned long) IGNBRK }, #endif // IGNBRK #ifdef IGNCR { "IGNCR", (unsigned long) IGNCR }, #endif // IGNCR #ifdef IGNPAR { "IGNPAR", (unsigned long) IGNPAR }, #endif // IGNPAR #ifdef ILL_BADIADDR { "ILL_BADIADDR", (unsigned long) ILL_BADIADDR }, #endif // ILL_BADIADDR #ifdef ILL_BADSTK { "ILL_BADSTK", (unsigned long) ILL_BADSTK }, #endif // ILL_BADSTK #ifdef ILL_COPROC { "ILL_COPROC", (unsigned long) ILL_COPROC }, #endif // ILL_COPROC #ifdef ILL_ILLADR { "ILL_ILLADR", (unsigned long) ILL_ILLADR }, #endif // ILL_ILLADR #ifdef ILL_ILLOPC { "ILL_ILLOPC", (unsigned long) ILL_ILLOPC }, #endif // ILL_ILLOPC #ifdef ILL_ILLOPN { "ILL_ILLOPN", (unsigned long) ILL_ILLOPN }, #endif // ILL_ILLOPN #ifdef ILL_ILLTRP { "ILL_ILLTRP", (unsigned long) ILL_ILLTRP }, #endif // ILL_ILLTRP #ifdef ILL_PRVOPC { "ILL_PRVOPC", (unsigned long) ILL_PRVOPC }, #endif // ILL_PRVOPC #ifdef ILL_PRVREG { "ILL_PRVREG", (unsigned long) ILL_PRVREG }, #endif // ILL_PRVREG #ifdef IMAXBEL { "IMAXBEL", (unsigned long) IMAXBEL }, #endif // IMAXBEL #ifdef INLCR { "INLCR", (unsigned long) INLCR }, #endif // INLCR #ifdef INPCK { "INPCK", (unsigned long) INPCK }, #endif // INPCK #ifdef INR_OPEN_CUR { "INR_OPEN_CUR", (unsigned long) INR_OPEN_CUR }, #endif // INR_OPEN_CUR #ifdef INR_OPEN_MAX { "INR_OPEN_MAX", (unsigned long) INR_OPEN_MAX }, #endif // INR_OPEN_MAX #ifdef INT16_WIDTH { "INT16_WIDTH", (unsigned long) INT16_WIDTH }, #endif // INT16_WIDTH #ifdef INT32_WIDTH { "INT32_WIDTH", (unsigned long) INT32_WIDTH }, #endif // INT32_WIDTH #ifdef INT64_WIDTH { "INT64_WIDTH", (unsigned long) INT64_WIDTH }, #endif // INT64_WIDTH #ifdef INT8_WIDTH { "INT8_WIDTH", (unsigned long) INT8_WIDTH }, #endif // INT8_WIDTH #ifdef INT_FAST16_WIDTH { "INT_FAST16_WIDTH", (unsigned long) INT_FAST16_WIDTH }, #endif // INT_FAST16_WIDTH #ifdef INT_FAST32_WIDTH { "INT_FAST32_WIDTH", (unsigned long) INT_FAST32_WIDTH }, #endif // INT_FAST32_WIDTH #ifdef INT_FAST64_WIDTH { "INT_FAST64_WIDTH", (unsigned long) INT_FAST64_WIDTH }, #endif // INT_FAST64_WIDTH #ifdef INT_FAST8_WIDTH { "INT_FAST8_WIDTH", (unsigned long) INT_FAST8_WIDTH }, #endif // INT_FAST8_WIDTH #ifdef INT_LEAST16_WIDTH { "INT_LEAST16_WIDTH", (unsigned long) INT_LEAST16_WIDTH }, #endif // INT_LEAST16_WIDTH #ifdef INT_LEAST32_WIDTH { "INT_LEAST32_WIDTH", (unsigned long) INT_LEAST32_WIDTH }, #endif // INT_LEAST32_WIDTH #ifdef INT_LEAST64_WIDTH { "INT_LEAST64_WIDTH", (unsigned long) INT_LEAST64_WIDTH }, #endif // INT_LEAST64_WIDTH #ifdef INT_LEAST8_WIDTH { "INT_LEAST8_WIDTH", (unsigned long) INT_LEAST8_WIDTH }, #endif // INT_LEAST8_WIDTH #ifdef INTMAX_WIDTH { "INTMAX_WIDTH", (unsigned long) INTMAX_WIDTH }, #endif // INTMAX_WIDTH #ifdef INTPTR_WIDTH { "INTPTR_WIDTH", (unsigned long) INTPTR_WIDTH }, #endif // INTPTR_WIDTH #ifdef ISIG { "ISIG", (unsigned long) ISIG }, #endif // ISIG #ifdef ISTRIP { "ISTRIP", (unsigned long) ISTRIP }, #endif // ISTRIP #ifdef IUCLC { "IUCLC", (unsigned long) IUCLC }, #endif // IUCLC #ifdef IUTF8 { "IUTF8", (unsigned long) IUTF8 }, #endif // IUTF8 #ifdef IXANY { "IXANY", (unsigned long) IXANY }, #endif // IXANY #ifdef IXOFF { "IXOFF", (unsigned long) IXOFF }, #endif // IXOFF #ifdef IXON { "IXON", (unsigned long) IXON }, #endif // IXON #ifdef LBMD_PI_CSUM_CRC16_T10DIF { "LBMD_PI_CSUM_CRC16_T10DIF", (unsigned long) LBMD_PI_CSUM_CRC16_T10DIF }, #endif // LBMD_PI_CSUM_CRC16_T10DIF #ifdef LBMD_PI_CSUM_CRC64_NVME { "LBMD_PI_CSUM_CRC64_NVME", (unsigned long) LBMD_PI_CSUM_CRC64_NVME }, #endif // LBMD_PI_CSUM_CRC64_NVME #ifdef LBMD_PI_CSUM_IP { "LBMD_PI_CSUM_IP", (unsigned long) LBMD_PI_CSUM_IP }, #endif // LBMD_PI_CSUM_IP #ifdef LBMD_PI_CSUM_NONE { "LBMD_PI_CSUM_NONE", (unsigned long) LBMD_PI_CSUM_NONE }, #endif // LBMD_PI_CSUM_NONE #ifdef LBMD_SIZE_VER0 { "LBMD_SIZE_VER0", (unsigned long) LBMD_SIZE_VER0 }, #endif // LBMD_SIZE_VER0 #ifdef LINK_MAX { "LINK_MAX", (unsigned long) LINK_MAX }, #endif // LINK_MAX #ifdef LITTLE_ENDIAN { "LITTLE_ENDIAN", (unsigned long) LITTLE_ENDIAN }, #endif // LITTLE_ENDIAN #ifdef LOCK_EX { "LOCK_EX", (unsigned long) LOCK_EX }, #endif // LOCK_EX #ifdef LOCK_NB { "LOCK_NB", (unsigned long) LOCK_NB }, #endif // LOCK_NB #ifdef LOCK_SH { "LOCK_SH", (unsigned long) LOCK_SH }, #endif // LOCK_SH #ifdef LOCK_UN { "LOCK_UN", (unsigned long) LOCK_UN }, #endif // LOCK_UN #ifdef LO_CRYPT_BLOW { "LO_CRYPT_BLOW", (unsigned long) LO_CRYPT_BLOW }, #endif // LO_CRYPT_BLOW #ifdef LO_CRYPT_CAST128 { "LO_CRYPT_CAST128", (unsigned long) LO_CRYPT_CAST128 }, #endif // LO_CRYPT_CAST128 #ifdef LO_CRYPT_CRYPTOAPI { "LO_CRYPT_CRYPTOAPI", (unsigned long) LO_CRYPT_CRYPTOAPI }, #endif // LO_CRYPT_CRYPTOAPI #ifdef LO_CRYPT_DES { "LO_CRYPT_DES", (unsigned long) LO_CRYPT_DES }, #endif // LO_CRYPT_DES #ifdef LO_CRYPT_DUMMY { "LO_CRYPT_DUMMY", (unsigned long) LO_CRYPT_DUMMY }, #endif // LO_CRYPT_DUMMY #ifdef LO_CRYPT_FISH2 { "LO_CRYPT_FISH2", (unsigned long) LO_CRYPT_FISH2 }, #endif // LO_CRYPT_FISH2 #ifdef LO_CRYPT_IDEA { "LO_CRYPT_IDEA", (unsigned long) LO_CRYPT_IDEA }, #endif // LO_CRYPT_IDEA #ifdef LO_CRYPT_NONE { "LO_CRYPT_NONE", (unsigned long) LO_CRYPT_NONE }, #endif // LO_CRYPT_NONE #ifdef LO_CRYPT_SKIPJACK { "LO_CRYPT_SKIPJACK", (unsigned long) LO_CRYPT_SKIPJACK }, #endif // LO_CRYPT_SKIPJACK #ifdef LO_CRYPT_XOR { "LO_CRYPT_XOR", (unsigned long) LO_CRYPT_XOR }, #endif // LO_CRYPT_XOR #ifdef LO_KEY_SIZE { "LO_KEY_SIZE", (unsigned long) LO_KEY_SIZE }, #endif // LO_KEY_SIZE #ifdef LO_NAME_SIZE { "LO_NAME_SIZE", (unsigned long) LO_NAME_SIZE }, #endif // LO_NAME_SIZE #ifdef LOOP_CHANGE_FD { "LOOP_CHANGE_FD", (unsigned long) LOOP_CHANGE_FD }, #endif // LOOP_CHANGE_FD #ifdef LOOP_CLR_FD { "LOOP_CLR_FD", (unsigned long) LOOP_CLR_FD }, #endif // LOOP_CLR_FD #ifdef LOOP_CONFIGURE { "LOOP_CONFIGURE", (unsigned long) LOOP_CONFIGURE }, #endif // LOOP_CONFIGURE #ifdef LOOP_CTL_ADD { "LOOP_CTL_ADD", (unsigned long) LOOP_CTL_ADD }, #endif // LOOP_CTL_ADD #ifdef LOOP_CTL_GET_FREE { "LOOP_CTL_GET_FREE", (unsigned long) LOOP_CTL_GET_FREE }, #endif // LOOP_CTL_GET_FREE #ifdef LOOP_CTL_REMOVE { "LOOP_CTL_REMOVE", (unsigned long) LOOP_CTL_REMOVE }, #endif // LOOP_CTL_REMOVE #ifdef LOOP_GET_STATUS { "LOOP_GET_STATUS", (unsigned long) LOOP_GET_STATUS }, #endif // LOOP_GET_STATUS #ifdef LOOP_GET_STATUS64 { "LOOP_GET_STATUS64", (unsigned long) LOOP_GET_STATUS64 }, #endif // LOOP_GET_STATUS64 #ifdef LOOP_SET_BLOCK_SIZE { "LOOP_SET_BLOCK_SIZE", (unsigned long) LOOP_SET_BLOCK_SIZE }, #endif // LOOP_SET_BLOCK_SIZE #ifdef LOOP_SET_CAPACITY { "LOOP_SET_CAPACITY", (unsigned long) LOOP_SET_CAPACITY }, #endif // LOOP_SET_CAPACITY #ifdef LOOP_SET_DIRECT_IO { "LOOP_SET_DIRECT_IO", (unsigned long) LOOP_SET_DIRECT_IO }, #endif // LOOP_SET_DIRECT_IO #ifdef LOOP_SET_FD { "LOOP_SET_FD", (unsigned long) LOOP_SET_FD }, #endif // LOOP_SET_FD #ifdef LOOP_SET_STATUS { "LOOP_SET_STATUS", (unsigned long) LOOP_SET_STATUS }, #endif // LOOP_SET_STATUS #ifdef LOOP_SET_STATUS64 { "LOOP_SET_STATUS64", (unsigned long) LOOP_SET_STATUS64 }, #endif // LOOP_SET_STATUS64 #ifdef LSMT_ROOT { "LSMT_ROOT", (unsigned long) LSMT_ROOT }, #endif // LSMT_ROOT #ifdef MADV_COLD { "MADV_COLD", (unsigned long) MADV_COLD }, #endif // MADV_COLD #ifdef MADV_COLLAPSE { "MADV_COLLAPSE", (unsigned long) MADV_COLLAPSE }, #endif // MADV_COLLAPSE #ifdef MADV_DODUMP { "MADV_DODUMP", (unsigned long) MADV_DODUMP }, #endif // MADV_DODUMP #ifdef MADV_DOFORK { "MADV_DOFORK", (unsigned long) MADV_DOFORK }, #endif // MADV_DOFORK #ifdef MADV_DONTDUMP { "MADV_DONTDUMP", (unsigned long) MADV_DONTDUMP }, #endif // MADV_DONTDUMP #ifdef MADV_DONTFORK { "MADV_DONTFORK", (unsigned long) MADV_DONTFORK }, #endif // MADV_DONTFORK #ifdef MADV_DONTNEED { "MADV_DONTNEED", (unsigned long) MADV_DONTNEED }, #endif // MADV_DONTNEED #ifdef MADV_DONTNEED_LOCKED { "MADV_DONTNEED_LOCKED", (unsigned long) MADV_DONTNEED_LOCKED }, #endif // MADV_DONTNEED_LOCKED #ifdef MADV_FREE { "MADV_FREE", (unsigned long) MADV_FREE }, #endif // MADV_FREE #ifdef MADV_GUARD_INSTALL { "MADV_GUARD_INSTALL", (unsigned long) MADV_GUARD_INSTALL }, #endif // MADV_GUARD_INSTALL #ifdef MADV_GUARD_REMOVE { "MADV_GUARD_REMOVE", (unsigned long) MADV_GUARD_REMOVE }, #endif // MADV_GUARD_REMOVE #ifdef MADV_HUGEPAGE { "MADV_HUGEPAGE", (unsigned long) MADV_HUGEPAGE }, #endif // MADV_HUGEPAGE #ifdef MADV_HWPOISON { "MADV_HWPOISON", (unsigned long) MADV_HWPOISON }, #endif // MADV_HWPOISON #ifdef MADV_KEEPONFORK { "MADV_KEEPONFORK", (unsigned long) MADV_KEEPONFORK }, #endif // MADV_KEEPONFORK #ifdef MADV_MERGEABLE { "MADV_MERGEABLE", (unsigned long) MADV_MERGEABLE }, #endif // MADV_MERGEABLE #ifdef MADV_NOHUGEPAGE { "MADV_NOHUGEPAGE", (unsigned long) MADV_NOHUGEPAGE }, #endif // MADV_NOHUGEPAGE #ifdef MADV_NORMAL { "MADV_NORMAL", (unsigned long) MADV_NORMAL }, #endif // MADV_NORMAL #ifdef MADV_PAGEOUT { "MADV_PAGEOUT", (unsigned long) MADV_PAGEOUT }, #endif // MADV_PAGEOUT #ifdef MADV_POPULATE_READ { "MADV_POPULATE_READ", (unsigned long) MADV_POPULATE_READ }, #endif // MADV_POPULATE_READ #ifdef MADV_POPULATE_WRITE { "MADV_POPULATE_WRITE", (unsigned long) MADV_POPULATE_WRITE }, #endif // MADV_POPULATE_WRITE #ifdef MADV_RANDOM { "MADV_RANDOM", (unsigned long) MADV_RANDOM }, #endif // MADV_RANDOM #ifdef MADV_REMOVE { "MADV_REMOVE", (unsigned long) MADV_REMOVE }, #endif // MADV_REMOVE #ifdef MADV_SEQUENTIAL { "MADV_SEQUENTIAL", (unsigned long) MADV_SEQUENTIAL }, #endif // MADV_SEQUENTIAL #ifdef MADV_SOFT_OFFLINE { "MADV_SOFT_OFFLINE", (unsigned long) MADV_SOFT_OFFLINE }, #endif // MADV_SOFT_OFFLINE #ifdef MADV_UNMERGEABLE { "MADV_UNMERGEABLE", (unsigned long) MADV_UNMERGEABLE }, #endif // MADV_UNMERGEABLE #ifdef MADV_WILLNEED { "MADV_WILLNEED", (unsigned long) MADV_WILLNEED }, #endif // MADV_WILLNEED #ifdef MADV_WIPEONFORK { "MADV_WIPEONFORK", (unsigned long) MADV_WIPEONFORK }, #endif // MADV_WIPEONFORK #ifdef MAP_32BIT { "MAP_32BIT", (unsigned long) MAP_32BIT }, #endif // MAP_32BIT #ifdef MAP_ABOVE4G { "MAP_ABOVE4G", (unsigned long) MAP_ABOVE4G }, #endif // MAP_ABOVE4G #ifdef MAP_ANON { "MAP_ANON", (unsigned long) MAP_ANON }, #endif // MAP_ANON #ifdef MAP_ANONYMOUS { "MAP_ANONYMOUS", (unsigned long) MAP_ANONYMOUS }, #endif // MAP_ANONYMOUS #ifdef MAP_DENYWRITE { "MAP_DENYWRITE", (unsigned long) MAP_DENYWRITE }, #endif // MAP_DENYWRITE #ifdef MAP_DENYWRITE { "MAP_DENYWRITE", (unsigned long) MAP_DENYWRITE }, #endif // MAP_DENYWRITE #ifdef MAP_DROPPABLE { "MAP_DROPPABLE", (unsigned long) MAP_DROPPABLE }, #endif // MAP_DROPPABLE #ifdef MAP_EXECUTABLE { "MAP_EXECUTABLE", (unsigned long) MAP_EXECUTABLE }, #endif // MAP_EXECUTABLE #ifdef MAP_EXECUTABLE { "MAP_EXECUTABLE", (unsigned long) MAP_EXECUTABLE }, #endif // MAP_EXECUTABLE #ifdef MAP_FILE { "MAP_FILE", (unsigned long) MAP_FILE }, #endif // MAP_FILE #ifdef MAP_FIXED { "MAP_FIXED", (unsigned long) MAP_FIXED }, #endif // MAP_FIXED #ifdef MAP_FIXED_NOREPLACE { "MAP_FIXED_NOREPLACE", (unsigned long) MAP_FIXED_NOREPLACE }, #endif // MAP_FIXED_NOREPLACE #ifdef MAP_GROWSDOWN { "MAP_GROWSDOWN", (unsigned long) MAP_GROWSDOWN }, #endif // MAP_GROWSDOWN #ifdef MAP_GROWSDOWN { "MAP_GROWSDOWN", (unsigned long) MAP_GROWSDOWN }, #endif // MAP_GROWSDOWN #ifdef MAP_HUGE_16GB { "MAP_HUGE_16GB", (unsigned long) MAP_HUGE_16GB }, #endif // MAP_HUGE_16GB #ifdef MAP_HUGE_16KB { "MAP_HUGE_16KB", (unsigned long) MAP_HUGE_16KB }, #endif // MAP_HUGE_16KB #ifdef MAP_HUGE_16MB { "MAP_HUGE_16MB", (unsigned long) MAP_HUGE_16MB }, #endif // MAP_HUGE_16MB #ifdef MAP_HUGE_1GB { "MAP_HUGE_1GB", (unsigned long) MAP_HUGE_1GB }, #endif // MAP_HUGE_1GB #ifdef MAP_HUGE_1MB { "MAP_HUGE_1MB", (unsigned long) MAP_HUGE_1MB }, #endif // MAP_HUGE_1MB #ifdef MAP_HUGE_256MB { "MAP_HUGE_256MB", (unsigned long) MAP_HUGE_256MB }, #endif // MAP_HUGE_256MB #ifdef MAP_HUGE_2GB { "MAP_HUGE_2GB", (unsigned long) MAP_HUGE_2GB }, #endif // MAP_HUGE_2GB #ifdef MAP_HUGE_2MB { "MAP_HUGE_2MB", (unsigned long) MAP_HUGE_2MB }, #endif // MAP_HUGE_2MB #ifdef MAP_HUGE_32MB { "MAP_HUGE_32MB", (unsigned long) MAP_HUGE_32MB }, #endif // MAP_HUGE_32MB #ifdef MAP_HUGE_512KB { "MAP_HUGE_512KB", (unsigned long) MAP_HUGE_512KB }, #endif // MAP_HUGE_512KB #ifdef MAP_HUGE_512MB { "MAP_HUGE_512MB", (unsigned long) MAP_HUGE_512MB }, #endif // MAP_HUGE_512MB #ifdef MAP_HUGE_64KB { "MAP_HUGE_64KB", (unsigned long) MAP_HUGE_64KB }, #endif // MAP_HUGE_64KB #ifdef MAP_HUGE_8MB { "MAP_HUGE_8MB", (unsigned long) MAP_HUGE_8MB }, #endif // MAP_HUGE_8MB #ifdef MAP_HUGE_MASK { "MAP_HUGE_MASK", (unsigned long) MAP_HUGE_MASK }, #endif // MAP_HUGE_MASK #ifdef MAP_HUGE_MASK { "MAP_HUGE_MASK", (unsigned long) MAP_HUGE_MASK }, #endif // MAP_HUGE_MASK #ifdef MAP_HUGE_SHIFT { "MAP_HUGE_SHIFT", (unsigned long) MAP_HUGE_SHIFT }, #endif // MAP_HUGE_SHIFT #ifdef MAP_HUGE_SHIFT { "MAP_HUGE_SHIFT", (unsigned long) MAP_HUGE_SHIFT }, #endif // MAP_HUGE_SHIFT #ifdef MAP_HUGETLB { "MAP_HUGETLB", (unsigned long) MAP_HUGETLB }, #endif // MAP_HUGETLB #ifdef MAP_HUGETLB { "MAP_HUGETLB", (unsigned long) MAP_HUGETLB }, #endif // MAP_HUGETLB #ifdef MAP_LOCKED { "MAP_LOCKED", (unsigned long) MAP_LOCKED }, #endif // MAP_LOCKED #ifdef MAP_LOCKED { "MAP_LOCKED", (unsigned long) MAP_LOCKED }, #endif // MAP_LOCKED #ifdef MAP_NONBLOCK { "MAP_NONBLOCK", (unsigned long) MAP_NONBLOCK }, #endif // MAP_NONBLOCK #ifdef MAP_NONBLOCK { "MAP_NONBLOCK", (unsigned long) MAP_NONBLOCK }, #endif // MAP_NONBLOCK #ifdef MAP_NORESERVE { "MAP_NORESERVE", (unsigned long) MAP_NORESERVE }, #endif // MAP_NORESERVE #ifdef MAP_NORESERVE { "MAP_NORESERVE", (unsigned long) MAP_NORESERVE }, #endif // MAP_NORESERVE #ifdef MAP_POPULATE { "MAP_POPULATE", (unsigned long) MAP_POPULATE }, #endif // MAP_POPULATE #ifdef MAP_POPULATE { "MAP_POPULATE", (unsigned long) MAP_POPULATE }, #endif // MAP_POPULATE #ifdef MAP_PRIVATE { "MAP_PRIVATE", (unsigned long) MAP_PRIVATE }, #endif // MAP_PRIVATE #ifdef MAP_SHARED { "MAP_SHARED", (unsigned long) MAP_SHARED }, #endif // MAP_SHARED #ifdef MAP_SHARED_VALIDATE { "MAP_SHARED_VALIDATE", (unsigned long) MAP_SHARED_VALIDATE }, #endif // MAP_SHARED_VALIDATE #ifdef MAP_STACK { "MAP_STACK", (unsigned long) MAP_STACK }, #endif // MAP_STACK #ifdef MAP_STACK { "MAP_STACK", (unsigned long) MAP_STACK }, #endif // MAP_STACK #ifdef MAP_SYNC { "MAP_SYNC", (unsigned long) MAP_SYNC }, #endif // MAP_SYNC #ifdef MAP_SYNC { "MAP_SYNC", (unsigned long) MAP_SYNC }, #endif // MAP_SYNC #ifdef MAP_TYPE { "MAP_TYPE", (unsigned long) MAP_TYPE }, #endif // MAP_TYPE #ifdef MAP_UNINITIALIZED { "MAP_UNINITIALIZED", (unsigned long) MAP_UNINITIALIZED }, #endif // MAP_UNINITIALIZED #ifdef MAX_CANON { "MAX_CANON", (unsigned long) MAX_CANON }, #endif // MAX_CANON #ifdef MAX_INPUT { "MAX_INPUT", (unsigned long) MAX_INPUT }, #endif // MAX_INPUT #ifdef MAX_LO_CRYPT { "MAX_LO_CRYPT", (unsigned long) MAX_LO_CRYPT }, #endif // MAX_LO_CRYPT #ifdef MCL_CURRENT { "MCL_CURRENT", (unsigned long) MCL_CURRENT }, #endif // MCL_CURRENT #ifdef MCL_FUTURE { "MCL_FUTURE", (unsigned long) MCL_FUTURE }, #endif // MCL_FUTURE #ifdef MCL_ONFAULT { "MCL_ONFAULT", (unsigned long) MCL_ONFAULT }, #endif // MCL_ONFAULT #ifdef MINIJAIL_ARCH_BITS { "MINIJAIL_ARCH_BITS", (unsigned long) MINIJAIL_ARCH_BITS }, #endif // MINIJAIL_ARCH_BITS #ifdef MINIJAIL_ARCH_NR { "MINIJAIL_ARCH_NR", (unsigned long) MINIJAIL_ARCH_NR }, #endif // MINIJAIL_ARCH_NR #ifdef MINSIGSTKSZ { "MINSIGSTKSZ", (unsigned long) MINSIGSTKSZ }, #endif // MINSIGSTKSZ #ifdef MLOCK_ONFAULT { "MLOCK_ONFAULT", (unsigned long) MLOCK_ONFAULT }, #endif // MLOCK_ONFAULT #ifdef MNT_ID_REQ_SIZE_VER0 { "MNT_ID_REQ_SIZE_VER0", (unsigned long) MNT_ID_REQ_SIZE_VER0 }, #endif // MNT_ID_REQ_SIZE_VER0 #ifdef MNT_ID_REQ_SIZE_VER1 { "MNT_ID_REQ_SIZE_VER1", (unsigned long) MNT_ID_REQ_SIZE_VER1 }, #endif // MNT_ID_REQ_SIZE_VER1 #ifdef MOUNT_ATTR__ATIME { "MOUNT_ATTR__ATIME", (unsigned long) MOUNT_ATTR__ATIME }, #endif // MOUNT_ATTR__ATIME #ifdef MOUNT_ATTR_IDMAP { "MOUNT_ATTR_IDMAP", (unsigned long) MOUNT_ATTR_IDMAP }, #endif // MOUNT_ATTR_IDMAP #ifdef MOUNT_ATTR_NOATIME { "MOUNT_ATTR_NOATIME", (unsigned long) MOUNT_ATTR_NOATIME }, #endif // MOUNT_ATTR_NOATIME #ifdef MOUNT_ATTR_NODEV { "MOUNT_ATTR_NODEV", (unsigned long) MOUNT_ATTR_NODEV }, #endif // MOUNT_ATTR_NODEV #ifdef MOUNT_ATTR_NODIRATIME { "MOUNT_ATTR_NODIRATIME", (unsigned long) MOUNT_ATTR_NODIRATIME }, #endif // MOUNT_ATTR_NODIRATIME #ifdef MOUNT_ATTR_NOEXEC { "MOUNT_ATTR_NOEXEC", (unsigned long) MOUNT_ATTR_NOEXEC }, #endif // MOUNT_ATTR_NOEXEC #ifdef MOUNT_ATTR_NOSUID { "MOUNT_ATTR_NOSUID", (unsigned long) MOUNT_ATTR_NOSUID }, #endif // MOUNT_ATTR_NOSUID #ifdef MOUNT_ATTR_NOSYMFOLLOW { "MOUNT_ATTR_NOSYMFOLLOW", (unsigned long) MOUNT_ATTR_NOSYMFOLLOW }, #endif // MOUNT_ATTR_NOSYMFOLLOW #ifdef MOUNT_ATTR_RDONLY { "MOUNT_ATTR_RDONLY", (unsigned long) MOUNT_ATTR_RDONLY }, #endif // MOUNT_ATTR_RDONLY #ifdef MOUNT_ATTR_RELATIME { "MOUNT_ATTR_RELATIME", (unsigned long) MOUNT_ATTR_RELATIME }, #endif // MOUNT_ATTR_RELATIME #ifdef MOUNT_ATTR_SIZE_VER0 { "MOUNT_ATTR_SIZE_VER0", (unsigned long) MOUNT_ATTR_SIZE_VER0 }, #endif // MOUNT_ATTR_SIZE_VER0 #ifdef MOUNT_ATTR_STRICTATIME { "MOUNT_ATTR_STRICTATIME", (unsigned long) MOUNT_ATTR_STRICTATIME }, #endif // MOUNT_ATTR_STRICTATIME #ifdef MOVE_MOUNT_BENEATH { "MOVE_MOUNT_BENEATH", (unsigned long) MOVE_MOUNT_BENEATH }, #endif // MOVE_MOUNT_BENEATH #ifdef MOVE_MOUNT_F_AUTOMOUNTS { "MOVE_MOUNT_F_AUTOMOUNTS", (unsigned long) MOVE_MOUNT_F_AUTOMOUNTS }, #endif // MOVE_MOUNT_F_AUTOMOUNTS #ifdef MOVE_MOUNT_F_EMPTY_PATH { "MOVE_MOUNT_F_EMPTY_PATH", (unsigned long) MOVE_MOUNT_F_EMPTY_PATH }, #endif // MOVE_MOUNT_F_EMPTY_PATH #ifdef MOVE_MOUNT_F_SYMLINKS { "MOVE_MOUNT_F_SYMLINKS", (unsigned long) MOVE_MOUNT_F_SYMLINKS }, #endif // MOVE_MOUNT_F_SYMLINKS #ifdef MOVE_MOUNT__MASK { "MOVE_MOUNT__MASK", (unsigned long) MOVE_MOUNT__MASK }, #endif // MOVE_MOUNT__MASK #ifdef MOVE_MOUNT_SET_GROUP { "MOVE_MOUNT_SET_GROUP", (unsigned long) MOVE_MOUNT_SET_GROUP }, #endif // MOVE_MOUNT_SET_GROUP #ifdef MOVE_MOUNT_T_AUTOMOUNTS { "MOVE_MOUNT_T_AUTOMOUNTS", (unsigned long) MOVE_MOUNT_T_AUTOMOUNTS }, #endif // MOVE_MOUNT_T_AUTOMOUNTS #ifdef MOVE_MOUNT_T_EMPTY_PATH { "MOVE_MOUNT_T_EMPTY_PATH", (unsigned long) MOVE_MOUNT_T_EMPTY_PATH }, #endif // MOVE_MOUNT_T_EMPTY_PATH #ifdef MOVE_MOUNT_T_SYMLINKS { "MOVE_MOUNT_T_SYMLINKS", (unsigned long) MOVE_MOUNT_T_SYMLINKS }, #endif // MOVE_MOUNT_T_SYMLINKS #ifdef MREMAP_DONTUNMAP { "MREMAP_DONTUNMAP", (unsigned long) MREMAP_DONTUNMAP }, #endif // MREMAP_DONTUNMAP #ifdef MREMAP_FIXED { "MREMAP_FIXED", (unsigned long) MREMAP_FIXED }, #endif // MREMAP_FIXED #ifdef MREMAP_MAYMOVE { "MREMAP_MAYMOVE", (unsigned long) MREMAP_MAYMOVE }, #endif // MREMAP_MAYMOVE #ifdef MS_ASYNC { "MS_ASYNC", (unsigned long) MS_ASYNC }, #endif // MS_ASYNC #ifdef MS_BIND { "MS_BIND", (unsigned long) MS_BIND }, #endif // MS_BIND #ifdef MS_DIRSYNC { "MS_DIRSYNC", (unsigned long) MS_DIRSYNC }, #endif // MS_DIRSYNC #ifdef MSG_BATCH { "MSG_BATCH", (unsigned long) MSG_BATCH }, #endif // MSG_BATCH #ifdef MSG_CMSG_CLOEXEC { "MSG_CMSG_CLOEXEC", (unsigned long) MSG_CMSG_CLOEXEC }, #endif // MSG_CMSG_CLOEXEC #ifdef MSG_CONFIRM { "MSG_CONFIRM", (unsigned long) MSG_CONFIRM }, #endif // MSG_CONFIRM #ifdef MSG_CTRUNC { "MSG_CTRUNC", (unsigned long) MSG_CTRUNC }, #endif // MSG_CTRUNC #ifdef MSG_DONTROUTE { "MSG_DONTROUTE", (unsigned long) MSG_DONTROUTE }, #endif // MSG_DONTROUTE #ifdef MSG_DONTWAIT { "MSG_DONTWAIT", (unsigned long) MSG_DONTWAIT }, #endif // MSG_DONTWAIT #ifdef MSG_EOR { "MSG_EOR", (unsigned long) MSG_EOR }, #endif // MSG_EOR #ifdef MSG_ERRQUEUE { "MSG_ERRQUEUE", (unsigned long) MSG_ERRQUEUE }, #endif // MSG_ERRQUEUE #ifdef MSG_FASTOPEN { "MSG_FASTOPEN", (unsigned long) MSG_FASTOPEN }, #endif // MSG_FASTOPEN #ifdef MSG_FIN { "MSG_FIN", (unsigned long) MSG_FIN }, #endif // MSG_FIN #ifdef MSG_MORE { "MSG_MORE", (unsigned long) MSG_MORE }, #endif // MSG_MORE #ifdef MSG_NOSIGNAL { "MSG_NOSIGNAL", (unsigned long) MSG_NOSIGNAL }, #endif // MSG_NOSIGNAL #ifdef MSG_OOB { "MSG_OOB", (unsigned long) MSG_OOB }, #endif // MSG_OOB #ifdef MSG_PEEK { "MSG_PEEK", (unsigned long) MSG_PEEK }, #endif // MSG_PEEK #ifdef MSG_PROXY { "MSG_PROXY", (unsigned long) MSG_PROXY }, #endif // MSG_PROXY #ifdef MSG_RST { "MSG_RST", (unsigned long) MSG_RST }, #endif // MSG_RST #ifdef MSG_SOCK_DEVMEM { "MSG_SOCK_DEVMEM", (unsigned long) MSG_SOCK_DEVMEM }, #endif // MSG_SOCK_DEVMEM #ifdef MSG_SYN { "MSG_SYN", (unsigned long) MSG_SYN }, #endif // MSG_SYN #ifdef MSG_TRUNC { "MSG_TRUNC", (unsigned long) MSG_TRUNC }, #endif // MSG_TRUNC #ifdef MSG_WAITALL { "MSG_WAITALL", (unsigned long) MSG_WAITALL }, #endif // MSG_WAITALL #ifdef MSG_WAITFORONE { "MSG_WAITFORONE", (unsigned long) MSG_WAITFORONE }, #endif // MSG_WAITFORONE #ifdef MSG_ZEROCOPY { "MSG_ZEROCOPY", (unsigned long) MSG_ZEROCOPY }, #endif // MSG_ZEROCOPY #ifdef MS_INVALIDATE { "MS_INVALIDATE", (unsigned long) MS_INVALIDATE }, #endif // MS_INVALIDATE #ifdef MS_MANDLOCK { "MS_MANDLOCK", (unsigned long) MS_MANDLOCK }, #endif // MS_MANDLOCK #ifdef MS_MGC_MSK { "MS_MGC_MSK", (unsigned long) MS_MGC_MSK }, #endif // MS_MGC_MSK #ifdef MS_MGC_VAL { "MS_MGC_VAL", (unsigned long) MS_MGC_VAL }, #endif // MS_MGC_VAL #ifdef MS_MOVE { "MS_MOVE", (unsigned long) MS_MOVE }, #endif // MS_MOVE #ifdef MS_NOATIME { "MS_NOATIME", (unsigned long) MS_NOATIME }, #endif // MS_NOATIME #ifdef MS_NODEV { "MS_NODEV", (unsigned long) MS_NODEV }, #endif // MS_NODEV #ifdef MS_NODIRATIME { "MS_NODIRATIME", (unsigned long) MS_NODIRATIME }, #endif // MS_NODIRATIME #ifdef MS_NOEXEC { "MS_NOEXEC", (unsigned long) MS_NOEXEC }, #endif // MS_NOEXEC #ifdef MS_NOSUID { "MS_NOSUID", (unsigned long) MS_NOSUID }, #endif // MS_NOSUID #ifdef MS_NOSYMFOLLOW { "MS_NOSYMFOLLOW", (unsigned long) MS_NOSYMFOLLOW }, #endif // MS_NOSYMFOLLOW #ifdef MS_RDONLY { "MS_RDONLY", (unsigned long) MS_RDONLY }, #endif // MS_RDONLY #ifdef MS_REC { "MS_REC", (unsigned long) MS_REC }, #endif // MS_REC #ifdef MS_REMOUNT { "MS_REMOUNT", (unsigned long) MS_REMOUNT }, #endif // MS_REMOUNT #ifdef MS_SILENT { "MS_SILENT", (unsigned long) MS_SILENT }, #endif // MS_SILENT #ifdef MS_SYNC { "MS_SYNC", (unsigned long) MS_SYNC }, #endif // MS_SYNC #ifdef MS_SYNCHRONOUS { "MS_SYNCHRONOUS", (unsigned long) MS_SYNCHRONOUS }, #endif // MS_SYNCHRONOUS #ifdef MS_VERBOSE { "MS_VERBOSE", (unsigned long) MS_VERBOSE }, #endif // MS_VERBOSE #ifdef NAME_MAX { "NAME_MAX", (unsigned long) NAME_MAX }, #endif // NAME_MAX #ifdef NCC { "NCC", (unsigned long) NCC }, #endif // NCC #ifdef NCCS { "NCCS", (unsigned long) NCCS }, #endif // NCCS #ifdef NFDBITS { "NFDBITS", (unsigned long) NFDBITS }, #endif // NFDBITS #ifdef NGREG { "NGREG", (unsigned long) NGREG }, #endif // NGREG #ifdef NGROUPS_MAX { "NGROUPS_MAX", (unsigned long) NGROUPS_MAX }, #endif // NGROUPS_MAX #ifdef NL0 { "NL0", (unsigned long) NL0 }, #endif // NL0 #ifdef NL1 { "NL1", (unsigned long) NL1 }, #endif // NL1 #ifdef NLDLY { "NLDLY", (unsigned long) NLDLY }, #endif // NLDLY #ifdef NOFLSH { "NOFLSH", (unsigned long) NOFLSH }, #endif // NOFLSH #ifdef NPROTO { "NPROTO", (unsigned long) NPROTO }, #endif // NPROTO #ifdef NR_FILE { "NR_FILE", (unsigned long) NR_FILE }, #endif // NR_FILE #ifdef NR_OPEN { "NR_OPEN", (unsigned long) NR_OPEN }, #endif // NR_OPEN #ifdef NSIG { "NSIG", (unsigned long) NSIG }, #endif // NSIG #ifdef O_ACCMODE { "O_ACCMODE", (unsigned long) O_ACCMODE }, #endif // O_ACCMODE #ifdef O_APPEND { "O_APPEND", (unsigned long) O_APPEND }, #endif // O_APPEND #ifdef O_ASYNC { "O_ASYNC", (unsigned long) O_ASYNC }, #endif // O_ASYNC #ifdef O_CLOEXEC { "O_CLOEXEC", (unsigned long) O_CLOEXEC }, #endif // O_CLOEXEC #ifdef O_CREAT { "O_CREAT", (unsigned long) O_CREAT }, #endif // O_CREAT #ifdef OCRNL { "OCRNL", (unsigned long) OCRNL }, #endif // OCRNL #ifdef O_DIRECTORY { "O_DIRECTORY", (unsigned long) O_DIRECTORY }, #endif // O_DIRECTORY #ifdef O_DSYNC { "O_DSYNC", (unsigned long) O_DSYNC }, #endif // O_DSYNC #ifdef O_EXCL { "O_EXCL", (unsigned long) O_EXCL }, #endif // O_EXCL #ifdef OFDEL { "OFDEL", (unsigned long) OFDEL }, #endif // OFDEL #ifdef OFILL { "OFILL", (unsigned long) OFILL }, #endif // OFILL #ifdef O_FSYNC { "O_FSYNC", (unsigned long) O_FSYNC }, #endif // O_FSYNC #ifdef OLCUC { "OLCUC", (unsigned long) OLCUC }, #endif // OLCUC #ifdef O_NDELAY { "O_NDELAY", (unsigned long) O_NDELAY }, #endif // O_NDELAY #ifdef ONLCR { "ONLCR", (unsigned long) ONLCR }, #endif // ONLCR #ifdef ONLRET { "ONLRET", (unsigned long) ONLRET }, #endif // ONLRET #ifdef ONOCR { "ONOCR", (unsigned long) ONOCR }, #endif // ONOCR #ifdef O_NOCTTY { "O_NOCTTY", (unsigned long) O_NOCTTY }, #endif // O_NOCTTY #ifdef O_NOFOLLOW { "O_NOFOLLOW", (unsigned long) O_NOFOLLOW }, #endif // O_NOFOLLOW #ifdef O_NONBLOCK { "O_NONBLOCK", (unsigned long) O_NONBLOCK }, #endif // O_NONBLOCK #ifdef OPEN_TREE_CLOEXEC { "OPEN_TREE_CLOEXEC", (unsigned long) OPEN_TREE_CLOEXEC }, #endif // OPEN_TREE_CLOEXEC #ifdef OPEN_TREE_CLONE { "OPEN_TREE_CLONE", (unsigned long) OPEN_TREE_CLONE }, #endif // OPEN_TREE_CLONE #ifdef OPOST { "OPOST", (unsigned long) OPOST }, #endif // OPOST #ifdef O_RDONLY { "O_RDONLY", (unsigned long) O_RDONLY }, #endif // O_RDONLY #ifdef O_RDWR { "O_RDWR", (unsigned long) O_RDWR }, #endif // O_RDWR #ifdef O_RSYNC { "O_RSYNC", (unsigned long) O_RSYNC }, #endif // O_RSYNC #ifdef O_SYNC { "O_SYNC", (unsigned long) O_SYNC }, #endif // O_SYNC #ifdef O_TRUNC { "O_TRUNC", (unsigned long) O_TRUNC }, #endif // O_TRUNC #ifdef OVERCOMMIT_ALWAYS { "OVERCOMMIT_ALWAYS", (unsigned long) OVERCOMMIT_ALWAYS }, #endif // OVERCOMMIT_ALWAYS #ifdef OVERCOMMIT_GUESS { "OVERCOMMIT_GUESS", (unsigned long) OVERCOMMIT_GUESS }, #endif // OVERCOMMIT_GUESS #ifdef OVERCOMMIT_NEVER { "OVERCOMMIT_NEVER", (unsigned long) OVERCOMMIT_NEVER }, #endif // OVERCOMMIT_NEVER #ifdef O_WRONLY { "O_WRONLY", (unsigned long) O_WRONLY }, #endif // O_WRONLY #ifdef PAGEMAP_SCAN { "PAGEMAP_SCAN", (unsigned long) PAGEMAP_SCAN }, #endif // PAGEMAP_SCAN #ifdef PARENB { "PARENB", (unsigned long) PARENB }, #endif // PARENB #ifdef PARMRK { "PARMRK", (unsigned long) PARMRK }, #endif // PARMRK #ifdef PARODD { "PARODD", (unsigned long) PARODD }, #endif // PARODD #ifdef PATH_MAX { "PATH_MAX", (unsigned long) PATH_MAX }, #endif // PATH_MAX #ifdef PDP_ENDIAN { "PDP_ENDIAN", (unsigned long) PDP_ENDIAN }, #endif // PDP_ENDIAN #ifdef PENDIN { "PENDIN", (unsigned long) PENDIN }, #endif // PENDIN #ifdef PF_ALG { "PF_ALG", (unsigned long) PF_ALG }, #endif // PF_ALG #ifdef PF_APPLETALK { "PF_APPLETALK", (unsigned long) PF_APPLETALK }, #endif // PF_APPLETALK #ifdef PF_ASH { "PF_ASH", (unsigned long) PF_ASH }, #endif // PF_ASH #ifdef PF_ATMPVC { "PF_ATMPVC", (unsigned long) PF_ATMPVC }, #endif // PF_ATMPVC #ifdef PF_ATMSVC { "PF_ATMSVC", (unsigned long) PF_ATMSVC }, #endif // PF_ATMSVC #ifdef PF_AX25 { "PF_AX25", (unsigned long) PF_AX25 }, #endif // PF_AX25 #ifdef PF_BLUETOOTH { "PF_BLUETOOTH", (unsigned long) PF_BLUETOOTH }, #endif // PF_BLUETOOTH #ifdef PF_BRIDGE { "PF_BRIDGE", (unsigned long) PF_BRIDGE }, #endif // PF_BRIDGE #ifdef PF_CAIF { "PF_CAIF", (unsigned long) PF_CAIF }, #endif // PF_CAIF #ifdef PF_CAN { "PF_CAN", (unsigned long) PF_CAN }, #endif // PF_CAN #ifdef PF_ECONET { "PF_ECONET", (unsigned long) PF_ECONET }, #endif // PF_ECONET #ifdef PF_FILE { "PF_FILE", (unsigned long) PF_FILE }, #endif // PF_FILE #ifdef PF_IB { "PF_IB", (unsigned long) PF_IB }, #endif // PF_IB #ifdef PF_IEEE802154 { "PF_IEEE802154", (unsigned long) PF_IEEE802154 }, #endif // PF_IEEE802154 #ifdef PF_INET { "PF_INET", (unsigned long) PF_INET }, #endif // PF_INET #ifdef PF_INET6 { "PF_INET6", (unsigned long) PF_INET6 }, #endif // PF_INET6 #ifdef PF_IPX { "PF_IPX", (unsigned long) PF_IPX }, #endif // PF_IPX #ifdef PF_IRDA { "PF_IRDA", (unsigned long) PF_IRDA }, #endif // PF_IRDA #ifdef PF_ISDN { "PF_ISDN", (unsigned long) PF_ISDN }, #endif // PF_ISDN #ifdef PF_IUCV { "PF_IUCV", (unsigned long) PF_IUCV }, #endif // PF_IUCV #ifdef PF_KCM { "PF_KCM", (unsigned long) PF_KCM }, #endif // PF_KCM #ifdef PF_KEY { "PF_KEY", (unsigned long) PF_KEY }, #endif // PF_KEY #ifdef PF_LLC { "PF_LLC", (unsigned long) PF_LLC }, #endif // PF_LLC #ifdef PF_LOCAL { "PF_LOCAL", (unsigned long) PF_LOCAL }, #endif // PF_LOCAL #ifdef PF_MAX { "PF_MAX", (unsigned long) PF_MAX }, #endif // PF_MAX #ifdef PF_MCTP { "PF_MCTP", (unsigned long) PF_MCTP }, #endif // PF_MCTP #ifdef PF_MPLS { "PF_MPLS", (unsigned long) PF_MPLS }, #endif // PF_MPLS #ifdef PF_NETBEUI { "PF_NETBEUI", (unsigned long) PF_NETBEUI }, #endif // PF_NETBEUI #ifdef PF_NETLINK { "PF_NETLINK", (unsigned long) PF_NETLINK }, #endif // PF_NETLINK #ifdef PF_NETROM { "PF_NETROM", (unsigned long) PF_NETROM }, #endif // PF_NETROM #ifdef PF_NFC { "PF_NFC", (unsigned long) PF_NFC }, #endif // PF_NFC #ifdef PF_PACKET { "PF_PACKET", (unsigned long) PF_PACKET }, #endif // PF_PACKET #ifdef PF_PHONET { "PF_PHONET", (unsigned long) PF_PHONET }, #endif // PF_PHONET #ifdef PF_PPPOX { "PF_PPPOX", (unsigned long) PF_PPPOX }, #endif // PF_PPPOX #ifdef PF_QIPCRTR { "PF_QIPCRTR", (unsigned long) PF_QIPCRTR }, #endif // PF_QIPCRTR #ifdef PF_RDS { "PF_RDS", (unsigned long) PF_RDS }, #endif // PF_RDS #ifdef PF_ROSE { "PF_ROSE", (unsigned long) PF_ROSE }, #endif // PF_ROSE #ifdef PF_ROUTE { "PF_ROUTE", (unsigned long) PF_ROUTE }, #endif // PF_ROUTE #ifdef PF_RXRPC { "PF_RXRPC", (unsigned long) PF_RXRPC }, #endif // PF_RXRPC #ifdef PF_SECURITY { "PF_SECURITY", (unsigned long) PF_SECURITY }, #endif // PF_SECURITY #ifdef PF_SMC { "PF_SMC", (unsigned long) PF_SMC }, #endif // PF_SMC #ifdef PF_SNA { "PF_SNA", (unsigned long) PF_SNA }, #endif // PF_SNA #ifdef PF_TIPC { "PF_TIPC", (unsigned long) PF_TIPC }, #endif // PF_TIPC #ifdef PF_UNIX { "PF_UNIX", (unsigned long) PF_UNIX }, #endif // PF_UNIX #ifdef PF_UNSPEC { "PF_UNSPEC", (unsigned long) PF_UNSPEC }, #endif // PF_UNSPEC #ifdef PF_VSOCK { "PF_VSOCK", (unsigned long) PF_VSOCK }, #endif // PF_VSOCK #ifdef PF_WANPIPE { "PF_WANPIPE", (unsigned long) PF_WANPIPE }, #endif // PF_WANPIPE #ifdef PF_X25 { "PF_X25", (unsigned long) PF_X25 }, #endif // PF_X25 #ifdef PF_XDP { "PF_XDP", (unsigned long) PF_XDP }, #endif // PF_XDP #ifdef PIPE_BUF { "PIPE_BUF", (unsigned long) PIPE_BUF }, #endif // PIPE_BUF #ifdef PKEY_DISABLE_ACCESS { "PKEY_DISABLE_ACCESS", (unsigned long) PKEY_DISABLE_ACCESS }, #endif // PKEY_DISABLE_ACCESS #ifdef PKEY_DISABLE_WRITE { "PKEY_DISABLE_WRITE", (unsigned long) PKEY_DISABLE_WRITE }, #endif // PKEY_DISABLE_WRITE #ifdef PKEY_UNRESTRICTED { "PKEY_UNRESTRICTED", (unsigned long) PKEY_UNRESTRICTED }, #endif // PKEY_UNRESTRICTED #ifdef POLL_ERR { "POLL_ERR", (unsigned long) POLL_ERR }, #endif // POLL_ERR #ifdef POLL_HUP { "POLL_HUP", (unsigned long) POLL_HUP }, #endif // POLL_HUP #ifdef POLL_IN { "POLL_IN", (unsigned long) POLL_IN }, #endif // POLL_IN #ifdef POLL_MSG { "POLL_MSG", (unsigned long) POLL_MSG }, #endif // POLL_MSG #ifdef POLL_OUT { "POLL_OUT", (unsigned long) POLL_OUT }, #endif // POLL_OUT #ifdef POLL_PRI { "POLL_PRI", (unsigned long) POLL_PRI }, #endif // POLL_PRI #ifdef PORT_16450 { "PORT_16450", (unsigned long) PORT_16450 }, #endif // PORT_16450 #ifdef PORT_16550 { "PORT_16550", (unsigned long) PORT_16550 }, #endif // PORT_16550 #ifdef PORT_16550A { "PORT_16550A", (unsigned long) PORT_16550A }, #endif // PORT_16550A #ifdef PORT_16650 { "PORT_16650", (unsigned long) PORT_16650 }, #endif // PORT_16650 #ifdef PORT_16650V2 { "PORT_16650V2", (unsigned long) PORT_16650V2 }, #endif // PORT_16650V2 #ifdef PORT_16654 { "PORT_16654", (unsigned long) PORT_16654 }, #endif // PORT_16654 #ifdef PORT_16750 { "PORT_16750", (unsigned long) PORT_16750 }, #endif // PORT_16750 #ifdef PORT_16850 { "PORT_16850", (unsigned long) PORT_16850 }, #endif // PORT_16850 #ifdef PORT_16C950 { "PORT_16C950", (unsigned long) PORT_16C950 }, #endif // PORT_16C950 #ifdef PORT_8250 { "PORT_8250", (unsigned long) PORT_8250 }, #endif // PORT_8250 #ifdef PORT_CIRRUS { "PORT_CIRRUS", (unsigned long) PORT_CIRRUS }, #endif // PORT_CIRRUS #ifdef PORT_MAX { "PORT_MAX", (unsigned long) PORT_MAX }, #endif // PORT_MAX #ifdef PORT_RSA { "PORT_RSA", (unsigned long) PORT_RSA }, #endif // PORT_RSA #ifdef PORT_STARTECH { "PORT_STARTECH", (unsigned long) PORT_STARTECH }, #endif // PORT_STARTECH #ifdef PORT_UNKNOWN { "PORT_UNKNOWN", (unsigned long) PORT_UNKNOWN }, #endif // PORT_UNKNOWN #ifdef POSIX_FADV_DONTNEED { "POSIX_FADV_DONTNEED", (unsigned long) POSIX_FADV_DONTNEED }, #endif // POSIX_FADV_DONTNEED #ifdef POSIX_FADV_NOREUSE { "POSIX_FADV_NOREUSE", (unsigned long) POSIX_FADV_NOREUSE }, #endif // POSIX_FADV_NOREUSE #ifdef POSIX_FADV_NORMAL { "POSIX_FADV_NORMAL", (unsigned long) POSIX_FADV_NORMAL }, #endif // POSIX_FADV_NORMAL #ifdef POSIX_FADV_RANDOM { "POSIX_FADV_RANDOM", (unsigned long) POSIX_FADV_RANDOM }, #endif // POSIX_FADV_RANDOM #ifdef POSIX_FADV_SEQUENTIAL { "POSIX_FADV_SEQUENTIAL", (unsigned long) POSIX_FADV_SEQUENTIAL }, #endif // POSIX_FADV_SEQUENTIAL #ifdef POSIX_FADV_WILLNEED { "POSIX_FADV_WILLNEED", (unsigned long) POSIX_FADV_WILLNEED }, #endif // POSIX_FADV_WILLNEED #ifdef POSIX_MADV_DONTNEED { "POSIX_MADV_DONTNEED", (unsigned long) POSIX_MADV_DONTNEED }, #endif // POSIX_MADV_DONTNEED #ifdef POSIX_MADV_NORMAL { "POSIX_MADV_NORMAL", (unsigned long) POSIX_MADV_NORMAL }, #endif // POSIX_MADV_NORMAL #ifdef POSIX_MADV_RANDOM { "POSIX_MADV_RANDOM", (unsigned long) POSIX_MADV_RANDOM }, #endif // POSIX_MADV_RANDOM #ifdef POSIX_MADV_SEQUENTIAL { "POSIX_MADV_SEQUENTIAL", (unsigned long) POSIX_MADV_SEQUENTIAL }, #endif // POSIX_MADV_SEQUENTIAL #ifdef POSIX_MADV_WILLNEED { "POSIX_MADV_WILLNEED", (unsigned long) POSIX_MADV_WILLNEED }, #endif // POSIX_MADV_WILLNEED #ifdef PR_CAP_AMBIENT { "PR_CAP_AMBIENT", (unsigned long) PR_CAP_AMBIENT }, #endif // PR_CAP_AMBIENT #ifdef PR_CAP_AMBIENT_CLEAR_ALL { "PR_CAP_AMBIENT_CLEAR_ALL", (unsigned long) PR_CAP_AMBIENT_CLEAR_ALL }, #endif // PR_CAP_AMBIENT_CLEAR_ALL #ifdef PR_CAP_AMBIENT_IS_SET { "PR_CAP_AMBIENT_IS_SET", (unsigned long) PR_CAP_AMBIENT_IS_SET }, #endif // PR_CAP_AMBIENT_IS_SET #ifdef PR_CAP_AMBIENT_LOWER { "PR_CAP_AMBIENT_LOWER", (unsigned long) PR_CAP_AMBIENT_LOWER }, #endif // PR_CAP_AMBIENT_LOWER #ifdef PR_CAP_AMBIENT_RAISE { "PR_CAP_AMBIENT_RAISE", (unsigned long) PR_CAP_AMBIENT_RAISE }, #endif // PR_CAP_AMBIENT_RAISE #ifdef PR_CAPBSET_DROP { "PR_CAPBSET_DROP", (unsigned long) PR_CAPBSET_DROP }, #endif // PR_CAPBSET_DROP #ifdef PR_CAPBSET_READ { "PR_CAPBSET_READ", (unsigned long) PR_CAPBSET_READ }, #endif // PR_CAPBSET_READ #ifdef PR_ENDIAN_BIG { "PR_ENDIAN_BIG", (unsigned long) PR_ENDIAN_BIG }, #endif // PR_ENDIAN_BIG #ifdef PR_ENDIAN_LITTLE { "PR_ENDIAN_LITTLE", (unsigned long) PR_ENDIAN_LITTLE }, #endif // PR_ENDIAN_LITTLE #ifdef PR_ENDIAN_PPC_LITTLE { "PR_ENDIAN_PPC_LITTLE", (unsigned long) PR_ENDIAN_PPC_LITTLE }, #endif // PR_ENDIAN_PPC_LITTLE #ifdef PR_FPEMU_NOPRINT { "PR_FPEMU_NOPRINT", (unsigned long) PR_FPEMU_NOPRINT }, #endif // PR_FPEMU_NOPRINT #ifdef PR_FPEMU_SIGFPE { "PR_FPEMU_SIGFPE", (unsigned long) PR_FPEMU_SIGFPE }, #endif // PR_FPEMU_SIGFPE #ifdef PR_FP_EXC_ASYNC { "PR_FP_EXC_ASYNC", (unsigned long) PR_FP_EXC_ASYNC }, #endif // PR_FP_EXC_ASYNC #ifdef PR_FP_EXC_DISABLED { "PR_FP_EXC_DISABLED", (unsigned long) PR_FP_EXC_DISABLED }, #endif // PR_FP_EXC_DISABLED #ifdef PR_FP_EXC_DIV { "PR_FP_EXC_DIV", (unsigned long) PR_FP_EXC_DIV }, #endif // PR_FP_EXC_DIV #ifdef PR_FP_EXC_INV { "PR_FP_EXC_INV", (unsigned long) PR_FP_EXC_INV }, #endif // PR_FP_EXC_INV #ifdef PR_FP_EXC_NONRECOV { "PR_FP_EXC_NONRECOV", (unsigned long) PR_FP_EXC_NONRECOV }, #endif // PR_FP_EXC_NONRECOV #ifdef PR_FP_EXC_OVF { "PR_FP_EXC_OVF", (unsigned long) PR_FP_EXC_OVF }, #endif // PR_FP_EXC_OVF #ifdef PR_FP_EXC_PRECISE { "PR_FP_EXC_PRECISE", (unsigned long) PR_FP_EXC_PRECISE }, #endif // PR_FP_EXC_PRECISE #ifdef PR_FP_EXC_RES { "PR_FP_EXC_RES", (unsigned long) PR_FP_EXC_RES }, #endif // PR_FP_EXC_RES #ifdef PR_FP_EXC_SW_ENABLE { "PR_FP_EXC_SW_ENABLE", (unsigned long) PR_FP_EXC_SW_ENABLE }, #endif // PR_FP_EXC_SW_ENABLE #ifdef PR_FP_EXC_UND { "PR_FP_EXC_UND", (unsigned long) PR_FP_EXC_UND }, #endif // PR_FP_EXC_UND #ifdef PR_FUTEX_HASH { "PR_FUTEX_HASH", (unsigned long) PR_FUTEX_HASH }, #endif // PR_FUTEX_HASH #ifdef PR_FUTEX_HASH_GET_SLOTS { "PR_FUTEX_HASH_GET_SLOTS", (unsigned long) PR_FUTEX_HASH_GET_SLOTS }, #endif // PR_FUTEX_HASH_GET_SLOTS #ifdef PR_FUTEX_HASH_SET_SLOTS { "PR_FUTEX_HASH_SET_SLOTS", (unsigned long) PR_FUTEX_HASH_SET_SLOTS }, #endif // PR_FUTEX_HASH_SET_SLOTS #ifdef PR_GET_AUXV { "PR_GET_AUXV", (unsigned long) PR_GET_AUXV }, #endif // PR_GET_AUXV #ifdef PR_GET_CHILD_SUBREAPER { "PR_GET_CHILD_SUBREAPER", (unsigned long) PR_GET_CHILD_SUBREAPER }, #endif // PR_GET_CHILD_SUBREAPER #ifdef PR_GET_DUMPABLE { "PR_GET_DUMPABLE", (unsigned long) PR_GET_DUMPABLE }, #endif // PR_GET_DUMPABLE #ifdef PR_GET_ENDIAN { "PR_GET_ENDIAN", (unsigned long) PR_GET_ENDIAN }, #endif // PR_GET_ENDIAN #ifdef PR_GET_FPEMU { "PR_GET_FPEMU", (unsigned long) PR_GET_FPEMU }, #endif // PR_GET_FPEMU #ifdef PR_GET_FPEXC { "PR_GET_FPEXC", (unsigned long) PR_GET_FPEXC }, #endif // PR_GET_FPEXC #ifdef PR_GET_FP_MODE { "PR_GET_FP_MODE", (unsigned long) PR_GET_FP_MODE }, #endif // PR_GET_FP_MODE #ifdef PR_GET_IO_FLUSHER { "PR_GET_IO_FLUSHER", (unsigned long) PR_GET_IO_FLUSHER }, #endif // PR_GET_IO_FLUSHER #ifdef PR_GET_KEEPCAPS { "PR_GET_KEEPCAPS", (unsigned long) PR_GET_KEEPCAPS }, #endif // PR_GET_KEEPCAPS #ifdef PR_GET_MDWE { "PR_GET_MDWE", (unsigned long) PR_GET_MDWE }, #endif // PR_GET_MDWE #ifdef PR_GET_MEMORY_MERGE { "PR_GET_MEMORY_MERGE", (unsigned long) PR_GET_MEMORY_MERGE }, #endif // PR_GET_MEMORY_MERGE #ifdef PR_GET_NAME { "PR_GET_NAME", (unsigned long) PR_GET_NAME }, #endif // PR_GET_NAME #ifdef PR_GET_NO_NEW_PRIVS { "PR_GET_NO_NEW_PRIVS", (unsigned long) PR_GET_NO_NEW_PRIVS }, #endif // PR_GET_NO_NEW_PRIVS #ifdef PR_GET_PDEATHSIG { "PR_GET_PDEATHSIG", (unsigned long) PR_GET_PDEATHSIG }, #endif // PR_GET_PDEATHSIG #ifdef PR_GET_SECCOMP { "PR_GET_SECCOMP", (unsigned long) PR_GET_SECCOMP }, #endif // PR_GET_SECCOMP #ifdef PR_GET_SECUREBITS { "PR_GET_SECUREBITS", (unsigned long) PR_GET_SECUREBITS }, #endif // PR_GET_SECUREBITS #ifdef PR_GET_SHADOW_STACK_STATUS { "PR_GET_SHADOW_STACK_STATUS", (unsigned long) PR_GET_SHADOW_STACK_STATUS }, #endif // PR_GET_SHADOW_STACK_STATUS #ifdef PR_GET_SPECULATION_CTRL { "PR_GET_SPECULATION_CTRL", (unsigned long) PR_GET_SPECULATION_CTRL }, #endif // PR_GET_SPECULATION_CTRL #ifdef PR_GET_TAGGED_ADDR_CTRL { "PR_GET_TAGGED_ADDR_CTRL", (unsigned long) PR_GET_TAGGED_ADDR_CTRL }, #endif // PR_GET_TAGGED_ADDR_CTRL #ifdef PR_GET_THP_DISABLE { "PR_GET_THP_DISABLE", (unsigned long) PR_GET_THP_DISABLE }, #endif // PR_GET_THP_DISABLE #ifdef PR_GET_TID_ADDRESS { "PR_GET_TID_ADDRESS", (unsigned long) PR_GET_TID_ADDRESS }, #endif // PR_GET_TID_ADDRESS #ifdef PR_GET_TIMERSLACK { "PR_GET_TIMERSLACK", (unsigned long) PR_GET_TIMERSLACK }, #endif // PR_GET_TIMERSLACK #ifdef PR_GET_TIMING { "PR_GET_TIMING", (unsigned long) PR_GET_TIMING }, #endif // PR_GET_TIMING #ifdef PR_GET_TSC { "PR_GET_TSC", (unsigned long) PR_GET_TSC }, #endif // PR_GET_TSC #ifdef PR_GET_UNALIGN { "PR_GET_UNALIGN", (unsigned long) PR_GET_UNALIGN }, #endif // PR_GET_UNALIGN #ifdef PRIO_MAX { "PRIO_MAX", (unsigned long) PRIO_MAX }, #endif // PRIO_MAX #ifdef PRIO_PGRP { "PRIO_PGRP", (unsigned long) PRIO_PGRP }, #endif // PRIO_PGRP #ifdef PRIO_PROCESS { "PRIO_PROCESS", (unsigned long) PRIO_PROCESS }, #endif // PRIO_PROCESS #ifdef PRIO_USER { "PRIO_USER", (unsigned long) PRIO_USER }, #endif // PRIO_USER #ifdef PR_LOCK_SHADOW_STACK_STATUS { "PR_LOCK_SHADOW_STACK_STATUS", (unsigned long) PR_LOCK_SHADOW_STACK_STATUS }, #endif // PR_LOCK_SHADOW_STACK_STATUS #ifdef PR_MCE_KILL { "PR_MCE_KILL", (unsigned long) PR_MCE_KILL }, #endif // PR_MCE_KILL #ifdef PR_MCE_KILL_CLEAR { "PR_MCE_KILL_CLEAR", (unsigned long) PR_MCE_KILL_CLEAR }, #endif // PR_MCE_KILL_CLEAR #ifdef PR_MCE_KILL_DEFAULT { "PR_MCE_KILL_DEFAULT", (unsigned long) PR_MCE_KILL_DEFAULT }, #endif // PR_MCE_KILL_DEFAULT #ifdef PR_MCE_KILL_EARLY { "PR_MCE_KILL_EARLY", (unsigned long) PR_MCE_KILL_EARLY }, #endif // PR_MCE_KILL_EARLY #ifdef PR_MCE_KILL_GET { "PR_MCE_KILL_GET", (unsigned long) PR_MCE_KILL_GET }, #endif // PR_MCE_KILL_GET #ifdef PR_MCE_KILL_LATE { "PR_MCE_KILL_LATE", (unsigned long) PR_MCE_KILL_LATE }, #endif // PR_MCE_KILL_LATE #ifdef PR_MCE_KILL_SET { "PR_MCE_KILL_SET", (unsigned long) PR_MCE_KILL_SET }, #endif // PR_MCE_KILL_SET #ifdef PR_MPX_DISABLE_MANAGEMENT { "PR_MPX_DISABLE_MANAGEMENT", (unsigned long) PR_MPX_DISABLE_MANAGEMENT }, #endif // PR_MPX_DISABLE_MANAGEMENT #ifdef PR_MPX_ENABLE_MANAGEMENT { "PR_MPX_ENABLE_MANAGEMENT", (unsigned long) PR_MPX_ENABLE_MANAGEMENT }, #endif // PR_MPX_ENABLE_MANAGEMENT #ifdef PR_MTE_TAG_SHIFT { "PR_MTE_TAG_SHIFT", (unsigned long) PR_MTE_TAG_SHIFT }, #endif // PR_MTE_TAG_SHIFT #ifdef PR_MTE_TCF_NONE { "PR_MTE_TCF_NONE", (unsigned long) PR_MTE_TCF_NONE }, #endif // PR_MTE_TCF_NONE #ifdef PR_MTE_TCF_SHIFT { "PR_MTE_TCF_SHIFT", (unsigned long) PR_MTE_TCF_SHIFT }, #endif // PR_MTE_TCF_SHIFT #ifdef PROCMAP_QUERY { "PROCMAP_QUERY", (unsigned long) PROCMAP_QUERY }, #endif // PROCMAP_QUERY #ifdef PROT_EXEC { "PROT_EXEC", (unsigned long) PROT_EXEC }, #endif // PROT_EXEC #ifdef PROT_GROWSDOWN { "PROT_GROWSDOWN", (unsigned long) PROT_GROWSDOWN }, #endif // PROT_GROWSDOWN #ifdef PROT_GROWSUP { "PROT_GROWSUP", (unsigned long) PROT_GROWSUP }, #endif // PROT_GROWSUP #ifdef PROT_NONE { "PROT_NONE", (unsigned long) PROT_NONE }, #endif // PROT_NONE #ifdef PROT_READ { "PROT_READ", (unsigned long) PROT_READ }, #endif // PROT_READ #ifdef PROT_SEM { "PROT_SEM", (unsigned long) PROT_SEM }, #endif // PROT_SEM #ifdef PROT_WRITE { "PROT_WRITE", (unsigned long) PROT_WRITE }, #endif // PROT_WRITE #ifdef PR_PAC_GET_ENABLED_KEYS { "PR_PAC_GET_ENABLED_KEYS", (unsigned long) PR_PAC_GET_ENABLED_KEYS }, #endif // PR_PAC_GET_ENABLED_KEYS #ifdef PR_PAC_RESET_KEYS { "PR_PAC_RESET_KEYS", (unsigned long) PR_PAC_RESET_KEYS }, #endif // PR_PAC_RESET_KEYS #ifdef PR_PAC_SET_ENABLED_KEYS { "PR_PAC_SET_ENABLED_KEYS", (unsigned long) PR_PAC_SET_ENABLED_KEYS }, #endif // PR_PAC_SET_ENABLED_KEYS #ifdef PR_PMLEN_SHIFT { "PR_PMLEN_SHIFT", (unsigned long) PR_PMLEN_SHIFT }, #endif // PR_PMLEN_SHIFT #ifdef PR_PPC_DEXCR_CTRL_CLEAR { "PR_PPC_DEXCR_CTRL_CLEAR", (unsigned long) PR_PPC_DEXCR_CTRL_CLEAR }, #endif // PR_PPC_DEXCR_CTRL_CLEAR #ifdef PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC { "PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC", (unsigned long) PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC }, #endif // PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC #ifdef PR_PPC_DEXCR_CTRL_EDITABLE { "PR_PPC_DEXCR_CTRL_EDITABLE", (unsigned long) PR_PPC_DEXCR_CTRL_EDITABLE }, #endif // PR_PPC_DEXCR_CTRL_EDITABLE #ifdef PR_PPC_DEXCR_CTRL_MASK { "PR_PPC_DEXCR_CTRL_MASK", (unsigned long) PR_PPC_DEXCR_CTRL_MASK }, #endif // PR_PPC_DEXCR_CTRL_MASK #ifdef PR_PPC_DEXCR_CTRL_SET { "PR_PPC_DEXCR_CTRL_SET", (unsigned long) PR_PPC_DEXCR_CTRL_SET }, #endif // PR_PPC_DEXCR_CTRL_SET #ifdef PR_PPC_DEXCR_CTRL_SET_ONEXEC { "PR_PPC_DEXCR_CTRL_SET_ONEXEC", (unsigned long) PR_PPC_DEXCR_CTRL_SET_ONEXEC }, #endif // PR_PPC_DEXCR_CTRL_SET_ONEXEC #ifdef PR_PPC_DEXCR_IBRTPD { "PR_PPC_DEXCR_IBRTPD", (unsigned long) PR_PPC_DEXCR_IBRTPD }, #endif // PR_PPC_DEXCR_IBRTPD #ifdef PR_PPC_DEXCR_NPHIE { "PR_PPC_DEXCR_NPHIE", (unsigned long) PR_PPC_DEXCR_NPHIE }, #endif // PR_PPC_DEXCR_NPHIE #ifdef PR_PPC_DEXCR_SBHE { "PR_PPC_DEXCR_SBHE", (unsigned long) PR_PPC_DEXCR_SBHE }, #endif // PR_PPC_DEXCR_SBHE #ifdef PR_PPC_DEXCR_SRAPD { "PR_PPC_DEXCR_SRAPD", (unsigned long) PR_PPC_DEXCR_SRAPD }, #endif // PR_PPC_DEXCR_SRAPD #ifdef PR_PPC_GET_DEXCR { "PR_PPC_GET_DEXCR", (unsigned long) PR_PPC_GET_DEXCR }, #endif // PR_PPC_GET_DEXCR #ifdef PR_PPC_SET_DEXCR { "PR_PPC_SET_DEXCR", (unsigned long) PR_PPC_SET_DEXCR }, #endif // PR_PPC_SET_DEXCR #ifdef PR_RISCV_CTX_SW_FENCEI_OFF { "PR_RISCV_CTX_SW_FENCEI_OFF", (unsigned long) PR_RISCV_CTX_SW_FENCEI_OFF }, #endif // PR_RISCV_CTX_SW_FENCEI_OFF #ifdef PR_RISCV_CTX_SW_FENCEI_ON { "PR_RISCV_CTX_SW_FENCEI_ON", (unsigned long) PR_RISCV_CTX_SW_FENCEI_ON }, #endif // PR_RISCV_CTX_SW_FENCEI_ON #ifdef PR_RISCV_SCOPE_PER_PROCESS { "PR_RISCV_SCOPE_PER_PROCESS", (unsigned long) PR_RISCV_SCOPE_PER_PROCESS }, #endif // PR_RISCV_SCOPE_PER_PROCESS #ifdef PR_RISCV_SCOPE_PER_THREAD { "PR_RISCV_SCOPE_PER_THREAD", (unsigned long) PR_RISCV_SCOPE_PER_THREAD }, #endif // PR_RISCV_SCOPE_PER_THREAD #ifdef PR_RISCV_SET_ICACHE_FLUSH_CTX { "PR_RISCV_SET_ICACHE_FLUSH_CTX", (unsigned long) PR_RISCV_SET_ICACHE_FLUSH_CTX }, #endif // PR_RISCV_SET_ICACHE_FLUSH_CTX #ifdef PR_RISCV_V_GET_CONTROL { "PR_RISCV_V_GET_CONTROL", (unsigned long) PR_RISCV_V_GET_CONTROL }, #endif // PR_RISCV_V_GET_CONTROL #ifdef PR_RISCV_V_SET_CONTROL { "PR_RISCV_V_SET_CONTROL", (unsigned long) PR_RISCV_V_SET_CONTROL }, #endif // PR_RISCV_V_SET_CONTROL #ifdef PR_RISCV_V_VSTATE_CTRL_CUR_MASK { "PR_RISCV_V_VSTATE_CTRL_CUR_MASK", (unsigned long) PR_RISCV_V_VSTATE_CTRL_CUR_MASK }, #endif // PR_RISCV_V_VSTATE_CTRL_CUR_MASK #ifdef PR_RISCV_V_VSTATE_CTRL_DEFAULT { "PR_RISCV_V_VSTATE_CTRL_DEFAULT", (unsigned long) PR_RISCV_V_VSTATE_CTRL_DEFAULT }, #endif // PR_RISCV_V_VSTATE_CTRL_DEFAULT #ifdef PR_RISCV_V_VSTATE_CTRL_MASK { "PR_RISCV_V_VSTATE_CTRL_MASK", (unsigned long) PR_RISCV_V_VSTATE_CTRL_MASK }, #endif // PR_RISCV_V_VSTATE_CTRL_MASK #ifdef PR_RISCV_V_VSTATE_CTRL_NEXT_MASK { "PR_RISCV_V_VSTATE_CTRL_NEXT_MASK", (unsigned long) PR_RISCV_V_VSTATE_CTRL_NEXT_MASK }, #endif // PR_RISCV_V_VSTATE_CTRL_NEXT_MASK #ifdef PR_RISCV_V_VSTATE_CTRL_OFF { "PR_RISCV_V_VSTATE_CTRL_OFF", (unsigned long) PR_RISCV_V_VSTATE_CTRL_OFF }, #endif // PR_RISCV_V_VSTATE_CTRL_OFF #ifdef PR_RISCV_V_VSTATE_CTRL_ON { "PR_RISCV_V_VSTATE_CTRL_ON", (unsigned long) PR_RISCV_V_VSTATE_CTRL_ON }, #endif // PR_RISCV_V_VSTATE_CTRL_ON #ifdef PR_SCHED_CORE { "PR_SCHED_CORE", (unsigned long) PR_SCHED_CORE }, #endif // PR_SCHED_CORE #ifdef PR_SCHED_CORE_CREATE { "PR_SCHED_CORE_CREATE", (unsigned long) PR_SCHED_CORE_CREATE }, #endif // PR_SCHED_CORE_CREATE #ifdef PR_SCHED_CORE_GET { "PR_SCHED_CORE_GET", (unsigned long) PR_SCHED_CORE_GET }, #endif // PR_SCHED_CORE_GET #ifdef PR_SCHED_CORE_MAX { "PR_SCHED_CORE_MAX", (unsigned long) PR_SCHED_CORE_MAX }, #endif // PR_SCHED_CORE_MAX #ifdef PR_SCHED_CORE_SCOPE_PROCESS_GROUP { "PR_SCHED_CORE_SCOPE_PROCESS_GROUP", (unsigned long) PR_SCHED_CORE_SCOPE_PROCESS_GROUP }, #endif // PR_SCHED_CORE_SCOPE_PROCESS_GROUP #ifdef PR_SCHED_CORE_SCOPE_THREAD { "PR_SCHED_CORE_SCOPE_THREAD", (unsigned long) PR_SCHED_CORE_SCOPE_THREAD }, #endif // PR_SCHED_CORE_SCOPE_THREAD #ifdef PR_SCHED_CORE_SCOPE_THREAD_GROUP { "PR_SCHED_CORE_SCOPE_THREAD_GROUP", (unsigned long) PR_SCHED_CORE_SCOPE_THREAD_GROUP }, #endif // PR_SCHED_CORE_SCOPE_THREAD_GROUP #ifdef PR_SCHED_CORE_SHARE_FROM { "PR_SCHED_CORE_SHARE_FROM", (unsigned long) PR_SCHED_CORE_SHARE_FROM }, #endif // PR_SCHED_CORE_SHARE_FROM #ifdef PR_SCHED_CORE_SHARE_TO { "PR_SCHED_CORE_SHARE_TO", (unsigned long) PR_SCHED_CORE_SHARE_TO }, #endif // PR_SCHED_CORE_SHARE_TO #ifdef PR_SET_CHILD_SUBREAPER { "PR_SET_CHILD_SUBREAPER", (unsigned long) PR_SET_CHILD_SUBREAPER }, #endif // PR_SET_CHILD_SUBREAPER #ifdef PR_SET_DUMPABLE { "PR_SET_DUMPABLE", (unsigned long) PR_SET_DUMPABLE }, #endif // PR_SET_DUMPABLE #ifdef PR_SET_ENDIAN { "PR_SET_ENDIAN", (unsigned long) PR_SET_ENDIAN }, #endif // PR_SET_ENDIAN #ifdef PR_SET_FPEMU { "PR_SET_FPEMU", (unsigned long) PR_SET_FPEMU }, #endif // PR_SET_FPEMU #ifdef PR_SET_FPEXC { "PR_SET_FPEXC", (unsigned long) PR_SET_FPEXC }, #endif // PR_SET_FPEXC #ifdef PR_SET_FP_MODE { "PR_SET_FP_MODE", (unsigned long) PR_SET_FP_MODE }, #endif // PR_SET_FP_MODE #ifdef PR_SET_IO_FLUSHER { "PR_SET_IO_FLUSHER", (unsigned long) PR_SET_IO_FLUSHER }, #endif // PR_SET_IO_FLUSHER #ifdef PR_SET_KEEPCAPS { "PR_SET_KEEPCAPS", (unsigned long) PR_SET_KEEPCAPS }, #endif // PR_SET_KEEPCAPS #ifdef PR_SET_MDWE { "PR_SET_MDWE", (unsigned long) PR_SET_MDWE }, #endif // PR_SET_MDWE #ifdef PR_SET_MEMORY_MERGE { "PR_SET_MEMORY_MERGE", (unsigned long) PR_SET_MEMORY_MERGE }, #endif // PR_SET_MEMORY_MERGE #ifdef PR_SET_MM { "PR_SET_MM", (unsigned long) PR_SET_MM }, #endif // PR_SET_MM #ifdef PR_SET_MM_ARG_END { "PR_SET_MM_ARG_END", (unsigned long) PR_SET_MM_ARG_END }, #endif // PR_SET_MM_ARG_END #ifdef PR_SET_MM_ARG_START { "PR_SET_MM_ARG_START", (unsigned long) PR_SET_MM_ARG_START }, #endif // PR_SET_MM_ARG_START #ifdef PR_SET_MM_AUXV { "PR_SET_MM_AUXV", (unsigned long) PR_SET_MM_AUXV }, #endif // PR_SET_MM_AUXV #ifdef PR_SET_MM_BRK { "PR_SET_MM_BRK", (unsigned long) PR_SET_MM_BRK }, #endif // PR_SET_MM_BRK #ifdef PR_SET_MM_END_CODE { "PR_SET_MM_END_CODE", (unsigned long) PR_SET_MM_END_CODE }, #endif // PR_SET_MM_END_CODE #ifdef PR_SET_MM_END_DATA { "PR_SET_MM_END_DATA", (unsigned long) PR_SET_MM_END_DATA }, #endif // PR_SET_MM_END_DATA #ifdef PR_SET_MM_ENV_END { "PR_SET_MM_ENV_END", (unsigned long) PR_SET_MM_ENV_END }, #endif // PR_SET_MM_ENV_END #ifdef PR_SET_MM_ENV_START { "PR_SET_MM_ENV_START", (unsigned long) PR_SET_MM_ENV_START }, #endif // PR_SET_MM_ENV_START #ifdef PR_SET_MM_EXE_FILE { "PR_SET_MM_EXE_FILE", (unsigned long) PR_SET_MM_EXE_FILE }, #endif // PR_SET_MM_EXE_FILE #ifdef PR_SET_MM_MAP { "PR_SET_MM_MAP", (unsigned long) PR_SET_MM_MAP }, #endif // PR_SET_MM_MAP #ifdef PR_SET_MM_MAP_SIZE { "PR_SET_MM_MAP_SIZE", (unsigned long) PR_SET_MM_MAP_SIZE }, #endif // PR_SET_MM_MAP_SIZE #ifdef PR_SET_MM_START_BRK { "PR_SET_MM_START_BRK", (unsigned long) PR_SET_MM_START_BRK }, #endif // PR_SET_MM_START_BRK #ifdef PR_SET_MM_START_CODE { "PR_SET_MM_START_CODE", (unsigned long) PR_SET_MM_START_CODE }, #endif // PR_SET_MM_START_CODE #ifdef PR_SET_MM_START_DATA { "PR_SET_MM_START_DATA", (unsigned long) PR_SET_MM_START_DATA }, #endif // PR_SET_MM_START_DATA #ifdef PR_SET_MM_START_STACK { "PR_SET_MM_START_STACK", (unsigned long) PR_SET_MM_START_STACK }, #endif // PR_SET_MM_START_STACK #ifdef PR_SET_NAME { "PR_SET_NAME", (unsigned long) PR_SET_NAME }, #endif // PR_SET_NAME #ifdef PR_SET_NO_NEW_PRIVS { "PR_SET_NO_NEW_PRIVS", (unsigned long) PR_SET_NO_NEW_PRIVS }, #endif // PR_SET_NO_NEW_PRIVS #ifdef PR_SET_PDEATHSIG { "PR_SET_PDEATHSIG", (unsigned long) PR_SET_PDEATHSIG }, #endif // PR_SET_PDEATHSIG #ifdef PR_SET_PTRACER { "PR_SET_PTRACER", (unsigned long) PR_SET_PTRACER }, #endif // PR_SET_PTRACER #ifdef PR_SET_SECCOMP { "PR_SET_SECCOMP", (unsigned long) PR_SET_SECCOMP }, #endif // PR_SET_SECCOMP #ifdef PR_SET_SECUREBITS { "PR_SET_SECUREBITS", (unsigned long) PR_SET_SECUREBITS }, #endif // PR_SET_SECUREBITS #ifdef PR_SET_SHADOW_STACK_STATUS { "PR_SET_SHADOW_STACK_STATUS", (unsigned long) PR_SET_SHADOW_STACK_STATUS }, #endif // PR_SET_SHADOW_STACK_STATUS #ifdef PR_SET_SPECULATION_CTRL { "PR_SET_SPECULATION_CTRL", (unsigned long) PR_SET_SPECULATION_CTRL }, #endif // PR_SET_SPECULATION_CTRL #ifdef PR_SET_SYSCALL_USER_DISPATCH { "PR_SET_SYSCALL_USER_DISPATCH", (unsigned long) PR_SET_SYSCALL_USER_DISPATCH }, #endif // PR_SET_SYSCALL_USER_DISPATCH #ifdef PR_SET_TAGGED_ADDR_CTRL { "PR_SET_TAGGED_ADDR_CTRL", (unsigned long) PR_SET_TAGGED_ADDR_CTRL }, #endif // PR_SET_TAGGED_ADDR_CTRL #ifdef PR_SET_THP_DISABLE { "PR_SET_THP_DISABLE", (unsigned long) PR_SET_THP_DISABLE }, #endif // PR_SET_THP_DISABLE #ifdef PR_SET_TIMERSLACK { "PR_SET_TIMERSLACK", (unsigned long) PR_SET_TIMERSLACK }, #endif // PR_SET_TIMERSLACK #ifdef PR_SET_TIMING { "PR_SET_TIMING", (unsigned long) PR_SET_TIMING }, #endif // PR_SET_TIMING #ifdef PR_SET_TSC { "PR_SET_TSC", (unsigned long) PR_SET_TSC }, #endif // PR_SET_TSC #ifdef PR_SET_UNALIGN { "PR_SET_UNALIGN", (unsigned long) PR_SET_UNALIGN }, #endif // PR_SET_UNALIGN #ifdef PR_SET_VMA { "PR_SET_VMA", (unsigned long) PR_SET_VMA }, #endif // PR_SET_VMA #ifdef PR_SET_VMA_ANON_NAME { "PR_SET_VMA_ANON_NAME", (unsigned long) PR_SET_VMA_ANON_NAME }, #endif // PR_SET_VMA_ANON_NAME #ifdef PR_SME_GET_VL { "PR_SME_GET_VL", (unsigned long) PR_SME_GET_VL }, #endif // PR_SME_GET_VL #ifdef PR_SME_SET_VL { "PR_SME_SET_VL", (unsigned long) PR_SME_SET_VL }, #endif // PR_SME_SET_VL #ifdef PR_SME_VL_LEN_MASK { "PR_SME_VL_LEN_MASK", (unsigned long) PR_SME_VL_LEN_MASK }, #endif // PR_SME_VL_LEN_MASK #ifdef PR_SPEC_INDIRECT_BRANCH { "PR_SPEC_INDIRECT_BRANCH", (unsigned long) PR_SPEC_INDIRECT_BRANCH }, #endif // PR_SPEC_INDIRECT_BRANCH #ifdef PR_SPEC_L1D_FLUSH { "PR_SPEC_L1D_FLUSH", (unsigned long) PR_SPEC_L1D_FLUSH }, #endif // PR_SPEC_L1D_FLUSH #ifdef PR_SPEC_NOT_AFFECTED { "PR_SPEC_NOT_AFFECTED", (unsigned long) PR_SPEC_NOT_AFFECTED }, #endif // PR_SPEC_NOT_AFFECTED #ifdef PR_SPEC_STORE_BYPASS { "PR_SPEC_STORE_BYPASS", (unsigned long) PR_SPEC_STORE_BYPASS }, #endif // PR_SPEC_STORE_BYPASS #ifdef PR_SVE_GET_VL { "PR_SVE_GET_VL", (unsigned long) PR_SVE_GET_VL }, #endif // PR_SVE_GET_VL #ifdef PR_SVE_SET_VL { "PR_SVE_SET_VL", (unsigned long) PR_SVE_SET_VL }, #endif // PR_SVE_SET_VL #ifdef PR_SVE_VL_LEN_MASK { "PR_SVE_VL_LEN_MASK", (unsigned long) PR_SVE_VL_LEN_MASK }, #endif // PR_SVE_VL_LEN_MASK #ifdef PR_SYS_DISPATCH_EXCLUSIVE_ON { "PR_SYS_DISPATCH_EXCLUSIVE_ON", (unsigned long) PR_SYS_DISPATCH_EXCLUSIVE_ON }, #endif // PR_SYS_DISPATCH_EXCLUSIVE_ON #ifdef PR_SYS_DISPATCH_INCLUSIVE_ON { "PR_SYS_DISPATCH_INCLUSIVE_ON", (unsigned long) PR_SYS_DISPATCH_INCLUSIVE_ON }, #endif // PR_SYS_DISPATCH_INCLUSIVE_ON #ifdef PR_SYS_DISPATCH_OFF { "PR_SYS_DISPATCH_OFF", (unsigned long) PR_SYS_DISPATCH_OFF }, #endif // PR_SYS_DISPATCH_OFF #ifdef PR_SYS_DISPATCH_ON { "PR_SYS_DISPATCH_ON", (unsigned long) PR_SYS_DISPATCH_ON }, #endif // PR_SYS_DISPATCH_ON #ifdef PR_TASK_PERF_EVENTS_DISABLE { "PR_TASK_PERF_EVENTS_DISABLE", (unsigned long) PR_TASK_PERF_EVENTS_DISABLE }, #endif // PR_TASK_PERF_EVENTS_DISABLE #ifdef PR_TASK_PERF_EVENTS_ENABLE { "PR_TASK_PERF_EVENTS_ENABLE", (unsigned long) PR_TASK_PERF_EVENTS_ENABLE }, #endif // PR_TASK_PERF_EVENTS_ENABLE #ifdef PR_TIMER_CREATE_RESTORE_IDS { "PR_TIMER_CREATE_RESTORE_IDS", (unsigned long) PR_TIMER_CREATE_RESTORE_IDS }, #endif // PR_TIMER_CREATE_RESTORE_IDS #ifdef PR_TIMER_CREATE_RESTORE_IDS_GET { "PR_TIMER_CREATE_RESTORE_IDS_GET", (unsigned long) PR_TIMER_CREATE_RESTORE_IDS_GET }, #endif // PR_TIMER_CREATE_RESTORE_IDS_GET #ifdef PR_TIMER_CREATE_RESTORE_IDS_OFF { "PR_TIMER_CREATE_RESTORE_IDS_OFF", (unsigned long) PR_TIMER_CREATE_RESTORE_IDS_OFF }, #endif // PR_TIMER_CREATE_RESTORE_IDS_OFF #ifdef PR_TIMER_CREATE_RESTORE_IDS_ON { "PR_TIMER_CREATE_RESTORE_IDS_ON", (unsigned long) PR_TIMER_CREATE_RESTORE_IDS_ON }, #endif // PR_TIMER_CREATE_RESTORE_IDS_ON #ifdef PR_TIMING_STATISTICAL { "PR_TIMING_STATISTICAL", (unsigned long) PR_TIMING_STATISTICAL }, #endif // PR_TIMING_STATISTICAL #ifdef PR_TIMING_TIMESTAMP { "PR_TIMING_TIMESTAMP", (unsigned long) PR_TIMING_TIMESTAMP }, #endif // PR_TIMING_TIMESTAMP #ifdef PR_TSC_ENABLE { "PR_TSC_ENABLE", (unsigned long) PR_TSC_ENABLE }, #endif // PR_TSC_ENABLE #ifdef PR_TSC_SIGSEGV { "PR_TSC_SIGSEGV", (unsigned long) PR_TSC_SIGSEGV }, #endif // PR_TSC_SIGSEGV #ifdef PR_UNALIGN_NOPRINT { "PR_UNALIGN_NOPRINT", (unsigned long) PR_UNALIGN_NOPRINT }, #endif // PR_UNALIGN_NOPRINT #ifdef PR_UNALIGN_SIGBUS { "PR_UNALIGN_SIGBUS", (unsigned long) PR_UNALIGN_SIGBUS }, #endif // PR_UNALIGN_SIGBUS #ifdef PTRDIFF_WIDTH { "PTRDIFF_WIDTH", (unsigned long) PTRDIFF_WIDTH }, #endif // PTRDIFF_WIDTH #ifdef RLIMIT_AS { "RLIMIT_AS", (unsigned long) RLIMIT_AS }, #endif // RLIMIT_AS #ifdef RLIMIT_CORE { "RLIMIT_CORE", (unsigned long) RLIMIT_CORE }, #endif // RLIMIT_CORE #ifdef RLIMIT_CPU { "RLIMIT_CPU", (unsigned long) RLIMIT_CPU }, #endif // RLIMIT_CPU #ifdef RLIMIT_DATA { "RLIMIT_DATA", (unsigned long) RLIMIT_DATA }, #endif // RLIMIT_DATA #ifdef RLIMIT_FSIZE { "RLIMIT_FSIZE", (unsigned long) RLIMIT_FSIZE }, #endif // RLIMIT_FSIZE #ifdef RLIMIT_LOCKS { "RLIMIT_LOCKS", (unsigned long) RLIMIT_LOCKS }, #endif // RLIMIT_LOCKS #ifdef RLIMIT_MEMLOCK { "RLIMIT_MEMLOCK", (unsigned long) RLIMIT_MEMLOCK }, #endif // RLIMIT_MEMLOCK #ifdef RLIMIT_MSGQUEUE { "RLIMIT_MSGQUEUE", (unsigned long) RLIMIT_MSGQUEUE }, #endif // RLIMIT_MSGQUEUE #ifdef RLIMIT_NICE { "RLIMIT_NICE", (unsigned long) RLIMIT_NICE }, #endif // RLIMIT_NICE #ifdef RLIMIT_NLIMITS { "RLIMIT_NLIMITS", (unsigned long) RLIMIT_NLIMITS }, #endif // RLIMIT_NLIMITS #ifdef RLIMIT_NOFILE { "RLIMIT_NOFILE", (unsigned long) RLIMIT_NOFILE }, #endif // RLIMIT_NOFILE #ifdef RLIMIT_NPROC { "RLIMIT_NPROC", (unsigned long) RLIMIT_NPROC }, #endif // RLIMIT_NPROC #ifdef RLIMIT_OFILE { "RLIMIT_OFILE", (unsigned long) RLIMIT_OFILE }, #endif // RLIMIT_OFILE #ifdef RLIMIT_RSS { "RLIMIT_RSS", (unsigned long) RLIMIT_RSS }, #endif // RLIMIT_RSS #ifdef RLIMIT_RTPRIO { "RLIMIT_RTPRIO", (unsigned long) RLIMIT_RTPRIO }, #endif // RLIMIT_RTPRIO #ifdef RLIMIT_RTTIME { "RLIMIT_RTTIME", (unsigned long) RLIMIT_RTTIME }, #endif // RLIMIT_RTTIME #ifdef RLIMIT_SIGPENDING { "RLIMIT_SIGPENDING", (unsigned long) RLIMIT_SIGPENDING }, #endif // RLIMIT_SIGPENDING #ifdef RLIMIT_STACK { "RLIMIT_STACK", (unsigned long) RLIMIT_STACK }, #endif // RLIMIT_STACK #ifdef RLIM_NLIMITS { "RLIM_NLIMITS", (unsigned long) RLIM_NLIMITS }, #endif // RLIM_NLIMITS #ifdef RLIM_SAVED_CUR { "RLIM_SAVED_CUR", (unsigned long) RLIM_SAVED_CUR }, #endif // RLIM_SAVED_CUR #ifdef RLIM_SAVED_MAX { "RLIM_SAVED_MAX", (unsigned long) RLIM_SAVED_MAX }, #endif // RLIM_SAVED_MAX #ifdef RNDADDENTROPY { "RNDADDENTROPY", (unsigned long) RNDADDENTROPY }, #endif // RNDADDENTROPY #ifdef RNDADDTOENTCNT { "RNDADDTOENTCNT", (unsigned long) RNDADDTOENTCNT }, #endif // RNDADDTOENTCNT #ifdef RNDCLEARPOOL { "RNDCLEARPOOL", (unsigned long) RNDCLEARPOOL }, #endif // RNDCLEARPOOL #ifdef RNDGETENTCNT { "RNDGETENTCNT", (unsigned long) RNDGETENTCNT }, #endif // RNDGETENTCNT #ifdef RNDGETPOOL { "RNDGETPOOL", (unsigned long) RNDGETPOOL }, #endif // RNDGETPOOL #ifdef RNDRESEEDCRNG { "RNDRESEEDCRNG", (unsigned long) RNDRESEEDCRNG }, #endif // RNDRESEEDCRNG #ifdef RNDZAPENTCNT { "RNDZAPENTCNT", (unsigned long) RNDZAPENTCNT }, #endif // RNDZAPENTCNT #ifdef R_OK { "R_OK", (unsigned long) R_OK }, #endif // R_OK #ifdef RUSAGE_CHILDREN { "RUSAGE_CHILDREN", (unsigned long) RUSAGE_CHILDREN }, #endif // RUSAGE_CHILDREN #ifdef RUSAGE_SELF { "RUSAGE_SELF", (unsigned long) RUSAGE_SELF }, #endif // RUSAGE_SELF #ifdef SA_INTERRUPT { "SA_INTERRUPT", (unsigned long) SA_INTERRUPT }, #endif // SA_INTERRUPT #ifdef SA_NOCLDSTOP { "SA_NOCLDSTOP", (unsigned long) SA_NOCLDSTOP }, #endif // SA_NOCLDSTOP #ifdef SA_NOCLDWAIT { "SA_NOCLDWAIT", (unsigned long) SA_NOCLDWAIT }, #endif // SA_NOCLDWAIT #ifdef SA_NODEFER { "SA_NODEFER", (unsigned long) SA_NODEFER }, #endif // SA_NODEFER #ifdef SA_NOMASK { "SA_NOMASK", (unsigned long) SA_NOMASK }, #endif // SA_NOMASK #ifdef SA_ONESHOT { "SA_ONESHOT", (unsigned long) SA_ONESHOT }, #endif // SA_ONESHOT #ifdef SA_ONSTACK { "SA_ONSTACK", (unsigned long) SA_ONSTACK }, #endif // SA_ONSTACK #ifdef SA_RESETHAND { "SA_RESETHAND", (unsigned long) SA_RESETHAND }, #endif // SA_RESETHAND #ifdef SA_RESTART { "SA_RESTART", (unsigned long) SA_RESTART }, #endif // SA_RESTART #ifdef SA_SIGINFO { "SA_SIGINFO", (unsigned long) SA_SIGINFO }, #endif // SA_SIGINFO #ifdef SA_STACK { "SA_STACK", (unsigned long) SA_STACK }, #endif // SA_STACK #ifdef S_BLKSIZE { "S_BLKSIZE", (unsigned long) S_BLKSIZE }, #endif // S_BLKSIZE #ifdef SCHED_BATCH { "SCHED_BATCH", (unsigned long) SCHED_BATCH }, #endif // SCHED_BATCH #ifdef SCHED_DEADLINE { "SCHED_DEADLINE", (unsigned long) SCHED_DEADLINE }, #endif // SCHED_DEADLINE #ifdef SCHED_EXT { "SCHED_EXT", (unsigned long) SCHED_EXT }, #endif // SCHED_EXT #ifdef SCHED_FIFO { "SCHED_FIFO", (unsigned long) SCHED_FIFO }, #endif // SCHED_FIFO #ifdef SCHED_FLAG_DL_OVERRUN { "SCHED_FLAG_DL_OVERRUN", (unsigned long) SCHED_FLAG_DL_OVERRUN }, #endif // SCHED_FLAG_DL_OVERRUN #ifdef SCHED_FLAG_KEEP_PARAMS { "SCHED_FLAG_KEEP_PARAMS", (unsigned long) SCHED_FLAG_KEEP_PARAMS }, #endif // SCHED_FLAG_KEEP_PARAMS #ifdef SCHED_FLAG_KEEP_POLICY { "SCHED_FLAG_KEEP_POLICY", (unsigned long) SCHED_FLAG_KEEP_POLICY }, #endif // SCHED_FLAG_KEEP_POLICY #ifdef SCHED_FLAG_RECLAIM { "SCHED_FLAG_RECLAIM", (unsigned long) SCHED_FLAG_RECLAIM }, #endif // SCHED_FLAG_RECLAIM #ifdef SCHED_FLAG_RESET_ON_FORK { "SCHED_FLAG_RESET_ON_FORK", (unsigned long) SCHED_FLAG_RESET_ON_FORK }, #endif // SCHED_FLAG_RESET_ON_FORK #ifdef SCHED_FLAG_UTIL_CLAMP_MAX { "SCHED_FLAG_UTIL_CLAMP_MAX", (unsigned long) SCHED_FLAG_UTIL_CLAMP_MAX }, #endif // SCHED_FLAG_UTIL_CLAMP_MAX #ifdef SCHED_FLAG_UTIL_CLAMP_MIN { "SCHED_FLAG_UTIL_CLAMP_MIN", (unsigned long) SCHED_FLAG_UTIL_CLAMP_MIN }, #endif // SCHED_FLAG_UTIL_CLAMP_MIN #ifdef SCHED_IDLE { "SCHED_IDLE", (unsigned long) SCHED_IDLE }, #endif // SCHED_IDLE #ifdef SCHED_NORMAL { "SCHED_NORMAL", (unsigned long) SCHED_NORMAL }, #endif // SCHED_NORMAL #ifdef SCHED_RESET_ON_FORK { "SCHED_RESET_ON_FORK", (unsigned long) SCHED_RESET_ON_FORK }, #endif // SCHED_RESET_ON_FORK #ifdef SCHED_RR { "SCHED_RR", (unsigned long) SCHED_RR }, #endif // SCHED_RR #ifdef SCM_DEVMEM_DMABUF { "SCM_DEVMEM_DMABUF", (unsigned long) SCM_DEVMEM_DMABUF }, #endif // SCM_DEVMEM_DMABUF #ifdef SCM_DEVMEM_LINEAR { "SCM_DEVMEM_LINEAR", (unsigned long) SCM_DEVMEM_LINEAR }, #endif // SCM_DEVMEM_LINEAR #ifdef SCM_INQ { "SCM_INQ", (unsigned long) SCM_INQ }, #endif // SCM_INQ #ifdef SCM_RIGHTS { "SCM_RIGHTS", (unsigned long) SCM_RIGHTS }, #endif // SCM_RIGHTS #ifdef SCM_TIMESTAMPING_OPT_STATS { "SCM_TIMESTAMPING_OPT_STATS", (unsigned long) SCM_TIMESTAMPING_OPT_STATS }, #endif // SCM_TIMESTAMPING_OPT_STATS #ifdef SCM_TIMESTAMPING_PKTINFO { "SCM_TIMESTAMPING_PKTINFO", (unsigned long) SCM_TIMESTAMPING_PKTINFO }, #endif // SCM_TIMESTAMPING_PKTINFO #ifdef SCM_TIMESTAMPING { "SCM_TIMESTAMPING", (unsigned long) SCM_TIMESTAMPING }, #endif // SCM_TIMESTAMPING #ifdef SCM_TIMESTAMPNS { "SCM_TIMESTAMPNS", (unsigned long) SCM_TIMESTAMPNS }, #endif // SCM_TIMESTAMPNS #ifdef SCM_TIMESTAMP { "SCM_TIMESTAMP", (unsigned long) SCM_TIMESTAMP }, #endif // SCM_TIMESTAMP #ifdef SCM_TS_OPT_ID { "SCM_TS_OPT_ID", (unsigned long) SCM_TS_OPT_ID }, #endif // SCM_TS_OPT_ID #ifdef SCM_TXTIME { "SCM_TXTIME", (unsigned long) SCM_TXTIME }, #endif // SCM_TXTIME #ifdef SCM_WIFI_STATUS { "SCM_WIFI_STATUS", (unsigned long) SCM_WIFI_STATUS }, #endif // SCM_WIFI_STATUS #ifdef SEEK_CUR { "SEEK_CUR", (unsigned long) SEEK_CUR }, #endif // SEEK_CUR #ifdef SEEK_DATA { "SEEK_DATA", (unsigned long) SEEK_DATA }, #endif // SEEK_DATA #ifdef SEEK_END { "SEEK_END", (unsigned long) SEEK_END }, #endif // SEEK_END #ifdef SEEK_HOLE { "SEEK_HOLE", (unsigned long) SEEK_HOLE }, #endif // SEEK_HOLE #ifdef SEEK_MAX { "SEEK_MAX", (unsigned long) SEEK_MAX }, #endif // SEEK_MAX #ifdef SEEK_SET { "SEEK_SET", (unsigned long) SEEK_SET }, #endif // SEEK_SET #ifdef SEGV_ACCADI { "SEGV_ACCADI", (unsigned long) SEGV_ACCADI }, #endif // SEGV_ACCADI #ifdef SEGV_ACCERR { "SEGV_ACCERR", (unsigned long) SEGV_ACCERR }, #endif // SEGV_ACCERR #ifdef SEGV_ADIDERR { "SEGV_ADIDERR", (unsigned long) SEGV_ADIDERR }, #endif // SEGV_ADIDERR #ifdef SEGV_ADIPERR { "SEGV_ADIPERR", (unsigned long) SEGV_ADIPERR }, #endif // SEGV_ADIPERR #ifdef SEGV_BNDERR { "SEGV_BNDERR", (unsigned long) SEGV_BNDERR }, #endif // SEGV_BNDERR #ifdef SEGV_CPERR { "SEGV_CPERR", (unsigned long) SEGV_CPERR }, #endif // SEGV_CPERR #ifdef SEGV_MAPERR { "SEGV_MAPERR", (unsigned long) SEGV_MAPERR }, #endif // SEGV_MAPERR #ifdef SEGV_MTEAERR { "SEGV_MTEAERR", (unsigned long) SEGV_MTEAERR }, #endif // SEGV_MTEAERR #ifdef SEGV_MTESERR { "SEGV_MTESERR", (unsigned long) SEGV_MTESERR }, #endif // SEGV_MTESERR #ifdef SEGV_PKUERR { "SEGV_PKUERR", (unsigned long) SEGV_PKUERR }, #endif // SEGV_PKUERR #ifdef SERIAL_IO_AU { "SERIAL_IO_AU", (unsigned long) SERIAL_IO_AU }, #endif // SERIAL_IO_AU #ifdef SERIAL_IO_HUB6 { "SERIAL_IO_HUB6", (unsigned long) SERIAL_IO_HUB6 }, #endif // SERIAL_IO_HUB6 #ifdef SERIAL_IO_MEM16 { "SERIAL_IO_MEM16", (unsigned long) SERIAL_IO_MEM16 }, #endif // SERIAL_IO_MEM16 #ifdef SERIAL_IO_MEM { "SERIAL_IO_MEM", (unsigned long) SERIAL_IO_MEM }, #endif // SERIAL_IO_MEM #ifdef SERIAL_IO_MEM32 { "SERIAL_IO_MEM32", (unsigned long) SERIAL_IO_MEM32 }, #endif // SERIAL_IO_MEM32 #ifdef SERIAL_IO_MEM32BE { "SERIAL_IO_MEM32BE", (unsigned long) SERIAL_IO_MEM32BE }, #endif // SERIAL_IO_MEM32BE #ifdef SERIAL_IO_PORT { "SERIAL_IO_PORT", (unsigned long) SERIAL_IO_PORT }, #endif // SERIAL_IO_PORT #ifdef SERIAL_IO_TSI { "SERIAL_IO_TSI", (unsigned long) SERIAL_IO_TSI }, #endif // SERIAL_IO_TSI #ifdef SER_RS485_ADDRB { "SER_RS485_ADDRB", (unsigned long) SER_RS485_ADDRB }, #endif // SER_RS485_ADDRB #ifdef SER_RS485_ADDR_DEST { "SER_RS485_ADDR_DEST", (unsigned long) SER_RS485_ADDR_DEST }, #endif // SER_RS485_ADDR_DEST #ifdef SER_RS485_ADDR_RECV { "SER_RS485_ADDR_RECV", (unsigned long) SER_RS485_ADDR_RECV }, #endif // SER_RS485_ADDR_RECV #ifdef SER_RS485_ENABLED { "SER_RS485_ENABLED", (unsigned long) SER_RS485_ENABLED }, #endif // SER_RS485_ENABLED #ifdef SER_RS485_MODE_RS422 { "SER_RS485_MODE_RS422", (unsigned long) SER_RS485_MODE_RS422 }, #endif // SER_RS485_MODE_RS422 #ifdef SER_RS485_RTS_AFTER_SEND { "SER_RS485_RTS_AFTER_SEND", (unsigned long) SER_RS485_RTS_AFTER_SEND }, #endif // SER_RS485_RTS_AFTER_SEND #ifdef SER_RS485_RTS_ON_SEND { "SER_RS485_RTS_ON_SEND", (unsigned long) SER_RS485_RTS_ON_SEND }, #endif // SER_RS485_RTS_ON_SEND #ifdef SER_RS485_RX_DURING_TX { "SER_RS485_RX_DURING_TX", (unsigned long) SER_RS485_RX_DURING_TX }, #endif // SER_RS485_RX_DURING_TX #ifdef SER_RS485_TERMINATE_BUS { "SER_RS485_TERMINATE_BUS", (unsigned long) SER_RS485_TERMINATE_BUS }, #endif // SER_RS485_TERMINATE_BUS #ifdef SHADOW_STACK_SET_TOKEN { "SHADOW_STACK_SET_TOKEN", (unsigned long) SHADOW_STACK_SET_TOKEN }, #endif // SHADOW_STACK_SET_TOKEN #ifdef SHUT_RD { "SHUT_RD", (unsigned long) SHUT_RD }, #endif // SHUT_RD #ifdef SHUT_RDWR { "SHUT_RDWR", (unsigned long) SHUT_RDWR }, #endif // SHUT_RDWR #ifdef SHUT_WR { "SHUT_WR", (unsigned long) SHUT_WR }, #endif // SHUT_WR #ifdef SI_ASYNCIO { "SI_ASYNCIO", (unsigned long) SI_ASYNCIO }, #endif // SI_ASYNCIO #ifdef SI_ASYNCNL { "SI_ASYNCNL", (unsigned long) SI_ASYNCNL }, #endif // SI_ASYNCNL #ifdef SI_DETHREAD { "SI_DETHREAD", (unsigned long) SI_DETHREAD }, #endif // SI_DETHREAD #ifdef S_IEXEC { "S_IEXEC", (unsigned long) S_IEXEC }, #endif // S_IEXEC #ifdef S_IFBLK { "S_IFBLK", (unsigned long) S_IFBLK }, #endif // S_IFBLK #ifdef S_IFCHR { "S_IFCHR", (unsigned long) S_IFCHR }, #endif // S_IFCHR #ifdef S_IFDIR { "S_IFDIR", (unsigned long) S_IFDIR }, #endif // S_IFDIR #ifdef S_IFIFO { "S_IFIFO", (unsigned long) S_IFIFO }, #endif // S_IFIFO #ifdef S_IFLNK { "S_IFLNK", (unsigned long) S_IFLNK }, #endif // S_IFLNK #ifdef S_IFMT { "S_IFMT", (unsigned long) S_IFMT }, #endif // S_IFMT #ifdef S_IFREG { "S_IFREG", (unsigned long) S_IFREG }, #endif // S_IFREG #ifdef S_IFSOCK { "S_IFSOCK", (unsigned long) S_IFSOCK }, #endif // S_IFSOCK #ifdef SIGABRT { "SIGABRT", (unsigned long) SIGABRT }, #endif // SIGABRT #ifdef SIGALRM { "SIGALRM", (unsigned long) SIGALRM }, #endif // SIGALRM #ifdef SIGBUS { "SIGBUS", (unsigned long) SIGBUS }, #endif // SIGBUS #ifdef SIGCHLD { "SIGCHLD", (unsigned long) SIGCHLD }, #endif // SIGCHLD #ifdef SIGCLD { "SIGCLD", (unsigned long) SIGCLD }, #endif // SIGCLD #ifdef SIGCONT { "SIGCONT", (unsigned long) SIGCONT }, #endif // SIGCONT #ifdef SIGEV_NONE { "SIGEV_NONE", (unsigned long) SIGEV_NONE }, #endif // SIGEV_NONE #ifdef SIGEV_SIGNAL { "SIGEV_SIGNAL", (unsigned long) SIGEV_SIGNAL }, #endif // SIGEV_SIGNAL #ifdef SIGEV_THREAD_ID { "SIGEV_THREAD_ID", (unsigned long) SIGEV_THREAD_ID }, #endif // SIGEV_THREAD_ID #ifdef SIGEV_THREAD { "SIGEV_THREAD", (unsigned long) SIGEV_THREAD }, #endif // SIGEV_THREAD #ifdef SIGFPE { "SIGFPE", (unsigned long) SIGFPE }, #endif // SIGFPE #ifdef SIGHUP { "SIGHUP", (unsigned long) SIGHUP }, #endif // SIGHUP #ifdef SIGILL { "SIGILL", (unsigned long) SIGILL }, #endif // SIGILL #ifdef SIGINT { "SIGINT", (unsigned long) SIGINT }, #endif // SIGINT #ifdef SIGIO { "SIGIO", (unsigned long) SIGIO }, #endif // SIGIO #ifdef SIGIOT { "SIGIOT", (unsigned long) SIGIOT }, #endif // SIGIOT #ifdef SIGKILL { "SIGKILL", (unsigned long) SIGKILL }, #endif // SIGKILL #ifdef SIGPIPE { "SIGPIPE", (unsigned long) SIGPIPE }, #endif // SIGPIPE #ifdef SIGPOLL { "SIGPOLL", (unsigned long) SIGPOLL }, #endif // SIGPOLL #ifdef SIGPROF { "SIGPROF", (unsigned long) SIGPROF }, #endif // SIGPROF #ifdef SIGPWR { "SIGPWR", (unsigned long) SIGPWR }, #endif // SIGPWR #ifdef SIGQUIT { "SIGQUIT", (unsigned long) SIGQUIT }, #endif // SIGQUIT #ifdef SIGSEGV { "SIGSEGV", (unsigned long) SIGSEGV }, #endif // SIGSEGV #ifdef SIGSTKFLT { "SIGSTKFLT", (unsigned long) SIGSTKFLT }, #endif // SIGSTKFLT #ifdef SIGSTKSZ { "SIGSTKSZ", (unsigned long) SIGSTKSZ }, #endif // SIGSTKSZ #ifdef SIGSTOP { "SIGSTOP", (unsigned long) SIGSTOP }, #endif // SIGSTOP #ifdef SIGSYS { "SIGSYS", (unsigned long) SIGSYS }, #endif // SIGSYS #ifdef SIGTERM { "SIGTERM", (unsigned long) SIGTERM }, #endif // SIGTERM #ifdef SIGTRAP { "SIGTRAP", (unsigned long) SIGTRAP }, #endif // SIGTRAP #ifdef SIGTSTP { "SIGTSTP", (unsigned long) SIGTSTP }, #endif // SIGTSTP #ifdef SIGTTIN { "SIGTTIN", (unsigned long) SIGTTIN }, #endif // SIGTTIN #ifdef SIGTTOU { "SIGTTOU", (unsigned long) SIGTTOU }, #endif // SIGTTOU #ifdef SIGURG { "SIGURG", (unsigned long) SIGURG }, #endif // SIGURG #ifdef SIGUSR1 { "SIGUSR1", (unsigned long) SIGUSR1 }, #endif // SIGUSR1 #ifdef SIGUSR2 { "SIGUSR2", (unsigned long) SIGUSR2 }, #endif // SIGUSR2 #ifdef SIGVTALRM { "SIGVTALRM", (unsigned long) SIGVTALRM }, #endif // SIGVTALRM #ifdef SIGWINCH { "SIGWINCH", (unsigned long) SIGWINCH }, #endif // SIGWINCH #ifdef SIGXCPU { "SIGXCPU", (unsigned long) SIGXCPU }, #endif // SIGXCPU #ifdef SIGXFSZ { "SIGXFSZ", (unsigned long) SIGXFSZ }, #endif // SIGXFSZ #ifdef SI_KERNEL { "SI_KERNEL", (unsigned long) SI_KERNEL }, #endif // SI_KERNEL #ifdef SI_MESGQ { "SI_MESGQ", (unsigned long) SI_MESGQ }, #endif // SI_MESGQ #ifdef SIOCADDDLCI { "SIOCADDDLCI", (unsigned long) SIOCADDDLCI }, #endif // SIOCADDDLCI #ifdef SIOCADDMULTI { "SIOCADDMULTI", (unsigned long) SIOCADDMULTI }, #endif // SIOCADDMULTI #ifdef SIOCADDRT { "SIOCADDRT", (unsigned long) SIOCADDRT }, #endif // SIOCADDRT #ifdef SIOCATMARK { "SIOCATMARK", (unsigned long) SIOCATMARK }, #endif // SIOCATMARK #ifdef SIOCBONDCHANGEACTIVE { "SIOCBONDCHANGEACTIVE", (unsigned long) SIOCBONDCHANGEACTIVE }, #endif // SIOCBONDCHANGEACTIVE #ifdef SIOCBONDENSLAVE { "SIOCBONDENSLAVE", (unsigned long) SIOCBONDENSLAVE }, #endif // SIOCBONDENSLAVE #ifdef SIOCBONDINFOQUERY { "SIOCBONDINFOQUERY", (unsigned long) SIOCBONDINFOQUERY }, #endif // SIOCBONDINFOQUERY #ifdef SIOCBONDRELEASE { "SIOCBONDRELEASE", (unsigned long) SIOCBONDRELEASE }, #endif // SIOCBONDRELEASE #ifdef SIOCBONDSETHWADDR { "SIOCBONDSETHWADDR", (unsigned long) SIOCBONDSETHWADDR }, #endif // SIOCBONDSETHWADDR #ifdef SIOCBONDSLAVEINFOQUERY { "SIOCBONDSLAVEINFOQUERY", (unsigned long) SIOCBONDSLAVEINFOQUERY }, #endif // SIOCBONDSLAVEINFOQUERY #ifdef SIOCBRADDBR { "SIOCBRADDBR", (unsigned long) SIOCBRADDBR }, #endif // SIOCBRADDBR #ifdef SIOCBRADDIF { "SIOCBRADDIF", (unsigned long) SIOCBRADDIF }, #endif // SIOCBRADDIF #ifdef SIOCBRDELBR { "SIOCBRDELBR", (unsigned long) SIOCBRDELBR }, #endif // SIOCBRDELBR #ifdef SIOCBRDELIF { "SIOCBRDELIF", (unsigned long) SIOCBRDELIF }, #endif // SIOCBRDELIF #ifdef SIOCDARP { "SIOCDARP", (unsigned long) SIOCDARP }, #endif // SIOCDARP #ifdef SIOCDELDLCI { "SIOCDELDLCI", (unsigned long) SIOCDELDLCI }, #endif // SIOCDELDLCI #ifdef SIOCDELMULTI { "SIOCDELMULTI", (unsigned long) SIOCDELMULTI }, #endif // SIOCDELMULTI #ifdef SIOCDELRT { "SIOCDELRT", (unsigned long) SIOCDELRT }, #endif // SIOCDELRT #ifdef SIOCDEVPRIVATE { "SIOCDEVPRIVATE", (unsigned long) SIOCDEVPRIVATE }, #endif // SIOCDEVPRIVATE #ifdef SIOCDIFADDR { "SIOCDIFADDR", (unsigned long) SIOCDIFADDR }, #endif // SIOCDIFADDR #ifdef SIOCDRARP { "SIOCDRARP", (unsigned long) SIOCDRARP }, #endif // SIOCDRARP #ifdef SIOCETHTOOL { "SIOCETHTOOL", (unsigned long) SIOCETHTOOL }, #endif // SIOCETHTOOL #ifdef SIOCGARP { "SIOCGARP", (unsigned long) SIOCGARP }, #endif // SIOCGARP #ifdef SIOCGHWTSTAMP { "SIOCGHWTSTAMP", (unsigned long) SIOCGHWTSTAMP }, #endif // SIOCGHWTSTAMP #ifdef SIOCGIFADDR { "SIOCGIFADDR", (unsigned long) SIOCGIFADDR }, #endif // SIOCGIFADDR #ifdef SIOCGIFBR { "SIOCGIFBR", (unsigned long) SIOCGIFBR }, #endif // SIOCGIFBR #ifdef SIOCGIFBRDADDR { "SIOCGIFBRDADDR", (unsigned long) SIOCGIFBRDADDR }, #endif // SIOCGIFBRDADDR #ifdef SIOCGIFCONF { "SIOCGIFCONF", (unsigned long) SIOCGIFCONF }, #endif // SIOCGIFCONF #ifdef SIOCGIFCOUNT { "SIOCGIFCOUNT", (unsigned long) SIOCGIFCOUNT }, #endif // SIOCGIFCOUNT #ifdef SIOCGIFDSTADDR { "SIOCGIFDSTADDR", (unsigned long) SIOCGIFDSTADDR }, #endif // SIOCGIFDSTADDR #ifdef SIOCGIFENCAP { "SIOCGIFENCAP", (unsigned long) SIOCGIFENCAP }, #endif // SIOCGIFENCAP #ifdef SIOCGIFFLAGS { "SIOCGIFFLAGS", (unsigned long) SIOCGIFFLAGS }, #endif // SIOCGIFFLAGS #ifdef SIOCGIFHWADDR { "SIOCGIFHWADDR", (unsigned long) SIOCGIFHWADDR }, #endif // SIOCGIFHWADDR #ifdef SIOCGIFINDEX { "SIOCGIFINDEX", (unsigned long) SIOCGIFINDEX }, #endif // SIOCGIFINDEX #ifdef SIOCGIFMAP { "SIOCGIFMAP", (unsigned long) SIOCGIFMAP }, #endif // SIOCGIFMAP #ifdef SIOCGIFMEM { "SIOCGIFMEM", (unsigned long) SIOCGIFMEM }, #endif // SIOCGIFMEM #ifdef SIOCGIFMETRIC { "SIOCGIFMETRIC", (unsigned long) SIOCGIFMETRIC }, #endif // SIOCGIFMETRIC #ifdef SIOCGIFMTU { "SIOCGIFMTU", (unsigned long) SIOCGIFMTU }, #endif // SIOCGIFMTU #ifdef SIOCGIFNAME { "SIOCGIFNAME", (unsigned long) SIOCGIFNAME }, #endif // SIOCGIFNAME #ifdef SIOCGIFNETMASK { "SIOCGIFNETMASK", (unsigned long) SIOCGIFNETMASK }, #endif // SIOCGIFNETMASK #ifdef SIOCGIFPFLAGS { "SIOCGIFPFLAGS", (unsigned long) SIOCGIFPFLAGS }, #endif // SIOCGIFPFLAGS #ifdef SIOCGIFSLAVE { "SIOCGIFSLAVE", (unsigned long) SIOCGIFSLAVE }, #endif // SIOCGIFSLAVE #ifdef SIOCGIFTXQLEN { "SIOCGIFTXQLEN", (unsigned long) SIOCGIFTXQLEN }, #endif // SIOCGIFTXQLEN #ifdef SIOCGIFVLAN { "SIOCGIFVLAN", (unsigned long) SIOCGIFVLAN }, #endif // SIOCGIFVLAN #ifdef SIOCGMIIPHY { "SIOCGMIIPHY", (unsigned long) SIOCGMIIPHY }, #endif // SIOCGMIIPHY #ifdef SIOCGMIIREG { "SIOCGMIIREG", (unsigned long) SIOCGMIIREG }, #endif // SIOCGMIIREG #ifdef SIOCGPGRP { "SIOCGPGRP", (unsigned long) SIOCGPGRP }, #endif // SIOCGPGRP #ifdef SIOCGRARP { "SIOCGRARP", (unsigned long) SIOCGRARP }, #endif // SIOCGRARP #ifdef SIOCGSKNS { "SIOCGSKNS", (unsigned long) SIOCGSKNS }, #endif // SIOCGSKNS #ifdef SIOCGSTAMP_NEW { "SIOCGSTAMP_NEW", (unsigned long) SIOCGSTAMP_NEW }, #endif // SIOCGSTAMP_NEW #ifdef SIOCGSTAMPNS_NEW { "SIOCGSTAMPNS_NEW", (unsigned long) SIOCGSTAMPNS_NEW }, #endif // SIOCGSTAMPNS_NEW #ifdef SIOCGSTAMPNS_OLD { "SIOCGSTAMPNS_OLD", (unsigned long) SIOCGSTAMPNS_OLD }, #endif // SIOCGSTAMPNS_OLD #ifdef SIOCGSTAMPNS { "SIOCGSTAMPNS", (unsigned long) SIOCGSTAMPNS }, #endif // SIOCGSTAMPNS #ifdef SIOCGSTAMP_OLD { "SIOCGSTAMP_OLD", (unsigned long) SIOCGSTAMP_OLD }, #endif // SIOCGSTAMP_OLD #ifdef SIOCGSTAMP { "SIOCGSTAMP", (unsigned long) SIOCGSTAMP }, #endif // SIOCGSTAMP #ifdef SIOCINQ { "SIOCINQ", (unsigned long) SIOCINQ }, #endif // SIOCINQ #ifdef SIOCOUTQNSD { "SIOCOUTQNSD", (unsigned long) SIOCOUTQNSD }, #endif // SIOCOUTQNSD #ifdef SIOCOUTQ { "SIOCOUTQ", (unsigned long) SIOCOUTQ }, #endif // SIOCOUTQ #ifdef SIOCPROTOPRIVATE { "SIOCPROTOPRIVATE", (unsigned long) SIOCPROTOPRIVATE }, #endif // SIOCPROTOPRIVATE #ifdef SIOCRTMSG { "SIOCRTMSG", (unsigned long) SIOCRTMSG }, #endif // SIOCRTMSG #ifdef SIOCSARP { "SIOCSARP", (unsigned long) SIOCSARP }, #endif // SIOCSARP #ifdef SIOCSHWTSTAMP { "SIOCSHWTSTAMP", (unsigned long) SIOCSHWTSTAMP }, #endif // SIOCSHWTSTAMP #ifdef SIOCSIFADDR { "SIOCSIFADDR", (unsigned long) SIOCSIFADDR }, #endif // SIOCSIFADDR #ifdef SIOCSIFBR { "SIOCSIFBR", (unsigned long) SIOCSIFBR }, #endif // SIOCSIFBR #ifdef SIOCSIFBRDADDR { "SIOCSIFBRDADDR", (unsigned long) SIOCSIFBRDADDR }, #endif // SIOCSIFBRDADDR #ifdef SIOCSIFDSTADDR { "SIOCSIFDSTADDR", (unsigned long) SIOCSIFDSTADDR }, #endif // SIOCSIFDSTADDR #ifdef SIOCSIFENCAP { "SIOCSIFENCAP", (unsigned long) SIOCSIFENCAP }, #endif // SIOCSIFENCAP #ifdef SIOCSIFFLAGS { "SIOCSIFFLAGS", (unsigned long) SIOCSIFFLAGS }, #endif // SIOCSIFFLAGS #ifdef SIOCSIFHWADDR { "SIOCSIFHWADDR", (unsigned long) SIOCSIFHWADDR }, #endif // SIOCSIFHWADDR #ifdef SIOCSIFHWBROADCAST { "SIOCSIFHWBROADCAST", (unsigned long) SIOCSIFHWBROADCAST }, #endif // SIOCSIFHWBROADCAST #ifdef SIOCSIFLINK { "SIOCSIFLINK", (unsigned long) SIOCSIFLINK }, #endif // SIOCSIFLINK #ifdef SIOCSIFMAP { "SIOCSIFMAP", (unsigned long) SIOCSIFMAP }, #endif // SIOCSIFMAP #ifdef SIOCSIFMEM { "SIOCSIFMEM", (unsigned long) SIOCSIFMEM }, #endif // SIOCSIFMEM #ifdef SIOCSIFMETRIC { "SIOCSIFMETRIC", (unsigned long) SIOCSIFMETRIC }, #endif // SIOCSIFMETRIC #ifdef SIOCSIFMTU { "SIOCSIFMTU", (unsigned long) SIOCSIFMTU }, #endif // SIOCSIFMTU #ifdef SIOCSIFNAME { "SIOCSIFNAME", (unsigned long) SIOCSIFNAME }, #endif // SIOCSIFNAME #ifdef SIOCSIFNETMASK { "SIOCSIFNETMASK", (unsigned long) SIOCSIFNETMASK }, #endif // SIOCSIFNETMASK #ifdef SIOCSIFPFLAGS { "SIOCSIFPFLAGS", (unsigned long) SIOCSIFPFLAGS }, #endif // SIOCSIFPFLAGS #ifdef SIOCSIFSLAVE { "SIOCSIFSLAVE", (unsigned long) SIOCSIFSLAVE }, #endif // SIOCSIFSLAVE #ifdef SIOCSIFTXQLEN { "SIOCSIFTXQLEN", (unsigned long) SIOCSIFTXQLEN }, #endif // SIOCSIFTXQLEN #ifdef SIOCSIFVLAN { "SIOCSIFVLAN", (unsigned long) SIOCSIFVLAN }, #endif // SIOCSIFVLAN #ifdef SIOCSMIIREG { "SIOCSMIIREG", (unsigned long) SIOCSMIIREG }, #endif // SIOCSMIIREG #ifdef SIOCSPGRP { "SIOCSPGRP", (unsigned long) SIOCSPGRP }, #endif // SIOCSPGRP #ifdef SIOCSRARP { "SIOCSRARP", (unsigned long) SIOCSRARP }, #endif // SIOCSRARP #ifdef SIOCWANDEV { "SIOCWANDEV", (unsigned long) SIOCWANDEV }, #endif // SIOCWANDEV #ifdef SIOGIFINDEX { "SIOGIFINDEX", (unsigned long) SIOGIFINDEX }, #endif // SIOGIFINDEX #ifdef SI_QUEUE { "SI_QUEUE", (unsigned long) SI_QUEUE }, #endif // SI_QUEUE #ifdef S_IREAD { "S_IREAD", (unsigned long) S_IREAD }, #endif // S_IREAD #ifdef S_IRUSR { "S_IRUSR", (unsigned long) S_IRUSR }, #endif // S_IRUSR #ifdef S_ISGID { "S_ISGID", (unsigned long) S_ISGID }, #endif // S_ISGID #ifdef SI_SIGIO { "SI_SIGIO", (unsigned long) SI_SIGIO }, #endif // SI_SIGIO #ifdef S_ISUID { "S_ISUID", (unsigned long) S_ISUID }, #endif // S_ISUID #ifdef S_ISVTX { "S_ISVTX", (unsigned long) S_ISVTX }, #endif // S_ISVTX #ifdef SI_TIMER { "SI_TIMER", (unsigned long) SI_TIMER }, #endif // SI_TIMER #ifdef SI_TKILL { "SI_TKILL", (unsigned long) SI_TKILL }, #endif // SI_TKILL #ifdef SI_USER { "SI_USER", (unsigned long) SI_USER }, #endif // SI_USER #ifdef S_IWRITE { "S_IWRITE", (unsigned long) S_IWRITE }, #endif // S_IWRITE #ifdef S_IWUSR { "S_IWUSR", (unsigned long) S_IWUSR }, #endif // S_IWUSR #ifdef S_IXUSR { "S_IXUSR", (unsigned long) S_IXUSR }, #endif // S_IXUSR #ifdef SIZE_WIDTH { "SIZE_WIDTH", (unsigned long) SIZE_WIDTH }, #endif // SIZE_WIDTH #ifdef SO_ACCEPTCONN { "SO_ACCEPTCONN", (unsigned long) SO_ACCEPTCONN }, #endif // SO_ACCEPTCONN #ifdef SO_ATTACH_BPF { "SO_ATTACH_BPF", (unsigned long) SO_ATTACH_BPF }, #endif // SO_ATTACH_BPF #ifdef SO_ATTACH_FILTER { "SO_ATTACH_FILTER", (unsigned long) SO_ATTACH_FILTER }, #endif // SO_ATTACH_FILTER #ifdef SO_ATTACH_REUSEPORT_CBPF { "SO_ATTACH_REUSEPORT_CBPF", (unsigned long) SO_ATTACH_REUSEPORT_CBPF }, #endif // SO_ATTACH_REUSEPORT_CBPF #ifdef SO_ATTACH_REUSEPORT_EBPF { "SO_ATTACH_REUSEPORT_EBPF", (unsigned long) SO_ATTACH_REUSEPORT_EBPF }, #endif // SO_ATTACH_REUSEPORT_EBPF #ifdef SO_BINDTODEVICE { "SO_BINDTODEVICE", (unsigned long) SO_BINDTODEVICE }, #endif // SO_BINDTODEVICE #ifdef SO_BINDTOIFINDEX { "SO_BINDTOIFINDEX", (unsigned long) SO_BINDTOIFINDEX }, #endif // SO_BINDTOIFINDEX #ifdef SO_BPF_EXTENSIONS { "SO_BPF_EXTENSIONS", (unsigned long) SO_BPF_EXTENSIONS }, #endif // SO_BPF_EXTENSIONS #ifdef SO_BROADCAST { "SO_BROADCAST", (unsigned long) SO_BROADCAST }, #endif // SO_BROADCAST #ifdef SO_BSDCOMPAT { "SO_BSDCOMPAT", (unsigned long) SO_BSDCOMPAT }, #endif // SO_BSDCOMPAT #ifdef SO_BUF_LOCK { "SO_BUF_LOCK", (unsigned long) SO_BUF_LOCK }, #endif // SO_BUF_LOCK #ifdef SO_BUSY_POLL { "SO_BUSY_POLL", (unsigned long) SO_BUSY_POLL }, #endif // SO_BUSY_POLL #ifdef SO_BUSY_POLL_BUDGET { "SO_BUSY_POLL_BUDGET", (unsigned long) SO_BUSY_POLL_BUDGET }, #endif // SO_BUSY_POLL_BUDGET #ifdef SOCK_CLOEXEC { "SOCK_CLOEXEC", (unsigned long) SOCK_CLOEXEC }, #endif // SOCK_CLOEXEC #ifdef SOCK_DCCP { "SOCK_DCCP", (unsigned long) SOCK_DCCP }, #endif // SOCK_DCCP #ifdef SOCK_DGRAM { "SOCK_DGRAM", (unsigned long) SOCK_DGRAM }, #endif // SOCK_DGRAM #ifdef SOCK_IOC_TYPE { "SOCK_IOC_TYPE", (unsigned long) SOCK_IOC_TYPE }, #endif // SOCK_IOC_TYPE #ifdef SOCK_NONBLOCK { "SOCK_NONBLOCK", (unsigned long) SOCK_NONBLOCK }, #endif // SOCK_NONBLOCK #ifdef SOCK_PACKET { "SOCK_PACKET", (unsigned long) SOCK_PACKET }, #endif // SOCK_PACKET #ifdef SOCK_RAW { "SOCK_RAW", (unsigned long) SOCK_RAW }, #endif // SOCK_RAW #ifdef SOCK_RCVBUF_LOCK { "SOCK_RCVBUF_LOCK", (unsigned long) SOCK_RCVBUF_LOCK }, #endif // SOCK_RCVBUF_LOCK #ifdef SOCK_RDM { "SOCK_RDM", (unsigned long) SOCK_RDM }, #endif // SOCK_RDM #ifdef SOCK_SEQPACKET { "SOCK_SEQPACKET", (unsigned long) SOCK_SEQPACKET }, #endif // SOCK_SEQPACKET #ifdef SOCK_SNDBUF_LOCK { "SOCK_SNDBUF_LOCK", (unsigned long) SOCK_SNDBUF_LOCK }, #endif // SOCK_SNDBUF_LOCK #ifdef SOCK_STREAM { "SOCK_STREAM", (unsigned long) SOCK_STREAM }, #endif // SOCK_STREAM #ifdef SOCK_TXREHASH_DEFAULT { "SOCK_TXREHASH_DEFAULT", (unsigned long) SOCK_TXREHASH_DEFAULT }, #endif // SOCK_TXREHASH_DEFAULT #ifdef SOCK_TXREHASH_DISABLED { "SOCK_TXREHASH_DISABLED", (unsigned long) SOCK_TXREHASH_DISABLED }, #endif // SOCK_TXREHASH_DISABLED #ifdef SOCK_TXREHASH_ENABLED { "SOCK_TXREHASH_ENABLED", (unsigned long) SOCK_TXREHASH_ENABLED }, #endif // SOCK_TXREHASH_ENABLED #ifdef SO_CNX_ADVICE { "SO_CNX_ADVICE", (unsigned long) SO_CNX_ADVICE }, #endif // SO_CNX_ADVICE #ifdef SO_COOKIE { "SO_COOKIE", (unsigned long) SO_COOKIE }, #endif // SO_COOKIE #ifdef SO_DEBUG { "SO_DEBUG", (unsigned long) SO_DEBUG }, #endif // SO_DEBUG #ifdef SO_DETACH_BPF { "SO_DETACH_BPF", (unsigned long) SO_DETACH_BPF }, #endif // SO_DETACH_BPF #ifdef SO_DETACH_FILTER { "SO_DETACH_FILTER", (unsigned long) SO_DETACH_FILTER }, #endif // SO_DETACH_FILTER #ifdef SO_DETACH_REUSEPORT_BPF { "SO_DETACH_REUSEPORT_BPF", (unsigned long) SO_DETACH_REUSEPORT_BPF }, #endif // SO_DETACH_REUSEPORT_BPF #ifdef SO_DEVMEM_DMABUF { "SO_DEVMEM_DMABUF", (unsigned long) SO_DEVMEM_DMABUF }, #endif // SO_DEVMEM_DMABUF #ifdef SO_DEVMEM_DONTNEED { "SO_DEVMEM_DONTNEED", (unsigned long) SO_DEVMEM_DONTNEED }, #endif // SO_DEVMEM_DONTNEED #ifdef SO_DEVMEM_LINEAR { "SO_DEVMEM_LINEAR", (unsigned long) SO_DEVMEM_LINEAR }, #endif // SO_DEVMEM_LINEAR #ifdef SO_DOMAIN { "SO_DOMAIN", (unsigned long) SO_DOMAIN }, #endif // SO_DOMAIN #ifdef SO_DONTROUTE { "SO_DONTROUTE", (unsigned long) SO_DONTROUTE }, #endif // SO_DONTROUTE #ifdef SO_ERROR { "SO_ERROR", (unsigned long) SO_ERROR }, #endif // SO_ERROR #ifdef SO_GET_FILTER { "SO_GET_FILTER", (unsigned long) SO_GET_FILTER }, #endif // SO_GET_FILTER #ifdef SO_INCOMING_CPU { "SO_INCOMING_CPU", (unsigned long) SO_INCOMING_CPU }, #endif // SO_INCOMING_CPU #ifdef SO_INCOMING_NAPI_ID { "SO_INCOMING_NAPI_ID", (unsigned long) SO_INCOMING_NAPI_ID }, #endif // SO_INCOMING_NAPI_ID #ifdef SO_INQ { "SO_INQ", (unsigned long) SO_INQ }, #endif // SO_INQ #ifdef SO_KEEPALIVE { "SO_KEEPALIVE", (unsigned long) SO_KEEPALIVE }, #endif // SO_KEEPALIVE #ifdef SOL_AAL { "SOL_AAL", (unsigned long) SOL_AAL }, #endif // SOL_AAL #ifdef SOL_ALG { "SOL_ALG", (unsigned long) SOL_ALG }, #endif // SOL_ALG #ifdef SOL_ATM { "SOL_ATM", (unsigned long) SOL_ATM }, #endif // SOL_ATM #ifdef SOL_BLUETOOTH { "SOL_BLUETOOTH", (unsigned long) SOL_BLUETOOTH }, #endif // SOL_BLUETOOTH #ifdef SOL_CAIF { "SOL_CAIF", (unsigned long) SOL_CAIF }, #endif // SOL_CAIF #ifdef SOL_DCCP { "SOL_DCCP", (unsigned long) SOL_DCCP }, #endif // SOL_DCCP #ifdef SOL_DECNET { "SOL_DECNET", (unsigned long) SOL_DECNET }, #endif // SOL_DECNET #ifdef SO_LINGER { "SO_LINGER", (unsigned long) SO_LINGER }, #endif // SO_LINGER #ifdef SOL_IRDA { "SOL_IRDA", (unsigned long) SOL_IRDA }, #endif // SOL_IRDA #ifdef SOL_IUCV { "SOL_IUCV", (unsigned long) SOL_IUCV }, #endif // SOL_IUCV #ifdef SOL_KCM { "SOL_KCM", (unsigned long) SOL_KCM }, #endif // SOL_KCM #ifdef SOL_LLC { "SOL_LLC", (unsigned long) SOL_LLC }, #endif // SOL_LLC #ifdef SOL_MCTP { "SOL_MCTP", (unsigned long) SOL_MCTP }, #endif // SOL_MCTP #ifdef SOL_MPTCP { "SOL_MPTCP", (unsigned long) SOL_MPTCP }, #endif // SOL_MPTCP #ifdef SOL_NETBEUI { "SOL_NETBEUI", (unsigned long) SOL_NETBEUI }, #endif // SOL_NETBEUI #ifdef SOL_NETLINK { "SOL_NETLINK", (unsigned long) SOL_NETLINK }, #endif // SOL_NETLINK #ifdef SOL_NFC { "SOL_NFC", (unsigned long) SOL_NFC }, #endif // SOL_NFC #ifdef SO_LOCK_FILTER { "SO_LOCK_FILTER", (unsigned long) SO_LOCK_FILTER }, #endif // SO_LOCK_FILTER #ifdef SOL_PACKET { "SOL_PACKET", (unsigned long) SOL_PACKET }, #endif // SOL_PACKET #ifdef SOL_PNPIPE { "SOL_PNPIPE", (unsigned long) SOL_PNPIPE }, #endif // SOL_PNPIPE #ifdef SOL_PPPOL2TP { "SOL_PPPOL2TP", (unsigned long) SOL_PPPOL2TP }, #endif // SOL_PPPOL2TP #ifdef SOL_RAW { "SOL_RAW", (unsigned long) SOL_RAW }, #endif // SOL_RAW #ifdef SOL_RDS { "SOL_RDS", (unsigned long) SOL_RDS }, #endif // SOL_RDS #ifdef SOL_RXRPC { "SOL_RXRPC", (unsigned long) SOL_RXRPC }, #endif // SOL_RXRPC #ifdef SOL_SMC { "SOL_SMC", (unsigned long) SOL_SMC }, #endif // SOL_SMC #ifdef SOL_SOCKET { "SOL_SOCKET", (unsigned long) SOL_SOCKET }, #endif // SOL_SOCKET #ifdef SOL_TIPC { "SOL_TIPC", (unsigned long) SOL_TIPC }, #endif // SOL_TIPC #ifdef SOL_TLS { "SOL_TLS", (unsigned long) SOL_TLS }, #endif // SOL_TLS #ifdef SOL_VSOCK { "SOL_VSOCK", (unsigned long) SOL_VSOCK }, #endif // SOL_VSOCK #ifdef SOL_X25 { "SOL_X25", (unsigned long) SOL_X25 }, #endif // SOL_X25 #ifdef SOL_XDP { "SOL_XDP", (unsigned long) SOL_XDP }, #endif // SOL_XDP #ifdef SO_MARK { "SO_MARK", (unsigned long) SO_MARK }, #endif // SO_MARK #ifdef SOMAXCONN { "SOMAXCONN", (unsigned long) SOMAXCONN }, #endif // SOMAXCONN #ifdef SO_MAX_PACING_RATE { "SO_MAX_PACING_RATE", (unsigned long) SO_MAX_PACING_RATE }, #endif // SO_MAX_PACING_RATE #ifdef SO_MEMINFO { "SO_MEMINFO", (unsigned long) SO_MEMINFO }, #endif // SO_MEMINFO #ifdef SO_NETNS_COOKIE { "SO_NETNS_COOKIE", (unsigned long) SO_NETNS_COOKIE }, #endif // SO_NETNS_COOKIE #ifdef SO_NO_CHECK { "SO_NO_CHECK", (unsigned long) SO_NO_CHECK }, #endif // SO_NO_CHECK #ifdef SO_NOFCS { "SO_NOFCS", (unsigned long) SO_NOFCS }, #endif // SO_NOFCS #ifdef SO_OOBINLINE { "SO_OOBINLINE", (unsigned long) SO_OOBINLINE }, #endif // SO_OOBINLINE #ifdef SO_PASSCRED { "SO_PASSCRED", (unsigned long) SO_PASSCRED }, #endif // SO_PASSCRED #ifdef SO_PASSPIDFD { "SO_PASSPIDFD", (unsigned long) SO_PASSPIDFD }, #endif // SO_PASSPIDFD #ifdef SO_PASSRIGHTS { "SO_PASSRIGHTS", (unsigned long) SO_PASSRIGHTS }, #endif // SO_PASSRIGHTS #ifdef SO_PASSSEC { "SO_PASSSEC", (unsigned long) SO_PASSSEC }, #endif // SO_PASSSEC #ifdef SO_PEEK_OFF { "SO_PEEK_OFF", (unsigned long) SO_PEEK_OFF }, #endif // SO_PEEK_OFF #ifdef SO_PEERCRED { "SO_PEERCRED", (unsigned long) SO_PEERCRED }, #endif // SO_PEERCRED #ifdef SO_PEERGROUPS { "SO_PEERGROUPS", (unsigned long) SO_PEERGROUPS }, #endif // SO_PEERGROUPS #ifdef SO_PEERNAME { "SO_PEERNAME", (unsigned long) SO_PEERNAME }, #endif // SO_PEERNAME #ifdef SO_PEERPIDFD { "SO_PEERPIDFD", (unsigned long) SO_PEERPIDFD }, #endif // SO_PEERPIDFD #ifdef SO_PEERSEC { "SO_PEERSEC", (unsigned long) SO_PEERSEC }, #endif // SO_PEERSEC #ifdef SO_PREFER_BUSY_POLL { "SO_PREFER_BUSY_POLL", (unsigned long) SO_PREFER_BUSY_POLL }, #endif // SO_PREFER_BUSY_POLL #ifdef SO_PRIORITY { "SO_PRIORITY", (unsigned long) SO_PRIORITY }, #endif // SO_PRIORITY #ifdef SO_PROTOCOL { "SO_PROTOCOL", (unsigned long) SO_PROTOCOL }, #endif // SO_PROTOCOL #ifdef SO_RCVBUF { "SO_RCVBUF", (unsigned long) SO_RCVBUF }, #endif // SO_RCVBUF #ifdef SO_RCVBUFFORCE { "SO_RCVBUFFORCE", (unsigned long) SO_RCVBUFFORCE }, #endif // SO_RCVBUFFORCE #ifdef SO_RCVLOWAT { "SO_RCVLOWAT", (unsigned long) SO_RCVLOWAT }, #endif // SO_RCVLOWAT #ifdef SO_RCVMARK { "SO_RCVMARK", (unsigned long) SO_RCVMARK }, #endif // SO_RCVMARK #ifdef SO_RCVPRIORITY { "SO_RCVPRIORITY", (unsigned long) SO_RCVPRIORITY }, #endif // SO_RCVPRIORITY #ifdef SO_RCVTIMEO_NEW { "SO_RCVTIMEO_NEW", (unsigned long) SO_RCVTIMEO_NEW }, #endif // SO_RCVTIMEO_NEW #ifdef SO_RCVTIMEO_OLD { "SO_RCVTIMEO_OLD", (unsigned long) SO_RCVTIMEO_OLD }, #endif // SO_RCVTIMEO_OLD #ifdef SO_RCVTIMEO { "SO_RCVTIMEO", (unsigned long) SO_RCVTIMEO }, #endif // SO_RCVTIMEO #ifdef SO_RESERVE_MEM { "SO_RESERVE_MEM", (unsigned long) SO_RESERVE_MEM }, #endif // SO_RESERVE_MEM #ifdef SO_REUSEADDR { "SO_REUSEADDR", (unsigned long) SO_REUSEADDR }, #endif // SO_REUSEADDR #ifdef SO_REUSEPORT { "SO_REUSEPORT", (unsigned long) SO_REUSEPORT }, #endif // SO_REUSEPORT #ifdef SO_RXQ_OVFL { "SO_RXQ_OVFL", (unsigned long) SO_RXQ_OVFL }, #endif // SO_RXQ_OVFL #ifdef SO_SECURITY_AUTHENTICATION { "SO_SECURITY_AUTHENTICATION", (unsigned long) SO_SECURITY_AUTHENTICATION }, #endif // SO_SECURITY_AUTHENTICATION #ifdef SO_SECURITY_ENCRYPTION_NETWORK { "SO_SECURITY_ENCRYPTION_NETWORK", (unsigned long) SO_SECURITY_ENCRYPTION_NETWORK }, #endif // SO_SECURITY_ENCRYPTION_NETWORK #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT { "SO_SECURITY_ENCRYPTION_TRANSPORT", (unsigned long) SO_SECURITY_ENCRYPTION_TRANSPORT }, #endif // SO_SECURITY_ENCRYPTION_TRANSPORT #ifdef SO_SELECT_ERR_QUEUE { "SO_SELECT_ERR_QUEUE", (unsigned long) SO_SELECT_ERR_QUEUE }, #endif // SO_SELECT_ERR_QUEUE #ifdef SO_SNDBUF { "SO_SNDBUF", (unsigned long) SO_SNDBUF }, #endif // SO_SNDBUF #ifdef SO_SNDBUFFORCE { "SO_SNDBUFFORCE", (unsigned long) SO_SNDBUFFORCE }, #endif // SO_SNDBUFFORCE #ifdef SO_SNDLOWAT { "SO_SNDLOWAT", (unsigned long) SO_SNDLOWAT }, #endif // SO_SNDLOWAT #ifdef SO_SNDTIMEO_NEW { "SO_SNDTIMEO_NEW", (unsigned long) SO_SNDTIMEO_NEW }, #endif // SO_SNDTIMEO_NEW #ifdef SO_SNDTIMEO_OLD { "SO_SNDTIMEO_OLD", (unsigned long) SO_SNDTIMEO_OLD }, #endif // SO_SNDTIMEO_OLD #ifdef SO_SNDTIMEO { "SO_SNDTIMEO", (unsigned long) SO_SNDTIMEO }, #endif // SO_SNDTIMEO #ifdef SO_TIMESTAMPING_NEW { "SO_TIMESTAMPING_NEW", (unsigned long) SO_TIMESTAMPING_NEW }, #endif // SO_TIMESTAMPING_NEW #ifdef SO_TIMESTAMPING_OLD { "SO_TIMESTAMPING_OLD", (unsigned long) SO_TIMESTAMPING_OLD }, #endif // SO_TIMESTAMPING_OLD #ifdef SO_TIMESTAMPING { "SO_TIMESTAMPING", (unsigned long) SO_TIMESTAMPING }, #endif // SO_TIMESTAMPING #ifdef SO_TIMESTAMP_NEW { "SO_TIMESTAMP_NEW", (unsigned long) SO_TIMESTAMP_NEW }, #endif // SO_TIMESTAMP_NEW #ifdef SO_TIMESTAMPNS_NEW { "SO_TIMESTAMPNS_NEW", (unsigned long) SO_TIMESTAMPNS_NEW }, #endif // SO_TIMESTAMPNS_NEW #ifdef SO_TIMESTAMPNS_OLD { "SO_TIMESTAMPNS_OLD", (unsigned long) SO_TIMESTAMPNS_OLD }, #endif // SO_TIMESTAMPNS_OLD #ifdef SO_TIMESTAMPNS { "SO_TIMESTAMPNS", (unsigned long) SO_TIMESTAMPNS }, #endif // SO_TIMESTAMPNS #ifdef SO_TIMESTAMP_OLD { "SO_TIMESTAMP_OLD", (unsigned long) SO_TIMESTAMP_OLD }, #endif // SO_TIMESTAMP_OLD #ifdef SO_TIMESTAMP { "SO_TIMESTAMP", (unsigned long) SO_TIMESTAMP }, #endif // SO_TIMESTAMP #ifdef SO_TXREHASH { "SO_TXREHASH", (unsigned long) SO_TXREHASH }, #endif // SO_TXREHASH #ifdef SO_TXTIME { "SO_TXTIME", (unsigned long) SO_TXTIME }, #endif // SO_TXTIME #ifdef SO_TYPE { "SO_TYPE", (unsigned long) SO_TYPE }, #endif // SO_TYPE #ifdef SO_WIFI_STATUS { "SO_WIFI_STATUS", (unsigned long) SO_WIFI_STATUS }, #endif // SO_WIFI_STATUS #ifdef SO_ZEROCOPY { "SO_ZEROCOPY", (unsigned long) SO_ZEROCOPY }, #endif // SO_ZEROCOPY #ifdef SS_DISABLE { "SS_DISABLE", (unsigned long) SS_DISABLE }, #endif // SS_DISABLE #ifdef SS_ONSTACK { "SS_ONSTACK", (unsigned long) SS_ONSTACK }, #endif // SS_ONSTACK #ifdef STATMOUNT_FS_SUBTYPE { "STATMOUNT_FS_SUBTYPE", (unsigned long) STATMOUNT_FS_SUBTYPE }, #endif // STATMOUNT_FS_SUBTYPE #ifdef STATMOUNT_FS_TYPE { "STATMOUNT_FS_TYPE", (unsigned long) STATMOUNT_FS_TYPE }, #endif // STATMOUNT_FS_TYPE #ifdef STATMOUNT_MNT_BASIC { "STATMOUNT_MNT_BASIC", (unsigned long) STATMOUNT_MNT_BASIC }, #endif // STATMOUNT_MNT_BASIC #ifdef STATMOUNT_MNT_GIDMAP { "STATMOUNT_MNT_GIDMAP", (unsigned long) STATMOUNT_MNT_GIDMAP }, #endif // STATMOUNT_MNT_GIDMAP #ifdef STATMOUNT_MNT_NS_ID { "STATMOUNT_MNT_NS_ID", (unsigned long) STATMOUNT_MNT_NS_ID }, #endif // STATMOUNT_MNT_NS_ID #ifdef STATMOUNT_MNT_OPTS { "STATMOUNT_MNT_OPTS", (unsigned long) STATMOUNT_MNT_OPTS }, #endif // STATMOUNT_MNT_OPTS #ifdef STATMOUNT_MNT_POINT { "STATMOUNT_MNT_POINT", (unsigned long) STATMOUNT_MNT_POINT }, #endif // STATMOUNT_MNT_POINT #ifdef STATMOUNT_MNT_ROOT { "STATMOUNT_MNT_ROOT", (unsigned long) STATMOUNT_MNT_ROOT }, #endif // STATMOUNT_MNT_ROOT #ifdef STATMOUNT_MNT_UIDMAP { "STATMOUNT_MNT_UIDMAP", (unsigned long) STATMOUNT_MNT_UIDMAP }, #endif // STATMOUNT_MNT_UIDMAP #ifdef STATMOUNT_OPT_ARRAY { "STATMOUNT_OPT_ARRAY", (unsigned long) STATMOUNT_OPT_ARRAY }, #endif // STATMOUNT_OPT_ARRAY #ifdef STATMOUNT_OPT_SEC_ARRAY { "STATMOUNT_OPT_SEC_ARRAY", (unsigned long) STATMOUNT_OPT_SEC_ARRAY }, #endif // STATMOUNT_OPT_SEC_ARRAY #ifdef STATMOUNT_PROPAGATE_FROM { "STATMOUNT_PROPAGATE_FROM", (unsigned long) STATMOUNT_PROPAGATE_FROM }, #endif // STATMOUNT_PROPAGATE_FROM #ifdef STATMOUNT_SB_BASIC { "STATMOUNT_SB_BASIC", (unsigned long) STATMOUNT_SB_BASIC }, #endif // STATMOUNT_SB_BASIC #ifdef STATMOUNT_SB_SOURCE { "STATMOUNT_SB_SOURCE", (unsigned long) STATMOUNT_SB_SOURCE }, #endif // STATMOUNT_SB_SOURCE #ifdef STATMOUNT_SUPPORTED_MASK { "STATMOUNT_SUPPORTED_MASK", (unsigned long) STATMOUNT_SUPPORTED_MASK }, #endif // STATMOUNT_SUPPORTED_MASK #ifdef SYNC_FILE_RANGE_WAIT_AFTER { "SYNC_FILE_RANGE_WAIT_AFTER", (unsigned long) SYNC_FILE_RANGE_WAIT_AFTER }, #endif // SYNC_FILE_RANGE_WAIT_AFTER #ifdef SYNC_FILE_RANGE_WAIT_BEFORE { "SYNC_FILE_RANGE_WAIT_BEFORE", (unsigned long) SYNC_FILE_RANGE_WAIT_BEFORE }, #endif // SYNC_FILE_RANGE_WAIT_BEFORE #ifdef SYNC_FILE_RANGE_WRITE { "SYNC_FILE_RANGE_WRITE", (unsigned long) SYNC_FILE_RANGE_WRITE }, #endif // SYNC_FILE_RANGE_WRITE #ifdef SYS_ACCEPT4 { "SYS_ACCEPT4", (unsigned long) SYS_ACCEPT4 }, #endif // SYS_ACCEPT4 #ifdef SYS_ACCEPT { "SYS_ACCEPT", (unsigned long) SYS_ACCEPT }, #endif // SYS_ACCEPT #ifdef SYS_BIND { "SYS_BIND", (unsigned long) SYS_BIND }, #endif // SYS_BIND #ifdef SYSCALL_DISPATCH_FILTER_ALLOW { "SYSCALL_DISPATCH_FILTER_ALLOW", (unsigned long) SYSCALL_DISPATCH_FILTER_ALLOW }, #endif // SYSCALL_DISPATCH_FILTER_ALLOW #ifdef SYSCALL_DISPATCH_FILTER_BLOCK { "SYSCALL_DISPATCH_FILTER_BLOCK", (unsigned long) SYSCALL_DISPATCH_FILTER_BLOCK }, #endif // SYSCALL_DISPATCH_FILTER_BLOCK #ifdef SYS_CONNECT { "SYS_CONNECT", (unsigned long) SYS_CONNECT }, #endif // SYS_CONNECT #ifdef SYS_GETPEERNAME { "SYS_GETPEERNAME", (unsigned long) SYS_GETPEERNAME }, #endif // SYS_GETPEERNAME #ifdef SYS_GETSOCKNAME { "SYS_GETSOCKNAME", (unsigned long) SYS_GETSOCKNAME }, #endif // SYS_GETSOCKNAME #ifdef SYS_GETSOCKOPT { "SYS_GETSOCKOPT", (unsigned long) SYS_GETSOCKOPT }, #endif // SYS_GETSOCKOPT #ifdef SYS_LISTEN { "SYS_LISTEN", (unsigned long) SYS_LISTEN }, #endif // SYS_LISTEN #ifdef SYS_RECV { "SYS_RECV", (unsigned long) SYS_RECV }, #endif // SYS_RECV #ifdef SYS_RECVFROM { "SYS_RECVFROM", (unsigned long) SYS_RECVFROM }, #endif // SYS_RECVFROM #ifdef SYS_RECVMMSG { "SYS_RECVMMSG", (unsigned long) SYS_RECVMMSG }, #endif // SYS_RECVMMSG #ifdef SYS_RECVMSG { "SYS_RECVMSG", (unsigned long) SYS_RECVMSG }, #endif // SYS_RECVMSG #ifdef SYS_SEND { "SYS_SEND", (unsigned long) SYS_SEND }, #endif // SYS_SEND #ifdef SYS_SENDMMSG { "SYS_SENDMMSG", (unsigned long) SYS_SENDMMSG }, #endif // SYS_SENDMMSG #ifdef SYS_SENDMSG { "SYS_SENDMSG", (unsigned long) SYS_SENDMSG }, #endif // SYS_SENDMSG #ifdef SYS_SENDTO { "SYS_SENDTO", (unsigned long) SYS_SENDTO }, #endif // SYS_SENDTO #ifdef SYS_SETSOCKOPT { "SYS_SETSOCKOPT", (unsigned long) SYS_SETSOCKOPT }, #endif // SYS_SETSOCKOPT #ifdef SYS_SHUTDOWN { "SYS_SHUTDOWN", (unsigned long) SYS_SHUTDOWN }, #endif // SYS_SHUTDOWN #ifdef SYS_SOCKET { "SYS_SOCKET", (unsigned long) SYS_SOCKET }, #endif // SYS_SOCKET #ifdef SYS_SOCKETPAIR { "SYS_SOCKETPAIR", (unsigned long) SYS_SOCKETPAIR }, #endif // SYS_SOCKETPAIR #ifdef TAB0 { "TAB0", (unsigned long) TAB0 }, #endif // TAB0 #ifdef TAB1 { "TAB1", (unsigned long) TAB1 }, #endif // TAB1 #ifdef TAB2 { "TAB2", (unsigned long) TAB2 }, #endif // TAB2 #ifdef TAB3 { "TAB3", (unsigned long) TAB3 }, #endif // TAB3 #ifdef TABDLY { "TABDLY", (unsigned long) TABDLY }, #endif // TABDLY #ifdef TCFLSH { "TCFLSH", (unsigned long) TCFLSH }, #endif // TCFLSH #ifdef TCGETA { "TCGETA", (unsigned long) TCGETA }, #endif // TCGETA #ifdef TCGETS { "TCGETS", (unsigned long) TCGETS }, #endif // TCGETS #ifdef TCGETS2 { "TCGETS2", (unsigned long) TCGETS2 }, #endif // TCGETS2 #ifdef TCGETX { "TCGETX", (unsigned long) TCGETX }, #endif // TCGETX #ifdef TCIFLUSH { "TCIFLUSH", (unsigned long) TCIFLUSH }, #endif // TCIFLUSH #ifdef TCIOFF { "TCIOFF", (unsigned long) TCIOFF }, #endif // TCIOFF #ifdef TCIOFLUSH { "TCIOFLUSH", (unsigned long) TCIOFLUSH }, #endif // TCIOFLUSH #ifdef TCION { "TCION", (unsigned long) TCION }, #endif // TCION #ifdef TCOFLUSH { "TCOFLUSH", (unsigned long) TCOFLUSH }, #endif // TCOFLUSH #ifdef TCOOFF { "TCOOFF", (unsigned long) TCOOFF }, #endif // TCOOFF #ifdef TCOON { "TCOON", (unsigned long) TCOON }, #endif // TCOON #ifdef TCSADRAIN { "TCSADRAIN", (unsigned long) TCSADRAIN }, #endif // TCSADRAIN #ifdef TCSAFLUSH { "TCSAFLUSH", (unsigned long) TCSAFLUSH }, #endif // TCSAFLUSH #ifdef TCSANOW { "TCSANOW", (unsigned long) TCSANOW }, #endif // TCSANOW #ifdef TCSBRK { "TCSBRK", (unsigned long) TCSBRK }, #endif // TCSBRK #ifdef TCSBRKP { "TCSBRKP", (unsigned long) TCSBRKP }, #endif // TCSBRKP #ifdef TCSETA { "TCSETA", (unsigned long) TCSETA }, #endif // TCSETA #ifdef TCSETAF { "TCSETAF", (unsigned long) TCSETAF }, #endif // TCSETAF #ifdef TCSETAW { "TCSETAW", (unsigned long) TCSETAW }, #endif // TCSETAW #ifdef TCSETS { "TCSETS", (unsigned long) TCSETS }, #endif // TCSETS #ifdef TCSETS2 { "TCSETS2", (unsigned long) TCSETS2 }, #endif // TCSETS2 #ifdef TCSETSF { "TCSETSF", (unsigned long) TCSETSF }, #endif // TCSETSF #ifdef TCSETSF2 { "TCSETSF2", (unsigned long) TCSETSF2 }, #endif // TCSETSF2 #ifdef TCSETSW { "TCSETSW", (unsigned long) TCSETSW }, #endif // TCSETSW #ifdef TCSETSW2 { "TCSETSW2", (unsigned long) TCSETSW2 }, #endif // TCSETSW2 #ifdef TCSETX { "TCSETX", (unsigned long) TCSETX }, #endif // TCSETX #ifdef TCSETXF { "TCSETXF", (unsigned long) TCSETXF }, #endif // TCSETXF #ifdef TCSETXW { "TCSETXW", (unsigned long) TCSETXW }, #endif // TCSETXW #ifdef TCXONC { "TCXONC", (unsigned long) TCXONC }, #endif // TCXONC #ifdef TIOCCBRK { "TIOCCBRK", (unsigned long) TIOCCBRK }, #endif // TIOCCBRK #ifdef TIOCCONS { "TIOCCONS", (unsigned long) TIOCCONS }, #endif // TIOCCONS #ifdef TIOCEXCL { "TIOCEXCL", (unsigned long) TIOCEXCL }, #endif // TIOCEXCL #ifdef TIOCGDEV { "TIOCGDEV", (unsigned long) TIOCGDEV }, #endif // TIOCGDEV #ifdef TIOCGETD { "TIOCGETD", (unsigned long) TIOCGETD }, #endif // TIOCGETD #ifdef TIOCGEXCL { "TIOCGEXCL", (unsigned long) TIOCGEXCL }, #endif // TIOCGEXCL #ifdef TIOCGICOUNT { "TIOCGICOUNT", (unsigned long) TIOCGICOUNT }, #endif // TIOCGICOUNT #ifdef TIOCGISO7816 { "TIOCGISO7816", (unsigned long) TIOCGISO7816 }, #endif // TIOCGISO7816 #ifdef TIOCGLCKTRMIOS { "TIOCGLCKTRMIOS", (unsigned long) TIOCGLCKTRMIOS }, #endif // TIOCGLCKTRMIOS #ifdef TIOCGPGRP { "TIOCGPGRP", (unsigned long) TIOCGPGRP }, #endif // TIOCGPGRP #ifdef TIOCGPKT { "TIOCGPKT", (unsigned long) TIOCGPKT }, #endif // TIOCGPKT #ifdef TIOCGPTLCK { "TIOCGPTLCK", (unsigned long) TIOCGPTLCK }, #endif // TIOCGPTLCK #ifdef TIOCGPTN { "TIOCGPTN", (unsigned long) TIOCGPTN }, #endif // TIOCGPTN #ifdef TIOCGPTPEER { "TIOCGPTPEER", (unsigned long) TIOCGPTPEER }, #endif // TIOCGPTPEER #ifdef TIOCGRS485 { "TIOCGRS485", (unsigned long) TIOCGRS485 }, #endif // TIOCGRS485 #ifdef TIOCGSERIAL { "TIOCGSERIAL", (unsigned long) TIOCGSERIAL }, #endif // TIOCGSERIAL #ifdef TIOCGSID { "TIOCGSID", (unsigned long) TIOCGSID }, #endif // TIOCGSID #ifdef TIOCGSOFTCAR { "TIOCGSOFTCAR", (unsigned long) TIOCGSOFTCAR }, #endif // TIOCGSOFTCAR #ifdef TIOCGWINSZ { "TIOCGWINSZ", (unsigned long) TIOCGWINSZ }, #endif // TIOCGWINSZ #ifdef TIOCINQ { "TIOCINQ", (unsigned long) TIOCINQ }, #endif // TIOCINQ #ifdef TIOCLINUX { "TIOCLINUX", (unsigned long) TIOCLINUX }, #endif // TIOCLINUX #ifdef TIOCMBIC { "TIOCMBIC", (unsigned long) TIOCMBIC }, #endif // TIOCMBIC #ifdef TIOCMBIS { "TIOCMBIS", (unsigned long) TIOCMBIS }, #endif // TIOCMBIS #ifdef TIOCM_CAR { "TIOCM_CAR", (unsigned long) TIOCM_CAR }, #endif // TIOCM_CAR #ifdef TIOCM_CD { "TIOCM_CD", (unsigned long) TIOCM_CD }, #endif // TIOCM_CD #ifdef TIOCM_CTS { "TIOCM_CTS", (unsigned long) TIOCM_CTS }, #endif // TIOCM_CTS #ifdef TIOCM_DSR { "TIOCM_DSR", (unsigned long) TIOCM_DSR }, #endif // TIOCM_DSR #ifdef TIOCM_DTR { "TIOCM_DTR", (unsigned long) TIOCM_DTR }, #endif // TIOCM_DTR #ifdef TIOCMGET { "TIOCMGET", (unsigned long) TIOCMGET }, #endif // TIOCMGET #ifdef TIOCMIWAIT { "TIOCMIWAIT", (unsigned long) TIOCMIWAIT }, #endif // TIOCMIWAIT #ifdef TIOCM_LE { "TIOCM_LE", (unsigned long) TIOCM_LE }, #endif // TIOCM_LE #ifdef TIOCM_LOOP { "TIOCM_LOOP", (unsigned long) TIOCM_LOOP }, #endif // TIOCM_LOOP #ifdef TIOCM_OUT1 { "TIOCM_OUT1", (unsigned long) TIOCM_OUT1 }, #endif // TIOCM_OUT1 #ifdef TIOCM_OUT2 { "TIOCM_OUT2", (unsigned long) TIOCM_OUT2 }, #endif // TIOCM_OUT2 #ifdef TIOCM_RI { "TIOCM_RI", (unsigned long) TIOCM_RI }, #endif // TIOCM_RI #ifdef TIOCM_RNG { "TIOCM_RNG", (unsigned long) TIOCM_RNG }, #endif // TIOCM_RNG #ifdef TIOCM_RTS { "TIOCM_RTS", (unsigned long) TIOCM_RTS }, #endif // TIOCM_RTS #ifdef TIOCMSET { "TIOCMSET", (unsigned long) TIOCMSET }, #endif // TIOCMSET #ifdef TIOCM_SR { "TIOCM_SR", (unsigned long) TIOCM_SR }, #endif // TIOCM_SR #ifdef TIOCM_ST { "TIOCM_ST", (unsigned long) TIOCM_ST }, #endif // TIOCM_ST #ifdef TIOCNOTTY { "TIOCNOTTY", (unsigned long) TIOCNOTTY }, #endif // TIOCNOTTY #ifdef TIOCNXCL { "TIOCNXCL", (unsigned long) TIOCNXCL }, #endif // TIOCNXCL #ifdef TIOCOUTQ { "TIOCOUTQ", (unsigned long) TIOCOUTQ }, #endif // TIOCOUTQ #ifdef TIOCPKT { "TIOCPKT", (unsigned long) TIOCPKT }, #endif // TIOCPKT #ifdef TIOCPKT_DATA { "TIOCPKT_DATA", (unsigned long) TIOCPKT_DATA }, #endif // TIOCPKT_DATA #ifdef TIOCPKT_DOSTOP { "TIOCPKT_DOSTOP", (unsigned long) TIOCPKT_DOSTOP }, #endif // TIOCPKT_DOSTOP #ifdef TIOCPKT_FLUSHREAD { "TIOCPKT_FLUSHREAD", (unsigned long) TIOCPKT_FLUSHREAD }, #endif // TIOCPKT_FLUSHREAD #ifdef TIOCPKT_FLUSHWRITE { "TIOCPKT_FLUSHWRITE", (unsigned long) TIOCPKT_FLUSHWRITE }, #endif // TIOCPKT_FLUSHWRITE #ifdef TIOCPKT_IOCTL { "TIOCPKT_IOCTL", (unsigned long) TIOCPKT_IOCTL }, #endif // TIOCPKT_IOCTL #ifdef TIOCPKT_NOSTOP { "TIOCPKT_NOSTOP", (unsigned long) TIOCPKT_NOSTOP }, #endif // TIOCPKT_NOSTOP #ifdef TIOCPKT_START { "TIOCPKT_START", (unsigned long) TIOCPKT_START }, #endif // TIOCPKT_START #ifdef TIOCPKT_STOP { "TIOCPKT_STOP", (unsigned long) TIOCPKT_STOP }, #endif // TIOCPKT_STOP #ifdef TIOCSBRK { "TIOCSBRK", (unsigned long) TIOCSBRK }, #endif // TIOCSBRK #ifdef TIOCSCTTY { "TIOCSCTTY", (unsigned long) TIOCSCTTY }, #endif // TIOCSCTTY #ifdef TIOCSERCONFIG { "TIOCSERCONFIG", (unsigned long) TIOCSERCONFIG }, #endif // TIOCSERCONFIG #ifdef TIOCSERGETLSR { "TIOCSERGETLSR", (unsigned long) TIOCSERGETLSR }, #endif // TIOCSERGETLSR #ifdef TIOCSERGETMULTI { "TIOCSERGETMULTI", (unsigned long) TIOCSERGETMULTI }, #endif // TIOCSERGETMULTI #ifdef TIOCSERGSTRUCT { "TIOCSERGSTRUCT", (unsigned long) TIOCSERGSTRUCT }, #endif // TIOCSERGSTRUCT #ifdef TIOCSERGWILD { "TIOCSERGWILD", (unsigned long) TIOCSERGWILD }, #endif // TIOCSERGWILD #ifdef TIOCSERSETMULTI { "TIOCSERSETMULTI", (unsigned long) TIOCSERSETMULTI }, #endif // TIOCSERSETMULTI #ifdef TIOCSERSWILD { "TIOCSERSWILD", (unsigned long) TIOCSERSWILD }, #endif // TIOCSERSWILD #ifdef TIOCSER_TEMT { "TIOCSER_TEMT", (unsigned long) TIOCSER_TEMT }, #endif // TIOCSER_TEMT #ifdef TIOCSETD { "TIOCSETD", (unsigned long) TIOCSETD }, #endif // TIOCSETD #ifdef TIOCSIG { "TIOCSIG", (unsigned long) TIOCSIG }, #endif // TIOCSIG #ifdef TIOCSISO7816 { "TIOCSISO7816", (unsigned long) TIOCSISO7816 }, #endif // TIOCSISO7816 #ifdef TIOCSLCKTRMIOS { "TIOCSLCKTRMIOS", (unsigned long) TIOCSLCKTRMIOS }, #endif // TIOCSLCKTRMIOS #ifdef TIOCSPGRP { "TIOCSPGRP", (unsigned long) TIOCSPGRP }, #endif // TIOCSPGRP #ifdef TIOCSPTLCK { "TIOCSPTLCK", (unsigned long) TIOCSPTLCK }, #endif // TIOCSPTLCK #ifdef TIOCSRS485 { "TIOCSRS485", (unsigned long) TIOCSRS485 }, #endif // TIOCSRS485 #ifdef TIOCSSERIAL { "TIOCSSERIAL", (unsigned long) TIOCSSERIAL }, #endif // TIOCSSERIAL #ifdef TIOCSSOFTCAR { "TIOCSSOFTCAR", (unsigned long) TIOCSSOFTCAR }, #endif // TIOCSSOFTCAR #ifdef TIOCSTI { "TIOCSTI", (unsigned long) TIOCSTI }, #endif // TIOCSTI #ifdef TIOCSWINSZ { "TIOCSWINSZ", (unsigned long) TIOCSWINSZ }, #endif // TIOCSWINSZ #ifdef TIOCVHANGUP { "TIOCVHANGUP", (unsigned long) TIOCVHANGUP }, #endif // TIOCVHANGUP #ifdef TOSTOP { "TOSTOP", (unsigned long) TOSTOP }, #endif // TOSTOP #ifdef UART_CLEAR_FIFO { "UART_CLEAR_FIFO", (unsigned long) UART_CLEAR_FIFO }, #endif // UART_CLEAR_FIFO #ifdef UART_NATSEMI { "UART_NATSEMI", (unsigned long) UART_NATSEMI }, #endif // UART_NATSEMI #ifdef UART_STARTECH { "UART_STARTECH", (unsigned long) UART_STARTECH }, #endif // UART_STARTECH #ifdef UART_USE_FIFO { "UART_USE_FIFO", (unsigned long) UART_USE_FIFO }, #endif // UART_USE_FIFO #ifdef UINT16_WIDTH { "UINT16_WIDTH", (unsigned long) UINT16_WIDTH }, #endif // UINT16_WIDTH #ifdef UINT32_WIDTH { "UINT32_WIDTH", (unsigned long) UINT32_WIDTH }, #endif // UINT32_WIDTH #ifdef UINT64_WIDTH { "UINT64_WIDTH", (unsigned long) UINT64_WIDTH }, #endif // UINT64_WIDTH #ifdef UINT8_WIDTH { "UINT8_WIDTH", (unsigned long) UINT8_WIDTH }, #endif // UINT8_WIDTH #ifdef UINT_FAST16_WIDTH { "UINT_FAST16_WIDTH", (unsigned long) UINT_FAST16_WIDTH }, #endif // UINT_FAST16_WIDTH #ifdef UINT_FAST32_WIDTH { "UINT_FAST32_WIDTH", (unsigned long) UINT_FAST32_WIDTH }, #endif // UINT_FAST32_WIDTH #ifdef UINT_FAST64_WIDTH { "UINT_FAST64_WIDTH", (unsigned long) UINT_FAST64_WIDTH }, #endif // UINT_FAST64_WIDTH #ifdef UINT_FAST8_WIDTH { "UINT_FAST8_WIDTH", (unsigned long) UINT_FAST8_WIDTH }, #endif // UINT_FAST8_WIDTH #ifdef UINT_LEAST16_WIDTH { "UINT_LEAST16_WIDTH", (unsigned long) UINT_LEAST16_WIDTH }, #endif // UINT_LEAST16_WIDTH #ifdef UINT_LEAST32_WIDTH { "UINT_LEAST32_WIDTH", (unsigned long) UINT_LEAST32_WIDTH }, #endif // UINT_LEAST32_WIDTH #ifdef UINT_LEAST64_WIDTH { "UINT_LEAST64_WIDTH", (unsigned long) UINT_LEAST64_WIDTH }, #endif // UINT_LEAST64_WIDTH #ifdef UINT_LEAST8_WIDTH { "UINT_LEAST8_WIDTH", (unsigned long) UINT_LEAST8_WIDTH }, #endif // UINT_LEAST8_WIDTH #ifdef UINTMAX_WIDTH { "UINTMAX_WIDTH", (unsigned long) UINTMAX_WIDTH }, #endif // UINTMAX_WIDTH #ifdef UINTPTR_WIDTH { "UINTPTR_WIDTH", (unsigned long) UINTPTR_WIDTH }, #endif // UINTPTR_WIDTH #ifdef VDISCARD { "VDISCARD", (unsigned long) VDISCARD }, #endif // VDISCARD #ifdef VEOF { "VEOF", (unsigned long) VEOF }, #endif // VEOF #ifdef VEOL { "VEOL", (unsigned long) VEOL }, #endif // VEOL #ifdef VEOL2 { "VEOL2", (unsigned long) VEOL2 }, #endif // VEOL2 #ifdef VERASE { "VERASE", (unsigned long) VERASE }, #endif // VERASE #ifdef VINTR { "VINTR", (unsigned long) VINTR }, #endif // VINTR #ifdef VKILL { "VKILL", (unsigned long) VKILL }, #endif // VKILL #ifdef VLNEXT { "VLNEXT", (unsigned long) VLNEXT }, #endif // VLNEXT #ifdef VMIN { "VMIN", (unsigned long) VMIN }, #endif // VMIN #ifdef VQUIT { "VQUIT", (unsigned long) VQUIT }, #endif // VQUIT #ifdef VREPRINT { "VREPRINT", (unsigned long) VREPRINT }, #endif // VREPRINT #ifdef VSTART { "VSTART", (unsigned long) VSTART }, #endif // VSTART #ifdef VSTOP { "VSTOP", (unsigned long) VSTOP }, #endif // VSTOP #ifdef VSUSP { "VSUSP", (unsigned long) VSUSP }, #endif // VSUSP #ifdef VSWTC { "VSWTC", (unsigned long) VSWTC }, #endif // VSWTC #ifdef VT0 { "VT0", (unsigned long) VT0 }, #endif // VT0 #ifdef VT1 { "VT1", (unsigned long) VT1 }, #endif // VT1 #ifdef VTDLY { "VTDLY", (unsigned long) VTDLY }, #endif // VTDLY #ifdef VTIME { "VTIME", (unsigned long) VTIME }, #endif // VTIME #ifdef VWERASE { "VWERASE", (unsigned long) VWERASE }, #endif // VWERASE #ifdef WCHAR_MAX { "WCHAR_MAX", (unsigned long) WCHAR_MAX }, #endif // WCHAR_MAX #ifdef WCHAR_MIN { "WCHAR_MIN", (unsigned long) WCHAR_MIN }, #endif // WCHAR_MIN #ifdef WCHAR_WIDTH { "WCHAR_WIDTH", (unsigned long) WCHAR_WIDTH }, #endif // WCHAR_WIDTH #ifdef WINT_WIDTH { "WINT_WIDTH", (unsigned long) WINT_WIDTH }, #endif // WINT_WIDTH #ifdef W_OK { "W_OK", (unsigned long) W_OK }, #endif // W_OK #ifdef XATTR_LIST_MAX { "XATTR_LIST_MAX", (unsigned long) XATTR_LIST_MAX }, #endif // XATTR_LIST_MAX #ifdef XATTR_NAME_MAX { "XATTR_NAME_MAX", (unsigned long) XATTR_NAME_MAX }, #endif // XATTR_NAME_MAX #ifdef XATTR_SIZE_MAX { "XATTR_SIZE_MAX", (unsigned long) XATTR_SIZE_MAX }, #endif // XATTR_SIZE_MAX #ifdef XCASE { "XCASE", (unsigned long) XCASE }, #endif // XCASE #ifdef X_OK { "X_OK", (unsigned long) X_OK }, #endif // X_OK #ifdef XTABS { "XTABS", (unsigned long) XTABS }, #endif // XTABS { NULL, 0 }, }; minijail-2026.05.18+git20261016.8787d1a/linux-x86/libsyscalls.gen.c000066400000000000000000000571621526451555500233700ustar00rootroot00000000000000/* GENERATED BY MAKEFILE */ #include #include "gen_syscalls-inl.h" #include "libsyscalls.h" const struct syscall_entry syscall_table[] = { #ifdef __NR_read { "read", __NR_read }, #endif #ifdef __NR_write { "write", __NR_write }, #endif #ifdef __NR_open { "open", __NR_open }, #endif #ifdef __NR_close { "close", __NR_close }, #endif #ifdef __NR_stat { "stat", __NR_stat }, #endif #ifdef __NR_fstat { "fstat", __NR_fstat }, #endif #ifdef __NR_lstat { "lstat", __NR_lstat }, #endif #ifdef __NR_poll { "poll", __NR_poll }, #endif #ifdef __NR_lseek { "lseek", __NR_lseek }, #endif #ifdef __NR_mmap { "mmap", __NR_mmap }, #endif #ifdef __NR_mprotect { "mprotect", __NR_mprotect }, #endif #ifdef __NR_munmap { "munmap", __NR_munmap }, #endif #ifdef __NR_brk { "brk", __NR_brk }, #endif #ifdef __NR_rt_sigaction { "rt_sigaction", __NR_rt_sigaction }, #endif #ifdef __NR_rt_sigprocmask { "rt_sigprocmask", __NR_rt_sigprocmask }, #endif #ifdef __NR_rt_sigreturn { "rt_sigreturn", __NR_rt_sigreturn }, #endif #ifdef __NR_ioctl { "ioctl", __NR_ioctl }, #endif #ifdef __NR_pread64 { "pread64", __NR_pread64 }, #endif #ifdef __NR_pwrite64 { "pwrite64", __NR_pwrite64 }, #endif #ifdef __NR_readv { "readv", __NR_readv }, #endif #ifdef __NR_writev { "writev", __NR_writev }, #endif #ifdef __NR_access { "access", __NR_access }, #endif #ifdef __NR_pipe { "pipe", __NR_pipe }, #endif #ifdef __NR_select { "select", __NR_select }, #endif #ifdef __NR_sched_yield { "sched_yield", __NR_sched_yield }, #endif #ifdef __NR_mremap { "mremap", __NR_mremap }, #endif #ifdef __NR_msync { "msync", __NR_msync }, #endif #ifdef __NR_mincore { "mincore", __NR_mincore }, #endif #ifdef __NR_madvise { "madvise", __NR_madvise }, #endif #ifdef __NR_shmget { "shmget", __NR_shmget }, #endif #ifdef __NR_shmat { "shmat", __NR_shmat }, #endif #ifdef __NR_shmctl { "shmctl", __NR_shmctl }, #endif #ifdef __NR_dup { "dup", __NR_dup }, #endif #ifdef __NR_dup2 { "dup2", __NR_dup2 }, #endif #ifdef __NR_pause { "pause", __NR_pause }, #endif #ifdef __NR_nanosleep { "nanosleep", __NR_nanosleep }, #endif #ifdef __NR_getitimer { "getitimer", __NR_getitimer }, #endif #ifdef __NR_alarm { "alarm", __NR_alarm }, #endif #ifdef __NR_setitimer { "setitimer", __NR_setitimer }, #endif #ifdef __NR_getpid { "getpid", __NR_getpid }, #endif #ifdef __NR_sendfile { "sendfile", __NR_sendfile }, #endif #ifdef __NR_socket { "socket", __NR_socket }, #endif #ifdef __NR_connect { "connect", __NR_connect }, #endif #ifdef __NR_accept { "accept", __NR_accept }, #endif #ifdef __NR_sendto { "sendto", __NR_sendto }, #endif #ifdef __NR_recvfrom { "recvfrom", __NR_recvfrom }, #endif #ifdef __NR_sendmsg { "sendmsg", __NR_sendmsg }, #endif #ifdef __NR_recvmsg { "recvmsg", __NR_recvmsg }, #endif #ifdef __NR_shutdown { "shutdown", __NR_shutdown }, #endif #ifdef __NR_bind { "bind", __NR_bind }, #endif #ifdef __NR_listen { "listen", __NR_listen }, #endif #ifdef __NR_getsockname { "getsockname", __NR_getsockname }, #endif #ifdef __NR_getpeername { "getpeername", __NR_getpeername }, #endif #ifdef __NR_socketpair { "socketpair", __NR_socketpair }, #endif #ifdef __NR_setsockopt { "setsockopt", __NR_setsockopt }, #endif #ifdef __NR_getsockopt { "getsockopt", __NR_getsockopt }, #endif #ifdef __NR_clone { "clone", __NR_clone }, #endif #ifdef __NR_fork { "fork", __NR_fork }, #endif #ifdef __NR_vfork { "vfork", __NR_vfork }, #endif #ifdef __NR_execve { "execve", __NR_execve }, #endif #ifdef __NR_exit { "exit", __NR_exit }, #endif #ifdef __NR_wait4 { "wait4", __NR_wait4 }, #endif #ifdef __NR_kill { "kill", __NR_kill }, #endif #ifdef __NR_uname { "uname", __NR_uname }, #endif #ifdef __NR_semget { "semget", __NR_semget }, #endif #ifdef __NR_semop { "semop", __NR_semop }, #endif #ifdef __NR_semctl { "semctl", __NR_semctl }, #endif #ifdef __NR_shmdt { "shmdt", __NR_shmdt }, #endif #ifdef __NR_msgget { "msgget", __NR_msgget }, #endif #ifdef __NR_msgsnd { "msgsnd", __NR_msgsnd }, #endif #ifdef __NR_msgrcv { "msgrcv", __NR_msgrcv }, #endif #ifdef __NR_msgctl { "msgctl", __NR_msgctl }, #endif #ifdef __NR_fcntl { "fcntl", __NR_fcntl }, #endif #ifdef __NR_flock { "flock", __NR_flock }, #endif #ifdef __NR_fsync { "fsync", __NR_fsync }, #endif #ifdef __NR_fdatasync { "fdatasync", __NR_fdatasync }, #endif #ifdef __NR_truncate { "truncate", __NR_truncate }, #endif #ifdef __NR_ftruncate { "ftruncate", __NR_ftruncate }, #endif #ifdef __NR_getdents { "getdents", __NR_getdents }, #endif #ifdef __NR_getcwd { "getcwd", __NR_getcwd }, #endif #ifdef __NR_chdir { "chdir", __NR_chdir }, #endif #ifdef __NR_fchdir { "fchdir", __NR_fchdir }, #endif #ifdef __NR_rename { "rename", __NR_rename }, #endif #ifdef __NR_mkdir { "mkdir", __NR_mkdir }, #endif #ifdef __NR_rmdir { "rmdir", __NR_rmdir }, #endif #ifdef __NR_creat { "creat", __NR_creat }, #endif #ifdef __NR_link { "link", __NR_link }, #endif #ifdef __NR_unlink { "unlink", __NR_unlink }, #endif #ifdef __NR_symlink { "symlink", __NR_symlink }, #endif #ifdef __NR_readlink { "readlink", __NR_readlink }, #endif #ifdef __NR_chmod { "chmod", __NR_chmod }, #endif #ifdef __NR_fchmod { "fchmod", __NR_fchmod }, #endif #ifdef __NR_chown { "chown", __NR_chown }, #endif #ifdef __NR_fchown { "fchown", __NR_fchown }, #endif #ifdef __NR_lchown { "lchown", __NR_lchown }, #endif #ifdef __NR_umask { "umask", __NR_umask }, #endif #ifdef __NR_gettimeofday { "gettimeofday", __NR_gettimeofday }, #endif #ifdef __NR_getrlimit { "getrlimit", __NR_getrlimit }, #endif #ifdef __NR_getrusage { "getrusage", __NR_getrusage }, #endif #ifdef __NR_sysinfo { "sysinfo", __NR_sysinfo }, #endif #ifdef __NR_times { "times", __NR_times }, #endif #ifdef __NR_ptrace { "ptrace", __NR_ptrace }, #endif #ifdef __NR_getuid { "getuid", __NR_getuid }, #endif #ifdef __NR_syslog { "syslog", __NR_syslog }, #endif #ifdef __NR_getgid { "getgid", __NR_getgid }, #endif #ifdef __NR_setuid { "setuid", __NR_setuid }, #endif #ifdef __NR_setgid { "setgid", __NR_setgid }, #endif #ifdef __NR_geteuid { "geteuid", __NR_geteuid }, #endif #ifdef __NR_getegid { "getegid", __NR_getegid }, #endif #ifdef __NR_setpgid { "setpgid", __NR_setpgid }, #endif #ifdef __NR_getppid { "getppid", __NR_getppid }, #endif #ifdef __NR_getpgrp { "getpgrp", __NR_getpgrp }, #endif #ifdef __NR_setsid { "setsid", __NR_setsid }, #endif #ifdef __NR_setreuid { "setreuid", __NR_setreuid }, #endif #ifdef __NR_setregid { "setregid", __NR_setregid }, #endif #ifdef __NR_getgroups { "getgroups", __NR_getgroups }, #endif #ifdef __NR_setgroups { "setgroups", __NR_setgroups }, #endif #ifdef __NR_setresuid { "setresuid", __NR_setresuid }, #endif #ifdef __NR_getresuid { "getresuid", __NR_getresuid }, #endif #ifdef __NR_setresgid { "setresgid", __NR_setresgid }, #endif #ifdef __NR_getresgid { "getresgid", __NR_getresgid }, #endif #ifdef __NR_getpgid { "getpgid", __NR_getpgid }, #endif #ifdef __NR_setfsuid { "setfsuid", __NR_setfsuid }, #endif #ifdef __NR_setfsgid { "setfsgid", __NR_setfsgid }, #endif #ifdef __NR_getsid { "getsid", __NR_getsid }, #endif #ifdef __NR_capget { "capget", __NR_capget }, #endif #ifdef __NR_capset { "capset", __NR_capset }, #endif #ifdef __NR_rt_sigpending { "rt_sigpending", __NR_rt_sigpending }, #endif #ifdef __NR_rt_sigtimedwait { "rt_sigtimedwait", __NR_rt_sigtimedwait }, #endif #ifdef __NR_rt_sigqueueinfo { "rt_sigqueueinfo", __NR_rt_sigqueueinfo }, #endif #ifdef __NR_rt_sigsuspend { "rt_sigsuspend", __NR_rt_sigsuspend }, #endif #ifdef __NR_sigaltstack { "sigaltstack", __NR_sigaltstack }, #endif #ifdef __NR_utime { "utime", __NR_utime }, #endif #ifdef __NR_mknod { "mknod", __NR_mknod }, #endif #ifdef __NR_uselib { "uselib", __NR_uselib }, #endif #ifdef __NR_personality { "personality", __NR_personality }, #endif #ifdef __NR_ustat { "ustat", __NR_ustat }, #endif #ifdef __NR_statfs { "statfs", __NR_statfs }, #endif #ifdef __NR_fstatfs { "fstatfs", __NR_fstatfs }, #endif #ifdef __NR_sysfs { "sysfs", __NR_sysfs }, #endif #ifdef __NR_getpriority { "getpriority", __NR_getpriority }, #endif #ifdef __NR_setpriority { "setpriority", __NR_setpriority }, #endif #ifdef __NR_sched_setparam { "sched_setparam", __NR_sched_setparam }, #endif #ifdef __NR_sched_getparam { "sched_getparam", __NR_sched_getparam }, #endif #ifdef __NR_sched_setscheduler { "sched_setscheduler", __NR_sched_setscheduler }, #endif #ifdef __NR_sched_getscheduler { "sched_getscheduler", __NR_sched_getscheduler }, #endif #ifdef __NR_sched_get_priority_max { "sched_get_priority_max", __NR_sched_get_priority_max }, #endif #ifdef __NR_sched_get_priority_min { "sched_get_priority_min", __NR_sched_get_priority_min }, #endif #ifdef __NR_sched_rr_get_interval { "sched_rr_get_interval", __NR_sched_rr_get_interval }, #endif #ifdef __NR_mlock { "mlock", __NR_mlock }, #endif #ifdef __NR_munlock { "munlock", __NR_munlock }, #endif #ifdef __NR_mlockall { "mlockall", __NR_mlockall }, #endif #ifdef __NR_munlockall { "munlockall", __NR_munlockall }, #endif #ifdef __NR_vhangup { "vhangup", __NR_vhangup }, #endif #ifdef __NR_modify_ldt { "modify_ldt", __NR_modify_ldt }, #endif #ifdef __NR_pivot_root { "pivot_root", __NR_pivot_root }, #endif #ifdef __NR__sysctl { "_sysctl", __NR__sysctl }, #endif #ifdef __NR_prctl { "prctl", __NR_prctl }, #endif #ifdef __NR_arch_prctl { "arch_prctl", __NR_arch_prctl }, #endif #ifdef __NR_adjtimex { "adjtimex", __NR_adjtimex }, #endif #ifdef __NR_setrlimit { "setrlimit", __NR_setrlimit }, #endif #ifdef __NR_chroot { "chroot", __NR_chroot }, #endif #ifdef __NR_sync { "sync", __NR_sync }, #endif #ifdef __NR_acct { "acct", __NR_acct }, #endif #ifdef __NR_settimeofday { "settimeofday", __NR_settimeofday }, #endif #ifdef __NR_mount { "mount", __NR_mount }, #endif #ifdef __NR_umount2 { "umount2", __NR_umount2 }, #endif #ifdef __NR_swapon { "swapon", __NR_swapon }, #endif #ifdef __NR_swapoff { "swapoff", __NR_swapoff }, #endif #ifdef __NR_reboot { "reboot", __NR_reboot }, #endif #ifdef __NR_sethostname { "sethostname", __NR_sethostname }, #endif #ifdef __NR_setdomainname { "setdomainname", __NR_setdomainname }, #endif #ifdef __NR_iopl { "iopl", __NR_iopl }, #endif #ifdef __NR_ioperm { "ioperm", __NR_ioperm }, #endif #ifdef __NR_create_module { "create_module", __NR_create_module }, #endif #ifdef __NR_init_module { "init_module", __NR_init_module }, #endif #ifdef __NR_delete_module { "delete_module", __NR_delete_module }, #endif #ifdef __NR_get_kernel_syms { "get_kernel_syms", __NR_get_kernel_syms }, #endif #ifdef __NR_query_module { "query_module", __NR_query_module }, #endif #ifdef __NR_quotactl { "quotactl", __NR_quotactl }, #endif #ifdef __NR_nfsservctl { "nfsservctl", __NR_nfsservctl }, #endif #ifdef __NR_getpmsg { "getpmsg", __NR_getpmsg }, #endif #ifdef __NR_putpmsg { "putpmsg", __NR_putpmsg }, #endif #ifdef __NR_afs_syscall { "afs_syscall", __NR_afs_syscall }, #endif #ifdef __NR_tuxcall { "tuxcall", __NR_tuxcall }, #endif #ifdef __NR_security { "security", __NR_security }, #endif #ifdef __NR_gettid { "gettid", __NR_gettid }, #endif #ifdef __NR_readahead { "readahead", __NR_readahead }, #endif #ifdef __NR_setxattr { "setxattr", __NR_setxattr }, #endif #ifdef __NR_lsetxattr { "lsetxattr", __NR_lsetxattr }, #endif #ifdef __NR_fsetxattr { "fsetxattr", __NR_fsetxattr }, #endif #ifdef __NR_getxattr { "getxattr", __NR_getxattr }, #endif #ifdef __NR_lgetxattr { "lgetxattr", __NR_lgetxattr }, #endif #ifdef __NR_fgetxattr { "fgetxattr", __NR_fgetxattr }, #endif #ifdef __NR_listxattr { "listxattr", __NR_listxattr }, #endif #ifdef __NR_llistxattr { "llistxattr", __NR_llistxattr }, #endif #ifdef __NR_flistxattr { "flistxattr", __NR_flistxattr }, #endif #ifdef __NR_removexattr { "removexattr", __NR_removexattr }, #endif #ifdef __NR_lremovexattr { "lremovexattr", __NR_lremovexattr }, #endif #ifdef __NR_fremovexattr { "fremovexattr", __NR_fremovexattr }, #endif #ifdef __NR_tkill { "tkill", __NR_tkill }, #endif #ifdef __NR_time { "time", __NR_time }, #endif #ifdef __NR_futex { "futex", __NR_futex }, #endif #ifdef __NR_sched_setaffinity { "sched_setaffinity", __NR_sched_setaffinity }, #endif #ifdef __NR_sched_getaffinity { "sched_getaffinity", __NR_sched_getaffinity }, #endif #ifdef __NR_set_thread_area { "set_thread_area", __NR_set_thread_area }, #endif #ifdef __NR_io_setup { "io_setup", __NR_io_setup }, #endif #ifdef __NR_io_destroy { "io_destroy", __NR_io_destroy }, #endif #ifdef __NR_io_getevents { "io_getevents", __NR_io_getevents }, #endif #ifdef __NR_io_submit { "io_submit", __NR_io_submit }, #endif #ifdef __NR_io_cancel { "io_cancel", __NR_io_cancel }, #endif #ifdef __NR_get_thread_area { "get_thread_area", __NR_get_thread_area }, #endif #ifdef __NR_lookup_dcookie { "lookup_dcookie", __NR_lookup_dcookie }, #endif #ifdef __NR_epoll_create { "epoll_create", __NR_epoll_create }, #endif #ifdef __NR_epoll_ctl_old { "epoll_ctl_old", __NR_epoll_ctl_old }, #endif #ifdef __NR_epoll_wait_old { "epoll_wait_old", __NR_epoll_wait_old }, #endif #ifdef __NR_remap_file_pages { "remap_file_pages", __NR_remap_file_pages }, #endif #ifdef __NR_getdents64 { "getdents64", __NR_getdents64 }, #endif #ifdef __NR_set_tid_address { "set_tid_address", __NR_set_tid_address }, #endif #ifdef __NR_restart_syscall { "restart_syscall", __NR_restart_syscall }, #endif #ifdef __NR_semtimedop { "semtimedop", __NR_semtimedop }, #endif #ifdef __NR_fadvise64 { "fadvise64", __NR_fadvise64 }, #endif #ifdef __NR_timer_create { "timer_create", __NR_timer_create }, #endif #ifdef __NR_timer_settime { "timer_settime", __NR_timer_settime }, #endif #ifdef __NR_timer_gettime { "timer_gettime", __NR_timer_gettime }, #endif #ifdef __NR_timer_getoverrun { "timer_getoverrun", __NR_timer_getoverrun }, #endif #ifdef __NR_timer_delete { "timer_delete", __NR_timer_delete }, #endif #ifdef __NR_clock_settime { "clock_settime", __NR_clock_settime }, #endif #ifdef __NR_clock_gettime { "clock_gettime", __NR_clock_gettime }, #endif #ifdef __NR_clock_getres { "clock_getres", __NR_clock_getres }, #endif #ifdef __NR_clock_nanosleep { "clock_nanosleep", __NR_clock_nanosleep }, #endif #ifdef __NR_exit_group { "exit_group", __NR_exit_group }, #endif #ifdef __NR_epoll_wait { "epoll_wait", __NR_epoll_wait }, #endif #ifdef __NR_epoll_ctl { "epoll_ctl", __NR_epoll_ctl }, #endif #ifdef __NR_tgkill { "tgkill", __NR_tgkill }, #endif #ifdef __NR_utimes { "utimes", __NR_utimes }, #endif #ifdef __NR_vserver { "vserver", __NR_vserver }, #endif #ifdef __NR_mbind { "mbind", __NR_mbind }, #endif #ifdef __NR_set_mempolicy { "set_mempolicy", __NR_set_mempolicy }, #endif #ifdef __NR_get_mempolicy { "get_mempolicy", __NR_get_mempolicy }, #endif #ifdef __NR_mq_open { "mq_open", __NR_mq_open }, #endif #ifdef __NR_mq_unlink { "mq_unlink", __NR_mq_unlink }, #endif #ifdef __NR_mq_timedsend { "mq_timedsend", __NR_mq_timedsend }, #endif #ifdef __NR_mq_timedreceive { "mq_timedreceive", __NR_mq_timedreceive }, #endif #ifdef __NR_mq_notify { "mq_notify", __NR_mq_notify }, #endif #ifdef __NR_mq_getsetattr { "mq_getsetattr", __NR_mq_getsetattr }, #endif #ifdef __NR_kexec_load { "kexec_load", __NR_kexec_load }, #endif #ifdef __NR_waitid { "waitid", __NR_waitid }, #endif #ifdef __NR_add_key { "add_key", __NR_add_key }, #endif #ifdef __NR_request_key { "request_key", __NR_request_key }, #endif #ifdef __NR_keyctl { "keyctl", __NR_keyctl }, #endif #ifdef __NR_ioprio_set { "ioprio_set", __NR_ioprio_set }, #endif #ifdef __NR_ioprio_get { "ioprio_get", __NR_ioprio_get }, #endif #ifdef __NR_inotify_init { "inotify_init", __NR_inotify_init }, #endif #ifdef __NR_inotify_add_watch { "inotify_add_watch", __NR_inotify_add_watch }, #endif #ifdef __NR_inotify_rm_watch { "inotify_rm_watch", __NR_inotify_rm_watch }, #endif #ifdef __NR_migrate_pages { "migrate_pages", __NR_migrate_pages }, #endif #ifdef __NR_openat { "openat", __NR_openat }, #endif #ifdef __NR_mkdirat { "mkdirat", __NR_mkdirat }, #endif #ifdef __NR_mknodat { "mknodat", __NR_mknodat }, #endif #ifdef __NR_fchownat { "fchownat", __NR_fchownat }, #endif #ifdef __NR_futimesat { "futimesat", __NR_futimesat }, #endif #ifdef __NR_newfstatat { "newfstatat", __NR_newfstatat }, #endif #ifdef __NR_unlinkat { "unlinkat", __NR_unlinkat }, #endif #ifdef __NR_renameat { "renameat", __NR_renameat }, #endif #ifdef __NR_linkat { "linkat", __NR_linkat }, #endif #ifdef __NR_symlinkat { "symlinkat", __NR_symlinkat }, #endif #ifdef __NR_readlinkat { "readlinkat", __NR_readlinkat }, #endif #ifdef __NR_fchmodat { "fchmodat", __NR_fchmodat }, #endif #ifdef __NR_faccessat { "faccessat", __NR_faccessat }, #endif #ifdef __NR_pselect6 { "pselect6", __NR_pselect6 }, #endif #ifdef __NR_ppoll { "ppoll", __NR_ppoll }, #endif #ifdef __NR_unshare { "unshare", __NR_unshare }, #endif #ifdef __NR_set_robust_list { "set_robust_list", __NR_set_robust_list }, #endif #ifdef __NR_get_robust_list { "get_robust_list", __NR_get_robust_list }, #endif #ifdef __NR_splice { "splice", __NR_splice }, #endif #ifdef __NR_tee { "tee", __NR_tee }, #endif #ifdef __NR_sync_file_range { "sync_file_range", __NR_sync_file_range }, #endif #ifdef __NR_vmsplice { "vmsplice", __NR_vmsplice }, #endif #ifdef __NR_move_pages { "move_pages", __NR_move_pages }, #endif #ifdef __NR_utimensat { "utimensat", __NR_utimensat }, #endif #ifdef __NR_epoll_pwait { "epoll_pwait", __NR_epoll_pwait }, #endif #ifdef __NR_signalfd { "signalfd", __NR_signalfd }, #endif #ifdef __NR_timerfd_create { "timerfd_create", __NR_timerfd_create }, #endif #ifdef __NR_eventfd { "eventfd", __NR_eventfd }, #endif #ifdef __NR_fallocate { "fallocate", __NR_fallocate }, #endif #ifdef __NR_timerfd_settime { "timerfd_settime", __NR_timerfd_settime }, #endif #ifdef __NR_timerfd_gettime { "timerfd_gettime", __NR_timerfd_gettime }, #endif #ifdef __NR_accept4 { "accept4", __NR_accept4 }, #endif #ifdef __NR_signalfd4 { "signalfd4", __NR_signalfd4 }, #endif #ifdef __NR_eventfd2 { "eventfd2", __NR_eventfd2 }, #endif #ifdef __NR_epoll_create1 { "epoll_create1", __NR_epoll_create1 }, #endif #ifdef __NR_dup3 { "dup3", __NR_dup3 }, #endif #ifdef __NR_pipe2 { "pipe2", __NR_pipe2 }, #endif #ifdef __NR_inotify_init1 { "inotify_init1", __NR_inotify_init1 }, #endif #ifdef __NR_preadv { "preadv", __NR_preadv }, #endif #ifdef __NR_pwritev { "pwritev", __NR_pwritev }, #endif #ifdef __NR_rt_tgsigqueueinfo { "rt_tgsigqueueinfo", __NR_rt_tgsigqueueinfo }, #endif #ifdef __NR_perf_event_open { "perf_event_open", __NR_perf_event_open }, #endif #ifdef __NR_recvmmsg { "recvmmsg", __NR_recvmmsg }, #endif #ifdef __NR_fanotify_init { "fanotify_init", __NR_fanotify_init }, #endif #ifdef __NR_fanotify_mark { "fanotify_mark", __NR_fanotify_mark }, #endif #ifdef __NR_prlimit64 { "prlimit64", __NR_prlimit64 }, #endif #ifdef __NR_name_to_handle_at { "name_to_handle_at", __NR_name_to_handle_at }, #endif #ifdef __NR_open_by_handle_at { "open_by_handle_at", __NR_open_by_handle_at }, #endif #ifdef __NR_clock_adjtime { "clock_adjtime", __NR_clock_adjtime }, #endif #ifdef __NR_syncfs { "syncfs", __NR_syncfs }, #endif #ifdef __NR_sendmmsg { "sendmmsg", __NR_sendmmsg }, #endif #ifdef __NR_setns { "setns", __NR_setns }, #endif #ifdef __NR_getcpu { "getcpu", __NR_getcpu }, #endif #ifdef __NR_process_vm_readv { "process_vm_readv", __NR_process_vm_readv }, #endif #ifdef __NR_process_vm_writev { "process_vm_writev", __NR_process_vm_writev }, #endif #ifdef __NR_kcmp { "kcmp", __NR_kcmp }, #endif #ifdef __NR_finit_module { "finit_module", __NR_finit_module }, #endif #ifdef __NR_sched_setattr { "sched_setattr", __NR_sched_setattr }, #endif #ifdef __NR_sched_getattr { "sched_getattr", __NR_sched_getattr }, #endif #ifdef __NR_renameat2 { "renameat2", __NR_renameat2 }, #endif #ifdef __NR_seccomp { "seccomp", __NR_seccomp }, #endif #ifdef __NR_getrandom { "getrandom", __NR_getrandom }, #endif #ifdef __NR_memfd_create { "memfd_create", __NR_memfd_create }, #endif #ifdef __NR_kexec_file_load { "kexec_file_load", __NR_kexec_file_load }, #endif #ifdef __NR_bpf { "bpf", __NR_bpf }, #endif #ifdef __NR_execveat { "execveat", __NR_execveat }, #endif #ifdef __NR_userfaultfd { "userfaultfd", __NR_userfaultfd }, #endif #ifdef __NR_membarrier { "membarrier", __NR_membarrier }, #endif #ifdef __NR_mlock2 { "mlock2", __NR_mlock2 }, #endif #ifdef __NR_copy_file_range { "copy_file_range", __NR_copy_file_range }, #endif #ifdef __NR_preadv2 { "preadv2", __NR_preadv2 }, #endif #ifdef __NR_pwritev2 { "pwritev2", __NR_pwritev2 }, #endif #ifdef __NR_pkey_mprotect { "pkey_mprotect", __NR_pkey_mprotect }, #endif #ifdef __NR_pkey_alloc { "pkey_alloc", __NR_pkey_alloc }, #endif #ifdef __NR_pkey_free { "pkey_free", __NR_pkey_free }, #endif #ifdef __NR_statx { "statx", __NR_statx }, #endif #ifdef __NR_io_pgetevents { "io_pgetevents", __NR_io_pgetevents }, #endif #ifdef __NR_rseq { "rseq", __NR_rseq }, #endif #ifdef __NR_uretprobe { "uretprobe", __NR_uretprobe }, #endif #ifdef __NR_uprobe { "uprobe", __NR_uprobe }, #endif #ifdef __NR_pidfd_send_signal { "pidfd_send_signal", __NR_pidfd_send_signal }, #endif #ifdef __NR_io_uring_setup { "io_uring_setup", __NR_io_uring_setup }, #endif #ifdef __NR_io_uring_enter { "io_uring_enter", __NR_io_uring_enter }, #endif #ifdef __NR_io_uring_register { "io_uring_register", __NR_io_uring_register }, #endif #ifdef __NR_open_tree { "open_tree", __NR_open_tree }, #endif #ifdef __NR_move_mount { "move_mount", __NR_move_mount }, #endif #ifdef __NR_fsopen { "fsopen", __NR_fsopen }, #endif #ifdef __NR_fsconfig { "fsconfig", __NR_fsconfig }, #endif #ifdef __NR_fsmount { "fsmount", __NR_fsmount }, #endif #ifdef __NR_fspick { "fspick", __NR_fspick }, #endif #ifdef __NR_pidfd_open { "pidfd_open", __NR_pidfd_open }, #endif #ifdef __NR_clone3 { "clone3", __NR_clone3 }, #endif #ifdef __NR_close_range { "close_range", __NR_close_range }, #endif #ifdef __NR_openat2 { "openat2", __NR_openat2 }, #endif #ifdef __NR_pidfd_getfd { "pidfd_getfd", __NR_pidfd_getfd }, #endif #ifdef __NR_faccessat2 { "faccessat2", __NR_faccessat2 }, #endif #ifdef __NR_process_madvise { "process_madvise", __NR_process_madvise }, #endif #ifdef __NR_epoll_pwait2 { "epoll_pwait2", __NR_epoll_pwait2 }, #endif #ifdef __NR_mount_setattr { "mount_setattr", __NR_mount_setattr }, #endif #ifdef __NR_quotactl_fd { "quotactl_fd", __NR_quotactl_fd }, #endif #ifdef __NR_landlock_create_ruleset { "landlock_create_ruleset", __NR_landlock_create_ruleset }, #endif #ifdef __NR_landlock_add_rule { "landlock_add_rule", __NR_landlock_add_rule }, #endif #ifdef __NR_landlock_restrict_self { "landlock_restrict_self", __NR_landlock_restrict_self }, #endif #ifdef __NR_memfd_secret { "memfd_secret", __NR_memfd_secret }, #endif #ifdef __NR_process_mrelease { "process_mrelease", __NR_process_mrelease }, #endif #ifdef __NR_futex_waitv { "futex_waitv", __NR_futex_waitv }, #endif #ifdef __NR_set_mempolicy_home_node { "set_mempolicy_home_node", __NR_set_mempolicy_home_node }, #endif #ifdef __NR_cachestat { "cachestat", __NR_cachestat }, #endif #ifdef __NR_fchmodat2 { "fchmodat2", __NR_fchmodat2 }, #endif #ifdef __NR_map_shadow_stack { "map_shadow_stack", __NR_map_shadow_stack }, #endif #ifdef __NR_futex_wake { "futex_wake", __NR_futex_wake }, #endif #ifdef __NR_futex_wait { "futex_wait", __NR_futex_wait }, #endif #ifdef __NR_futex_requeue { "futex_requeue", __NR_futex_requeue }, #endif #ifdef __NR_statmount { "statmount", __NR_statmount }, #endif #ifdef __NR_listmount { "listmount", __NR_listmount }, #endif #ifdef __NR_lsm_get_self_attr { "lsm_get_self_attr", __NR_lsm_get_self_attr }, #endif #ifdef __NR_lsm_set_self_attr { "lsm_set_self_attr", __NR_lsm_set_self_attr }, #endif #ifdef __NR_lsm_list_modules { "lsm_list_modules", __NR_lsm_list_modules }, #endif #ifdef __NR_mseal { "mseal", __NR_mseal }, #endif #ifdef __NR_setxattrat { "setxattrat", __NR_setxattrat }, #endif #ifdef __NR_getxattrat { "getxattrat", __NR_getxattrat }, #endif #ifdef __NR_listxattrat { "listxattrat", __NR_listxattrat }, #endif #ifdef __NR_removexattrat { "removexattrat", __NR_removexattrat }, #endif #ifdef __NR_open_tree_attr { "open_tree_attr", __NR_open_tree_attr }, #endif #ifdef __NR_file_getattr { "file_getattr", __NR_file_getattr }, #endif #ifdef __NR_file_setattr { "file_setattr", __NR_file_setattr }, #endif { NULL, -1 }, }; const size_t syscall_table_size = sizeof(syscall_table) / sizeof(syscall_table[0]); minijail-2026.05.18+git20261016.8787d1a/minijail0.1000066400000000000000000000457111526451555500203010ustar00rootroot00000000000000.TH MINIJAIL0 "1" "March 2016" "ChromiumOS" "User Commands" .SH NAME minijail0 \- sandbox a process .SH SYNOPSIS .B minijail0 [\fIOPTION\fR]... <\fIPROGRAM\fR> [\fIargs\fR]... .SH DESCRIPTION .PP Runs PROGRAM inside a sandbox. .TP \fB-a \fR Run using the alternate syscall table named \fItable\fR. Only available on kernels and architectures that support the \fBPR_ALT_SYSCALL\fR option of \fBprctl\fR(2). .TP \fB-b [,[dest][,]]\fR, \fB--bind-mount=[,[dest][,]]\fR Bind-mount \fIsrc\fR into the chroot directory at \fIdest\fR, optionally writeable. The \fIsrc\fR path must be an absolute path. .IP If \fIdest\fR is not specified, it will default to \fIsrc\fR. If the destination does not exist, it will be created as a file or directory based on the \fIsrc\fR type (including missing parent directories). .IP To create a writable bind-mount set \fIwritable\fR to \fB1\fR. If not specified it will default to \fB0\fR (read-only). .TP \fB-B \fR Skip setting securebits in \fImask\fR when restricting capabilities (\fB-c\fR). \fImask\fR is a hex constant that represents the mask of securebits that will be preserved. See \fBcapabilities\fR(7) for the complete list. By default, \fBSECURE_NOROOT\fR, \fBSECURE_NO_SETUID_FIXUP\fR, and \fBSECURE_KEEP_CAPS\fR (together with their respective locks) are set. \fBSECBIT_NO_CAP_AMBIENT_RAISE\fR (and its respective lock) is never set because the permitted and inheritable capability sets have already been set through \fB-c\fR. .TP \fB-c \fR Restrict capabilities to \fIcaps\fR, which is either a hex constant or a string that will be passed to \fBcap_from_text\fR(3) (only the effective capability mask will be considered). The value will be used as the permitted, effective, and inheritable sets. When used in conjunction with \fB-u\fR and \fB-g\fR, this allows a program to have access to only certain parts of root's default privileges while running as another user and group ID altogether. Note that these capabilities are not inherited by subprocesses of the process given capabilities unless those subprocesses have POSIX file capabilities or the \fB--ambient\fR flag is also passed. See \fBcapabilities\fR(7). .TP \fB-C \fR Change root (using \fBchroot\fR(2)) to \fIdir\fR. .TP \fB-d\fR, \fB--mount-dev\fR Create a new /dev mount with a minimal set of nodes. Implies \fB-v\fR. Additional nodes can be bound with the \fB-b\fR or \fB-k\fR options. .RS .IP \(bu 3 The initial set of nodes are: full null tty urandom zero. .IP \(bu Symlinks are also created for: fd ptmx stderr stdin stdout. .IP \(bu Directores are also created for: shm. .RE .TP \fB-e[file]\fR Enter a new network namespace, or if \fIfile\fR is specified, enter an existing network namespace specified by \fIfile\fR which is typically of the form /proc//ns/net. .TP \fB-f \fR Write the pid of the jailed process to \fIfile\fR. .TP \fB-g Change groups to the specified \fIgroup\fR name, or numeric group ID \fIgid\fR. .TP \fB-G\fR Inherit all the supplementary groups of the user specified with \fB-u\fR. It is an error to use this option without having specified a \fBuser name\fR to \fB-u\fR. .TP \fB--add-suppl-group \fR Add the specified \fIgroup\fR name, or numeric group ID \fIgid\fR, to the process' supplementary groups list. Can be specified multiple times to add several groups. Incompatible with -y and -G. .TP \fB-h\fR Print a help message. .TP \fB-H\fR Print a help message detailing supported system call names for seccomp_filter. (Other direct numbers may be specified if minijail0 is not in sync with the host kernel or something like 32/64-bit compatibility issues exist.) .TP \fB-i\fR Exit immediately after \fBfork\fR(2). The jailed process will keep running in the background. .IP Normally minijail will fork+exec the specified \fIprogram\fR so that it can set up the right security settings in the new child process. The initial minijail process will stay resident and wait for the \fIprogram\fR to exit so the script that ran minijail will correctly block (e.g. standalone scripts). Specifying \fB-i\fR makes that initial process exit immediately and free up the resources. .IP This option is recommended for daemons and init services when you want to background the long running \fIprogram\fR. .TP \fB-I\fR Run \fIprogram\fR as init (pid 1) inside a new pid namespace (implies \fB-p\fR). .IP Most programs don't expect to run as an init which is why minijail will do it for you by default. Basically, the \fIprogram\fR needs to reap any processes it forks to avoid leaving zombies behind. Signal handling needs care since the kernel will mask all signals that don't have handlers registered (all default handlers are ignored and cannot be changed). .IP This means a minijail process (acting as init) will remain resident by default. While using \fB-I\fR is recommended when possible, strict review is required to make sure the \fIprogram\fR continues to work as expected. .IP \fB-i\fR and \fB-I\fR may be safely used together. The \fB-i\fR option controls the first minijail process outside of the pid namespace while the \fB-I\fR option controls the minijail process inside of the pid namespace. .TP \fB-k ,,[,[,]]\fR, \fB--mount=,,[,[,]]\fR Mount \fIsrc\fR, a \fItype\fR filesystem, at \fIdest\fR. If a chroot or pivot root is active, \fIdest\fR will automatically be placed below that path. .IP The \fIflags\fR field is optional and may be a mix of \fIMS_XXX\fR or hex constants separated by \fI|\fR characters. See \fBmount\fR(2) for details. \fIMS_NODEV|MS_NOSUID|MS_NOEXEC\fR is the default value (a writable mount with nodev/nosuid/noexec bits set), and it is strongly recommended that all mounts have these three bits set whenever possible. If you need to disable all three, then specify something like \fIMS_SILENT\fR. .IP The \fIdata\fR field is optional and is a comma delimited string (see \fBmount\fR(2) for details). It is passed directly to the kernel, so all fields here are filesystem specific. For \fItmpfs\fR, if no data is specified, we will default to \fImode=0755,size=10M\fR. If you want other settings, you will need to specify them explicitly yourself. .IP If the mount is not a pseudo filesystem (e.g. proc or sysfs), \fIsrc\fR path must be an absolute path (e.g. \fI/dev/sda1\fR and not \fIsda1\fR). .IP If the destination does not exist, it will be created as a directory (including missing parent directories). .TP \fB-K[mode]\fR Don't mark all existing mounts as MS_SLAVE. This option is \fBdangerous\fR as it negates most of the functionality of \fB-v\fR. You very likely don't need this. .IP You may specify a mount propagation mode in which case, that will be used instead of the default MS_SLAVE. See the \fBmount\fR(2) man page and the kernel docs \fIDocumentation/filesystems/sharedsubtree.txt\fR for more technical details, but a brief guide: .RS .IP \(bu 3 \fBslave\fR Changes in the parent mount namespace will propagate in, but changes in this mount namespace will not propagate back out. This is usually what people want to use, and is the default behavior if you don't specify \fB-K\fR. .IP \(bu \fBprivate\fR No changes in either mount namespace will propagate. This provides the most isolation. .IP \(bu \fBshared\fR Changes in the parent and this mount namespace will freely propagate back and forth. This is not recommended. .IP \(bu \fBunbindable\fR Mark all mounts as unbindable. .RE .TP \fB-l\fR Run inside a new IPC namespace. This option makes the program's System V IPC namespace independent. .TP \fB-L\fR Report blocked syscalls when using a seccomp filter. On kernels with support for SECCOMP_RET_LOG, every blocked syscall will be reported through the audit subsystem (see \fBseccomp\fR(2) for more details on SECCOMP_RET_LOG availability.) On all other kernels, the first failing syscall will be logged to syslog. This latter case will also force certain syscalls to be allowed in order to write to syslog. Note: this option is disabled and ignored for release builds. .TP \fB-m[ [, ]]\fR Set the uid mapping of a user namespace (implies \fB-pU\fR). Same arguments as \fBnewuidmap\fR(1). Multiple mappings should be separated by ','. With no mapping, map the current uid to root inside the user namespace. .TP \fB-M[ [, ]]\fR Set the gid mapping of a user namespace (implies \fB-pU\fR). Same arguments as \fBnewgidmap\fR(1). Multiple mappings should be separated by ','. With no mapping, map the current gid to root inside the user namespace. .TP \fB-n\fR Set the process's \fIno_new_privs\fR bit. See \fBprctl\fR(2) and the kernel source file \fIDocumentation/userspace-api/no_new_privs.txt\fR for more info. .TP \fB-N\fR Run inside a new cgroup namespace. This option runs the program with a cgroup view showing the program's cgroup as the root. This is only available on v4.6+ of the Linux kernel. .TP \fB-p\fR Run inside a new PID namespace. This option will make it impossible for the program to see or affect processes that are not its descendants. This implies \fB-v\fR and \fB-r\fR, since otherwise the process can see outside its namespace by inspecting /proc. .IP If the \fIprogram\fR exits, all of its children will be killed immediately by the kernel. If you need to daemonize or background things, use the \fB-i\fR option. .IP See \fBpid_namespaces\fR(7) for more info. .TP \fB-P \fR Set \fIdir\fR as the root fs using \fBpivot_root\fR. Implies \fB-v\fR, not compatible with \fB-C\fR. .TP \fB-r\fR Remount /proc readonly. This implies \fB-v\fR. Remounting /proc readonly means that even if the process has write access to a system config knob in /proc (e.g., in /sys/kernel), it cannot change the value. .TP \fB-R ,,\fR Set an rlimit value, see \fBgetrlimit\fR(2) for more details. .IP \fIrlim_type\fR may be specified using symbolic constants like \fIRLIMIT_AS\fR. .IP \fIrlim_cur\fR and \fIrlim_max\fR are specified either with a number (decimal or hex starting with \fI0x\fR), or with the string \fIunlimited\fR (which will translate to \fIRLIM_INFINITY\fR). .TP \fB-s\fR Enable \fBseccomp\fR(2) in mode 1, which restricts the child process to a very small set of system calls. You most likely do not want to use this with the seccomp filter mode (\fB-S\fR) as they are completely different (even though they have similar names). .TP \fB-S \fR Enable \fBseccomp\fR(2) in mode 13 which restricts the child process to a set of system calls defined in the policy file. Note that system call names may be different based on the runtime environment; see \fBminijail0\fR(5) for more details. .TP \fB-t[size]\fR Mounts a tmpfs filesystem on /tmp. /tmp must exist already (e.g. in the chroot). The filesystem has a default size of "64M", overridden with an optional argument. It has standard /tmp permissions (1777), and is mounted nodev/noexec/nosuid. Implies \fB-v\fR. .TP \fB-T \fR Assume binary's ELF linkage type is \fItype\fR, which must be either 'static' or 'dynamic'. Either setting will prevent minijail0 from manually parsing the ELF header to determine the type. Type 'static' can be used to avoid preload hooking, and will force minijail0 to instead set everything up before the program is executed. Type 'dynamic' will force minijail0 to preload \fIlibminijailpreload.so\fR to setup hooks, but will fail on actually statically-linked binaries. .TP \fB-u \fR Change users to the specified \fIuser\fR name, or numeric user ID \fIuid\fR. .TP \fB-U\fR Enter a new user namespace (implies \fB-p\fR). .TP \fB-v\fR, \fB--ns-mount\fR Run inside a new VFS namespace. This option prevents mounts performed by the program from affecting the rest of the system (but see \fB-K\fR). .TP \fB-V \fR Enter the VFS namespace specified by \fIfile\fR. .TP \fB-w\fR Create and join a new anonymous session keyring. See \fBkeyrings\fR(7) for more details. .TP \fB-y\fR Keep the current user's supplementary groups. .TP \fB-Y\fR Synchronize seccomp filters across thread group. .TP \fB-z\fR Don't forward any signals to the jailed process. For example, when not using \fB-i\fR, sending \fBSIGINT\fR (e.g., CTRL-C on the terminal), will kill the minijail0 process, not the jailed process. .TP \fB--ambient\fR Raise ambient capabilities to match the mask specified by \fB-c\fR. Since ambient capabilities are preserved across \fBexecve\fR(2), this allows for process trees to have a restricted set of capabilities, even if they are capability-dumb binaries. See \fBcapabilities\fR(7). .TP \fB--uts[=hostname]\fR Create a new UTS/hostname namespace, and optionally set the hostname in the new namespace to \fIhostname\fR. .TP \fB--env-reset\fR Clear the current environment instead of having the program inherit the active environment. This is often used to start the program with a minimal sanitized environment. .TP \fB--env-add \fR Adds or replace the specified environment variable \fINAME\fR in the program's environment before starting it, and set it to the specified \fIvalue\fR. This option can be used several times to set any number of environment variables. .TP \fB--logging=\fR Use \fIsystem\fR as the logging system. \fIsystem\fR must be one of \fBauto\fR (the default), \fBsyslog\fR, or \fBstderr\fR. .IP \fBauto\fR will use \fBstderr\fR if connected to a tty (e.g. run directly by a user), otherwise it will use \fBsyslog\fR. .TP \fB--profile \fR Choose from one of the available sandboxing profiles, which are simple way to get a standardized environment. See the .BR "SANDBOXING PROFILES" section below for the full list of supported values for \fIprofile\fR. .TP \fB--preload-library \fR Allows overriding the default path of \fI/lib/libminijailpreload.so\fR. This is only really useful for testing. .TP \fB--seccomp-bpf-binary \fR This is similar to \fB-S\fR, but instead of using a policy file, \fB--secomp-bpf-binary\fR expects a arch-and-kernel-version-specific pre-compiled BPF binary (such as the ones produced by \fBparse_seccomp_policy\fR). Note that the filter might be different based on the runtime environment; see \fBminijail0\fR(5) for more details. .TP \fB--allow-speculative-execution\fR Allow speculative execution features that may cause data leaks across processes. This passes the \fISECCOMP_FILTER_FLAG_SPEC_ALLOW\fR flag to seccomp which disables mitigations against certain speculative execution attacks; namely Branch Target Injection (spectre-v2) and Speculative Store Bypass (spectre-v4). These mitigations incur a runtime performance hit, so it is useful to be able to disable them in order to quantify their performance impact. .IP \fBWARNING:\fR It is dangerous to use this option on programs that process untrusted input, which is normally what Minijail is used for. Do not enable this option unless you know what you're doing. .IP See the kernel documentation \fIDocumentation/userspace-api/spec_ctrl.rst\fR and \fIDocumentation/admin-guide/hw-vuln/spectre.rst\fR for more information. .TP \fB--fs-default-paths\fR Adds a set of allowed paths to allow running common system executables. .IP \fBLandlock support:\fR This flag, and the following \fB--fs*\fR flags, impact what filesystem access is available and require kernel support for Landlock in order to be enforced. Landlock is available on Linux kernels >= 5.13, or ChromeOS kernels >= 5.10, as long as it is configured to be available as outlined in the Landlock kernel docs at \fIhttps://docs.kernel.org/userspace-api/landlock.html#kernel-support\fR. .IP \fBFlag combinations:\fR As long as \fB--fs-path*\fR or \fB--fs-default-paths\fR are used, Minijail will utilize Landlock if it is available. Additional \fB--fs-path*\fR flags used will expand the allowlisted paths and access rights. .TP \fB--enable-profile-fs-restrictions\fR Limit paths available when using minimalistic-mountns. .TP \fB--fs-path-rx\fR Adds an allowed read-execute path. .TP \fB--fs-path-ro\fR Adds an allowed read-only path. .TP \fB--fs-path-rw\fR Adds an allowed read-write path. .TP \fB--fs-path-advanced-rw\fR Adds an allowed advanced read-write path. .TP \fB--no-fs-restrictions\fR Disables path-based filesystem restrictions. .TP \fB--no-default-runtime-environment\fR Disables default seccomp policy and setting of no_new_privs. The default runtime environment is used by Minijail if no other seccomp policy is set, unless this flag is set to opt out. .TP \fB--no-new-sessions\fR Skips having Minijail call setsid(). This is useful when running a process that expects to have a controlling terminal set. .TP \fB--preserve-fd\fR Preserves an fd and makes it available in the child process. The fd is preserved with the same integer value. .TP \fB--config \fR Use a Minijail configuration file to set options, through commandline-option-equivalent key-value pairs. See \fBminijail0\fR(5) for more details on the format of the configuration file. .SH SANDBOXING PROFILES The following sandboxing profiles are supported: .TP \fBminimalistic-mountns\fR Set up a minimalistic mount namespace. Equivalent to \fB-v -P /var/empty -b / -b /proc -b /dev/log -t -r --mount-dev\fR. .TP \fBminimalistic-mountns-nodev\fR Set up a minimalistic mount namespace with an empty /dev path. Equivalent to \fB-v -P /var/empty -b/ -b/proc -t -r\fR. .SH IMPLEMENTATION This program is broken up into two parts: \fBminijail0\fR (the frontend) and a helper library called \fBlibminijailpreload\fR. Some jailings can only be achieved from the process to which they will actually apply: .IP \(bu 3 capability use (without using ambient capabilities): non-ambient capabilities are not inherited across \fBexecve\fR(2) unless the file being executed has POSIX file capabilities. Ambient capabilities (the \fB--ambient\fR flag) fix capability inheritance across \fBexecve\fR(2) to avoid the need for file capabilities. .IP \(bu seccomp: a meaningful seccomp filter policy should disallow \fBexecve\fR(2), to prevent a compromised process from executing a different binary. However, this would prevent the seccomp policy from being applied before \fBexecve\fR(2). .PP To this end, \fBlibminijailpreload\fR is forcibly loaded into all dynamically-linked target programs by default; we pass the specific restrictions in an environment variable which the preloaded library looks for. The forcibly-loaded library then applies the restrictions to the newly-loaded program. .PP This behavior can be disabled by the use of the \fB-T static\fR flag. There are other cases in which the use of this flag might be useful: .IP \(bu 3 When \fIprogram\fR is linked against a different version of \fBlibc.so\fR than \fBlibminijailpreload.so\fR. .IP \(bu When \fBexecve\fR(2) has side-effects that interact badly with the jailing process. If the system uses SELinux, \fBexecve\fR(2) can cause an automatic domain transition, which would then require that the target domain allows the operations to jail \fIprogram\fR. .SH AUTHOR The ChromiumOS Authors .SH COPYRIGHT Copyright \(co 2011 The ChromiumOS Authors License BSD-like. .SH "SEE ALSO" .BR libminijail.h , .BR minijail0 (5), .BR seccomp (2) minijail-2026.05.18+git20261016.8787d1a/minijail0.5000066400000000000000000000160051526451555500202770ustar00rootroot00000000000000.TH MINIJAIL0 "5" "July 2011" "ChromiumOS" "User Commands" .SH NAME minijail0 \- sandbox a process .SH DESCRIPTION .PP Runs PROGRAM inside a sandbox. See \fBminijail0\fR(1) for details. .SH EXAMPLES Safely switch from user \fIroot\fR to \fInobody\fR, switch to primary group \fInobody\fR, drop all capabilities, and inherit any supplementary groups from user \fInobody\fR: .IP .EX # minijail0 -u nobody -g nobody -c 0 -G /usr/bin/whoami nobody .EE .PP Run in a PID and VFS namespace without superuser capabilities (but still as root) and with a private view of /proc: .IP .EX # minijail0 -p -v -r -c 0 /bin/ps PID TTY TIME CMD 1 pts/0 00:00:00 minijail0 2 pts/0 00:00:00 ps .EE .PP Running a process with a seccomp filter policy at reduced privileges: .IP .EX # minijail0 -S /usr/share/minijail0/$(uname -m)/cat.policy -- \\ /bin/cat /proc/self/seccomp_filter ... .EE .SH SECCOMP_FILTER POLICY The policy file supplied to the \fB-S\fR argument supports the following syntax: .IP .EX \fB\fR:\fB\fR \fB\fR:\fB\fR \fB\fR \fB# any single line comment\fR .EE .PP Long lines may be broken up using \\ at the end. .PP A policy that emulates \fBseccomp\fR(2) in mode 1 may look like: .IP .EX read: 1 write: 1 sig_return: 1 exit: 1 .EE .PP The "1" acts as a wildcard and allows any use of the mentioned system call. More advanced filtering is possible if your kernel supports CONFIG_FTRACE_SYSCALLS. For example, we can allow a process to open any file read only and mmap PROT_READ only: .IP .EX # open with O_LARGEFILE|O_RDONLY|O_NONBLOCK or some combination. open: arg1 == 32768 || arg1 == 0 || arg1 == 34816 || arg1 == 2048 mmap2: arg2 == 0x0 munmap: 1 close: 1 .EE .PP The supported arguments may be found by reviewing the system call prototypes in the Linux kernel source code. Be aware that any non-numeric comparison may be subject to time-of-check-time-of-use attacks and cannot be considered safe. .PP \fBexecve\fR may only be used when invoking with CAP_SYS_ADMIN privileges. .PP In order to promote reusability, policy files can include other policy files using the following syntax: .IP .EX \fB@include /absolute/path/to/file.policy\fR \fB@include ./path/relative/to/CWD/file.policy\fR .EE .PP Inclusion is limited to a single level (i.e. files that are \fB@include\fRd cannot themselves \fB@include\fR more files), since that makes the policies harder to understand. .SH SECCOMP_FILTER SYNTAX More formally, the expression after the colon can be an expression in Disjunctive Normal Form (DNF): a disjunction ("or", \fI||\fR) of conjunctions ("and", \fI&&\fR) of atoms. .SS "Atom Syntax" Atoms are of the form \fIarg{DNUM} {OP} {VAL}\fR where: .IP \(bu 3 \fIDNUM\fR is a decimal number .IP \(bu \fIOP\fR is an unsigned comparison operator: \fI==\fR, \fI!=\fR, \fI<\fR, \fI<=\fR, \fI>\fR, \fI>=\fR, \fI&\fR (flags set), or \fIin\fR (inclusion) .IP \(bu \fIVAL\fR is a constant expression. It can be a named constant (like \fBO_RDONLY\fR), a number (octal, decimal, or hexadecimal), a mask of constants separated by \fI|\fR, or a parenthesized constant expression. Constant expressions can also be prefixed with the bitwise complement operator \fI~\fR to produce their complement. .PP \fI==\fR, \fI!=\fR, \fI<\fR, \fI<=\fR, \fI>\fR, and \fI>=\fR should be pretty self explanatory. .PP \fI&\fR will test for a flag being set, for example, O_RDONLY for .BR open (2): .IP .EX open: arg1 & O_RDONLY .EE .PP Minijail supports most common named constants, like O_RDONLY. It's preferable to use named constants rather than numeric values as not all architectures use the same numeric value. .PP When the possible combinations of allowed flags grow, specifying them all can be cumbersome. This is where the \fIin\fR operator comes handy. The system call will be allowed iff the flags set in the argument are included (as a set) in the flags in the policy: .IP .EX mmap: arg3 in MAP_PRIVATE|MAP_ANONYMOUS .EE .PP This will allow \fBmmap\fR(2) as long as \fIarg3\fR (flags) has any combination of MAP_PRIVATE and MAP_ANONYMOUS, but nothing else. One common use of this is to restrict \fBmmap\fR(2) / \fBmprotect\fR(2) to only allow write^exec mappings: .IP .EX mmap: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE .EE .SS "Return Values" By default, blocked syscalls call the process to be killed. The \fIreturn {NUM}\fR syntax can be used to force a specific errno to be returned instead. .IP .EX read: return EBADF .EE .PP This expression will block the \fBread\fR(2) syscall, make it return -1, and set \fBerrno\fR to EBADF (9 on x86 platforms). .PP An expression can also include an optional \fIreturn \fR clause, separated by a semicolon: .IP .EX read: arg0 == 0; return EBADF .EE .PP This is, if the first argument to read is 0, then allow the syscall; else, block the syscall, return -1, and set \fBerrno\fR to EBADF. .SH SECCOMP_FILTER POLICY WRITING Determining policy for seccomp_filter can be time consuming. System calls are often named in arch-specific, or legacy tainted, ways. E.g., geteuid versus geteuid32. On process death due to a seccomp filter rule, the offending system call number will be supplied with a best guess of the ABI defined name. This information may be used to produce working baseline policies. However, if the process being contained has a fairly tight working domain, using \fBtools/generate_seccomp_policy.py\fR with the output of \fBstrace -f -e raw=all \fR can generate the list of system calls that are needed. Note that when using libminijail or minijail with preloading, supporting initial process setup calls will not be required. Be conservative. .PP It's also possible to analyze the binary checking for all non-dead functions and determining if any of them issue system calls. There is no active implementation for this, but something like code.google.com/p/seccompsandbox is one possible runtime variant. .SH CONFIGURATION FILE A configuration file can be used to specify command line options and other settings. .PP It supports the following syntax: .IP .EX \fB% minijail-config-file v0\fR \fB
Use alternate syscall table
.\n" " -s Use seccomp mode 1 (not the same as -S).\n" "\n" "Other options:\n" " --config \n" " Load the Minijail configuration file .\n" " If used, must be specified ahead of other options.\n" " --gen-config \n" " Convert the current flags to a config file, then exit.\n" " Only flags impacting the jailed process are included \n" " (this flag, --config, and help messages are not).\n" " This should be set first to avoid evaluating other flags, \n" " or set later to evaluate users and paths currently available\n" " (example: checking if -u is a valid user).\n" " Path must be specified.\n" " --profile

\n" " Configure minijail0 to run with the

sandboxing profile,\n" " which is a convenient way to express multiple flags\n" " that are typically used together.\n" " See the minijail0(1) man page for the full list.\n" " -n Set no_new_privs. See prctl(2) for details.\n" " -c Restrict caps to .\n" " --ambient Raise ambient capabilities. Requires -c.\n" " -B Skip setting securebits when restricting caps (-c).\n" " By default, SECURE_NOROOT, SECURE_NO_SETUID_FIXUP, and \n" " SECURE_KEEP_CAPS (with their respective locks) are set.\n" " -f Write the pid of the jailed process to .\n" " -i Exit immediately after fork(2); i.e. background the program.\n" " -z Don't forward signals to jailed process.\n" " -R \n" " Call setrlimit(3); can be specified multiple times.\n" " -T Assume is a ELF binary;\n" " may be 'static' or 'dynamic'.\n" " This will avoid accessing binary before execve(2).\n" " Type 'static' will avoid preload hooking.\n" " -w Create and join a new anonymous session keyring.\n" " --env-reset Clear the current environment instead of having \n" " inherit the active environment. Often used to start \n" " with a minimal sanitized environment.\n" " --env-add \n" " Sets the specified environment variable \n" " in the 's environment before starting it.\n" "\n" "Uncommon options:\n" " --allow-speculative-execution\n" " Allow speculative execution by disabling mitigations.\n" " --enable-profile-fs-restrictions\n" " Limit paths available when using minimalistic-mountns.\n" " --fs-default-paths\n" " Adds a set of allowed paths to allow running common system \n" " executables.\n" " --fs-path-rx\n" " Adds an allowed read-execute path.\n" " --fs-path-ro\n" " Adds an allowed read-only path.\n" " --fs-path-rw\n" " Adds an allowed read-write path.\n" " --fs-path-advanced-rw\n" " Adds an allowed advanced read-write path.\n" " --landlock-abi \n" " Set the Landlock ABI version to target.\n" " Defaults to 2 to prevent implicit behavior changes\n" " when running on newer kernels. Set to 3 or 5 to\n" " explicitly enable newer restrictions.\n" " --no-fs-restrictions\n" " Disables path-based filesystem restrictions.\n" " --no-default-runtime-environment\n" " Disables default seccomp policy and setting of no_new_privs.\n" " --no-new-sessions\n" " Skips having Minijail call setsid(). This is useful when\n" " running a process that expects to have a controlling\n" " terminal set.\n" " --preserve-fd\n" " Preserves an fd and makes it available in the child process.\n" " The fd is preserved with the same integer value.\n" " --preload-library=\n" " Overrides the path to \"" PRELOADPATH "\".\n" " This is only really useful for local testing.\n" " --logging=\n" " Set the logging system output: 'auto' (default),\n" " 'syslog', or 'stderr'.\n" " -h Help (this message).\n" " -H Seccomp filter help message.\n"; /* clang-format on */ static void usage(const char *progn) { printf("Usage: %s [options] [--] [args...]\n\n%s", progn, help_text); printf("\nsyscalls allowed when logging (-L):\n "); for (size_t i = 0; i < log_syscalls_len; ++i) printf(" %s", log_syscalls[i]); printf("\n"); } static void seccomp_filter_usage(const char *progn) { const struct syscall_entry *entry = syscall_table; printf("Usage: %s -S [args...]\n\n" "System call names supported:\n", progn); for (; entry->name && entry->nr >= 0; ++entry) printf(" %s [%d]\n", entry->name, entry->nr); printf("\nSee minijail0(5) for example policies.\n"); } /* * Return the next unconsumed option char/value parsed from * |*conf_entry_list|. |optarg| is updated to point to an argument from * the entry value. If all options have been consumed, |*conf_entry_list| * will be freed and -1 will be returned. */ static int getopt_from_conf(const struct option *longopts, struct config_entry_list **conf_entry_list, size_t *conf_index) { int opt = -1; /* If we've consumed all the options in the this config, reset it. */ if (*conf_index >= (*conf_entry_list)->num_entries) { free_config_entry_list(*conf_entry_list); *conf_entry_list = NULL; *conf_index = 0; return opt; } struct config_entry *entry = &(*conf_entry_list)->entries[*conf_index]; /* Look up a matching long option. */ size_t i = 0; const struct option *curr_opt; bool long_option_found = false; for (curr_opt = &longopts[0]; curr_opt->name != NULL; curr_opt = &longopts[++i]) if (streq(entry->key, curr_opt->name)) { long_option_found = true; opt = curr_opt->val; break; } /* Look up matching short option. */ if (!long_option_found && strlen(entry->key) == 1 && isalpha(*entry->key) && strchr(optstring, *entry->key) != NULL) { opt = *entry->key; } else if (curr_opt->name == NULL) { errx(1, "Unable to recognize '%s' as Minijail conf entry key, " "please refer to minijail0(5) for syntax and examples.", entry->key); } optarg = (char *)entry->value; (*conf_index)++; return opt; } /* * Similar to getopt(3), return the next option char/value as it * parses through the CLI argument list. Config entries in * |*conf_entry_list| will be parsed with precedence over CLI options. * Same as getopt(3), |optarg| is pointing to the option argument. */ static int getopt_conf_or_cli(int argc, char *const argv[], struct config_entry_list **conf_entry_list, size_t *conf_index) { int opt = -1; if (*conf_entry_list != NULL) opt = getopt_from_conf(long_options, conf_entry_list, conf_index); if (opt == -1) opt = getopt_long(argc, argv, optstring, long_options, NULL); return opt; } static char *getname_from_opt(int opt) { unsigned int i; const struct option *entry = long_options; for (i = 0; i < ARRAY_SIZE(long_options); i++) { if (opt == entry->val) { return xstrdup(entry->name); } entry++; } return NULL; } static void free_options_list(struct option_entry *opt_entry_head) { while (opt_entry_head) { struct option_entry *entry = opt_entry_head; opt_entry_head = opt_entry_head->next; free(entry->name); free(entry->args); free(entry); } } static void set_child_env(char ***envp, char *arg, char *const environ[]) { /* We expect VAR=value format for arg. */ char *delim = strchr(arg, '='); if (!delim) { errx(1, "Expected an argument of the " "form VAR=value (got '%s')", arg); } *delim = '\0'; const char *env_value = delim + 1; if (!*envp) { /* * We got our first --env-add. Initialize *envp by * copying our current env to the future child env. */ *envp = minijail_copy_env(environ); if (!*envp) err(1, "Failed to allocate memory."); } if (minijail_setenv(envp, arg, env_value, 1)) err(1, "minijail_setenv() failed."); } int parse_args(struct minijail *j, int argc, char *const argv[], char *const environ[], bool *exit_immediately, ElfType *elftype, const char **preload_path, char ***envp) { enum seccomp_type { None, Strict, Filter, BpfBinaryFilter }; enum seccomp_type seccomp = None; int opt; bool use_seccomp_filter = false; bool use_seccomp_filter_binary = false; bool use_seccomp_log = false; bool forward_signals = true; bool have_bind_mounts = false; bool chroot = false; bool pivot_root = false; bool use_mount_ns = false; bool change_remount = false; char *remount_mode = NULL; bool inherit_suppl_gids = false; bool keep_suppl_gids = false; bool caps = false; bool use_ambient_caps = false; bool use_uid = false; bool use_gid = false; uid_t uid = 0; gid_t gid = 0; gid_t *suppl_gids = NULL; size_t suppl_gids_count = 0; char *uidmap = NULL, *gidmap = NULL; bool set_uidmap = false; bool set_gidmap = false; size_t tmp_size = 0; attribute_cleanup_str char *filter_path = NULL; int log_to_stderr = -1; struct config_entry_list *conf_entry_list = NULL; size_t conf_index = 0; bool parse_mode = false; struct option_entry *opt_entry_head = NULL; struct option_entry *opt_entry_tail = NULL; char *config_path = NULL; bool fs_path_flag_used = false; bool fs_path_rules_enabled = true; while ((opt = getopt_conf_or_cli(argc, argv, &conf_entry_list, &conf_index)) != -1) { /* Track options for conf file generation. */ struct option_entry *opt_entry = calloc(1, sizeof(*opt_entry)); char *opt_name = getname_from_opt(opt); if (opt_name != NULL) { opt_entry->name = opt_name; } else { char str[2] = {opt, '\0'}; opt_entry->name = xstrdup(str); } if (optarg != NULL) { opt_entry->args = xstrdup(optarg); } if (opt_entry_head) { opt_entry_tail->next = opt_entry; } else { opt_entry_head = opt_entry; } opt_entry_tail = opt_entry; switch (opt) { case 'u': if (use_uid) errx(1, "-u provided multiple times."); use_uid = true; if (!parse_mode) set_user(j, optarg, &uid, &gid); break; case 'g': if (use_gid) errx(1, "-g provided multiple times."); use_gid = true; if (!parse_mode) set_group(j, optarg, &gid); break; case 'n': minijail_no_new_privs(j); break; case 's': if (seccomp != None && seccomp != Strict) { errx(1, "Do not use -s, -S, or " "--seccomp-bpf-binary together"); } seccomp = Strict; minijail_use_seccomp(j); break; case 'S': if (seccomp != None && seccomp != Filter) { errx(1, "Do not use -s, -S, or " "--seccomp-bpf-binary together"); } seccomp = Filter; minijail_use_seccomp_filter(j); free(filter_path); filter_path = xstrdup(optarg); use_seccomp_filter = true; break; case 'l': minijail_namespace_ipc(j); break; case 'L': if (seccomp == BpfBinaryFilter) { errx(1, "-L does not work with " "--seccomp-bpf-binary"); } use_seccomp_log = true; minijail_log_seccomp_filter_failures(j); break; case 'b': if (!parse_mode) add_binding(j, optarg); have_bind_mounts = true; break; case 'B': skip_securebits(j, optarg); break; case 'c': caps = true; use_caps(j, optarg); break; case 'C': use_chroot(j, optarg, &chroot, pivot_root); break; case 'k': if (!parse_mode) add_mount(j, optarg); break; case 'K': free(remount_mode); remount_mode = optarg == NULL ? NULL : xstrdup(optarg); change_remount = true; break; case 'P': use_pivot_root(j, optarg, &pivot_root, chroot); break; case 'f': if (!parse_mode && 0 != minijail_write_pid_file(j, optarg)) errx(1, "Could not prepare pid file path"); break; case 't': minijail_namespace_vfs(j); if (!tmp_size) { /* * Avoid clobbering |tmp_size| if it was already * set. */ tmp_size = DEFAULT_TMP_SIZE; } if (optarg) { uint64_t parsed_size; if (parse_size(&parsed_size, optarg)) errx(1, "Invalid /tmp tmpfs size"); if (parsed_size > SIZE_MAX) errx(1, "/tmp tmpfs size too large"); tmp_size = parsed_size; } break; case 'v': minijail_namespace_vfs(j); /* * Set the default mount propagation in the command-line * tool to MS_SLAVE. * * When executing the sandboxed program in a new mount * namespace the Minijail library will by default * remount all mounts with the MS_PRIVATE flag. While * this is an appropriate, safe default for the library, * MS_PRIVATE can be problematic: unmount events will * not propagate into mountpoints marked as MS_PRIVATE. * This means that if a mount is unmounted in the root * mount namespace, it will not be unmounted in the * non-root mount namespace. * This in turn can be problematic because activity in * the non-root mount namespace can now directly * influence the root mount namespace (e.g. preventing * re-mounts of said mount), which would be a privilege * inversion. * * Setting the default in the command-line to MS_SLAVE * will still prevent mounts from leaking out of the * non-root mount namespace but avoid these * privilege-inversion issues. * For cases where mounts should not flow *into* the * namespace either, the user can pass -Kprivate. * Note that mounts are marked as MS_PRIVATE by default * by the kernel, so unless the init process (like * systemd) or something else marks them as shared, this * won't do anything. */ minijail_remount_mode(j, MS_SLAVE); use_mount_ns = true; break; case 'V': if (!parse_mode) minijail_namespace_enter_vfs(j, optarg); break; case 'r': minijail_remount_proc_readonly(j); break; case 'G': if (keep_suppl_gids) errx(1, "-y and -G are not compatible"); minijail_inherit_usergroups(j); inherit_suppl_gids = true; break; case 'y': if (inherit_suppl_gids) errx(1, "-y and -G are not compatible"); minijail_keep_supplementary_gids(j); keep_suppl_gids = true; break; case 'N': minijail_namespace_cgroups(j); break; case 'p': minijail_namespace_pids(j); break; case 'e': if (!parse_mode) { if (optarg) minijail_namespace_enter_net(j, optarg); else minijail_namespace_net(j); } break; case 'i': *exit_immediately = true; break; case 'H': seccomp_filter_usage(argv[0]); exit(0); case 'I': minijail_namespace_pids(j); minijail_run_as_init(j); break; case 'U': minijail_namespace_user(j); minijail_namespace_pids(j); break; case 'm': set_uidmap = true; if (uidmap) { free(uidmap); uidmap = NULL; } if (optarg) uidmap = xstrdup(optarg); break; case 'M': set_gidmap = true; if (gidmap) { free(gidmap); gidmap = NULL; } if (optarg) gidmap = xstrdup(optarg); break; case 'a': if (0 != minijail_use_alt_syscall(j, optarg)) errx(1, "Could not set alt-syscall table"); break; case 'R': add_rlimit(j, optarg); break; case 'T': if (streq(optarg, "static")) *elftype = ELFSTATIC; else if (streq(optarg, "dynamic")) *elftype = ELFDYNAMIC; else { errx(1, "ELF type must be 'static' or " "'dynamic'"); } break; case 'w': minijail_new_session_keyring(j); break; case 'Y': minijail_set_seccomp_filter_tsync(j); break; case 'z': forward_signals = false; break; case 'd': minijail_namespace_vfs(j); minijail_mount_dev(j); break; /* Long options. */ case OPT_AMBIENT: use_ambient_caps = true; minijail_set_ambient_caps(j); break; case OPT_UTS: minijail_namespace_uts(j); if (optarg) minijail_namespace_set_hostname(j, optarg); break; case OPT_LOGGING: if (streq(optarg, "auto")) log_to_stderr = -1; else if (streq(optarg, "syslog")) log_to_stderr = 0; else if (streq(optarg, "stderr")) log_to_stderr = 1; else errx(1, "--logger must be 'syslog' or 'stderr'"); break; case OPT_LANDLOCK_ABI: { char *end; long abi = strtol(optarg, &end, 10); if (*end != '\0' || abi < 0) { errx(1, "--landlock-abi must be a positive " "integer"); } minijail_set_landlock_abi(j, abi); break; } case OPT_PROFILE: use_profile(j, optarg, &pivot_root, chroot, &tmp_size); break; case OPT_PRELOAD_LIBRARY: *preload_path = optarg == NULL ? NULL : xstrdup(optarg); break; case OPT_ENABLE_PROFILE_FS_RESTRICTIONS: minijail_set_enable_profile_fs_restrictions(j); break; case OPT_FS_DEFAULT_PATHS: minijail_enable_default_fs_restrictions(j); fs_path_flag_used = true; break; case OPT_FS_PATH_RX: minijail_add_fs_restriction_rx(j, optarg); fs_path_flag_used = true; break; case OPT_FS_PATH_RO: minijail_add_fs_restriction_ro(j, optarg); fs_path_flag_used = true; break; case OPT_FS_PATH_RW: minijail_add_fs_restriction_rw(j, optarg); fs_path_flag_used = true; break; case OPT_FS_PATH_ADVANCED_RW: minijail_add_fs_restriction_advanced_rw(j, optarg); fs_path_flag_used = true; break; case OPT_NO_FS_RESTRICTIONS: minijail_disable_fs_restrictions(j); fs_path_rules_enabled = false; break; case OPT_NO_DEFAULT_RUNTIME: minijail_set_enable_default_runtime(j, false); break; case OPT_NO_NEW_SESSIONS: minijail_set_enable_new_sessions(j, false); break; case OPT_PRESERVE_FD: { char *fd_end; int fd_to_preserve = strtol(optarg, &fd_end, 10); if (*fd_end != '\0') { errx(1, "--preserve-fd must be an integer"); } minijail_preserve_fd(j, fd_to_preserve, fd_to_preserve); break; } case OPT_SECCOMP_BPF_BINARY: if (seccomp != None && seccomp != BpfBinaryFilter) { errx(1, "Do not use -s, -S, or " "--seccomp-bpf-binary together"); } if (use_seccomp_log) errx(1, "-L does not work with " "--seccomp-bpf-binary"); seccomp = BpfBinaryFilter; minijail_use_seccomp_filter(j); free(filter_path); filter_path = xstrdup(optarg); use_seccomp_filter_binary = true; break; case OPT_ADD_SUPPL_GROUP: suppl_group_add(&suppl_gids_count, &suppl_gids, optarg); break; case OPT_ALLOW_SPECULATIVE_EXECUTION: minijail_set_seccomp_filter_allow_speculation(j); break; case OPT_CONFIG: { if (conf_entry_list != NULL) { errx(1, "Nested config file specification is " "not allowed."); } conf_entry_list = new_config_entry_list(); conf_index = 0; #if defined(BLOCK_NOEXEC_CONF) /* * Check the conf file is in a exec mount. * With a W^X invariant, it excludes writable * mounts. */ struct statfs conf_statfs; if (statfs(optarg, &conf_statfs) != 0) err(1, "statfs(%s) failed.", optarg); if ((conf_statfs.f_flags & MS_NOEXEC) != 0) errx(1, "Conf file must be in a exec " "mount: %s", optarg); #endif #if defined(ENFORCE_ROOTFS_CONF) /* Make sure the conf file is in the same device as the * rootfs. */ struct stat root_stat; struct stat conf_stat; if (stat("/", &root_stat) != 0) err(1, "stat(/) failed."); if (stat(optarg, &conf_stat) != 0) err(1, "stat(%s) failed.", optarg); if (root_stat.st_dev != conf_stat.st_dev) errx(1, "Conf file must be in the rootfs."); #endif attribute_cleanup_fp FILE *config_file = fopen(optarg, "re"); if (!config_file) err(1, "Failed to open %s", optarg); if (!parse_config_file(config_file, conf_entry_list)) { errx( 1, "Unable to parse %s as Minijail conf file, " "please refer to minijail0(5) for syntax " "and examples.", optarg); } break; } case OPT_GEN_CONFIG: parse_mode = true; free(config_path); config_path = xstrdup(optarg); break; case OPT_ENV_ADD: /* * We either copy our current env to the child env * then add the requested envvar to it, or just * add the requested envvar to the already existing * envp. */ set_child_env(envp, optarg, environ); break; case OPT_ENV_RESET: if (*envp && *envp != environ) { /* * We already started to initialize the future * child env, because we got some --env-add * earlier on the command-line, so first, * free the memory we allocated. * If |*envp| happens to point to |environ|, * don't attempt to free it. */ minijail_free_env(*envp); } /* Allocate an empty environment for the child. */ *envp = calloc(1, sizeof(char *)); if (!*envp) err(1, "Failed to allocate memory."); break; default: usage(argv[0]); exit(opt == 'h' ? 0 : 1); } } if (log_to_stderr == -1) { /* Autodetect default logging output. */ log_to_stderr = isatty(STDIN_FILENO) ? 1 : 0; } if (log_to_stderr) { init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO); /* * When logging to stderr, ensure the FD survives the jailing. */ if (0 != minijail_preserve_fd(j, STDERR_FILENO, STDERR_FILENO)) { errx(1, "Could not preserve stderr"); } } if (fs_path_flag_used && !fs_path_rules_enabled) { errx(1, "Can't combine --no-fs-restrictions " "with directly using fs path flags"); } /* Handle config file generation. */ if (parse_mode) { const struct option_entry *r = opt_entry_head; if (access(config_path, F_OK) == 0) { errx(1, "'%s' exists. Specify a new filename.", config_path); } attribute_cleanup_fp FILE *fp = fopen(config_path, "w"); if (fp == NULL) { err(1, "'%s' not writable. Specify a new filename.", config_path); } fprintf(fp, "%% minijail-config-file v0\n\n"); while (r != NULL) { /* Add all flags except --config and --gen-config. */ if (!streq(r->name, config_flag_name) && !streq(r->name, gen_config_flag_name)) { if (r->args == NULL) { fprintf(fp, "%s\n", r->name); } else { fprintf(fp, "%s = %s\n", r->name, r->args); } } r = r->next; } exit(0); } free_options_list(opt_entry_head); opt_entry_head = NULL; opt_entry_tail = NULL; /* Set up uid/gid mapping. */ if (set_uidmap || set_gidmap) { set_ugid_mapping(j, set_uidmap, uid, uidmap, set_gidmap, gid, gidmap); } /* Can only set ambient caps when using regular caps. */ if (use_ambient_caps && !caps) { errx(1, "Can't set ambient capabilities (--ambient) " "without actually using capabilities (-c)"); } /* Set up signal handlers in minijail unless asked not to. */ if (forward_signals) minijail_forward_signals(j); /* * Only allow bind mounts when entering a chroot, using pivot_root, or * a new mount namespace. */ if (have_bind_mounts && !(chroot || pivot_root || use_mount_ns)) { errx(1, "Bind mounts require a chroot, pivot_root, or " " new mount namespace"); } /* * / is only remounted when entering a new mount namespace, so unless * that's set there is no need for the -K/-K flags. */ if (change_remount && !use_mount_ns) { errx(1, "No need to use -K (skip remounting '/') or " "-K (remount '/' as ) " "without -v (new mount namespace).\n" "Do you need to add '-v' explicitly?"); } /* Configure the remount flag here to avoid having -v override it. */ if (change_remount) { if (remount_mode != NULL) { set_remount_mode(j, remount_mode); } else { minijail_skip_remount_private(j); } } /* * Proceed in setting the supplementary gids specified on the * cmdline options. */ if (suppl_gids_count) { minijail_set_supplementary_gids(j, suppl_gids_count, suppl_gids); free(suppl_gids); } /* * We parse seccomp filters here to make sure we've collected all * cmdline options. */ if (use_seccomp_filter) { minijail_parse_seccomp_filters(j, filter_path); } else if (use_seccomp_filter_binary) { set_seccomp_filters(j, filter_path); } else if (minijail_get_enable_default_runtime(j)) { if (access(default_policy_path, F_OK) == 0) { /* TODO(b/254506006): support more flags for runtime * options. */ minijail_use_seccomp_filter(j); set_seccomp_filters(j, default_policy_path); } /* Set no_new_privs in addition to the seccomp policy. */ minijail_no_new_privs(j); } /* Mount a tmpfs under /tmp and set its size. */ if (tmp_size) minijail_mount_tmp_size(j, tmp_size); /* Add Landlock rules for each processed mount arg. */ minijail_add_minimalistic_mountns_fs_rules(j); /* * Copy our current env to the child if its |*envp| has not * already been initialized from --env-(reset|add) usage. */ if (!*envp) { *envp = minijail_copy_env(environ); if (!*envp) err(1, "Failed to allocate memory."); } /* * There should be at least one additional unparsed argument: the * executable name. */ if (argc == optind) { usage(argv[0]); exit(1); } if (*elftype == ELFERROR) { /* * -T was not specified. * Get the path to the program adjusted for changing root. */ char *program_path = minijail_get_original_path(j, argv[optind]); /* Check that we can access the target program. */ if (access(program_path, X_OK)) { errx(1, "Target program '%s' is not accessible", argv[optind]); } /* Check if target is statically or dynamically linked. */ *elftype = get_elf_linkage(program_path); free(program_path); } if (log_to_stderr && *elftype != ELFSTATIC) { if (minijail_setenv(envp, kLoggingEnvVar, kLoggingEnvValueStderr, 1)) err(1, "minijail_setenv() failed."); } /* * Setting capabilities need either a dynamically-linked binary, or the * use of ambient capabilities for them to be able to survive an * execve(2). */ if (caps && *elftype == ELFSTATIC && !use_ambient_caps) { errx(1, "Can't run statically-linked binaries with capabilities" " (-c) without also setting ambient capabilities. " "Try passing --ambient."); } return optind; } minijail-2026.05.18+git20261016.8787d1a/minijail0_cli.h000066400000000000000000000012321526451555500212050ustar00rootroot00000000000000/* Copyright 2018 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Helpers for the minijail0 program. Split out for unittesting. */ #ifndef MINIJAIL_MINIJAIL0_CLI_H_ #define MINIJAIL_MINIJAIL0_CLI_H_ #include #include "elfparse.h" #ifdef __cplusplus extern "C" { #endif struct minijail; int parse_args(struct minijail *j, int argc, char *const argv[], char *const environ[], bool *exit_immediately, ElfType *elftype, const char **preload_path, char ***envp); #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* MINIJAIL_MINIJAIL0_CLI_H_ */ minijail-2026.05.18+git20261016.8787d1a/minijail0_cli_unittest.cc000066400000000000000000000570161526451555500233150ustar00rootroot00000000000000/* Copyright 2018 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test the minijail0 CLI using gtest. * * Note: We don't verify that the minijail struct was set correctly from these * flags as only libminijail.c knows that definition. If we wanted to improve * this test, we'd have to pull that struct into a common (internal) header. */ #include #include #include #include #include #include #include "config_parser.h" #include "libminijail-private.h" #include "libminijail.h" #include "minijail0_cli.h" #include "test_util.h" #include "util.h" extern "C" char** environ; namespace { constexpr char kValidUser[] = "nobody"; constexpr char kValidUid[] = "100"; constexpr char kValidGroup[] = "users"; constexpr char kValidGid[] = "100"; bool GetEnvValue(const std::vector& env, const std::string& key, std::string* value_out = nullptr) { const std::string prefix = key + "="; for (const auto& entry : env) { if (entry.rfind(prefix, 0) == 0) { if (value_out) *value_out = entry.substr(prefix.size()); return true; } } return false; } class CliTest : public ::testing::Test { protected: virtual void SetUp() { // Most tests do not care about this logic. For the few that do, make // them opt into it so they can validate specifically. elftype_ = ELFDYNAMIC; } virtual void TearDown() {} // We use a vector of strings rather than const char * pointers because we // need the backing memory to be writable. The CLI might mutate the strings // as it parses things (which is normally permissible with argv). int parse_args_(const std::vector& argv, bool* exit_immediately, ElfType* elftype, std::vector* child_env = nullptr) { // Make sure we reset the getopts state when scanning a new argv. Setting // this to 0 is a GNU extension, but AOSP/BSD also checks this (as an alias // to their "optreset"). optind = 0; // We create & destroy this for every parse_args call because some API // calls can dupe memory which confuses LSAN. https://crbug.com/844615 struct minijail* j = minijail_new(); std::vector pargv; pargv.push_back("minijail0"); for (const std::string& arg : argv) pargv.push_back(arg.c_str()); // We grab stdout from parse_args itself as it might dump things we don't // usually care about like help output. testing::internal::CaptureStdout(); const char* preload_path = PRELOADPATH; const char* default_preload_path = preload_path; char** envp = NULL; int ret = parse_args(j, pargv.size(), const_cast(pargv.data()), NULL, exit_immediately, elftype, &preload_path, &envp); testing::internal::GetCapturedStdout(); if (child_env) { child_env->clear(); if (envp) { for (char** entry = envp; *entry; ++entry) child_env->emplace_back(*entry); } } if (envp && envp != environ) minijail_free_env(envp); if (preload_path && preload_path != default_preload_path) free(const_cast(preload_path)); minijail_destroy(j); return ret; } int parse_args_(const std::vector& argv, std::vector* child_env) { return parse_args_(argv, &exit_immediately_, &elftype_, child_env); } int parse_args_(const std::vector& argv) { return parse_args_(argv, &exit_immediately_, &elftype_, nullptr); } ElfType elftype_; bool exit_immediately_; }; } // namespace // Should exit non-zero when there's no arguments. TEST_F(CliTest, no_args) { std::vector argv = {}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Should exit zero when we asked for help. TEST_F(CliTest, help) { std::vector argv = {"-h"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), ""); argv = {"--help"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), ""); argv = {"-H"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), ""); } // Just a simple program to run. TEST_F(CliTest, valid_program) { std::vector argv = {"/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } // Valid calls to the change user option. TEST_F(CliTest, valid_set_user) { std::vector argv = {"-u", "", "/bin/sh"}; argv[1] = kValidUser; ASSERT_TRUE(parse_args_(argv)); argv[1] = kValidUid; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the change user option. TEST_F(CliTest, invalid_set_user) { std::vector argv = {"-u", "", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[1] = "1000x"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Supplying -u more than once is bad. argv = {"-u", kValidUser, "-u", kValidUid, "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "-u provided multiple times"); } // Valid calls to the change group option. TEST_F(CliTest, valid_set_group) { std::vector argv = {"-g", "", "/bin/sh"}; argv[1] = kValidGroup; ASSERT_TRUE(parse_args_(argv)); argv[1] = kValidGid; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the change group option. TEST_F(CliTest, invalid_set_group) { std::vector argv = {"-g", "", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[1] = "1000x"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Supplying -g more than once is bad. argv = {"-g", kValidGroup, "-g", kValidGid, "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "-g provided multiple times"); } // Valid calls to the add-suppl-group option. TEST_F(CliTest, valid_add_supp_group) { std::vector argv = {"--add-suppl-group", "", "/bin/sh"}; argv[1] = kValidGroup; ASSERT_TRUE(parse_args_(argv)); argv[1] = kValidGid; ASSERT_TRUE(parse_args_(argv)); std::vector argv2 = {"--add-suppl-group", "", "--add-suppl-group", "", "/bin/sh"}; argv[1] = kValidGroup; argv[2] = kValidGid; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the add-suppl-group option. TEST_F(CliTest, invalid_add_supp_group) { std::vector argv = {"--add-suppl-group", "", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[1] = "1000x"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the skip securebits option. TEST_F(CliTest, valid_skip_securebits) { // An empty string is the same as 0. std::vector argv = {"-B", "", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); argv[1] = "0xAB"; ASSERT_TRUE(parse_args_(argv)); argv[1] = "1234"; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the skip securebits option. TEST_F(CliTest, invalid_skip_securebits) { std::vector argv = {"-B", "", "/bin/sh"}; argv[1] = "xja"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the caps option. TEST_F(CliTest, valid_caps) { // An empty string is the same as 0. std::vector argv = {"-c", "", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); argv[1] = "0xAB"; ASSERT_TRUE(parse_args_(argv)); argv[1] = "1234"; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the caps option. TEST_F(CliTest, invalid_caps) { std::vector argv = {"-c", "", "/bin/sh"}; argv[1] = "xja"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the logging option. TEST_F(CliTest, valid_logging) { std::vector argv = {"--logging", "", "/bin/sh"}; // This should list all valid logging targets. const std::vector profiles = { "stderr", "syslog", }; for (const auto& profile : profiles) { argv[1] = profile; ASSERT_TRUE(parse_args_(argv)); } } // Invalid calls to the logging option. TEST_F(CliTest, invalid_logging) { std::vector argv = {"--logging", "", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[1] = "stdout"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the rlimit option. TEST_F(CliTest, valid_rlimit) { std::vector argv = {"-R", "", "/bin/sh"}; argv[1] = "0,1,2"; ASSERT_TRUE(parse_args_(argv)); argv[1] = "0,0x100,4"; ASSERT_TRUE(parse_args_(argv)); argv[1] = "1,1,unlimited"; ASSERT_TRUE(parse_args_(argv)); argv[1] = "2,unlimited,2"; ASSERT_TRUE(parse_args_(argv)); argv[1] = "RLIMIT_AS,unlimited,unlimited"; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the rlimit option. TEST_F(CliTest, invalid_rlimit) { std::vector argv = {"-R", "", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Missing cur & max. argv[1] = "0"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Missing max. argv[1] = "0,0"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Too many options. argv[1] = "0,0,0,0"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Non-numeric limits argv[1] = "0,0,invalid-limit"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Invalid number. argv[1] = "0,0,0j"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Invalid hex number. argv[1] = "0,0x1jf,0"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Invalid rlimit constant. argv[1] = "RLIMIT_GOGOOGOG,0,0"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the profile option. TEST_F(CliTest, valid_profile) { std::vector argv = {"--profile", "", "/bin/sh"}; // This should list all valid profiles. const std::vector profiles = { "minimalistic-mountns", "minimalistic-mountns-nodev", }; for (const auto& profile : profiles) { argv[1] = profile; ASSERT_TRUE(parse_args_(argv)); } } // Invalid calls to the profile option. TEST_F(CliTest, invalid_profile) { std::vector argv = {"--profile", "", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[1] = "random-unknown-profile"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid usage of the no-fs-restrictions option. TEST_F(CliTest, valid_no_fs_restrictions) { std::vector argv = {"--profile", "minimalistic-mountns", "--no-fs-restrictions", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } // Invalid usage of the no-fs-restrictions option. TEST_F(CliTest, invalid_no_fs_restrictions) { // Using an fs-path-* flag at the same time shouldn't be allowed. std::vector argv = {"--fs-path-rx", "/", "--no-fs-restrictions", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the chroot option. TEST_F(CliTest, valid_chroot) { std::vector argv = {"-C", "/", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } // Valid calls to the pivot root option. TEST_F(CliTest, valid_pivot_root) { std::vector argv = {"-P", "/", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } // We cannot handle multiple options with chroot/profile/pivot root. TEST_F(CliTest, conflicting_roots) { std::vector argv; // Chroot & pivot root. argv = {"-C", "/", "-P", "/", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Chroot & minimalistic-mountns profile. argv = {"-C", "/", "--profile", "minimalistic-mountns", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Pivot root & minimalistic-mountns profile. argv = {"-P", "/", "--profile", "minimalistic-mountns", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the uidmap option. TEST_F(CliTest, valid_uidmap) { std::vector argv = {"-m", "/bin/sh"}; // Use a default map (no option from user). ASSERT_TRUE(parse_args_(argv)); // Use a single map. argv = {"-m0 0 1", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); // Multiple maps. argv = {"-m0 0 1,100 100 1", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } // Valid calls to the gidmap option. TEST_F(CliTest, valid_gidmap) { std::vector argv = {"-M", "/bin/sh"}; // Use a default map (no option from user). ASSERT_TRUE(parse_args_(argv)); // Use a single map. argv = {"-M0 0 1", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); // Multiple maps. argv = {"-M0 0 1,100 100 1", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the uidmap/gidmap options. // Note: Can't really test these as all validation is delayed/left to the // runtime kernel. Minijail will simply write verbatim what the user gave // it to the corresponding /proc/.../[ug]id_map. // Valid calls to the binding option. TEST_F(CliTest, valid_binding) { std::vector argv = {"-v", "-b", "", "/bin/sh"}; // Dest & writable are optional. argv[1] = "/"; ASSERT_TRUE(parse_args_(argv)); // Writable is optional. argv[1] = "/,/"; ASSERT_TRUE(parse_args_(argv)); // Writable is an integer. argv[1] = "/,/,0"; ASSERT_TRUE(parse_args_(argv)); argv[1] = "/,/,1"; ASSERT_TRUE(parse_args_(argv)); // Dest is optional. argv[1] = "/,,0"; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the binding option. TEST_F(CliTest, invalid_binding) { std::vector argv = {"-v", "-b", "", "/bin/sh"}; // Missing source. argv[2] = ""; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Too many args. argv[2] = "/,/,0,what"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Missing mount namespace/etc... argv = {"-b", "/", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Bad value for . argv = {"-b", "/,,writable", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the mount option. TEST_F(CliTest, valid_mount) { std::vector argv = {"-v", "-k", "", "/bin/sh"}; // Flags & data are optional. argv[2] = "none,/,none"; ASSERT_TRUE(parse_args_(argv)); // Data is optional. argv[2] = "none,/,none,0xe"; ASSERT_TRUE(parse_args_(argv)); // Flags are optional. argv[2] = "none,/,none,,mode=755"; ASSERT_TRUE(parse_args_(argv)); // Multiple data options to the kernel. argv[2] = "none,/,none,0xe,mode=755,uid=0,gid=10"; ASSERT_TRUE(parse_args_(argv)); // Single MS constant. argv[2] = "none,/,none,MS_NODEV,mode=755"; ASSERT_TRUE(parse_args_(argv)); // Multiple MS constants. argv[2] = "none,/,none,MS_NODEV|MS_NOEXEC,mode=755"; ASSERT_TRUE(parse_args_(argv)); // Mixed constant & number. argv[2] = "none,/,none,MS_NODEV|0xf,mode=755"; ASSERT_TRUE(parse_args_(argv)); } // Invalid calls to the mount option. TEST_F(CliTest, invalid_mount) { std::vector argv = {"-v", "-k", "", "/bin/sh"}; // Missing source. argv[2] = ""; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Missing dest. argv[2] = "none"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Missing type. argv[2] = "none,/"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Unknown MS constant. argv[2] = "none,/,none,MS_WHOOPS"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Extra arguments for bind mount (MS_BIND). argv[2] = "none,/,none,MS_BIND,extra"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); // Extra arguments for bind mount (MS_BIND) with empty data. argv[2] = "none,/,none,MS_BIND,"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the remount mode option. TEST_F(CliTest, valid_remount_mode) { std::vector argv = {"-v", "", "/bin/sh"}; // Mode is optional. argv[1] = "-K"; ASSERT_TRUE(parse_args_(argv)); // This should list all valid modes. const std::vector modes = { "shared", "private", "slave", "unbindable", }; for (const auto& mode : modes) { argv[1] = "-K" + mode; ASSERT_TRUE(parse_args_(argv)); } } // Invalid calls to the remount mode option. TEST_F(CliTest, invalid_remount_mode) { std::vector argv = {"-v", "", "/bin/sh"}; // Unknown mode. argv[1] = "-Kfoo"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } TEST_F(CliTest, invalid_L_combo) { std::vector argv = {"", "", "", "/bin/sh"}; // Cannot call minijail0 with -L and a pre-compiled seccomp policy. argv[0] = "-L"; argv[1] = "--seccomp-bpf-binary"; argv[2] = "source"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); argv[0] = "--seccomp-bpf-binary"; argv[1] = "source"; argv[2] = "-L"; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the clear env option. TEST_F(CliTest, valid_clear_env) { std::vector argv = {"--env-reset", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } // Valid calls to the set env option. TEST_F(CliTest, valid_set_env) { std::vector argv1 = {"--env-add", "NAME=value", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv1)); // multiple occurences are allowed. std::vector argv2 = {"--env-add", "A=b", "--env-add", "b=C=D", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv2)); // --env-reset before any --env-add to not pass our own env. std::vector argv3 = {"--env-reset", "--env-add", "A=b", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv3)); // --env-add before an --env-reset doesn't have any effect, but is allowed. std::vector argv4 = {"--env-add", "A=b", "--env-reset", "/bin/sh"}; ASSERT_TRUE(parse_args_(argv4)); } // Invalid calls to the set env options. TEST_F(CliTest, invalid_set_env) { // invalid env=value arguments. std::vector argv2 = {"--env-add", "", "/bin/sh"}; argv2[1] = "INVALID"; ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), ""); argv2[1] = "="; ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), ""); argv2[1] = "=foo"; ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), ""); } TEST_F(CliTest, logging_stderr_sets_env_for_dynamic_targets) { std::vector argv = {"--logging=stderr", "/bin/sh"}; ElfType elftype = ELFDYNAMIC; bool exit_immediately = false; std::vector child_env; ASSERT_TRUE(parse_args_(argv, &exit_immediately, &elftype, &child_env)); std::string value; ASSERT_TRUE(GetEnvValue(child_env, kLoggingEnvVar, &value)); EXPECT_EQ(value, kLoggingEnvValueStderr); } TEST_F(CliTest, logging_stderr_skips_env_for_static_targets) { std::vector argv = {"--logging=stderr", "/bin/sh"}; ElfType elftype = ELFSTATIC; bool exit_immediately = false; std::vector child_env; ASSERT_TRUE(parse_args_(argv, &exit_immediately, &elftype, &child_env)); EXPECT_FALSE(GetEnvValue(child_env, kLoggingEnvVar)); } TEST_F(CliTest, logging_auto_defaults_to_syslog_without_tty) { if (isatty(STDIN_FILENO)) { GTEST_SKIP() << "stdin is a TTY; cannot assert auto behavior"; } std::vector argv = {"--logging=auto", "/bin/sh"}; ElfType elftype = ELFDYNAMIC; bool exit_immediately = false; std::vector child_env; ASSERT_TRUE(parse_args_(argv, &exit_immediately, &elftype, &child_env)); EXPECT_FALSE(GetEnvValue(child_env, kLoggingEnvVar)); } TEST_F(CliTest, logging_stderr_respects_env_reset) { std::vector argv = {"--env-reset", "--logging=stderr", "/bin/sh"}; ElfType elftype = ELFDYNAMIC; bool exit_immediately = false; std::vector child_env; ASSERT_TRUE(parse_args_(argv, &exit_immediately, &elftype, &child_env)); std::string value; ASSERT_TRUE(GetEnvValue(child_env, kLoggingEnvVar, &value)); EXPECT_EQ(value, kLoggingEnvValueStderr); } // Valid calls to the gen-config option. TEST_F(CliTest, valid_gen_config) { std::string config_path = std::tmpnam(NULL); std::vector argv = {"--gen-config=" + config_path, "--ambient", "--fs-path-rx=/", "-n"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), ""); } TEST_F(CliTest, valid_gen_config_with_users_mounts_not_found) { std::string config_path = std::tmpnam(NULL); std::vector argv = {"--gen-config=" + config_path, "--ambient", "-u fake-user", "-g fake-group", "-b /fake-mount", "-k /fake-mount", "-e /fake-path", "-f /fake-path", "-V /fake-path", "-n"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), ""); } // Invalid calls to the gen-config option. TEST_F(CliTest, invalid_gen_config) { std::vector argv = {"--gen-config=/", "--ambient", "--fs-path-rx=/", "-n"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } TEST_F(CliTest, invalid_gen_config_not_writable) { std::vector argv = {"--gen-config=/sys/foo", "--ambient", "--fs-path-rx=/", "-n"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Android unit tests do not support data file yet. #if !defined(__ANDROID__) TEST_F(CliTest, conf_parsing_invalid_key) { std::vector argv = {"--config", source_path("test/invalid.conf"), "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } TEST_F(CliTest, conf_parsing) { std::vector argv = {"--config", source_path("test/valid.conf"), "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } TEST_F(CliTest, conf_parsing_seccomp) { std::string seccomp_path = std::tmpnam(NULL); std::ofstream seccomp_stream(seccomp_path); // Intentionally empty policy. std::string config_path = std::tmpnam(NULL); std::ofstream config_stream(config_path); config_stream << "% minijail-config-file v0\n" "S = " + seccomp_path; config_stream.flush(); std::vector argv = {"--config", config_path, "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } TEST_F(CliTest, conf_parsing_with_dac_override) { std::vector argv = {"-c 2", "--config", source_path("test/valid.conf"), "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } TEST_F(CliTest, conf_fs_path) { std::vector argv = { "-c 2", "--config", source_path("test/landlock.conf"), "/bin/sh"}; ASSERT_TRUE(parse_args_(argv)); } #endif // !__ANDROID__ minijail-2026.05.18+git20261016.8787d1a/navbar.md000066400000000000000000000005321526451555500201260ustar00rootroot00000000000000# Minijail [home]: /README.md * [Home][home] * [User Docs](https://chromium.googlesource.com/chromiumos/docs/+/HEAD/sandboxing.md) * [Bugs](https://crbug.com/?q=component:OS>Systems>Minijail) * [License](./LICENSE) * [Hacking](./HACKING.md) * [Release](./RELEASE.md) * [Contact](https://groups.google.com/a/chromium.org/forum/#!forum/minijail) minijail-2026.05.18+git20261016.8787d1a/parse_seccomp_policy.cc000066400000000000000000000054461526451555500230550ustar00rootroot00000000000000/* Copyright 2016 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include "bpf.h" #include "syscall_filter.h" #include "util.h" namespace { void DumpBpfProg(struct sock_fprog* fprog) { struct sock_filter* filter = fprog->filter; unsigned short len = fprog->len; printf("len == %d\n", len); printf("filter:\n"); for (size_t i = 0; i < len; i++) { printf("%zu: \t{ code=%#x, jt=%u, jf=%u, k=%#x \t}\n", i, filter[i].code, filter[i].jt, filter[i].jf, filter[i].k); } } void Usage(const char* progn, int status) { // clang-format off fprintf(status ? stderr : stdout, "Usage: %s [--dump[=]] []\n" "\n" "With no , or when is -, reads from standard input.\n" "\n" " --dump[=]: Dump the BPF program into stdout (or ,\n" " -d[]: if provided). Useful if you want to inspect it\n" " with libseccomp's scmp_bpf_disasm.\n", progn); // clang-format on exit(status); } } // namespace int main(int argc, char** argv) { init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO); static const char optstring[] = "d:h"; static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"dump", optional_argument, 0, 'd'}, {0, 0, 0, 0}, }; bool dump = false; std::string dump_path; int opt; while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) { switch (opt) { case 'h': Usage(argv[0], 0); return 0; case 'd': dump = true; if (optarg) dump_path = optarg; break; } } FILE* f = stdin; // If there is at least one additional unparsed argument, treat it as the // policy script. if (argc > optind && !streq(argv[optind], "-")) f = fopen(argv[optind], "re"); if (!f) pdie("fopen(%s) failed", argv[1]); struct filter_options fopts { .action = ACTION_RET_KILL, .allow_logging = 0, .allow_syscalls_for_logging = 0, .allow_duplicate_syscalls = allow_duplicate_syscalls(), }; struct sock_fprog fp; int res = compile_filter(argv[1], f, &fp, &fopts); fclose(f); if (res != 0) die("compile_filter failed"); if (dump) { FILE* out = stdout; if (!dump_path.empty()) { out = fopen(dump_path.c_str(), "we"); if (!out) pdie("fopen(%s) failed", dump_path.c_str()); } if (fwrite(fp.filter, sizeof(struct sock_filter), fp.len, out) != fp.len) pdie("fwrite(%s) failed", dump_path.c_str()); fclose(out); } else { DumpBpfProg(&fp); } free(fp.filter); return 0; } minijail-2026.05.18+git20261016.8787d1a/platform2_preinstall.sh000077500000000000000000000004541526451555500230400ustar00rootroot00000000000000#!/bin/bash # Copyright 2015 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. set -e v=$1 include_dir=$2 sed \ -e "s/@BSLOT@/${v}/g" \ -e "s:@INCLUDE_DIR@:${include_dir}:g" \ "libminijail.pc.in" > "libminijail.pc" minijail-2026.05.18+git20261016.8787d1a/policies/000077500000000000000000000000001526451555500201425ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/policies/cros_default_v0.policy000066400000000000000000000001031526451555500244340ustar00rootroot00000000000000@denylist # ChromeOS default runtime policy v0. bpf: return EPERM minijail-2026.05.18+git20261016.8787d1a/rust/000077500000000000000000000000001526451555500173305ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/rust/OWNERS000066400000000000000000000002731526451555500202720ustar00rootroot00000000000000# Copyright 2019 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. allenwebb@google.com victorhsieh@google.com minijail-2026.05.18+git20261016.8787d1a/rust/minijail-sys/000077500000000000000000000000001526451555500217405ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/rust/minijail-sys/Cargo.toml000066400000000000000000000004721526451555500236730ustar00rootroot00000000000000[package] name = "minijail-sys" version = "0.0.14" description = "Provides raw (unsafe) bindings to the libminijail C library." authors = ["The ChromiumOS Authors"] edition = "2018" build = "build.rs" [lib] path = "lib.rs" [dependencies] libc = "0.2.44" [build-dependencies] bindgen = "0.72" pkg-config = "0.3" minijail-2026.05.18+git20261016.8787d1a/rust/minijail-sys/build.rs000066400000000000000000000106041526451555500234060ustar00rootroot00000000000000// Copyright 2019 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! Minijail's build script invoked by cargo. //! //! This script prefers linking against a pkg-config provided libminijail, but will fall back to //! building libminijail statically. //! //! The build process can be modified with the following environment variables: //! //! MINIJAIL_DO_NOT_BUILD: Skip building minijail. This tends to result in a linker error later, //! but may be acceptable when just running clippy. //! //! MINIJAIL_BINDGEN_TARGET: Overrides which clang target is used to generate bindings. use std::env; use std::io; use std::path::{Path, PathBuf}; use std::process::Command; use bindgen::EnumVariation; const HEADER_FILENAME: &str = "libminijail.h"; const OUT_FILENAME: &str = "libminijail.rs"; /// Returns the target triplet prefix for gcc commands. No prefix is required /// for native builds. fn get_cross_compile_prefix() -> String { if let Ok(cross_compile) = env::var("CROSS_COMPILE") { return cross_compile; } let target = env::var("TARGET").unwrap(); if env::var("HOST").unwrap() == target { return String::from(""); } let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let env = if target.ends_with("-gnueabihf") { String::from("gnueabihf") } else { env::var("CARGO_CFG_TARGET_ENV").unwrap() }; return format!("{}-{}-{}-", arch, os, env); } fn set_up_libminijail() -> io::Result { // Prefer a system-provided Minijail library. if let Ok(info) = pkg_config::Config::new().probe("libminijail") { for path in info.include_paths { let header_path = path.join(HEADER_FILENAME); if header_path.exists() { return Ok(header_path); } } } if env::var("MINIJAIL_DO_NOT_BUILD").as_deref().unwrap_or("") == "" { let current_dir = env::var("CARGO_MANIFEST_DIR").unwrap() + "/../.."; let out_dir = env::var("OUT_DIR").unwrap(); let profile = env::var("PROFILE").unwrap(); let jobs = env::var("JOBS").unwrap_or("4".to_string()); let status = Command::new("make") .current_dir(&out_dir) .env("OUT", &out_dir) .env("MODE", if profile == "release" { "opt" } else { "debug" }) .env("CROSS_COMPILE", get_cross_compile_prefix()) .env("BUILD_STATIC_LIBS", "yes") .arg("-C") .arg(¤t_dir) .arg("-j") .arg(&jobs) .status()?; if !status.success() { std::process::exit(status.code().unwrap_or(1)); } println!("cargo:rustc-link-search=native={}", &out_dir); println!("cargo:rustc-link-lib=static=minijail.pic"); } let header_dir = Path::new("../../"); let header_path = header_dir.join(HEADER_FILENAME); Ok(header_path) } fn bindings_generation(header_path: &str) -> io::Result<()> { println!("cargo:rerun-if-changed={}", header_path); println!("cargo:rerun-if-changed=build.rs"); let mut builder = bindgen::Builder::default() .header(header_path) .default_enum_style(EnumVariation::Rust { non_exhaustive: false, }) .blocklist_type("__rlim64_t") .raw_line("pub type __rlim64_t = u64;") .blocklist_type("__u\\d{1,2}") .raw_line("pub type __u8 = u8;") .raw_line("pub type __u16 = u16;") .raw_line("pub type __u32 = u32;") .blocklist_type("__uint64_t") .allowlist_function("^minijail_.*") .allowlist_var("^MINIJAIL_.*") .size_t_is_usize(true) .layout_tests(false) .disable_header_comment() .clang_arg("-DUSE_BINDGEN") .clang_arg("-D_FILE_OFFSET_BITS=64") .clang_arg("-D_LARGEFILE_SOURCE") .clang_arg("-D_LARGEFILE64_SOURCE"); if let Ok(target) = env::var("MINIJAIL_BINDGEN_TARGET") { builder = builder.clang_arg(format!("--target={}", target)); } let bindings = builder.generate().expect("failed to generate bindings"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings.write_to_file(out_path.join(OUT_FILENAME)) } fn main() -> io::Result<()> { let header_path = set_up_libminijail()?; bindings_generation(header_path.to_str().unwrap()) } minijail-2026.05.18+git20261016.8787d1a/rust/minijail-sys/lib.rs000066400000000000000000000006121526451555500230530ustar00rootroot00000000000000// Copyright 2019 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /// libminijail bindings for Rust. #[allow( clippy::all, non_camel_case_types, non_snake_case, non_upper_case_globals )] mod libminijail { include!(concat!(env!("OUT_DIR"), "/libminijail.rs")); } pub use crate::libminijail::*; minijail-2026.05.18+git20261016.8787d1a/rust/minijail/000077500000000000000000000000001526451555500211245ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/rust/minijail/Cargo.toml000066400000000000000000000005301526451555500230520ustar00rootroot00000000000000[package] name = "minijail" version = "0.2.3" description = "Provides a safe Rust friendly interface to libminijail." authors = ["The ChromiumOS Authors"] edition = "2018" [dependencies] libc = "0.2.44" minijail-sys = { path = "../minijail-sys" } # provided by ebuild [[test]] name = "fork_remap" path = "tests/fork_remap.rs" harness = false minijail-2026.05.18+git20261016.8787d1a/rust/minijail/src/000077500000000000000000000000001526451555500217135ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/rust/minijail/src/lib.rs000066400000000000000000001300301526451555500230240ustar00rootroot00000000000000// Copyright 2017 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::ffi::CString; use std::fmt::{self, Display}; use std::fs; use std::io; use std::os::raw::{c_char, c_int, c_ulong, c_ushort}; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; use std::path::{Path, PathBuf}; use std::ptr::{null, null_mut}; use std::result::Result as StdResult; use std::sync::Once; use libc::pid_t; use minijail_sys::*; enum Program { Filename(PathBuf), FileDescriptor(RawFd), } /// Configuration of a command to be run in a jail. pub struct Command { program: Program, preserve_fds: Vec<(RawFd, RawFd)>, // Ownership of the backing data of args_cptr is provided by args_cstr. args_cstr: Vec, args_cptr: Vec<*const c_char>, // Ownership of the backing data of env_cptr is provided by env_cstr. env_cstr: Option>, env_cptr: Option>, } impl Command { /// This exposes a subset of what Command can do, before we are ready to commit to a stable /// API. pub fn new_for_path, S: AsRef, A: AsRef>( path: P, keep_fds: &[RawFd], args: &[S], env_vars: Option<&[A]>, ) -> Result { let mut cmd = Command::new(Program::Filename(path.as_ref().to_path_buf())) .keep_fds(keep_fds) .args(args)?; if let Some(env_vars) = env_vars { cmd = cmd.envs(env_vars)?; } Ok(cmd) } fn new(program: Program) -> Command { Command { program, preserve_fds: Vec::new(), args_cstr: Vec::new(), args_cptr: Vec::new(), env_cstr: None, env_cptr: None, } } fn keep_fds(mut self, keep_fds: &[RawFd]) -> Command { self.preserve_fds = keep_fds .iter() .map(|&a| (a, a)) .collect::>(); self } fn remap_fds(mut self, remap_fds: &[(RawFd, RawFd)]) -> Command { self.preserve_fds = remap_fds.to_vec(); self } fn args>(mut self, args: &[S]) -> Result { let (args_cstr, args_cptr) = to_execve_cstring_array(args)?; self.args_cstr = args_cstr; self.args_cptr = args_cptr; Ok(self) } fn envs>(mut self, vars: &[S]) -> Result { let (env_cstr, env_cptr) = to_execve_cstring_array(vars)?; self.env_cstr = Some(env_cstr); self.env_cptr = Some(env_cptr); Ok(self) } fn argv(&self) -> *const *mut c_char { self.args_cptr.as_ptr() as *const *mut c_char } fn envp(&self) -> *const *mut c_char { (match self.env_cptr { Some(ref env_cptr) => env_cptr.as_ptr(), None => null_mut(), }) as *const *mut c_char } } /// Abstracts paths and executable file descriptors in a way that the run implementation can cover /// both. trait Runnable { fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result; } impl Runnable for &Path { fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result { let path_str = self .to_str() .ok_or_else(|| Error::PathToCString(self.to_path_buf()))?; let path_cstr = CString::new(path_str).map_err(|_| Error::StrToCString(path_str.to_owned()))?; let mut pid: pid_t = 0; let ret = unsafe { minijail_run_env_pid_pipes( jail.jail, path_cstr.as_ptr(), cmd.argv(), cmd.envp(), &mut pid, null_mut(), null_mut(), null_mut(), ) }; if ret < 0 { return Err(Error::ForkingMinijail(ret)); } Ok(pid) } } impl Runnable for RawFd { fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result { let mut pid: pid_t = 0; let ret = unsafe { minijail_run_fd_env_pid_pipes( jail.jail, *self, cmd.argv(), cmd.envp(), &mut pid, null_mut(), null_mut(), null_mut(), ) }; if ret < 0 { return Err(Error::ForkingMinijail(ret)); } Ok(pid) } } #[derive(Debug)] pub enum Error { // minijail failed to accept bind mount. BindMount { errno: i32, src: PathBuf, dst: PathBuf, }, // minijail failed to accept mount. Mount { errno: i32, src: PathBuf, dest: PathBuf, fstype: String, flags: usize, data: String, }, /// Failure to count the number of threads in /proc/self/tasks. CheckingMultiThreaded(io::Error), /// minjail_new failed, this is an allocation failure. CreatingMinijail, /// minijail_fork failed with the given error code. ForkingMinijail(i32), /// Attempt to `fork` while already multithreaded. ForkingWhileMultiThreaded, /// The seccomp policy path doesn't exist. SeccompPath(PathBuf), /// The string passed in didn't parse to a valid CString. StrToCString(String), /// The path passed in didn't parse to a valid CString. PathToCString(PathBuf), /// Failed to call dup2 to set stdin, stdout, or stderr to /dev/null. DupDevNull(i32), /// Failed to set up /dev/null for FDs 0, 1, or 2. OpenDevNull(io::Error), /// Failed to read policy bpf from file. ReadProgram(io::Error), /// Setting the specified alt-syscall table failed with errno. Is the table in the kernel? SetAltSyscallTable { errno: i32, name: String }, /// Setting the specified rlimit failed with errno. SetRlimit { errno: i32, kind: libc::c_int }, /// chroot failed with the provided errno. SettingChrootDirectory(i32, PathBuf), /// pivot_root failed with the provided errno. SettingPivotRootDirectory(i32, PathBuf), /// There is an entry in /proc/self/fd that isn't a valid PID. ReadFdDirEntry(io::Error), /// /proc/self/fd failed to open. ReadFdDir(io::Error), /// An entry in /proc/self/fd is not an integer ProcFd(String), /// Minijail refused to preserve an FD in the inherit list of `fork()`. PreservingFd(i32), /// Program size is too large ProgramTooLarge, /// Alignment of file should be divisible by the alignment of sock_filter. WrongProgramAlignment, /// File size should be non-zero and a multiple of sock_filter WrongProgramSize, /// The command was not found. NoCommand, /// The command could not be run. NoAccess, /// Process was killed by SIGSYS indicating a seccomp violation. SeccompViolation(i32), /// Process was killed by a signal other than SIGSYS. Killed(u8), /// Process finished returning a non-zero code. ReturnCode(u8), /// Failed to wait the process. Wait(i32), /// Failed to clone the log fd CloneLogFd(io::Error), } impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; match self { BindMount { src, dst, errno } => write!( f, "failed to accept bind mount {} -> {}: {}", src.display(), dst.display(), io::Error::from_raw_os_error(*errno), ), Mount { errno, src, dest, fstype, flags, data, } => write!( f, "failed to accept mount {} -> {} of type {:?} with flags 0x{:x} \ and data {:?}: {}", src.display(), dest.display(), fstype, flags, data, io::Error::from_raw_os_error(*errno), ), CheckingMultiThreaded(e) => write!( f, "Failed to count the number of threads from /proc/self/tasks {}", e ), CreatingMinijail => write!(f, "minjail_new failed due to an allocation failure"), ForkingMinijail(e) => write!(f, "minijail_fork failed with error {}", e), ForkingWhileMultiThreaded => write!(f, "Attempt to call fork() while multithreaded"), SeccompPath(p) => write!(f, "missing seccomp policy path: {}", p.display()), StrToCString(s) => write!(f, "failed to convert string into CString: {}", s), PathToCString(s) => write!(f, "failed to convert path into CString: {}", s.display()), DupDevNull(errno) => write!( f, "failed to call dup2 to set stdin, stdout, or stderr to /dev/null: {}", io::Error::from_raw_os_error(*errno), ), OpenDevNull(e) => write!( f, "fail to open /dev/null for setting FDs 0, 1, or 2: {}", e, ), ReadProgram(e) => write!(f, "failed to read from bpf file: {}", e), SetAltSyscallTable { name, errno } => write!( f, "failed to set alt-syscall table {}: {}", name, io::Error::from_raw_os_error(*errno), ), SetRlimit { errno, kind } => write!(f, "failed to set rlimit {}: {}", kind, errno), SettingChrootDirectory(errno, p) => write!( f, "failed to set chroot {}: {}", p.display(), io::Error::from_raw_os_error(*errno), ), SettingPivotRootDirectory(errno, p) => write!( f, "failed to set pivot root {}: {}", p.display(), io::Error::from_raw_os_error(*errno), ), ReadFdDirEntry(e) => write!(f, "failed to read an entry in /proc/self/fd: {}", e), ReadFdDir(e) => write!(f, "failed to open /proc/self/fd: {}", e), ProcFd(s) => write!(f, "an entry in /proc/self/fd is not an integer: {}", s), PreservingFd(e) => write!(f, "fork failed in minijail_preserve_fd with error {}", e), ProgramTooLarge => write!(f, "bpf program is too large (max 64K instructions)"), WrongProgramAlignment => write!( f, "the alignment of bpf file was not a multiple of that of sock_filter" ), WrongProgramSize => write!(f, "bpf file was empty or not a multiple of sock_filter"), NoCommand => write!(f, "command was not found"), NoAccess => write!(f, "unable to execute command"), SeccompViolation(s) => write!(f, "seccomp violation syscall #{}", s), Killed(s) => write!(f, "killed with signal number {}", s), ReturnCode(e) => write!(f, "exited with code {}", e), Wait(errno) => write!( f, "failed to wait: {}", io::Error::from_raw_os_error(*errno) ), CloneLogFd(e) => write!(f, "failed to clone log fd: {}", e), } } } impl std::error::Error for Error {} pub type Result = StdResult; /// Configuration to jail a process based on wrapping libminijail. /// /// Intentionally leave out everything related to `minijail_run`. Forking is /// hard to reason about w.r.t. memory and resource safety. It is better to avoid /// forking from rust code. Leave forking to the library user, who can make /// an informed decision about when to fork to minimize risk. /// # Examples /// * Load seccomp policy - like "minijail0 -n -S myfilter.policy" /// /// ``` /// # use minijail::Minijail; /// # fn seccomp_filter_test() -> Result<(), ()> { /// let mut j = Minijail::new().map_err(|_| ())?; /// j.no_new_privs(); /// j.parse_seccomp_filters("my_filter.policy").map_err(|_| ())?; /// j.use_seccomp_filter(); /// unsafe { // `fork` will close all the programs FDs. /// j.fork(None).map_err(|_| ())?; /// } /// # Ok(()) /// # } /// ``` /// /// * Keep stdin, stdout, and stderr open after jailing. /// /// ``` /// # use minijail::Minijail; /// # use std::os::unix::io::RawFd; /// # fn seccomp_filter_test() -> Result<(), ()> { /// let j = Minijail::new().map_err(|_| ())?; /// let preserve_fds: Vec = vec![0, 1, 2]; /// unsafe { // `fork` will close all the programs FDs. /// j.fork(Some(&preserve_fds)).map_err(|_| ())?; /// } /// # Ok(()) /// # } /// ``` /// # Errors /// The `fork` function might not return an error if it fails after forking. A /// partial jail is not recoverable and will instead result in killing the /// process. pub struct Minijail { jail: *mut minijail, disable_multithreaded_check: bool, // Meant to hold on to log_fd for the duration of the Minijail // Is a combination of log_fd and min priority. log_fd: Option<(std::os::fd::OwnedFd, libc::c_int)>, } #[link(name = "c")] extern "C" { fn __libc_current_sigrtmax() -> libc::c_int; } fn translate_wait_error(ret: libc::c_int) -> Result<()> { if ret == 0 { return Ok(()); } if ret < 0 { return Err(Error::Wait(ret)); } if ret == MINIJAIL_ERR_NO_COMMAND as libc::c_int { return Err(Error::NoCommand); } if ret == MINIJAIL_ERR_NO_ACCESS as libc::c_int { return Err(Error::NoAccess); } let sig_base: libc::c_int = MINIJAIL_ERR_SIG_BASE as libc::c_int; let sig_max_code: libc::c_int = unsafe { __libc_current_sigrtmax() } + sig_base; if ret > sig_base && ret <= sig_max_code { return Err(Error::Killed( (ret - MINIJAIL_ERR_SIG_BASE as libc::c_int) as u8, )); } if ret > 0 && ret <= 0xff { return Err(Error::ReturnCode(ret as u8)); } unreachable!("Unexpected returned value from wait: {}", ret); } impl Minijail { /// Creates a new jail configuration. pub fn new() -> Result { let j = unsafe { // libminijail actually owns the minijail structure. It will live until we call // minijail_destroy. minijail_new() }; if j.is_null() { return Err(Error::CreatingMinijail); } Ok(Minijail { jail: j, disable_multithreaded_check: false, log_fd: None, }) } /// Clones self to a new `Minijail`. Useful because `fork` can only be called once on a /// `Minijail`. pub fn try_clone(&self) -> Result { let mut jail_out = Minijail::new()?; jail_out.disable_multithreaded_check = self.disable_multithreaded_check; unsafe { // Safe to clone one minijail to the other as minijail_clone doesn't modify the source // jail(`self`) and leaves a valid minijail in the destination(`jail_out`). let ret = minijail_copy_jail(self.jail, jail_out.jail); if ret < 0 { return Err(Error::ReturnCode(ret as u8)); } } if let Some((log_fd, min_priority)) = &self.log_fd { jail_out.log_to_fd( log_fd.try_clone().map_err(Error::CloneLogFd)?, *min_priority, ); } Ok(jail_out) } // The following functions are safe because they only set values in the // struct already owned by minijail. The struct's lifetime is tied to // `struct Minijail` so it is guaranteed to be valid pub fn change_uid(&mut self, uid: libc::uid_t) { unsafe { minijail_change_uid(self.jail, uid); } } pub fn change_gid(&mut self, gid: libc::gid_t) { unsafe { minijail_change_gid(self.jail, gid); } } pub fn change_user(&mut self, user: &str) -> Result<()> { let user_cstring = CString::new(user).map_err(|_| Error::StrToCString(user.to_owned()))?; unsafe { minijail_change_user(self.jail, user_cstring.as_ptr()); } Ok(()) } pub fn change_group(&mut self, group: &str) -> Result<()> { let group_cstring = CString::new(group).map_err(|_| Error::StrToCString(group.to_owned()))?; unsafe { minijail_change_group(self.jail, group_cstring.as_ptr()); } Ok(()) } pub fn set_supplementary_gids(&mut self, ids: &[libc::gid_t]) { unsafe { minijail_set_supplementary_gids(self.jail, ids.len(), ids.as_ptr()); } } pub fn keep_supplementary_gids(&mut self) { unsafe { minijail_keep_supplementary_gids(self.jail); } } // rlim_t is defined in minijail-sys to be u64 on all platforms, to avoid // issues on 32-bit platforms. It's also useful to us here to avoid // libc::rlim64_t, which is not defined at all on Android. pub fn set_rlimit(&mut self, kind: libc::c_int, cur: rlim_t, max: rlim_t) -> Result<()> { let errno = unsafe { minijail_rlimit(self.jail, kind, cur, max) }; if errno == 0 { Ok(()) } else { Err(Error::SetRlimit { errno, kind }) } } pub fn use_seccomp(&mut self) { unsafe { minijail_use_seccomp(self.jail); } } pub fn no_new_privs(&mut self) { unsafe { minijail_no_new_privs(self.jail); } } pub fn use_seccomp_filter(&mut self) { unsafe { minijail_use_seccomp_filter(self.jail); } } pub fn set_seccomp_filter_tsync(&mut self) { unsafe { minijail_set_seccomp_filter_tsync(self.jail); } } pub fn parse_seccomp_program>(&mut self, path: P) -> Result<()> { if !path.as_ref().is_file() { return Err(Error::SeccompPath(path.as_ref().to_owned())); } let buffer = fs::read(path).map_err(Error::ReadProgram)?; self.parse_seccomp_bytes(&buffer) } pub fn parse_seccomp_bytes(&mut self, buffer: &[u8]) -> Result<()> { if buffer.len() % std::mem::size_of::() != 0 { return Err(Error::WrongProgramSize); } let count = buffer.len() / std::mem::size_of::(); if count > (!0 as u16) as usize { return Err(Error::ProgramTooLarge); } if buffer.as_ptr() as usize % std::mem::align_of::() != 0 { return Err(Error::WrongProgramAlignment); } // Safe cast because we checked that the buffer address is divisible by the alignment of // sock_filter. #[allow(clippy::cast_ptr_alignment)] let header = sock_fprog { len: count as c_ushort, filter: buffer.as_ptr() as *mut sock_filter, }; unsafe { minijail_set_seccomp_filters(self.jail, &header); } Ok(()) } pub fn parse_seccomp_filters>(&mut self, path: P) -> Result<()> { if !path.as_ref().is_file() { return Err(Error::SeccompPath(path.as_ref().to_owned())); } let pathstring = path .as_ref() .as_os_str() .to_str() .ok_or_else(|| Error::PathToCString(path.as_ref().to_owned()))?; let filename = CString::new(pathstring).map_err(|_| Error::PathToCString(path.as_ref().to_owned()))?; unsafe { minijail_parse_seccomp_filters(self.jail, filename.as_ptr()); } Ok(()) } pub fn log_seccomp_filter_failures(&mut self) { unsafe { minijail_log_seccomp_filter_failures(self.jail); } } pub fn use_caps(&mut self, capmask: u64) { unsafe { minijail_use_caps(self.jail, capmask); } } pub fn capbset_drop(&mut self, capmask: u64) { unsafe { minijail_capbset_drop(self.jail, capmask); } } pub fn set_ambient_caps(&mut self) { unsafe { minijail_set_ambient_caps(self.jail); } } pub fn reset_signal_mask(&mut self) { unsafe { minijail_reset_signal_mask(self.jail); } } pub fn run_as_init(&mut self) { unsafe { minijail_run_as_init(self.jail); } } pub fn namespace_pids(&mut self) { unsafe { minijail_namespace_pids(self.jail); } } pub fn namespace_user(&mut self) { unsafe { minijail_namespace_user(self.jail); } } pub fn namespace_user_disable_setgroups(&mut self) { unsafe { minijail_namespace_user_disable_setgroups(self.jail); } } pub fn namespace_vfs(&mut self) { unsafe { minijail_namespace_vfs(self.jail); } } pub fn new_session_keyring(&mut self) { unsafe { minijail_new_session_keyring(self.jail); } } pub fn skip_remount_private(&mut self) { unsafe { minijail_skip_remount_private(self.jail); } } pub fn namespace_ipc(&mut self) { unsafe { minijail_namespace_ipc(self.jail); } } pub fn namespace_net(&mut self) { unsafe { minijail_namespace_net(self.jail); } } pub fn namespace_cgroups(&mut self) { unsafe { minijail_namespace_cgroups(self.jail); } } pub fn remount_proc_readonly(&mut self) { unsafe { minijail_remount_proc_readonly(self.jail); } } pub fn set_remount_mode(&mut self, mode: c_ulong) { unsafe { minijail_remount_mode(self.jail, mode) } } pub fn uidmap(&mut self, uid_map: &str) -> Result<()> { let map_cstring = CString::new(uid_map).map_err(|_| Error::StrToCString(uid_map.to_owned()))?; unsafe { minijail_uidmap(self.jail, map_cstring.as_ptr()); } Ok(()) } pub fn gidmap(&mut self, gid_map: &str) -> Result<()> { let map_cstring = CString::new(gid_map).map_err(|_| Error::StrToCString(gid_map.to_owned()))?; unsafe { minijail_gidmap(self.jail, map_cstring.as_ptr()); } Ok(()) } pub fn inherit_usergroups(&mut self) { unsafe { minijail_inherit_usergroups(self.jail); } } pub fn use_alt_syscall(&mut self, table_name: &str) -> Result<()> { let table_name_string = CString::new(table_name).map_err(|_| Error::StrToCString(table_name.to_owned()))?; let ret = unsafe { minijail_use_alt_syscall(self.jail, table_name_string.as_ptr()) }; if ret < 0 { return Err(Error::SetAltSyscallTable { errno: ret, name: table_name.to_owned(), }); } Ok(()) } pub fn log_to_fd(&mut self, fd: std::os::fd::OwnedFd, min_priority: c_int) { // Minijail doesn't close the fd when it is destroyed, so this is safe. unsafe { minijail_log_to_fd(fd.as_raw_fd(), min_priority); } // minijail_log_to_fd "borrows" the fd (in Rust parlance), so we need to store the // fd as long as Minijail is alive for correctness. self.log_fd = Some((fd, min_priority)); } pub fn enter_chroot>(&mut self, dir: P) -> Result<()> { let pathstring = dir .as_ref() .as_os_str() .to_str() .ok_or_else(|| Error::PathToCString(dir.as_ref().to_owned()))?; let dirname = CString::new(pathstring).map_err(|_| Error::PathToCString(dir.as_ref().to_owned()))?; let ret = unsafe { minijail_enter_chroot(self.jail, dirname.as_ptr()) }; if ret < 0 { return Err(Error::SettingChrootDirectory(ret, dir.as_ref().to_owned())); } Ok(()) } pub fn enter_pivot_root>(&mut self, dir: P) -> Result<()> { let pathstring = dir .as_ref() .as_os_str() .to_str() .ok_or_else(|| Error::PathToCString(dir.as_ref().to_owned()))?; let dirname = CString::new(pathstring).map_err(|_| Error::PathToCString(dir.as_ref().to_owned()))?; let ret = unsafe { minijail_enter_pivot_root(self.jail, dirname.as_ptr()) }; if ret < 0 { return Err(Error::SettingPivotRootDirectory( ret, dir.as_ref().to_owned(), )); } Ok(()) } pub fn mount, P2: AsRef>( &mut self, src: P1, dest: P2, fstype: &str, flags: usize, ) -> Result<()> { self.mount_with_data(src, dest, fstype, flags, "") } pub fn mount_with_data, P2: AsRef>( &mut self, src: P1, dest: P2, fstype: &str, flags: usize, data: &str, ) -> Result<()> { let src_os = src .as_ref() .as_os_str() .to_str() .ok_or_else(|| Error::PathToCString(src.as_ref().to_owned()))?; let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?; let dest_os = dest .as_ref() .as_os_str() .to_str() .ok_or_else(|| Error::PathToCString(dest.as_ref().to_owned()))?; let dest_path = CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?; let fstype_string = CString::new(fstype).map_err(|_| Error::StrToCString(fstype.to_owned()))?; let data_string = CString::new(data).map_err(|_| Error::StrToCString(data.to_owned()))?; let ret = unsafe { minijail_mount_with_data( self.jail, src_path.as_ptr(), dest_path.as_ptr(), fstype_string.as_ptr(), flags as _, data_string.as_ptr(), ) }; if ret < 0 { return Err(Error::Mount { errno: ret, src: src.as_ref().to_owned(), dest: dest.as_ref().to_owned(), fstype: fstype.to_owned(), flags, data: data.to_owned(), }); } Ok(()) } pub fn mount_dev(&mut self) { unsafe { minijail_mount_dev(self.jail); } } pub fn mount_tmp(&mut self) { unsafe { minijail_mount_tmp(self.jail); } } pub fn mount_tmp_size(&mut self, size: usize) { unsafe { minijail_mount_tmp_size(self.jail, size); } } pub fn mount_bind, P2: AsRef>( &mut self, src: P1, dest: P2, writable: bool, ) -> Result<()> { let src_os = src .as_ref() .as_os_str() .to_str() .ok_or_else(|| Error::PathToCString(src.as_ref().to_owned()))?; let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?; let dest_os = dest .as_ref() .as_os_str() .to_str() .ok_or_else(|| Error::PathToCString(dest.as_ref().to_owned()))?; let dest_path = CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?; let ret = unsafe { minijail_bind( self.jail, src_path.as_ptr(), dest_path.as_ptr(), writable as _, ) }; if ret < 0 { return Err(Error::BindMount { errno: ret, src: src.as_ref().to_owned(), dst: dest.as_ref().to_owned(), }); } Ok(()) } /// Disables the check that prevents forking in a multithreaded environment. /// This is only safe if the child process calls exec immediately after /// forking. The state of locks, and whether or not they will unlock /// is undefined. Additionally, objects allocated on other threads that /// expect to be dropped when those threads cease execution will not be /// dropped. /// Thus, nothing should be called that relies on shared synchronization /// primitives referenced outside of the current thread. The safest /// way to use this is to immediately exec in the child. pub fn disable_multithreaded_check(&mut self) { self.disable_multithreaded_check = true; } /// Forks and execs a child and puts it in the previously configured minijail. /// FDs 0, 1, and 2 are overwritten with /dev/null FDs unless they are included in the /// inheritable_fds list. This function may abort in the child on error because a partially /// entered jail isn't recoverable. pub fn run, S: AsRef>( &self, cmd: P, inheritable_fds: &[RawFd], args: &[S], ) -> Result { self.run_internal( Command::new(Program::Filename(cmd.as_ref().to_path_buf())) .keep_fds(inheritable_fds) .args(args)?, ) } /// Behaves the same as `run()` except `inheritable_fds` is a list of fd /// mappings rather than just a list of fds to preserve. pub fn run_remap, S: AsRef>( &self, cmd: P, inheritable_fds: &[(RawFd, RawFd)], args: &[S], ) -> Result { self.run_internal( Command::new(Program::Filename(cmd.as_ref().to_path_buf())) .remap_fds(inheritable_fds) .args(args)?, ) } /// Behaves the same as `run()` except cmd is a file descriptor to the executable. pub fn run_fd>( &self, cmd: &F, inheritable_fds: &[RawFd], args: &[S], ) -> Result { self.run_internal( Command::new(Program::FileDescriptor(cmd.as_raw_fd())) .keep_fds(inheritable_fds) .args(args)?, ) } /// Behaves the same as `run()` except cmd is a file descriptor to the executable, and /// `inheritable_fds` is a list of fd mappings rather than just a list of fds to preserve. pub fn run_fd_remap>( &self, cmd: &F, inheritable_fds: &[(RawFd, RawFd)], args: &[S], ) -> Result { self.run_internal( Command::new(Program::FileDescriptor(cmd.as_raw_fd())) .remap_fds(inheritable_fds) .args(args)?, ) } /// A generic version of `run()` that is a super set of all variants. pub fn run_command(&self, cmd: Command) -> Result { self.run_internal(cmd) } fn run_internal(&self, cmd: Command) -> Result { for (src_fd, dst_fd) in cmd.preserve_fds.iter() { let ret = unsafe { minijail_preserve_fd(self.jail, *src_fd, *dst_fd) }; if ret < 0 { return Err(Error::PreservingFd(ret)); } } let dev_null = fs::OpenOptions::new() .read(true) .write(true) .open("/dev/null") .map_err(Error::OpenDevNull)?; // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list. // These will only be closed when this process exits. for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] { if !cmd.preserve_fds.iter().any(|(_, fd)| *fd == *io_fd) { let ret = unsafe { minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd) }; if ret < 0 { return Err(Error::PreservingFd(ret)); } } } unsafe { minijail_close_open_fds(self.jail); } match cmd.program { Program::Filename(ref path) => path.as_path().run_command(self, &cmd), Program::FileDescriptor(fd) => fd.run_command(self, &cmd), } } /// Forks a child and puts it in the previously configured minijail. /// /// # Safety /// `fork` is unsafe because it closes all open FD for this process. That /// could cause a lot of trouble if not handled carefully. FDs 0, 1, and 2 /// are overwritten with /dev/null FDs unless they are included in the /// inheritable_fds list. /// /// Also, any Rust objects that own fds may try to close them after the fork. If they belong /// to a fd number that was mapped to, the mapped fd will be closed instead. /// /// This Function may abort in the child on error because a partially /// entered jail isn't recoverable. /// /// Once this is invoked the object is no longer usable, after this call /// this minijail object is invalid. pub unsafe fn fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result { let m: Vec<(RawFd, RawFd)> = inheritable_fds .unwrap_or(&[]) .iter() .map(|&a| (a, a)) .collect(); self.fork_remap(&m) } /// Behaves the same as `fork()` except `inheritable_fds` is a list of fd /// mappings rather than just a list of fds to preserve. /// /// # Safety /// See `fork`. pub unsafe fn fork_remap(&self, inheritable_fds: &[(RawFd, RawFd)]) -> Result { if !self.disable_multithreaded_check && !is_single_threaded().map_err(Error::CheckingMultiThreaded)? { // This test will fail during `cargo test` because the test harness always spawns a test // thread. We will make an exception for that case because the tests for this module // should always be run in a serial fashion using `--test-threads=1`. #[cfg(not(test))] return Err(Error::ForkingWhileMultiThreaded); } for (src_fd, dst_fd) in inheritable_fds { let ret = minijail_preserve_fd(self.jail, *src_fd, *dst_fd); if ret < 0 { return Err(Error::PreservingFd(ret)); } } let dev_null = fs::OpenOptions::new() .read(true) .write(true) .open("/dev/null") .map_err(Error::OpenDevNull)?; // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list. // These will only be closed when this process exits. for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] { if !inheritable_fds.iter().any(|(_, fd)| *fd == *io_fd) { let ret = minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd); if ret < 0 { return Err(Error::PreservingFd(ret)); } } } minijail_close_open_fds(self.jail); let ret = minijail_fork(self.jail); if ret < 0 { return Err(Error::ForkingMinijail(ret)); } if ret == 0 { // Safe because dev_null was remapped. let _ = dev_null.into_raw_fd(); } Ok(ret as pid_t) } pub fn wait(&self) -> Result<()> { let ret: libc::c_int; // This is safe because it does not modify the struct. unsafe { ret = minijail_wait(self.jail); } translate_wait_error(ret) } /// Send a SIGTERM to the child process and wait for its return code. pub fn kill(&self) -> Result<()> { let ret = unsafe { // The kill does not change any internal state. minijail_kill(self.jail) }; // minijail_kill waits for the process, so also translate the returned wait error. translate_wait_error(ret) } } impl Drop for Minijail { /// Frees the Minijail created in Minijail::new. This will not terminate the /// minijailed process. fn drop(&mut self) { unsafe { // Destroys the minijail's memory. It is safe to do here because all references to // this object have been dropped. minijail_destroy(self.jail); } } } // Check if a `/proc/*/task/*` is a kthread. fn task_is_kthread(path: &Path) -> io::Result { let status = fs::read_to_string(path.join("status"))?; Ok(status.contains("\nKthread:\t1\n")) } // Count the number of threads in the current process. fn num_threads() -> io::Result { let mut count = 0; for entry in fs::read_dir("/proc/self/task")? { let entry = entry?; if !task_is_kthread(&entry.path())? { count += 1; } } Ok(count) } // Return true if the current thread is the only thread in the process. fn is_single_threaded() -> io::Result { Ok(num_threads()? == 1) } fn to_execve_cstring_array>( slice: &[S], ) -> Result<(Vec, Vec<*const c_char>)> { // Converts each incoming `str` to a `CString`, and then puts each `CString` pointer into a // null terminated array, suitable for use as an argv or envp parameter to `execve`. let mut vec_cstr = Vec::with_capacity(slice.len()); let mut vec_cptr = Vec::with_capacity(slice.len() + 1); for s in slice { let cstr = CString::new(s.as_ref()).map_err(|_| Error::StrToCString(s.as_ref().to_owned()))?; vec_cstr.push(cstr); vec_cptr.push(vec_cstr.last().unwrap().as_ptr()); } vec_cptr.push(null()); Ok((vec_cstr, vec_cptr)) } static LOGGING_INIT_LOCK: Once = Once::new(); pub fn init_default_logging() { LOGGING_INIT_LOCK.call_once(|| { log_to_fd(libc::STDERR_FILENO, 6 /* SYSLOG_LOG_INFO */) }) } fn log_to_fd(fd: RawFd, priority: c_int) { unsafe { minijail_log_to_fd(fd, priority); } } #[cfg(test)] mod tests { use super::*; use std::fs::File; use libc::{FD_CLOEXEC, F_GETFD, F_SETFD}; const SHELL: &str = "/bin/sh"; const EMPTY_STRING_SLICE: &[&str] = &[]; fn clear_cloexec(fd_owner: &A) -> StdResult<(), io::Error> { let fd = fd_owner.as_raw_fd(); // Safe because fd is read only. let flags = unsafe { libc::fcntl(fd, F_GETFD) }; if flags == -1 { return Err(io::Error::last_os_error()); } let masked_flags = flags & !FD_CLOEXEC; // Safe because this has no side effect(s) on the current process. if masked_flags != flags && unsafe { libc::fcntl(fd, F_SETFD, masked_flags) } == -1 { Err(io::Error::last_os_error()) } else { Ok(()) } } #[test] fn create_and_free() { init_default_logging(); unsafe { let j = minijail_new(); assert_ne!(std::ptr::null_mut(), j); minijail_destroy(j); } let j = Minijail::new().unwrap(); drop(j); } #[test] // Test that setting a seccomp filter with no-new-privs works as non-root. // This is equivalent to minijail0 -n -S fn seccomp_no_new_privs() { init_default_logging(); let mut j = Minijail::new().unwrap(); j.no_new_privs(); j.parse_seccomp_filters("src/test_filter.policy").unwrap(); j.use_seccomp_filter(); j.run("/bin/true", &[], EMPTY_STRING_SLICE).unwrap(); } #[test] // Test that open FDs get closed and that FDs in the inherit list are left open. fn close_fds() { init_default_logging(); unsafe { // Using libc to open/close FDs for testing. const FILE_PATH: &[u8] = b"/dev/null\0"; let j = Minijail::new().unwrap(); let first = libc::open(FILE_PATH.as_ptr() as *const c_char, libc::O_RDONLY); assert!(first >= 0); // This appears unused but its function is to be a file descriptor that is closed // inside run_remap after the fork. If it is not closed, the script will fail. let second = libc::open(FILE_PATH.as_ptr() as *const c_char, libc::O_RDONLY); assert!(second >= 0); let fds: Vec<(RawFd, RawFd)> = vec![(first, 0), (1, 1), (2, 2)]; j.run_remap( SHELL, &fds, &[ SHELL, "-c", r#" if [ `ls -l /proc/self/fd/ | grep '/dev/null' | wc -l` != '1' ]; then ls -l /proc/self/fd/ # Included to make debugging easier. exit 1 fi "#, ], ) .unwrap(); j.wait().unwrap(); } } macro_rules! expect_result { ($call:expr, $expected:pat) => { let got = $call; match got { $expected => {} _ => { panic!("got {:?} expected {:?}", got, stringify!($expected)); } } }; } #[test] fn wait_success() { init_default_logging(); let j = Minijail::new().unwrap(); j.run("/bin/true", &[1, 2], EMPTY_STRING_SLICE).unwrap(); expect_result!(j.wait(), Ok(())); } #[test] fn wait_killed() { init_default_logging(); let j = Minijail::new().unwrap(); j.run( SHELL, &[1, 2], &[SHELL, "-c", "kill -9 $$ &\n/usr/bin/sleep 5"], ) .unwrap(); expect_result!(j.wait(), Err(Error::Killed(9))); } #[test] fn wait_returncode() { init_default_logging(); let j = Minijail::new().unwrap(); j.run("/bin/false", &[1, 2], EMPTY_STRING_SLICE).unwrap(); expect_result!(j.wait(), Err(Error::ReturnCode(1))); } #[test] fn wait_noaccess() { init_default_logging(); let j = Minijail::new().unwrap(); j.run("/dev/null", &[1, 2], EMPTY_STRING_SLICE).unwrap(); expect_result!(j.wait(), Err(Error::NoAccess)); } #[test] fn wait_nocommand() { init_default_logging(); let j = Minijail::new().unwrap(); j.run("/bin/does not exist", &[1, 2], EMPTY_STRING_SLICE) .unwrap(); // TODO(b/194221986) Fix libminijail so that Error::NoAccess is not sometimes returned. assert!(matches!( j.wait(), Err(Error::NoCommand) | Err(Error::NoAccess) )); } #[test] #[ignore] // TODO(b/323475944) Fix unit test failures. fn runnable_fd_success() { init_default_logging(); let bin_file = File::open("/bin/true").unwrap(); // On ChromeOS targets /bin/true is actually a script, so drop CLOEXEC to prevent ENOENT. clear_cloexec(&bin_file).unwrap(); let j = Minijail::new().unwrap(); j.run_fd(&bin_file, &[1, 2], EMPTY_STRING_SLICE).unwrap(); expect_result!(j.wait(), Ok(())); } #[test] fn kill_success() { init_default_logging(); let j = Minijail::new().unwrap(); j.run( Path::new("/usr/bin/sleep"), &[1, 2], &["/usr/bin/sleep", "5"], ) .unwrap(); const EXPECTED_SIGNAL: u8 = libc::SIGTERM as u8; expect_result!(j.kill(), Err(Error::Killed(EXPECTED_SIGNAL))); } #[test] #[ignore] // privileged operation. fn chroot() { init_default_logging(); let mut j = Minijail::new().unwrap(); j.enter_chroot(".").unwrap(); j.run("/bin/true", &[], EMPTY_STRING_SLICE).unwrap(); } #[test] #[ignore] // privileged operation. fn namespace_vfs() { init_default_logging(); let mut j = Minijail::new().unwrap(); j.namespace_vfs(); j.run("/bin/true", &[], EMPTY_STRING_SLICE).unwrap(); } #[test] fn run() { init_default_logging(); let j = Minijail::new().unwrap(); j.run("/bin/true", &[], EMPTY_STRING_SLICE).unwrap(); } #[test] fn run_clone() { init_default_logging(); let j = Minijail::new().unwrap(); let b = j.try_clone().unwrap(); // Pass the same FDs to both clones and make sure they don't conflict. j.run("/bin/true", &[1, 2], EMPTY_STRING_SLICE).unwrap(); b.run("/bin/true", &[1, 2], EMPTY_STRING_SLICE).unwrap(); } #[test] fn run_string_vec() { init_default_logging(); let j = Minijail::new().unwrap(); let args = vec!["ignored".to_string()]; j.run(Path::new("/bin/true"), &[], &args).unwrap(); } } minijail-2026.05.18+git20261016.8787d1a/rust/minijail/src/test_filter.policy000066400000000000000000000000761526451555500254630ustar00rootroot00000000000000close: 1 exit: 1 futex: 1 getpid: 1 lseek: 1 read: 1 write: 1 minijail-2026.05.18+git20261016.8787d1a/rust/minijail/tests/000077500000000000000000000000001526451555500222665ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/rust/minijail/tests/fork_remap.rs000066400000000000000000000062701526451555500247660ustar00rootroot00000000000000// Copyright 2021 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! A test of Minijail::fork_remap. //! //! It needs to be run on its own because it forks the process and by default cargo test is //! multi-threaded, and we do not want copies of the other worker threads leaking into the child //! process. use std::fs::{read_link, File, OpenOptions}; use std::io::{self, Read}; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use std::path::Path; use minijail::Minijail; const DEV_NULL: &str = "/dev/null"; const DEV_ZERO: &str = "/dev/zero"; const PROC_CMDLINE: &str = "/proc/self/cmdline"; fn open_path(path: &str) -> Result { OpenOptions::new() .read(true) .write(false) .open(Path::new(path)) } fn main() { let mut check_file1 = open_path(DEV_ZERO).unwrap(); let mut check_file2 = open_path(PROC_CMDLINE).unwrap(); let j = Minijail::new().unwrap(); let mut stdio_expected = String::new(); let mut file2_expected = String::new(); for &p in &[0, 1, 2, check_file1.as_raw_fd(), check_file2.as_raw_fd()] { let path = format!("/proc/self/fd/{}", p); let target = read_link(Path::new(&path)); eprintln!("P: {} -> {:?}", p, &target); if p == 2 { stdio_expected = target.unwrap().to_string_lossy().to_string(); } else if p == check_file2.as_raw_fd() { file2_expected = target.unwrap().to_string_lossy().to_string(); } } // Swap fd1 and fd2. let dest_fd1: RawFd = check_file2.as_raw_fd(); let dest_fd2: RawFd = check_file1.as_raw_fd(); if unsafe { j.fork_remap(&[ // fd 0 tests stdio mapped to /dev/null. (2, 1), // One-to-many. (2, 2), // Identity. (check_file1.as_raw_fd(), dest_fd1), // Cross-over. (check_file2.as_raw_fd(), dest_fd2), // Cross-over. ]) } .unwrap() != 0 { j.wait().unwrap(); eprintln!("Parent done."); return; } // Safe because we are re-taking ownership of remapped fds after forking. unsafe { check_file1.into_raw_fd(); check_file1 = File::from_raw_fd(dest_fd1); check_file2.into_raw_fd(); check_file2 = File::from_raw_fd(dest_fd2); } for (p, expected) in &[ (0, DEV_NULL), (1, &stdio_expected), (2, &stdio_expected), (dest_fd1, DEV_ZERO), (dest_fd2, &file2_expected), ] { let path = format!("/proc/self/fd/{}", p); let target = read_link(Path::new(&path)); eprintln!(" C: {} -> {:?}", p, &target); if !matches!(&target, Ok(p) if p == Path::new(expected)) { panic!(" C: got {:?}; expected Ok({:?})", target, expected); } } const BUFFER_LEN: usize = 16; let mut buffer = [0xffu8; BUFFER_LEN]; check_file1.read_exact(&mut buffer).unwrap(); assert_eq!(&buffer, &[0u8; BUFFER_LEN]); let mut file2_contents = Vec::::new(); check_file2.read_to_end(&mut file2_contents).unwrap(); eprintln!(" Child done."); } minijail-2026.05.18+git20261016.8787d1a/scoped_minijail.h000066400000000000000000000011061526451555500216330ustar00rootroot00000000000000/* Copyright 2016 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef _SCOPED_MINIJAIL_H_ #define _SCOPED_MINIJAIL_H_ #include #include "libminijail.h" namespace mj { namespace internal { struct ScopedMinijailDeleter { inline void operator()(minijail *j) const { if (j) { minijail_destroy(j); } } }; } // namespace internal } // namespace mj using ScopedMinijail = std::unique_ptr; #endif /* _SCOPED_MINIJAIL_H_ */ minijail-2026.05.18+git20261016.8787d1a/setup.py000077500000000000000000000025031526451555500200500ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """A file that specifies how to install minijail's python-based tool(s).""" import os import setuptools this_directory = os.path.abspath(os.path.dirname(__file__)) with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f: long_description = f.read() setuptools.setup( name="minijail", version="18", description="A set of tools for Minijail", classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: BSD License", "Operating System :: Linux", ], python_requires=">=3.8", license="3-clause BSD", long_description=long_description, long_description_content_type="text/markdown", author="Minijail Developers", author_email="minijail-dev@google.com", url="https://google.github.io/minijail/", packages=["minijail"], package_dir={"minijail": "tools"}, entry_points={ "console_scripts": [ "compile_seccomp_policy = minijail.compile_seccomp_policy:main", "generate_seccomp_policy = minijail.generate_seccomp_policy:main", "generate_constants_json = minijail.generate_constants_json:main", ], }, ) minijail-2026.05.18+git20261016.8787d1a/signal_handler.c000066400000000000000000000031431526451555500214520ustar00rootroot00000000000000/* Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include "signal_handler.h" #include "util.h" /* * si_syscall was added in glibc-2.17+, but Android still uses glibc-2.15 * for its prebuilt binary host toolchains. Add a compat hack for it. */ static int get_si_syscall(const siginfo_t *info) { #if defined(si_syscall) return info->si_syscall; #else typedef struct { void *ip; int nr; unsigned int arch; } local_siginfo_t; union { const siginfo_t *info; const local_siginfo_t *local_info; } local_info = { .info = info, }; return local_info.local_info->nr; #endif } void log_sigsys_handler(int sig attribute_unused, siginfo_t *info, void *void_context attribute_unused) { const char *syscall_name; int nr = get_si_syscall(info); syscall_name = lookup_syscall_name(nr); if (syscall_name) die("blocked syscall: %s", syscall_name); else die("blocked syscall: %d", nr); /* * We trapped on a syscall that should have killed the process. * This should never ever return, but we're paranoid. */ for (;;) _exit(1); } int install_sigsys_handler() { int ret = 0; struct sigaction act; sigset_t mask; memset(&act, 0, sizeof(act)); act.sa_sigaction = &log_sigsys_handler; act.sa_flags = SA_SIGINFO; sigemptyset(&mask); sigaddset(&mask, SIGSYS); ret = sigaction(SIGSYS, &act, NULL); if (ret < 0) return ret; ret = sigprocmask(SIG_UNBLOCK, &mask, NULL); if (ret < 0) return ret; return 0; } minijail-2026.05.18+git20261016.8787d1a/signal_handler.h000066400000000000000000000004771526451555500214660ustar00rootroot00000000000000/* signal_handler.h * Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Signal handling functions. */ #ifndef SIGNAL_HANDLER_H #define SIGNAL_HANDLER_H int install_sigsys_handler(void); #endif /* SIGNAL_HANDLER_H */ minijail-2026.05.18+git20261016.8787d1a/syscall_filter.c000066400000000000000000000577761526451555500215440ustar00rootroot00000000000000/* Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include "syscall_filter.h" #include "util.h" /* clang-format off */ #define ONE_INSTR 1 #define TWO_INSTRS 2 #define compiler_warn(_state, _msg, ...) \ warn("%s: %s(%zd): " _msg, __func__, (_state)->filename, \ (_state)->line_number, ## __VA_ARGS__) #define compiler_pwarn(_state, _msg, ...) \ compiler_warn(_state, _msg ": %m", ## __VA_ARGS__) /* clang-format on */ int seccomp_can_softfail(void) { #if defined(USE_SECCOMP_SOFTFAIL) return 1; #endif return 0; } int str_to_op(const char *op_str) { if (streq(op_str, "==")) { return EQ; } else if (streq(op_str, "!=")) { return NE; } else if (streq(op_str, "<")) { return LT; } else if (streq(op_str, "<=")) { return LE; } else if (streq(op_str, ">")) { return GT; } else if (streq(op_str, ">=")) { return GE; } else if (streq(op_str, "&")) { return SET; } else if (streq(op_str, "in")) { return IN; } else { return 0; } } struct sock_filter *new_instr_buf(size_t count) { struct sock_filter *buf = calloc(count, sizeof(struct sock_filter)); if (!buf) die("could not allocate BPF instruction buffer"); return buf; } struct filter_block *new_filter_block(void) { struct filter_block *block = calloc(1, sizeof(struct filter_block)); if (!block) die("could not allocate BPF filter block"); block->instrs = NULL; block->last = block->next = NULL; return block; } void append_filter_block(struct filter_block *head, struct sock_filter *instrs, size_t len) { struct filter_block *new_last; /* * If |head| has no filter assigned yet, * we don't create a new node. */ if (head->instrs == NULL) { new_last = head; } else { new_last = new_filter_block(); if (head->next != NULL) { head->last->next = new_last; head->last = new_last; } else { head->last = head->next = new_last; } head->total_len += len; } new_last->instrs = instrs; new_last->total_len = new_last->len = len; new_last->last = new_last->next = NULL; } void extend_filter_block_list(struct filter_block *list, struct filter_block *another) { if (list->last != NULL) { list->last->next = another; list->last = another->last; } else { list->next = another; list->last = another->last; } list->total_len += another->total_len; } void append_ret_kill(struct filter_block *head) { struct sock_filter *filter = new_instr_buf(ONE_INSTR); set_bpf_ret_kill(filter); append_filter_block(head, filter, ONE_INSTR); } void append_ret_kill_process(struct filter_block *head) { struct sock_filter *filter = new_instr_buf(ONE_INSTR); set_bpf_ret_kill_process(filter); append_filter_block(head, filter, ONE_INSTR); } void append_ret_trap(struct filter_block *head) { struct sock_filter *filter = new_instr_buf(ONE_INSTR); set_bpf_ret_trap(filter); append_filter_block(head, filter, ONE_INSTR); } void append_ret_errno(struct filter_block *head, int errno_val) { struct sock_filter *filter = new_instr_buf(ONE_INSTR); set_bpf_ret_errno(filter, errno_val); append_filter_block(head, filter, ONE_INSTR); } void append_ret_log(struct filter_block *head) { struct sock_filter *filter = new_instr_buf(ONE_INSTR); set_bpf_ret_log(filter); append_filter_block(head, filter, ONE_INSTR); } void append_allow_syscall(struct filter_block *head, int nr) { struct sock_filter *filter = new_instr_buf(ALLOW_SYSCALL_LEN); size_t len = bpf_allow_syscall(filter, nr); if (len != ALLOW_SYSCALL_LEN) die("error building syscall number comparison"); append_filter_block(head, filter, len); } void copy_parser_state(struct parser_state *src, struct parser_state *dest) { const char *filename = strdup(src->filename); if (!filename) pdie("strdup(src->filename) failed"); dest->line_number = src->line_number; dest->filename = filename; } /* * Inserts the current state into the array of previous syscall states at the * index |ind| if it is a newly encountered syscall. Returns true if it is a * newly encountered syscall and false if it is a duplicate. */ bool insert_and_check_duplicate_syscall(struct parser_state **previous_syscalls, struct parser_state *state, size_t ind) { if (ind >= get_num_syscalls()) { die("syscall index %zu out of range: %zu total syscalls", ind, get_num_syscalls()); } struct parser_state *prev_state_ptr = previous_syscalls[ind]; if (prev_state_ptr == NULL) { previous_syscalls[ind] = calloc(1, sizeof(struct parser_state)); if (!previous_syscalls[ind]) die("could not allocate parser_state buffer"); copy_parser_state(state, previous_syscalls[ind]); return true; } return false; } static void allow_selected_syscalls(struct filter_block *head, const char *const *allowlist, size_t allowlist_len, bool log_additions) { unsigned int i; for (i = 0; i < allowlist_len; i++) { if (log_additions) warn("allowing syscall: %s", allowlist[i]); append_allow_syscall(head, lookup_syscall(allowlist[i], NULL)); } } unsigned int get_label_id(struct bpf_labels *labels, const char *label_str) { int label_id = bpf_label_id(labels, label_str); if (label_id < 0) die("could not allocate BPF label string"); return label_id; } unsigned int group_end_lbl(struct bpf_labels *labels, int nr, int idx) { char lbl_str[MAX_BPF_LABEL_LEN]; snprintf(lbl_str, MAX_BPF_LABEL_LEN, "%d_%d_end", nr, idx); return get_label_id(labels, lbl_str); } unsigned int success_lbl(struct bpf_labels *labels, int nr) { char lbl_str[MAX_BPF_LABEL_LEN]; snprintf(lbl_str, MAX_BPF_LABEL_LEN, "%d_success", nr); return get_label_id(labels, lbl_str); } int is_implicit_relative_path(const char *filename) { return filename[0] != '/' && (filename[0] != '.' || filename[1] != '/'); } int compile_atom(struct parser_state *state, struct filter_block *head, char *atom, struct bpf_labels *labels, int nr, int grp_idx) { /* Splits the atom. */ char *atom_ptr = NULL; char *argidx_str = strtok_r(atom, " ", &atom_ptr); if (argidx_str == NULL) { compiler_warn(state, "empty atom"); return -1; } char *operator_str = strtok_r(NULL, " ", &atom_ptr); if (operator_str == NULL) { compiler_warn(state, "invalid atom '%s'", argidx_str); return -1; } char *constant_str = strtok_r(NULL, " ", &atom_ptr); if (constant_str == NULL) { compiler_warn(state, "invalid atom '%s %s'", argidx_str, operator_str); return -1; } /* Checks that there are no extra tokens. */ const char *extra = strtok_r(NULL, " ", &atom_ptr); if (extra != NULL) { compiler_warn(state, "extra token '%s'", extra); return -1; } if (strncmp(argidx_str, "arg", 3)) { compiler_warn(state, "invalid argument token '%s'", argidx_str); return -1; } char *argidx_ptr; long int argidx = strtol(argidx_str + 3, &argidx_ptr, 10); /* * Checks that an actual argument index was parsed, * and that there was nothing left after the index. */ if (argidx_ptr == argidx_str + 3 || *argidx_ptr != '\0') { compiler_warn(state, "invalid argument index '%s'", argidx_str + 3); return -1; } int op = str_to_op(operator_str); if (op < MIN_OPERATOR) { compiler_warn(state, "invalid operator '%s'", operator_str); return -1; } char *constant_str_ptr; long int c = parse_constant(constant_str, &constant_str_ptr); if (constant_str_ptr == constant_str) { compiler_warn(state, "invalid constant '%s'", constant_str); return -1; } /* * Looks up the label for the end of the AND statement * this atom belongs to. */ unsigned int id = group_end_lbl(labels, nr, grp_idx); /* * Builds a BPF comparison between a syscall argument * and a constant. * The comparison lives inside an AND statement. * If the comparison succeeds, we continue * to the next comparison. * If this comparison fails, the whole AND statement * will fail, so we jump to the end of this AND statement. */ struct sock_filter *comp_block; size_t len = bpf_arg_comp(&comp_block, op, argidx, c, id); if (len == 0) return -1; append_filter_block(head, comp_block, len); return 0; } int compile_errno(struct parser_state *state, struct filter_block *head, char *ret_errno, enum block_action action) { char *errno_ptr = NULL; /* Splits the 'return' keyword and the actual errno value. */ char *ret_str = strtok_r(ret_errno, " ", &errno_ptr); if (!ret_str || strncmp(ret_str, "return", strlen("return"))) return -1; char *errno_val_str = strtok_r(NULL, " ", &errno_ptr); if (errno_val_str) { char *errno_val_ptr; int errno_val = parse_constant(errno_val_str, &errno_val_ptr); /* Checks to see if we parsed an actual errno. */ if (errno_val_ptr == errno_val_str || errno_val == -1) { compiler_warn(state, "invalid errno value '%s'", errno_val_ptr); return -1; } append_ret_errno(head, errno_val); } else { switch (action) { case ACTION_RET_KILL: append_ret_kill(head); break; case ACTION_RET_KILL_PROCESS: append_ret_kill_process(head); break; case ACTION_RET_TRAP: append_ret_trap(head); break; case ACTION_RET_LOG: compiler_warn(state, "invalid action: ACTION_RET_LOG"); return -1; } } return 0; } struct filter_block *compile_policy_line(struct parser_state *state, int nr, const char *policy_line, unsigned int entry_lbl_id, struct bpf_labels *labels, enum block_action action) { /* * |policy_line| should be an expression of the form: * "arg0 == 3 && arg1 == 5 || arg0 == 0x8" * * This is, an expression in DNF (disjunctive normal form); * a disjunction ('||') of one or more conjunctions ('&&') * of one or more atoms. * * Atoms are of the form "arg{DNUM} {OP} {NUM}" * where: * - DNUM is a decimal number. * - OP is an operator: ==, !=, & (flags set), or 'in' (inclusion). * - NUM is an octal, decimal, or hexadecimal number. * * When the syscall arguments make the expression true, * the syscall is allowed. If not, the process is killed. * * To block a syscall without killing the process, * |policy_line| can be of the form: * "return " * * This "return {NUM}" policy line will block the syscall, * make it return -1 and set |errno| to NUM. * * A regular policy line can also include a "return " clause, * separated by a semicolon (';'): * "arg0 == 3 && arg1 == 5 || arg0 == 0x8; return {NUM}" * * If the syscall arguments don't make the expression true, * the syscall will be blocked as above instead of killing the process. */ size_t len = 0; int grp_idx = 0; /* Checks for empty policy lines. */ if (strlen(policy_line) == 0) { compiler_warn(state, "empty policy line"); return NULL; } /* We will modify |policy_line|, so let's make a copy. */ char *line = strdup(policy_line); if (!line) return NULL; /* * We build the filter section as a collection of smaller * "filter blocks" linked together in a singly-linked list. */ struct filter_block *head = new_filter_block(); /* * Filter sections begin with a label where the main filter * will jump after checking the syscall number. */ struct sock_filter *entry_label = new_instr_buf(ONE_INSTR); set_bpf_lbl(entry_label, entry_lbl_id); append_filter_block(head, entry_label, ONE_INSTR); /* Checks whether we're unconditionally blocking this syscall. */ if (strncmp(line, "return", strlen("return")) == 0) { if (compile_errno(state, head, line, action) < 0) { free_block_list(head); free(line); return NULL; } free(line); return head; } /* Splits the optional "return " part. */ char *line_ptr; char *arg_filter = strtok_r(line, ";", &line_ptr); char *ret_errno = strtok_r(NULL, ";", &line_ptr); /* * Splits the policy line by '||' into conjunctions and each conjunction * by '&&' into atoms. */ char *arg_filter_str = arg_filter; char *group; while ((group = tokenize(&arg_filter_str, "||")) != NULL) { char *group_str = group; char *comp; while ((comp = tokenize(&group_str, "&&")) != NULL) { /* Compiles each atom into a BPF block. */ if (compile_atom(state, head, comp, labels, nr, grp_idx) < 0) { free_block_list(head); free(line); return NULL; } } /* * If the AND statement succeeds, we're done, * so jump to SUCCESS line. */ unsigned int id = success_lbl(labels, nr); struct sock_filter *group_end_block = new_instr_buf(TWO_INSTRS); len = set_bpf_jump_lbl(group_end_block, id); /* * The end of each AND statement falls after the * jump to SUCCESS. */ id = group_end_lbl(labels, nr, grp_idx++); len += set_bpf_lbl(group_end_block + len, id); append_filter_block(head, group_end_block, len); } /* * If no AND statements succeed, we end up here, * because we never jumped to SUCCESS. * If we have to return an errno, do it, * otherwise just kill the task. */ if (ret_errno) { if (compile_errno(state, head, ret_errno, action) < 0) { free_block_list(head); free(line); return NULL; } } else { switch (action) { case ACTION_RET_KILL: append_ret_kill(head); break; case ACTION_RET_KILL_PROCESS: append_ret_kill_process(head); break; case ACTION_RET_TRAP: append_ret_trap(head); break; case ACTION_RET_LOG: append_ret_log(head); break; } } /* * Every time the filter succeeds we jump to a predefined SUCCESS * label. Add that label and BPF RET_ALLOW code now. */ unsigned int id = success_lbl(labels, nr); struct sock_filter *success_block = new_instr_buf(TWO_INSTRS); len = set_bpf_lbl(success_block, id); len += set_bpf_ret_allow(success_block + len); append_filter_block(head, success_block, len); free(line); return head; } int parse_include_statement(struct parser_state *state, char *policy_line, unsigned int include_level, const char **ret_filename) { if (strncmp("@include", policy_line, strlen("@include")) != 0) { compiler_warn(state, "invalid statement '%s'", policy_line); return -1; } if (policy_line[strlen("@include")] != ' ') { compiler_warn(state, "invalid include statement '%s'", policy_line); return -1; } /* * Disallow nested includes: only the initial policy file can have * @include statements. * Nested includes are not currently necessary and make the policy * harder to understand. */ if (include_level > 0) { compiler_warn(state, "@include statement nested too deep"); return -1; } char *statement = policy_line; /* Discard "@include" token. */ (void)strsep(&statement, " "); /* * compile_filter() below receives a FILE*, so it's not trivial to open * included files relative to the initial policy filename. * To avoid mistakes, force the included file path to be absolute * (start with '/'), or to explicitly load the file relative to CWD by * using './'. */ const char *filename = statement; if (is_implicit_relative_path(filename)) { compiler_warn( state, "implicit relative path '%s' not supported, use './%s'", filename, filename); return -1; } *ret_filename = filename; return 0; } int compile_file(const char *filename, FILE *policy_file, struct filter_block *head, struct filter_block **arg_blocks, struct bpf_labels *labels, const struct filter_options *filteropts, struct parser_state **previous_syscalls, unsigned int include_level) { /* clang-format off */ struct parser_state state = { .filename = filename, .line_number = 0, }; /* clang-format on */ /* * Loop through all the lines in the policy file. * Build a jump table for the syscall number. * If the policy line has an arg filter, build the arg filter * as well. * Chain the filter sections together and dump them into * the final buffer at the end. */ attribute_cleanup_str char *line = NULL; size_t len = 0; int ret = 0; while (getmultiline(&line, &len, policy_file) != -1) { char *policy_line = line; policy_line = strip(policy_line); state.line_number++; /* Allow comments and empty lines. */ if (*policy_line == '#' || *policy_line == '\0') { /* Reuse |line| in the next getline() call. */ continue; } /* Allow @include and @frequency statements. */ if (*policy_line == '@') { const char *filename = NULL; /* Ignore @frequency statements. */ if (strncmp("@frequency", policy_line, strlen("@frequency")) == 0) { compiler_warn(&state, "ignored @frequency statement"); continue; } if (parse_include_statement(&state, policy_line, include_level, &filename) != 0) { compiler_warn( &state, "failed to parse include statement"); ret = -1; goto out; } attribute_cleanup_fp FILE *included_file = fopen(filename, "re"); if (included_file == NULL) { compiler_pwarn(&state, "fopen('%s') failed", filename); ret = -1; goto out; } if (compile_file(filename, included_file, head, arg_blocks, labels, filteropts, previous_syscalls, include_level + 1) == -1) { compiler_warn(&state, "'@include %s' failed", filename); ret = -1; goto out; } continue; } /* * If it's not a comment, or an empty line, or an @include * statement, treat |policy_line| as a regular policy line. */ char *syscall_name = strsep(&policy_line, ":"); if (policy_line == NULL) { warn("compile_file: malformed policy line, missing " "':'"); ret = -1; goto out; } policy_line = strip(policy_line); if (*policy_line == '\0') { compiler_warn(&state, "empty policy line"); ret = -1; goto out; } syscall_name = strip(syscall_name); size_t ind = 0; int nr = lookup_syscall(syscall_name, &ind); if (nr < 0) { compiler_warn(&state, "nonexistent syscall '%s'", syscall_name); if (filteropts->allow_logging) { /* * If we're logging failures, assume we're in a * debugging case and continue. * This is not super risky because an invalid * syscall name is likely caused by a typo or by * leftover lines from a different architecture. * In either case, not including a policy line * is equivalent to killing the process if the * syscall is made, so there's no added attack * surface. */ /* Reuse |line| in the next getline() call. */ continue; } ret = -1; goto out; } if (!insert_and_check_duplicate_syscall(previous_syscalls, &state, ind)) { if (!filteropts->allow_duplicate_syscalls) ret = -1; compiler_warn(&state, "syscall %s redefined here", lookup_syscall_name(nr)); compiler_warn(previous_syscalls[ind], "previous definition here"); } /* * For each syscall, add either a simple ALLOW, * or an arg filter block. */ if (streq(policy_line, "1")) { /* Add simple ALLOW. */ append_allow_syscall(head, nr); } else { /* * Create and jump to the label that will hold * the arg filter block. */ unsigned int id = bpf_label_id(labels, syscall_name); struct sock_filter *nr_comp = new_instr_buf(ALLOW_SYSCALL_LEN); bpf_allow_syscall_args(nr_comp, nr, id); append_filter_block(head, nr_comp, ALLOW_SYSCALL_LEN); /* Build the arg filter block. */ struct filter_block *block = compile_policy_line(&state, nr, policy_line, id, labels, filteropts->action); if (!block) { if (*arg_blocks) { free_block_list(*arg_blocks); *arg_blocks = NULL; } warn("could not allocate filter block"); ret = -1; goto out; } if (*arg_blocks) { extend_filter_block_list(*arg_blocks, block); } else { *arg_blocks = block; } } /* Reuse |line| in the next getline() call. */ } /* getline(3) returned -1. This can mean EOF or an error. */ if (!feof(policy_file)) { if (*arg_blocks) { free_block_list(*arg_blocks); *arg_blocks = NULL; } warn("getmultiline() failed"); ret = -1; } out: return ret; } int compile_filter(const char *filename, FILE *initial_file, struct sock_fprog *prog, const struct filter_options *filteropts) { int ret = 0; struct bpf_labels labels; labels.count = 0; if (!initial_file) { warn("compile_filter: |initial_file| is NULL"); return -1; } struct filter_block *head = new_filter_block(); struct filter_block *arg_blocks = NULL; /* * Create the data structure that will keep track of what system * calls we have already defined if the option is true. */ size_t num_syscalls = get_num_syscalls(); struct parser_state **previous_syscalls = calloc(num_syscalls, sizeof(*previous_syscalls)); /* Start filter by validating arch. */ struct sock_filter *valid_arch = new_instr_buf(ARCH_VALIDATION_LEN); size_t len = bpf_validate_arch(valid_arch); append_filter_block(head, valid_arch, len); /* Load syscall number. */ struct sock_filter *load_nr = new_instr_buf(ONE_INSTR); len = bpf_load_syscall_nr(load_nr); append_filter_block(head, load_nr, len); /* * On kernels without SECCOMP_RET_LOG, Minijail can attempt to write the * first failing syscall to syslog(3). In order for syslog(3) to work, * some syscalls need to be unconditionally allowed. */ if (filteropts->allow_syscalls_for_logging) allow_selected_syscalls(head, log_syscalls, log_syscalls_len, true /* log_additions */); if (filteropts->include_libc_compatibility_allowlist) allow_selected_syscalls(head, libc_compatibility_syscalls, libc_compatibility_syscalls_len, false /* log_additions */); if (compile_file(filename, initial_file, head, &arg_blocks, &labels, filteropts, previous_syscalls, 0 /* include_level */) != 0) { warn("compile_filter: compile_file() failed"); ret = -1; goto free_filter; } /* * If none of the syscalls match, either fall through to LOG, TRAP, or * KILL. */ switch (filteropts->action) { case ACTION_RET_KILL: append_ret_kill(head); break; case ACTION_RET_KILL_PROCESS: append_ret_kill_process(head); break; case ACTION_RET_TRAP: append_ret_trap(head); break; case ACTION_RET_LOG: if (filteropts->allow_logging) { append_ret_log(head); } else { warn("compile_filter: cannot use RET_LOG without " "allowing logging"); ret = -1; goto free_filter; } break; default: warn("compile_filter: invalid log action %d", filteropts->action); ret = -1; goto free_filter; } /* Allocate the final buffer, now that we know its size. */ size_t final_filter_len = head->total_len + (arg_blocks ? arg_blocks->total_len : 0); if (final_filter_len > BPF_MAXINSNS) { ret = -1; goto free_filter; } struct sock_filter *final_filter = calloc(final_filter_len, sizeof(struct sock_filter)); if (!final_filter) die("could not allocate final BPF filter"); if (flatten_block_list(head, final_filter, 0, final_filter_len) < 0) { free(final_filter); ret = -1; goto free_filter; } if (flatten_block_list(arg_blocks, final_filter, head->total_len, final_filter_len) < 0) { free(final_filter); ret = -1; goto free_filter; } if (bpf_resolve_jumps(&labels, final_filter, final_filter_len) < 0) { free(final_filter); ret = -1; goto free_filter; } prog->filter = final_filter; prog->len = final_filter_len; free_filter: free_block_list(head); free_block_list(arg_blocks); free_label_strings(&labels); free_previous_syscalls(previous_syscalls); return ret; } int flatten_block_list(struct filter_block *head, struct sock_filter *filter, size_t index, size_t cap) { size_t _index = index; struct filter_block *curr; size_t i; for (curr = head; curr; curr = curr->next) { for (i = 0; i < curr->len; i++) { if (_index >= cap) return -1; filter[_index++] = curr->instrs[i]; } } return 0; } void free_block_list(struct filter_block *head) { struct filter_block *current, *prev; current = head; while (current) { free(current->instrs); prev = current; current = current->next; free(prev); } } void free_previous_syscalls(struct parser_state **previous_syscalls) { size_t num_syscalls = get_num_syscalls(); for (size_t i = 0; i < num_syscalls; i++) { struct parser_state *state = previous_syscalls[i]; if (state) { free((char *)state->filename); free(state); } } free(previous_syscalls); } minijail-2026.05.18+git20261016.8787d1a/syscall_filter.h000066400000000000000000000041311526451555500215220ustar00rootroot00000000000000/* syscall_filter.h * Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Syscall filter functions. */ #ifndef SYSCALL_FILTER_H #define SYSCALL_FILTER_H #include #include "bpf.h" #ifdef __cplusplus extern "C" { #endif struct filter_block { struct sock_filter *instrs; size_t len; struct filter_block *next; struct filter_block *last; size_t total_len; }; struct parser_state { const char *filename; size_t line_number; }; enum block_action { ACTION_RET_KILL = 0, ACTION_RET_TRAP, ACTION_RET_LOG, ACTION_RET_KILL_PROCESS, }; struct filter_options { enum block_action action; int allow_logging; int allow_syscalls_for_logging; bool allow_duplicate_syscalls; bool include_libc_compatibility_allowlist; }; struct bpf_labels; struct filter_block *compile_policy_line(struct parser_state *state, int nr, const char *policy_line, unsigned int label_id, struct bpf_labels *labels, enum block_action action); int compile_file(const char *filename, FILE *policy_file, struct filter_block *head, struct filter_block **arg_blocks, struct bpf_labels *labels, const struct filter_options *filteropts, struct parser_state **previous_syscalls, unsigned int include_level); int compile_filter(const char *filename, FILE *policy_file, struct sock_fprog *prog, const struct filter_options *filteropts); struct filter_block *new_filter_block(void); int flatten_block_list(struct filter_block *head, struct sock_filter *filter, size_t index, size_t cap); void free_block_list(struct filter_block *head); void free_previous_syscalls(struct parser_state **previous_syscalls); int seccomp_can_softfail(void); static inline bool allow_duplicate_syscalls(void) { #if defined(ALLOW_DUPLICATE_SYSCALLS) return true; #endif return false; } static inline bool allow_libc_compatibility_syscalls(void) { #if defined(ALLOW_LIBC_COMPATIBILITY_SYSCALLS) return true; #endif return false; } #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* SYSCALL_FILTER_H */ minijail-2026.05.18+git20261016.8787d1a/syscall_filter_unittest.cc000066400000000000000000001714461526451555500236350ustar00rootroot00000000000000/* Copyright 2016 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test syscall filtering using gtest. */ #include #include #include /* For O_WRONLY. */ #include #include #include #include "bpf.h" #include "syscall_filter.h" #include "syscall_filter_unittest_macros.h" #include "test_util.h" #include "util.h" namespace { // Simple C++ -> C wrappers to simplify test code. enum ret_trap { USE_RET_KILL = 0, USE_RET_TRAP = 1, }; enum use_logging { NO_LOGGING = 0, USE_SIGSYS_LOGGING = 1, USE_RET_LOG_LOGGING = 2, }; int test_compile_filter(const std::string& filename, FILE* policy_file, struct sock_fprog* prog, enum block_action action = ACTION_RET_KILL, enum use_logging allow_logging = NO_LOGGING, bool allow_dup_syscalls = true, bool include_libc_compatibility_allowlist = false) { struct filter_options filteropts { .action = action, .allow_logging = allow_logging != NO_LOGGING, .allow_syscalls_for_logging = allow_logging == USE_SIGSYS_LOGGING, .allow_duplicate_syscalls = allow_dup_syscalls, .include_libc_compatibility_allowlist = include_libc_compatibility_allowlist, }; return compile_filter(filename.c_str(), policy_file, prog, &filteropts); } int test_compile_file(std::string filename, FILE* policy_file, struct filter_block* head, struct filter_block** arg_blocks, struct bpf_labels* labels, enum block_action action = ACTION_RET_KILL, enum use_logging allow_logging = NO_LOGGING, unsigned int include_level = 0, bool allow_dup_syscalls = false) { struct filter_options filteropts { .action = action, .allow_logging = allow_logging != NO_LOGGING, .allow_syscalls_for_logging = allow_logging == USE_SIGSYS_LOGGING, .allow_duplicate_syscalls = allow_dup_syscalls, .include_libc_compatibility_allowlist = false, }; size_t num_syscalls = get_num_syscalls(); struct parser_state** previous_syscalls = (struct parser_state**)calloc(num_syscalls, sizeof(struct parser_state*)); int res = compile_file(filename.c_str(), policy_file, head, arg_blocks, labels, &filteropts, previous_syscalls, include_level); free_previous_syscalls(previous_syscalls); return res; } struct filter_block* test_compile_policy_line( struct parser_state* state, int nr, const std::string& policy_line, unsigned int label_id, struct bpf_labels* labels, enum block_action action = ACTION_RET_KILL) { return compile_policy_line(state, nr, policy_line.c_str(), label_id, labels, action); } std::span get_libc_compatibility_allowlist() { return std::span(libc_compatibility_syscalls, libc_compatibility_syscalls_len); } } // namespace /* Test that setting one BPF instruction works. */ TEST(bpf, set_bpf_instr) { struct sock_filter instr; unsigned char code = BPF_LD + BPF_W + BPF_ABS; unsigned int k = 4; unsigned char jt = 1, jf = 2; size_t len = set_bpf_instr(&instr, code, k, jt, jf); EXPECT_EQ(len, 1U); EXPECT_EQ_BLOCK(&instr, code, k, jt, jf); } TEST(bpf, bpf_load_arg) { struct sock_filter load_arg[BPF_LOAD_ARG_LEN]; const int argidx = 1; size_t len = bpf_load_arg(load_arg, argidx); EXPECT_EQ(len, BPF_LOAD_ARG_LEN); #if defined(BITS32) EXPECT_EQ_STMT(&load_arg[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx)); #elif defined(BITS64) EXPECT_EQ_STMT(&load_arg[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx)); EXPECT_EQ_STMT(&load_arg[1], BPF_ST, 0); EXPECT_EQ_STMT(&load_arg[2], BPF_LD + BPF_W + BPF_ABS, HI_ARG(argidx)); EXPECT_EQ_STMT(&load_arg[3], BPF_ST, 1); #endif } TEST(bpf, bpf_comp_jeq) { struct sock_filter comp_jeq[BPF_COMP_LEN]; unsigned long c = 1; unsigned char jt = 1; unsigned char jf = 2; size_t len = bpf_comp_jeq(comp_jeq, c, jt, jf); EXPECT_EQ(len, BPF_COMP_LEN); #if defined(BITS32) EXPECT_EQ_BLOCK(&comp_jeq[0], BPF_JMP + BPF_JEQ + BPF_K, c, jt, jf); #elif defined(BITS64) EXPECT_EQ_BLOCK(&comp_jeq[0], BPF_JMP + BPF_JEQ + BPF_K, 0, 0, jf + 2); EXPECT_EQ_STMT(&comp_jeq[1], BPF_LD + BPF_MEM, 0); EXPECT_EQ_BLOCK(&comp_jeq[2], BPF_JMP + BPF_JEQ + BPF_K, c, jt, jf); #endif } TEST(bpf, bpf_comp_jset) { struct sock_filter comp_jset[BPF_COMP_LEN]; unsigned long mask = (1UL << (sizeof(unsigned long) * 8 - 1)) | O_WRONLY; unsigned char jt = 1; unsigned char jf = 2; size_t len = bpf_comp_jset(comp_jset, mask, jt, jf); EXPECT_EQ(len, BPF_COMP_LEN); #if defined(BITS32) EXPECT_EQ_BLOCK(&comp_jset[0], BPF_JMP + BPF_JSET + BPF_K, mask, jt, jf); #elif defined(BITS64) EXPECT_EQ_BLOCK(&comp_jset[0], BPF_JMP + BPF_JSET + BPF_K, 0x80000000, jt + 2, 0); EXPECT_EQ_STMT(&comp_jset[1], BPF_LD + BPF_MEM, 0); EXPECT_EQ_BLOCK(&comp_jset[2], BPF_JMP + BPF_JSET + BPF_K, O_WRONLY, jt, jf); #endif } TEST(bpf, bpf_comp_jin) { struct sock_filter comp_jin[BPF_COMP_LEN]; unsigned long mask = (1UL << (sizeof(unsigned long) * 8 - 1)) | O_WRONLY; unsigned char jt = 10; unsigned char jf = 20; size_t len = bpf_comp_jin(comp_jin, mask, jt, jf); EXPECT_EQ(len, BPF_COMP_LEN); #if defined(BITS32) EXPECT_EQ_BLOCK(&comp_jin[0], BPF_JMP + BPF_JSET + BPF_K, ~mask, jf, jt); #elif defined(BITS64) EXPECT_EQ_BLOCK(&comp_jin[0], BPF_JMP + BPF_JSET + BPF_K, 0x7FFFFFFF, jf + 2, 0); EXPECT_EQ_STMT(&comp_jin[1], BPF_LD + BPF_MEM, 0); EXPECT_EQ_BLOCK(&comp_jin[2], BPF_JMP + BPF_JSET + BPF_K, ~O_WRONLY, jf, jt); #endif } TEST(bpf, bpf_arg_comp) { struct sock_filter* arg_comp; int op = EQ; const int argidx = 1; unsigned long c = 3; unsigned int label_id = 0; size_t len = bpf_arg_comp(&arg_comp, op, argidx, c, label_id); EXPECT_EQ(len, BPF_ARG_COMP_LEN + 1); #if defined(BITS32) EXPECT_EQ_STMT(&arg_comp[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx)); EXPECT_EQ_BLOCK(&arg_comp[1], BPF_JMP + BPF_JEQ + BPF_K, c, 1, 0); EXPECT_JUMP_LBL(&arg_comp[2]); #elif defined(BITS64) EXPECT_EQ_STMT(&arg_comp[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx)); EXPECT_EQ_STMT(&arg_comp[1], BPF_ST, 0); EXPECT_EQ_STMT(&arg_comp[2], BPF_LD + BPF_W + BPF_ABS, HI_ARG(argidx)); EXPECT_EQ_STMT(&arg_comp[3], BPF_ST, 1); EXPECT_EQ_BLOCK(&arg_comp[4], BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 2); EXPECT_EQ_STMT(&arg_comp[5], BPF_LD + BPF_MEM, 0); EXPECT_EQ_BLOCK(&arg_comp[6], BPF_JMP + BPF_JEQ + BPF_K, c, 1, 0); EXPECT_JUMP_LBL(&arg_comp[7]); #endif free(arg_comp); } TEST(bpf, bpf_arg_comp_invalid) { struct sock_filter* arg_comp; int op = MIN_OPERATOR + 32; const int argidx = 1; unsigned long c = 3; unsigned int label_id = 0; size_t len = bpf_arg_comp(&arg_comp, op, argidx, c, label_id); EXPECT_EQ(len, 0); free(arg_comp); } TEST(bpf, bpf_validate_arch) { struct sock_filter validate_arch[ARCH_VALIDATION_LEN]; size_t len = bpf_validate_arch(validate_arch); EXPECT_EQ(len, ARCH_VALIDATION_LEN); EXPECT_ARCH_VALIDATION(validate_arch); } TEST(bpf, bpf_allow_syscall) { struct sock_filter allow_syscall[ALLOW_SYSCALL_LEN]; int nr = 1; size_t len = bpf_allow_syscall(allow_syscall, nr); EXPECT_EQ(len, ALLOW_SYSCALL_LEN); EXPECT_ALLOW_SYSCALL(allow_syscall, nr); } TEST(bpf, bpf_allow_syscall_args) { struct sock_filter allow_syscall[ALLOW_SYSCALL_LEN]; int nr = 1; unsigned int id = 1024; size_t len = bpf_allow_syscall_args(allow_syscall, nr, id); EXPECT_EQ(len, ALLOW_SYSCALL_LEN); EXPECT_ALLOW_SYSCALL_ARGS(allow_syscall, nr, id, JUMP_JT, JUMP_JF); } class BpfLabelTest : public ::testing::Test { protected: virtual void SetUp() { labels_.count = 0; } virtual void TearDown() { free_label_strings(&labels_); } struct bpf_labels labels_; }; TEST_F(BpfLabelTest, zero_length_filter) { int res = bpf_resolve_jumps(&labels_, nullptr, 0); EXPECT_EQ(res, 0); EXPECT_EQ(labels_.count, 0U); } TEST_F(BpfLabelTest, single_label) { struct sock_filter test_label[1]; int id = bpf_label_id(&labels_, "test"); set_bpf_lbl(test_label, id); int res = bpf_resolve_jumps(&labels_, test_label, 1); EXPECT_EQ(res, 0); EXPECT_EQ(labels_.count, 1U); } TEST_F(BpfLabelTest, repeated_label) { struct sock_filter test_label[2]; int id = bpf_label_id(&labels_, "test"); set_bpf_lbl(&test_label[0], id); set_bpf_lbl(&test_label[1], id); int res = bpf_resolve_jumps(&labels_, test_label, 2); EXPECT_EQ(res, -1); } TEST_F(BpfLabelTest, jump_with_no_label) { struct sock_filter test_jump[1]; set_bpf_jump_lbl(test_jump, 14831); int res = bpf_resolve_jumps(&labels_, test_jump, 1); EXPECT_EQ(res, -1); } TEST_F(BpfLabelTest, jump_to_valid_label) { struct sock_filter test_jump[2]; int id = bpf_label_id(&labels_, "test"); set_bpf_jump_lbl(&test_jump[0], id); set_bpf_lbl(&test_jump[1], id); int res = bpf_resolve_jumps(&labels_, test_jump, 2); EXPECT_EQ(res, 0); EXPECT_EQ(labels_.count, 1U); } TEST_F(BpfLabelTest, jump_to_invalid_label) { struct sock_filter test_jump[2]; int id = bpf_label_id(&labels_, "test"); set_bpf_jump_lbl(&test_jump[0], id + 1); set_bpf_lbl(&test_jump[1], id); int res = bpf_resolve_jumps(&labels_, test_jump, 2); EXPECT_EQ(res, -1); } TEST_F(BpfLabelTest, jump_to_unresolved_label) { struct sock_filter test_jump[2]; int id = bpf_label_id(&labels_, "test"); /* Notice the order of the instructions is reversed. */ set_bpf_lbl(&test_jump[0], id); set_bpf_jump_lbl(&test_jump[1], id); int res = bpf_resolve_jumps(&labels_, test_jump, 2); EXPECT_EQ(res, -1); } TEST_F(BpfLabelTest, too_many_labels) { unsigned int i; char label[20]; for (i = 0; i < BPF_LABELS_MAX; i++) { snprintf(label, 20, "test%u", i); (void)bpf_label_id(&labels_, label); } int id = bpf_label_id(&labels_, "test"); /* Insertion failed... */ EXPECT_EQ(id, -1); /* ... because the label lookup table is full. */ EXPECT_EQ(labels_.count, BPF_LABELS_MAX); } class ArgFilterTest : public ::testing::Test { protected: virtual void SetUp() { labels_.count = 0; state_.filename = "policy"; state_.line_number = 1; } virtual void TearDown() { free_label_strings(&labels_); } struct bpf_labels labels_; int nr_ = 1; unsigned int id_ = 0; struct parser_state state_; }; TEST_F(ArgFilterTest, empty_atom) { std::string fragment = ""; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, whitespace_atom) { std::string fragment = "\t "; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, no_comparison) { std::string fragment = "arg0"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, no_constant) { std::string fragment = "arg0 =="; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, arg0_equals) { std::string fragment = "arg0 == 0"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ curr_block = curr_block->next; EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is SECCOMP_RET_KILL. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, arg0_equals_trap) { std::string fragment = "arg0 == 0"; struct filter_block* block = test_compile_policy_line( &state_, nr_, fragment, id_, &labels_, ACTION_RET_TRAP); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ curr_block = curr_block->next; EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is SECCOMP_RET_TRAP. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_TRAP(curr_block); /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, arg0_equals_log) { std::string fragment = "arg0 == 0"; struct filter_block* block = test_compile_policy_line( &state_, nr_, fragment, id_, &labels_, ACTION_RET_LOG); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ curr_block = curr_block->next; EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is SECCOMP_RET_LOG. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_LOG(curr_block); /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, arg0_short_gt_ge_comparisons) { for (const std::string fragment : {"arg1 < 0xff", "arg1 <= 0xff", "arg1 > 0xff", "arg1 >= 0xff"}) { struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_SHORT_GT_GE_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); // First block is a label. struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); // Second block is a short gt/ge comparison. curr_block = curr_block->next; EXPECT_SHORT_GT_GE_COMP(curr_block); // Third block is a jump and a label (end of AND group). curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); // Fourth block is SECCOMP_RET_KILL. curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); // Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } } #if defined(BITS64) TEST_F(ArgFilterTest, arg0_long_gt_ge_comparisons) { for (const std::string fragment : {"arg1 < 0xbadc0ffee0ddf00d", "arg1 <= 0xbadc0ffee0ddf00d", "arg1 > 0xbadc0ffee0ddf00d", "arg1 >= 0xbadc0ffee0ddf00d"}) { struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_GT_GE_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); // First block is a label. struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); // Second block is a gt/ge comparison. curr_block = curr_block->next; EXPECT_GT_GE_COMP(curr_block); // Third block is a jump and a label (end of AND group). curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); // Fourth block is SECCOMP_RET_KILL. curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); // Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } } #endif TEST_F(ArgFilterTest, arg0_mask) { std::string fragment = "arg1 & O_RDWR"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ curr_block = curr_block->next; EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is SECCOMP_RET_KILL. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, arg0_flag_set_inclusion) { std::string fragment = "arg0 in O_RDONLY|O_CREAT"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is SECCOMP_RET_KILL. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, arg0_eq_mask) { std::string fragment = "arg1 == O_WRONLY|O_CREAT"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); EXPECT_EQ(curr_block->instrs[BPF_ARG_COMP_LEN - 1].k, (unsigned int)(O_WRONLY | O_CREAT)); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is SECCOMP_RET_KILL. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, and_or) { std::string fragment = "arg0 == 0 && arg1 == 0 || arg0 == 1"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + 3 * (BPF_ARG_COMP_LEN + 1) + 2 + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison ("arg0 == 0"). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Third block is a comparison ("arg1 == 0"). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Fourth block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fifth block is a comparison ("arg0 == 1"). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Sixth block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Seventh block is SECCOMP_RET_KILL. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); /* Eigth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, ret_errno) { std::string fragment = "arg0 == 0 || arg0 == 1; return 1"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + 2 * (BPF_ARG_COMP_LEN + 1) + 2 + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison ("arg0 == 0"). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is a comparison ("arg0 == 1"). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Fifth block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Sixth block is SECCOMP_RET_ERRNO. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_EQ_STMT(curr_block->instrs, BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (1 & SECCOMP_RET_DATA)); /* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, unconditional_errno) { std::string fragment = "return 1"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is SECCOMP_RET_ERRNO. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_EQ_STMT(curr_block->instrs, BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (1 & SECCOMP_RET_DATA)); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, invalid_arg_token) { std::string fragment = "org0 == 0"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, invalid_arg_number) { std::string fragment = "argnn == 0"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, extra_chars_in_arg_token) { std::string fragment = "arg0n == 0"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, invalid_operator) { std::string fragment = "arg0 invalidop 0"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, invalid_constant) { std::string fragment = "arg0 == INVALIDCONSTANT"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, extra_tokens) { std::string fragment = "arg0 == 0 EXTRATOKEN"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, invalid_errno) { std::string fragment = "arg0 == 0 && arg1 == 1; return errno"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_EQ(block, nullptr); } TEST_F(ArgFilterTest, log_no_ret_error) { std::string fragment = "arg0 == 0"; struct filter_block* block = test_compile_policy_line( &state_, nr_, fragment, id_, &labels_, ACTION_RET_TRAP); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is SECCOMP_RET_TRAP, with no errno. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_TRAP(curr_block); /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, log_bad_ret_error) { std::string fragment = "arg0 == 0; return"; struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison ("arg0 == 0"). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* * Sixth block is NOT SECCOMP_RET_ERRNO, it should be SECCOMP_RET_KILL. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); /* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } TEST_F(ArgFilterTest, no_log_bad_ret_error) { std::string fragment = "arg0 == 0; return"; struct filter_block* block = test_compile_policy_line( &state_, nr_, fragment, id_, &labels_, ACTION_RET_TRAP); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); /* First block is a label. */ struct filter_block* curr_block = block; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_LBL(curr_block->instrs); /* Second block is a comparison ("arg0 == 0"). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* * Sixth block is *not* SECCOMP_RET_ERRNO, it should be * SECCOMP_RET_TRAP. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_TRAP(curr_block); /* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); free_block_list(block); } namespace { class FileTest : public ::testing::Test { protected: virtual void SetUp() { labels_.count = 0; head_ = new_filter_block(); arg_blocks_ = nullptr; } virtual void TearDown() { free_label_strings(&labels_); free_block_list(head_); free_block_list(arg_blocks_); } struct bpf_labels labels_; struct filter_block* head_; struct filter_block* arg_blocks_; }; } // namespace TEST_F(FileTest, malformed_policy) { std::string policy = "malformed"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_); fclose(policy_file); /* * Policy is malformed, but process should not crash. */ ASSERT_EQ(res, -1); } TEST_F(FileTest, double_free_on_compile_error) { std::string policy = "read:arg0 == 0\n" "write:0"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_); fclose(policy_file); /* * Policy is malformed, but process should not crash. */ ASSERT_EQ(res, -1); } TEST_F(FileTest, invalid_return) { std::string policy = "read:arg0 == 0; ;"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_); fclose(policy_file); /* * Policy is malformed, but process should not crash. */ ASSERT_EQ(res, -1); } TEST_F(FileTest, seccomp_global_allowlist) { std::string policy = ""; FILE *policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); struct sock_fprog actual; int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL, NO_LOGGING, false /* allow_dup_syscalls */, true /* include_libc_compatibility_allowlist */); fclose(policy_file); ASSERT_EQ(res, 0); std::span allowlist = get_libc_compatibility_allowlist(); size_t offset_after_allowlist = ARCH_VALIDATION_LEN + 1 + 2 * allowlist.size(); EXPECT_EQ(actual.len, offset_after_allowlist + 1); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); for (size_t i = 0; i < allowlist.size(); ++i) { int allowlist_entry_nr = lookup_syscall(allowlist[i], nullptr); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1 + 2 * i, allowlist_entry_nr); } EXPECT_EQ_STMT(actual.filter + offset_after_allowlist, BPF_RET + BPF_K, SECCOMP_RET_KILL); free(actual.filter); } TEST_F(FileTest, seccomp_global_allowlist_does_not_conflict) { std::string policy; for (const char *entry : get_libc_compatibility_allowlist()) { policy += entry; policy += ": 1\n"; } FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); struct sock_fprog actual; int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL, NO_LOGGING, false /* allow_dup_syscalls */, true /* include_libc_compatibility_allowlist */); fclose(policy_file); ASSERT_EQ(res, 0); std::span allowlist = get_libc_compatibility_allowlist(); /* NOTE: due to how the global allowlist is added, this results in duplicate * allowlist entries in `actual` when both the global allowlist & the given * policy share a syscall. This is suboptimal, but the existence of global * allowlist entries is highly discouraged & the list should remain empty most * of the time. When it does contain entries, it should generally contain * fewer than five. */ size_t offset_after_allowlist = ARCH_VALIDATION_LEN + 1 + 4 * allowlist.size(); EXPECT_EQ(actual.len, offset_after_allowlist + 1); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); for (size_t i = 0; i < allowlist.size() * 2; ++i) { int allowlist_entry_nr = lookup_syscall(allowlist[i % allowlist.size()], nullptr); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1 + 2 * i, allowlist_entry_nr); } EXPECT_EQ_STMT(actual.filter + offset_after_allowlist, BPF_RET + BPF_K, SECCOMP_RET_KILL); free(actual.filter); } TEST_F(FileTest, seccomp_mode1) { std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_); fclose(policy_file); /* * Checks return value and that the blocks only allow expected syscalls. */ ASSERT_EQ(res, 0); struct filter_block* curr_block = head_; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_read); curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_write); curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_rt_sigreturn); curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_exit); EXPECT_EQ(curr_block->next, nullptr); } TEST_F(FileTest, seccomp_read) { std::string policy = "read: arg0 == 0\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; const int LABEL_ID = 0; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_); fclose(policy_file); /* * Checks return value, that the blocks only allow expected syscalls, and that * labels between |head_| and |arg_blocks_| match. */ ASSERT_EQ(res, 0); struct filter_block* curr_block = head_; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL_ARGS(curr_block->instrs, __NR_read, LABEL_ID, JUMP_JT, JUMP_JF); curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_write); curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_rt_sigreturn); curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_exit); ASSERT_NE(arg_blocks_, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(arg_blocks_->total_len, exp_total_len); /* First block is a label. */ curr_block = arg_blocks_; ASSERT_NE(curr_block, nullptr); EXPECT_EQ(curr_block->len, 1U); EXPECT_ACTUAL_LBL(curr_block->instrs, LABEL_ID); /* Second block is a comparison. */ curr_block = curr_block->next; EXPECT_COMP(curr_block); /* Third block is a jump and a label (end of AND group). */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_GROUP_END(curr_block); /* Fourth block is SECCOMP_RET_KILL. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_KILL(curr_block); /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW(curr_block); EXPECT_EQ(curr_block->next, nullptr); } TEST_F(FileTest, multiline) { std::string policy = "read:\\\n1\n" "openat:arg0 \\\nin\\\n \\\n5"; const int LABEL_ID = 0; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_); fclose(policy_file); /* * Policy should be valid. */ ASSERT_EQ(res, 0); /* First block is the read. */ struct filter_block* curr_block = head_; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_read); /* Second block is the open. */ curr_block = curr_block->next; ASSERT_NE(curr_block, nullptr); EXPECT_ALLOW_SYSCALL_ARGS(curr_block->instrs, __NR_openat, LABEL_ID, JUMP_JT, JUMP_JF); EXPECT_EQ(curr_block->next, nullptr); } TEST(FilterTest, seccomp_mode1) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, and * only allows expected syscalls. */ ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, 13); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K, SECCOMP_RET_KILL); free(actual.filter); } TEST(FilterTest, seccomp_mode1_with_check) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter( "policy", policy_file, &actual, ACTION_RET_KILL, NO_LOGGING, false /* allow duplicate syscalls */); fclose(policy_file); /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, and * only allows expected syscalls. */ ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, 13); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K, SECCOMP_RET_KILL); free(actual.filter); } /* * This fails even with allow_duplicate_syscalls set to true because the * creation of labels for the arguments causes conflicts which cause the * compile_filter function to fail. */ TEST(FilterTest, duplicate_read_with_args) { struct sock_fprog actual; std::string policy = "read: arg0 == 0\n" "read: arg1 == 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_EQ(res, -1); } /* * This does not fail because only one instance of read defines an argument. */ TEST(FilterTest, duplicate_read_with_one_arg) { struct sock_fprog actual; std::string policy = "read: arg0 == 0\n" "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); /* TODO: Don't know how to generate a correct value to validate the filter * that is generated. */ ASSERT_EQ(res, 0); free(actual.filter); } TEST(FilterTest, seccomp_mode1_trap) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP); fclose(policy_file); /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, and * only allows expected syscalls. */ ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, 13); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K, SECCOMP_RET_TRAP); free(actual.filter); } TEST(FilterTest, seccomp_mode1_log) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG, USE_RET_LOG_LOGGING); fclose(policy_file); /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, and * only allows expected syscalls. */ ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, 13); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K, SECCOMP_RET_LOG); free(actual.filter); } TEST(FilterTest, seccomp_mode1_log_fails) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG, NO_LOGGING); fclose(policy_file); /* * ACTION_RET_LOG should never be used without allowing logging. */ ASSERT_EQ(res, -1); } TEST(FilterTest, seccomp_mode1_ret_kill_process) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL_PROCESS, NO_LOGGING); fclose(policy_file); /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, and * only allows expected syscalls. */ ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, 13); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS); free(actual.filter); } TEST(FilterTest, seccomp_read_write) { struct sock_fprog actual; std::string policy = "read: arg0 == 0\n" "write: arg0 == 1 || arg0 == 2\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, and * only allows expected syscalls, jumping to correct arg filter * offsets. */ ASSERT_EQ(res, 0); size_t exp_total_len = 27 + 3 * (BPF_ARG_COMP_LEN + 1); EXPECT_EQ(actual.len, exp_total_len); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); EXPECT_ALLOW_SYSCALL_ARGS(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read, 7, 0, 0); EXPECT_ALLOW_SYSCALL_ARGS(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write, 12 + BPF_ARG_COMP_LEN, 0, 0); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K, SECCOMP_RET_KILL); free(actual.filter); } TEST(FilterTest, misplaced_whitespace) { struct sock_fprog actual; std::string policy = "read :1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); /* Checks return value and filter length. */ ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, ARCH_VALIDATION_LEN + 1 /* load syscall nr */ + ALLOW_SYSCALL_LEN + 1 /* ret kill */); free(actual.filter); } TEST(FilterTest, missing_atom) { struct sock_fprog actual; std::string policy = "open:\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_NE(res, 0); } TEST(FilterTest, whitespace_atom) { struct sock_fprog actual; std::string policy = "open:\t \n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_NE(res, 0); } TEST(FilterTest, invalid_name) { struct sock_fprog actual; std::string policy = "notasyscall: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_NE(res, 0); } TEST(FilterTest, invalid_arg) { struct sock_fprog actual; std::string policy = "open: argnn ==\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_NE(res, 0); } TEST(FilterTest, invalid_tokens) { struct sock_fprog actual; std::string policy = "read: arg0 == 1 |||| arg0 == 2\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_NE(res, 0); } TEST(FilterTest, nonexistent) { struct sock_fprog actual; int res = test_compile_filter("policy", nullptr, &actual); ASSERT_NE(res, 0); } TEST(FilterTest, log) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP, USE_SIGSYS_LOGGING); fclose(policy_file); size_t i; size_t index = 0; /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, only allows expected syscalls, * and returns TRAP on failure. * NOTE(jorgelo): the filter is longer since we add the syscalls needed * for logging. */ ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, 13 + 2 * log_syscalls_len); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); index = ARCH_VALIDATION_LEN + 1; for (i = 0; i < log_syscalls_len; i++) EXPECT_ALLOW_SYSCALL(actual.filter + (index + 2 * i), lookup_syscall(log_syscalls[i], NULL)); index += 2 * log_syscalls_len; EXPECT_ALLOW_SYSCALL(actual.filter + index, __NR_read); EXPECT_ALLOW_SYSCALL(actual.filter + index + 2, __NR_write); EXPECT_ALLOW_SYSCALL(actual.filter + index + 4, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(actual.filter + index + 6, __NR_exit); EXPECT_EQ_STMT(actual.filter + index + 8, BPF_RET + BPF_K, SECCOMP_RET_TRAP); free(actual.filter); } TEST(FilterTest, allow_log_but_kill) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL, USE_SIGSYS_LOGGING); fclose(policy_file); size_t i; size_t index = 0; /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, only allows expected syscalls, * and kills on failure. * NOTE(jorgelo): the filter is longer since we add the syscalls needed * for logging. */ ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, 13 + 2 * log_syscalls_len); EXPECT_ARCH_VALIDATION(actual.filter); EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); index = ARCH_VALIDATION_LEN + 1; for (i = 0; i < log_syscalls_len; i++) EXPECT_ALLOW_SYSCALL(actual.filter + (index + 2 * i), lookup_syscall(log_syscalls[i], NULL)); index += 2 * log_syscalls_len; EXPECT_ALLOW_SYSCALL(actual.filter + index, __NR_read); EXPECT_ALLOW_SYSCALL(actual.filter + index + 2, __NR_write); EXPECT_ALLOW_SYSCALL(actual.filter + index + 4, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(actual.filter + index + 6, __NR_exit); EXPECT_EQ_STMT(actual.filter + index + 8, BPF_RET + BPF_K, SECCOMP_RET_KILL); free(actual.filter); } TEST(FilterTest, frequency) { struct sock_fprog actual; std::string frequency = "@frequency ./path/is/ignored.frequency\n"; FILE* policy_file = write_to_pipe(frequency); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_EQ(res, 0); free(actual.filter); } TEST(FilterTest, include_invalid_token) { struct sock_fprog actual; std::string invalid_token = "@unclude ./test/seccomp.policy\n"; FILE* policy_file = write_to_pipe(invalid_token); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_NE(res, 0); } TEST(FilterTest, include_no_space) { struct sock_fprog actual; std::string no_space = "@includetest/seccomp.policy\n"; FILE* policy_file = write_to_pipe(no_space); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_NE(res, 0); } TEST(FilterTest, include_double_token) { struct sock_fprog actual; std::string double_token = "@includeinclude ./test/seccomp.policy\n"; FILE* policy_file = write_to_pipe(double_token); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_NE(res, 0); } TEST(FilterTest, include_no_file) { struct sock_fprog actual; std::string no_file = "@include\n"; FILE* policy_file = write_to_pipe(no_file); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_NE(res, 0); } TEST(FilterTest, include_space_no_file) { struct sock_fprog actual; std::string space_no_file = "@include \n"; FILE* policy_file = write_to_pipe(space_no_file); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_NE(res, 0); } TEST(FilterTest, include_implicit_relative_path) { struct sock_fprog actual; std::string implicit_relative_path = "@include test/seccomp.policy\n"; FILE* policy_file = write_to_pipe(implicit_relative_path); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_NE(res, 0); } TEST(FilterTest, include_extra_text) { struct sock_fprog actual; std::string extra_text = "@include /some/file: sneaky comment\n"; FILE* policy_file = write_to_pipe(extra_text); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_NE(res, 0); } TEST(FilterTest, include_split_filename) { struct sock_fprog actual; std::string split_filename = "@include /some/file:colon.policy\n"; FILE* policy_file = write_to_pipe(split_filename); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); EXPECT_NE(res, 0); } TEST(FilterTest, include_nonexistent_file) { struct sock_fprog actual; std::string include_policy = "@include ./nonexistent.policy\n"; FILE* policy_file = write_to_pipe(include_policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_NE(res, 0); } // TODO(jorgelo): Android unit tests don't currently support data files. // Re-enable by creating a temporary policy file at runtime. #if !defined(__ANDROID__) TEST(FilterTest, include) { struct sock_fprog compiled_plain; struct sock_fprog compiled_with_include; std::string policy_plain = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n"; FILE* file_plain = write_to_pipe(policy_plain); ASSERT_NE(file_plain, nullptr); int res_plain = test_compile_filter("policy", file_plain, &compiled_plain, ACTION_RET_KILL); fclose(file_plain); std::string policy_with_include = "@include " + source_path("test/seccomp.policy") + "\n"; FILE* file_with_include = write_to_pipe(policy_with_include); ASSERT_NE(file_with_include, nullptr); int res_with_include = test_compile_filter( "policy", file_with_include, &compiled_with_include, ACTION_RET_KILL); fclose(file_with_include); /* * Checks that filter length is the same for a plain policy and an equivalent * policy with an @include statement. Also checks that the filter generated * from the policy with an @include statement is exactly the same as one * generated from a plain policy. */ ASSERT_EQ(res_plain, 0); ASSERT_EQ(res_with_include, 0); EXPECT_EQ(compiled_plain.len, 13); EXPECT_EQ(compiled_with_include.len, 13); EXPECT_ARCH_VALIDATION(compiled_with_include.filter); EXPECT_EQ_STMT(compiled_with_include.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS, syscall_nr); EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 1, __NR_read); EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 3, __NR_write); EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 5, __NR_rt_sigreturn); EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 7, __NR_exit); EXPECT_EQ_STMT(compiled_with_include.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K, SECCOMP_RET_KILL); free(compiled_plain.filter); free(compiled_with_include.filter); } TEST(FilterTest, include_same_syscalls) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n" "@include " + source_path("test/seccomp.policy") + "\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, ARCH_VALIDATION_LEN + 1 /* load syscall nr */ + 2 * 8 /* check syscalls twice */ + 1 /* filter return */); free(actual.filter); } TEST(FilterTest, include_same_syscalls_with_check) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n" "@include " + source_path("test/seccomp.policy") + "\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter( "policy", policy_file, &actual, ACTION_RET_KILL, NO_LOGGING, false /* allow duplicate syscalls */); fclose(policy_file); ASSERT_EQ(res, -1); } TEST(FilterTest, include_two) { struct sock_fprog actual; std::string policy = "@include " + source_path("test/seccomp.policy") + "\n" + "@include " + source_path("test/seccomp.policy") + "\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_EQ(res, 0); EXPECT_EQ(actual.len, ARCH_VALIDATION_LEN + 1 /* load syscall nr */ + 2 * 8 /* check syscalls twice */ + 1 /* filter return */); free(actual.filter); } TEST(FilterTest, include_invalid_policy) { struct sock_fprog actual; std::string policy = "read: 1\n" "write: 1\n" "rt_sigreturn: 1\n" "exit: 1\n" "@include ./test/invalid_syscall_name.policy\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); /* Ensure the included (invalid) policy file exists. */ FILE* included_file = fopen(source_path("test/invalid_syscall_name.policy").c_str(), "re"); ASSERT_NE(included_file, nullptr); fclose(included_file); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_NE(res, 0); } TEST(FilterTest, include_nested) { struct sock_fprog actual; std::string policy = "@include ./test/nested.policy\n"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); /* Ensure the policy file exists. */ FILE* included_file = fopen(source_path("test/nested.policy").c_str(), "re"); ASSERT_NE(included_file, nullptr); fclose(included_file); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); ASSERT_NE(res, 0); } #endif // !__ANDROID__ TEST(FilterTest, error_cleanup_leak) { struct sock_fprog actual; std::string policy = "read:&&\n" "read:&&"; FILE* policy_file = write_to_pipe(policy); ASSERT_NE(policy_file, nullptr); int res = test_compile_filter("policy", policy_file, &actual); fclose(policy_file); /* * Policy is malformed, but process should not leak. */ ASSERT_EQ(res, -1); } minijail-2026.05.18+git20261016.8787d1a/syscall_filter_unittest_macros.h000066400000000000000000000141201526451555500250240ustar00rootroot00000000000000/* Copyright 2016 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SYSCALL_FILTER_UNITTEST_MACROS_H #define SYSCALL_FILTER_UNITTEST_MACROS_H #include "bpf.h" /* BPF testing macros. */ #define EXPECT_EQ_BLOCK(_block, _code, _k, _jt, _jf) \ do { \ EXPECT_EQ((_block)->code, _code); \ EXPECT_EQ((_block)->k, (unsigned int)(_k)); \ EXPECT_EQ((_block)->jt, _jt); \ EXPECT_EQ((_block)->jf, _jf); \ } while (0) #define EXPECT_EQ_STMT(_block, _code, _k) \ EXPECT_EQ_BLOCK(_block, _code, _k, 0, 0) #define EXPECT_COMP(_block) \ do { \ EXPECT_EQ((_block)->len, BPF_ARG_COMP_LEN + 1); \ EXPECT_EQ((_block)->instrs->code, BPF_LD + BPF_W + BPF_ABS); \ } while (0) #define EXPECT_GT_GE_COMP(_block) \ do { \ EXPECT_EQ((_block)->len, BPF_ARG_GT_GE_COMP_LEN + 1); \ EXPECT_EQ((_block)->instrs->code, BPF_LD + BPF_W + BPF_ABS); \ } while (0) #define EXPECT_SHORT_GT_GE_COMP(_block) \ do { \ EXPECT_EQ((_block)->len, BPF_ARG_SHORT_GT_GE_COMP_LEN + 1); \ EXPECT_EQ((_block)->instrs->code, BPF_LD + BPF_W + BPF_ABS); \ } while (0) #define EXPECT_LBL(_block) \ do { \ EXPECT_TRUE((_block)->code == (BPF_JMP + BPF_JA)); \ EXPECT_TRUE((_block)->jt == LABEL_JT); \ EXPECT_TRUE((_block)->jf == LABEL_JF); \ } while (0) #define EXPECT_ACTUAL_LBL(_block, _id) \ do { \ EXPECT_TRUE((_block)->code == (BPF_JMP + BPF_JA)); \ EXPECT_TRUE((_block)->k == (_id)); \ EXPECT_TRUE((_block)->jt == LABEL_JT); \ EXPECT_TRUE((_block)->jf == LABEL_JF); \ } while (0) #define EXPECT_JUMP_LBL(_block) \ do { \ EXPECT_EQ((_block)->code, BPF_JMP + BPF_JA); \ EXPECT_EQ((_block)->jt, JUMP_JT); \ EXPECT_EQ((_block)->jf, JUMP_JF); \ } while (0) #define EXPECT_GROUP_END(_block) \ do { \ EXPECT_EQ((_block)->len, 2U); \ EXPECT_JUMP_LBL(&(_block)->instrs[0]); \ EXPECT_LBL(&(_block)->instrs[1]); \ } while (0) #define EXPECT_KILL(_block) \ do { \ EXPECT_EQ((_block)->len, 1U); \ EXPECT_EQ_STMT((_block)->instrs, BPF_RET + BPF_K, \ SECCOMP_RET_KILL); \ } while (0) #define EXPECT_TRAP(_block) \ do { \ EXPECT_EQ((_block)->len, 1U); \ EXPECT_EQ_STMT((_block)->instrs, BPF_RET + BPF_K, \ SECCOMP_RET_TRAP); \ } while (0) #define EXPECT_LOG(_block) \ do { \ EXPECT_EQ((_block)->len, 1U); \ EXPECT_EQ_STMT((_block)->instrs, BPF_RET + BPF_K, \ SECCOMP_RET_LOG); \ } while (0) #define EXPECT_ALLOW(_block) \ do { \ EXPECT_EQ((_block)->len, 2U); \ EXPECT_LBL(&(_block)->instrs[0]); \ EXPECT_EQ_STMT(&(_block)->instrs[1], BPF_RET + BPF_K, \ SECCOMP_RET_ALLOW); \ } while (0) #define EXPECT_ARCH_VALIDATION(_filter) \ do { \ EXPECT_EQ_STMT(&(_filter)[0], BPF_LD + BPF_W + BPF_ABS, \ arch_nr); \ EXPECT_EQ_BLOCK(&(_filter)[1], BPF_JMP + BPF_JEQ + BPF_K, \ MINIJAIL_ARCH_NR, SKIP, NEXT); \ EXPECT_EQ_STMT(&(_filter)[2], BPF_RET + BPF_K, \ SECCOMP_RET_KILL); \ } while (0) #define EXPECT_ALLOW_SYSCALL(_filter, _nr) \ do { \ EXPECT_EQ_BLOCK(&(_filter)[0], BPF_JMP + BPF_JEQ + BPF_K, \ (_nr), NEXT, SKIP); \ EXPECT_EQ_STMT(&(_filter)[1], BPF_RET + BPF_K, \ SECCOMP_RET_ALLOW); \ } while (0) #define EXPECT_ALLOW_SYSCALL_ARGS(_filter, _nr, _id, _jt, _jf) \ do { \ EXPECT_EQ_BLOCK(&(_filter)[0], BPF_JMP + BPF_JEQ + BPF_K, \ (_nr), NEXT, SKIP); \ EXPECT_EQ_BLOCK(&(_filter)[1], BPF_JMP + BPF_JA, (_id), (_jt), \ (_jf)); \ } while (0) #endif // SYSCALL_FILTER_UNITTEST_MACROS_H minijail-2026.05.18+git20261016.8787d1a/syscall_wrapper.c000066400000000000000000000015041526451555500217110ustar00rootroot00000000000000/* Copyright 2016 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "syscall_wrapper.h" #define _GNU_SOURCE #include #include /* * Older glibc builds predate seccomp inclusion. These arches are the ones * AOSP needs and doesn't provide anything newer. All other targets can upgrade * their kernel headers. */ #ifndef SYS_seccomp #if defined(__x86_64__) #define SYS_seccomp 317 #elif defined(__i386__) #define SYS_seccomp 354 #elif defined(__aarch64__) #define SYS_seccomp 277 #elif defined(__arm__) #define SYS_seccomp 383 #else #error "Update your kernel headers" #endif #endif int sys_seccomp(unsigned int operation, unsigned int flags, void *args) { return syscall(SYS_seccomp, operation, flags, args); } minijail-2026.05.18+git20261016.8787d1a/syscall_wrapper.h000066400000000000000000000017031526451555500217170ustar00rootroot00000000000000/* Copyright 2016 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef _SYSCALL_WRAPPER_H_ #define _SYSCALL_WRAPPER_H_ #ifdef __cplusplus extern "C" { #endif /* Seccomp filter related flags. */ #ifndef PR_SET_NO_NEW_PRIVS #define PR_SET_NO_NEW_PRIVS 38 #endif #ifndef SECCOMP_MODE_FILTER #define SECCOMP_MODE_FILTER 2 /* Uses user-supplied filter. */ #endif #ifndef SECCOMP_SET_MODE_STRICT #define SECCOMP_SET_MODE_STRICT 0 #endif #ifndef SECCOMP_SET_MODE_FILTER #define SECCOMP_SET_MODE_FILTER 1 #endif #ifndef SECCOMP_FILTER_FLAG_TSYNC #define SECCOMP_FILTER_FLAG_TSYNC 1 #endif #ifndef SECCOMP_FILTER_FLAG_SPEC_ALLOW #define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1 << 2) #endif /* End seccomp filter related flags. */ int sys_seccomp(unsigned int operation, unsigned int flags, void *args); #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* _SYSCALL_WRAPPER_H_ */ minijail-2026.05.18+git20261016.8787d1a/system.c000066400000000000000000000461141526451555500200310ustar00rootroot00000000000000/* Copyright 2017 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "system.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "syscall_wrapper.h" #include "util.h" /* * SECBIT_NO_CAP_AMBIENT_RAISE was added in kernel 4.3, so fill in the * definition if the securebits header doesn't provide it. */ #ifndef SECBIT_NO_CAP_AMBIENT_RAISE #define SECBIT_NO_CAP_AMBIENT_RAISE (issecure_mask(6)) #endif #ifndef SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED #define SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED (issecure_mask(7)) #endif /* * SECBIT_EXEC_RESTRICT_FILE was added in kernel 6.14, so fill in the * definition if the securebits header doesn't provide it. */ #ifndef SECBIT_EXEC_RESTRICT_FILE #define SECBIT_EXEC_RESTRICT_FILE (issecure_mask(8)) #endif #ifndef SECBIT_EXEC_RESTRICT_FILE_LOCKED #define SECBIT_EXEC_RESTRICT_FILE_LOCKED (issecure_mask(9)) #endif /* * SECBIT_EXEC_DENY_INTERACTIVE was added in kernel 6.14, so fill in the * definition if the securebits header doesn't provide it. */ #ifndef SECBIT_EXEC_DENY_INTERACTIVE #define SECBIT_EXEC_DENY_INTERACTIVE (issecure_mask(10)) #endif #ifndef SECBIT_EXEC_DENY_INTERACTIVE_LOCKED #define SECBIT_EXEC_DENY_INTERACTIVE_LOCKED (issecure_mask(11)) #endif /* * Assert the value of SECURE_ALL_BITS at compile-time to detect a change in * the set of secure bits coming from the kernel headers. * Kernel 6.14 introduced new secure bits that need to be removed when * running on older kernels. An older kernel can be detected when the * prctl(PR_SET_SECUREBITS, ...) fails with errno set to EPERM. * When this is detected, remove the new bits and try the prctl call again. */ #if defined(__ANDROID__) _Static_assert(SECURE_ALL_BITS == 0x555, "SECURE_ALL_BITS == 0x555."); #endif #define SECURE_BITS_6_14 \ (SECBIT_EXEC_RESTRICT_FILE | SECBIT_EXEC_DENY_INTERACTIVE) #define SECURE_LOCK_BITS_6_14 \ (SECBIT_EXEC_RESTRICT_FILE_LOCKED | SECBIT_EXEC_DENY_INTERACTIVE_LOCKED) /* Used by lookup_(user|group) functions. */ #define MAX_PWENT_SZ (1 << 20) #define MAX_GRENT_SZ (1 << 20) int secure_noroot_set_and_locked(uint64_t mask) { return (mask & (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED)) == (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED); } int lock_securebits(uint64_t skip_mask, bool require_keep_caps) { /* The general idea is to set all bits, subject to exceptions below. */ unsigned long securebits = SECURE_ALL_BITS | SECURE_ALL_LOCKS; /* * SECBIT_KEEP_CAPS is special in that it is automatically cleared on * execve(2). This implies that attempts to set SECBIT_KEEP_CAPS (as is * the default) in processes that have it locked already (such as nested * minijail usage) would fail. Thus, unless the caller requires it, * allow it to remain off if it is already locked. */ if (!require_keep_caps) { int current_securebits = prctl(PR_GET_SECUREBITS); if (current_securebits < 0) { pwarn("prctl(PR_GET_SECUREBITS) failed"); return -1; } if ((current_securebits & SECBIT_KEEP_CAPS_LOCKED) != 0 && (current_securebits & SECBIT_KEEP_CAPS) == 0) { securebits &= ~SECBIT_KEEP_CAPS; } } /* * Ambient capabilities can only be raised if they're already present * in the permitted *and* inheritable set. Therefore, we don't really * need to lock the NO_CAP_AMBIENT_RAISE securebit, since we are already * configuring the permitted and inheritable set. */ securebits &= ~(SECBIT_NO_CAP_AMBIENT_RAISE | SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED); /* Don't set any bits that the user requested not to be touched. */ securebits &= ~skip_mask; if (!securebits) { warn("not locking any securebits"); return 0; } int securebits_ret = prctl(PR_SET_SECUREBITS, securebits); if (securebits_ret < 0) { if (errno == EPERM && (securebits & (SECURE_BITS_6_14 | SECURE_LOCK_BITS_6_14)) != 0) { /* Possibly running on kernel < 6.14. */ securebits &= ~(SECURE_BITS_6_14 | SECURE_LOCK_BITS_6_14); securebits_ret = prctl(PR_SET_SECUREBITS, securebits); } if (securebits_ret < 0) { pwarn("prctl(PR_SET_SECUREBITS) failed"); return -1; } } return 0; } int write_proc_file(pid_t pid, const char *content, const char *basename) { attribute_cleanup_fd int fd = -1; int ret; size_t sz, len; ssize_t written; char filename[32]; sz = sizeof(filename); ret = snprintf(filename, sz, "/proc/%d/%s", pid, basename); if (ret < 0 || (size_t)ret >= sz) { warn("failed to generate %s filename", basename); return -1; } fd = open(filename, O_WRONLY | O_CLOEXEC); if (fd < 0) { pwarn("failed to open '%s'", filename); return -errno; } len = strlen(content); written = write(fd, content, len); if (written < 0) { pwarn("failed to write '%s'", filename); return -errno; } if ((size_t)written < len) { warn("failed to write %zu bytes to '%s'", len, filename); return -1; } return 0; } /* * Run newuidmap or newgidmap helper to set up a uid/gid map. * These are privileged helper binaries that consult /etc/subuid and /etc/subgid * to allow unprivileged users to set up multi-range ID mappings. * * The map string is in the format "inside outside count\n..." (newlines * between entries, as converted from commas by minijail_uidmap/gidmap). * We convert this to the helper's argv format: * newuidmap [ ...] */ int run_id_map_helper(pid_t pid, const char *map, const char *helper) { /* * Count tokens in the map string to size argv. * Each entry is "inside outside count" (3 tokens), separated by * whitespace. We over-count slightly since we start at 1, * but that just means a few extra NULL slots from calloc. */ size_t token_count = 1; const char *p; for (p = map; *p; p++) { if (isspace(*p)) token_count++; } /* Allocate argv: helper + pid_str + tokens + NULL terminator. */ size_t max_args = 2 + token_count + 1; const char **argv = calloc(max_args, sizeof(char *)); if (!argv) return -ENOMEM; char pid_str[32]; snprintf(pid_str, sizeof(pid_str), "%d", pid); argv[0] = helper; argv[1] = pid_str; /* * Tokenize the map string on whitespace. * The tokens are already the numeric strings we need for argv. */ char *map_copy = strdup(map); if (!map_copy) { free(argv); return -ENOMEM; } int argc = 2; char *saveptr = NULL; char *token = strtok_r(map_copy, " \t\n", &saveptr); while (token) { argv[argc++] = token; token = strtok_r(NULL, " \t\n", &saveptr); } argv[argc] = NULL; if (argc == 2) { warn("id map is empty"); free(map_copy); free(argv); return -EINVAL; } if ((argc - 2) % 3 != 0) { warn("id map has %d tokens, expected a multiple of 3", argc - 2); free(map_copy); free(argv); return -EINVAL; } pid_t child = fork(); if (child < 0) { pwarn("failed to fork for %s", helper); free(map_copy); free(argv); return -errno; } if (child == 0) { execv(helper, (char *const *)argv); pwarn("execv(%s) failed", helper); _exit(EXIT_FAILURE); } free(map_copy); free(argv); int status; if (waitpid(child, &status, 0) < 0) return -errno; if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { warn("%s failed with status %d", helper, WIFEXITED(status) ? WEXITSTATUS(status) : -1); return -EPERM; } return 0; } /* * We specifically do not use cap_valid() as that only tells us the last * valid cap we were *compiled* against (i.e. what the version of kernel * headers says). If we run on a different kernel version, then it's not * uncommon for that to be less (if an older kernel) or more (if a newer * kernel). * Normally, we suck up the answer via /proc. On Android, not all processes are * guaranteed to be able to access '/proc/sys/kernel/cap_last_cap' so we * programmatically find the value by calling prctl(PR_CAPBSET_READ). */ unsigned int get_last_valid_cap(void) { unsigned int last_valid_cap = 0; if (is_android()) { for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0; ++last_valid_cap) ; /* |last_valid_cap| will be the first failing value. */ if (last_valid_cap > 0) { last_valid_cap--; } } else { static const char cap_file[] = "/proc/sys/kernel/cap_last_cap"; FILE *fp = fopen(cap_file, "re"); if (!fp) pdie("fopen(%s)", cap_file); if (fscanf(fp, "%u", &last_valid_cap) != 1) pdie("fscanf(%s)", cap_file); fclose(fp); } /* Caps are bitfields stored in 64-bit int. */ if (last_valid_cap > 64) pdie("unable to detect last valid cap: %u > 64", last_valid_cap); return last_valid_cap; } int cap_ambient_supported(void) { return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0; } int config_net_loopback(void) { const char ifname[] = "lo"; attribute_cleanup_fd int sock = -1; struct ifreq ifr; /* Make sure people don't try to add really long names. */ _Static_assert(sizeof(ifname) <= IFNAMSIZ, "interface name too long"); sock = socket(AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sock < 0) { pwarn("socket(AF_LOCAL) failed"); return -1; } /* * Do the equiv of `ip link set up lo`. The kernel will assign * IPv4 (127.0.0.1) & IPv6 (::1) addresses automatically! */ strcpy(ifr.ifr_name, ifname); if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { pwarn("ioctl(SIOCGIFFLAGS) failed"); return -1; } /* The kernel preserves ifr.ifr_name for use. */ ifr.ifr_flags |= IFF_UP | IFF_RUNNING; if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) { pwarn("ioctl(SIOCSIFFLAGS) failed"); return -1; } return 0; } int write_pid_to_path(pid_t pid, const char *path) { FILE *fp = fopen(path, "we"); if (!fp) { pwarn("failed to open '%s'", path); return -errno; } if (fprintf(fp, "%d\n", (int)pid) < 0) { /* fprintf(3) does not set errno on failure. */ warn("fprintf(%s) failed", path); fclose(fp); return -1; } if (fclose(fp)) { pwarn("fclose(%s) failed", path); return -errno; } return 0; } /* * Create the |path| directory and its parents (if need be) with |mode|. * If not |isdir|, then |path| is actually a file, so the last component * will not be created. */ int mkdir_p(const char *path, mode_t mode, bool isdir) { int rc; char *dir = strdup(path); if (!dir) { rc = errno; pwarn("strdup(%s) failed", path); return -rc; } /* Starting from the root, work our way out to the end. */ char *p = strchr(dir + 1, '/'); while (p) { *p = '\0'; if (mkdir(dir, mode) && errno != EEXIST) { rc = errno; pwarn("mkdir(%s, 0%o) failed", dir, mode); free(dir); return -rc; } *p = '/'; p = strchr(p + 1, '/'); } /* * Create the last directory. We still check EEXIST here in case * of trailing slashes. */ free(dir); if (isdir && mkdir(path, mode) && errno != EEXIST) { rc = errno; pwarn("mkdir(%s, 0%o) failed", path, mode); return -rc; } return 0; } /* * get_mount_flags: Obtain the mount flags of the mount where |source| lives. */ int get_mount_flags(const char *source, unsigned long *mnt_flags) { if (mnt_flags) { struct statvfs stvfs_buf; int rc = statvfs(source, &stvfs_buf); if (rc) { rc = errno; pwarn("failed to look up mount flags: source=%s", source); return -rc; } *mnt_flags = stvfs_buf.f_flag; } return 0; } /* * setup_mount_destination: Ensures the mount target exists. * Creates it if needed and possible. */ int setup_mount_destination(const char *source, const char *dest, uid_t uid, uid_t gid, bool bind) { int rc; struct stat st_buf; bool domkdir; /* * Use lstat so an untrusted symlink at |dest| is treated as * "exists" and we fall straight through to mount() instead of * creating/chowning the symlink target as root. */ rc = lstat(dest, &st_buf); if (rc == 0) /* destination exists */ return 0; /* * Try to create the destination. * Either make a directory or touch a file depending on the source type. * * If the source isn't an absolute path, assume it is a filesystem type * such as "tmpfs" and create a directory to mount it on. The dest will * be something like "none" or "proc" which we shouldn't be checking. */ if (source[0] == '/') { /* The source is an absolute path -- it better exist! */ rc = stat(source, &st_buf); if (rc) { rc = errno; pwarn("stat(%s) failed", source); return -rc; } /* * If bind mounting, we only create a directory if the source * is a directory, else we always bind mount it as a file to * support device nodes, sockets, etc... * * For all other mounts, we assume a block/char source is * going to want a directory to mount to. If the source is * something else (e.g. a fifo or socket), this probably will * not do the right thing, but we'll fail later on when we try * to mount(), so shouldn't be a big deal. */ domkdir = S_ISDIR(st_buf.st_mode) || (!bind && (S_ISBLK(st_buf.st_mode) || S_ISCHR(st_buf.st_mode))); } else { /* The source is a relative path -- assume it's a pseudo fs. */ /* Disallow relative bind mounts. */ if (bind) { warn("relative bind-mounts are not allowed: source=%s", source); return -EINVAL; } domkdir = true; } /* * Now that we know what we want to do, do it! * We always create the intermediate dirs and the final path with 0755 * perms and root/root ownership. This shouldn't be a problem because * the actual mount will set those perms/ownership on the mount point * which is all people should need to access it. */ rc = mkdir_p(dest, 0755, domkdir); if (rc) return rc; if (!domkdir) { attribute_cleanup_fd int fd = open( dest, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0700); if (fd < 0) { rc = errno; if (rc == EEXIST) { struct stat lspec_st; if (lstat(dest, &lspec_st) == 0 && S_ISREG(lspec_st.st_mode)) { return 0; } } pwarn("open(%s) failed", dest); return -rc; } } if (lchown(dest, uid, gid)) { rc = errno; pwarn("chown(%s, %u, %u) failed", dest, uid, gid); return -rc; } return 0; } /* * lookup_user: Gets the uid/gid for the given username. */ int lookup_user(const char *user, uid_t *uid, gid_t *gid) { char *buf = NULL; struct passwd pw; struct passwd *ppw = NULL; /* * sysconf(_SC_GETPW_R_SIZE_MAX), under glibc, is documented to return * a suggested starting size for the buffer, so let's try getting this * size first, and fallback to a default othersise. */ ssize_t sz = sysconf(_SC_GETPW_R_SIZE_MAX); if (sz == -1) sz = 65536; /* your guess is as good as mine... */ do { buf = malloc(sz); if (!buf) return -ENOMEM; int err = getpwnam_r(user, &pw, buf, sz, &ppw); /* * We're safe to free the buffer here. The strings inside |pw| * point inside |buf|, but we don't use any of them; this leaves * the pointers dangling but it's safe. * |ppw| points at |pw| if getpwnam_r(3) succeeded. */ free(buf); if (err == ERANGE) { /* |buf| was too small, retry with a bigger one. */ sz <<= 1; } else if (err != 0) { /* We got an error not related to the size of |buf|. */ return -err; } else if (!ppw) { /* Not found. */ return -ENOENT; } else { *uid = ppw->pw_uid; *gid = ppw->pw_gid; return 0; } } while (sz <= MAX_PWENT_SZ); /* A buffer of size MAX_PWENT_SZ is still too small, return an error. */ return -ERANGE; } /* * lookup_group: Gets the gid for the given group name. */ int lookup_group(const char *group, gid_t *gid) { char *buf = NULL; struct group gr; struct group *pgr = NULL; /* * sysconf(_SC_GETGR_R_SIZE_MAX), under glibc, is documented to return * a suggested starting size for the buffer, so let's try getting this * size first, and fallback to a default otherwise. */ ssize_t sz = sysconf(_SC_GETGR_R_SIZE_MAX); if (sz == -1) sz = 65536; /* and mine is as good as yours, really */ do { buf = malloc(sz); if (!buf) return -ENOMEM; int err = getgrnam_r(group, &gr, buf, sz, &pgr); /* * We're safe to free the buffer here. The strings inside |gr| * point inside |buf|, but we don't use any of them; this leaves * the pointers dangling but it's safe. * |pgr| points at |gr| if getgrnam_r(3) succeeded. */ free(buf); if (err == ERANGE) { /* |buf| was too small, retry with a bigger one. */ sz <<= 1; } else if (err != 0) { /* We got an error not related to the size of |buf|. */ return -err; } else if (!pgr) { /* Not found. */ return -ENOENT; } else { *gid = pgr->gr_gid; return 0; } } while (sz <= MAX_GRENT_SZ); /* A buffer of size MAX_GRENT_SZ is still too small, return an error. */ return -ERANGE; } static bool seccomp_action_is_available(const char *wanted) { if (is_android()) { /* * Accessing |actions_avail| is generating SELinux denials, so * skip for now. * TODO(crbug.com/978022, jorgelo): Remove once the denial is * fixed. */ return false; } static const char actions_avail_path[] = "/proc/sys/kernel/seccomp/actions_avail"; attribute_cleanup_fp FILE *f = fopen(actions_avail_path, "re"); if (!f) { pwarn("fopen(%s) failed", actions_avail_path); return false; } attribute_cleanup_str char *actions_avail = NULL; size_t buf_size = 0; if (getline(&actions_avail, &buf_size, f) < 0) { pwarn("getline() failed"); return false; } /* * This is just substring search, which means that partial matches will * match too (e.g. "action" would match "longaction"). There are no * seccomp actions which include other actions though, so we're good for * now. Eventually we might want to split the string by spaces. */ return strstr(actions_avail, wanted) != NULL; } int seccomp_ret_log_available(void) { static int ret_log_available = -1; if (ret_log_available == -1) ret_log_available = seccomp_action_is_available("log"); return ret_log_available; } int seccomp_ret_kill_process_available(void) { static int ret_kill_process_available = -1; if (ret_kill_process_available == -1) ret_kill_process_available = seccomp_action_is_available("kill_process"); return ret_kill_process_available; } bool sys_set_no_new_privs(void) { /* * Set no_new_privs. See and * in the kernel source tree for an explanation of the parameters. */ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0) { return true; } else { pwarn("prctl(PR_SET_NO_NEW_PRIVS) failed"); return false; } } bool seccomp_filter_flags_available(unsigned int flags) { return sys_seccomp(SECCOMP_SET_MODE_FILTER, flags, NULL) != -1 || errno != EINVAL; } bool is_canonical_path(const char *path) { attribute_cleanup_str char *rp = realpath(path, NULL); if (!rp) { pwarn("realpath(%s) failed", path); return false; } if (streq(path, rp)) { return true; } size_t path_len = strlen(path); size_t rp_len = strlen(rp); /* If |path| has a single trailing slash, that's OK. */ return path_len == rp_len + 1 && strncmp(path, rp, rp_len) == 0 && path[path_len - 1] == '/'; } minijail-2026.05.18+git20261016.8787d1a/system.h000066400000000000000000000037611526451555500200370ustar00rootroot00000000000000/* Copyright 2017 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Wrappers for system functionality. */ #ifndef _SYSTEM_H_ #define _SYSTEM_H_ #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* Control the ambient capability set. */ #ifndef PR_CAP_AMBIENT #define PR_CAP_AMBIENT 47 #endif #ifndef PR_CAP_AMBIENT_IS_SET #define PR_CAP_AMBIENT_IS_SET 1 #endif #ifndef PR_CAP_AMBIENT_RAISE #define PR_CAP_AMBIENT_RAISE 2 #endif #ifndef PR_CAP_AMBIENT_LOWER #define PR_CAP_AMBIENT_LOWER 3 #endif #ifndef PR_CAP_AMBIENT_CLEAR_ALL #define PR_CAP_AMBIENT_CLEAR_ALL 4 #endif int secure_noroot_set_and_locked(uint64_t mask); int lock_securebits(uint64_t skip_mask, bool require_keep_caps); unsigned int get_last_valid_cap(void); int cap_ambient_supported(void); int config_net_loopback(void); int write_pid_to_path(pid_t pid, const char *path); int write_proc_file(pid_t pid, const char *content, const char *basename); int run_id_map_helper(pid_t pid, const char *map, const char *helper); int mkdir_p(const char *path, mode_t mode, bool isdir); int get_mount_flags(const char *source, unsigned long *mnt_flags); int setup_mount_destination(const char *source, const char *dest, uid_t uid, uid_t gid, bool bind); int lookup_user(const char *user, uid_t *uid, gid_t *gid); int lookup_group(const char *group, gid_t *gid); /* sys_set_no_new_privs: returns true if successful. */ bool sys_set_no_new_privs(void); int seccomp_ret_log_available(void); int seccomp_ret_kill_process_available(void); bool seccomp_filter_flags_available(unsigned int flags); /* * is_canonical_path: checks whether @path is a canonical path. * This means: * -Absolute. * -No symlinks. * -No /./, /../, or extra '/'. * -Single trailing '/' is OK. */ bool is_canonical_path(const char *path); #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* _SYSTEM_H_ */ minijail-2026.05.18+git20261016.8787d1a/system_unittest.cc000066400000000000000000000230501526451555500221250ustar00rootroot00000000000000/* Copyright 2017 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test system.[ch] module code using gtest. */ #include #include #include #include #include #include #include #include #include #include #include #include "system.h" #include "unittest_util.h" namespace { // A random path that really really should not exist on the host. constexpr const char kNoSuchDir[] = "/.x/..x/...x/path/should/not/exist/"; // A random file that should exist on both Linux and Android. constexpr const char kValidFile[] = "/etc/hosts"; // A random directory that should exist. constexpr const char kValidDir[] = "/"; // A random character device that should exist. constexpr const char kValidCharDev[] = "/dev/null"; } // namespace TEST(secure_noroot_set_and_locked, zero_mask) { ASSERT_EQ(secure_noroot_set_and_locked(0), 0); } TEST(secure_noroot_set_and_locked, set) { ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_NOROOT) | issecure_mask(SECURE_NOROOT_LOCKED)), 1); } TEST(secure_noroot_set_and_locked, not_set) { ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_KEEP_CAPS) | issecure_mask(SECURE_NOROOT_LOCKED)), 0); } // Sanity check for the cap range. TEST(get_last_valid_cap, basic) { unsigned int cap = get_last_valid_cap(); // We pick 35 as it's been that since at least v3.0. // If this test is run on older kernels, it might fail. EXPECT_GE(cap, 35u); // Pick a really large number that we probably won't hit for a long time. // It helps that caps are bitfields. EXPECT_LT(cap, 128u); } // Might be useful to figure out the return value, but for now, // just make sure it doesn't crash? TEST(cap_ambient_supported, smoke) { cap_ambient_supported(); } // An invalid path should return an error. TEST(write_pid_to_path, bad_path) { EXPECT_NE(0, write_pid_to_path(0, kNoSuchDir)); } // Make sure we can write a pid to the file. TEST(write_pid_to_path, basic) { TemporaryFile tmp; ASSERT_TRUE(tmp.is_valid()); EXPECT_EQ(0, write_pid_to_path(1234, tmp.path.c_str())); FILE* fp = fopen(tmp.path.c_str(), "re"); EXPECT_NE(nullptr, fp); char data[6] = {}; EXPECT_EQ(5u, fread(data, 1, sizeof(data), fp)); fclose(fp); EXPECT_STREQ(data, "1234\n"); } // If the destination exists, there's nothing to do. // Also check trailing slash handling. TEST(mkdir_p, dest_exists) { EXPECT_EQ(0, mkdir_p("/", 0, true)); EXPECT_EQ(0, mkdir_p("///", 0, true)); EXPECT_EQ(0, mkdir_p("/proc", 0, true)); EXPECT_EQ(0, mkdir_p("/proc/", 0, true)); EXPECT_EQ(0, mkdir_p("/dev", 0, true)); EXPECT_EQ(0, mkdir_p("/dev/", 0, true)); } // Create a directory tree that doesn't exist. TEST(mkdir_p, create_tree) { TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); // Run `mkdir -p /a/b/c`. std::string path_a = dir.path + "/a"; std::string path_a_b = path_a + "/b"; std::string path_a_b_c = path_a_b + "/c"; // First try creating it as a file. EXPECT_EQ(0, mkdir_p(path_a_b_c.c_str(), 0700, false)); // Make sure the final path doesn't exist yet. struct stat st; EXPECT_EQ(0, stat(path_a_b.c_str(), &st)); EXPECT_EQ(true, S_ISDIR(st.st_mode)); EXPECT_EQ(-1, stat(path_a_b_c.c_str(), &st)); // Then create it as a complete dir. EXPECT_EQ(0, mkdir_p(path_a_b_c.c_str(), 0700, true)); // Make sure the final dir actually exists. EXPECT_EQ(0, stat(path_a_b_c.c_str(), &st)); EXPECT_EQ(true, S_ISDIR(st.st_mode)); } // Return success on NULL pointer. TEST(get_mount_flags, null_ptr) { ASSERT_EQ(0, get_mount_flags("/proc", nullptr)); } // Successfully obtain mount flags. TEST(get_mount_flags, mount_flags) { struct statvfs stvfs_buf; ASSERT_EQ(0, statvfs("/proc", &stvfs_buf)); TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); unsigned long mount_flags = -1; ASSERT_EQ(0, get_mount_flags("/proc", &mount_flags)); EXPECT_EQ(stvfs_buf.f_flag, mount_flags); // Same thing holds for children of a mount. mount_flags = -1; ASSERT_EQ(0, get_mount_flags("/proc/self", &mount_flags)); EXPECT_EQ(stvfs_buf.f_flag, mount_flags); } // Non-existent paths fail with the proper errno value. TEST(get_mount_flags, nonexistent_path) { unsigned long mount_flags = -1; ASSERT_EQ(-ENOENT, get_mount_flags("/does/not/exist", &mount_flags)); } // If the destination exists, there's nothing to do. TEST(setup_mount_destination, dest_exists) { // Pick some paths that should always exist. We pass in invalid pointers // for other args so we crash if the dest check doesn't short circuit. EXPECT_EQ(0, setup_mount_destination(nullptr, kValidDir, 0, 0, false)); EXPECT_EQ(0, setup_mount_destination(nullptr, "/proc", 0, 0, true)); EXPECT_EQ(0, setup_mount_destination(nullptr, "/dev", 0, 0, false)); } // When given a bind mount where the source is relative, reject it. TEST(setup_mount_destination, reject_relative_bind) { // Pick a destination we know doesn't exist. EXPECT_NE(0, setup_mount_destination("foo", kNoSuchDir, 0, 0, true)); } // A mount of a pseudo filesystem should make the destination dir. TEST(setup_mount_destination, create_pseudo_fs) { TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); // Passing -1 for user ID/group ID tells chown to make no changes. std::string no_chmod = dir.path + "/no_chmod"; EXPECT_EQ(0, setup_mount_destination("none", no_chmod.c_str(), -1, -1, false)); // We check it's a directory by deleting it as such. EXPECT_EQ(0, rmdir(no_chmod.c_str())); // Confirm that a bad user ID/group ID fails the function as expected. // On Android, Bionic manages user IDs directly: there is no /etc/passwd file. // This results in most user IDs being valid. Instead of trying to find an // invalid user ID, just skip this check. if (!is_android()) { std::string with_chmod = dir.path + "/with_chmod"; EXPECT_NE(0, setup_mount_destination("none", with_chmod.c_str(), UINT_MAX / 2, UINT_MAX / 2, false)); } } // If the source path does not exist, we should error out. TEST(setup_mount_destination, missing_source) { // The missing dest path is so we can exercise the source logic. EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, false)); EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, true)); } // A bind mount of a directory should create the destination dir. TEST(setup_mount_destination, create_bind_dir) { TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); // Passing -1 for user ID/group ID tells chown to make no changes. std::string child_dir = dir.path + "/child_dir"; EXPECT_EQ( 0, setup_mount_destination(kValidDir, child_dir.c_str(), -1, -1, true)); // We check it's a directory by deleting it as such. EXPECT_EQ(0, rmdir(child_dir.c_str())); } // A bind mount of a file should create the destination file. TEST(setup_mount_destination, create_bind_file) { TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); // Passing -1 for user ID/group ID tells chown to make no changes. std::string child_file = dir.path + "/child_file"; EXPECT_EQ( 0, setup_mount_destination(kValidFile, child_file.c_str(), -1, -1, true)); // We check it's a file by deleting it as such. EXPECT_EQ(0, unlink(child_file.c_str())); } // A mount of a character device should create the destination char. TEST(setup_mount_destination, create_char_dev) { TemporaryDir dir; ASSERT_TRUE(dir.is_valid()); // Passing -1 for user ID/group ID tells chown to make no changes. std::string child_dev = dir.path + "/child_dev"; EXPECT_EQ(0, setup_mount_destination(kValidCharDev, child_dev.c_str(), -1, -1, false)); // We check it's a directory by deleting it as such. EXPECT_EQ(0, rmdir(child_dev.c_str())); } TEST(seccomp_actions_available, smoke) { seccomp_ret_log_available(); seccomp_ret_kill_process_available(); } TEST(set_no_new_privs, basic) { EXPECT_TRUE(sys_set_no_new_privs()); } TEST(is_canonical_path, basic) { EXPECT_FALSE(is_canonical_path("/proc/self")); EXPECT_FALSE(is_canonical_path("relative")); EXPECT_FALSE(is_canonical_path("/proc/./1")); EXPECT_FALSE(is_canonical_path("/proc/../proc/1")); EXPECT_TRUE(is_canonical_path("/")); EXPECT_TRUE(is_canonical_path("/proc")); EXPECT_TRUE(is_canonical_path("/proc/1")); } TEST(is_canonical_path, trailing_slash) { EXPECT_TRUE(is_canonical_path("/proc/1/")); EXPECT_FALSE(is_canonical_path("/proc/1//")); } // Valid maps with a helper that always succeeds. TEST(run_id_map_helper, success) { EXPECT_EQ(0, run_id_map_helper(getpid(), "0 1000 1", "/bin/true")); EXPECT_EQ(0, run_id_map_helper(getpid(), "0 1000 1\n100 2000 50", "/bin/true")); } // Token count must be a positive multiple of 3. TEST(run_id_map_helper, invalid_map) { EXPECT_EQ(-EINVAL, run_id_map_helper(getpid(), "", "/bin/true")); EXPECT_EQ(-EINVAL, run_id_map_helper(getpid(), " \t\n ", "/bin/true")); EXPECT_EQ(-EINVAL, run_id_map_helper(getpid(), "0 1000", "/bin/true")); EXPECT_EQ(-EINVAL, run_id_map_helper(getpid(), "0 1000 1 extra", "/bin/true")); } // Helper failure or missing helper should return -EPERM. TEST(run_id_map_helper, helper_failure) { EXPECT_EQ(-EPERM, run_id_map_helper(getpid(), "0 1000 1", "/bin/false")); EXPECT_EQ(-EPERM, run_id_map_helper(getpid(), "0 1000 1", "/nonexistent/path/to/helper")); } minijail-2026.05.18+git20261016.8787d1a/test/000077500000000000000000000000001526451555500173125ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/test/.clang-format000066400000000000000000000004311526451555500216630ustar00rootroot00000000000000BasedOnStyle: Google AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false BinPackArguments: false BinPackParameters: false CommentPragmas: NOLINT:.* DerivePointerAlignment: false PointerAlignment: Left TabWidth: 2 minijail-2026.05.18+git20261016.8787d1a/test/invalid.conf000066400000000000000000000000701526451555500216040ustar00rootroot00000000000000% minijail-config-file v0 # Comments bad-key = whatever minijail-2026.05.18+git20261016.8787d1a/test/invalid_arg_filter.policy000066400000000000000000000000171526451555500243550ustar00rootroot00000000000000open: argnn == minijail-2026.05.18+git20261016.8787d1a/test/invalid_syscall_name.policy000066400000000000000000000000171526451555500247110ustar00rootroot00000000000000notasyscall: 1 minijail-2026.05.18+git20261016.8787d1a/test/landlock.conf000066400000000000000000000001721526451555500217500ustar00rootroot00000000000000% minijail-config-file v0 fs-default-paths fs-path-ro = / fs-path-rx = /lib fs-path-rw = /tmp fs-path-advanced-rw = /tmp minijail-2026.05.18+git20261016.8787d1a/test/libminijail_test.cpp000066400000000000000000000071561526451555500233510ustar00rootroot00000000000000// Copyright (C) 2016 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include const uid_t kSystemUid = 1000U; gid_t groups[] = {kSystemUid + 1, kSystemUid + 2}; size_t getgroups_with_alloc(gid_t** plist) { *plist = NULL; int nsupp_groups = getgroups(0, NULL); if (nsupp_groups < 0) { PLOG(ERROR) << "getgroups(0)"; return 0; } if (nsupp_groups == 0) { LOG(INFO) << "No supplementary groups."; return 0; } *plist = (gid_t*)calloc((size_t)nsupp_groups, sizeof(gid_t)); nsupp_groups = getgroups(nsupp_groups, *plist); if (nsupp_groups < 0) { PLOG(ERROR) << "getgroups(nsupp_groups)"; free(*plist); return 0; } return nsupp_groups; } bool check_ugid(uid_t expected_id) { bool success = true; uid_t ruid = getuid(); if (ruid != expected_id) { LOG(ERROR) << "rUID " << ruid << " is not " << expected_id; success = false; } gid_t rgid = getgid(); if (rgid != expected_id) { LOG(ERROR) << "rGID " << ruid << " is not " << expected_id; success = false; } return success; } bool check_groups(size_t expected_size, gid_t* expected_list) { bool success = true; gid_t* actual_list; size_t actual_size = getgroups_with_alloc(&actual_list); if (expected_size != actual_size) { LOG(ERROR) << "Mismatched supplementary group list size: expected " << expected_size << ", actual " << actual_size; success = false; } for (size_t i = 0; i < expected_size; i++) { bool found = false; for (size_t j = 0; j < actual_size; j++) { if (expected_list[i] == actual_list[j]) { // Test next expected GID. found = true; break; } } if (!found) { LOG(ERROR) << "Expected GID " << expected_list[i] << " not found."; success = false; } } free(actual_list); return success; } void log_resugid() { uid_t ruid, euid, suid; gid_t rgid, egid, sgid; getresuid(&ruid, &euid, &suid); getresgid(&rgid, &egid, &sgid); LOG(INFO) << "ruid " << ruid << " euid " << euid << " suid " << suid; LOG(INFO) << "rgid " << rgid << " egid " << egid << " sgid " << sgid; gid_t* list; size_t nsupp_groups = getgroups_with_alloc(&list); for (size_t i = 0; i < (size_t)nsupp_groups; i++) { LOG(INFO) << "supp gid " << i + 1 << " " << list[i]; } free(list); } int main(void) { minijail* j = minijail_new(); minijail_change_user(j, "system"); minijail_change_group(j, "system"); size_t num_groups = arraysize(groups); minijail_set_supplementary_gids(j, num_groups, groups); minijail_use_caps(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID)); minijail_enter(j); bool success = check_ugid(kSystemUid); success = success && check_groups(num_groups, groups); minijail_destroy(j); minijail* j2 = minijail_new(); minijail_change_uid(j2, 5 * kSystemUid); minijail_change_gid(j2, 5 * kSystemUid); minijail_enter(j2); success = success && check_ugid(5 * kSystemUid); return success ? 0 : 1; } minijail-2026.05.18+git20261016.8787d1a/test/nested.policy000066400000000000000000000000361526451555500220140ustar00rootroot00000000000000@include ./test/nested.policy minijail-2026.05.18+git20261016.8787d1a/test/read_stdin000066400000000000000000000003421526451555500213500ustar00rootroot00000000000000#!/bin/sh # Copyright 2012 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. read -r line if [ "${line}" = "test" ]; then exit 0 else exit 1 fi minijail-2026.05.18+git20261016.8787d1a/test/seccomp.policy000066400000000000000000000000511526451555500221600ustar00rootroot00000000000000read: 1 write: 1 rt_sigreturn: 1 exit: 1 minijail-2026.05.18+git20261016.8787d1a/test/stdin_stdout.policy000066400000000000000000000001061526451555500232530ustar00rootroot00000000000000read: arg0 == 0 write: arg0 == 1 || arg0 == 2 rt_sigreturn: 1 exit: 1 minijail-2026.05.18+git20261016.8787d1a/test/valid.conf000066400000000000000000000003631526451555500212620ustar00rootroot00000000000000% minijail-config-file v0 # Comments # enable mount namespace ns-mount # mounts and bind-mounts mount = none,/,none bind-mount = /,/ mount-dev # Preload library. preload-library = /fake-path # Remount mode. K = shared # Single char options. i minijail-2026.05.18+git20261016.8787d1a/test_util.cc000066400000000000000000000024071526451555500206610ustar00rootroot00000000000000/* Copyright 2021 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "test_util.h" #include #include #include #include #include "util.h" #define MAX_PIPE_CAPACITY (4096) FILE* write_to_pipe(const std::string& content) { int pipefd[2]; if (pipe(pipefd) == -1) { die("pipe(pipefd) failed"); } size_t len = content.length(); if (len > MAX_PIPE_CAPACITY) die("write_to_pipe cannot handle >4KB content."); size_t i = 0; unsigned int attempts = 0; ssize_t ret; while (i < len) { ret = write(pipefd[1], content.c_str() + i, len - i); if (ret == -1) { close(pipefd[0]); close(pipefd[1]); return NULL; } /* If we write 0 bytes three times in a row, fail. */ if (ret == 0) { if (++attempts >= 3) { close(pipefd[0]); close(pipefd[1]); warn("write() returned 0 three times in a row"); return NULL; } continue; } attempts = 0; i += (size_t)ret; } close(pipefd[1]); return fdopen(pipefd[0], "r"); } std::string source_path(const std::string& file) { std::string srcdir = getenv("SRC") ?: "."; return srcdir + "/" + file; } minijail-2026.05.18+git20261016.8787d1a/test_util.h000066400000000000000000000042751526451555500205300ustar00rootroot00000000000000/* test_util.h * Copyright 2021 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Utility functions in testing. */ #ifndef _TEST_UTIL_H_ #define _TEST_UTIL_H_ #include #include #include #include #include #include "config_parser.h" namespace mj { namespace internal { // Functor for |ScopedFILE| (below). struct ScopedFILECloser { inline void operator()(FILE *x) const { if (x) { fclose(x); } } }; // Functor for |ScopedConfigEntry| (below). struct ScopedConfigEntryDeleter { inline void operator()(config_entry *entry) const { if (entry) { free(entry); } } }; // Functor for |ScopedStr| (below). struct ScopedStrDeleter { inline void operator()(char *str) const { if (str) { free(str); } } }; } // namespace internal } // namespace mj using ScopedFILE = std::unique_ptr; using ScopedConfigEntry = std::unique_ptr; using ScopedStr = std::unique_ptr; class ScopedFD { public: explicit ScopedFD(int fd) : fd_(fd) {} ~ScopedFD() { if (fd_ != -1) close(fd_); } ScopedFD(const ScopedFD&) = delete; ScopedFD& operator=(const ScopedFD&) = delete; int get() const { return fd_; } private: int fd_ = -1; }; /* * write_to_pipe: write a string as the file content into a pipe based * file handle. This is particularly useful when testing with temporary data * files, without dealing with complexities such as relative file path, file * permission and etc. However, a pipe has limited capacity so write_to_pipe * will hang when a big enough string is written. This is for use in testing * only. * * Returns a FILE* that contains @content. */ FILE *write_to_pipe(const std::string& content); /* * source_path: return the path to a test fixture located in the current * source tree. This uses the `SRC` environment variable as the root of the * tree, falling back to the current directory. */ std::string source_path(const std::string& file); #endif /* _TEST_UTIL_H_ */ minijail-2026.05.18+git20261016.8787d1a/testrunner.cc000066400000000000000000000012431526451555500210530ustar00rootroot00000000000000/* Copyright 2017 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Main entrypoint for gtest. * Redirects logging to stderr to avoid syslog logspam. */ #include #include #include "util.h" namespace { class Environment : public ::testing::Environment { public: ~Environment() override = default; void SetUp() override { init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO); } }; } // namespace int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new Environment()); return RUN_ALL_TESTS(); } minijail-2026.05.18+git20261016.8787d1a/tools/000077500000000000000000000000001526451555500174735ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/tools/Android.bp000066400000000000000000000043751526451555500214070ustar00rootroot00000000000000// Copyright (C) 2019 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "external_minijail_license" // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["external_minijail_license"], } python_binary_host { name: "minijail_compile_seccomp_policy", main: "compile_seccomp_policy.py", srcs: [ "arch.py", "bpf.py", "compile_seccomp_policy.py", "compiler.py", "parser.py", ], } python_test_host { name: "minijail_parser_unittest", main: "parser_unittest.py", srcs: [ "arch.py", "bpf.py", "parser.py", "parser_unittest.py", ], data: [ "testdata/arch_64.json", ], } python_test_host { name: "minijail_compiler_unittest", main: "compiler_unittest.py", srcs: [ "arch.py", "bpf.py", "compiler.py", "compiler_unittest.py", "parser.py", ], data: [ "testdata/arch_64.json", ], } python_binary_host { name: "minijail_generate_constants_json", main: "generate_constants_json.py", srcs: [ "generate_constants_json.py", ], } cc_genrule { name: "minijail_constants_json", host_supported: true, vendor_available: true, recovery_available: true, tools: ["minijail_generate_constants_json"], cmd: "$(location minijail_generate_constants_json) --output=$(out) $(in)", srcs: [ ":libminijail_gen_constants_llvmir", ":libminijail_gen_syscall_llvmir", ], out: ["constants.json"], } minijail-2026.05.18+git20261016.8787d1a/tools/README.md000066400000000000000000000116221526451555500207540ustar00rootroot00000000000000# Minijail tools ## generate_seccomp_policy.py This script lets you build a Minijail seccomp-bpf filter from strace output. This is very useful if the process that is traced has a fairly tight working domain, and it can be traced in a few scenarios that will exercise all of the needed syscalls. In particular, you should always make sure that failure cases are also exercised to account for calls to `abort(2)`. If `libminijail` or `minijail0` are used with preloading (the default with dynamically-linked executables), the first few system calls after the first call to `execve(2)` might not be needed, since the seccomp-bpf filter is installed after that point in a sandboxed process. ### Sample usage ```shell strace -f -e raw=all -o strace.txt -- ./tools/generate_seccomp_policy.py strace.txt > .policy ``` ### Using linux audit logs to generate policy *** note **NOTE**: Certain syscalls made by `minijail0` may be misattributed to the sandboxed binary and may result in a policy that is overly-permissive. Please pay some extra attention when manually reviewing the allowable args for these syscalls: `ioctl`, `socket`, `prctl`, `mmap`, `mprotect`, and `mmap2`. *** Linux kernel v4.14+ support `SECCOMP_RET_LOG`. This allows minijail to log syscalls via the [audit subsystem][1] (Redhat has a nice overview [here][2]) instead of blocking them. One caveat of this approach is that `SECCOMP_RET_LOG` does not log syscall arguments for finer grained filtering. The audit subsystem itself has a mechanism to log all syscalls. Though a `SYSCALL` event is more voluminous than a corresponding `SECCOMP` event. We employ here a combination of both techniques. We rely on `SECCOMP` for all except the syscalls for which we want finer grained filtering. Note that this requires python3 bindings for `auparse` which are generally available in distro packages named `python3-audit` or `python-audit`. #### Per-boot setup of audit rules on DUT Set up `audit` rules and an empty seccomp policy for later use. This can be done in the `pre-start` section of your upstart conf. `$UID` is the uid for your process. Using root will lead to logspam. As mentioned above, these extra audit rules enable `SYSCALL` auditing which in turn lets the tool inspect arguments for a pre-selected subset of syscalls. The list of syscalls here matches the list of keys in `arg_inspection`. ```shell for arch in b32 b64; do auditctl -a exit,always -F uid=$UID -F arch=$arch -S ioctl -S socket \ -S prctl -S mmap -S mprotect \ $([ "$arch" = "b32" ] && echo "-S mmap2") -c done touch /tmp/empty.policy ``` #### Run your program under minijail with an empty policy Again, this can be done via your upstart conf. Just be sure to stimulate all corner cases, error conditions, etc for comprehensive coverage. ```shell minijail0 -u $UID -g $GID -L -S /tmp/empty.policy -- ``` #### Generate policy using the audit.log ```shell ./tools/generate_seccomp_policy.py --audit-comm $PROGRAM_NAME audit.log \ > $PROGRAM_NAME.policy ``` Note that the tool can also consume multiple audit logs and/or strace traces to produce one unified policy. ## compile_seccomp_policy.py An external seccomp-bpf compiler that is documented [here][3]. This uses a slightly different syntax and generates highly-optimized BPF binaries that can be provided to `minijail0`'s `--seccomp-bpf-binary` or `libminijail`'s `minijail_set_secomp_filters()`. This requires the existence of an architecture-specific `constants.json` file that contains the mapping of syscall names to numbers, the values of any compile-time constants that could be used to simplify the parameter declaration for filters (like `O_RDONLY` and any other constant defined in typical headers in `/usr/include`). Policy files can also include references to frequency files, which enable profile-guided optimization of the generated BPF code. The generated BPF code can be analyzed using [libseccomp](https://github.com/seccomp/libseccomp)'s `tools/scmp_bpf_disasm`. ### Sample usage ```shell make minijail0 constants.json # Create the .policy file using the syntax described in the documentation. cat > test/seccomp.policy < ``` ## generate_constants_json.py This script generates the `constants.json` file from LLVM IR assembly files. This makes it easier to generate architecture-specific `constants.json` files at build-time. [1]: https://people.redhat.com/sgrubb/audit/ [2]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/chap-system_auditing [3]: https://docs.google.com/document/d/e/2PACX-1vQOeYLWmJJrRWvglnMo5cynkUe0gZ9wVsndLLePkJg6dfUXSOUWoveBBeY3u5nQMlEU4dt_vRgj0ifR/pub minijail-2026.05.18+git20261016.8787d1a/tools/__init__.py000066400000000000000000000000001526451555500215720ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/tools/arch.py000066400000000000000000000031311526451555500207600ustar00rootroot00000000000000# Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Architecture-specific information.""" import collections import json class Arch( collections.namedtuple( "Arch", [ "arch_nr", "arch_name", "bits", "syscalls", "constants", "syscall_groups", ], ) ): """Holds architecture-specific information.""" def truncate_word(self, value): """Return the value truncated to fit in a word.""" return value & self.max_unsigned @property def min_signed(self): """The smallest signed value that can be represented in a word.""" return -(1 << (self.bits - 1)) @property def max_unsigned(self): """The largest unsigned value that can be represented in a word.""" return (1 << self.bits) - 1 @staticmethod def load_from_json(json_path): """Return an Arch from a .json file.""" with open(json_path, "rb") as json_file: return Arch.load_from_json_bytes(json_file.read()) @staticmethod def load_from_json_bytes(json_bytes): """Return an Arch from a json string.""" constants = json.loads(json_bytes) return Arch( arch_nr=constants["arch_nr"], arch_name=constants["arch_name"], bits=constants["bits"], syscalls=constants["syscalls"], constants=constants["constants"], syscall_groups=constants.get("syscall_groups", {}), ) minijail-2026.05.18+git20261016.8787d1a/tools/bpf.py000066400000000000000000000530101526451555500206130ustar00rootroot00000000000000# Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Tools to interact with BPF programs.""" import abc import collections import struct # This comes from syscall(2). Most architectures only support passing 6 args to # syscalls, but ARM supports passing 7. MAX_SYSCALL_ARGUMENTS = 7 # The following fields were copied from : # Instruction classes BPF_LD = 0x00 BPF_LDX = 0x01 BPF_ST = 0x02 BPF_STX = 0x03 BPF_ALU = 0x04 BPF_JMP = 0x05 BPF_RET = 0x06 BPF_MISC = 0x07 # LD/LDX fields. # Size BPF_W = 0x00 BPF_H = 0x08 BPF_B = 0x10 # Mode BPF_IMM = 0x00 BPF_ABS = 0x20 BPF_IND = 0x40 BPF_MEM = 0x60 BPF_LEN = 0x80 BPF_MSH = 0xA0 # JMP fields. BPF_JA = 0x00 BPF_JEQ = 0x10 BPF_JGT = 0x20 BPF_JGE = 0x30 BPF_JSET = 0x40 # Source BPF_K = 0x00 BPF_X = 0x08 BPF_MAXINSNS = 4096 # The following fields were copied from : SECCOMP_RET_KILL_PROCESS = 0x80000000 SECCOMP_RET_KILL_THREAD = 0x00000000 SECCOMP_RET_TRAP = 0x00030000 SECCOMP_RET_ERRNO = 0x00050000 SECCOMP_RET_TRACE = 0x7FF00000 SECCOMP_RET_USER_NOTIF = 0x7FC00000 SECCOMP_RET_LOG = 0x7FFC0000 SECCOMP_RET_ALLOW = 0x7FFF0000 SECCOMP_RET_ACTION_FULL = 0xFFFF0000 SECCOMP_RET_DATA = 0x0000FFFF def arg_offset(arg_index, hi=False): """Return the BPF_LD|BPF_W|BPF_ABS addressing-friendly register offset.""" offsetof_args = 4 + 4 + 8 arg_width = 8 return offsetof_args + arg_width * arg_index + (arg_width // 2) * hi def simulate(instructions, arch, syscall_number, *args): """Simulate a BPF program with the given arguments.""" args = (args + (0,) * (MAX_SYSCALL_ARGUMENTS - len(args)))[ :MAX_SYSCALL_ARGUMENTS ] input_memory = struct.pack( "IIQ" + "Q" * MAX_SYSCALL_ARGUMENTS, syscall_number, arch, 0, *args ) register = 0 program_counter = 0 cost = 0 while program_counter < len(instructions): ins = instructions[program_counter] program_counter += 1 cost += 1 if ins.code == BPF_LD | BPF_W | BPF_ABS: register = struct.unpack("I", input_memory[ins.k : ins.k + 4])[0] elif ins.code == BPF_JMP | BPF_JA | BPF_K: program_counter += ins.k elif ins.code == BPF_JMP | BPF_JEQ | BPF_K: if register == ins.k: program_counter += ins.jt else: program_counter += ins.jf elif ins.code == BPF_JMP | BPF_JGT | BPF_K: if register > ins.k: program_counter += ins.jt else: program_counter += ins.jf elif ins.code == BPF_JMP | BPF_JGE | BPF_K: if register >= ins.k: program_counter += ins.jt else: program_counter += ins.jf elif ins.code == BPF_JMP | BPF_JSET | BPF_K: if register & ins.k != 0: program_counter += ins.jt else: program_counter += ins.jf elif ins.code == BPF_RET: if ins.k == SECCOMP_RET_KILL_PROCESS: return (cost, "KILL_PROCESS") if ins.k == SECCOMP_RET_KILL_THREAD: return (cost, "KILL_THREAD") if ins.k == SECCOMP_RET_TRAP: return (cost, "TRAP") if (ins.k & SECCOMP_RET_ACTION_FULL) == SECCOMP_RET_ERRNO: return (cost, "ERRNO", ins.k & SECCOMP_RET_DATA) if ins.k == SECCOMP_RET_TRACE: return (cost, "TRACE") if ins.k == SECCOMP_RET_USER_NOTIF: return (cost, "USER_NOTIF") if ins.k == SECCOMP_RET_LOG: return (cost, "LOG") if ins.k == SECCOMP_RET_ALLOW: return (cost, "ALLOW") raise Exception("unknown return %#x" % ins.k) else: raise Exception("unknown instruction %r" % (ins,)) raise Exception("out-of-bounds") class SockFilter( collections.namedtuple("SockFilter", ["code", "jt", "jf", "k"]) ): """A representation of struct sock_filter.""" __slots__ = () def encode(self): """Return an encoded version of the SockFilter.""" return struct.pack("HBBI", self.code, self.jt, self.jf, self.k) class AbstractBlock(abc.ABC): """A class that implements the visitor pattern.""" @abc.abstractmethod def accept(self, visitor): pass class BasicBlock(AbstractBlock): """A concrete implementation of AbstractBlock that has been compiled.""" def __init__(self, instructions): super().__init__() self._instructions = instructions def accept(self, visitor): if visitor.visited(self): return visitor.visit(self) @property def instructions(self): return self._instructions @property def opcodes(self): return b"".join(i.encode() for i in self._instructions) def __eq__(self, o): if not isinstance(o, BasicBlock): return False return self._instructions == o._instructions class KillProcess(BasicBlock): """A BasicBlock that unconditionally returns KILL_PROCESS.""" def __init__(self): super().__init__( [SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_KILL_PROCESS)] ) class KillThread(BasicBlock): """A BasicBlock that unconditionally returns KILL_THREAD.""" def __init__(self): super().__init__( [SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_KILL_THREAD)] ) class Trap(BasicBlock): """A BasicBlock that unconditionally returns TRAP.""" def __init__(self): super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_TRAP)]) class Trace(BasicBlock): """A BasicBlock that unconditionally returns TRACE.""" def __init__(self): super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_TRACE)]) class UserNotify(BasicBlock): """A BasicBlock that unconditionally returns USER_NOTIF.""" def __init__(self): super().__init__( [SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_USER_NOTIF)] ) class Log(BasicBlock): """A BasicBlock that unconditionally returns LOG.""" def __init__(self): super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_LOG)]) class ReturnErrno(BasicBlock): """A BasicBlock that unconditionally returns the specified errno.""" def __init__(self, errno): super().__init__( [ SockFilter( BPF_RET, 0x00, 0x00, SECCOMP_RET_ERRNO | (errno & SECCOMP_RET_DATA), ) ] ) self.errno = errno class Allow(BasicBlock): """A BasicBlock that unconditionally returns ALLOW.""" def __init__(self): super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_ALLOW)]) class ValidateArch(AbstractBlock): """An AbstractBlock that validates the architecture.""" def __init__(self, next_block): super().__init__() self.next_block = next_block def accept(self, visitor): if visitor.visited(self): return self.next_block.accept(visitor) visitor.visit(self) class SyscallEntry(AbstractBlock): """An abstract block that represents a syscall comparison in a DAG.""" def __init__(self, syscall_number, jt, jf, *, op=BPF_JEQ): super().__init__() self.op = op self.syscall_number = syscall_number self.jt = jt self.jf = jf def __lt__(self, o): # Defined because we want to compare tuples that contain SyscallEntries. return False def __gt__(self, o): # Defined because we want to compare tuples that contain SyscallEntries. return False def accept(self, visitor): if visitor.visited(self): return self.jt.accept(visitor) self.jf.accept(visitor) visitor.visit(self) def __lt__(self, o): # Defined because we want to compare tuples that contain SyscallEntries. return False def __gt__(self, o): # Defined because we want to compare tuples that contain SyscallEntries. return False class WideAtom(AbstractBlock): """A BasicBlock that represents a 32-bit wide atom.""" def __init__( self, arg_offset, op, value, jt, jf ): # pylint: disable=redefined-outer-name super().__init__() self.arg_offset = arg_offset self.op = op self.value = value self.jt = jt self.jf = jf def accept(self, visitor): if visitor.visited(self): return self.jt.accept(visitor) self.jf.accept(visitor) visitor.visit(self) class Atom(AbstractBlock): """A BasicBlock that represents an atom (a simple comparison operation).""" def __init__(self, arg_index, op, value, jt, jf): super().__init__() if op == "==": op = BPF_JEQ elif op == "!=": op = BPF_JEQ jt, jf = jf, jt elif op == ">": op = BPF_JGT elif op == "<=": op = BPF_JGT jt, jf = jf, jt elif op == ">=": op = BPF_JGE elif op == "<": op = BPF_JGE jt, jf = jf, jt elif op == "&": op = BPF_JSET elif op == "in": op = BPF_JSET # The mask is negated, so the comparison will be true when the # argument includes a flag that wasn't listed in the original # (non-negated) mask. This would be the failure case, so we switch # |jt| and |jf|. value = (~value) & ((1 << 64) - 1) jt, jf = jf, jt else: raise Exception("Unknown operator %s" % op) self.arg_index = arg_index self.op = op self.jt = jt self.jf = jf self.value = value def accept(self, visitor): if visitor.visited(self): return self.jt.accept(visitor) self.jf.accept(visitor) visitor.visit(self) class AbstractVisitor(abc.ABC): """An abstract visitor.""" def __init__(self): self._visited = set() def visited(self, block): if id(block) in self._visited: return True self._visited.add(id(block)) return False def process(self, block): block.accept(self) return block def visit(self, block): if isinstance(block, KillProcess): self.visitKillProcess(block) elif isinstance(block, KillThread): self.visitKillThread(block) elif isinstance(block, Trap): self.visitTrap(block) elif isinstance(block, ReturnErrno): self.visitReturnErrno(block) elif isinstance(block, Trace): self.visitTrace(block) elif isinstance(block, UserNotify): self.visitUserNotify(block) elif isinstance(block, Log): self.visitLog(block) elif isinstance(block, Allow): self.visitAllow(block) elif isinstance(block, BasicBlock): self.visitBasicBlock(block) elif isinstance(block, ValidateArch): self.visitValidateArch(block) elif isinstance(block, SyscallEntry): self.visitSyscallEntry(block) elif isinstance(block, WideAtom): self.visitWideAtom(block) elif isinstance(block, Atom): self.visitAtom(block) else: raise Exception("Unknown block type: %r" % block) @abc.abstractmethod def visitKillProcess(self, block): pass @abc.abstractmethod def visitKillThread(self, block): pass @abc.abstractmethod def visitTrap(self, block): pass @abc.abstractmethod def visitReturnErrno(self, block): pass @abc.abstractmethod def visitTrace(self, block): pass @abc.abstractmethod def visitUserNotify(self, block): pass @abc.abstractmethod def visitLog(self, block): pass @abc.abstractmethod def visitAllow(self, block): pass @abc.abstractmethod def visitBasicBlock(self, block): pass @abc.abstractmethod def visitValidateArch(self, block): pass @abc.abstractmethod def visitSyscallEntry(self, block): pass @abc.abstractmethod def visitWideAtom(self, block): pass @abc.abstractmethod def visitAtom(self, block): pass class CopyingVisitor(AbstractVisitor): """A visitor that copies Blocks.""" def __init__(self): super().__init__() self._mapping = {} def process(self, block): self._mapping = {} block.accept(self) return self._mapping[id(block)] def visitKillProcess(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = KillProcess() def visitKillThread(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = KillThread() def visitTrap(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = Trap() def visitReturnErrno(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = ReturnErrno(block.errno) def visitTrace(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = Trace() def visitUserNotify(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = UserNotify() def visitLog(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = Log() def visitAllow(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = Allow() def visitBasicBlock(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = BasicBlock(block.instructions) def visitValidateArch(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = ValidateArch( self._mapping[id(block.next_block)] ) def visitSyscallEntry(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = SyscallEntry( block.syscall_number, self._mapping[id(block.jt)], self._mapping[id(block.jf)], op=block.op, ) def visitWideAtom(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = WideAtom( block.arg_offset, block.op, block.value, self._mapping[id(block.jt)], self._mapping[id(block.jf)], ) def visitAtom(self, block): assert id(block) not in self._mapping self._mapping[id(block)] = Atom( block.arg_index, block.op, block.value, self._mapping[id(block.jt)], self._mapping[id(block.jf)], ) class LoweringVisitor(CopyingVisitor): """A visitor that lowers Atoms into WideAtoms.""" def __init__(self, *, arch): super().__init__() self._bits = arch.bits def visitAtom(self, block): assert id(block) not in self._mapping lo = block.value & 0xFFFFFFFF hi = (block.value >> 32) & 0xFFFFFFFF lo_block = WideAtom( arg_offset(block.arg_index, False), block.op, lo, self._mapping[id(block.jt)], self._mapping[id(block.jf)], ) if self._bits == 32: self._mapping[id(block)] = lo_block return if block.op in (BPF_JGE, BPF_JGT): # hi_1,lo_1 hi_2,lo_2 # # hi_1 > hi_2 || hi_1 == hi_2 && lo_1 lo_2 if hi == 0: # Special case: it's not needed to check whether |hi_1 == hi_2|, # because it's true iff the JGT test fails. self._mapping[id(block)] = WideAtom( arg_offset(block.arg_index, True), BPF_JGT, hi, self._mapping[id(block.jt)], lo_block, ) return hi_eq_block = WideAtom( arg_offset(block.arg_index, True), BPF_JEQ, hi, lo_block, self._mapping[id(block.jf)], ) self._mapping[id(block)] = WideAtom( arg_offset(block.arg_index, True), BPF_JGT, hi, self._mapping[id(block.jt)], hi_eq_block, ) return if block.op == BPF_JSET: # hi_1,lo_1 & hi_2,lo_2 # # hi_1 & hi_2 || lo_1 & lo_2 if hi == 0: # Special case: |hi_1 & hi_2| will never be True, so jump # directly into the |lo_1 & lo_2| case. self._mapping[id(block)] = lo_block return self._mapping[id(block)] = WideAtom( arg_offset(block.arg_index, True), block.op, hi, self._mapping[id(block.jt)], lo_block, ) return assert block.op == BPF_JEQ, block.op # hi_1,lo_1 == hi_2,lo_2 # # hi_1 == hi_2 && lo_1 == lo_2 self._mapping[id(block)] = WideAtom( arg_offset(block.arg_index, True), block.op, hi, lo_block, self._mapping[id(block.jf)], ) class FlatteningVisitor: """A visitor that flattens a DAG of Block objects.""" def __init__(self, *, arch, kill_action): self._visited = set() self._kill_action = kill_action self._instructions = [] self._arch = arch self._offsets = {} @property def result(self): return BasicBlock(self._instructions) def _distance(self, block): distance = self._offsets[id(block)] + len(self._instructions) assert distance >= 0 return distance def _emit_load_arg(self, offset): return [SockFilter(BPF_LD | BPF_W | BPF_ABS, 0, 0, offset)] def _emit_jmp(self, op, value, jt_distance, jf_distance): if jt_distance < 0x100 and jf_distance < 0x100: return [ SockFilter( BPF_JMP | op | BPF_K, jt_distance, jf_distance, value ), ] if jt_distance + 1 < 0x100: return [ SockFilter(BPF_JMP | op | BPF_K, jt_distance + 1, 0, value), SockFilter(BPF_JMP | BPF_JA, 0, 0, jf_distance), ] if jf_distance + 1 < 0x100: return [ SockFilter(BPF_JMP | op | BPF_K, 0, jf_distance + 1, value), SockFilter(BPF_JMP | BPF_JA, 0, 0, jt_distance), ] return [ SockFilter(BPF_JMP | op | BPF_K, 0, 1, value), SockFilter(BPF_JMP | BPF_JA, 0, 0, jt_distance + 1), SockFilter(BPF_JMP | BPF_JA, 0, 0, jf_distance), ] def visited(self, block): if id(block) in self._visited: return True self._visited.add(id(block)) return False def visit(self, block): assert id(block) not in self._offsets if isinstance(block, BasicBlock): instructions = block.instructions elif isinstance(block, ValidateArch): instructions = ( [ SockFilter(BPF_LD | BPF_W | BPF_ABS, 0, 0, 4), SockFilter( BPF_JMP | BPF_JEQ | BPF_K, self._distance(block.next_block) + 1, 0, self._arch.arch_nr, ), ] + self._kill_action.instructions + [ SockFilter(BPF_LD | BPF_W | BPF_ABS, 0, 0, 0), ] ) elif isinstance(block, SyscallEntry): instructions = self._emit_jmp( block.op, block.syscall_number, self._distance(block.jt), self._distance(block.jf), ) elif isinstance(block, WideAtom): instructions = self._emit_load_arg( block.arg_offset ) + self._emit_jmp( block.op, block.value, self._distance(block.jt), self._distance(block.jf), ) else: raise Exception("Unknown block type: %r" % block) self._instructions = instructions + self._instructions self._offsets[id(block)] = -len(self._instructions) class ArgFilterForwardingVisitor: """A visitor that forwards visitation to all arg filters.""" def __init__(self, visitor): self._visited = set() self.visitor = visitor def visited(self, block): if id(block) in self._visited: return True self._visited.add(id(block)) return False def visit(self, block): # All arg filters are BasicBlocks. if not isinstance(block, BasicBlock): return # But the ALLOW, KILL_PROCESS, TRAP, etc. actions are too and we don't # want to visit them just yet. if isinstance( block, ( KillProcess, KillThread, Trap, ReturnErrno, Trace, UserNotify, Log, Allow, ), ): return block.accept(self.visitor) minijail-2026.05.18+git20261016.8787d1a/tools/compile_seccomp_policy.py000077500000000000000000000133111526451555500245670ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Helper tool to compile a BPF program from a Minijail seccomp filter. This script will take a Minijail seccomp policy file and compile it into a BPF program suitable for use with Minijail in the current architecture. """ import argparse import os import sys try: import parser import arch import bpf import compiler except ImportError: from minijail import arch from minijail import bpf from minijail import compiler from minijail import parser CONSTANTS_ERR_MSG = """Could not find 'constants.json' file. See 'generate_constants_json.py -h'.""" HEADER_TEMPLATE = """/* DO NOT EDIT GENERATED FILE */ #ifndef MJ_SECCOMP_%(upper_name)s_H #define MJ_SECCOMP_%(upper_name)s_H #include static const unsigned char %(name)s_binary_seccomp_policy[] __attribute__((__aligned__(4))) = { %(program)s }; static const struct { uint16_t cnt; const void *bpf; } %(name)s_seccomp_bpf_program = { .cnt = sizeof(%(name)s_binary_seccomp_policy) / 8, .bpf = %(name)s_binary_seccomp_policy, }; #endif """ def parse_args(argv): """Return the parsed CLI arguments for this tool.""" arg_parser = argparse.ArgumentParser(description=__doc__) arg_parser.add_argument( "--optimization-strategy", default=compiler.OptimizationStrategy.BST, type=compiler.OptimizationStrategy, choices=list(compiler.OptimizationStrategy), ) arg_parser.add_argument("--include-depth-limit", default=10) arg_parser.add_argument("--arch-json", default="constants.json") arg_parser.add_argument( "--denylist", action="store_true", help="Compile as a denylist policy rather than the default allowlist.", ) arg_parser.add_argument( "--default-action", type=str, help=( "Use the specified default action, overriding any @default " "action found in the .policy files. " "This allows the use of permissive actions (allow, log, trace, " "user-notify) since it is not valid to specify a permissive " "action in .policy files. This is useful for debugging." ), ) arg_parser.add_argument( "--use-kill-process", action="store_true", help=( "Use SECCOMP_RET_KILL_PROCESS instead of " "SECCOMP_RET_KILL_THREAD (requires Linux v4.14+)." ), ) arg_parser.add_argument( "--use-ret-log", action="store_true", help=( "Change all seccomp failures to return SECCOMP_RET_LOG instead " "of killing (requires SECCOMP_RET_LOG kernel support)." ), ) arg_parser.add_argument( "--output-header-file", action="store_true", help=( "Output the compiled bpf to a constant variable in a C header " "file instead of a binary file (output should not have a .h " "extension, one will be added)." ), ) arg_parser.add_argument( "policy", help="The seccomp policy.", type=argparse.FileType("r") ) arg_parser.add_argument("output", help="The BPF program.") return arg_parser.parse_args(argv), arg_parser def main(argv=None): """Main entrypoint.""" if argv is None: argv = sys.argv[1:] opts, arg_parser = parse_args(argv) if not os.path.exists(opts.arch_json): arg_parser.error(CONSTANTS_ERR_MSG) parsed_arch = arch.Arch.load_from_json(opts.arch_json) policy_compiler = compiler.PolicyCompiler(parsed_arch) # Set ret_log to true if the MINIJAIL_DEFAULT_RET_LOG environment variable # is present. if "MINIJAIL_DEFAULT_RET_LOG" in os.environ: print( """ \n********************** Warning: MINJAIL_DEFAULT_RET_LOG is on, policy will not have any effect **********************\n """ ) opts.use_ret_log = True if opts.use_ret_log: kill_action = bpf.Log() elif opts.denylist: # Default action for a denylist policy is return EPERM kill_action = bpf.ReturnErrno(parsed_arch.constants["EPERM"]) elif opts.use_kill_process: kill_action = bpf.KillProcess() else: kill_action = bpf.KillThread() override_default_action = None if opts.default_action: parser_state = parser.ParserState("") override_default_action = parser.PolicyParser( parsed_arch, kill_action=bpf.KillProcess() ).parse_action(next(parser_state.tokenize([opts.default_action]))) compiled_policy = policy_compiler.compile_file( opts.policy.name, optimization_strategy=opts.optimization_strategy, kill_action=kill_action, include_depth_limit=opts.include_depth_limit, override_default_action=override_default_action, denylist=opts.denylist, ret_log=opts.use_ret_log, ) # Outputs the bpf binary to a c header file instead of a binary file. if opts.output_header_file: output_file_base = opts.output with open( output_file_base + ".h", "w", encoding="utf-8" ) as output_file: program = ", ".join("%i" % x for x in compiled_policy.opcodes) output_file.write( HEADER_TEMPLATE % { "upper_name": output_file_base.upper(), "name": output_file_base, "program": program, } ) else: with open(opts.output, "wb") as outf: outf.write(compiled_policy.opcodes) return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:])) minijail-2026.05.18+git20261016.8787d1a/tools/compiler.py000066400000000000000000000345721526451555500216720ustar00rootroot00000000000000# Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """A BPF compiler for the Minijail policy file.""" import enum try: import parser # pylint: disable=wrong-import-order import bpf except ImportError: from minijail import bpf from minijail import parser # pylint: disable=wrong-import-order class OptimizationStrategy(enum.Enum): """The available optimization strategies.""" # Generate a linear chain of syscall number checks. Works best for policies # with very few syscalls. LINEAR = "linear" # Generate a binary search tree for the syscalls. Works best for policies # with a lot of syscalls, where no one syscall dominates. BST = "bst" def __str__(self): return self.value class SyscallPolicyEntry: """The parsed version of a seccomp policy line.""" def __init__(self, name, number, frequency): self.name = name self.number = number self.frequency = frequency self.accumulated = 0 self.filter = None def __repr__(self): return ( "SyscallPolicyEntry" ) % ( self.name, self.number, self.frequency, self.filter.instructions if self.filter else None, ) def simulate(self, arch, syscall_number, *args): """Simulate the policy with the given arguments.""" if not self.filter: return (0, "ALLOW") return bpf.simulate( self.filter.instructions, arch, syscall_number, *args ) class SyscallPolicyRange: """A contiguous range of SyscallPolicyEntries that have the same action.""" def __init__(self, *entries): self.numbers = (entries[0].number, entries[-1].number + 1) self.frequency = sum(e.frequency for e in entries) self.accumulated = 0 self.filter = entries[0].filter def __repr__(self): return "SyscallPolicyRange" % ( self.numbers, self.frequency, self.filter.instructions if self.filter else None, ) def simulate(self, arch, syscall_number, *args): """Simulate the policy with the given arguments.""" if not self.filter: return (0, "ALLOW") return self.filter.simulate(arch, syscall_number, *args) def _convert_to_ranges(entries): entries = list(sorted(entries, key=lambda r: r.number)) lower = 0 while lower < len(entries): upper = lower + 1 while upper < len(entries): if entries[upper - 1].filter != entries[upper].filter: break if entries[upper - 1].number + 1 != entries[upper].number: break upper += 1 yield SyscallPolicyRange(*entries[lower:upper]) lower = upper def _compile_single_range( entry, accept_action, reject_action, lower_bound=0, upper_bound=1e99 ): action = accept_action if entry.filter: action = entry.filter if entry.numbers[1] - entry.numbers[0] == 1: # Single syscall. # Accept if |X == nr|. return ( 1, bpf.SyscallEntry( entry.numbers[0], action, reject_action, op=bpf.BPF_JEQ ), ) elif entry.numbers[0] == lower_bound: # Syscall range aligned with the lower bound. # Accept if |X < nr[1]|. return ( 1, bpf.SyscallEntry( entry.numbers[1], reject_action, action, op=bpf.BPF_JGE ), ) elif entry.numbers[1] == upper_bound: # Syscall range aligned with the upper bound. # Accept if |X >= nr[0]|. return ( 1, bpf.SyscallEntry( entry.numbers[0], action, reject_action, op=bpf.BPF_JGE ), ) # Syscall range in the middle. # Accept if |nr[0] <= X < nr[1]|. upper_entry = bpf.SyscallEntry( entry.numbers[1], reject_action, action, op=bpf.BPF_JGE ) return ( 2, bpf.SyscallEntry( entry.numbers[0], upper_entry, reject_action, op=bpf.BPF_JGE ), ) def _compile_ranges_linear(ranges, accept_action, reject_action): # Compiles the list of ranges into a simple linear list of comparisons. In # order to make the generated code a bit more efficient, we sort the # ranges by frequency, so that the most frequently-called syscalls appear # earlier in the chain. cost = 0 accumulated_frequencies = 0 next_action = reject_action for entry in sorted(ranges, key=lambda r: r.frequency): current_cost, next_action = _compile_single_range( entry, accept_action, next_action ) accumulated_frequencies += entry.frequency cost += accumulated_frequencies * current_cost return (cost, next_action) def _compile_entries_linear(entries, accept_action, reject_action): return _compile_ranges_linear( _convert_to_ranges(entries), accept_action, reject_action )[1] def _compile_entries_bst(entries, accept_action, reject_action): # Instead of generating a linear list of comparisons, this method generates # a binary search tree, where some of the leaves can be linear chains of # comparisons. # # Even though we are going to perform a binary search over the syscall # number, we would still like to rotate some of the internal nodes of the # binary search tree so that more frequently-used syscalls can be accessed # more cheaply (i.e. fewer internal nodes need to be traversed to reach # them). # # This uses Dynamic Programming to generate all possible BSTs efficiently # (in O(n^3)) so that we can get the absolute minimum-cost tree that matches # all syscall entries. It does so by considering all of the O(n^2) possible # sub-intervals, and for each one of those try all of the O(n) partitions of # that sub-interval. At each step, it considers putting the remaining # entries in a linear comparison chain as well as another BST, and chooses # the option that minimizes the total overall cost. # # Between every pair of non-contiguous allowed syscalls, there are two # locally optimal options as to where to set the partition for the # subsequent ranges: aligned to the end of the left subrange or to the # beginning of the right subrange. The fact that these two options have # slightly different costs, combined with the possibility of a subtree to # use the linear chain strategy (which has a completely different cost # model), causes the target cost function that we are trying to optimize to # not be unimodal / convex. This unfortunately means that more clever # techniques like using ternary search (which would reduce the overall # complexity to O(n^2 log n)) do not work in all cases. ranges = list(_convert_to_ranges(entries)) accumulated = 0 for entry in ranges: accumulated += entry.frequency entry.accumulated = accumulated # Memoization cache to build the DP table top-down, which is easier to # understand. memoized_costs = {} def _generate_syscall_bst(ranges, indices, bounds=(0, 2**64 - 1)): assert bounds[0] <= ranges[indices[0]].numbers[0], (indices, bounds) assert ranges[indices[1] - 1].numbers[1] <= bounds[1], (indices, bounds) if bounds in memoized_costs: return memoized_costs[bounds] if indices[1] - indices[0] == 1: if bounds == ranges[indices[0]].numbers: # If bounds are tight around the syscall, it costs nothing. memoized_costs[bounds] = ( 0, ranges[indices[0]].filter or accept_action, ) return memoized_costs[bounds] result = _compile_single_range( ranges[indices[0]], accept_action, reject_action ) memoized_costs[bounds] = ( result[0] * ranges[indices[0]].frequency, result[1], ) return memoized_costs[bounds] # Try the linear model first and use that as the best estimate so far. best_cost = _compile_ranges_linear( ranges[slice(*indices)], accept_action, reject_action ) # Now recursively go through all possible partitions of the interval # currently being considered. previous_accumulated = ( ranges[indices[0]].accumulated - ranges[indices[0]].frequency ) bst_comparison_cost = ( ranges[indices[1] - 1].accumulated - previous_accumulated ) for i, entry in enumerate(ranges[slice(*indices)]): candidates = [entry.numbers[0]] if i: candidates.append(ranges[i - 1 + indices[0]].numbers[1]) for cutoff_bound in candidates: if not bounds[0] < cutoff_bound < bounds[1]: continue if not indices[0] < i + indices[0] < indices[1]: continue left_subtree = _generate_syscall_bst( ranges, (indices[0], i + indices[0]), (bounds[0], cutoff_bound), ) right_subtree = _generate_syscall_bst( ranges, (i + indices[0], indices[1]), (cutoff_bound, bounds[1]), ) best_cost = min( best_cost, ( bst_comparison_cost + left_subtree[0] + right_subtree[0], bpf.SyscallEntry( cutoff_bound, right_subtree[1], left_subtree[1], op=bpf.BPF_JGE, ), ), ) memoized_costs[bounds] = best_cost return memoized_costs[bounds] return _generate_syscall_bst(ranges, (0, len(ranges)))[1] class PolicyCompiler: """A parser for the Minijail seccomp policy file format.""" def __init__(self, arch): self._arch = arch def compile_file( self, policy_filename, *, optimization_strategy, kill_action, include_depth_limit=10, override_default_action=None, denylist=False, ret_log=False, ): """Return a compiled BPF program from the provided policy file.""" policy_parser = parser.PolicyParser( self._arch, kill_action=kill_action, include_depth_limit=include_depth_limit, override_default_action=override_default_action, denylist=denylist, ret_log=ret_log, ) parsed_policy = policy_parser.parse_file(policy_filename) entries = [ self.compile_filter_statement( filter_statement, kill_action=kill_action, denylist=denylist ) for filter_statement in parsed_policy.filter_statements ] visitor = bpf.FlatteningVisitor( arch=self._arch, kill_action=kill_action ) if denylist: accept_action = kill_action reject_action = bpf.Allow() else: accept_action = bpf.Allow() reject_action = parsed_policy.default_action if entries: if optimization_strategy == OptimizationStrategy.BST: next_action = _compile_entries_bst( entries, accept_action, reject_action ) else: next_action = _compile_entries_linear( entries, accept_action, reject_action ) next_action.accept(bpf.ArgFilterForwardingVisitor(visitor)) reject_action.accept(visitor) accept_action.accept(visitor) bpf.ValidateArch(next_action).accept(visitor) else: reject_action.accept(visitor) bpf.ValidateArch(reject_action).accept(visitor) return visitor.result def compile_filter_statement( self, filter_statement, *, kill_action, denylist=False ): """Compile one parser.FilterStatement into BPF.""" policy_entry = SyscallPolicyEntry( filter_statement.syscall.name, filter_statement.syscall.number, filter_statement.frequency, ) # In each step of the way, the false action is the one that is taken if # the immediate boolean condition does not match. This means that the # false action taken here is the one that applies if the whole # expression fails to match. false_action = filter_statement.filters[-1].action if not denylist and false_action == bpf.Allow(): return policy_entry # We then traverse the list of filters backwards since we want # the root of the DAG to be the very first boolean operation in # the filter chain. for filt in filter_statement.filters[:-1][::-1]: for disjunction in filt.expression: # This is the jump target of the very last comparison in the # conjunction. Given that any conjunction that succeeds should # make the whole expression succeed, make the very last # comparison jump to the accept action if it succeeds. true_action = filt.action for atom in disjunction: block = bpf.Atom( atom.argument_index, atom.op, atom.value, true_action, false_action, ) true_action = block false_action = true_action policy_filter = false_action # Lower all Atoms into WideAtoms. lowering_visitor = bpf.LoweringVisitor(arch=self._arch) policy_filter = lowering_visitor.process(policy_filter) # Flatten the IR DAG into a single BasicBlock. flattening_visitor = bpf.FlatteningVisitor( arch=self._arch, kill_action=kill_action ) policy_filter.accept(flattening_visitor) policy_entry.filter = flattening_visitor.result return policy_entry minijail-2026.05.18+git20261016.8787d1a/tools/compiler_unittest.py000077500000000000000000000543001526451555500236230ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Unittests for the compiler module.""" import os import parser # pylint: disable=wrong-import-order import random import shutil import tempfile import unittest from importlib import resources import arch import bpf import compiler ARCH_64 = arch.Arch.load_from_json_bytes( resources.files("testdata").joinpath("arch_64.json").read_bytes() ) class CompileFilterStatementTests(unittest.TestCase): """Tests for PolicyCompiler.compile_filter_statement.""" def setUp(self): self.arch = ARCH_64 self.compiler = compiler.PolicyCompiler(self.arch) def _compile(self, line): with tempfile.NamedTemporaryFile(mode="w") as policy_file: policy_file.write(line) policy_file.flush() policy_parser = parser.PolicyParser( self.arch, kill_action=bpf.KillProcess() ) parsed_policy = policy_parser.parse_file(policy_file.name) assert len(parsed_policy.filter_statements) == 1 return self.compiler.compile_filter_statement( parsed_policy.filter_statements[0], kill_action=bpf.KillProcess(), ) def test_allow(self): """Accept lines where the syscall is accepted unconditionally.""" block = self._compile("read: allow") self.assertEqual(block.filter, None) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 1)[1], "ALLOW", ) def test_arg0_eq_generated_code(self): """Accept lines with an argument filter with ==.""" block = self._compile("read: arg0 == 0x100") # It might be a bit brittle to check the generated code in each test # case instead of just the behavior, but there should be at least one # test where this happens. self.assertEqual( block.filter.instructions, [ bpf.SockFilter( bpf.BPF_LD | bpf.BPF_W | bpf.BPF_ABS, 0, 0, bpf.arg_offset(0, True), ), # Jump to KILL_PROCESS if the high word does not match. bpf.SockFilter(bpf.BPF_JMP | bpf.BPF_JEQ | bpf.BPF_K, 0, 2, 0), bpf.SockFilter( bpf.BPF_LD | bpf.BPF_W | bpf.BPF_ABS, 0, 0, bpf.arg_offset(0, False), ), # Jump to KILL_PROCESS if the low word does not match. bpf.SockFilter( bpf.BPF_JMP | bpf.BPF_JEQ | bpf.BPF_K, 1, 0, 0x100 ), bpf.SockFilter(bpf.BPF_RET, 0, 0, bpf.SECCOMP_RET_KILL_PROCESS), bpf.SockFilter(bpf.BPF_RET, 0, 0, bpf.SECCOMP_RET_ALLOW), ], ) def test_arg0_comparison_operators(self): """Accept lines with an argument filter with comparison operators.""" biases = (-1, 0, 1) # For each operator, store the expectations of simulating the program # against the constant plus each entry from the |biases| array. cases = ( ("==", ("KILL_PROCESS", "ALLOW", "KILL_PROCESS")), ("!=", ("ALLOW", "KILL_PROCESS", "ALLOW")), ("<", ("ALLOW", "KILL_PROCESS", "KILL_PROCESS")), ("<=", ("ALLOW", "ALLOW", "KILL_PROCESS")), (">", ("KILL_PROCESS", "KILL_PROCESS", "ALLOW")), (">=", ("KILL_PROCESS", "ALLOW", "ALLOW")), ) for operator, expectations in cases: block = self._compile("read: arg0 %s 0x100" % operator) # Check the filter's behavior. for bias, expectation in zip(biases, expectations): self.assertEqual( block.simulate( self.arch.arch_nr, self.arch.syscalls["read"], 0x100 + bias, )[1], expectation, ) def test_arg0_mask_operator(self): """Accept lines with an argument filter with &.""" block = self._compile("read: arg0 & 0x3") self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[1], "KILL_PROCESS", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 1)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 2)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 3)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 4)[1], "KILL_PROCESS", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 5)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 6)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 7)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 8)[1], "KILL_PROCESS", ) def test_arg0_in_operator(self): """Accept lines with an argument filter with in.""" block = self._compile("read: arg0 in 0x3") # The 'in' operator only ensures that no bits outside the mask are set, # which means that 0 is always allowed. self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 1)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 2)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 3)[1], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 4)[1], "KILL_PROCESS", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 5)[1], "KILL_PROCESS", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 6)[1], "KILL_PROCESS", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 7)[1], "KILL_PROCESS", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 8)[1], "KILL_PROCESS", ) def test_arg0_short_gt_ge_comparisons(self): """Ensure that the short comparison optimization kicks in.""" if self.arch.bits == 32: return short_constant_str = "0xdeadbeef" short_constant = int(short_constant_str, base=0) long_constant_str = "0xbadc0ffee0ddf00d" long_constant = int(long_constant_str, base=0) biases = (-1, 0, 1) # For each operator, store the expectations of simulating the program # against the constant plus each entry from the |biases| array. cases = ( ("<", ("ALLOW", "KILL_PROCESS", "KILL_PROCESS")), ("<=", ("ALLOW", "ALLOW", "KILL_PROCESS")), (">", ("KILL_PROCESS", "KILL_PROCESS", "ALLOW")), (">=", ("KILL_PROCESS", "ALLOW", "ALLOW")), ) for operator, expectations in cases: short_block = self._compile( "read: arg0 %s %s" % (operator, short_constant_str) ) long_block = self._compile( "read: arg0 %s %s" % (operator, long_constant_str) ) # Check that the emitted code is shorter when the high word of the # constant is zero. self.assertLess( len(short_block.filter.instructions), len(long_block.filter.instructions), ) # Check the filter's behavior. for bias, expectation in zip(biases, expectations): self.assertEqual( long_block.simulate( self.arch.arch_nr, self.arch.syscalls["read"], long_constant + bias, )[1], expectation, ) self.assertEqual( short_block.simulate( self.arch.arch_nr, self.arch.syscalls["read"], short_constant + bias, )[1], expectation, ) def test_and_or(self): """Accept lines with a complex expression in DNF.""" block = self._compile("read: arg0 == 0 && arg1 == 0 || arg0 == 1") self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0, 0)[ 1 ], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0, 1)[ 1 ], "KILL_PROCESS", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 1, 0)[ 1 ], "ALLOW", ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 1, 1)[ 1 ], "ALLOW", ) def test_trap(self): """Accept lines that trap unconditionally.""" block = self._compile("read: trap") self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[1], "TRAP", ) def test_ret_errno(self): """Accept lines that return errno.""" block = self._compile("read : arg0 == 0 || arg0 == 1 ; return 1") self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[ 1: ], ("ERRNO", 1), ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 1)[ 1: ], ("ERRNO", 1), ) self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 2)[1], "KILL_PROCESS", ) def test_ret_errno_unconditionally(self): """Accept lines that return errno unconditionally.""" block = self._compile("read: return 1") self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[ 1: ], ("ERRNO", 1), ) def test_trace(self): """Accept lines that trace unconditionally.""" block = self._compile("read: trace") self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[1], "TRACE", ) def test_user_notify(self): """Accept lines that notify unconditionally.""" block = self._compile("read: user-notify") self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[1], "USER_NOTIF", ) def test_log(self): """Accept lines that log unconditionally.""" block = self._compile("read: log") self.assertEqual( block.simulate(self.arch.arch_nr, self.arch.syscalls["read"], 0)[1], "LOG", ) def test_mmap_write_xor_exec(self): """Accept the idiomatic filter for mmap.""" block = self._compile( "read : arg0 in ~PROT_WRITE || arg0 in ~PROT_EXEC" ) prot_exec_and_write = 6 for prot in range(0, 0xF): if (prot & prot_exec_and_write) == prot_exec_and_write: self.assertEqual( block.simulate( self.arch.arch_nr, self.arch.syscalls["read"], prot )[1], "KILL_PROCESS", ) else: self.assertEqual( block.simulate( self.arch.arch_nr, self.arch.syscalls["read"], prot )[1], "ALLOW", ) class CompileFileTests(unittest.TestCase): """Tests for PolicyCompiler.compile_file.""" def setUp(self): self.arch = ARCH_64 self.compiler = compiler.PolicyCompiler(self.arch) self.tempdir = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.tempdir) def _write_file(self, filename, contents): """Helper to write out a file for testing.""" path = os.path.join(self.tempdir, filename) with open(path, "w", encoding="utf-8") as outf: outf.write(contents) return path def test_compile(self): """Ensure compilation works with all strategies.""" self._write_file( "test.frequency", """ read: 1 close: 10 """, ) path = self._write_file( "test.policy", """ @frequency ./test.frequency read: 1 close: 1 """, ) program = self.compiler.compile_file( path, optimization_strategy=compiler.OptimizationStrategy.LINEAR, kill_action=bpf.KillProcess(), ) self.assertGreater( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls["read"], 0, )[0], bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls["close"], 0, )[0], ) def test_compile_bst(self): """Ensure compilation with BST is cheaper than the linear model.""" self._write_file( "test.frequency", """ read: 1 close: 10 """, ) path = self._write_file( "test.policy", """ @frequency ./test.frequency read: 1 close: 1 """, ) for strategy in list(compiler.OptimizationStrategy): program = self.compiler.compile_file( path, optimization_strategy=strategy, kill_action=bpf.KillProcess(), ) self.assertGreater( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls["read"], 0, )[0], bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls["close"], 0, )[0], ) self.assertEqual( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls["read"], 0, )[1], "ALLOW", ) self.assertEqual( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls["close"], 0, )[1], "ALLOW", ) def test_compile_empty_file(self): """Accept empty files.""" path = self._write_file( "test.policy", """ @default kill-thread """, ) for strategy in list(compiler.OptimizationStrategy): program = self.compiler.compile_file( path, optimization_strategy=strategy, kill_action=bpf.KillProcess(), ) self.assertEqual( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls["read"], 0, )[1], "KILL_THREAD", ) def test_compile_simulate(self): """Ensure policy reflects script by testing some random scripts.""" iterations = 5 for i in range(iterations): num_entries = 64 * (i + 1) // iterations syscalls = dict( zip( random.sample(list(self.arch.syscalls.keys()), num_entries), (random.randint(1, 1024) for _ in range(num_entries)), ) ) frequency_contents = "\n".join( "%s: %d" % s for s in syscalls.items() ) policy_contents = "@frequency ./test.frequency\n" + "\n".join( "%s: 1" % s[0] for s in syscalls.items() ) self._write_file("test.frequency", frequency_contents) path = self._write_file("test.policy", policy_contents) for strategy in list(compiler.OptimizationStrategy): program = self.compiler.compile_file( path, optimization_strategy=strategy, kill_action=bpf.KillProcess(), ) for name, number in self.arch.syscalls.items(): expected_result = ( "ALLOW" if name in syscalls else "KILL_PROCESS" ) self.assertEqual( bpf.simulate( program.instructions, self.arch.arch_nr, number, 0 )[1], expected_result, ( "syscall name: %s, syscall number: %d, " "strategy: %s, policy:\n%s" ) % (name, number, strategy, policy_contents), ) @unittest.skipIf(not int(os.getenv("SLOW_TESTS", "0")), "slow") def test_compile_huge_policy(self): """Ensure jumps while compiling a huge policy are still valid.""" # Given that the BST strategy is O(n^3), don't choose a crazy large # value, but it still needs to be around 128 so that we exercise the # codegen paths that depend on the length of the jump. # # Immediate jump offsets in BPF comparison instructions are limited to # 256 instructions, so given that every syscall filter consists of a # load and jump instructions, with 128 syscalls there will be at least # one jump that's further than 256 instructions. num_entries = 128 syscalls = dict(random.sample(self.arch.syscalls.items(), num_entries)) # Here we force every single filter to be distinct. Otherwise the # codegen layer will coalesce filters that compile to the same # instructions. policy_contents = "\n".join( "%s: arg0 == %d" % s for s in syscalls.items() ) path = self._write_file("test.policy", policy_contents) program = self.compiler.compile_file( path, optimization_strategy=compiler.OptimizationStrategy.BST, kill_action=bpf.KillProcess(), ) for name, number in self.arch.syscalls.items(): expected_result = "ALLOW" if name in syscalls else "KILL_PROCESS" self.assertEqual( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls[name], number, )[1], expected_result, ) self.assertEqual( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls[name], number + 1, )[1], "KILL_PROCESS", ) def test_compile_huge_filter(self): """Ensure jumps while compiling a huge policy are still valid.""" # This is intended to force cases where the AST visitation would result # in a combinatorial explosion of calls to Block.accept(). An optimized # implementation should be O(n). num_entries = 128 syscalls = {} # Here we force every single filter to be distinct. Otherwise the # codegen layer will coalesce filters that compile to the same # instructions. policy_contents = [] for name in random.sample(list(self.arch.syscalls.keys()), num_entries): values = random.sample(range(1024), num_entries) syscalls[name] = values policy_contents.append( "%s: %s" % (name, " || ".join("arg0 == %d" % value for value in values)) ) path = self._write_file("test.policy", "\n".join(policy_contents)) program = self.compiler.compile_file( path, optimization_strategy=compiler.OptimizationStrategy.LINEAR, kill_action=bpf.KillProcess(), ) for name, values in syscalls.items(): self.assertEqual( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls[name], random.choice(values), )[1], "ALLOW", ) self.assertEqual( bpf.simulate( program.instructions, self.arch.arch_nr, self.arch.syscalls[name], 1025, )[1], "KILL_PROCESS", ) if __name__ == "__main__": unittest.main() minijail-2026.05.18+git20261016.8787d1a/tools/generate_constants_json.py000077500000000000000000000122531526451555500247720ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Helper tool to generate cross-compiled syscall and constant tables to JSON. This script takes the LLVM IR of libconstants.gen.c and libsyscalls.gen.c and generates the `constants.json` file with that. LLVM IR files are moderately architecture-neutral (at least for this case). """ import argparse import collections import json import re import sys _STRING_CONSTANT_RE = re.compile(r'(@[a-zA-Z0-9.]+) = .*c"([^"\\]+)\\00".*') _TABLE_ENTRY_RE = re.compile( r"%struct.(?:constant|syscall)_entry\s*{\s*([^}]+)\s*}" ) # This looks something like # # i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i32 0, i32 0), i32 5 # # For arm-v7a. What we are interested in are the @.str.x and the very last # number. _TABLE_ENTRY_CONTENTS = re.compile(r".*?(null|@[a-zA-Z0-9.]+).* (-?\d+)") # pylint: disable=line-too-long # When testing clang-r458909, we found a new constant_entry pattern: # %struct.constant_entry { ptr @.str.894, i32 ptrtoint (ptr @.str.895 to i32) }, # For the same constant, current clang-r458507 generates: # %struct.constant_entry { i8* getelementptr inbounds # ([19 x i8], [19 x i8]* @.str.894, i32 0, i32 0), # i32 ptrtoint ([9 x i8]* @.str.895 to i32) }, # This is for a char* constant defined in linux-x86/libconstants.gen.c: # { "FS_KEY_DESC_PREFIX", (unsigned long) FS_KEY_DESC_PREFIX }, # and FS_KEY_DESC_PREFIX is defined as a char* "fscrypt:" # Current output for that constant in constants.json is: # "FS_KEY_DESC_PREFIX": 0, # but that value does not seem to be useful or accurate. # So here we define a pattern to ignore such pointer constants: # pylint: enable=line-too-long _IGNORED_ENTRY_CONTENTS = re.compile(r".*? ptrto.* \(.*\)") ParseResults = collections.namedtuple( "ParseResults", ["table_name", "table_entries"] ) HELP_EPILOG = """\ Generate LLVM IR: clang -S -emit-llvm libconstants.gen.c libsyscalls.gen.c """ def parse_llvm_ir(ir): """Parses a single LLVM IR file.""" string_constants = collections.OrderedDict() table_entries = collections.OrderedDict() table_name = "" for line in ir: string_constant_match = _STRING_CONSTANT_RE.match(line) if string_constant_match: string_constants[ string_constant_match.group(1) ] = string_constant_match.group(2) continue if "@syscall_table" in line or "@constant_table" in line: if "@syscall_table" in line: table_name = "syscalls" else: table_name = "constants" for entry in _TABLE_ENTRY_RE.findall(line): groups = _TABLE_ENTRY_CONTENTS.match(entry) if not groups: if _IGNORED_ENTRY_CONTENTS.match(entry): continue raise ValueError("Failed to parse table entry %r" % entry) name, value = groups.groups() if name == "null": # This is the end-of-table marker. break table_entries[string_constants[name]] = int(value) return ParseResults(table_name=table_name, table_entries=table_entries) def main(argv=None): """Main entrypoint.""" if argv is None: argv = sys.argv[1:] parser = argparse.ArgumentParser(description=__doc__, epilog=HELP_EPILOG) parser.add_argument( "--output", help="The path of the generated constants.json file.", type=argparse.FileType("w"), required=True, ) parser.add_argument( "llvm_ir_files", help="An LLVM IR file with one of the {constants,syscall} table.", metavar="llvm_ir_file", nargs="+", type=argparse.FileType("r"), ) opts = parser.parse_args(argv) constants_json = {} for ir in opts.llvm_ir_files: parse_results = parse_llvm_ir(ir) constants_json[parse_results.table_name] = parse_results.table_entries # Populate the top-level fields. constants_json["arch_nr"] = constants_json["constants"]["MINIJAIL_ARCH_NR"] constants_json["bits"] = constants_json["constants"]["MINIJAIL_ARCH_BITS"] # It is a bit more complicated to generate the arch_name, since the # constants can only output numeric values. Use a hardcoded mapping instead. if constants_json["arch_nr"] == 0xC000003E: constants_json["arch_name"] = "x86_64" elif constants_json["arch_nr"] == 0x40000003: constants_json["arch_name"] = "x86" elif constants_json["arch_nr"] == 0xC00000B7: constants_json["arch_name"] = "arm64" elif constants_json["arch_nr"] == 0x40000028: constants_json["arch_name"] = "arm" elif constants_json["arch_nr"] == 0xC0000102: constants_json["arch_name"] = "loong64" elif constants_json["arch_nr"] == 0xC00000F3: constants_json["arch_name"] = "riscv64" else: raise ValueError( "Unknown architecture: 0x%08X" % constants_json["arch_nr"] ) json.dump(constants_json, opts.output, indent=" ") return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:])) minijail-2026.05.18+git20261016.8787d1a/tools/generate_seccomp_policy.py000077500000000000000000000334161526451555500247410ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # This script will take any number of trace files generated by strace(1) # and output a system call filtering policy suitable for use with Minijail. """Tool to generate a minijail seccomp filter from strace or audit output.""" import argparse import collections import datetime import os import re import sys # auparse may not be installed and is currently optional. try: import auparse except ImportError: auparse = None YEAR = datetime.datetime.now().year NOTICE = f"""# Copyright {YEAR} The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """ ALLOW = "1" # This ignores any leading PID tag and trailing , and extracts # the syscall name and the argument list. LINE_RE = re.compile(r"^\s*(?:\[[^]]*\]|\d+)?\s*([a-zA-Z0-9_]+)\(([^)<]*)") SOCKETCALLS = { "accept", "bind", "connect", "getpeername", "getsockname", "getsockopt", "listen", "recv", "recvfrom", "recvmsg", "send", "sendmsg", "sendto", "setsockopt", "shutdown", "socket", "socketpair", } # List of private ARM syscalls. These can be found in any ARM specific unistd.h # such as Linux's arch/arm/include/uapi/asm/unistd.h. PRIVATE_ARM_SYSCALLS = { 983041: "ARM_breakpoint", 983042: "ARM_cacheflush", 983043: "ARM_usr26", 983044: "ARM_usr32", 983045: "ARM_set_tls", } ArgInspectionEntry = collections.namedtuple( "ArgInspectionEntry", ("arg_index", "value_set") ) # pylint: disable=too-few-public-methods class BucketInputFiles(argparse.Action): """Buckets input files using simple content based heuristics. Attributes: audit_logs: Mutually exclusive list of audit log filenames. traces: Mutually exclusive list of strace log filenames. """ def __call__(self, parser, namespace, values, option_string=None): audit_logs = [] traces = [] strace_line_re = re.compile(r"[a-z]+[0-9]*\(.+\) += ") audit_line_re = re.compile(r"type=(SYSCALL|SECCOMP)") for filename in values: if not os.path.exists(filename): parser.error(f"Input file {filename} not found.") with open(filename, mode="r", encoding="utf-8") as input_file: for line in input_file.readlines(): if strace_line_re.search(line): traces.append(filename) break if audit_line_re.search(line): audit_logs.append(filename) break else: # Treat it as an strace log to retain legacy behaviour and # also just in case the strace regex is imperfect. traces.append(filename) setattr(namespace, "audit_logs", audit_logs) setattr(namespace, "traces", traces) # pylint: enable=too-few-public-methods def parse_args(argv): """Returns the parsed CLI arguments for this tool.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--verbose", action="store_true", help="output informational messages to stderr", ) parser.add_argument( "--frequency", type=argparse.FileType("w"), help="frequency file" ) parser.add_argument( "--policy", type=argparse.FileType("w"), default=sys.stdout, help="policy file", ) parser.add_argument( "input-logs", action=BucketInputFiles, help="strace and/or audit logs", nargs="+", ) parser.add_argument( "--audit-comm", type=str, metavar="PROCESS_NAME", help="relevant process name from the audit.log files", ) opts = parser.parse_args(argv) if opts.audit_logs and not auparse: parser.error( "Python bindings for the audit subsystem were not found.\n" "Please install the python3-audit (sometimes python-audit)" " package for your distro to process audit logs: " f"{opts.audit_logs}" ) if opts.audit_logs and not opts.audit_comm: parser.error( f"--audit-comm is required when using audit logs as input:" f" {opts.audit_logs}" ) if not opts.audit_logs and opts.audit_comm: parser.error( "--audit-comm was specified yet none of the input files " "matched our hueristic for an audit log" ) return opts def get_seccomp_bpf_filter(syscall, entry): """Returns a minijail seccomp-bpf filter expression for the syscall.""" arg_index = entry.arg_index arg_values = entry.value_set atoms = [] if syscall in ("mmap", "mmap2", "mprotect") and arg_index == 2: # See if there is at least one instance of any of these syscalls trying # to map memory with both PROT_EXEC and PROT_WRITE. If there isn't, we # can craft a concise expression to forbid this. write_and_exec = set(("PROT_EXEC", "PROT_WRITE")) for arg_value in arg_values: if write_and_exec.issubset( set(p.strip() for p in arg_value.split("|")) ): break else: atoms.extend(["arg2 in ~PROT_EXEC", "arg2 in ~PROT_WRITE"]) arg_values = set() atoms.extend(f"arg{arg_index} == {arg_value}" for arg_value in arg_values) return " || ".join(atoms) def parse_trace_file(trace_filename, syscalls, arg_inspection): """Parses one file produced by strace.""" uses_socketcall = "i386" in trace_filename or ( "x86" in trace_filename and "64" not in trace_filename ) with open(trace_filename, encoding="utf-8") as trace_file: for line in trace_file: matches = LINE_RE.match(line) if not matches: continue syscall, args = matches.groups() if uses_socketcall and syscall in SOCKETCALLS: syscall = "socketcall" # strace omits the 'ARM_' prefix on all private ARM syscalls. Add # it manually here as a workaround. These syscalls are exclusive # to ARM so we don't need to predicate this on a trace_filename # based heuristic for the arch. if f"ARM_{syscall}" in PRIVATE_ARM_SYSCALLS.values(): syscall = f"ARM_{syscall}" syscalls[syscall] += 1 args = [arg.strip() for arg in args.split(",")] if syscall in arg_inspection: arg_value = args[arg_inspection[syscall].arg_index] arg_inspection[syscall].value_set.add(arg_value) def parse_audit_log(audit_log, audit_comm, syscalls, arg_inspection): """Parses one audit.log file generated by the Linux audit subsystem.""" unknown_syscall_re = re.compile(r"unknown-syscall\((?P\d+)\)") au = auparse.AuParser(auparse.AUSOURCE_FILE, audit_log) # Quick validity check for whether this parses as a valid audit log. The # first event should have at least one record. if not au.first_record(): raise ValueError(f"Unable to parse audit log file {audit_log.name}") # Iterate through events where _any_ contained record matches # ((type == SECCOMP || type == SYSCALL) && comm == audit_comm). au.search_add_item("type", "=", "SECCOMP", auparse.AUSEARCH_RULE_CLEAR) au.search_add_item("type", "=", "SYSCALL", auparse.AUSEARCH_RULE_OR) au.search_add_item( "comm", "=", f'"{audit_comm}"', auparse.AUSEARCH_RULE_AND ) # auparse_find_field(3) will ignore preceding fields in the record and # at the same time happily cross record boundaries when looking for the # field. This helper method always seeks the cursor back to the first # field in the record and stops searching before crossing over to the # next record; making the search far less error prone. # Also implicitly seeks the internal 'cursor' to the matching field # for any subsequent calls like auparse_interpret_field. def _find_field_in_current_record(name): au.first_field() while True: if au.get_field_name() == name: return au.get_field_str() if not au.next_field(): return None while au.search_next_event(): # The event may have multiple records. Loop through all. au.first_record() for _ in range(au.get_num_records()): event_type = _find_field_in_current_record("type") comm = _find_field_in_current_record("comm") # Some of the records in this event may not be relevant # despite the event-specific search filter. Skip those. if ( event_type not in ("SECCOMP", "SYSCALL") or comm != f'"{audit_comm}"' ): au.next_record() continue if not _find_field_in_current_record("syscall"): raise ValueError( f'Could not find field "syscall" in event of ' f"type {event_type}" ) # Intepret the syscall field that's under our 'cursor' following the # find. Interpreting fields yields human friendly names instead # of integers. E.g '16' -> 'ioctl'. syscall = au.interpret_field() # TODO(crbug/1172449): Add these syscalls to upstream # audit-userspace and remove this workaround. # This is redundant but safe for non-ARM architectures due to the # disjoint set of private syscall numbers. match = unknown_syscall_re.match(syscall) if match: syscall_num = int(match.group("syscall_num")) syscall = PRIVATE_ARM_SYSCALLS.get(syscall_num, syscall) if (syscall in arg_inspection and event_type == "SECCOMP") or ( syscall not in arg_inspection and event_type == "SYSCALL" ): # Skip SECCOMP records for syscalls that require argument # inspection. Similarly, skip SYSCALL records for syscalls # that do not require argument inspection. Technically such # records wouldn't exist per our setup instructions but audit # sometimes lets a few records slip through. au.next_record() continue elif event_type == "SYSCALL": arg_field_name = f"a{arg_inspection[syscall].arg_index}" if not _find_field_in_current_record(arg_field_name): raise ValueError( f'Could not find field "{arg_field_name}"' f"in event of type {event_type}" ) # Intepret the arg field that's under our 'cursor' following the # find. This may yield a more human friendly name. # E.g '5401' -> 'TCGETS'. arg_inspection[syscall].value_set.add(au.interpret_field()) syscalls[syscall] += 1 au.next_record() def main(argv=None): """Main entrypoint.""" if argv is None: argv = sys.argv[1:] opts = parse_args(argv) syscalls = collections.defaultdict(int) arg_inspection = { "socket": ArgInspectionEntry(0, set([])), # int domain "ioctl": ArgInspectionEntry(1, set([])), # int request "prctl": ArgInspectionEntry(0, set([])), # int option "mmap": ArgInspectionEntry(2, set([])), # int prot "mmap2": ArgInspectionEntry(2, set([])), # int prot "mprotect": ArgInspectionEntry(2, set([])), # int prot } if opts.verbose: # Print an informational message to stderr in case the filetype # detection heuristics are wonky. print( "Generating a seccomp policy using these input files:", file=sys.stderr, ) print(f"Strace logs: {opts.traces}", file=sys.stderr) print(f"Audit logs: {opts.audit_logs}", file=sys.stderr) for trace_filename in opts.traces: parse_trace_file(trace_filename, syscalls, arg_inspection) for audit_log in opts.audit_logs: parse_audit_log(audit_log, opts.audit_comm, syscalls, arg_inspection) # Add the basic set if they are not yet present. basic_set = [ "restart_syscall", "exit", "exit_group", "rt_sigreturn", ] for basic_syscall in basic_set: if basic_syscall not in syscalls: syscalls[basic_syscall] = 1 # If a frequency file isn't used then sort the syscalls based on frequency # to make the common case fast (by checking frequent calls earlier). # Otherwise, sort alphabetically to make it easier for humans to see which # calls are in use (and if necessary manually add a new syscall to the # list). if opts.frequency is None: sorted_syscalls = list( x[0] for x in sorted( syscalls.items(), key=lambda pair: pair[1], reverse=True ) ) else: sorted_syscalls = list( x[0] for x in sorted(syscalls.items(), key=lambda pair: pair[0]) ) print(NOTICE, file=opts.policy) if opts.frequency is not None: print(NOTICE, file=opts.frequency) for syscall in sorted_syscalls: if syscall in arg_inspection: arg_filter = get_seccomp_bpf_filter( syscall, arg_inspection[syscall] ) else: arg_filter = ALLOW print(f"{syscall}: {arg_filter}", file=opts.policy) if opts.frequency is not None: print(f"{syscall}: {syscalls[syscall]}", file=opts.frequency) if __name__ == "__main__": sys.exit(main(sys.argv[1:])) minijail-2026.05.18+git20261016.8787d1a/tools/parser.py000066400000000000000000001024041526451555500213420ustar00rootroot00000000000000# Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """A parser for the Minijail policy file.""" import collections import itertools import os.path import re try: import bpf except ImportError: from minijail import bpf # Representations of numbers with different radix (base) in C. HEX_REGEX = r"-?0[xX][0-9a-fA-F]+" OCTAL_REGEX = r"-?0[0-7]+" DECIMAL_REGEX = r"-?[0-9]+" Token = collections.namedtuple( "Token", ["type", "value", "filename", "line", "line_number", "column"] ) # A regex that can tokenize a Minijail policy file line. _TOKEN_SPECIFICATION = ( ("COMMENT", r"#.*$"), ("WHITESPACE", r"\s+"), ("CONTINUATION", r"\\$"), ("DEFAULT", r"@default\b"), ("INCLUDE", r"@include\b"), ("FREQUENCY", r"@frequency\b"), ("DENYLIST", r"@denylist$"), ("PATH", r"(?:\.)?/\S+"), ("NUMERIC_CONSTANT", f"{HEX_REGEX}|{OCTAL_REGEX}|{DECIMAL_REGEX}"), ("COLON", r":"), ("SEMICOLON", r";"), ("COMMA", r","), ("BITWISE_COMPLEMENT", r"~"), ("LPAREN", r"\("), ("RPAREN", r"\)"), ("LBRACE", r"\{"), ("RBRACE", r"\}"), ("RBRACKET", r"\]"), ("LBRACKET", r"\["), ("OR", r"\|\|"), ("AND", r"&&"), ("BITWISE_OR", r"\|"), ("OP", r"&|\bin\b|==|!=|<=|<|>=|>"), ("EQUAL", r"="), ("ARGUMENT", r"\barg[0-9]+\b"), ("RETURN", r"\breturn\b"), ( "ACTION", r"\ballow\b|\bkill-process\b|\bkill-thread\b|\bkill\b|\btrap\b|" r"\btrace\b|\blog\b|\buser-notify\b", ), ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z_0-9-@]*"), ) _TOKEN_RE = re.compile( "|".join(r"(?P<%s>%s)" % pair for pair in _TOKEN_SPECIFICATION) ) class ParseException(Exception): """An exception that is raised when parsing fails.""" # pylint: disable=too-many-arguments def __init__( self, message, filename, *, line="", line_number=1, token=None ): if token: line = token.line line_number = token.line_number column = token.column length = len(token.value) else: column = len(line) length = 1 message = ("%s(%d:%d): %s") % ( filename, line_number, column + 1, message, ) message += "\n %s" % line message += "\n %s%s" % (" " * column, "^" * length) super().__init__(message) class ParserState: """Stores the state of the Parser to provide better diagnostics.""" def __init__(self, filename): self._filename = filename self._line = "" self._line_number = 0 @property def filename(self): """Return the name of the file being processed.""" return self._filename @property def line(self): """Return the current line being processed.""" return self._line @property def line_number(self): """Return the current line number being processed.""" return self._line_number def error(self, message, token=None): """Raise a ParserException with the provided message.""" raise ParseException( message, self.filename, line=self._line, line_number=self._line_number, token=token, ) def tokenize(self, lines): """Return a list of tokens for the current line.""" tokens = [] for line_number, line in enumerate(lines): self._line_number = line_number + 1 self._line = line.rstrip("\r\n") last_end = 0 for token in _TOKEN_RE.finditer(self._line): if token.start() != last_end: self.error( "invalid token", token=Token( "INVALID", self._line[last_end : token.start()], self.filename, self._line, self._line_number, last_end, ), ) last_end = token.end() # Omit whitespace and comments now to avoid sprinkling this # logic elsewhere. if token.lastgroup in ("WHITESPACE", "COMMENT", "CONTINUATION"): continue tokens.append( Token( token.lastgroup, token.group(), self.filename, self._line, self._line_number, token.start(), ) ) if last_end != len(self._line): self.error( "invalid token", token=Token( "INVALID", self._line[last_end:], self.filename, self._line, self._line_number, last_end, ), ) if self._line.endswith("\\"): # This line is not finished yet. continue if tokens: # Return a copy of the token list so that the caller can be free # to modify it. yield tokens[::] tokens.clear() Atom = collections.namedtuple("Atom", ["argument_index", "op", "value"]) """A single boolean comparison within a filter expression.""" Filter = collections.namedtuple("Filter", ["expression", "action"]) """The result of parsing a DNF filter expression, with its action. Since the expression is in Disjunctive Normal Form, it is composed of two levels of lists, one for disjunctions and the inner one for conjunctions. The elements of the inner list are Atoms. """ Syscall = collections.namedtuple("Syscall", ["name", "number"]) """A system call.""" ParsedFilterStatement = collections.namedtuple( "ParsedFilterStatement", ["syscalls", "filters", "token"] ) """The result of parsing a filter statement. Statements have a list of syscalls, and an associated list of filters that will be evaluated sequentially when any of the syscalls is invoked. """ FilterStatement = collections.namedtuple( "FilterStatement", ["syscall", "frequency", "filters"] ) """The filter list for a particular syscall. This is a mapping from one syscall to a list of filters that are evaluated sequentially. The last filter is always an unconditional action. """ ParsedPolicy = collections.namedtuple( "ParsedPolicy", ["default_action", "filter_statements"] ) """The result of parsing a minijail .policy file.""" # pylint: disable=too-few-public-methods class PolicyParser: """A parser for the Minijail seccomp policy file format.""" def __init__( self, arch, *, kill_action, include_depth_limit=10, override_default_action=None, denylist=False, ret_log=False, ): self._parser_states = [ParserState("")] self._kill_action = kill_action self._include_depth_limit = include_depth_limit if denylist: self._default_action = bpf.Allow() else: self._default_action = self._kill_action self._override_default_action = override_default_action self._frequency_mapping = collections.defaultdict(int) self._arch = arch self._denylist = denylist self._ret_log = ret_log @property def _parser_state(self): return self._parser_states[-1] # single-constant = identifier # | numeric-constant # ; def _parse_single_constant(self, token): if token.type == "IDENTIFIER": if token.value not in self._arch.constants: self._parser_state.error("invalid constant", token=token) single_constant = self._arch.constants[token.value] elif token.type == "NUMERIC_CONSTANT": # As `int(_, 0)` in Python != `strtol(_, _, 0)` in C, to make sure # the number parsing behaves exactly in C, instead of using `int()` # directly, we list out all the possible formats for octal, decimal # and hex numbers, and determine the corresponding base by regex. try: if re.match(HEX_REGEX, token.value): base = 16 elif re.match(OCTAL_REGEX, token.value): base = 8 elif re.match(DECIMAL_REGEX, token.value): base = 10 else: # This should never happen. raise ValueError single_constant = int(token.value, base=base) except ValueError: self._parser_state.error("invalid constant", token=token) else: self._parser_state.error("invalid constant", token=token) if single_constant > self._arch.max_unsigned: self._parser_state.error("unsigned overflow", token=token) elif single_constant < self._arch.min_signed: self._parser_state.error("signed underflow", token=token) elif single_constant < 0: # This converts the constant to an unsigned representation of the # same value, since BPF only uses unsigned values. single_constant = self._arch.truncate_word(single_constant) return single_constant # constant = [ '~' ] , '(' , value , ')' # | [ '~' ] , single-constant # ; def _parse_constant(self, tokens): negate = False if tokens[0].type == "BITWISE_COMPLEMENT": negate = True tokens.pop(0) if not tokens: self._parser_state.error("empty complement") if tokens[0].type == "BITWISE_COMPLEMENT": self._parser_state.error( "invalid double complement", token=tokens[0] ) if tokens[0].type == "LPAREN": last_open_paren = tokens.pop(0) single_value = self.parse_value(tokens) if not tokens or tokens[0].type != "RPAREN": self._parser_state.error( "unclosed parenthesis", token=last_open_paren ) else: single_value = self._parse_single_constant(tokens[0]) tokens.pop(0) if negate: single_value = self._arch.truncate_word(~single_value) return single_value # value = constant , [ { '|' , constant } ] # ; def parse_value(self, tokens): """Parse constants separated bitwise OR operator |. Constants can be: - A number that can be parsed with strtol() in C. - A named constant expression. - A parenthesized, valid constant expression. - A valid constant expression prefixed with the unary bitwise complement operator ~. - A series of valid constant expressions separated by bitwise OR operator |. If there is an error parsing any of the constants, the whole process fails. """ value = 0 while tokens: value |= self._parse_constant(tokens) if not tokens or tokens[0].type != "BITWISE_OR": break tokens.pop(0) else: self._parser_state.error("empty constant") return value # atom = argument , op , value # ; def _parse_atom(self, tokens): if not tokens: self._parser_state.error("missing argument") argument = tokens.pop(0) if argument.type != "ARGUMENT": self._parser_state.error("invalid argument", token=argument) if not tokens: self._parser_state.error("missing operator") operator = tokens.pop(0) if operator.type != "OP": self._parser_state.error("invalid operator", token=operator) value = self.parse_value(tokens) argument_index = int(argument.value[3:]) if not 0 <= argument_index < bpf.MAX_SYSCALL_ARGUMENTS: self._parser_state.error("invalid argument", token=argument) return Atom(argument_index, operator.value, value) # clause = atom , [ { '&&' , atom } ] # ; def _parse_clause(self, tokens): atoms = [] while tokens: atoms.append(self._parse_atom(tokens)) if not tokens or tokens[0].type != "AND": break tokens.pop(0) else: self._parser_state.error("empty clause") return atoms # argument-expression = clause , [ { '||' , clause } ] # ; def parse_argument_expression(self, tokens): """Parse a argument expression in Disjunctive Normal Form. Since BPF disallows back jumps, we build the basic blocks in reverse order so that all the jump targets are known by the time we need to reference them. """ clauses = [] while tokens: clauses.append(self._parse_clause(tokens)) if not tokens or tokens[0].type != "OR": break tokens.pop(0) else: self._parser_state.error("empty argument expression") return clauses # default-action = 'kill-process' # | 'kill-thread' # | 'kill' # | 'trap' # | 'user-notify' # ; def _parse_default_action(self, tokens): if not tokens: self._parser_state.error("missing default action") action_token = tokens.pop(0) if action_token.type != "ACTION": return self._parser_state.error( "invalid default action", token=action_token ) if action_token.value == "kill-process": return bpf.KillProcess() if action_token.value == "kill-thread": return bpf.KillThread() if action_token.value == "kill": return self._kill_action if action_token.value == "trap": return bpf.Trap() if action_token.value == "user-notify": return bpf.UserNotify() return self._parser_state.error( "invalid permissive default action", token=action_token ) # action = 'allow' | '1' # | 'kill-process' # | 'kill-thread' # | 'kill' # | 'trap' # | 'trace' # | 'log' # | 'user-notify' # | 'return' , single-constant # ; def parse_action(self, tokens): if not tokens: self._parser_state.error("missing action") action_token = tokens.pop(0) # denylist policies must specify a return for every line. if self._denylist: if action_token.type != "RETURN": self._parser_state.error("invalid denylist policy") if action_token.type == "ACTION": if action_token.value == "allow": return bpf.Allow() if action_token.value == "kill": return self._kill_action if action_token.value == "kill-process": return bpf.KillProcess() if action_token.value == "kill-thread": return bpf.KillThread() if action_token.value == "trap": return bpf.Trap() if action_token.value == "trace": return bpf.Trace() if action_token.value == "user-notify": return bpf.UserNotify() if action_token.value == "log": return bpf.Log() elif action_token.type == "NUMERIC_CONSTANT": constant = self._parse_single_constant(action_token) if constant == 1: return bpf.Allow() elif action_token.type == "RETURN": if not tokens: self._parser_state.error("missing return value") if self._ret_log: tokens.pop(0) return bpf.Log() else: return bpf.ReturnErrno( self._parse_single_constant(tokens.pop(0)) ) return self._parser_state.error("invalid action", token=action_token) # single-filter = action # | argument-expression , [ ';' , action ] # | '!','(', argument-expression, [ ';', action ], ')' # ; def _parse_single_filter(self, tokens): if not tokens: self._parser_state.error("missing filter") if tokens[0].type == "ARGUMENT": # Only argument expressions can start with an ARGUMENT token. argument_expression = self.parse_argument_expression(tokens) if tokens and tokens[0].type == "SEMICOLON": tokens.pop(0) action = self.parse_action(tokens) else: action = bpf.Allow() return Filter(argument_expression, action) else: return Filter(None, self.parse_action(tokens)) # filter = '{' , single-filter , [ { ',' , single-filter } ] , '}' # | single-filter # ; def parse_filter(self, tokens): """Parse a filter and return a list of Filter objects.""" if not tokens: self._parser_state.error("missing filter") filters = [] if tokens[0].type == "LBRACE": opening_brace = tokens.pop(0) while tokens: filters.append(self._parse_single_filter(tokens)) if not tokens or tokens[0].type != "COMMA": break tokens.pop(0) if not tokens or tokens[0].type != "RBRACE": self._parser_state.error("unclosed brace", token=opening_brace) tokens.pop(0) else: filters.append(self._parse_single_filter(tokens)) return filters # key-value-pair = identifier , '=', identifier , [ { ',' , identifier } ] # ; def _parse_key_value_pair(self, tokens): if not tokens: self._parser_state.error("missing key") key = tokens.pop(0) if key.type != "IDENTIFIER": self._parser_state.error("invalid key", token=key) if not tokens: self._parser_state.error("missing equal") if tokens[0].type != "EQUAL": self._parser_state.error("invalid equal", token=tokens[0]) tokens.pop(0) value_list = [] while tokens: value = tokens.pop(0) if value.type != "IDENTIFIER": self._parser_state.error("invalid value", token=value) value_list.append(value.value) if not tokens or tokens[0].type != "COMMA": break tokens.pop(0) else: self._parser_state.error("empty value") return (key.value, value_list) # metadata = '[' , key-value-pair , [ { ';' , key-value-pair } ] , ']' # ; def _parse_metadata(self, tokens): if not tokens: self._parser_state.error("missing opening bracket") opening_bracket = tokens.pop(0) if opening_bracket.type != "LBRACKET": self._parser_state.error( "invalid opening bracket", token=opening_bracket ) metadata = {} while tokens: first_token = tokens[0] key, value = self._parse_key_value_pair(tokens) if key in metadata: self._parser_state.error( 'duplicate metadata key: "%s"' % key, token=first_token ) metadata[key] = value if not tokens or tokens[0].type != "SEMICOLON": break tokens.pop(0) if not tokens or tokens[0].type != "RBRACKET": self._parser_state.error("unclosed bracket", token=opening_bracket) tokens.pop(0) return metadata # syscall-descriptor = syscall-name , [ metadata ] # | syscall-group-name , [ metadata ] # ; def _parse_syscall_descriptor(self, tokens): if not tokens: self._parser_state.error("missing syscall descriptor") syscall_descriptor = tokens.pop(0) # `kill` as a syscall name is a special case since kill is also a valid # action and actions have precendence over identifiers. if ( syscall_descriptor.type != "IDENTIFIER" and syscall_descriptor.value != "kill" ): self._parser_state.error( "invalid syscall descriptor", token=syscall_descriptor ) if tokens and tokens[0].type == "LBRACKET": metadata = self._parse_metadata(tokens) if ( "arch" in metadata and self._arch.arch_name not in metadata["arch"] ): return () if "@" in syscall_descriptor.value: # This is a syscall group. subtokens = syscall_descriptor.value.split("@") if len(subtokens) != 2: self._parser_state.error( "invalid syscall group name", token=syscall_descriptor ) syscall_group_name, syscall_namespace_name = subtokens if syscall_namespace_name not in self._arch.syscall_groups: self._parser_state.error( "nonexistent syscall group namespace", token=syscall_descriptor, ) syscall_namespace = self._arch.syscall_groups[ syscall_namespace_name ] if syscall_group_name not in syscall_namespace: self._parser_state.error( "nonexistent syscall group", token=syscall_descriptor ) return ( Syscall(name, self._arch.syscalls[name]) for name in syscall_namespace[syscall_group_name] ) if syscall_descriptor.value not in self._arch.syscalls: self._parser_state.error( "nonexistent syscall", token=syscall_descriptor ) return ( Syscall( syscall_descriptor.value, self._arch.syscalls[syscall_descriptor.value], ), ) # filter-statement = # '{' , syscall-descriptor , [ { ',', syscall-descriptor } ] , '}' , # ':' , filter # | syscall-descriptor , ':' , filter # ; def parse_filter_statement(self, tokens): """Parse a filter statement and return a ParsedFilterStatement.""" if not tokens: self._parser_state.error("empty filter statement") syscall_descriptors = [] if tokens[0].type == "LBRACE": opening_brace = tokens.pop(0) while tokens: syscall_descriptors.extend( self._parse_syscall_descriptor(tokens) ) if not tokens or tokens[0].type != "COMMA": break tokens.pop(0) if not tokens or tokens[0].type != "RBRACE": self._parser_state.error("unclosed brace", token=opening_brace) tokens.pop(0) else: syscall_descriptors.extend(self._parse_syscall_descriptor(tokens)) if not tokens: self._parser_state.error("missing colon") if tokens[0].type != "COLON": self._parser_state.error("invalid colon", token=tokens[0]) # Given that there can be multiple syscalls and filters in a single # filter statement, use the colon token as the anchor for error location # purposes. colon_token = tokens.pop(0) parsed_filter = self.parse_filter(tokens) if not syscall_descriptors: return None return ParsedFilterStatement( tuple(syscall_descriptors), parsed_filter, colon_token ) # include-statement = '@include' , posix-path # ; def _parse_include_statement(self, tokens): if not tokens: self._parser_state.error("empty filter statement") if tokens[0].type != "INCLUDE": self._parser_state.error("invalid include", token=tokens[0]) tokens.pop(0) if not tokens: self._parser_state.error("empty include path") include_path = tokens.pop(0) if include_path.type != "PATH": self._parser_state.error("invalid include path", token=include_path) if len(self._parser_states) == self._include_depth_limit: self._parser_state.error("@include statement nested too deep") include_filename = os.path.normpath( os.path.join( os.path.dirname(self._parser_state.filename), include_path.value ) ) if not os.path.isfile(include_filename): self._parser_state.error( "Could not @include %s" % include_filename, token=include_path ) return self._parse_policy_file(include_filename) def _parse_frequency_file(self, filename): self._parser_states.append(ParserState(filename)) try: frequency_mapping = collections.defaultdict(int) with open(filename, encoding="utf-8") as frequency_file: for tokens in self._parser_state.tokenize(frequency_file): syscall_numbers = self._parse_syscall_descriptor(tokens) if not tokens: self._parser_state.error("missing colon") if tokens[0].type != "COLON": self._parser_state.error( "invalid colon", token=tokens[0] ) tokens.pop(0) if not tokens: self._parser_state.error("missing number") number = tokens.pop(0) if number.type != "NUMERIC_CONSTANT": self._parser_state.error("invalid number", token=number) number_value = int(number.value, base=0) if number_value < 0: self._parser_state.error("invalid number", token=number) for syscall_number in syscall_numbers: frequency_mapping[syscall_number] += number_value return frequency_mapping finally: self._parser_states.pop() # frequency-statement = '@frequency' , posix-path # ; def _parse_frequency_statement(self, tokens): if not tokens: self._parser_state.error("empty frequency statement") if tokens[0].type != "FREQUENCY": self._parser_state.error("invalid frequency", token=tokens[0]) tokens.pop(0) if not tokens: self._parser_state.error("empty frequency path") frequency_path = tokens.pop(0) if frequency_path.type != "PATH": self._parser_state.error( "invalid frequency path", token=frequency_path ) frequency_filename = os.path.normpath( os.path.join( os.path.dirname(self._parser_state.filename), frequency_path.value, ) ) if not os.path.isfile(frequency_filename): self._parser_state.error( "Could not open frequency file %s" % frequency_filename, token=frequency_path, ) return self._parse_frequency_file(frequency_filename) # default-statement = '@default' , default-action # ; def _parse_default_statement(self, tokens): if not tokens: self._parser_state.error("empty default statement") if tokens[0].type != "DEFAULT": self._parser_state.error("invalid default", token=tokens[0]) tokens.pop(0) if not tokens: self._parser_state.error("empty action") return self._parse_default_action(tokens) def _parse_policy_file(self, filename): self._parser_states.append(ParserState(filename)) try: statements = [] denylist_header = False with open(filename, encoding="utf-8") as policy_file: for tokens in self._parser_state.tokenize(policy_file): if tokens[0].type == "INCLUDE": statements.extend(self._parse_include_statement(tokens)) elif tokens[0].type == "FREQUENCY": for ( syscall_number, frequency, ) in self._parse_frequency_statement(tokens).items(): self._frequency_mapping[syscall_number] += frequency elif tokens[0].type == "DEFAULT": self._default_action = self._parse_default_statement( tokens ) elif tokens[0].type == "DENYLIST": tokens.pop() if not self._denylist: self._parser_state.error( "policy is denylist, but " "flag --denylist not " "passed in." ) else: denylist_header = True else: statement = self.parse_filter_statement(tokens) if statement is None: # If all the syscalls in the statement are for # another arch, skip the whole statement. continue statements.append(statement) if tokens: self._parser_state.error( "extra tokens", token=tokens[0] ) if self._denylist and not denylist_header: self._parser_state.error( "policy must contain @denylist flag to" " be compiled with --denylist flag." ) return statements finally: self._parser_states.pop() def parse_file(self, filename): """Parse a file and return the list of FilterStatements.""" self._frequency_mapping = collections.defaultdict(int) try: statements = self._parse_policy_file(filename) except RecursionError: raise ParseException( "recursion limit exceeded", filename, line=self._parser_states[-1].line, ) # Collapse statements into a single syscall-to-filter-list, remembering # the token for each filter for better diagnostics. syscall_filter_mapping = {} syscall_filter_definitions = {} filter_statements = [] for syscalls, filters, token in statements: for syscall in syscalls: if syscall not in syscall_filter_mapping: filter_statements.append( FilterStatement( syscall, self._frequency_mapping.get(syscall, 1), [] ) ) syscall_filter_mapping[syscall] = filter_statements[-1] syscall_filter_definitions[syscall] = [] for filt in filters: syscall_filter_mapping[syscall].filters.append(filt) syscall_filter_definitions[syscall].append(token) default_action = self._override_default_action or self._default_action for filter_statement in filter_statements: unconditional_actions_suffix = list( itertools.dropwhile( lambda filt: filt.expression is not None, filter_statement.filters, ) ) if len(unconditional_actions_suffix) == 1: # The last filter already has an unconditional action, no need # to add another one. continue if len(unconditional_actions_suffix) > 1: previous_definition_token = syscall_filter_definitions[ filter_statement.syscall ][-len(unconditional_actions_suffix)] current_definition_token = syscall_filter_definitions[ filter_statement.syscall ][-len(unconditional_actions_suffix) + 1] raise ParseException( ( "Syscall %s (number %d) already had " "an unconditional action applied" ) % ( filter_statement.syscall.name, filter_statement.syscall.number, ), filename=current_definition_token.filename, token=current_definition_token, ) from ParseException( "Previous definition", filename=previous_definition_token.filename, token=previous_definition_token, ) assert not unconditional_actions_suffix filter_statement.filters.append( Filter(expression=None, action=default_action) ) return ParsedPolicy(default_action, filter_statements) minijail-2026.05.18+git20261016.8787d1a/tools/parser_unittest.py000077500000000000000000001164161526451555500233140ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Unittests for the parser module.""" import os import parser # pylint: disable=wrong-import-order import shutil import tempfile import unittest from importlib import resources import arch import bpf ARCH_64 = arch.Arch.load_from_json_bytes( resources.files("testdata").joinpath("arch_64.json").read_bytes() ) class TokenizerTests(unittest.TestCase): """Tests for ParserState.tokenize.""" @staticmethod def _tokenize(line): parser_state = parser.ParserState("") return list(parser_state.tokenize([line]))[0] def test_tokenize(self): """Accept valid tokens.""" self.assertEqual( [ (token.type, token.value) for token in TokenizerTests._tokenize( "@include /minijail.policy" ) ], [ ("INCLUDE", "@include"), ("PATH", "/minijail.policy"), ], ) self.assertEqual( [ (token.type, token.value) for token in TokenizerTests._tokenize( "@include ./minijail.policy" ) ], [ ("INCLUDE", "@include"), ("PATH", "./minijail.policy"), ], ) self.assertEqual( [ (token.type, token.value) for token in TokenizerTests._tokenize( "read: arg0 in ~0xffff || arg0 & (1|2) && arg0 == 0755; " "return ENOSYS # ignored" ) ], [ ("IDENTIFIER", "read"), ("COLON", ":"), ("ARGUMENT", "arg0"), ("OP", "in"), ("BITWISE_COMPLEMENT", "~"), ("NUMERIC_CONSTANT", "0xffff"), ("OR", "||"), ("ARGUMENT", "arg0"), ("OP", "&"), ("LPAREN", "("), ("NUMERIC_CONSTANT", "1"), ("BITWISE_OR", "|"), ("NUMERIC_CONSTANT", "2"), ("RPAREN", ")"), ("AND", "&&"), ("ARGUMENT", "arg0"), ("OP", "=="), ("NUMERIC_CONSTANT", "0755"), ("SEMICOLON", ";"), ("RETURN", "return"), ("IDENTIFIER", "ENOSYS"), ], ) # Ensure that tokens that have an otherwise valid token as prefix are # still matched correctly. self.assertEqual( [ (token.type, token.value) for token in TokenizerTests._tokenize( "inotify_wait return_sys killall trace_sys" ) ], [ ("IDENTIFIER", "inotify_wait"), ("IDENTIFIER", "return_sys"), ("IDENTIFIER", "killall"), ("IDENTIFIER", "trace_sys"), ], ) def test_tokenize_invalid_token(self): """Reject tokenizer errors.""" with self.assertRaisesRegex( parser.ParseException, ( r"\(1:1\): invalid token\n" r" %invalid-token%\n" r" \^" ), ): TokenizerTests._tokenize("%invalid-token%") class ParseConstantTests(unittest.TestCase): """Tests for PolicyParser.parse_value.""" def setUp(self): self.arch = ARCH_64 self.parser = parser.PolicyParser( self.arch, kill_action=bpf.KillProcess() ) def _tokenize(self, line): # pylint: disable=protected-access return list(self.parser._parser_state.tokenize([line]))[0] def test_parse_constant_unsigned(self): """Accept reasonably-sized unsigned constants.""" self.assertEqual( self.parser.parse_value(self._tokenize("0x80000000")), 0x80000000 ) if self.arch.bits == 64: self.assertEqual( self.parser.parse_value(self._tokenize("0x8000000000000000")), 0x8000000000000000, ) def test_parse_constant_unsigned_too_big(self): """Reject unreasonably-sized unsigned constants.""" if self.arch.bits == 32: with self.assertRaisesRegex( parser.ParseException, "unsigned overflow" ): self.parser.parse_value(self._tokenize("0x100000000")) with self.assertRaisesRegex(parser.ParseException, "unsigned overflow"): self.parser.parse_value(self._tokenize("0x10000000000000000")) def test_parse_constant_signed(self): """Accept reasonably-sized signed constants.""" self.assertEqual( self.parser.parse_value(self._tokenize("-1")), self.arch.max_unsigned, ) def test_parse_constant_signed_too_negative(self): """Reject unreasonably-sized signed constants.""" if self.arch.bits == 32: with self.assertRaisesRegex( parser.ParseException, "signed underflow" ): self.parser.parse_value(self._tokenize("-0x800000001")) with self.assertRaisesRegex(parser.ParseException, "signed underflow"): self.parser.parse_value(self._tokenize("-0x8000000000000001")) def test_parse_mask(self): """Accept parsing a mask value.""" self.assertEqual( self.parser.parse_value(self._tokenize("0x1|0x2|0x4|0x8")), 0xF ) def test_parse_parenthesized_expressions(self): """Accept parsing parenthesized expressions.""" bad_expressions = [ "(1", "|(1)", "(1)|", "()", "(", "((", "(()", "(()1", ] for expression in bad_expressions: with self.assertRaises(parser.ParseException, msg=expression): self.parser.parse_value(self._tokenize(expression)) bad_partial_expressions = [ "1)", "(1)1", "1(0)", ] for expression in bad_partial_expressions: tokens = self._tokenize(expression) self.parser.parse_value(tokens) self.assertNotEqual(tokens, []) good_expressions = [ "(3)", "(1)|2", "1|(2)", "(1)|(2)", "((3))", "0|(1|2)", "(0|1|2)", ] for expression in good_expressions: self.assertEqual( self.parser.parse_value(self._tokenize(expression)), 3 ) def test_parse_constant_complements(self): """Accept complementing constants.""" self.assertEqual( self.parser.parse_value(self._tokenize("~0")), self.arch.max_unsigned, ) self.assertEqual( self.parser.parse_value(self._tokenize("~0|~0")), self.arch.max_unsigned, ) if self.arch.bits == 32: self.assertEqual( self.parser.parse_value( self._tokenize("~0x005AF0FF|~0xFFA50FFF") ), 0xFFFFFF00, ) self.assertEqual( self.parser.parse_value( self._tokenize("0x0F|~(0x005AF000|0x00A50FFF)|0xF0") ), 0xFF0000FF, ) else: self.assertEqual( self.parser.parse_value( self._tokenize("~0x00005A5AF0F0FFFF|~0xFFFFA5A50F0FFFFF") ), 0xFFFFFFFFFFFF0000, ) self.assertEqual( self.parser.parse_value( self._tokenize( "0x00FF|~(0x00005A5AF0F00000|0x0000A5A50F0FFFFF)|0xFF00" ) ), 0xFFFF00000000FFFF, ) def test_parse_double_complement(self): """Reject double-complementing constants.""" with self.assertRaisesRegex(parser.ParseException, "double complement"): self.parser.parse_value(self._tokenize("~~0")) def test_parse_empty_complement(self): """Reject complementing nothing.""" with self.assertRaisesRegex(parser.ParseException, "empty complement"): self.parser.parse_value(self._tokenize("0|~")) def test_parse_named_constant(self): """Accept parsing a named constant.""" self.assertEqual(self.parser.parse_value(self._tokenize("O_RDONLY")), 0) def test_parse_empty_constant(self): """Reject parsing nothing.""" with self.assertRaisesRegex(parser.ParseException, "empty constant"): self.parser.parse_value([]) with self.assertRaisesRegex(parser.ParseException, "empty constant"): self.parser.parse_value(self._tokenize("0|")) def test_parse_invalid_constant(self): """Reject parsing invalid constants.""" with self.assertRaisesRegex(parser.ParseException, "invalid constant"): self.parser.parse_value(self._tokenize("foo")) class ParseFilterExpressionTests(unittest.TestCase): """Tests for PolicyParser.parse_argument_expression.""" def setUp(self): self.arch = ARCH_64 self.parser = parser.PolicyParser( self.arch, kill_action=bpf.KillProcess() ) def _tokenize(self, line): # pylint: disable=protected-access return list(self.parser._parser_state.tokenize([line]))[0] def test_parse_argument_expression(self): """Accept valid argument expressions.""" self.assertEqual( self.parser.parse_argument_expression( self._tokenize( "arg0 in 0xffff || arg0 == PROT_EXEC && arg1 == PROT_WRITE" ) ), [ [parser.Atom(0, "in", 0xFFFF)], [parser.Atom(0, "==", 4), parser.Atom(1, "==", 2)], ], ) def test_parse_number_argument_expression(self): """Accept argument expressions with any octal/decimal/hex number.""" # 4607 == 010777 == 0x11ff self.assertEqual( self.parser.parse_argument_expression( self._tokenize("arg0 in 4607") ), [ [parser.Atom(0, "in", 4607)], ], ) self.assertEqual( self.parser.parse_argument_expression( self._tokenize("arg0 in 010777") ), [ [parser.Atom(0, "in", 4607)], ], ) self.assertEqual( self.parser.parse_argument_expression( self._tokenize("arg0 in 0x11ff") ), [ [parser.Atom(0, "in", 4607)], ], ) def test_parse_empty_argument_expression(self): """Reject empty argument expressions.""" with self.assertRaisesRegex( parser.ParseException, "empty argument expression" ): self.parser.parse_argument_expression( self._tokenize("arg0 in 0xffff ||") ) def test_parse_empty_clause(self): """Reject empty clause.""" with self.assertRaisesRegex(parser.ParseException, "empty clause"): self.parser.parse_argument_expression( self._tokenize("arg0 in 0xffff &&") ) def test_parse_invalid_argument(self): """Reject invalid argument.""" with self.assertRaisesRegex(parser.ParseException, "invalid argument"): self.parser.parse_argument_expression( self._tokenize("argX in 0xffff") ) def test_parse_invalid_operator(self): """Reject invalid operator.""" with self.assertRaisesRegex(parser.ParseException, "invalid operator"): self.parser.parse_argument_expression( self._tokenize("arg0 = 0xffff") ) def test_parse_missing_operator(self): """Reject missing operator.""" with self.assertRaisesRegex(parser.ParseException, "missing operator"): self.parser.parse_argument_expression(self._tokenize("arg0")) def test_parse_missing_operand(self): """Reject missing operand.""" with self.assertRaisesRegex(parser.ParseException, "empty constant"): self.parser.parse_argument_expression(self._tokenize("arg0 ==")) class ParseFilterTests(unittest.TestCase): """Tests for PolicyParser.parse_filter.""" def setUp(self): self.arch = ARCH_64 self.parser = parser.PolicyParser( self.arch, kill_action=bpf.KillProcess() ) def _tokenize(self, line): # pylint: disable=protected-access return list(self.parser._parser_state.tokenize([line]))[0] def test_parse_filter(self): """Accept valid filters.""" self.assertEqual( self.parser.parse_filter(self._tokenize("arg0 == 0")), [ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("kill-process")), [ parser.Filter(None, bpf.KillProcess()), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("kill-thread")), [ parser.Filter(None, bpf.KillThread()), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("trap")), [ parser.Filter(None, bpf.Trap()), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("return ENOSYS")), [ parser.Filter( None, bpf.ReturnErrno(self.arch.constants["ENOSYS"]) ), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("trace")), [ parser.Filter(None, bpf.Trace()), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("user-notify")), [ parser.Filter(None, bpf.UserNotify()), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("log")), [ parser.Filter(None, bpf.Log()), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("allow")), [ parser.Filter(None, bpf.Allow()), ], ) self.assertEqual( self.parser.parse_filter(self._tokenize("1")), [ parser.Filter(None, bpf.Allow()), ], ) self.assertEqual( self.parser.parse_filter( self._tokenize("{ arg0 == 0, arg0 == 1; return ENOSYS, trap }") ), [ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), parser.Filter( [[parser.Atom(0, "==", 1)]], bpf.ReturnErrno(self.arch.constants["ENOSYS"]), ), parser.Filter(None, bpf.Trap()), ], ) def test_parse_missing_return_value(self): """Reject missing return value.""" with self.assertRaisesRegex( parser.ParseException, "missing return value" ): self.parser.parse_filter(self._tokenize("return")) def test_parse_invalid_return_value(self): """Reject invalid return value.""" with self.assertRaisesRegex(parser.ParseException, "invalid constant"): self.parser.parse_filter(self._tokenize("return arg0")) def test_parse_unclosed_brace(self): """Reject unclosed brace.""" with self.assertRaisesRegex(parser.ParseException, "unclosed brace"): self.parser.parse_filter(self._tokenize("{ allow")) class ParseFilterDenylistTests(unittest.TestCase): """Tests for PolicyParser.parse_filter with a denylist policy.""" def setUp(self): self.arch = ARCH_64 self.kill_action = bpf.KillProcess() self.parser = parser.PolicyParser( self.arch, kill_action=self.kill_action, denylist=True ) def _tokenize(self, line): # pylint: disable=protected-access return list(self.parser._parser_state.tokenize([line]))[0] def test_parse_filter(self): """Accept only filters that return an errno.""" self.assertEqual( self.parser.parse_filter( self._tokenize("arg0 == 0; return ENOSYS") ), [ parser.Filter( [[parser.Atom(0, "==", 0)]], bpf.ReturnErrno(self.arch.constants["ENOSYS"]), ), ], ) class ParseFilterStatementTests(unittest.TestCase): """Tests for PolicyParser.parse_filter_statement.""" def setUp(self): self.arch = ARCH_64 self.parser = parser.PolicyParser( self.arch, kill_action=bpf.KillProcess() ) def _tokenize(self, line): # pylint: disable=protected-access return list(self.parser._parser_state.tokenize([line]))[0] def assertEqualIgnoringToken(self, actual, expected, msg=None): """Similar to assertEqual, but ignores the token field.""" if ( actual.syscalls != expected.syscalls or actual.filters != expected.filters ): self.fail("%r != %r: %s" % (actual, expected, msg)) def test_parse_filter_statement(self): """Accept valid filter statements.""" self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize("read: arg0 == 0") ), parser.ParsedFilterStatement( syscalls=(parser.Syscall("read", 0),), filters=[ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), ], token=None, ), ) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize("{read, write}: arg0 == 0") ), parser.ParsedFilterStatement( syscalls=( parser.Syscall("read", 0), parser.Syscall("write", 1), ), filters=[ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), ], token=None, ), ) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize("io@libc: arg0 == 0") ), parser.ParsedFilterStatement( syscalls=( parser.Syscall("read", 0), parser.Syscall("write", 1), ), filters=[ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), ], token=None, ), ) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize("file-io@systemd: arg0 == 0") ), parser.ParsedFilterStatement( syscalls=( parser.Syscall("read", 0), parser.Syscall("write", 1), ), filters=[ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), ], token=None, ), ) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize("kill: arg0 == 0") ), parser.ParsedFilterStatement( syscalls=(parser.Syscall("kill", 62),), filters=[ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), ], token=None, ), ) def test_parse_metadata(self): """Accept valid filter statements with metadata.""" self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize("read[arch=test]: arg0 == 0") ), parser.ParsedFilterStatement( syscalls=(parser.Syscall("read", 0),), filters=[ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), ], token=None, ), ) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize( "{read, nonexistent[arch=nonexistent]}: arg0 == 0" ) ), parser.ParsedFilterStatement( syscalls=(parser.Syscall("read", 0),), filters=[ parser.Filter([[parser.Atom(0, "==", 0)]], bpf.Allow()), ], token=None, ), ) def test_parse_unclosed_brace(self): """Reject unclosed brace.""" with self.assertRaisesRegex(parser.ParseException, "unclosed brace"): self.parser.parse_filter(self._tokenize("{ allow")) def test_parse_invalid_syscall_group(self): """Reject invalid syscall groups.""" with self.assertRaisesRegex(parser.ParseException, "unclosed brace"): self.parser.parse_filter_statement( self._tokenize("{ read, write: arg0 == 0") ) def test_parse_missing_colon(self): """Reject missing colon.""" with self.assertRaisesRegex(parser.ParseException, "missing colon"): self.parser.parse_filter_statement(self._tokenize("read")) def test_parse_invalid_colon(self): """Reject invalid colon.""" with self.assertRaisesRegex(parser.ParseException, "invalid colon"): self.parser.parse_filter_statement(self._tokenize("read arg0")) def test_parse_missing_filter(self): """Reject missing filter.""" with self.assertRaisesRegex(parser.ParseException, "missing filter"): self.parser.parse_filter_statement(self._tokenize("read:")) class ParseFileTests(unittest.TestCase): """Tests for PolicyParser.parse_file.""" def setUp(self): self.arch = ARCH_64 self.parser = parser.PolicyParser( self.arch, kill_action=bpf.KillProcess() ) self.tempdir = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.tempdir) def _write_file(self, filename, contents): """Helper to write out a file for testing.""" path = os.path.join(self.tempdir, filename) with open(path, "w", encoding="utf-8") as outf: outf.write(contents) return path def test_parse_simple(self): """Allow simple policy files.""" path = self._write_file( "test.policy", """ # Comment. read: allow write: allow """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("read", 0), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ], ), parser.FilterStatement( syscall=parser.Syscall("write", 1), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ], ), ], ), ) def test_parse_multiline(self): """Allow simple multi-line policy files.""" path = self._write_file( "test.policy", """ # Comment. read: \ allow write: allow """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("read", 0), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ], ), parser.FilterStatement( syscall=parser.Syscall("write", 1), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ], ), ], ), ) def test_parse_default(self): """Allow defining a default action.""" path = self._write_file( "test.policy", """ @default kill-thread read: allow """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillThread(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("read", 0), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ], ), ], ), ) def test_parse_default_permissive(self): """Reject defining a permissive default action.""" path = self._write_file( "test.policy", """ @default log read: allow """, ) with self.assertRaisesRegex( parser.ParseException, r"invalid permissive default action" ): self.parser.parse_file(path) def test_parse_simple_grouped(self): """Allow simple policy files.""" path = self._write_file( "test.policy", """ # Comment. {read, write}: allow """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("read", 0), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ], ), parser.FilterStatement( syscall=parser.Syscall("write", 1), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ], ), ], ), ) def test_parse_other_arch(self): """Allow entries that only target another architecture.""" path = self._write_file( "test.policy", """ # Comment. read[arch=nonexistent]: allow write: allow """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("write", 1), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ], ), ], ), ) def test_parse_include(self): """Allow including policy files.""" path = self._write_file( "test.include.policy", """ {read, write}: arg0 == 0; allow """, ) path = self._write_file( "test.policy", """ @include ./test.include.policy read: return ENOSYS """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("read", 0), frequency=1, filters=[ parser.Filter( [[parser.Atom(0, "==", 0)]], bpf.Allow() ), parser.Filter( None, bpf.ReturnErrno(self.arch.constants["ENOSYS"]), ), ], ), parser.FilterStatement( syscall=parser.Syscall("write", 1), frequency=1, filters=[ parser.Filter( [[parser.Atom(0, "==", 0)]], bpf.Allow() ), parser.Filter(None, bpf.KillProcess()), ], ), ], ), ) def test_parse_invalid_include(self): """Reject including invalid policy files.""" with self.assertRaisesRegex( parser.ParseException, r"empty include path" ): path = self._write_file( "test.policy", """ @include """, ) self.parser.parse_file(path) with self.assertRaisesRegex( parser.ParseException, r"invalid include path" ): path = self._write_file( "test.policy", """ @include arg0 """, ) self.parser.parse_file(path) with self.assertRaisesRegex( parser.ParseException, r"@include statement nested too deep" ): path = self._write_file( "test.policy", """ @include ./test.policy """, ) self.parser.parse_file(path) with self.assertRaisesRegex( parser.ParseException, r"Could not @include .*" ): path = self._write_file( "test.policy", """ @include ./nonexistent.policy """, ) self.parser.parse_file(path) def test_parse_frequency(self): """Allow including frequency files.""" self._write_file( "test.frequency", """ read: 2 write: 3 """, ) path = self._write_file( "test.policy", """ @frequency ./test.frequency read: allow """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("read", 0), frequency=2, filters=[ parser.Filter(None, bpf.Allow()), ], ), ], ), ) def test_parse_invalid_frequency(self): """Reject including invalid frequency files.""" path = self._write_file( "test.policy", """@frequency ./test.frequency""" ) with self.assertRaisesRegex(parser.ParseException, r"missing colon"): self._write_file( "test.frequency", """ read """, ) self.parser.parse_file(path) with self.assertRaisesRegex(parser.ParseException, r"invalid colon"): self._write_file( "test.frequency", """ read foo """, ) self.parser.parse_file(path) with self.assertRaisesRegex(parser.ParseException, r"missing number"): self._write_file( "test.frequency", """ read: """, ) self.parser.parse_file(path) with self.assertRaisesRegex(parser.ParseException, r"invalid number"): self._write_file( "test.frequency", """ read: foo """, ) self.parser.parse_file(path) with self.assertRaisesRegex(parser.ParseException, r"invalid number"): self._write_file( "test.frequency", """ read: -1 """, ) self.parser.parse_file(path) with self.assertRaisesRegex( parser.ParseException, r"empty frequency path" ): path = self._write_file( "test.policy", """ @frequency """, ) self.parser.parse_file(path) with self.assertRaisesRegex( parser.ParseException, r"invalid frequency path" ): path = self._write_file( "test.policy", """ @frequency arg0 """, ) self.parser.parse_file(path) with self.assertRaisesRegex( parser.ParseException, r"Could not open frequency file.*" ): path = self._write_file( "test.policy", """ @frequency ./nonexistent.frequency """, ) self.parser.parse_file(path) def test_parse_multiple_unconditional(self): """Reject actions after an unconditional action.""" path = self._write_file( "test.policy", """ read: allow read: allow """, ) with self.assertRaisesRegex( parser.ParseException, ( r"test.policy\(3:17\): " r"Syscall read.*already had an unconditional action " r"applied" ), ): self.parser.parse_file(path) path = self._write_file( "test.policy", """ read: log read: arg0 == 0; log """, ) with self.assertRaisesRegex( parser.ParseException, ( r"test.policy\(3:17\): " r"Syscall read.*already had an unconditional action " r"applied" ), ): self.parser.parse_file(path) def test_parse_allowlist_denylist_header(self): """Reject trying to compile denylist policy file as allowlist.""" with self.assertRaisesRegex( parser.ParseException, r"policy is denylist, but flag --denylist " "not passed in", ): path = self._write_file( "test.policy", """ @denylist """, ) self.parser.parse_file(path) class ParseFileDenylistTests(unittest.TestCase): """Tests for PolicyParser.parse_file.""" def setUp(self): self.arch = ARCH_64 self.kill_action = bpf.KillProcess() self.parser = parser.PolicyParser( self.arch, kill_action=self.kill_action, denylist=True ) self.tempdir = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.tempdir) def _write_file(self, filename, contents): """Helper to write out a file for testing.""" path = os.path.join(self.tempdir, filename) with open(path, "w", encoding="utf-8") as outf: outf.write(contents) return path def test_parse_simple(self): """Allow simple denylist policy files.""" path = self._write_file( "test.policy", """ # Comment. @denylist read: return ENOSYS write: return ENOSYS """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.Allow(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("read", 0), frequency=1, filters=[ parser.Filter( None, bpf.ReturnErrno(self.arch.constants["ENOSYS"]), ), ], ), parser.FilterStatement( syscall=parser.Syscall("write", 1), frequency=1, filters=[ parser.Filter( None, bpf.ReturnErrno(self.arch.constants["ENOSYS"]), ), ], ), ], ), ) def test_parse_simple_with_arg(self): """Allow simple denylist policy files.""" path = self._write_file( "test.policy", """ # Comment. @denylist read: return ENOSYS write: arg0 == 0 ; return ENOSYS """, ) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.Allow(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall("read", 0), frequency=1, filters=[ parser.Filter( None, bpf.ReturnErrno(self.arch.constants["ENOSYS"]), ), ], ), parser.FilterStatement( syscall=parser.Syscall("write", 1), frequency=1, filters=[ parser.Filter( [[parser.Atom(0, "==", 0)]], bpf.ReturnErrno(self.arch.constants["ENOSYS"]), ), parser.Filter(None, bpf.Allow()), ], ), ], ), ) def test_parse_denylist_no_header(self): """Reject trying to compile denylist policy file as allowlist.""" with self.assertRaisesRegex( parser.ParseException, r"policy must contain @denylist flag to be " "compiled with --denylist flag", ): path = self._write_file( "test.policy", """ read: return ENOSYS """, ) self.parser.parse_file(path) if __name__ == "__main__": unittest.main() minijail-2026.05.18+git20261016.8787d1a/tools/seccomp_policy_lint.py000077500000000000000000000136761526451555500241230ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """A linter for the Minijail seccomp policy file.""" import argparse import re import sys from typing import List, NamedTuple, Optional, Set # The syscalls we have determined are more dangerous and need justification # for inclusion in a policy. DANGEROUS_SYSCALLS = ( "clone", "mount", "setns", "kill", "execve", "execveat", "getrandom", "bpf", "socket", "ptrace", "swapon", "swapoff", # TODO(b/193169195): Add argument granularity for the below syscalls. "prctl", "ioctl", "mmap", "mmap2", "mprotect", ) # If a dangerous syscall uses these rules, then it's considered safe. SYSCALL_SAFE_RULES = { "getrandom": ("arg2 in ~GRND_RANDOM",), "mmap": ( "arg2 == PROT_READ || arg2 == PROT_NONE", "arg2 in ~PROT_EXEC", "arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE", ), "mmap2": ( "arg2 == PROT_READ || arg2 == PROT_NONE", "arg2 in ~PROT_EXEC", "arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE", ), "mprotect": ( "arg2 == PROT_READ || arg2 == PROT_NONE", "arg2 in ~PROT_EXEC", "arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE", ), } GLOBAL_SAFE_RULES = ( "kill", "kill-process", "kill-thread", "return 1", ) class CheckPolicyReturn(NamedTuple): """Represents a return value from check_seccomp_policy Contains a message to print to the user and a list of errors that were found in the file. """ message: str errors: List[str] def parse_args(argv): """Return the parsed CLI arguments for this tool.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--denylist", action="store_true", help="Check as a denylist policy rather than the default allowlist.", ) parser.add_argument( "--dangerous-syscalls", action="store", default=",".join(DANGEROUS_SYSCALLS), help="Comma-separated list of dangerous sycalls (overrides default).", ) parser.add_argument( "--assume-filename", help="The filename when parsing stdin.", ) parser.add_argument( "policy", help="The seccomp policy.", type=argparse.FileType("r", encoding="utf-8"), ) return parser.parse_args(argv), parser def check_seccomp_policy( check_file, dangerous_syscalls: Set[str], filename: Optional[str] = None ): """Fail if the seccomp policy file has dangerous, undocumented syscalls. Takes in a file object and a set of dangerous syscalls as arguments. """ if filename is None: filename = check_file.name found_syscalls = set() errors = [] msg = "" contains_dangerous_syscall = False prev_line_comment = False for line_num, line in enumerate(check_file): if re.match(r"^\s*#", line): prev_line_comment = True elif re.match(r"^\s*$", line): # Empty lines shouldn't reset prev_line_comment. continue else: match = re.match(r"^\s*(\w*)\s*:\s*(.*)\s*", line) if match: syscall = match.group(1) rule = match.group(2) err_prefix = f"{filename}:{line_num}:{syscall}:" if syscall in found_syscalls: errors.append(f"{err_prefix} duplicate entry found") else: found_syscalls.add(syscall) if syscall in dangerous_syscalls: contains_dangerous_syscall = True if not prev_line_comment: # Dangerous syscalls must be commented. safe_rules = SYSCALL_SAFE_RULES.get(syscall, ()) if rule in GLOBAL_SAFE_RULES or rule in safe_rules: pass elif safe_rules: # Dangerous syscalls with known safe rules must # use those rules. errors.append( f"{err_prefix} syscall is dangerous and " f"should use one of the rules: {safe_rules}" ) else: errors.append( f"{err_prefix} syscall is dangerous and " "requires a comment on the preceding line" ) prev_line_comment = False else: # This line is probably a continuation from the previous line. # TODO(b/203216289): Support line breaks. pass if contains_dangerous_syscall: msg = ( f"seccomp: {filename} contains dangerous syscalls, so" " requires review from chromeos-security@" ) else: msg = ( f"seccomp: {filename} does not contain any dangerous" " syscalls, so does not require review from" " chromeos-security@" ) if errors: return CheckPolicyReturn(msg, errors) return CheckPolicyReturn(msg, errors) def main(argv=None): """Main entrypoint.""" if argv is None: argv = sys.argv[1:] opts, _arg_parser = parse_args(argv) filename = opts.assume_filename if opts.assume_filename else opts.policy check = check_seccomp_policy( opts.policy, set(opts.dangerous_syscalls.split(",")), filename=filename ) formatted_items = "" if check.errors: item_prefix = "\n * " formatted_items = item_prefix + item_prefix.join(check.errors) print("* " + check.message + formatted_items) return 1 if check.errors else 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:])) minijail-2026.05.18+git20261016.8787d1a/tools/seccomp_policy_lint_unittest.py000077500000000000000000000077441526451555500260610ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2020 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Unittests for the seccomp policy linter module.""" from pathlib import Path import tempfile import unittest import seccomp_policy_lint class CheckSeccompPolicyTests(unittest.TestCase): """Tests for check_seccomp_policy.""" def setUp(self): self.tempdir = Path(tempfile.mkdtemp()) def _write_file(self, filename, contents): """Helper to write out a file for testing.""" path = self.tempdir / filename path.write_text(contents) return path def test_check_simple(self): """Allow simple policy files.""" path = self._write_file( "test.policy", """ # Comment.\n read: 1\n write: 1\n """, ) exp_out = seccomp_policy_lint.CheckPolicyReturn( f"seccomp: {path.resolve()} does not contain any dangerous" " syscalls, so does not require review from" " chromeos-security@", [], ) with path.open("r", encoding="utf-8") as check_file: self.assertEqual( seccomp_policy_lint.check_seccomp_policy( check_file, seccomp_policy_lint.DANGEROUS_SYSCALLS ), exp_out, ) def test_check_dangerous_comment(self): """Dangerous syscalls must have a comment and need to be reviewed.""" path = self._write_file( "test.policy", """ # Comment.\n\n\n clone: 1\n write: 1\n """, ) exp_out = seccomp_policy_lint.CheckPolicyReturn( f"seccomp: {path.resolve()} contains dangerous syscalls," " so requires review from chromeos-security@", [], ) with path.open("r", encoding="utf-8") as check_file: self.assertEqual( seccomp_policy_lint.check_seccomp_policy( check_file, seccomp_policy_lint.DANGEROUS_SYSCALLS ), exp_out, ) def test_check_dangerous_no_comment(self): """Dangerous syscalls without a comment should cause an error.""" path = self._write_file( "test.policy", """ # Comment.\n mount: 1\n clone: 1\n """, ) exp_out = seccomp_policy_lint.CheckPolicyReturn( f"seccomp: {path.resolve()} contains dangerous syscalls," " so requires review from chromeos-security@", [ ( f"{path.resolve()}:5:clone: syscall is dangerous " "and requires a comment on the preceding line" ) ], ) with path.open("r", encoding="utf-8") as check_file: self.assertEqual( seccomp_policy_lint.check_seccomp_policy( check_file, seccomp_policy_lint.DANGEROUS_SYSCALLS ), exp_out, ) def test_check_duplicate_syscall(self): """Policy files cannot have duplicate syscalls..""" path = self._write_file( "test.policy", """ # Comment.\n clone: 1\n clone: arg0 == 3 """, ) exp_out = seccomp_policy_lint.CheckPolicyReturn( f"seccomp: {path.resolve()} contains dangerous syscalls," " so requires review from chromeos-security@", [f"{path.resolve()}:5:clone: duplicate entry found"], ) with path.open("r", encoding="utf-8") as check_file: self.assertEqual( seccomp_policy_lint.check_seccomp_policy( check_file, seccomp_policy_lint.DANGEROUS_SYSCALLS ), exp_out, ) if __name__ == "__main__": unittest.main() minijail-2026.05.18+git20261016.8787d1a/tools/testdata/000077500000000000000000000000001526451555500213045ustar00rootroot00000000000000minijail-2026.05.18+git20261016.8787d1a/tools/testdata/arch_64.json000066400000000000000000000141741526451555500234340ustar00rootroot00000000000000{ "arch_nr": 3735928559, "arch_name": "test", "bits": 64, "syscalls": { "read": 0, "write": 1, "open": 2, "close": 3, "kill": 62, "syscall_4": 4, "syscall_5": 5, "syscall_6": 6, "syscall_7": 7, "syscall_8": 8, "syscall_9": 9, "syscall_10": 10, "syscall_11": 11, "syscall_12": 12, "syscall_13": 13, "syscall_14": 14, "syscall_15": 15, "syscall_16": 16, "syscall_17": 17, "syscall_18": 18, "syscall_19": 19, "syscall_20": 20, "syscall_21": 21, "syscall_22": 22, "syscall_23": 23, "syscall_24": 24, "syscall_25": 25, "syscall_26": 26, "syscall_27": 27, "syscall_28": 28, "syscall_29": 29, "syscall_30": 30, "syscall_31": 31, "syscall_32": 32, "syscall_33": 33, "syscall_34": 34, "syscall_35": 35, "syscall_36": 36, "syscall_37": 37, "syscall_38": 38, "syscall_39": 39, "syscall_40": 40, "syscall_41": 41, "syscall_42": 42, "syscall_43": 43, "syscall_44": 44, "syscall_45": 45, "syscall_46": 46, "syscall_47": 47, "syscall_48": 48, "syscall_49": 49, "syscall_50": 50, "syscall_51": 51, "syscall_52": 52, "syscall_53": 53, "syscall_54": 54, "syscall_55": 55, "syscall_56": 56, "syscall_57": 57, "syscall_58": 58, "syscall_59": 59, "syscall_60": 60, "syscall_61": 61, "syscall_63": 63, "syscall_64": 64, "syscall_65": 65, "syscall_66": 66, "syscall_67": 67, "syscall_68": 68, "syscall_69": 69, "syscall_70": 70, "syscall_71": 71, "syscall_72": 72, "syscall_73": 73, "syscall_74": 74, "syscall_75": 75, "syscall_76": 76, "syscall_77": 77, "syscall_78": 78, "syscall_79": 79, "syscall_80": 80, "syscall_81": 81, "syscall_82": 82, "syscall_83": 83, "syscall_84": 84, "syscall_85": 85, "syscall_86": 86, "syscall_87": 87, "syscall_88": 88, "syscall_89": 89, "syscall_90": 90, "syscall_91": 91, "syscall_92": 92, "syscall_93": 93, "syscall_94": 94, "syscall_95": 95, "syscall_96": 96, "syscall_97": 97, "syscall_98": 98, "syscall_99": 99, "syscall_100": 100, "syscall_101": 101, "syscall_102": 102, "syscall_103": 103, "syscall_104": 104, "syscall_105": 105, "syscall_106": 106, "syscall_107": 107, "syscall_108": 108, "syscall_109": 109, "syscall_110": 110, "syscall_111": 111, "syscall_112": 112, "syscall_113": 113, "syscall_114": 114, "syscall_115": 115, "syscall_116": 116, "syscall_117": 117, "syscall_118": 118, "syscall_119": 119, "syscall_120": 120, "syscall_121": 121, "syscall_122": 122, "syscall_123": 123, "syscall_124": 124, "syscall_125": 125, "syscall_126": 126, "syscall_127": 127, "syscall_128": 128, "syscall_129": 129, "syscall_130": 130, "syscall_131": 131, "syscall_132": 132, "syscall_133": 133, "syscall_134": 134, "syscall_135": 135, "syscall_136": 136, "syscall_137": 137, "syscall_138": 138, "syscall_139": 139, "syscall_140": 140, "syscall_141": 141, "syscall_142": 142, "syscall_143": 143, "syscall_144": 144, "syscall_145": 145, "syscall_146": 146, "syscall_147": 147, "syscall_148": 148, "syscall_149": 149, "syscall_150": 150, "syscall_151": 151, "syscall_152": 152, "syscall_153": 153, "syscall_154": 154, "syscall_155": 155, "syscall_156": 156, "syscall_157": 157, "syscall_158": 158, "syscall_159": 159, "syscall_160": 160, "syscall_161": 161, "syscall_162": 162, "syscall_163": 163, "syscall_164": 164, "syscall_165": 165, "syscall_166": 166, "syscall_167": 167, "syscall_168": 168, "syscall_169": 169, "syscall_170": 170, "syscall_171": 171, "syscall_172": 172, "syscall_173": 173, "syscall_174": 174, "syscall_175": 175, "syscall_176": 176, "syscall_177": 177, "syscall_178": 178, "syscall_179": 179, "syscall_180": 180, "syscall_181": 181, "syscall_182": 182, "syscall_183": 183, "syscall_184": 184, "syscall_185": 185, "syscall_186": 186, "syscall_187": 187, "syscall_188": 188, "syscall_189": 189, "syscall_190": 190, "syscall_191": 191, "syscall_192": 192, "syscall_193": 193, "syscall_194": 194, "syscall_195": 195, "syscall_196": 196, "syscall_197": 197, "syscall_198": 198, "syscall_199": 199, "syscall_200": 200, "syscall_201": 201, "syscall_202": 202, "syscall_203": 203, "syscall_204": 204, "syscall_205": 205, "syscall_206": 206, "syscall_207": 207, "syscall_208": 208, "syscall_209": 209, "syscall_210": 210, "syscall_211": 211, "syscall_212": 212, "syscall_213": 213, "syscall_214": 214, "syscall_215": 215, "syscall_216": 216, "syscall_217": 217, "syscall_218": 218, "syscall_219": 219, "syscall_220": 220, "syscall_221": 221, "syscall_222": 222, "syscall_223": 223, "syscall_224": 224, "syscall_225": 225, "syscall_226": 226, "syscall_227": 227, "syscall_228": 228, "syscall_229": 229, "syscall_230": 230, "syscall_231": 231, "syscall_232": 232, "syscall_233": 233, "syscall_234": 234, "syscall_235": 235, "syscall_236": 236, "syscall_237": 237, "syscall_238": 238, "syscall_239": 239, "syscall_240": 240, "syscall_241": 241, "syscall_242": 242, "syscall_243": 243, "syscall_244": 244, "syscall_245": 245, "syscall_246": 246, "syscall_247": 247, "syscall_248": 248, "syscall_249": 249, "syscall_250": 250, "syscall_251": 251, "syscall_252": 252, "syscall_253": 253, "syscall_254": 254, "syscall_255": 255 }, "constants": { "ENOSYS": 38, "O_RDONLY": 0, "PROT_WRITE": 2, "PROT_EXEC": 4 }, "syscall_groups": { "libc": { "io": [ "read", "write" ] }, "systemd": { "file-io": [ "read", "write" ] } } } minijail-2026.05.18+git20261016.8787d1a/unittest_util.h000066400000000000000000000046141526451555500214250ustar00rootroot00000000000000/* unittest_util.h * Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Utility functions for unit tests. */ #include #include #include #include #include #include "util.h" namespace { constexpr bool is_android_constexpr() { #if defined(__ANDROID__) return true; #else return false; #endif } // Returns a template path that can be used as an argument to mkstemp / mkdtemp. constexpr const char* temp_path_pattern() { if (is_android_constexpr()) return "/data/local/tmp/minijail.tests.XXXXXX"; else return "minijail.tests.XXXXXX"; } // Recursively deletes the subtree rooted at |path|. bool rmdir_recursive(const std::string& path) { auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int { if (file_type == FTW_DP) { if (rmdir(child) == -1) { fprintf(stderr, "rmdir(%s): %s\n", child, strerror(errno)); return -1; } } else if (file_type == FTW_F) { if (unlink(child) == -1) { fprintf(stderr, "unlink(%s): %s\n", child, strerror(errno)); return -1; } } return 0; }; return nftw(path.c_str(), callback, 128, FTW_DEPTH) == 0; } } // namespace // Creates a temporary directory that will be cleaned up upon leaving scope. class TemporaryDir { public: TemporaryDir() : path(temp_path_pattern()) { if (mkdtemp(const_cast(path.c_str())) == nullptr) path.clear(); } ~TemporaryDir() { if (!is_valid()) return; rmdir_recursive(path.c_str()); } bool is_valid() const { return !path.empty(); } std::string path; private: TemporaryDir(const TemporaryDir&) = delete; TemporaryDir& operator=(const TemporaryDir&) = delete; }; // Creates a named temporary file that will be cleaned up upon leaving scope. class TemporaryFile { public: TemporaryFile() : path(temp_path_pattern()) { int fd = mkstemp(const_cast(path.c_str())); if (fd == -1) { path.clear(); return; } close(fd); } ~TemporaryFile() { if (!is_valid()) return; unlink(path.c_str()); } bool is_valid() const { return !path.empty(); } std::string path; private: TemporaryFile(const TemporaryFile&) = delete; TemporaryFile& operator=(const TemporaryFile&) = delete; }; minijail-2026.05.18+git20261016.8787d1a/util.c000066400000000000000000000410321526451555500174540ustar00rootroot00000000000000/* Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #define _GNU_SOURCE #include "util.h" #include #include #include #include #include #include #include #include #include "libconstants.h" #include "libsyscalls.h" /* * These are syscalls used by the syslog() C library call. You can find them * by running a simple test program. See below for x86_64 behavior: * $ cat test.c * #include * main() { syslog(0, "foo"); } * $ gcc test.c -static * $ strace ./a.out * ... * socket(PF_FILE, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3 <- look for socket connection * connect(...) <- important * sendto(...) <- important * exit_group(0) <- finish! */ const char *const log_syscalls[] = { #if defined(__x86_64__) #if defined(__ANDROID__) "socket", "connect", "fcntl", "writev", #else "socket", "connect", "sendto", "writev", #endif #elif defined(__i386__) #if defined(__ANDROID__) // From Android 17, x86 uses the regular socket syscalls. "socket", "connect", // Before Android 17, they were multiplexed through socketcall. "socketcall", "writev", "fcntl64", "clock_gettime", #else "socketcall", "time", "writev", #endif #elif defined(__arm__) #if defined(__ANDROID__) "clock_gettime", "connect", "fcntl64", "socket", "writev", #else "socket", "connect", "gettimeofday", "send", "writev", #endif #elif defined(__aarch64__) #if defined(__ANDROID__) "connect", "fcntl", "sendto", "socket", "writev", #else "socket", "connect", "send", "writev", #endif #elif defined(__hppa__) || defined(__ia64__) || defined(__mips__) || \ defined(__powerpc__) || defined(__sparc__) "socket", "connect", "send", #elif defined(__loongarch64) "socket", "connect", "sendto", #elif defined(__riscv) #if defined(__ANDROID__) "connect", "fcntl", "sendto", "socket", "writev", #else "socket", "connect", "sendto", #endif #else #error "Unsupported platform" #endif }; const size_t log_syscalls_len = ARRAY_SIZE(log_syscalls); /* * These syscalls are globally allowed. ChromeOS devs: Do **not** add to this * list without approval from the security team. * * This list should be made empty (and mostly remain so) after a better * mechanism is implemented: b/393353891 */ const char *const libc_compatibility_syscalls[] = { "fstat", #if defined(__arm__) "fstat64", #endif }; const size_t libc_compatibility_syscalls_len = ARRAY_SIZE(libc_compatibility_syscalls); /* clang-format off */ static struct logging_config_t { /* The logging system to use. The default is syslog. */ enum logging_system_t logger; /* File descriptor to log to. Only used when logger is LOG_TO_FD. */ int fd; /* Minimum priority to log. Only used when logger is LOG_TO_FD. */ int min_priority; } logging_config = { .logger = LOG_TO_SYSLOG, }; /* clang-format on */ #if defined(USE_EXIT_ON_DIE) #define do_abort() exit(1) #else #define do_abort() abort() #endif #if defined(__clang__) #define attribute_no_optimize __attribute__((optnone)) #else #define attribute_no_optimize __attribute__((__optimize__(0))) #endif /* Forces the compiler to perform no optimizations on |var|. */ static void attribute_no_optimize alias(const void *var) { (void)var; } void do_fatal_log(int priority, const char *format, ...) { va_list args, stack_args; va_start(args, format); va_copy(stack_args, args); if (logging_config.logger == LOG_TO_SYSLOG) { vsyslog(priority, format, args); } else { vdprintf(logging_config.fd, format, args); dprintf(logging_config.fd, "\n"); } va_end(args); /* * Write another copy of the first few characters of the message into a * stack-based buffer so that it can appear in minidumps. Choosing a * small-ish buffer size since breakpad will only pick up the first few * kilobytes of each stack, so that will prevent this buffer from * kicking out other stack frames. */ char log_line[512]; vsnprintf(log_line, sizeof(log_line), format, stack_args); va_end(stack_args); alias(log_line); do_abort(); } void do_log(int priority, const char *format, ...) { if (logging_config.logger == LOG_TO_SYSLOG) { va_list args; va_start(args, format); vsyslog(priority, format, args); va_end(args); return; } if (logging_config.min_priority < priority) return; va_list args; va_start(args, format); vdprintf(logging_config.fd, format, args); va_end(args); dprintf(logging_config.fd, "\n"); } /* * Returns the syscall nr and optionally populates the index in the pointer * |ind| if it is non-NULL. */ int lookup_syscall(const char *name, size_t *ind) { size_t ind_tmp = 0; const struct syscall_entry *entry = syscall_table; for (; entry->name && entry->nr >= 0; ++entry) { if (streq(entry->name, name)) { if (ind != NULL) *ind = ind_tmp; return entry->nr; } ind_tmp++; } if (ind != NULL) *ind = -1; return -1; } const char *lookup_syscall_name(long nr) { const struct syscall_entry *entry = syscall_table; for (; entry->name && entry->nr >= 0; ++entry) if (entry->nr == nr) return entry->name; return NULL; } long int parse_single_constant(char *constant_str, char **endptr) { const struct constant_entry *entry = constant_table; long int res = 0; for (; entry->name; ++entry) { if (streq(entry->name, constant_str)) { *endptr = constant_str + strlen(constant_str); return entry->value; } } errno = 0; res = strtol(constant_str, endptr, 0); if (errno == ERANGE) { if (res == LONG_MAX) { /* See if the constant fits in an unsigned long int. */ errno = 0; res = strtoul(constant_str, endptr, 0); if (errno == ERANGE) { /* * On unsigned overflow, use the same convention * as when strtol(3) finds no digits: set * |*endptr| to |constant_str| and return 0. */ warn("unsigned overflow: '%s'", constant_str); *endptr = constant_str; return 0; } } else if (res == LONG_MIN) { /* * Same for signed underflow: set |*endptr| to * |constant_str| and return 0. */ warn("signed underflow: '%s'", constant_str); *endptr = constant_str; return 0; } } if (**endptr != '\0') { warn("trailing garbage after constant: '%s'", constant_str); *endptr = constant_str; return 0; } return res; } static char *tokenize_parenthesized_expression(char **stringp) { char *ret = NULL, *found = NULL; size_t paren_count = 1; /* If the string is NULL, there are no parens to be found. */ if (stringp == NULL || *stringp == NULL) return NULL; /* If the string is not on an open paren, the results are undefined. */ if (**stringp != '(') return NULL; for (found = *stringp + 1; *found; ++found) { switch (*found) { case '(': ++paren_count; break; case ')': --paren_count; if (!paren_count) { *found = '\0'; ret = *stringp + 1; *stringp = found + 1; return ret; } break; } } /* We got to the end without finding the closing paren. */ warn("unclosed parenthesis: '%s'", *stringp); return NULL; } long int parse_constant(char *constant_str_nonnull, char **endptr) { long int value = 0, current_value; /* * The function API says both inputs have to be non-NULL. The code * happens to handle NULL pointers because it resuses the input pointer * as it tokenizes/walks it until the tokenize functions sets it to * NULL. But because of the attributes on the function arguments, the * compiler incorrectly assumes the variable can't become NULL in here, * so we have to create another variable to effectively cast it away. */ char *constant_str = constant_str_nonnull; char *group, *lastpos = constant_str; /* * If |endptr| is provided, parsing errors are signaled as |endptr| * pointing to |constant_str|. */ if (endptr) *endptr = constant_str; /* * Try to parse constant expressions. Valid constant expressions are: * * - A number that can be parsed with strtol(3). * - A named constant expression. * - A parenthesized, valid constant expression. * - A valid constant expression prefixed with the unary bitwise * complement operator ~. * - A series of valid constant expressions separated by pipes. Note * that since |constant_str| is an atom, there can be no spaces * between the constant and the pipe. * * If there is an error parsing any of the constants, the whole process * fails. */ while (constant_str && *constant_str) { bool negate = false; if (*constant_str == '~') { negate = true; ++constant_str; } if (*constant_str == '(') { group = tokenize_parenthesized_expression(&constant_str); if (group == NULL) return 0; char *end = group; /* Recursively parse the parenthesized subexpression. */ current_value = parse_constant(group, &end); if (end == group) return 0; if (constant_str && *constant_str) { /* * If this is not the end of the atom, there * should be another | followed by more stuff. */ if (*constant_str != '|') { warn("unterminated constant " "expression: '%s'", constant_str); return 0; } ++constant_str; if (*constant_str == '\0') { warn("unterminated constant " "expression: '%s'", constant_str); return 0; } } lastpos = end; } else { group = tokenize(&constant_str, "|"); char *end = group; current_value = parse_single_constant(group, &end); if (end == group) return 0; lastpos = end; } if (negate) current_value = ~current_value; value |= current_value; } if (endptr) *endptr = lastpos; return value; } int parse_size(uint64_t *result, const char *sizespec) { uint64_t size; unsigned long long parsed; char *end; /* strtoull supports leading whitespace, -, and + signs. */ if (sizespec[0] < '0' || sizespec[0] > '9') return -EINVAL; /* Clear+check errno so we handle ULLONG_MAX correctly. */ errno = 0; parsed = strtoull(sizespec, &end, 10); if (errno) return -errno; size = parsed; /* See if there's a suffix. */ if (*end != '\0') { static const char suffixes[] = "KMGTPE"; size_t i; /* Only allow 1 suffix. */ if (end[1] != '\0') return -EINVAL; for (i = 0; i < sizeof(suffixes) - 1; ++i) { if (*end == suffixes[i]) { /* Make sure we don't overflow. */ const int scale = (i + 1) * 10; uint64_t mask = ~((UINT64_C(1) << (64 - scale)) - 1); if (size & mask) return -ERANGE; size <<= scale; break; } } /* Unknown suffix. */ if (i == sizeof(suffixes) - 1) return -EINVAL; } *result = size; return 0; } char *strip(char *s) { char *end; while (*s && isblank(*s)) s++; end = s + strlen(s) - 1; while (end >= s && *end && (isblank(*end) || *end == '\n')) end--; *(end + 1) = '\0'; return s; } char *tokenize(char **stringp, const char *delim) { char *ret = NULL; /* If the string is NULL, there are no tokens to be found. */ if (stringp == NULL || *stringp == NULL) return NULL; /* * If the delimiter is NULL or empty, * the full string makes up the only token. */ if (delim == NULL || *delim == '\0') { ret = *stringp; *stringp = NULL; return ret; } char *found = strstr(*stringp, delim); if (!found) { /* * The delimiter was not found, so the full string * makes up the only token, and we're done. */ ret = *stringp; *stringp = NULL; } else { /* There's a token here, possibly empty. That's OK. */ *found = '\0'; ret = *stringp; *stringp = found + strlen(delim); } return ret; } char *path_join(const char *external_path, const char *internal_path) { char *path = NULL; return asprintf(&path, "%s/%s", external_path, internal_path) < 0 ? NULL : path; } bool path_is_parent(const char *parent, const char *child) { /* * -Make sure |child| starts with |parent|. * -Make sure that if |child| is longer than |parent|, either: * --the last character in |parent| is a path separator, or * --the character immediately following |parent| in |child| is a path * separator. */ size_t parent_len = strlen(parent); return strncmp(parent, child, parent_len) == 0 && (strlen(child) > parent_len ? (parent[parent_len - 1] == '/' || child[parent_len] == '/') : false); } void *consumebytes(size_t length, char **buf, size_t *buflength) { char *p = *buf; if (length > *buflength) return NULL; *buf += length; *buflength -= length; return p; } char *consumestr(char **buf, size_t *buflength) { size_t len = strnlen(*buf, *buflength); if (len == *buflength) /* There's no null-terminator. */ return NULL; return consumebytes(len + 1, buf, buflength); } void init_logging(enum logging_system_t logger, int fd, int min_priority) { logging_config.logger = logger; logging_config.fd = fd; logging_config.min_priority = min_priority; } bool logging_to_syslog(void) { return logging_config.logger == LOG_TO_SYSLOG; } void minijail_free_env(char **env) { if (!env) return; for (char **entry = env; *entry; ++entry) { free(*entry); } free(env); } char **minijail_copy_env(char *const *env) { if (!env) return calloc(1, sizeof(char *)); int len = 0; while (env[len]) ++len; char **copy = calloc(len + 1, sizeof(char *)); if (!copy) return NULL; for (char **entry = copy; *env; ++env, ++entry) { *entry = strdup(*env); if (!*entry) { minijail_free_env(copy); return NULL; } } return copy; } /* * Utility function used by minijail_setenv, minijail_unsetenv and * minijail_getenv, returns true if |name| is found, false if not. * If found, |*i| is |name|'s index. If not, |*i| is the length of |envp|. */ static bool getenv_index(char **envp, const char *name, int *i) { if (!envp || !name || !i) return false; size_t name_len = strlen(name); for (*i = 0; envp[*i]; ++(*i)) { /* * If we find a match the size of |name|, we must check * that the next character is a '=', indicating that * the full varname of envp[i] is exactly |name| and * not just happening to start with |name|. */ if (!strncmp(envp[*i], name, name_len) && (envp[*i][name_len] == '=')) { return true; } } /* No match found, |*i| contains the number of elements in |envp|. */ return false; } int minijail_setenv(char ***env, const char *name, const char *value, int overwrite) { if (!env || !*env || !name || !*name || !value) return EINVAL; char **dest = NULL; int i; /* Look in env to check if this var name already exists. */ if (getenv_index(*env, name, &i)) { if (!overwrite) return 0; dest = &(*env)[i]; } char *new_entry = NULL; if (asprintf(&new_entry, "%s=%s", name, value) == -1) return ENOMEM; if (dest) { free(*dest); *dest = new_entry; return 0; } /* getenv_index has set |i| to the length of |env|. */ ++i; char **new_env = realloc(*env, (i + 1) * sizeof(char *)); if (!new_env) { free(new_entry); return ENOMEM; } new_env[i - 1] = new_entry; new_env[i] = NULL; *env = new_env; return 0; } /* * This is like getline() but supports line wrapping with \. */ ssize_t getmultiline(char **lineptr, size_t *n, FILE *stream) { ssize_t ret = getline(lineptr, n, stream); if (ret < 0) return ret; char *line = *lineptr; /* Eat the newline to make processing below easier. */ if (ret > 0 && line[ret - 1] == '\n') line[--ret] = '\0'; /* If the line doesn't end in a backslash, we're done. */ if (ret <= 0 || line[ret - 1] != '\\') return ret; /* This line ends in a backslash. Get the nextline. */ line[--ret] = '\0'; size_t next_n = 0; attribute_cleanup_str char *next_line = NULL; ssize_t next_ret = getmultiline(&next_line, &next_n, stream); if (next_ret == -1) { /* We couldn't fully read the line, so return an error. */ return -1; } /* Merge the lines. */ *n = ret + next_ret + 2; line = realloc(line, *n); if (!line) return -1; line[ret] = ' '; memcpy(&line[ret + 1], next_line, next_ret + 1); *lineptr = line; return *n - 1; } char *minijail_getenv(char **envp, const char *name) { if (!envp || !name) return NULL; int i; if (!getenv_index(envp, name, &i)) return NULL; /* Return a ptr to the value after the '='. */ return envp[i] + strlen(name) + 1; } bool minijail_unsetenv(char **envp, const char *name) { if (!envp || !name) return false; int i; if (!getenv_index(envp, name, &i)) return false; /* We found a match, replace it by the last entry of the array. */ int last; for (last = i; envp[last]; ++last) continue; --last; envp[i] = envp[last]; envp[last] = NULL; return true; } minijail-2026.05.18+git20261016.8787d1a/util.h000066400000000000000000000320351526451555500174640ustar00rootroot00000000000000/* util.h * Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Logging and other utility functions. */ #ifndef _UTIL_H_ #define _UTIL_H_ #include #include #include #include #include #include #include #include #include "libsyscalls.h" #ifdef __cplusplus extern "C" { #endif /* * Silence compiler warnings for unused variables/functions. * * If the definition is actually used, the attribute should be removed, but if * it's forgotten or left in place, it doesn't cause a problem. * * If the definition is actually unused, the compiler is free to remove it from * the output so as to save size. If you want to make sure the definition is * kept (e.g. for ABI compatibility), look at the "used" attribute instead. */ #define attribute_unused __attribute__((__unused__)) /* * Mark the symbol as "weak" in the ELF output. This provides a fallback symbol * that may be overriden at link time. See this page for more details: * https://en.wikipedia.org/wiki/Weak_symbol */ #define attribute_weak __attribute__((__weak__)) /* * Mark the function as a printf-style function. * @format_idx The index in the function argument list where the format string * is passed (where the first argument is "1"). * @check_idx The index in the function argument list where the first argument * used in the format string is passed. * Some examples: * foo([1] const char *format, [2] ...): format=1 check=2 * foo([1] int, [2] const char *format, [3] ...): format=2 check=3 * foo([1] const char *format, [2] const char *, [3] ...): format=1 check=3 */ #define attribute_printf(format_idx, check_idx) \ __attribute__((__format__(__printf__, format_idx, check_idx))) /* * The specified function arguments may not be NULL. Params starts counting * from 1, not 0. If no params are specified, then all function arguments are * marked as non-NULL. Thus, params should only be specified if a function * accepts NULL pointers for any of the arguments. * NB: Keep in sync with libminijail.h style. */ #define attribute_nonnull(params) __attribute__((__nonnull__ params)) #ifndef __cplusplus /* If writing C++, use std::unique_ptr with a destructor instead. */ /* * Mark a local variable for automatic cleanup when exiting its scope. * See attribute_cleanup_fp as an example below. * Make sure any variable using this is always initialized to something. * @func The function to call on (a pointer to) the variable. */ #define attribute_cleanup(func) __attribute__((__cleanup__(func))) /* * Automatically close a FILE* when exiting its scope. * Make sure the pointer is always initialized. * Some examples: * attribute_cleanup_fp FILE *fp = fopen(...); * attribute_cleanup_fp FILE *fp = NULL; * ... * fp = fopen(...); * * NB: This will automatically close the underlying fd, so do not use this * with fdopen calls if the fd should be left open. */ #define attribute_cleanup_fp attribute_cleanup(_cleanup_fp) static inline void _cleanup_fp(FILE **fp) { if (*fp) fclose(*fp); } /* * Automatically close a fd when exiting its scope. * Make sure the fd is always initialized. * Some examples: * attribute_cleanup_fd int fd = open(...); * attribute_cleanup_fd int fd = -1; * ... * fd = open(...); * * NB: Be careful when using this with attribute_cleanup_fp and fdopen. */ #define attribute_cleanup_fd attribute_cleanup(_cleanup_fd) static inline void _cleanup_fd(int *fd) { if (*fd >= 0) close(*fd); } /* * Automatically free a heap allocation when exiting its scope. * Make sure the pointer is always initialized. * Some examples: * attribute_cleanup_str char *s = strdup(...); * attribute_cleanup_str char *s = NULL; * ... * s = strdup(...); */ #define attribute_cleanup_str attribute_cleanup(_cleanup_str) static inline void _cleanup_str(char **ptr) { free(*ptr); } #endif /* __cplusplus */ /* clang-format off */ #define die(_msg, ...) \ do_fatal_log(LOG_ERR, "libminijail[%d]: " _msg, getpid(), ## __VA_ARGS__) #define pdie(_msg, ...) \ die(_msg ": %m", ## __VA_ARGS__) #define warn(_msg, ...) \ do_log(LOG_WARNING, "libminijail[%d]: " _msg, getpid(), ## __VA_ARGS__) #define pwarn(_msg, ...) \ warn(_msg ": %m", ## __VA_ARGS__) #define info(_msg, ...) \ do_log(LOG_INFO, "libminijail[%d]: " _msg, getpid(), ## __VA_ARGS__) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* clang-format on */ extern const char *const log_syscalls[]; extern const size_t log_syscalls_len; extern const char *const libc_compatibility_syscalls[]; extern const size_t libc_compatibility_syscalls_len; enum logging_system_t { /* Log to syslog. This is the default. */ LOG_TO_SYSLOG = 0, /* Log to a file descriptor. */ LOG_TO_FD, }; /* * Even though this function internally calls abort(2)/exit(2), it is * intentionally not marked with the noreturn attribute. When marked as * noreturn, clang coalesces several of the do_fatal_log() calls in methods that * have a large number of such calls (like minijail_enter()), making it * impossible for breakpad to correctly identify the line where it was called, * making the backtrace somewhat useless. */ extern void do_fatal_log(int priority, const char *format, ...) attribute_printf(2, 3); extern void do_log(int priority, const char *format, ...) attribute_printf(2, 3); static inline int is_android(void) { #if defined(__ANDROID__) return 1; #else return 0; #endif } static inline bool compiled_with_asan(void) { #if defined(__SANITIZE_ADDRESS__) /* For gcc. */ return true; #elif defined(__has_feature) /* For clang. */ return __has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer); #else return false; #endif } void __asan_init(void) attribute_weak; void __hwasan_init(void) attribute_weak; static inline bool running_with_asan(void) { /* * There are some configurations under which ASan needs a dynamic (as * opposed to compile-time) test. Some Android processes that start * before /data is mounted run with non-instrumented libminijail.so, so * the symbol-sniffing code must be present to make the right decision. */ return compiled_with_asan() || &__asan_init != 0 || &__hwasan_init != 0; } static inline bool debug_logging_allowed(void) { #if defined(ALLOW_DEBUG_LOGGING) return true; #else return false; #endif } static inline bool seccomp_default_ret_log(void) { #if defined(SECCOMP_DEFAULT_RET_LOG) return true; #else return false; #endif } static inline bool block_symlinks_in_bindmount_paths(void) { #if defined(BLOCK_SYMLINKS_IN_BINDMOUNT_PATHS) return true; #else return false; #endif } static inline bool block_symlinks_in_noninit_mountns_tmp(void) { #if defined(BLOCK_SYMLINKS_IN_NONINIT_MOUNTNS_TMP) return true; #else return false; #endif } static inline size_t get_num_syscalls(void) { return syscall_table_size; } int lookup_syscall(const char *name, size_t *ind) attribute_nonnull((1)); const char *lookup_syscall_name(long nr); long int parse_single_constant(char *constant_str, char **endptr) attribute_nonnull(); long int parse_constant(char *constant_str, char **endptr) attribute_nonnull((1)); /* * parse_size: parse a string to a positive integer bytes with optional suffix. * @size The output parsed size, in bytes * @sizespec The input string to parse * * A single 1-char suffix is supported like "10K" or "6G". These use base 1024, * not base 1000. i.e. "1K" is "1024". It is case-sensitive. * * Returns 0 on success, negative errno on failure. * Only writes to |size| on success. */ int parse_size(uint64_t *size, const char *sizespec) attribute_nonnull(); char *strip(char *s) attribute_nonnull(); /* * streq: determine whether two strings are equal. */ attribute_nonnull() static inline bool streq(const char *s1, const char *s2) { return strcmp(s1, s2) == 0; } /* * tokenize: locate the next token in @stringp using the @delim * @stringp A pointer to the string to scan for tokens * @delim The delimiter to split by * * Note that, unlike strtok, @delim is not a set of characters, but the full * delimiter. e.g. "a,;b,;c" with a delim of ",;" will yield ["a","b","c"]. * * Note that, unlike strtok, this may return an empty token. e.g. "a,,b" with * strtok will yield ["a","b"], but this will yield ["a","","b"]. */ char *tokenize(char **stringp, const char *delim); char *path_join(const char *external_path, const char *internal_path) attribute_nonnull(); /* * path_is_parent: checks whether @parent is a parent of @child. * Note: this function does not evaluate '.' or '..' nor does it resolve * symlinks. */ bool path_is_parent(const char *parent, const char *child) attribute_nonnull(); /* * consumebytes: consumes @length bytes from a buffer @buf of length @buflength * @length Number of bytes to consume * @buf Buffer to consume from * @buflength Size of @buf * * Returns a pointer to the base of the bytes, or NULL for errors. */ void *consumebytes(size_t length, char **buf, size_t *buflength) attribute_nonnull(); /* * consumestr: consumes a C string from a buffer @buf of length @length * @buf Buffer to consume * @length Length of buffer * * Returns a pointer to the base of the string, or NULL for errors. */ char *consumestr(char **buf, size_t *buflength) attribute_nonnull(); /* * init_logging: initializes the module-wide logging. * @logger The logging system to use. * @fd The file descriptor to log into. Ignored unless * @logger = LOG_TO_FD. * @min_priority The minimum priority to display. Corresponds to syslog's * priority parameter. Ignored unless @logger = LOG_TO_FD. */ void init_logging(enum logging_system_t logger, int fd, int min_priority); /* * logging_to_syslog: report whether the active logging backend is syslog. * * Returns true when logging is handled by syslog, false otherwise. */ bool logging_to_syslog(void); /* * minjail_free_env: Frees an environment array plus the environment strings it * points to. The environment and its constituent strings must have been * allocated (as opposed to pointing to static data), e.g. by using * minijail_copy_env() and minijail_setenv(). * * @env The environment to free. */ void minijail_free_env(char **env); /* * minjail_copy_env: Copy an environment array (such as passed to execve), * duplicating the environment strings and the array pointing at them. * * @env The environment to copy. * * Returns a pointer to the copied environment or NULL on memory allocation * failure. */ char **minijail_copy_env(char *const *env); /* * minjail_setenv: Set an environment variable in @env. Semantics match the * standard setenv() function, but this operates on @env, not the global * environment. @env must be dynamically allocated (as opposed to pointing to * static data), e.g. via minijail_copy_env(). @name and @value get copied into * newly-allocated memory. * * @env Address of the environment to modify. Might be re-allocated to * make room for the new entry. * @name Name of the key to set. * @value The value to set. * @overwrite Whether to replace the existing value for @name. If non-zero and * the entry is already present, no changes will be made. * * Returns 0 and modifies *@env on success, returns an error code otherwise. */ int minijail_setenv(char ***env, const char *name, const char *value, int overwrite); /* * getmultiline: This is like getline() but supports line wrapping with \. * * @lineptr Address of a buffer that a mutli-line is stored. * @n Number of bytes stored in *lineptr. * @stream Input stream to read from. * * Returns number of bytes read or -1 on failure to read (including EOF). */ ssize_t getmultiline(char **lineptr, size_t *n, FILE *stream) attribute_nonnull(); /* * minjail_getenv: Get an environment variable from @envp. Semantics match the * standard getenv() function, but this operates on @envp, not the global * environment (usually referred to as `extern char **environ`). * * @env Address of the environment to read from. * @name Name of the key to get. * * Returns a pointer to the corresponding environment value. The caller must * take care not to modify the pointed value, as this points directly to memory * pointed to by @envp. * If the environment variable name is not found, returns NULL. */ char *minijail_getenv(char **env, const char *name); /* * minjail_unsetenv: Clear the environment variable @name from the @envp array * of pointers to strings that have the KEY=VALUE format. If the operation is * successful, the array will contain one item less than before the call. * Only the first occurence is removed. * * @envp Address of the environment to clear the variable from. * @name Name of the variable to clear. * * Returns false and modifies *@envp on success, returns true otherwise. */ bool minijail_unsetenv(char **envp, const char *name); #ifdef __cplusplus }; /* extern "C" */ #endif #endif /* _UTIL_H_ */ minijail-2026.05.18+git20261016.8787d1a/util_unittest.cc000066400000000000000000000346341526451555500215700ustar00rootroot00000000000000/* Copyright 2018 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test util.[ch] module code using gtest. */ #include #include #include #include #include #include #include #include "bpf.h" #include "test_util.h" #include "util.h" namespace { std::string dump_env(const char* const* env) { std::string result; for (; *env; ++env) { result += *env; result += "\n"; } return result; } } // namespace // Sanity check for the strip func. TEST(strip, basic) { char str[] = " foo\t"; ASSERT_EQ("foo", std::string(strip(str))); } // Make sure we don't crash with various "null"-like inputs. TEST(tokenize, null_stringp) { ASSERT_EQ(nullptr, tokenize(nullptr, nullptr)); ASSERT_EQ(nullptr, tokenize(nullptr, "")); ASSERT_EQ(nullptr, tokenize(nullptr, ",")); char* p = nullptr; ASSERT_EQ(nullptr, tokenize(&p, nullptr)); } // Make sure we don't crash with various "null"-like inputs. TEST(tokenize, null_delim) { char str[] = "a,b,c"; char* p = str; ASSERT_EQ(str, tokenize(&p, nullptr)); ASSERT_EQ(nullptr, p); ASSERT_EQ(str, std::string("a,b,c")); p = str; ASSERT_EQ(str, tokenize(&p, "")); ASSERT_EQ(nullptr, p); ASSERT_EQ(str, std::string("a,b,c")); } // Sanity check for the tokenize func. TEST(tokenize, basic) { char str[] = "a,b,c"; char* p = str; ASSERT_EQ("a", std::string(tokenize(&p, ","))); ASSERT_EQ("b", std::string(tokenize(&p, ","))); ASSERT_EQ("c", std::string(tokenize(&p, ","))); ASSERT_EQ(nullptr, p); ASSERT_EQ(nullptr, tokenize(&p, ",")); } // Check edge case with an empty string. TEST(tokenize, empty_string) { char str[] = ""; char* p = str; ASSERT_EQ("", std::string(tokenize(&p, ","))); ASSERT_EQ(nullptr, p); ASSERT_EQ(nullptr, tokenize(&p, ",")); } // Check behavior with empty tokens at the start/middle/end. TEST(tokenize, empty_tokens) { char str[] = ",,a,b,,,c,,"; char* p = str; ASSERT_EQ("", std::string(tokenize(&p, ","))); ASSERT_EQ("", std::string(tokenize(&p, ","))); ASSERT_EQ("a", std::string(tokenize(&p, ","))); ASSERT_EQ("b", std::string(tokenize(&p, ","))); ASSERT_EQ("", std::string(tokenize(&p, ","))); ASSERT_EQ("", std::string(tokenize(&p, ","))); ASSERT_EQ("c", std::string(tokenize(&p, ","))); ASSERT_EQ("", std::string(tokenize(&p, ","))); ASSERT_EQ("", std::string(tokenize(&p, ","))); ASSERT_EQ(nullptr, p); ASSERT_EQ(nullptr, tokenize(&p, ",")); } // Check environment manipulation functions. TEST(environment, copy_and_modify) { minijail_free_env(nullptr); char** env = minijail_copy_env(nullptr); EXPECT_EQ("", dump_env(env)); minijail_free_env(env); const char* const kConstEnv[] = { "val1=1", "val2=2", "dup=1", "dup=2", "empty=", nullptr, }; // libc unfortunately uses char* const[] as the type for the environment, and // we match that. It's actually missing a const-ness of the chars making up // the environment strings, but we need that to initialize the |kEnv| // constant. Hence, do a cast here to force things into alignment... char* const* kEnv = const_cast(kConstEnv); env = minijail_copy_env(kEnv); EXPECT_EQ("val1=1\nval2=2\ndup=1\ndup=2\nempty=\n", dump_env(env)); minijail_free_env(env); env = minijail_copy_env(kEnv); EXPECT_EQ("val1=1\nval2=2\ndup=1\ndup=2\nempty=\n", dump_env(env)); EXPECT_EQ(EINVAL, minijail_setenv(nullptr, "val1", "3", 1)); char** env_ret = nullptr; EXPECT_EQ(EINVAL, minijail_setenv(&env_ret, "val1", "3", 1)); env_ret = env; EXPECT_EQ(EINVAL, minijail_setenv(&env_ret, nullptr, "3", 1)); EXPECT_EQ(env, env_ret); EXPECT_EQ(EINVAL, minijail_setenv(&env_ret, "", "3", 1)); EXPECT_EQ(env, env_ret); EXPECT_EQ(EINVAL, minijail_setenv(&env_ret, "", nullptr, 1)); EXPECT_EQ(env, env_ret); EXPECT_EQ(0, minijail_setenv(&env, "val1", "3", 0)); EXPECT_EQ("val1=1\nval2=2\ndup=1\ndup=2\nempty=\n", dump_env(env)); EXPECT_EQ(0, minijail_setenv(&env, "val1", "3", 1)); EXPECT_EQ("val1=3\nval2=2\ndup=1\ndup=2\nempty=\n", dump_env(env)); EXPECT_EQ(0, minijail_setenv(&env, "val2", "4", 1)); EXPECT_EQ("val1=3\nval2=4\ndup=1\ndup=2\nempty=\n", dump_env(env)); EXPECT_EQ(0, minijail_setenv(&env, "dup", "5", 1)); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\n", dump_env(env)); EXPECT_EQ(0, minijail_setenv(&env, "empty", "6", 1)); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=6\n", dump_env(env)); EXPECT_EQ(0, minijail_setenv(&env, "empty", "", 1)); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\n", dump_env(env)); EXPECT_EQ(0, minijail_setenv(&env, "new1", "7", 0)); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\n", dump_env(env)); EXPECT_EQ(0, minijail_setenv(&env, "new2", "8", 1)); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\nnew2=8\n", dump_env(env)); EXPECT_EQ(nullptr, minijail_getenv(nullptr, "dup")); EXPECT_EQ(nullptr, minijail_getenv(nullptr, nullptr)); EXPECT_EQ(nullptr, minijail_getenv(env, nullptr)); EXPECT_EQ(nullptr, minijail_getenv(env, "dup=")); EXPECT_EQ(nullptr, minijail_getenv(env, "du")); EXPECT_EQ(std::string("8"), minijail_getenv(env, "new2")); EXPECT_EQ(std::string("3"), minijail_getenv(env, "val1")); EXPECT_EQ(std::string("5"), minijail_getenv(env, "dup")); EXPECT_EQ(false, minijail_unsetenv(env, "nonexisting")); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\nnew2=8\n", dump_env(env)); EXPECT_EQ(false, minijail_unsetenv(env, "")); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\nnew2=8\n", dump_env(env)); EXPECT_EQ(false, minijail_unsetenv(env, nullptr)); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\nnew2=8\n", dump_env(env)); EXPECT_EQ(false, minijail_unsetenv(nullptr, nullptr)); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\nnew2=8\n", dump_env(env)); EXPECT_EQ(false, minijail_unsetenv(nullptr, "nonexisting")); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\nnew2=8\n", dump_env(env)); EXPECT_EQ(false, minijail_unsetenv(env, "val1=")); EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\nnew2=8\n", dump_env(env)); EXPECT_EQ(true, minijail_unsetenv(env, "val1")); EXPECT_EQ("new2=8\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\n", dump_env(env)); EXPECT_EQ(true, minijail_unsetenv(env, "empty")); EXPECT_EQ("new2=8\nval2=4\ndup=5\ndup=2\nnew1=7\n", dump_env(env)); EXPECT_EQ(true, minijail_unsetenv(env, "new2")); EXPECT_EQ("new1=7\nval2=4\ndup=5\ndup=2\n", dump_env(env)); EXPECT_EQ(true, minijail_unsetenv(env, "new1")); EXPECT_EQ("dup=2\nval2=4\ndup=5\n", dump_env(env)); minijail_free_env(env); } TEST(logging_to_syslog, reports_syslog_when_initialized) { init_logging(LOG_TO_SYSLOG, -1, LOG_INFO); EXPECT_TRUE(logging_to_syslog()); } TEST(logging_to_syslog, reports_fd_when_initialized_to_fd) { init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO); EXPECT_FALSE(logging_to_syslog()); init_logging(LOG_TO_SYSLOG, -1, LOG_INFO); } TEST(parse_single_constant, formats) { char* end; long int c = 0; std::string constant; // Check base 10 works. constant = "1234"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(1234, c); // Check base 16 works. constant = "0x1234"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(0x1234, c); // Check base 8 works. constant = "01234"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(01234, c); } TEST(parse_constant, unsigned) { char* end; long int c = 0; std::string constant; #if defined(BITS32) constant = "0x80000000"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(0x80000000U, static_cast(c)); #elif defined(BITS64) constant = "0x8000000000000000"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(0x8000000000000000UL, static_cast(c)); #else #error "unknown bits!" #endif } TEST(parse_constant, unsigned_toobig) { char* end; long int c = 0; std::string constant; #if defined(BITS32) constant = "0x100000000"; // Too big for 32-bit unsigned long int. c = parse_constant(const_cast(constant.data()), &end); // Error case should return 0. EXPECT_EQ(0, c); #elif defined(BITS64) constant = "0x10000000000000000"; c = parse_constant(const_cast(constant.data()), &end); // Error case should return 0. EXPECT_EQ(0, c); #else #error "unknown bits!" #endif } TEST(parse_constant, signed) { char* end; long int c = 0; std::string constant = "-1"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(-1, c); } TEST(parse_constant, signed_toonegative) { char* end; long int c = 0; std::string constant; #if defined(BITS32) constant = "-0x80000001"; c = parse_constant(const_cast(constant.data()), &end); // Error case should return 0. EXPECT_EQ(0, c); #elif defined(BITS64) constant = "-0x8000000000000001"; c = parse_constant(const_cast(constant.data()), &end); // Error case should return 0. EXPECT_EQ(0, c); #else #error "unknown bits!" #endif } TEST(parse_constant, complements) { char* end; long int c = 0; std::string constant; #if defined(BITS32) constant = "~0x005AF0FF|~0xFFA50FFF"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(c, 0xFFFFFF00); constant = "0x0F|~(0x005AF000|0x00A50FFF)|0xF0"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(c, 0xFF0000FF); #elif defined(BITS64) constant = "~0x00005A5AF0F0FFFF|~0xFFFFA5A50F0FFFFF"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(c, 0xFFFFFFFFFFFF0000UL); constant = "0x00FF|~(0x00005A5AF0F00000|0x0000A5A50F0FFFFF)|0xFF00"; c = parse_constant(const_cast(constant.data()), &end); EXPECT_EQ(c, 0xFFFF00000000FFFFUL); #else #error "unknown bits!" #endif } TEST(parse_constant, parenthesized_expresions) { char* end; const std::vector bad_expressions = { "(1", "1)", "(1)1", "|(1)", "(1)|", "()", "(", "((", "(()", "(()1", "1(0)", }; for (const auto* expression : bad_expressions) { std::string mutable_expression = expression; long int c = parse_constant(const_cast(mutable_expression.data()), &end); EXPECT_EQ(reinterpret_cast(end), reinterpret_cast(mutable_expression.data())); // Error case should return 0. EXPECT_EQ(c, 0) << "For expression: \"" << expression << "\""; } const std::vector good_expressions = { "(3)", "(1)|2", "1|(2)", "(1)|(2)", "((3))", "0|(1|2)", "(0|1|2)", }; for (const auto* expression : good_expressions) { std::string mutable_expression = expression; long int c = parse_constant(const_cast(mutable_expression.data()), &end); EXPECT_EQ(c, 3) << "For expression: \"" << expression << "\""; } } TEST(parse_size, complete) { uint64_t size; ASSERT_EQ(0, parse_size(&size, "42")); ASSERT_EQ(42U, size); ASSERT_EQ(0, parse_size(&size, "16K")); ASSERT_EQ(16384U, size); ASSERT_EQ(0, parse_size(&size, "1M")); ASSERT_EQ(1024U * 1024, size); uint64_t gigabyte = UINT64_C(1024) * 1024 * 1024; ASSERT_EQ(0, parse_size(&size, "3G")); ASSERT_EQ(3U, size / gigabyte); ASSERT_EQ(0U, size % gigabyte); ASSERT_EQ(0, parse_size(&size, "4294967294")); ASSERT_EQ(3U, size / gigabyte); ASSERT_EQ(gigabyte - 2, size % gigabyte); uint64_t exabyte = gigabyte * 1024 * 1024 * 1024; ASSERT_EQ(0, parse_size(&size, "9E")); ASSERT_EQ(9U, size / exabyte); ASSERT_EQ(0U, size % exabyte); ASSERT_EQ(0, parse_size(&size, "15E")); ASSERT_EQ(15U, size / exabyte); ASSERT_EQ(0U, size % exabyte); // Check values that don't fit in 64-bits. ASSERT_EQ(-ERANGE, parse_size(&size, "16384P")); ASSERT_EQ(-ERANGE, parse_size(&size, "1638400P")); ASSERT_EQ(-ERANGE, parse_size(&size, "16E")); ASSERT_EQ(-ERANGE, parse_size(&size, "16000000000E")); // Check limits right around 64-bits. ASSERT_EQ(0, parse_size(&size, "18446744073709551614")); ASSERT_EQ(ULLONG_MAX - 1, size); ASSERT_EQ(0, parse_size(&size, "18446744073709551615")); ASSERT_EQ(ULLONG_MAX, size); ASSERT_EQ(-ERANGE, parse_size(&size, "18446744073709551616")); // Only allow 1 valid suffix. ASSERT_EQ(-EINVAL, parse_size(&size, "7GTPE")); // Check edge cases that strto* APIs accept, but we don't. ASSERT_EQ(-EINVAL, parse_size(&size, "-8")); ASSERT_EQ(-EINVAL, parse_size(&size, "+8")); ASSERT_EQ(-EINVAL, parse_size(&size, " -8")); ASSERT_EQ(-EINVAL, parse_size(&size, " +8")); ASSERT_EQ(-EINVAL, parse_size(&size, "")); ASSERT_EQ(-EINVAL, parse_size(&size, "14u")); ASSERT_EQ(-EINVAL, parse_size(&size, "14B")); ASSERT_EQ(-EINVAL, parse_size(&size, "14Z")); ASSERT_EQ(-EINVAL, parse_size(&size, "14.2G")); ASSERT_EQ(-EINVAL, parse_size(&size, "G")); ASSERT_EQ(-EINVAL, parse_size(&size, "; /bin/rm -- ")); } TEST(path_join, basic) { char* path = path_join("a", "b"); ASSERT_EQ(std::string("a/b"), path); free(path); } TEST(path_is_parent, simple) { EXPECT_TRUE(path_is_parent("/dev", "/dev/rtc")); EXPECT_TRUE(path_is_parent("/dev/", "/dev/rtc")); EXPECT_TRUE(path_is_parent("/sys", "/sys/power")); EXPECT_TRUE(path_is_parent("/sys/power", "/sys/power/something")); EXPECT_TRUE(path_is_parent("/sys", "/sys/sys/power")); EXPECT_FALSE(path_is_parent("/dev", "")); EXPECT_FALSE(path_is_parent("/dev", "/sys")); EXPECT_FALSE(path_is_parent("/dev", "dev")); EXPECT_FALSE(path_is_parent("/dev", "/sys/dev")); EXPECT_FALSE(path_is_parent("/dev", "/device")); } TEST(getmultiline, basic) { std::string config = "\n" "mount = none\n" "mount =\\\n" "none\n" "binding = none,/tmp\n" "binding = none,\\\n" "/tmp"; FILE* config_file = write_to_pipe(config); ASSERT_NE(config_file, nullptr); char* line = NULL; size_t len = 0; ASSERT_EQ(0, getmultiline(&line, &len, config_file)); EXPECT_EQ(std::string(line), ""); ASSERT_EQ(12, getmultiline(&line, &len, config_file)); EXPECT_EQ(std::string(line), "mount = none"); ASSERT_EQ(12, getmultiline(&line, &len, config_file)); EXPECT_EQ(std::string(line), "mount = none"); ASSERT_EQ(19, getmultiline(&line, &len, config_file)); EXPECT_EQ(std::string(line), "binding = none,/tmp"); ASSERT_EQ(20, getmultiline(&line, &len, config_file)); EXPECT_EQ(std::string(line), "binding = none, /tmp"); free(line); }