Snap for 8730993 from 70e1cac63d9545c24b6667f99813e868f6c75c80 to mainline-tzdata3-release
Change-Id: Id09474bbc20323009fb714a1f89c6a2313887bdc
diff --git a/.github/mistaken-pull-closer.yml b/.github/mistaken-pull-closer.yml
new file mode 100644
index 0000000..eb4cb89
--- /dev/null
+++ b/.github/mistaken-pull-closer.yml
@@ -0,0 +1,17 @@
+# The JSONPath filter expression used to identify which PRs to close.
+# The data filtered is the pull request data along with other metadata passed in
+# by probot.
+# See https://goessner.net/articles/JsonPath/
+# `true` will close all PRs.
+filters:
+ - true
+
+# The message to post to the closed PR.
+commentBody: |
+ 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/master/README.md) which provides full instructions
+ on how to get involved.
+
+# Whether to add a label to the closed PR.
+addLabel: false
diff --git a/.github/workflows/build-test-ci.yml b/.github/workflows/build-test-ci.yml
index 74a95aa..bed8a43 100644
--- a/.github/workflows/build-test-ci.yml
+++ b/.github/workflows/build-test-ci.yml
@@ -8,14 +8,6 @@
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:
diff --git a/.github/workflows/close-pull-request.yml b/.github/workflows/close-pull-request.yml
deleted file mode 100644
index da11c4a..0000000
--- a/.github/workflows/close-pull-request.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
deleted file mode 100644
index 42839cc..0000000
--- a/.github/workflows/coverity.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-# 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@v2
- - name: Install system packages
- run: sudo apt-get install -y libcap-dev
- - uses: vapier/coverity-scan-action@v0
- with:
- project: google%2Fminijail
- email: [email protected]
- token: ${{ secrets.COVERITY_SCAN_TOKEN }}
diff --git a/.gitignore b/.gitignore
index a0038c4..9ce33c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,9 +24,13 @@
# Executables when compiling in-tree.
/dump_constants
+/libminijail_unittest
/minijail0
+/minijail0_cli_unittest
+/syscall_filter_unittest
+/system_unittest
+/util_unittest
/parse_seccomp_policy
-/*_unittest
# common.mk TEST().
*.test
diff --git a/Android.bp b/Android.bp
index 0a80f77..e98cdad 100644
--- a/Android.bp
+++ b/Android.bp
@@ -62,7 +62,6 @@
unittestSrcFiles = [
"testrunner.cc",
- "test_util.cc",
]
minijailCommonLibraries = ["libcap"]
@@ -92,6 +91,7 @@
vendor_available: true,
product_available: true,
recovery_available: true,
+ header_libs: ["libc_headers"], // TODO(b/153662223): Clean this up.
srcs: ["gen_syscalls.c"],
cflags: [
"-dD",
@@ -102,7 +102,6 @@
apex_available: [
"//apex_available:platform",
"com.android.adbd",
- "com.android.compos",
"com.android.media.swcodec",
"com.android.virt",
],
@@ -121,7 +120,6 @@
apex_available: [
"//apex_available:platform",
"com.android.adbd",
- "com.android.compos",
"com.android.media.swcodec",
"com.android.virt",
],
@@ -132,6 +130,7 @@
vendor_available: true,
product_available: true,
recovery_available: true,
+ header_libs: ["libc_headers"], // TODO(b/153662223): Clean this up.
srcs: ["gen_constants.c"],
cflags: [
"-dD",
@@ -142,7 +141,6 @@
apex_available: [
"//apex_available:platform",
"com.android.adbd",
- "com.android.compos",
"com.android.media.swcodec",
"com.android.virt",
],
@@ -161,7 +159,6 @@
apex_available: [
"//apex_available:platform",
"com.android.adbd",
- "com.android.compos",
"com.android.media.swcodec",
"com.android.virt",
],
@@ -192,7 +189,6 @@
apex_available: [
"//apex_available:platform",
"com.android.adbd",
- "com.android.compos",
"com.android.media.swcodec",
"com.android.virt",
],
@@ -205,6 +201,7 @@
product_available: true,
recovery_available: true,
host_supported: true,
+ header_libs: ["libc_headers"], // TODO(b/153662223): Clean this up.
cflags: [
"-S",
"-O0",
@@ -227,6 +224,7 @@
product_available: true,
recovery_available: true,
host_supported: true,
+ header_libs: ["libc_headers"], // TODO(b/153662223): Clean this up.
cflags: [
"-S",
"-O0",
@@ -279,7 +277,6 @@
apex_available: [
"//apex_available:platform",
"com.android.adbd",
- "com.android.compos",
"com.android.media.swcodec",
"com.android.virt",
],
@@ -444,7 +441,6 @@
"-DPRELOADPATH=\"/invalid\"",
],
srcs: libminijailSrcFiles + [
- "config_parser.c",
"elfparse.c",
"minijail0_cli.c",
"minijail0_cli_unittest.cc",
@@ -458,41 +454,6 @@
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.
@@ -541,7 +502,6 @@
"-DPRELOADPATH=\"/invalidminijailpreload.so\"",
],
srcs: [
- "config_parser.c",
"elfparse.c",
"minijail0.c",
"minijail0_cli.c",
@@ -578,7 +538,6 @@
],
apex_available: [
"//apex_available:platform",
- "com.android.compos",
"com.android.virt",
],
}
@@ -598,7 +557,6 @@
],
apex_available: [
"//apex_available:platform",
- "com.android.compos",
"com.android.virt",
],
}
diff --git a/DIR_METADATA b/DIR_METADATA
deleted file mode 100644
index 4a465c7..0000000
--- a/DIR_METADATA
+++ /dev/null
@@ -1,17 +0,0 @@
-# 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: "[email protected]"
diff --git a/Makefile b/Makefile
index f44f867..b7022b5 100644
--- a/Makefile
+++ b/Makefile
@@ -10,9 +10,6 @@
PRELOADPATH = "$(LIBDIR)/$(PRELOADNAME)"
CPPFLAGS += -DPRELOADPATH='$(PRELOADPATH)'
-# 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)"'
@@ -21,23 +18,10 @@
CPPFLAGS += -DUSE_SECCOMP_SOFTFAIL
endif
-BLOCK_NOEXEC_CONF ?= no
-ifeq ($(BLOCK_NOEXEC_CONF),yes)
-CPPFLAGS += -DBLOCK_NOEXEC_CONF
-endif
-
-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
ifeq ($(USE_ASAN),yes)
@@ -62,27 +46,20 @@
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++14
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
-UNITTEST_LIBS += $(GTEST_LIBS)
CORE_OBJECT_FILES := libminijail.o syscall_filter.o signal_handler.o \
bpf.o util.o system.o syscall_wrapper.o \
- config_parser.o libconstants.gen.o libsyscalls.gen.o
-UNITTEST_DEPS += $(CORE_OBJECT_FILES)
+ libconstants.gen.o libsyscalls.gen.o
all: CC_BINARY(minijail0) CC_LIBRARY(libminijail.so) \
CC_LIBRARY(libminijailpreload.so)
@@ -95,7 +72,7 @@
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) \
@@ -111,14 +88,14 @@
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
+CXX_BINARY(libminijail_unittest): LDLIBS += -lcap $(GTEST_LIBS)
+ifeq ($(USE_SYSTEM_GTEST),no)
+CXX_BINARY(libminijail_unittest): $(GTEST_LIBS)
+endif
+CXX_BINARY(libminijail_unittest): libminijail_unittest.o $(CORE_OBJECT_FILES) \
+ testrunner.o
clean: CLEAN(libminijail_unittest)
TEST(CXX_BINARY(libminijail_unittest)): CC_LIBRARY(libminijailpreload.so)
@@ -130,33 +107,43 @@
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
+CXX_BINARY(minijail0_cli_unittest): LDLIBS += -lcap $(GTEST_LIBS)
+ifeq ($(USE_SYSTEM_GTEST),no)
+CXX_BINARY(minijail0_cli_unittest): $(GTEST_LIBS)
+endif
+CXX_BINARY(minijail0_cli_unittest): minijail0_cli_unittest.o \
+ $(CORE_OBJECT_FILES) minijail0_cli.o elfparse.o testrunner.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
+CXX_BINARY(syscall_filter_unittest): LDLIBS += -lcap $(GTEST_LIBS)
+ifeq ($(USE_SYSTEM_GTEST),no)
+CXX_BINARY(syscall_filter_unittest): $(GTEST_LIBS)
+endif
+CXX_BINARY(syscall_filter_unittest): syscall_filter_unittest.o \
+ $(CORE_OBJECT_FILES) testrunner.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
+CXX_BINARY(system_unittest): LDLIBS += -lcap $(GTEST_LIBS)
+ifeq ($(USE_SYSTEM_GTEST),no)
+CXX_BINARY(system_unittest): $(GTEST_LIBS)
+endif
+CXX_BINARY(system_unittest): system_unittest.o \
+ $(CORE_OBJECT_FILES) testrunner.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
+CXX_BINARY(util_unittest): LDLIBS += -lcap $(GTEST_LIBS)
+ifeq ($(USE_SYSTEM_GTEST),no)
+CXX_BINARY(util_unittest): $(GTEST_LIBS)
+endif
+CXX_BINARY(util_unittest): util_unittest.o \
+ $(CORE_OBJECT_FILES) testrunner.o
clean: CLEAN(util_unittest)
@@ -183,9 +170,10 @@
# 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)/,,$<) -> $@"
+libsyscalls.gen.c: $(SRC)/Makefile $(SRC)/libsyscalls.h
+ @printf "Generating target-arch specific $@...\n"
$(QUIET)CC="$(CC)" $(SRC)/gen_syscalls.sh "$@"
+ @printf "$@ done.\n"
clean: CLEAN(libsyscalls.gen.c)
$(eval $(call add_object_rules,libsyscalls.gen.o,CC,c,CFLAGS))
@@ -196,9 +184,10 @@
# 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)/,,$<) -> $@"
+libconstants.gen.c: $(SRC)/Makefile $(SRC)/libconstants.h
+ @printf "Generating target-arch specific $@...\n"
$(QUIET)CC="$(CC)" $(SRC)/gen_constants.sh "$@"
+ @printf "$@ done.\n"
clean: CLEAN(libconstants.gen.c)
$(eval $(call add_object_rules,libconstants.gen.o,CC,c,CFLAGS))
@@ -210,7 +199,7 @@
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-release-1.11.0/googletest
+GTEST_DIR = googletest-release-1.10.0/googletest
# Flags passed to the preprocessor.
# Set Google Test's header directory as a system directory, such that
@@ -229,7 +218,7 @@
clean: clean_gtest
clean_gtest:
- $(QUIET)rm -f gtest.a gtest_main.a *.o
+ rm -f gtest.a gtest_main.a *.o
# Builds gtest.a and gtest_main.a.
diff --git a/OWNERS b/OWNERS
index 2823db3..ebadeca 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,5 @@
set noparent
-include OWNERS_GENERAL
-# Emeritus.
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/OWNERS_GENERAL b/OWNERS_GENERAL
deleted file mode 100644
index e5179ef..0000000
--- a/OWNERS_GENERAL
+++ /dev/null
@@ -1,3 +0,0 @@
[email protected]
[email protected]
[email protected]
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
index a7ce524..d76d432 100644
--- a/PRESUBMIT.cfg
+++ b/PRESUBMIT.cfg
@@ -6,6 +6,3 @@
# 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/
diff --git a/README.md b/README.md
index 71a34c6..3b528e5 100644
--- a/README.md
+++ b/README.md
@@ -114,51 +114,3 @@
https://crrev.com/c/4585/ added the original implementation.
Source: Conversations with original authors, ellyjones@ and wad@.
-
-## How to manually upgrade Minijail on Chrome OS
-
-Minijail is manually upgraded on Chrome OS so that there is a way to test
-changes in the Chrome OS commit queue. Committed changes have already passed
-Android's presubmit checks, but the ebuild upgrade CL goes through the Chrome
-OS commit queue and must pass the tests before any additional changes are
-available for use on Chrome OS. To upgrade minijail on Chrome OS, complete the
-following steps.
-
-```bash
-# Sync Minijail repo
-cd ~/chromiumos/src/aosp/external/minijail
-git checkout m/main
-repo sync .
-
-# Set up local branch.
-cd ~/trunk/src/third_party/chromiumos-overlay/
-repo start minijail . # replace minijail with the local branch name you want.
-
-# Run upgrade script.
-~/trunk/chromite/scripts/cros_uprev --force --overlay-type public \
- --packages chromeos-base/minijail:dev-rust/minijail-sys:dev-rust/minijail
-```
-
-At this point the Minijail-related packages should be upgraded, so you may want
-to add the changes to a commit and do some local testing before uploading a
-change list. Here are the recommended local tests to try (make sure you are
-**not** working on the minijail packages first i.e. `cros_workon list-all`):
-
-```bash
-# Check build.
-./build_packages --board=${BOARD}
-
-# Check unit tests.
-FEATURES=test emerge-${BOARD} chromeos-base/minijail dev-rust/minijail-sys \
- dev-rust/minijail
-
-# Check integration tests.
-cros deploy <DUT> chromeos-base/minijail
-tast run <DUT> security.Minijail.* security.MinijailSeccomp
-```
-
-Finally, when uploading the CL make sure to include the list of changes
-since the last uprev. The command to generate the list is as follows:
-```bash
-git log --oneline --no-merges <previous hash in ebuild file>..HEAD
-```
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 539379e..bdded19 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,19 +12,5 @@
{
"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"
- }
]
}
diff --git a/arch.h b/arch.h
index 253b45f..e027831 100644
--- a/arch.h
+++ b/arch.h
@@ -62,17 +62,6 @@
#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"
diff --git a/config_parser.c b/config_parser.c
deleted file mode 100644
index 4972cf5..0000000
--- a/config_parser.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright 2021 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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 (strcmp(line, "% minijail-config-file v0\n") == 0);
-}
-
-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 the following errors.
- */
- if (errno == EINVAL || errno == ENOMEM) {
- 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;
-}
diff --git a/config_parser.h b/config_parser.h
deleted file mode 100644
index 36c96db..0000000
--- a/config_parser.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2021 The Chromium OS Authors. All rights reserved.
- * 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 <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-
-#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 */
diff --git a/config_parser_unittest.cc b/config_parser_unittest.cc
deleted file mode 100644
index a9c6571..0000000
--- a/config_parser_unittest.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-/* Copyright 2021 The Chromium OS Authors. All rights reserved.
- * 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 <gtest/gtest.h>
-#include <string>
-
-#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<std::string> 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<std::string> 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");
-}
diff --git a/elfparse.c b/elfparse.c
index 96caa59..e76ebc4 100644
--- a/elfparse.c
+++ b/elfparse.c
@@ -11,7 +11,6 @@
#include <unistd.h>
#include "elfparse.h"
-#include "util.h"
int is_elf_magic (const uint8_t *buf)
{
@@ -68,7 +67,7 @@
ElfType get_elf_linkage(const char *path)
{
ElfType ret = ELFERROR;
- attribute_cleanup_fp FILE *elf_file = NULL;
+ FILE *elf_file = NULL;
uint8_t pHeader[HEADERSIZE] = "";
elf_file = fopen(path, "re");
@@ -113,6 +112,7 @@
*/
ret = ELFDYNAMIC;
}
+ fclose(elf_file);
}
return ret;
}
diff --git a/gen_syscalls-inl.h b/gen_syscalls-inl.h
index d7c43aa..2004f7a 100644
--- a/gen_syscalls-inl.h
+++ b/gen_syscalls-inl.h
@@ -73,7 +73,3 @@
#ifndef __NR_io_uring_setup
#define __NR_io_uring_setup 425
#endif
-
-#ifndef __NR_faccessat2
-#define __NR_faccessat2 439
-#endif
diff --git a/get_googletest.sh b/get_googletest.sh
index 07910b4..b379441 100755
--- a/get_googletest.sh
+++ b/get_googletest.sh
@@ -1,6 +1,6 @@
#/bin/bash
-PV="1.11.0"
+PV="1.10.0"
wget -q -nc --secure-protocol=TLSv1 "https://github.com/google/googletest/archive/release-${PV}.tar.gz" -O "googletest-release-${PV}.tar.gz"
tar zxvf "googletest-release-${PV}.tar.gz"
diff --git a/libminijail-private.h b/libminijail-private.h
index 8feec55..503f09b 100644
--- a/libminijail-private.h
+++ b/libminijail-private.h
@@ -19,9 +19,8 @@
*/
#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 *kFdEnvVar = "__MINIJAIL_FD";
+static const char *kLdPreloadEnvVar = "LD_PRELOAD";
struct minijail;
@@ -47,7 +46,9 @@
* 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);
+extern int minijail_marshal(const struct minijail *j,
+ char *buf,
+ size_t size);
/* minijail_unmarshal: initializes @j from @serialized
* @j minijail to initialize
@@ -56,8 +57,9 @@
*
* Returns 0 on success.
*/
-extern int minijail_unmarshal(struct minijail *j, char *serialized,
- size_t length);
+extern int minijail_unmarshal(struct minijail *j,
+ char *serialized,
+ size_t length);
/* minijail_from_fd: builds @j from @fd
* @j minijail to initialize
diff --git a/libminijail.c b/libminijail.c
index aab1294..0820dbb 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -36,8 +36,8 @@
#include <syscall.h>
#include <unistd.h>
-#include "libminijail-private.h"
#include "libminijail.h"
+#include "libminijail-private.h"
#include "signal_handler.h"
#include "syscall_filter.h"
@@ -47,19 +47,19 @@
/* Until these are reliably available in linux/prctl.h. */
#ifndef PR_ALT_SYSCALL
-#define PR_ALT_SYSCALL 0x43724f53
+# define PR_ALT_SYSCALL 0x43724f53
#endif
/* New cgroup namespace might not be in linux-headers yet. */
#ifndef CLONE_NEWCGROUP
-#define CLONE_NEWCGROUP 0x02000000
+# 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 128U
+#define MAX_PRESERVED_FDS 32U
/* Keyctl commands. */
#define KEYCTL_JOIN_SESSION_KEYRING 1
@@ -189,17 +189,11 @@
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) {
@@ -374,8 +368,7 @@
j->flags.set_suppl_gids = 1;
}
-void API minijail_keep_supplementary_gids(struct minijail *j)
-{
+void API minijail_keep_supplementary_gids(struct minijail *j) {
j->flags.keep_suppl_gids = 1;
}
@@ -426,7 +419,7 @@
"before minijail_parse_seccomp_filters()");
}
- if (seccomp_is_logging_allowed(j) && !seccomp_ret_log_available()) {
+ if (j->flags.seccomp_filter_logging && !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
@@ -462,8 +455,8 @@
* 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");
+ die("SECCOMP_RET_LOG not available, cannot use thread sync with "
+ "logging at the same time");
}
if (debug_logging_allowed()) {
@@ -730,7 +723,7 @@
*/
if (!strncmp(b->dest, path_inside_chroot, strlen(b->dest))) {
const char *relative_path =
- path_inside_chroot + strlen(b->dest);
+ path_inside_chroot + strlen(b->dest);
return path_join(b->src, relative_path);
}
b = b->next;
@@ -806,8 +799,7 @@
return 0;
}
-int API minijail_create_session(struct minijail *j)
-{
+int API minijail_create_session(struct minijail *j) {
j->flags.setsid = 1;
return 0;
}
@@ -985,10 +977,6 @@
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)
@@ -1098,7 +1086,7 @@
* Allow logging?
*/
filteropts.allow_logging =
- debug_logging_allowed() && seccomp_is_logging_allowed(j);
+ debug_logging_allowed() && j->flags.seccomp_filter_logging;
/* What to do on a blocked system call? */
if (filteropts.allow_logging) {
@@ -1141,7 +1129,7 @@
if (!seccomp_should_use_filters(j))
return;
- attribute_cleanup_fp FILE *file = fopen(path, "re");
+ FILE *file = fopen(path, "re");
if (!file) {
pdie("failed to open seccomp filter file '%s'", path);
}
@@ -1150,16 +1138,13 @@
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);
+ fclose(file);
}
void API minijail_parse_seccomp_filters_from_fd(struct minijail *j, int fd)
{
char *fd_path, *path;
- attribute_cleanup_fp FILE *file = NULL;
+ FILE *file;
if (!seccomp_should_use_filters(j))
return;
@@ -1180,10 +1165,8 @@
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;
+ free(path);
+ fclose(file);
}
void API minijail_set_seccomp_filters(struct minijail *j,
@@ -1192,7 +1175,7 @@
if (!seccomp_should_use_filters(j))
return;
- if (seccomp_is_logging_allowed(j)) {
+ if (j->flags.seccomp_filter_logging) {
die("minijail_log_seccomp_filter_failures() is incompatible "
"with minijail_set_seccomp_filters()");
}
@@ -1293,8 +1276,6 @@
}
for (i = 0; i < j->cgroup_count; ++i)
marshal_append_string(state, j->cgroups[i]);
- if (j->seccomp_policy_path)
- marshal_append_string(state, j->seccomp_policy_path);
}
size_t API minijail_size(const struct minijail *j)
@@ -1338,7 +1319,7 @@
j->hooks_head = NULL;
j->hooks_tail = NULL;
- if (j->user) { /* stale pointer */
+ if (j->user) { /* stale pointer */
char *user = consumestr(&serialized, &length);
if (!user)
goto clear_pointers;
@@ -1347,7 +1328,7 @@
goto clear_pointers;
}
- if (j->suppl_gid_list) { /* stale pointer */
+ if (j->suppl_gid_list) { /* stale pointer */
if (j->suppl_gid_count > NGROUPS_MAX) {
goto bad_gid_list;
}
@@ -1364,7 +1345,7 @@
memcpy(j->suppl_gid_list, gid_list_bytes, gid_list_size);
}
- if (j->chrootdir) { /* stale pointer */
+ if (j->chrootdir) { /* stale pointer */
char *chrootdir = consumestr(&serialized, &length);
if (!chrootdir)
goto bad_chrootdir;
@@ -1373,7 +1354,7 @@
goto bad_chrootdir;
}
- if (j->hostname) { /* stale pointer */
+ if (j->hostname) { /* stale pointer */
char *hostname = consumestr(&serialized, &length);
if (!hostname)
goto bad_hostname;
@@ -1382,7 +1363,7 @@
goto bad_hostname;
}
- if (j->alt_syscall_table) { /* stale pointer */
+ if (j->alt_syscall_table) { /* stale pointer */
char *alt_syscall_table = consumestr(&serialized, &length);
if (!alt_syscall_table)
goto bad_syscall_table;
@@ -1431,8 +1412,8 @@
type = consumestr(&serialized, &length);
if (!type)
goto bad_mounts;
- has_data =
- consumebytes(sizeof(*has_data), &serialized, &length);
+ has_data = consumebytes(sizeof(*has_data), &serialized,
+ &length);
if (!has_data)
goto bad_mounts;
if (*has_data) {
@@ -1459,23 +1440,8 @@
++j->cgroup_count;
}
- 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);
@@ -1491,12 +1457,12 @@
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->hostname)
+ free(j->hostname);
+bad_hostname:
if (j->suppl_gid_list)
free(j->suppl_gid_list);
bad_gid_list:
@@ -1509,7 +1475,6 @@
j->hostname = NULL;
j->alt_syscall_table = NULL;
j->cgroup_count = 0;
- j->seccomp_policy_path = NULL;
out:
return ret;
}
@@ -1520,56 +1485,39 @@
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,
- },
+ {
+ "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",
- },
+ { "ptmx", "pts/ptmx", },
+ { "fd", "/proc/self/fd", },
+ { "stdin", "fd/0", },
+ { "stdout", "fd/1", },
+ { "stderr", "fd/2", },
};
/*
@@ -1590,7 +1538,7 @@
static int mount_dev(char **dev_path_ret)
{
int ret;
- attribute_cleanup_fd int dev_fd = -1;
+ int dev_fd;
size_t i;
mode_t mask;
char *dev_path;
@@ -1604,8 +1552,8 @@
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");
+ ret = mount("minijail-devfs", dev_path, "tmpfs",
+ MS_NOEXEC | MS_NOSUID, "size=5M,mode=755");
if (ret) {
rmdir(dev_path);
return ret;
@@ -1615,7 +1563,7 @@
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);
+ dev_fd = open(dev_path, O_DIRECTORY|O_PATH|O_CLOEXEC);
if (dev_fd < 0) {
ret = 1;
goto done;
@@ -1625,7 +1573,7 @@
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));
+ makedev(ds->major, ds->minor));
if (ret)
goto done;
}
@@ -1644,7 +1592,8 @@
goto done;
/* Restore old mask. */
-done:
+ done:
+ close(dev_fd);
umask(mask);
if (ret)
@@ -1667,14 +1616,14 @@
if (umount2("/dev", MNT_DETACH))
goto done;
- if (asprintf(&dest, "%s/dev", j->chrootdir ?: "") < 0)
+ if (asprintf(&dest, "%s/dev", j->chrootdir ? : "") < 0)
goto done;
if (mount(dev_path, dest, NULL, MS_MOVE, NULL))
goto done;
ret = 0;
-done:
+ done:
free(dest);
mount_dev_cleanup(dev_path);
@@ -1803,8 +1752,7 @@
static int enter_pivot_root(const struct minijail *j)
{
- attribute_cleanup_fd int oldroot = -1;
- attribute_cleanup_fd int newroot = -1;
+ int oldroot, newroot;
run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_CHROOT);
@@ -1854,6 +1802,10 @@
/* Change back to the new root. */
if (fchdir(newroot))
return -errno;
+ if (close(oldroot))
+ return -errno;
+ if (close(newroot))
+ return -errno;
if (chroot("/"))
return -errno;
/* Set correct CWD for getcwd(3). */
@@ -1972,10 +1924,7 @@
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.
- */
+ /* See http://man7.org/linux/man-pages/man7/user_namespaces.7.html. */
warn("could not disable setgroups(2)");
} else
kill_child_and_die(
@@ -2025,8 +1974,7 @@
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) {
+ j->flags.set_suppl_gids > 1) {
die("can only do one of inherit, keep, or set supplementary "
"groups");
}
@@ -2191,7 +2139,7 @@
}
if (j->flags.seccomp_filter) {
- if (seccomp_is_logging_allowed(j)) {
+ if (j->flags.seccomp_filter_logging) {
warn("logging seccomp filter failures");
if (!seccomp_ret_log_available()) {
/*
@@ -2241,7 +2189,8 @@
static pid_t forward_pid = -1;
-static void forward_signal(int sig, siginfo_t *siginfo attribute_unused,
+static void forward_signal(int sig,
+ siginfo_t *siginfo attribute_unused,
void *void_context attribute_unused)
{
if (forward_pid != -1) {
@@ -2367,12 +2316,12 @@
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);
+ "MS_REC | temp->remount_mode, NULL) "
+ "failed", temp->mount_name);
temp = temp->next;
}
}
+
}
if (j->flags.ipc && unshare(CLONE_NEWIPC)) {
@@ -2383,8 +2332,7 @@
if (unshare(CLONE_NEWUTS))
pdie("unshare(CLONE_NEWUTS) failed");
- if (j->hostname &&
- sethostname(j->hostname, strlen(j->hostname)))
+ if (j->hostname && sethostname(j->hostname, strlen(j->hostname)))
pdie("sethostname(%s) failed", j->hostname);
}
@@ -2525,19 +2473,22 @@
{
size_t sz = 0;
size_t bytes = read(fd, &sz, sizeof(sz));
- attribute_cleanup_str char *buf = NULL;
+ char *buf;
int r;
if (sizeof(sz) != bytes)
return -EINVAL;
- if (sz > USHRT_MAX) /* arbitrary check */
+ if (sz > USHRT_MAX) /* arbitrary sanity check */
return -E2BIG;
buf = malloc(sz);
if (!buf)
return -ENOMEM;
bytes = read(fd, buf, sz);
- if (bytes != sz)
+ if (bytes != sz) {
+ free(buf);
return -EINVAL;
+ }
r = minijail_unmarshal(j, buf, sz);
+ free(buf);
return r;
}
@@ -2547,20 +2498,24 @@
if (!sz)
return -EINVAL;
- attribute_cleanup_str char *buf = malloc(sz);
+ char *buf = malloc(sz);
if (!buf)
return -ENOMEM;
int err = minijail_marshal(j, buf, sz);
if (err)
- return err;
+ goto error;
/* Sends [size][minijail]. */
err = write_exactly(fd, &sz, sizeof(sz));
if (err)
- return err;
+ goto error;
- return write_exactly(fd, buf, sz);
+ err = write_exactly(fd, buf, sz);
+
+error:
+ free(buf);
+ return err;
}
int API minijail_copy_jail(const struct minijail *from, struct minijail *out)
@@ -2569,15 +2524,18 @@
if (!sz)
return -EINVAL;
- attribute_cleanup_str char *buf = malloc(sz);
+ char *buf = malloc(sz);
if (!buf)
return -ENOMEM;
int err = minijail_marshal(from, buf, sz);
if (err)
- return err;
+ goto error;
- return minijail_unmarshal(out, buf, sz);
+ err = minijail_unmarshal(out, buf, sz);
+error:
+ free(buf);
+ return err;
}
static int setup_preload(const struct minijail *j attribute_unused,
@@ -2590,7 +2548,7 @@
const char *preload_path = j->preload_path ?: PRELOADPATH;
char *newenv = NULL;
int ret = 0;
- const char *oldenv = minijail_getenv(*child_env, kLdPreloadEnvVar);
+ const char *oldenv = getenv(kLdPreloadEnvVar);
if (!oldenv)
oldenv = "";
@@ -2607,19 +2565,6 @@
#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);
@@ -2678,11 +2623,11 @@
static_assert(FD_SETSIZE >= MAX_PRESERVED_FDS * 2 - 1,
"If true, ensure_no_fd_conflict will always find an unused fd.");
-/* If parent_fd will be used by a child fd, move it to an unused fd. */
-static int ensure_no_fd_conflict(const fd_set *child_fds, int child_fd,
- int *parent_fd)
+/* If p->parent_fd will be used by a child_fd, move it to an unused fd. */
+static int ensure_no_fd_conflict(const fd_set* child_fds,
+ struct preserved_fd* p)
{
- if (!FD_ISSET(*parent_fd, child_fds)) {
+ if (!FD_ISSET(p->parent_fd, child_fds)){
return 0;
}
@@ -2690,8 +2635,8 @@
* If no other parent_fd matches the child_fd then use it instead of a
* temporary.
*/
- int fd = child_fd;
- if (fd == -1 || fd_is_open(fd)) {
+ int fd = p->child_fd;
+ if (fd_is_open(fd)) {
fd = FD_SETSIZE - 1;
while (FD_ISSET(fd, child_fds) || fd_is_open(fd)) {
--fd;
@@ -2701,45 +2646,61 @@
}
}
- int ret = dup2(*parent_fd, fd);
+ int ret = dup2(p->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);
+ warn("mapped fd overlap: moving %d to %d", p->parent_fd, fd);
if (ret == -1) {
return -1;
}
- *parent_fd = fd;
+ p->parent_fd = fd;
return 0;
}
-/*
- * Populate child_fds_out with the set of file descriptors that will be replaced
- * by redirect_fds().
- *
- * NOTE: This creates temporaries for parent file descriptors that would
- * otherwise be overwritten during redirect_fds().
- */
-static int get_child_fds(struct minijail *j, fd_set *child_fds_out)
+static int redirect_fds(struct minijail *j)
{
+ fd_set child_fds;
+ FD_ZERO(&child_fds);
+
/* 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 (FD_ISSET(child_fd, child_fds_out)) {
+ if (FD_ISSET(child_fd, &child_fds)) {
die("fd %d is mapped more than once", child_fd);
}
- int *parent_fd = &j->preserved_fds[i].parent_fd;
- if (ensure_no_fd_conflict(child_fds_out, child_fd, parent_fd) ==
- -1) {
+ if (ensure_no_fd_conflict(&child_fds,
+ &j->preserved_fds[i]) == -1) {
return -1;
}
- FD_SET(child_fd, child_fds_out);
+ FD_SET(child_fd, &child_fds);
+ }
+
+ for (size_t i = 0; i < j->preserved_fd_count; i++) {
+ if (j->preserved_fds[i].parent_fd ==
+ j->preserved_fds[i].child_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 (!FD_ISSET(parent_fd, &child_fds)) {
+ close(parent_fd);
+ }
}
return 0;
}
@@ -2757,80 +2718,6 @@
char **child_env;
};
-/*
- * Move pipe_fds if they conflict with a child_fd.
- */
-static int avoid_pipe_conflicts(struct minijail_run_state *state,
- fd_set *child_fds_out)
-{
- 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(child_fds_out, -1, &pipe_fds[i][0]) ==
- -1) {
- return -1;
- }
- if (pipe_fds[i][1] != -1 &&
- ensure_no_fd_conflict(child_fds_out, -1, &pipe_fds[i][1]) ==
- -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, fd_set *child_fds)
-{
- 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 (!FD_ISSET(parent_fd, child_fds)) {
- close(parent_fd);
- }
- }
- return 0;
-}
-
static void minijail_free_run_state(struct minijail_run_state *state)
{
state->child_pid = -1;
@@ -2889,21 +2776,13 @@
if (setsid() < 0) {
pdie("setsid() failed");
}
-
- if (isatty(STDIN_FILENO)) {
- if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) != 0) {
- pwarn("failed to set controlling terminal");
- }
- }
}
}
/*
* 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.
+ * filename - The program to exec in the child. 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.
@@ -2916,7 +2795,6 @@
*/
struct minijail_run_config {
const char *filename;
- int elf_fd;
char *const *argv;
char *const *envp;
int use_preload;
@@ -2936,7 +2814,6 @@
{
struct minijail_run_config config = {
.filename = filename,
- .elf_fd = -1,
.argv = argv,
.envp = NULL,
.use_preload = true,
@@ -2945,26 +2822,11 @@
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,
@@ -2979,7 +2841,6 @@
{
struct minijail_run_config config = {
.filename = filename,
- .elf_fd = -1,
.argv = argv,
.envp = NULL,
.use_preload = true,
@@ -2995,7 +2856,6 @@
{
struct minijail_run_config config = {
.filename = filename,
- .elf_fd = -1,
.argv = argv,
.envp = NULL,
.use_preload = true,
@@ -3015,27 +2875,6 @@
{
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,
@@ -3053,7 +2892,6 @@
{
struct minijail_run_config config = {
.filename = filename,
- .elf_fd = -1,
.argv = argv,
.envp = NULL,
.use_preload = false,
@@ -3064,13 +2902,14 @@
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,
+ 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,
@@ -3092,7 +2931,6 @@
{
struct minijail_run_config config = {
.filename = filename,
- .elf_fd = -1,
.argv = argv,
.envp = envp,
.use_preload = false,
@@ -3107,9 +2945,7 @@
pid_t API minijail_fork(struct minijail *j)
{
- struct minijail_run_config config = {
- .elf_fd = -1,
- };
+ struct minijail_run_config config = {};
return minijail_run_config_internal(j, &config);
}
@@ -3128,25 +2964,6 @@
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");
- }
-
- /*
- * 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");
@@ -3157,6 +2974,10 @@
* Before we fork(2) and execve(2) the child process, we need
* to open a pipe(2) to send the minijail configuration over.
*/
+ state_out->child_env =
+ minijail_copy_env(config->envp ? config->envp : environ);
+ if (!state_out->child_env)
+ return ENOMEM;
if (setup_preload(j, &state_out->child_env) ||
setup_pipe(&state_out->child_env, state_out->pipe_fds))
return -EFAULT;
@@ -3249,9 +3070,8 @@
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 with EPERM; "
+ "is this process missing CAP_SYS_ADMIN?");
pdie("clone(CLONE_NEWPID | ...) failed");
}
} else {
@@ -3317,15 +3137,13 @@
/* Accept any pending SIGPIPE. */
while (true) {
const struct timespec zero_time = {0, 0};
- const int sig =
- sigtimedwait(&to_block, NULL, &zero_time);
+ 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);
+ die("unexpected signal %d", sig);
}
}
@@ -3368,7 +3186,7 @@
}
if (j->flags.close_open_fds) {
- const size_t kMaxInheritableFdsSize = 11 + MAX_PRESERVED_FDS;
+ const size_t kMaxInheritableFdsSize = 10 + MAX_PRESERVED_FDS;
int inheritable_fds[kMaxInheritableFdsSize];
size_t size = 0;
@@ -3405,29 +3223,11 @@
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. */
- fd_set child_fds;
- FD_ZERO(&child_fds);
- if (get_child_fds(j, &child_fds))
- die("failed to set up fd redirections");
-
- if (avoid_pipe_conflicts(state_out, &child_fds))
- 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(&child_fds, -1, &elf_fd))
- die("failed to redirect elf_fd");
-
- if (redirect_fds(j, &child_fds))
+ if (redirect_fds(j))
die("failed to set up fd redirections");
if (sync_child)
@@ -3482,7 +3282,7 @@
* Best effort. Don't bother checking the return value.
*/
prctl(PR_SET_NAME, "minijail-init");
- init(child_pid); /* Never returns. */
+ init(child_pid); /* Never returns. */
}
state_out->child_pid = child_pid;
}
@@ -3517,16 +3317,10 @@
*/
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);
- }
+ execve(config->filename, config->argv, child_env);
- ret = (errno == ENOENT ? MINIJAIL_ERR_NO_COMMAND
- : MINIJAIL_ERR_NO_ACCESS);
+ ret = (errno == ENOENT ? MINIJAIL_ERR_NO_COMMAND : MINIJAIL_ERR_NO_ACCESS);
+ pwarn("execve(%s) failed", config->filename);
_exit(ret);
}
@@ -3591,38 +3385,32 @@
if (!WIFEXITED(st)) {
int error_status = st;
- if (!WIFSIGNALED(st)) {
- return error_status;
- }
-
- int signum = WTERMSIG(st);
- /*
- * We return MINIJAIL_ERR_JAIL 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_JAIL;
- } else {
+ if (WIFSIGNALED(st)) {
+ int signum = WTERMSIG(st);
if (signum != expected_signal) {
warn("child process %d received signal %d",
j->initpid, signum);
}
- error_status = MINIJAIL_ERR_SIG_BASE + signum;
+ /*
+ * We return MINIJAIL_ERR_JAIL 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) {
+ error_status = MINIJAIL_ERR_JAIL;
+ } else {
+ 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);
+ info("child process %d exited with status %d",
+ j->initpid, exit_status);
return exit_status;
}
@@ -3679,8 +3467,6 @@
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);
}
diff --git a/libminijail.h b/libminijail.h
index d2dce7a..cfd42d2 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -344,13 +344,6 @@
/*
* 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[]);
-
-/*
- * 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().
*/
@@ -411,23 +404,6 @@
int *pstdout_fd, int *pstderr_fd);
/*
- * 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);
-
-/*
* 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
diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc
index 868b7d7..78e3cfb 100644
--- a/libminijail_unittest.cc
+++ b/libminijail_unittest.cc
@@ -9,7 +9,6 @@
#include <dirent.h>
#include <fcntl.h>
-#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -474,94 +473,6 @@
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.
@@ -625,56 +536,6 @@
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.
- if (running_with_asan())
- GTEST_SKIP();
-
- ScopedMinijail j(minijail_new());
- minijail_set_preload_path(j.get(), kPreloadPath);
-
- char *argv[4];
- argv[0] = const_cast<char*>(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<char*>(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.
@@ -740,56 +601,6 @@
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<char*>(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;
@@ -1281,7 +1092,7 @@
argv[0] = const_cast<char*>(kShellPath);
argv[1] = "-c";
argv[2] = "grep -E 'shared:|master:|propagate_from:|unbindable:' "
- "/proc/self/mountinfo";
+ "/proc/self/mountinfo";
argv[3] = NULL;
mj_run_ret = minijail_run_pid_pipes_no_preload(
j, argv[0], argv, &pid, NULL, &child_stdout, NULL);
diff --git a/libminijailpreload.c b/libminijailpreload.c
index b5a3c75..a98b736 100644
--- a/libminijailpreload.c
+++ b/libminijailpreload.c
@@ -11,7 +11,6 @@
#include "libminijail.h"
#include "libminijail-private.h"
-#include "util.h"
#include <dlfcn.h>
#include <stdio.h>
@@ -24,24 +23,18 @@
static int (*real_main) (int, char **, char **);
static void *libc_handle;
-static void truncate_preload_env(char **envp, const char *name)
+static void die(const char *failed)
{
- 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);
- }
- }
+ syslog(LOG_ERR, "libminijail: %s", failed);
+ abort();
+}
+
+static void unset_in_env(char **envp, const char *name)
+{
+ int i;
+ for (i = 0; envp[i]; i++)
+ if (!strncmp(envp[i], name, strlen(name)))
+ envp[i][0] = '\0';
}
/** @brief Fake main(), spliced in before the real call to main() by
@@ -78,10 +71,12 @@
die("preload: failed to parse minijail from parent");
close(fd);
- minijail_unsetenv(envp, kFdEnvVar);
-
- truncate_preload_env(envp, kLdPreloadEnvVar);
-
+ unset_in_env(envp, kFdEnvVar);
+ /* TODO(ellyjones): this trashes existing preloads, so one can't do:
+ * LD_PRELOAD="/tmp/test.so libminijailpreload.so" prog; the
+ * descendants of prog will have no LD_PRELOAD set at all.
+ */
+ unset_in_env(envp, kLdPreloadEnvVar);
/* Strip out flags meant for the parent. */
minijail_preenter(j);
minijail_enter(j);
diff --git a/linux-x86/libsyscalls.gen.c b/linux-x86/libsyscalls.gen.c
index 1403380..e0b2e53 100644
--- a/linux-x86/libsyscalls.gen.c
+++ b/linux-x86/libsyscalls.gen.c
@@ -1038,27 +1038,6 @@
#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
{ NULL, -1 },
};
diff --git a/minijail0.1 b/minijail0.1
index a53ec6f..7dc6f74 100644
--- a/minijail0.1
+++ b/minijail0.1
@@ -12,7 +12,7 @@
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 <src>[,[dest][,<writeable>]]\fR, \fB--bind-mount=<src>[,[dest][,<writeable>]]\fR
+\fB-b <src>[,[dest][,<writeable>]]
Bind-mount \fIsrc\fR into the chroot directory at \fIdest\fR, optionally writeable.
The \fIsrc\fR path must be an absolute path.
@@ -116,7 +116,7 @@
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 <src>,<dest>,<type>[,<flags>[,<data>]]\fR, \fB--mount=<src>,<dest>,<type>[,<flags>[,<data>]]\fR
+\fB-k <src>,<dest>,<type>[,<flags>[,<data>]]\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.
@@ -257,7 +257,7 @@
\fB-U\fR
Enter a new user namespace (implies \fB-p\fR).
.TP
-\fB-v\fR, \fB--ns-mount\fR
+\fB-v\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
@@ -289,16 +289,6 @@
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 <NAME=value>\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=<system>\fR
Use \fIsystem\fR as the logging system. \fIsystem\fR must be one of
\fBauto\fR (the default), \fBsyslog\fR, or \fBstderr\fR.
@@ -337,11 +327,6 @@
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--config <file path>\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
diff --git a/minijail0.5 b/minijail0.5
index 3e4f114..65d1626 100644
--- a/minijail0.5
+++ b/minijail0.5
@@ -157,31 +157,6 @@
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.
-
-It supports the following syntax:
- \fB% minijail-config-file v0\fR
- \fB<option>\fR=\fB<argument>\fR
- \fB<no-argument-option>\fR
- \fB<empty line>\fR
- \fB# any single line comment\fR
-
-Long lines may be broken up using \\ at the end.
-
-The special directive "% minijail-config-file v0" must occupy the first line.
-"v0" also declares the version of the config file format.
-
-Keys contain only alphabetic characters and '-'. Values can be any non-empty
-string. Leading and trailing whitespaces around keys and
-values are permitted but will be stripped before processing.
-
-Currently all long options are supported such as
-\fBmount\fR, \fBbind-mount\fR. For a option that has no argument, the option
-will occupy a single line, without '=' and value. Otherwise, any string that
-is given after the '=' is interpreted as the argument.
-
.SH AUTHOR
The Chromium OS Authors <[email protected]>
.SH COPYRIGHT
diff --git a/minijail0.c b/minijail0.c
index 9b1fcf3..f216d6e 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -4,7 +4,6 @@
*/
#include <dlfcn.h>
-#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -16,17 +15,15 @@
#include "minijail0_cli.h"
#include "util.h"
-int main(int argc, char *argv[], char *environ[])
+int main(int argc, char *argv[])
{
struct minijail *j = minijail_new();
const char *dl_mesg = NULL;
const char *preload_path = PRELOADPATH;
int exit_immediately = 0;
ElfType elftype = ELFERROR;
- char **envp = NULL;
- int consumed = parse_args(j, argc, argv, environ,
- &exit_immediately, &elftype,
- &preload_path, &envp);
+ int consumed = parse_args(j, argc, argv, &exit_immediately, &elftype,
+ &preload_path);
argc -= consumed;
argv += consumed;
@@ -40,8 +37,10 @@
* the process is already a process group leader.
*/
if (setpgid(0 /* use calling PID */, 0 /* make PGID = PID */)) {
- if (errno != EPERM)
- err(1, "setpgid(0, 0) failed");
+ if (errno != EPERM) {
+ fprintf(stderr, "setpgid(0, 0) failed\n");
+ exit(1);
+ }
}
if (elftype == ELFSTATIC) {
@@ -59,17 +58,16 @@
/* Check that we can dlopen() libminijailpreload.so. */
if (!dlopen(preload_path, RTLD_LAZY | RTLD_LOCAL)) {
dl_mesg = dlerror();
- errx(1, "dlopen(): %s", dl_mesg);
+ fprintf(stderr, "dlopen(): %s\n", dl_mesg);
return 1;
}
minijail_set_preload_path(j, preload_path);
- if (envp) {
- minijail_run_env(j, argv[0], argv, envp);
- } else {
- minijail_run(j, argv[0], argv);
- }
+ minijail_run(j, argv[0], argv);
} else {
- errx(1, "Target program '%s' is not a valid ELF file", argv[0]);
+ fprintf(stderr,
+ "Target program '%s' is not a valid ELF file.\n",
+ argv[0]);
+ return 1;
}
if (exit_immediately)
diff --git a/minijail0_cli.c b/minijail0_cli.c
index e366846..3461579 100644
--- a/minijail0_cli.c
+++ b/minijail0_cli.c
@@ -4,9 +4,7 @@
*/
#include <dlfcn.h>
-#include <err.h>
#include <errno.h>
-#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <stdbool.h>
@@ -15,9 +13,7 @@
#include <string.h>
#include <sys/capability.h>
#include <sys/mount.h>
-#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/vfs.h>
#include <unistd.h>
#include <linux/filter.h>
@@ -25,7 +21,6 @@
#include "libminijail.h"
#include "libsyscalls.h"
-#include "config_parser.h"
#include "elfparse.h"
#include "minijail0_cli.h"
#include "system.h"
@@ -41,16 +36,20 @@
static void *xmalloc(size_t size)
{
void *ret = malloc(size);
- if (!ret)
- err(1, "malloc() failed");
+ if (!ret) {
+ perror("malloc() failed");
+ exit(1);
+ }
return ret;
}
static char *xstrdup(const char *s)
{
char *ret = strdup(s);
- if (!ret)
- err(1, "strdup() failed");
+ if (!ret) {
+ perror("strdup() failed");
+ exit(1);
+ }
return ret;
}
@@ -58,7 +57,7 @@
gid_t *out_gid)
{
char *end = NULL;
- uid_t uid = strtoul(arg, &end, 10);
+ int uid = strtod(arg, &end);
if (!*end && *arg) {
*out_uid = uid;
minijail_change_uid(j, uid);
@@ -67,21 +66,22 @@
int ret = lookup_user(arg, out_uid, out_gid);
if (ret) {
- errno = -ret;
- err(1, "Bad user '%s'", arg);
+ fprintf(stderr, "Bad user '%s': %s\n", arg, strerror(-ret));
+ exit(1);
}
ret = minijail_change_user(j, arg);
if (ret) {
- errno = -ret;
- err(1, "minijail_change_user('%s') failed", arg);
+ fprintf(stderr, "minijail_change_user('%s') failed: %s\n", arg,
+ strerror(-ret));
+ exit(1);
}
}
static void set_group(struct minijail *j, const char *arg, gid_t *out_gid)
{
char *end = NULL;
- gid_t gid = strtoul(arg, &end, 10);
+ int gid = strtod(arg, &end);
if (!*end && *arg) {
*out_gid = gid;
minijail_change_gid(j, gid);
@@ -90,8 +90,8 @@
int ret = lookup_group(arg, out_gid);
if (ret) {
- errno = -ret;
- err(1, "Bad group '%s'", arg);
+ fprintf(stderr, "Bad group '%s': %s\n", arg, strerror(-ret));
+ exit(1);
}
minijail_change_gid(j, *out_gid);
@@ -102,30 +102,33 @@
* to build the supplementary gids array.
*/
static void suppl_group_add(size_t *suppl_gids_count, gid_t **suppl_gids,
- char *arg)
-{
+ char *arg) {
char *end = NULL;
- gid_t gid = strtoul(arg, &end, 10);
+ int groupid = strtod(arg, &end);
+ gid_t gid;
int ret;
if (!*end && *arg) {
/* A gid number has been specified, proceed. */
+ gid = groupid;
} else if ((ret = lookup_group(arg, &gid))) {
/*
* A group name has been specified,
* but doesn't exist: we bail out.
*/
- errno = -ret;
- err(1, "Bad group '%s'", arg);
+ fprintf(stderr, "Bad group '%s': %s\n", arg, strerror(-ret));
+ exit(1);
}
/*
* From here, gid is guaranteed to be set and valid,
* we add it to our supplementary gids array.
*/
- *suppl_gids =
- realloc(*suppl_gids, sizeof(gid_t) * ++(*suppl_gids_count));
- if (!suppl_gids)
- err(1, "failed to allocate memory");
+ *suppl_gids = realloc(*suppl_gids,
+ sizeof(gid_t) * ++(*suppl_gids_count));
+ if (!suppl_gids) {
+ fprintf(stderr, "failed to allocate memory.\n");
+ exit(1);
+ }
(*suppl_gids)[*suppl_gids_count - 1] = gid;
}
@@ -135,8 +138,10 @@
uint64_t securebits_skip_mask;
char *end = NULL;
securebits_skip_mask = strtoull(arg, &end, 16);
- if (*end)
- errx(1, "Invalid securebit mask: '%s'", arg);
+ if (*end) {
+ fprintf(stderr, "Invalid securebit mask: '%s'\n", arg);
+ exit(1);
+ }
minijail_skip_setting_securebits(j, securebits_skip_mask);
}
@@ -162,10 +167,11 @@
*/
continue;
}
- err(1,
- "Could not get the value of the %d-th "
- "capability",
- i);
+ fprintf(stderr,
+ "Could not get the value of "
+ "the %d-th capability: %m\n",
+ i);
+ exit(1);
}
if (cap_value == CAP_SET)
caps |= (one << i);
@@ -174,8 +180,10 @@
} else {
char *end = NULL;
caps = strtoull(arg, &end, 16);
- if (*end)
- errx(1, "Invalid cap set: '%s'", arg);
+ if (*end) {
+ fprintf(stderr, "Invalid cap set: '%s'\n", arg);
+ exit(1);
+ }
}
minijail_use_caps(j, caps);
@@ -186,8 +194,10 @@
char *src = tokenize(&arg, ",");
char *dest = tokenize(&arg, ",");
char *flags = tokenize(&arg, ",");
- if (!src || src[0] == '\0' || arg != NULL)
- errx(1, "Bad binding: %s %s", src, dest);
+ if (!src || src[0] == '\0' || arg != NULL) {
+ fprintf(stderr, "Bad binding: %s %s\n", src, dest);
+ exit(1);
+ }
if (dest == NULL || dest[0] == '\0')
dest = src;
int writable;
@@ -195,10 +205,14 @@
writable = 0;
else if (!strcmp(flags, "1"))
writable = 1;
- else
- errx(1, "Bad value for <writable>: %s", flags);
- if (minijail_bind(j, src, dest, writable))
- errx(1, "minijail_bind failed");
+ else {
+ fprintf(stderr, "Bad value for <writable>: %s\n", flags);
+ exit(1);
+ }
+ if (minijail_bind(j, src, dest, writable)) {
+ fprintf(stderr, "minijail_bind failed.\n");
+ exit(1);
+ }
}
static void add_rlimit(struct minijail *j, char *arg)
@@ -207,9 +221,10 @@
char *cur = tokenize(&arg, ",");
char *max = tokenize(&arg, ",");
char *end;
- if (!type || type[0] == '\0' || !cur || cur[0] == '\0' || !max ||
- max[0] == '\0' || arg != NULL) {
- errx(1, "Bad rlimit '%s'", arg);
+ if (!type || type[0] == '\0' || !cur || cur[0] == '\0' ||
+ !max || max[0] == '\0' || arg != NULL) {
+ fprintf(stderr, "Bad rlimit '%s'.\n", arg);
+ exit(1);
}
rlim_t cur_rlim;
rlim_t max_rlim;
@@ -218,25 +233,34 @@
} else {
end = NULL;
cur_rlim = strtoul(cur, &end, 0);
- if (*end)
- errx(1, "Bad soft limit: '%s'", cur);
+ if (*end) {
+ fprintf(stderr, "Bad soft limit: '%s'.\n", cur);
+ exit(1);
+ }
}
if (!strcmp(max, "unlimited")) {
max_rlim = RLIM_INFINITY;
} else {
end = NULL;
max_rlim = strtoul(max, &end, 0);
- if (*end)
- errx(1, "Bad hard limit: '%s'", max);
+ if (*end) {
+ fprintf(stderr, "Bad hard limit: '%s'.\n", max);
+ exit(1);
+ }
}
end = NULL;
int resource = parse_single_constant(type, &end);
- if (type == end)
- errx(1, "Bad rlimit: '%s'", type);
+ if (type == end) {
+ fprintf(stderr, "Bad rlimit: '%s'.\n", type);
+ exit(1);
+ }
- if (minijail_rlimit(j, resource, cur_rlim, max_rlim))
- errx(1, "minijail_rlimit '%s,%s,%s' failed", type, cur, max);
+ if (minijail_rlimit(j, resource, cur_rlim, max_rlim)) {
+ fprintf(stderr, "minijail_rlimit '%s,%s,%s' failed.\n", type,
+ cur, max);
+ exit(1);
+ }
}
static void add_mount(struct minijail *j, char *arg)
@@ -247,9 +271,10 @@
char *flags = tokenize(&arg, ",");
char *data = tokenize(&arg, ",");
char *end;
- if (!src || src[0] == '\0' || !dest || dest[0] == '\0' || !type ||
- type[0] == '\0') {
- errx(1, "Bad mount: %s %s %s", src, dest, type);
+ if (!src || src[0] == '\0' || !dest || dest[0] == '\0' ||
+ !type || type[0] == '\0') {
+ fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type);
+ exit(1);
}
/*
@@ -272,12 +297,17 @@
} else {
end = NULL;
mountflags = parse_constant(flags, &end);
- if (flags == end)
- errx(1, "Bad mount flags: %s", flags);
+ if (flags == end) {
+ fprintf(stderr, "Bad mount flags: %s\n", flags);
+ exit(1);
+ }
}
- if (minijail_mount_with_data(j, src, dest, type, mountflags, data))
- errx(1, "minijail_mount failed");
+ if (minijail_mount_with_data(j, src, dest, type,
+ mountflags, data)) {
+ fprintf(stderr, "minijail_mount failed.\n");
+ exit(1);
+ }
}
static char *build_idmap(id_t id, id_t lowerid)
@@ -287,7 +317,8 @@
ret = snprintf(idmap, IDMAP_LEN, "%d %d 1", id, lowerid);
if (ret < 0 || (size_t)ret >= IDMAP_LEN) {
free(idmap);
- errx(1, "Could not build id map");
+ fprintf(stderr, "Could not build id map.\n");
+ exit(1);
}
return idmap;
}
@@ -301,14 +332,20 @@
return 0;
caps = cap_get_proc();
- if (!caps)
- err(1, "Could not get process' capabilities");
+ if (!caps) {
+ fprintf(stderr, "Could not get process' capabilities: %m\n");
+ exit(1);
+ }
- if (cap_get_flag(caps, CAP_SETGID, CAP_EFFECTIVE, &cap_value))
- err(1, "Could not get the value of CAP_SETGID");
+ if (cap_get_flag(caps, CAP_SETGID, CAP_EFFECTIVE, &cap_value)) {
+ fprintf(stderr, "Could not get the value of CAP_SETGID: %m\n");
+ exit(1);
+ }
- if (cap_free(caps))
- err(1, "Could not free capabilities");
+ if (cap_free(caps)) {
+ fprintf(stderr, "Could not free capabilities: %m\n");
+ exit(1);
+ }
return cap_value == CAP_SET;
}
@@ -329,8 +366,10 @@
*/
uidmap = build_idmap(uid, getuid());
}
- if (0 != minijail_uidmap(j, uidmap))
- errx(1, "Could not set uid map");
+ if (0 != minijail_uidmap(j, uidmap)) {
+ fprintf(stderr, "Could not set uid map.\n");
+ exit(1);
+ }
free(uidmap);
}
if (set_gidmap) {
@@ -354,8 +393,10 @@
*/
minijail_namespace_user_disable_setgroups(j);
}
- if (0 != minijail_gidmap(j, gidmap))
- errx(1, "Could not set gid map");
+ if (0 != minijail_gidmap(j, gidmap)) {
+ fprintf(stderr, "Could not set gid map.\n");
+ exit(1);
+ }
free(gidmap);
}
}
@@ -363,20 +404,30 @@
static void use_chroot(struct minijail *j, const char *path, int *chroot,
int pivot_root)
{
- if (pivot_root)
- errx(1, "Could not set chroot because -P was specified");
- if (minijail_enter_chroot(j, path))
- errx(1, "Could not set chroot");
+ if (pivot_root) {
+ fprintf(stderr, "Could not set chroot because "
+ "'-P' was specified.\n");
+ exit(1);
+ }
+ if (minijail_enter_chroot(j, path)) {
+ fprintf(stderr, "Could not set chroot.\n");
+ exit(1);
+ }
*chroot = 1;
}
static void use_pivot_root(struct minijail *j, const char *path,
int *pivot_root, int chroot)
{
- if (chroot)
- errx(1, "Could not set pivot_root because -C was specified");
- if (minijail_enter_pivot_root(j, path))
- errx(1, "Could not set pivot_root");
+ if (chroot) {
+ fprintf(stderr, "Could not set pivot_root because "
+ "'-C' was specified.\n");
+ exit(1);
+ }
+ if (minijail_enter_pivot_root(j, path)) {
+ fprintf(stderr, "Could not set pivot_root.\n");
+ exit(1);
+ }
minijail_namespace_vfs(j);
*pivot_root = 1;
}
@@ -389,13 +440,19 @@
if (!strcmp(profile, "minimalistic-mountns") ||
!strcmp(profile, "minimalistic-mountns-nodev")) {
minijail_namespace_vfs(j);
- if (minijail_bind(j, "/", "/", 0))
- errx(1, "minijail_bind(/) failed");
- if (minijail_bind(j, "/proc", "/proc", 0))
- errx(1, "minijail_bind(/proc) failed");
+ if (minijail_bind(j, "/", "/", 0)) {
+ fprintf(stderr, "minijail_bind(/) failed.\n");
+ exit(1);
+ }
+ if (minijail_bind(j, "/proc", "/proc", 0)) {
+ fprintf(stderr, "minijail_bind(/proc) failed.\n");
+ exit(1);
+ }
if (!strcmp(profile, "minimalistic-mountns")) {
- if (minijail_bind(j, "/dev/log", "/dev/log", 0))
- errx(1, "minijail_bind(/dev/log) failed");
+ if (minijail_bind(j, "/dev/log", "/dev/log", 0)) {
+ fprintf(stderr, "minijail_bind(/dev/log) failed.\n");
+ exit(1);
+ }
minijail_mount_dev(j);
}
if (!*tmp_size) {
@@ -404,8 +461,10 @@
}
minijail_remount_proc_readonly(j);
use_pivot_root(j, DEFAULT_PIVOT_ROOT, pivot_root, chroot);
- } else
- errx(1, "Unrecognized profile name '%s'", profile);
+ } else {
+ fprintf(stderr, "Unrecognized profile name '%s'\n", profile);
+ exit(1);
+ }
}
static void set_remount_mode(struct minijail *j, const char *mode)
@@ -419,25 +478,35 @@
msmode = MS_SLAVE;
else if (!strcmp(mode, "unbindable"))
msmode = MS_UNBINDABLE;
- else
- errx(1, "Unknown remount mode: '%s'", mode);
+ else {
+ fprintf(stderr, "Unknown remount mode: '%s'\n", mode);
+ exit(1);
+ }
minijail_remount_mode(j, msmode);
}
static void read_seccomp_filter(const char *filter_path,
struct sock_fprog *filter)
{
- attribute_cleanup_fp FILE *f = fopen(filter_path, "re");
- if (!f)
- err(1, "failed to open %s", filter_path);
+ FILE *f = fopen(filter_path, "re");
+ if (!f) {
+ fprintf(stderr, "failed to open %s: %m", filter_path);
+ exit(1);
+ }
off_t filter_size = 0;
- if (fseeko(f, 0, SEEK_END) == -1 || (filter_size = ftello(f)) == -1)
- err(1, "failed to get file size of %s", filter_path);
+ if (fseeko(f, 0, SEEK_END) == -1 || (filter_size = ftello(f)) == -1) {
+ fclose(f);
+ fprintf(stderr, "failed to get file size of %s: %m",
+ filter_path);
+ exit(1);
+ }
if (filter_size % sizeof(struct sock_filter) != 0) {
- errx(1,
- "filter size (%" PRId64 ") of %s is not a multiple of"
- " %zu",
- filter_size, filter_path, sizeof(struct sock_filter));
+ fclose(f);
+ fprintf(stderr,
+ "filter size (%" PRId64
+ ") of %s is not a multiple of %zu: %m",
+ filter_size, filter_path, sizeof(struct sock_filter));
+ exit(1);
}
rewind(f);
@@ -445,195 +514,116 @@
filter->filter = xmalloc(filter_size);
if (fread(filter->filter, sizeof(struct sock_filter), filter->len, f) !=
filter->len) {
- err(1, "failed read %s", filter_path);
+ fclose(f);
+ fprintf(stderr, "failed read %s: %m", filter_path);
+ exit(1);
}
+ fclose(f);
}
-/*
- * Long options use values starting at 0x100 so that they're out of range of
- * bytes which is how command line options are processed. Practically speaking,
- * we could get by with the (7-bit) ASCII range, but UTF-8 codepoints would be a
- * bit confusing, and honestly there's no reason to "optimize" here.
- *
- * The long enum values are internal to this file and can freely change at any
- * time without breaking anything. Please keep alphabetically ordered.
- */
-enum {
- /* Everything after this point only have long options. */
- LONG_OPTION_BASE = 0x100,
- OPT_ADD_SUPPL_GROUP,
- OPT_ALLOW_SPECULATIVE_EXECUTION,
- OPT_AMBIENT,
- OPT_CONFIG,
- OPT_ENV_ADD,
- OPT_ENV_RESET,
- OPT_LOGGING,
- OPT_PRELOAD_LIBRARY,
- OPT_PROFILE,
- OPT_SECCOMP_BPF_BINARY,
- OPT_UTS,
-};
-
-/*
- * NB: When adding new options, prefer long-option only. Add a short option
- * only if its meaning is intuitive/obvious at a glance.
- *
- * Keep this sorted.
- */
-static const char optstring[] =
- "+a:b:c:de::f:g:hik:lm::nprst::u:vwyzB:C:GHIK::LM::NP:R:S:T:UV:Y";
-
-static const struct option long_options[] = {
- {"help", no_argument, 0, 'h'},
- {"mount-dev", no_argument, 0, 'd'},
- {"ambient", no_argument, 0, OPT_AMBIENT},
- {"uts", optional_argument, 0, OPT_UTS},
- {"logging", required_argument, 0, OPT_LOGGING},
- {"profile", required_argument, 0, OPT_PROFILE},
- {"preload-library", required_argument, 0, OPT_PRELOAD_LIBRARY},
- {"seccomp-bpf-binary", required_argument, 0, OPT_SECCOMP_BPF_BINARY},
- {"add-suppl-group", required_argument, 0, OPT_ADD_SUPPL_GROUP},
- {"allow-speculative-execution", no_argument, 0,
- OPT_ALLOW_SPECULATIVE_EXECUTION},
- {"config", required_argument, 0, OPT_CONFIG},
- {"env-add", required_argument, 0, OPT_ENV_ADD},
- {"env-reset", no_argument, 0, OPT_ENV_RESET},
- {"mount", required_argument, 0, 'k'},
- {"bind-mount", required_argument, 0, 'b'},
- {"ns-mount", no_argument, 0, 'v'},
- {0, 0, 0, 0},
-};
-
-/*
- * Pull the usage string out into the top-level to help with long-lines. We
- * want the output to be wrapped at 80 cols when it's shown to the user in the
- * terminal, but we don't want the source wrapped to 80 cols because that will
- * effectively make terminal output wrap to much lower levels (like <70).
- */
-/* clang-format off */
-static const char help_text[] =
-"Account (user/group) options:\n"
-" -u <user> Change uid to <user>.\n"
-" -g <group> Change gid to <group>.\n"
-" -G Inherit supplementary groups from new uid.\n"
-" Incompatible with -y or --add-suppl-group.\n"
-" -y Keep original uid's supplementary groups.\n"
-" Incompatible with -G or --add-suppl-group.\n"
-" --add-suppl-group <group>\n"
-" Add <group> to the proccess' supplementary groups.\n"
-" Can be specified multiple times to add several groups.\n"
-" Incompatible with -y or -G.\n"
-"\n"
-"Mount/path options:\n"
-" -b <src[,dst[,writable]]>, --bind-mount <...>\n"
-" Bind <src> to <dst>.\n"
-" -k <src,dst,fstype[,flags[,data]]>, --mount <...>\n"
-" Mount <src> at <dst>. <flags> and <data> can be specified as\n"
-" in mount(2). Multiple instances allowed.\n"
-" -K Do not change share mode of any existing mounts.\n"
-" -K<mode> Mark all existing mounts as <mode> instead of MS_PRIVATE.\n"
-" -r Remount /proc read-only (implies -v).\n"
-" -d, --mount-dev\n"
-" Create a new /dev with a minimal set of device nodes\n"
-" (implies -v). See minijail0(1) for exact list.\n"
-" -t[size] Mount tmpfs at /tmp (implies -v).\n"
-" Optional argument specifies size (default \"64M\").\n"
-" -C <dir> chroot(2) to <dir>. Incompatible with -P.\n"
-" -P <dir> pivot_root(2) to <dir> (implies -v). Incompatible with -C.\n"
-"\n"
-"Namespace options:\n"
-" -N Enter a new cgroup namespace.\n"
-" -l Enter new IPC namespace.\n"
-" -v, --ns-mount\n"
-" Enter new mount namespace.\n"
-" -V <file> Enter specified mount namespace.\n"
-" -e[file] Enter new network namespace, or existing |file| if provided.\n"
-" -p Enter new pid namespace (implies -vr).\n"
-" -I Run as init (pid 1) inside a new pid namespace (implies -p).\n"
-" -U Enter new user namespace (implies -p).\n"
-" -m[<uid> <loweruid> <count>]\n"
-" Set the uid map of a user namespace (implies -pU).\n"
-" Same arguments as newuidmap(1); mappings are comma separated.\n"
-" With no mapping, map the current uid to root.\n"
-" Incompatible with -b without the 'writable' option.\n"
-" -M[<gid> <lowergid> <count>]\n"
-" Set the gid map of a user namespace (implies -pU).\n"
-" Same arguments as newgidmap(1); mappings are comma separated.\n"
-" With no mapping, map the current gid to root.\n"
-" Incompatible with -b without the 'writable' option.\n"
-" --uts[=name] Enter a new UTS namespace (and set hostname).\n"
-"\n"
-"Seccomp options:\n"
-" -S <file> Set seccomp filter using <file>.\n"
-" E.g., '-S /usr/share/filters/<prog>.$(uname -m)'.\n"
-" Requires -n when not running as root.\n"
-" --seccomp-bpf-binary=<f>\n"
-" Set a pre-compiled seccomp filter using <f>.\n"
-" E.g., '-S /usr/share/filters/<prog>.$(uname -m).bpf'.\n"
-" Requires -n when not running as root.\n"
-" The user is responsible for ensuring that the binary\n"
-" was compiled for the correct architecture / kernel version.\n"
-" -L Report blocked syscalls when using seccomp filter.\n"
-" If the kernel does not support SECCOMP_RET_LOG, some syscalls\n"
-" will automatically be allowed (see below).\n"
-" -Y Synchronize seccomp filters across thread group.\n"
-" -a <table> Use alternate syscall table <table>.\n"
-" -s Use seccomp mode 1 (not the same as -S).\n"
-"\n"
-"Other options:\n"
-" --config <file>\n"
-" Load the Minijail configuration file <file>.\n"
-" If used, must be specified ahead of other options.\n"
-" --profile <p>\n"
-" Configure minijail0 to run with the <p> 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 <caps> Restrict caps to <caps>.\n"
-" --ambient Raise ambient capabilities. Requires -c.\n"
-" -B <mask> Skip setting <mask> 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 <file> Write the pid of the jailed process to <file>.\n"
-" -i Exit immediately after fork(2); i.e. background the program.\n"
-" -z Don't forward signals to jailed process.\n"
-" -R <type,cur,max>\n"
-" Call setrlimit(3); can be specified multiple times.\n"
-" -T <type> Assume <program> is a <type> ELF binary;\n"
-" <type> may be 'static' or 'dynamic'.\n"
-" This will avoid accessing <program> 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 <program>\n"
-" inherit the active environment. Often used to start <program>\n"
-" with a minimal sanitized environment.\n"
-" --env-add <NAME=value>\n"
-" Sets the specified environment variable <NAME>\n"
-" in the <program>'s environment before starting it.\n"
-"\n"
-"Uncommon options:\n"
-" --allow-speculative-execution\n"
-" Allow speculative execution by disabling mitigations.\n"
-" --preload-library=<file>\n"
-" Overrides the path to \"" PRELOADPATH "\".\n"
-" This is only really useful for local testing.\n"
-" --logging=<output>\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] [--] <program> [args...]\n\n%s", progn,
- help_text);
+ size_t i;
+ /* clang-format off */
+ printf("Usage: %s [-dGhHiIKlLnNprRstUvyYz]\n"
+ " [-a <table>]\n"
+ " [-b <src>[,[dest][,<writeable>]]] [-k <src>,<dest>,<type>[,<flags>[,<data>]]]\n"
+ " [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n"
+ " [-m[<uid> <loweruid> <count>]*] [-M[<gid> <lowergid> <count>]*] [--profile <name>]\n"
+ " [-R <type,cur,max>] [-S <file>] [-t[size]] [-T <type>] [-u <user>] [-V <file>]\n"
+ " <program> [args...]\n"
+ " -a <table>: Use alternate syscall table <table>.\n"
+ " -b <...>: Bind <src> to <dest> in chroot.\n"
+ " Multiple instances allowed.\n"
+ " -B <mask>: Skip setting securebits in <mask> when restricting capabilities (-c).\n"
+ " By default, SECURE_NOROOT, SECURE_NO_SETUID_FIXUP, and \n"
+ " SECURE_KEEP_CAPS (together with their respective locks) are set.\n"
+ " There are eight securebits in total.\n"
+ " -k <...>: Mount <src> at <dest> in chroot.\n"
+ " <flags> and <data> can be specified as in mount(2).\n"
+ " Multiple instances allowed.\n"
+ " -c <caps>: Restrict caps to <caps>.\n"
+ " -C <dir>: chroot(2) to <dir>.\n"
+ " Not compatible with -P.\n"
+ " -P <dir>: pivot_root(2) to <dir> (implies -v).\n"
+ " Not compatible with -C.\n"
+ " --mount-dev, Create a new /dev with a minimal set of device nodes (implies -v).\n"
+ " -d: See the minijail0(1) man page for the exact set.\n"
+ " -e[file]: Enter new network namespace, or existing one if |file| is provided.\n"
+ " -f <file>: Write the pid of the jailed process to <file>.\n"
+ " -g <group>: Change gid to <group>.\n"
+ " -G: Inherit supplementary groups from new uid.\n"
+ " Not compatible with -y or --add-suppl-group.\n"
+ " -y: Keep original uid's supplementary groups.\n"
+ " Not compatible with -G or --add-suppl-group.\n"
+ " --add-suppl-group <g>:Add <g> to the proccess' supplementary groups,\n"
+ " can be specified multiple times to add several groups.\n"
+ " Not compatible with -y or -G.\n"
+ " -h: Help (this message).\n"
+ " -H: Seccomp filter help message.\n"
+ " -i: Exit immediately after fork(2). The jailed process will run\n"
+ " in the background.\n"
+ " -I: Run <program> as init (pid 1) inside a new pid namespace (implies -p).\n"
+ " -K: Do not change share mode of any existing mounts.\n"
+ " -K<mode>: Mark all existing mounts as <mode> instead of MS_PRIVATE.\n"
+ " -l: Enter new IPC namespace.\n"
+ " -L: Report blocked syscalls when using seccomp filter.\n"
+ " If the kernel does not support SECCOMP_RET_LOG,\n"
+ " forces the following syscalls to be allowed:\n"
+ " ", progn);
+ /* clang-format on */
+ for (i = 0; i < log_syscalls_len; i++)
+ printf("%s ", log_syscalls[i]);
- printf("\nsyscalls allowed when logging (-L):\n ");
- for (size_t i = 0; i < log_syscalls_len; ++i)
- printf(" %s", log_syscalls[i]);
- printf("\n");
+ /* clang-format off */
+ printf("\n"
+ " -m[map]: Set the uid map of a user namespace (implies -pU).\n"
+ " Same arguments as newuidmap(1), multiple mappings should be separated by ',' (comma).\n"
+ " With no mapping, map the current uid to root inside the user namespace.\n"
+ " Not compatible with -b without the 'writable' option.\n"
+ " -M[map]: Set the gid map of a user namespace (implies -pU).\n"
+ " Same arguments as newgidmap(1), multiple mappings should be separated by ',' (comma).\n"
+ " With no mapping, map the current gid to root inside the user namespace.\n"
+ " Not compatible with -b without the 'writable' option.\n"
+ " -n: Set no_new_privs.\n"
+ " -N: Enter a new cgroup namespace.\n"
+ " -p: Enter new pid namespace (implies -vr).\n"
+ " -r: Remount /proc read-only (implies -v).\n"
+ " -R: Set rlimits, can be specified multiple times.\n"
+ " -s: Use seccomp mode 1 (not the same as -S).\n"
+ " -S <file>: Set seccomp filter using <file>.\n"
+ " E.g., '-S /usr/share/filters/<prog>.$(uname -m)'.\n"
+ " Requires -n when not running as root.\n"
+ " -t[size]: Mount tmpfs at /tmp (implies -v).\n"
+ " Optional argument specifies size (default \"64M\").\n"
+ " -T <type>: Assume <program> is a <type> ELF binary; <type> can be 'static' or 'dynamic'.\n"
+ " This will avoid accessing <program> binary before execve(2).\n"
+ " Type 'static' will avoid preload hooking.\n"
+ " -u <user>: Change uid to <user>.\n"
+ " -U: Enter new user namespace (implies -p).\n"
+ " -v: Enter new mount namespace.\n"
+ " -V <file>: Enter specified mount namespace.\n"
+ " -w: Create and join a new anonymous session keyring.\n"
+ " -Y: Synchronize seccomp filters across thread group.\n"
+ " -z: Don't forward signals to jailed process.\n"
+ " --ambient: Raise ambient capabilities. Requires -c.\n"
+ " --uts[=name]: Enter a new UTS namespace (and set hostname).\n"
+ " --logging=<s>:Use <s> as the logging system.\n"
+ " <s> must be 'auto' (default), 'syslog', or 'stderr'.\n"
+ " --profile <p>:Configure minijail0 to run with the <p> 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"
+ " --preload-library=<f>:Overrides the path to \"" PRELOADPATH "\".\n"
+ " This is only really useful for local testing.\n"
+ " --seccomp-bpf-binary=<f>:Set a pre-compiled seccomp filter using <f>.\n"
+ " E.g., '-S /usr/share/filters/<prog>.$(uname -m).bpf'.\n"
+ " Requires -n when not running as root.\n"
+ " The user is responsible for ensuring that the binary\n"
+ " was compiled for the correct architecture / kernel version.\n"
+ " --allow-speculative-execution:Allow speculative execution and disable\n"
+ " mitigations for speculative execution attacks.\n");
+ /* clang-format on */
}
static void seccomp_filter_usage(const char *progn)
@@ -647,98 +637,12 @@
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;
- for (curr_opt = &longopts[0]; curr_opt->name != NULL;
- curr_opt = &longopts[++i])
- if (strcmp(entry->key, curr_opt->name) == 0)
- break;
- 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);
- }
- opt = curr_opt->val;
- 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 precendences 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 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[], int *exit_immediately,
- ElfType *elftype, const char **preload_path,
- char ***envp)
+ int *exit_immediately, ElfType *elftype,
+ const char **preload_path)
{
- enum seccomp_type { None, Strict, Filter, BpfBinaryFilter };
- enum seccomp_type seccomp = None;
int opt;
- int use_seccomp_filter = 0;
- int use_seccomp_filter_binary = 0;
- int use_seccomp_log = 0;
+ int use_seccomp_filter = 0, use_seccomp_filter_binary = 0;
int forward = 1;
int binding = 0;
int chroot = 0, pivot_root = 0;
@@ -746,6 +650,7 @@
const char *remount_mode = NULL;
int inherit_suppl_gids = 0, keep_suppl_gids = 0;
int caps = 0, ambient_caps = 0;
+ int seccomp = -1;
bool use_uid = false, use_gid = false;
uid_t uid = 0;
gid_t gid = 0;
@@ -756,21 +661,43 @@
size_t tmp_size = 0;
const char *filter_path = NULL;
int log_to_stderr = -1;
- struct config_entry_list *conf_entry_list = NULL;
- size_t conf_index = 0;
- while ((opt = getopt_conf_or_cli(argc, argv, &conf_entry_list,
- &conf_index)) != -1) {
+ const char *optstring =
+ "+u:g:sS:c:C:P:b:B:V:f:m::M::k:a:e::R:T:vrGhHinNplLt::IUK::wyYzd";
+ /* clang-format off */
+ const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"mount-dev", no_argument, 0, 'd'},
+ {"ambient", no_argument, 0, 128},
+ {"uts", optional_argument, 0, 129},
+ {"logging", required_argument, 0, 130},
+ {"profile", required_argument, 0, 131},
+ {"preload-library", required_argument, 0, 132},
+ {"seccomp-bpf-binary", required_argument, 0, 133},
+ {"add-suppl-group", required_argument, 0, 134},
+ {"allow-speculative-execution", no_argument, 0, 135},
+ {0, 0, 0, 0},
+ };
+ /* clang-format on */
+
+ while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) !=
+ -1) {
switch (opt) {
case 'u':
- if (use_uid)
- errx(1, "-u provided multiple times.");
+ if (use_uid) {
+ fprintf(stderr,
+ "-u provided multiple times.\n");
+ exit(1);
+ }
use_uid = true;
set_user(j, optarg, &uid, &gid);
break;
case 'g':
- if (use_gid)
- errx(1, "-g provided multiple times.");
+ if (use_gid) {
+ fprintf(stderr,
+ "-g provided multiple times.\n");
+ exit(1);
+ }
use_gid = true;
set_group(j, optarg, &gid);
break;
@@ -778,19 +705,23 @@
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");
+ if (seccomp != -1 && seccomp != 1) {
+ fprintf(stderr,
+ "Do not use -s, -S, or "
+ "--seccomp-bpf-binary together.\n");
+ exit(1);
}
- seccomp = Strict;
+ seccomp = 1;
minijail_use_seccomp(j);
break;
case 'S':
- if (seccomp != None && seccomp != Filter) {
- errx(1, "Do not use -s, -S, or "
- "--seccomp-bpf-binary together");
+ if (seccomp != -1 && seccomp != 2) {
+ fprintf(stderr,
+ "Do not use -s, -S, or "
+ "--seccomp-bpf-binary together.\n");
+ exit(1);
}
- seccomp = Filter;
+ seccomp = 2;
minijail_use_seccomp_filter(j);
filter_path = optarg;
use_seccomp_filter = 1;
@@ -799,11 +730,6 @@
minijail_namespace_ipc(j);
break;
case 'L':
- if (seccomp == BpfBinaryFilter) {
- errx(1, "-L does not work with "
- "--seccomp-bpf-binary");
- }
- use_seccomp_log = 1;
minijail_log_seccomp_filter_failures(j);
break;
case 'b':
@@ -831,8 +757,11 @@
use_pivot_root(j, optarg, &pivot_root, chroot);
break;
case 'f':
- if (0 != minijail_write_pid_file(j, optarg))
- errx(1, "Could not prepare pid file path");
+ if (0 != minijail_write_pid_file(j, optarg)) {
+ fprintf(stderr,
+ "Could not prepare pid file path.\n");
+ exit(1);
+ }
break;
case 't':
minijail_namespace_vfs(j);
@@ -845,7 +774,8 @@
}
if (optarg != NULL &&
0 != parse_size(&tmp_size, optarg)) {
- errx(1, "Invalid /tmp tmpfs size");
+ fprintf(stderr, "Invalid /tmp tmpfs size.\n");
+ exit(1);
}
break;
case 'v':
@@ -890,14 +820,20 @@
minijail_remount_proc_readonly(j);
break;
case 'G':
- if (keep_suppl_gids)
- errx(1, "-y and -G are not compatible");
+ if (keep_suppl_gids) {
+ fprintf(stderr,
+ "-y and -G are not compatible.\n");
+ exit(1);
+ }
minijail_inherit_usergroups(j);
inherit_suppl_gids = 1;
break;
case 'y':
- if (inherit_suppl_gids)
- errx(1, "-y and -G are not compatible");
+ if (inherit_suppl_gids) {
+ fprintf(stderr,
+ "-y and -G are not compatible.\n");
+ exit(1);
+ }
minijail_keep_supplementary_gids(j);
keep_suppl_gids = 1;
break;
@@ -946,8 +882,11 @@
gidmap = xstrdup(optarg);
break;
case 'a':
- if (0 != minijail_use_alt_syscall(j, optarg))
- errx(1, "Could not set alt-syscall table");
+ if (0 != minijail_use_alt_syscall(j, optarg)) {
+ fprintf(stderr,
+ "Could not set alt-syscall table.\n");
+ exit(1);
+ }
break;
case 'R':
add_rlimit(j, optarg);
@@ -958,8 +897,9 @@
else if (!strcmp(optarg, "dynamic"))
*elftype = ELFDYNAMIC;
else {
- errx(1, "ELF type must be 'static' or "
- "'dynamic'");
+ fprintf(stderr, "ELF type must be 'static' or "
+ "'dynamic'.\n");
+ exit(1);
}
break;
case 'w':
@@ -976,125 +916,53 @@
minijail_mount_dev(j);
break;
/* Long options. */
- case OPT_AMBIENT:
+ case 128: /* Ambient caps. */
ambient_caps = 1;
minijail_set_ambient_caps(j);
break;
- case OPT_UTS:
+ case 129: /* UTS/hostname namespace. */
minijail_namespace_uts(j);
if (optarg)
minijail_namespace_set_hostname(j, optarg);
break;
- case OPT_LOGGING:
- if (!strcmp(optarg, "auto"))
+ case 130: /* Logging. */
+ if (!strcmp(optarg, "auto")) {
log_to_stderr = -1;
- else if (!strcmp(optarg, "syslog"))
+ } else if (!strcmp(optarg, "syslog")) {
log_to_stderr = 0;
- else if (!strcmp(optarg, "stderr"))
+ } else if (!strcmp(optarg, "stderr")) {
log_to_stderr = 1;
- else
- errx(1,
- "--logger must be 'syslog' or 'stderr'");
+ } else {
+ fprintf(stderr, "--logger must be 'syslog' or "
+ "'stderr'.\n");
+ exit(1);
+ }
break;
- case OPT_PROFILE:
+ case 131: /* Profile */
use_profile(j, optarg, &pivot_root, chroot, &tmp_size);
break;
- case OPT_PRELOAD_LIBRARY:
+ case 132: /* PRELOADPATH */
*preload_path = optarg;
break;
- case OPT_SECCOMP_BPF_BINARY:
- if (seccomp != None && seccomp != BpfBinaryFilter) {
- errx(1, "Do not use -s, -S, or "
- "--seccomp-bpf-binary together");
+ case 133: /* seccomp-bpf binary. */
+ if (seccomp != -1 && seccomp != 3) {
+ fprintf(stderr,
+ "Do not use -s, -S, or "
+ "--seccomp-bpf-binary together.\n");
+ exit(1);
}
- if (use_seccomp_log == 1)
- errx(1, "-L does not work with "
- "--seccomp-bpf-binary");
- seccomp = BpfBinaryFilter;
+ seccomp = 3;
minijail_use_seccomp_filter(j);
filter_path = optarg;
use_seccomp_filter_binary = 1;
break;
- case OPT_ADD_SUPPL_GROUP:
- suppl_group_add(&suppl_gids_count, &suppl_gids, optarg);
+ case 134:
+ suppl_group_add(&suppl_gids_count, &suppl_gids,
+ optarg);
break;
- case OPT_ALLOW_SPECULATIVE_EXECUTION:
+ case 135:
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_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);
@@ -1112,7 +980,8 @@
*/
if (0 !=
minijail_preserve_fd(j, STDERR_FILENO, STDERR_FILENO)) {
- errx(1, "Could not preserve stderr");
+ fprintf(stderr, "Could not preserve stderr.\n");
+ exit(1);
}
}
@@ -1124,8 +993,9 @@
/* Can only set ambient caps when using regular caps. */
if (ambient_caps && !caps) {
- errx(1, "Can't set ambient capabilities (--ambient) "
- "without actually using capabilities (-c)");
+ fprintf(stderr, "Can't set ambient capabilities (--ambient) "
+ "without actually using capabilities (-c).\n");
+ exit(1);
}
/* Set up signal handlers in minijail unless asked not to. */
@@ -1137,8 +1007,9 @@
* a new mount namespace.
*/
if (binding && !(chroot || pivot_root || mount_ns)) {
- errx(1, "Bind mounts require a chroot, pivot_root, or "
- " new mount namespace");
+ fprintf(stderr, "Bind mounts require a chroot, pivot_root, or "
+ " new mount namespace.\n");
+ exit(1);
}
/*
@@ -1146,10 +1017,11 @@
* that's set there is no need for the -K/-K<mode> flags.
*/
if (change_remount && !mount_ns) {
- errx(1, "No need to use -K (skip remounting '/') or "
- "-K<mode> (remount '/' as <mode>) "
- "without -v (new mount namespace).\n"
- "Do you need to add '-v' explicitly?");
+ fprintf(stderr, "No need to use -K (skip remounting '/') or "
+ "-K<mode> (remount '/' as <mode>)\n"
+ "without -v (new mount namespace).\n"
+ "Do you need to add '-v' explicitly?\n");
+ exit(1);
}
/* Configure the remount flag here to avoid having -v override it. */
@@ -1167,7 +1039,7 @@
*/
if (suppl_gids_count) {
minijail_set_supplementary_gids(j, suppl_gids_count,
- suppl_gids);
+ suppl_gids);
free(suppl_gids);
}
@@ -1189,16 +1061,6 @@
minijail_mount_tmp_size(j, tmp_size);
/*
- * 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.
*/
@@ -1217,8 +1079,10 @@
/* Check that we can access the target program. */
if (access(program_path, X_OK)) {
- errx(1, "Target program '%s' is not accessible",
- argv[optind]);
+ fprintf(stderr,
+ "Target program '%s' is not accessible.\n",
+ argv[optind]);
+ exit(1);
}
/* Check if target is statically or dynamically linked. */
@@ -1232,9 +1096,11 @@
* execve(2).
*/
if (caps && *elftype == ELFSTATIC && !ambient_caps) {
- errx(1, "Can't run statically-linked binaries with capabilities"
- " (-c) without also setting ambient capabilities. "
- "Try passing --ambient.");
+ fprintf(stderr, "Can't run statically-linked binaries with "
+ "capabilities (-c) without also setting "
+ "ambient capabilities. Try passing "
+ "--ambient.\n");
+ exit(1);
}
return optind;
diff --git a/minijail0_cli.h b/minijail0_cli.h
index cd504b3..583c763 100644
--- a/minijail0_cli.h
+++ b/minijail0_cli.h
@@ -17,9 +17,8 @@
struct minijail;
int parse_args(struct minijail *j, int argc, char *const argv[],
- char *const environ[], int *exit_immediately,
- ElfType *elftype, const char **preload_path,
- char ***envp);
+ int *exit_immediately, ElfType *elftype,
+ const char **preload_path);
#ifdef __cplusplus
}; /* extern "C" */
diff --git a/minijail0_cli_unittest.cc b/minijail0_cli_unittest.cc
index 7b20ecd..76ff37f 100644
--- a/minijail0_cli_unittest.cc
+++ b/minijail0_cli_unittest.cc
@@ -14,10 +14,8 @@
#include <gtest/gtest.h>
-#include "config_parser.h"
#include "libminijail.h"
#include "minijail0_cli.h"
-#include "test_util.h"
namespace {
@@ -60,10 +58,9 @@
testing::internal::CaptureStdout();
const char* preload_path = PRELOADPATH;
- char **envp = NULL;
int ret =
parse_args(j, pargv.size(), const_cast<char* const*>(pargv.data()),
- NULL, exit_immediately, elftype, &preload_path, &envp);
+ exit_immediately, elftype, &preload_path);
testing::internal::GetCapturedStdout();
minijail_destroy(j);
@@ -529,80 +526,3 @@
argv[1] = "-Kfoo";
ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
}
-
-TEST_F(CliTest, invalid_L_combo) {
- std::vector<std::string> 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<std::string> 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<std::string> argv1 = {"--env-add", "NAME=value", "/bin/sh"};
- ASSERT_TRUE(parse_args_(argv1));
-
- // multiple occurences are allowed.
- std::vector<std::string> 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<std::string> 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<std::string> 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<std::string> 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), "");
-}
-
-// Android unit tests do not support data file yet.
-#if !defined(__ANDROID__)
-
-TEST_F(CliTest, conf_parsing_invalid_key) {
- std::vector<std::string> argv = {"--config", source_path("test/invalid.conf"),
- "/bin/sh"};
-
- ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
-}
-
-TEST_F(CliTest, conf_parsing) {
- std::vector<std::string> argv = {"--config",
- source_path("test/valid.conf"),
- "/bin/sh"};
-
- ASSERT_TRUE(parse_args_(argv));
-}
-
-#endif // !__ANDROID__
diff --git a/parse_seccomp_policy.cc b/parse_seccomp_policy.cc
index a6daac5..000b80d 100644
--- a/parse_seccomp_policy.cc
+++ b/parse_seccomp_policy.cc
@@ -47,8 +47,8 @@
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[] = {
+ const char* optstring = "d:h";
+ const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"dump", optional_argument, 0, 'd'},
{0, 0, 0, 0},
diff --git a/rust/OWNERS b/rust/OWNERS
index f8111bb..1db5ac3 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -2,5 +2,5 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/rust/minijail-sys/Cargo.toml b/rust/minijail-sys/Cargo.toml
index 4c49c95..934c605 100644
--- a/rust/minijail-sys/Cargo.toml
+++ b/rust/minijail-sys/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "minijail-sys"
-version = "0.0.13"
+version = "0.0.11"
description = "Provides raw (unsafe) bindings to the libminijail C library."
authors = ["The Chromium OS Authors"]
edition = "2018"
@@ -14,4 +14,3 @@
[build-dependencies]
pkg-config = "0.3"
-which = "4.0.0"
diff --git a/rust/minijail-sys/build.rs b/rust/minijail-sys/build.rs
index f73191c..8c7ea6d 100644
--- a/rust/minijail-sys/build.rs
+++ b/rust/minijail-sys/build.rs
@@ -7,35 +7,10 @@
/// This script prefers linking against a pkg-config provided libminijail, but will fall back to
/// building libminijail statically.
use std::env;
-use std::fs::remove_file;
use std::io;
-use std::path::Path;
use std::process::Command;
-/// 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<()> {
+fn main() -> io::Result<()> {
// Minijail requires libcap at runtime.
pkg_config::Config::new().probe("libcap").unwrap();
@@ -52,10 +27,9 @@
.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("CC_STATIC_LIBRARY(libminijail.pic.a)")
.status()?;
if !status.success() {
std::process::exit(status.code().unwrap_or(1));
@@ -64,53 +38,3 @@
println!("cargo:rustc-link-lib=static=minijail.pic");
Ok(())
}
-
-fn bindings_generation() -> io::Result<()> {
- let bindgen = match which::which("bindgen") {
- Ok(v) => v,
- // Use already generated copy if bindgen is not present.
- _ => return Ok(()),
- };
-
- // If CROS_RUST is set, skip generation.
- let gen_file = Path::new("./libminijail.rs");
- if gen_file.exists() {
- if env::var("CROS_RUST") == Ok(String::from("1")) {
- return Ok(());
- }
- remove_file(gen_file).expect("Failed to remove generated file.");
- }
- let header_dir = Path::new("../../");
- let header_path = header_dir.join("libminijail.h");
- println!("cargo:rerun-if-changed={}", header_path.display());
- let status = Command::new(&bindgen)
- .args(&["--default-enum-style", "rust"])
- .args(&["--blacklist-type", "__rlim64_t"])
- .args(&["--raw-line", "pub type __rlim64_t = u64;"])
- .args(&["--blacklist-type", "__u\\d{1,2}"])
- .args(&["--raw-line", "pub type __u8 = u8;"])
- .args(&["--raw-line", "pub type __u16 = u16;"])
- .args(&["--raw-line", "pub type __u32 = u32;"])
- .args(&["--blacklist-type", "__uint64_t"])
- .args(&["--whitelist-function", "^minijail_.*"])
- .args(&["--whitelist-var", "^MINIJAIL_.*"])
- .arg("--no-layout-tests")
- .arg("--disable-header-comment")
- .args(&["--output", gen_file.to_str().unwrap()])
- .arg(header_path.to_str().unwrap())
- .args(&[
- "--",
- "-DUSE_BINDGEN",
- "-D_FILE_OFFSET_BITS=64",
- "-D_LARGEFILE_SOURCE",
- "-D_LARGEFILE64_SOURCE",
- ])
- .status()?;
- assert!(status.success());
- Ok(())
-}
-
-fn main() -> io::Result<()> {
- set_up_libminijail()?;
- bindings_generation()
-}
diff --git a/rust/minijail-sys/lib.rs b/rust/minijail-sys/lib.rs
index c418150..59db51e 100644
--- a/rust/minijail-sys/lib.rs
+++ b/rust/minijail-sys/lib.rs
@@ -37,11 +37,6 @@
//
// Enum variants in rust are customarily camel case, but bindgen will leave the original names
// intact.
-#[allow(
- clippy::all,
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals
-)]
+#[allow(non_camel_case_types)]
mod libminijail;
pub use crate::libminijail::*;
diff --git a/rust/minijail-sys/libminijail.rs b/rust/minijail-sys/libminijail.rs
index 42b1a8d..594a479 100644
--- a/rust/minijail-sys/libminijail.rs
+++ b/rust/minijail-sys/libminijail.rs
@@ -1,3 +1,6 @@
+/* automatically generated by rust-bindgen */
+#![allow(clippy::all)]
+
pub type __rlim64_t = u64;
pub type __u8 = u8;
pub type __u16 = u16;
@@ -10,7 +13,6 @@
pub type gid_t = __gid_t;
pub type uid_t = __uid_t;
pub type pid_t = __pid_t;
-pub type size_t = ::std::os::raw::c_ulong;
#[repr(C)]
pub struct sock_filter {
pub code: __u16,
@@ -27,17 +29,15 @@
pub const MINIJAIL_ERR_NO_ACCESS: _bindgen_ty_1 = _bindgen_ty_1::MINIJAIL_ERR_NO_ACCESS;
pub const MINIJAIL_ERR_NO_COMMAND: _bindgen_ty_1 = _bindgen_ty_1::MINIJAIL_ERR_NO_COMMAND;
pub const MINIJAIL_ERR_SIG_BASE: _bindgen_ty_1 = _bindgen_ty_1::MINIJAIL_ERR_SIG_BASE;
-pub const MINIJAIL_ERR_MOUNT: _bindgen_ty_1 = _bindgen_ty_1::MINIJAIL_ERR_MOUNT;
pub const MINIJAIL_ERR_PRELOAD: _bindgen_ty_1 = _bindgen_ty_1::MINIJAIL_ERR_PRELOAD;
pub const MINIJAIL_ERR_JAIL: _bindgen_ty_1 = _bindgen_ty_1::MINIJAIL_ERR_JAIL;
pub const MINIJAIL_ERR_INIT: _bindgen_ty_1 = _bindgen_ty_1::MINIJAIL_ERR_INIT;
#[repr(u32)]
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum _bindgen_ty_1 {
MINIJAIL_ERR_NO_ACCESS = 126,
MINIJAIL_ERR_NO_COMMAND = 127,
MINIJAIL_ERR_SIG_BASE = 128,
- MINIJAIL_ERR_MOUNT = 251,
MINIJAIL_ERR_PRELOAD = 252,
MINIJAIL_ERR_JAIL = 253,
MINIJAIL_ERR_INIT = 254,
@@ -51,7 +51,7 @@
unsafe extern "C" fn(context: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int,
>;
#[repr(u32)]
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum minijail_hook_event_t {
MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS = 0,
MINIJAIL_HOOK_EVENT_PRE_EXECVE = 1,
@@ -68,7 +68,7 @@
pub fn minijail_change_gid(j: *mut minijail, gid: gid_t);
}
extern "C" {
- pub fn minijail_set_supplementary_gids(j: *mut minijail, size: size_t, list: *const gid_t);
+ pub fn minijail_set_supplementary_gids(j: *mut minijail, size: usize, list: *const gid_t);
}
extern "C" {
pub fn minijail_keep_supplementary_gids(j: *mut minijail);
@@ -98,9 +98,6 @@
pub fn minijail_set_seccomp_filter_tsync(j: *mut minijail);
}
extern "C" {
- pub fn minijail_set_seccomp_filter_allow_speculation(j: *mut minijail);
-}
-extern "C" {
pub fn minijail_set_seccomp_filters(j: *mut minijail, filter: *const sock_fprog);
}
extern "C" {
@@ -256,7 +253,7 @@
pub fn minijail_mount_tmp(j: *mut minijail);
}
extern "C" {
- pub fn minijail_mount_tmp_size(j: *mut minijail, size: size_t);
+ pub fn minijail_mount_tmp_size(j: *mut minijail, size: usize);
}
extern "C" {
pub fn minijail_mount_dev(j: *mut minijail);
@@ -289,11 +286,7 @@
) -> ::std::os::raw::c_int;
}
extern "C" {
- pub fn minijail_add_remount(
- j: *mut minijail,
- mount_name: *const ::std::os::raw::c_char,
- remount_mode: ::std::os::raw::c_ulong,
- ) -> ::std::os::raw::c_int;
+ pub fn minijail_copy_jail(from: *const minijail, out: *mut minijail) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn minijail_add_hook(
@@ -361,30 +354,6 @@
) -> ::std::os::raw::c_int;
}
extern "C" {
- pub fn minijail_run_env_pid_pipes(
- j: *mut minijail,
- filename: *const ::std::os::raw::c_char,
- argv: *const *mut ::std::os::raw::c_char,
- envp: *const *mut ::std::os::raw::c_char,
- pchild_pid: *mut pid_t,
- pstdin_fd: *mut ::std::os::raw::c_int,
- pstdout_fd: *mut ::std::os::raw::c_int,
- pstderr_fd: *mut ::std::os::raw::c_int,
- ) -> ::std::os::raw::c_int;
-}
-extern "C" {
- pub fn minijail_run_fd_env_pid_pipes(
- j: *mut minijail,
- elf_fd: ::std::os::raw::c_int,
- argv: *const *mut ::std::os::raw::c_char,
- envp: *const *mut ::std::os::raw::c_char,
- pchild_pid: *mut pid_t,
- pstdin_fd: *mut ::std::os::raw::c_int,
- pstdout_fd: *mut ::std::os::raw::c_int,
- pstderr_fd: *mut ::std::os::raw::c_int,
- ) -> ::std::os::raw::c_int;
-}
-extern "C" {
pub fn minijail_run_pid_pipes_no_preload(
j: *mut minijail,
filename: *const ::std::os::raw::c_char,
@@ -420,8 +389,5 @@
pub fn minijail_destroy(j: *mut minijail);
}
extern "C" {
- pub fn minijail_copy_jail(from: *const minijail, out: *mut minijail) -> ::std::os::raw::c_int;
-}
-extern "C" {
pub fn minijail_log_to_fd(fd: ::std::os::raw::c_int, min_priority: ::std::os::raw::c_int);
}
diff --git a/rust/minijail/Cargo.toml b/rust/minijail/Cargo.toml
index e6c08b8..db0652f 100644
--- a/rust/minijail/Cargo.toml
+++ b/rust/minijail/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "minijail"
-version = "0.2.3"
+version = "0.2.1"
description = "Provides a safe Rust friendly interface to libminijail."
authors = ["The Chromium OS Authors"]
edition = "2018"
diff --git a/rust/minijail/src/lib.rs b/rust/minijail/src/lib.rs
index 5028041..ba59075 100644
--- a/rust/minijail/src/lib.rs
+++ b/rust/minijail/src/lib.rs
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+use libc::pid_t;
+use minijail_sys::*;
use std::ffi::CString;
use std::fmt::{self, Display};
use std::fs;
@@ -10,154 +12,6 @@
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 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<CString>,
- args_cptr: Vec<*const c_char>,
-
- // Ownership of the backing data of env_cptr is provided by env_cstr.
- env_cstr: Option<Vec<CString>>,
- env_cptr: Option<Vec<*const c_char>>,
-}
-
-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<P: AsRef<Path>, S: AsRef<str>, A: AsRef<str>>(
- path: P,
- keep_fds: &[RawFd],
- args: &[S],
- env_vars: Option<&[A]>,
- ) -> Result<Command> {
- 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::<Vec<(RawFd, RawFd)>>();
- self
- }
-
- fn remap_fds(mut self, remap_fds: &[(RawFd, RawFd)]) -> Command {
- self.preserve_fds = remap_fds.to_vec();
- self
- }
-
- fn args<S: AsRef<str>>(mut self, args: &[S]) -> Result<Command> {
- let (args_cstr, args_cptr) = to_execve_cstring_array(args)?;
- self.args_cstr = args_cstr;
- self.args_cptr = args_cptr;
- Ok(self)
- }
-
- fn envs<S: AsRef<str>>(mut self, vars: &[S]) -> Result<Command> {
- 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<pid_t>;
-}
-
-impl Runnable for &Path {
- fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t> {
- 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<pid_t> {
- 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 {
@@ -229,8 +83,6 @@
Killed(u8),
/// Process finished returning a non-zero code.
ReturnCode(u8),
- /// Failed to wait the process.
- Wait(i32),
}
impl Display for Error {
@@ -319,14 +171,13 @@
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)),
}
}
}
impl std::error::Error for Error {}
-pub type Result<T> = StdResult<T, Error>;
+pub type Result<T> = std::result::Result<T, Error>;
/// Configuration to jail a process based on wrapping libminijail.
///
@@ -382,9 +233,6 @@
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);
}
@@ -401,7 +249,7 @@
if ret > 0 && ret <= 0xff {
return Err(Error::ReturnCode(ret as u8));
}
- unreachable!("Unexpected returned value from wait: {}", ret);
+ unreachable!();
}
impl Minijail {
@@ -465,7 +313,7 @@
}
pub fn set_supplementary_gids(&mut self, ids: &[libc::gid_t]) {
unsafe {
- minijail_set_supplementary_gids(self.jail, ids.len() as size_t, ids.as_ptr());
+ minijail_set_supplementary_gids(self.jail, ids.len(), ids.as_ptr());
}
}
pub fn keep_supplementary_gids(&mut self) {
@@ -510,9 +358,6 @@
}
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::<sock_filter>() != 0 {
return Err(Error::WrongProgramSize);
}
@@ -767,7 +612,7 @@
}
pub fn mount_tmp_size(&mut self, size: usize) {
unsafe {
- minijail_mount_tmp_size(self.jail, size as size_t);
+ minijail_mount_tmp_size(self.jail, size);
}
}
pub fn mount_bind<P1: AsRef<Path>, P2: AsRef<Path>>(
@@ -817,10 +662,13 @@
inheritable_fds: &[RawFd],
args: &[S],
) -> Result<pid_t> {
- self.run_internal(
- Command::new(Program::Filename(cmd.as_ref().to_path_buf()))
- .keep_fds(inheritable_fds)
- .args(args)?,
+ self.run_remap(
+ cmd,
+ &inheritable_fds
+ .iter()
+ .map(|&a| (a, a))
+ .collect::<Vec<(RawFd, RawFd)>>(),
+ args,
)
}
@@ -832,49 +680,25 @@
inheritable_fds: &[(RawFd, RawFd)],
args: &[S],
) -> Result<pid_t> {
- self.run_internal(
- Command::new(Program::Filename(cmd.as_ref().to_path_buf()))
- .remap_fds(inheritable_fds)
- .args(args)?,
- )
- }
+ let cmd_os = cmd
+ .as_ref()
+ .to_str()
+ .ok_or_else(|| Error::PathToCString(cmd.as_ref().to_owned()))?;
+ let cmd_cstr = CString::new(cmd_os).map_err(|_| Error::StrToCString(cmd_os.to_owned()))?;
- /// Behaves the same as `run()` except cmd is a file descriptor to the executable.
- pub fn run_fd<F: AsRawFd, S: AsRef<str>>(
- &self,
- cmd: &F,
- inheritable_fds: &[RawFd],
- args: &[S],
- ) -> Result<pid_t> {
- self.run_internal(
- Command::new(Program::FileDescriptor(cmd.as_raw_fd()))
- .keep_fds(inheritable_fds)
- .args(args)?,
- )
- }
+ // Converts each incoming `args` string to a `CString`, and then puts each `CString` pointer
+ // into a null terminated array, suitable for use as an argv parameter to `execve`.
+ let mut args_cstr = Vec::with_capacity(args.len());
+ let mut args_array = Vec::with_capacity(args.len());
+ for arg in args {
+ let arg_cstr = CString::new(arg.as_ref())
+ .map_err(|_| Error::StrToCString(arg.as_ref().to_owned()))?;
+ args_array.push(arg_cstr.as_ptr());
+ args_cstr.push(arg_cstr);
+ }
+ args_array.push(null());
- /// 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<F: AsRawFd, S: AsRef<str>>(
- &self,
- cmd: &F,
- inheritable_fds: &[(RawFd, RawFd)],
- args: &[S],
- ) -> Result<pid_t> {
- 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<pid_t> {
- self.run_internal(cmd)
- }
-
- fn run_internal(&self, cmd: Command) -> Result<pid_t> {
- for (src_fd, dst_fd) in cmd.preserve_fds.iter() {
+ for (src_fd, dst_fd) in inheritable_fds {
let ret = unsafe { minijail_preserve_fd(self.jail, *src_fd, *dst_fd) };
if ret < 0 {
return Err(Error::PreservingFd(ret));
@@ -889,7 +713,7 @@
// 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) {
+ if !inheritable_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));
@@ -901,10 +725,22 @@
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),
+ let mut pid = 0;
+ let ret = unsafe {
+ minijail_run_pid_pipes(
+ self.jail,
+ cmd_cstr.as_ptr(),
+ args_array.as_ptr() as *const *mut c_char,
+ &mut pid,
+ null_mut(),
+ null_mut(),
+ null_mut(),
+ )
+ };
+ if ret < 0 {
+ return Err(Error::ForkingMinijail(ret));
}
+ Ok(pid)
}
/// Forks a child and puts it in the previously configured minijail.
@@ -1024,54 +860,15 @@
}
}
-fn to_execve_cstring_array<S: AsRef<str>>(
- slice: &[S],
-) -> Result<(Vec<CString>, 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))
-}
-
#[cfg(test)]
mod tests {
+ use std::process::exit;
+
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<A: AsRawFd>(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() {
unsafe {
@@ -1092,7 +889,9 @@
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();
+ if unsafe { j.fork(None).unwrap() } == 0 {
+ exit(0);
+ }
}
#[test]
@@ -1104,28 +903,14 @@
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();
+ let fds: Vec<RawFd> = vec![0, 1, 2, first];
+ if j.fork(Some(&fds)).unwrap() == 0 {
+ assert!(libc::close(second) < 0); // Should fail as second should be closed already.
+ assert_eq!(libc::close(first), 0); // Should succeed as first should be untouched.
+ exit(0);
+ }
}
}
@@ -1179,29 +964,14 @@
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]
- fn runnable_fd_success() {
- let bin_file = File::open("/bin/true").unwrap();
- // On Chrome OS 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(()));
+ expect_result!(j.wait(), Err(Error::NoCommand));
}
#[test]
fn kill_success() {
let j = Minijail::new().unwrap();
j.run(
- Path::new("/usr/bin/sleep"),
+ Path::new("usr/bin/sleep"),
&[1, 2],
&["/usr/bin/sleep", "5"],
)
@@ -1215,7 +985,9 @@
fn chroot() {
let mut j = Minijail::new().unwrap();
j.enter_chroot(".").unwrap();
- j.run("/bin/true", &[], &EMPTY_STRING_SLICE).unwrap();
+ if unsafe { j.fork(None).unwrap() } == 0 {
+ exit(0);
+ }
}
#[test]
@@ -1223,7 +995,9 @@
fn namespace_vfs() {
let mut j = Minijail::new().unwrap();
j.namespace_vfs();
- j.run("/bin/true", &[], &EMPTY_STRING_SLICE).unwrap();
+ if unsafe { j.fork(None).unwrap() } == 0 {
+ exit(0);
+ }
}
#[test]
diff --git a/syscall_filter.c b/syscall_filter.c
index de5441c..fcdbaa8 100644
--- a/syscall_filter.c
+++ b/syscall_filter.c
@@ -568,6 +568,49 @@
return 0;
}
+/*
+ * This is like getline() but supports line wrapping with \.
+ */
+static 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;
+ char *next_line = NULL;
+ ssize_t next_ret = getmultiline(&next_line, &next_n, stream);
+ if (next_ret == -1) {
+ free(next_line);
+ /* 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) {
+ free(next_line);
+ return -1;
+ }
+ line[ret] = ' ';
+ memcpy(&line[ret + 1], next_line, next_ret + 1);
+ free(next_line);
+ *lineptr = line;
+ return *n - 1;
+}
+
int compile_file(const char *filename, FILE *policy_file,
struct filter_block *head, struct filter_block **arg_blocks,
struct bpf_labels *labels,
@@ -589,7 +632,7 @@
* Chain the filter sections together and dump them into
* the final buffer at the end.
*/
- attribute_cleanup_str char *line = NULL;
+ char *line = NULL;
size_t len = 0;
int ret = 0;
@@ -624,16 +667,15 @@
&state,
"failed to parse include statement");
ret = -1;
- goto out;
+ goto free_line;
}
- attribute_cleanup_fp FILE *included_file =
- fopen(filename, "re");
+ FILE *included_file = fopen(filename, "re");
if (included_file == NULL) {
compiler_pwarn(&state, "fopen('%s') failed",
filename);
ret = -1;
- goto out;
+ goto free_line;
}
if (compile_file(filename, included_file, head,
arg_blocks, labels, filteropts,
@@ -641,9 +683,11 @@
include_level + 1) == -1) {
compiler_warn(&state, "'@include %s' failed",
filename);
+ fclose(included_file);
ret = -1;
- goto out;
+ goto free_line;
}
+ fclose(included_file);
continue;
}
@@ -656,14 +700,14 @@
warn("compile_file: malformed policy line, missing "
"':'");
ret = -1;
- goto out;
+ goto free_line;
}
policy_line = strip(policy_line);
if (*policy_line == '\0') {
compiler_warn(&state, "empty policy line");
ret = -1;
- goto out;
+ goto free_line;
}
syscall_name = strip(syscall_name);
@@ -688,7 +732,7 @@
continue;
}
ret = -1;
- goto out;
+ goto free_line;
}
if (!insert_and_check_duplicate_syscall(previous_syscalls,
@@ -731,7 +775,7 @@
}
warn("could not allocate filter block");
ret = -1;
- goto out;
+ goto free_line;
}
if (*arg_blocks) {
@@ -752,7 +796,8 @@
ret = -1;
}
-out:
+free_line:
+ free(line);
return ret;
}
diff --git a/syscall_filter_unittest.cc b/syscall_filter_unittest.cc
index 79755f9..74f79da 100644
--- a/syscall_filter_unittest.cc
+++ b/syscall_filter_unittest.cc
@@ -15,11 +15,21 @@
#include "bpf.h"
#include "syscall_filter.h"
#include "syscall_filter_unittest_macros.h"
-#include "test_util.h"
#include "util.h"
namespace {
+// TODO(jorgelo): Android unit tests don't currently support data files.
+// Re-enable by creating a temporary policy file at runtime.
+#if !defined(__ANDROID__)
+
+std::string source_path(std::string file) {
+ std::string srcdir = getenv("SRC") ? : ".";
+ return srcdir + "/" + file;
+}
+
+#endif
+
// Simple C++ -> C wrappers to simplify test code.
enum ret_trap {
@@ -34,7 +44,7 @@
};
int test_compile_filter(
- const std::string& filename,
+ std::string filename,
FILE* policy_file,
struct sock_fprog* prog,
enum block_action action = ACTION_RET_KILL,
@@ -79,7 +89,7 @@
struct filter_block* test_compile_policy_line(
struct parser_state* state,
int nr,
- const std::string& policy_line,
+ std::string policy_line,
unsigned int label_id,
struct bpf_labels* labels,
enum block_action action = ACTION_RET_KILL) {
@@ -500,7 +510,7 @@
}
TEST_F(ArgFilterTest, arg0_short_gt_ge_comparisons) {
- for (const std::string fragment :
+ for (std::string fragment :
{"arg1 < 0xff", "arg1 <= 0xff", "arg1 > 0xff", "arg1 >= 0xff"}) {
struct filter_block* block =
test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
@@ -542,7 +552,7 @@
#if defined(BITS64)
TEST_F(ArgFilterTest, arg0_long_gt_ge_comparisons) {
- for (const std::string fragment :
+ for (std::string fragment :
{"arg1 < 0xbadc0ffee0ddf00d", "arg1 <= 0xbadc0ffee0ddf00d",
"arg1 > 0xbadc0ffee0ddf00d", "arg1 >= 0xbadc0ffee0ddf00d"}) {
struct filter_block* block =
@@ -1030,6 +1040,44 @@
namespace {
+FILE* write_policy_to_pipe(std::string policy) {
+ int pipefd[2];
+ if (pipe(pipefd) == -1) {
+ pwarn("pipe(pipefd) failed");
+ return nullptr;
+ }
+
+ size_t len = policy.length();
+ size_t i = 0;
+ unsigned int attempts = 0;
+ ssize_t ret;
+ while (i < len) {
+ ret = write(pipefd[1], policy.c_str() + i, len - i);
+ if (ret == -1) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return nullptr;
+ }
+
+ /* 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 nullptr;
+ }
+ continue;
+ }
+
+ attempts = 0;
+ i += (size_t)ret;
+ }
+
+ close(pipefd[1]);
+ return fdopen(pipefd[0], "r");
+}
+
class FileTest : public ::testing::Test {
protected:
virtual void SetUp() {
@@ -1053,7 +1101,7 @@
std::string policy =
"malformed";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_file("policy", policy_file, head_, &arg_blocks_,
&labels_);
@@ -1070,7 +1118,7 @@
"read:arg0 == 0\n"
"write:0";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_file("policy", policy_file, head_, &arg_blocks_,
&labels_);
@@ -1086,7 +1134,7 @@
std::string policy =
"read:arg0 == 0; ;";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_file("policy", policy_file, head_, &arg_blocks_,
&labels_);
@@ -1105,7 +1153,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_file("policy", policy_file, head_, &arg_blocks_,
&labels_);
@@ -1140,7 +1188,7 @@
const int LABEL_ID = 0;
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_file("policy", policy_file, head_, &arg_blocks_,
&labels_);
@@ -1207,7 +1255,7 @@
const int LABEL_ID = 0;
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_file("policy", policy_file, head_, &arg_blocks_,
&labels_);
@@ -1243,7 +1291,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1280,7 +1328,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual,
@@ -1325,7 +1373,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1345,7 +1393,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1364,7 +1412,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res =
@@ -1403,7 +1451,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG,
@@ -1442,7 +1490,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG,
@@ -1463,7 +1511,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL_PROCESS,
@@ -1502,7 +1550,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1543,7 +1591,7 @@
struct sock_fprog actual;
std::string policy = "read :1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1561,7 +1609,7 @@
struct sock_fprog actual;
std::string policy = "open:\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1573,7 +1621,7 @@
struct sock_fprog actual;
std::string policy = "open:\t \n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1585,7 +1633,7 @@
struct sock_fprog actual;
std::string policy = "notasyscall: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1597,7 +1645,7 @@
struct sock_fprog actual;
std::string policy = "open: argnn ==\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1609,7 +1657,7 @@
struct sock_fprog actual;
std::string policy = "read: arg0 == 1 |||| arg0 == 2\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1631,7 +1679,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP,
@@ -1678,7 +1726,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL,
@@ -1721,7 +1769,7 @@
struct sock_fprog actual;
std::string frequency = "@frequency ./path/is/ignored.frequency\n";
- FILE* policy_file = write_to_pipe(frequency);
+ FILE* policy_file = write_policy_to_pipe(frequency);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1734,7 +1782,7 @@
struct sock_fprog actual;
std::string invalid_token = "@unclude ./test/seccomp.policy\n";
- FILE* policy_file = write_to_pipe(invalid_token);
+ FILE* policy_file = write_policy_to_pipe(invalid_token);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1745,7 +1793,7 @@
struct sock_fprog actual;
std::string no_space = "@includetest/seccomp.policy\n";
- FILE* policy_file = write_to_pipe(no_space);
+ FILE* policy_file = write_policy_to_pipe(no_space);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1756,7 +1804,7 @@
struct sock_fprog actual;
std::string double_token = "@includeinclude ./test/seccomp.policy\n";
- FILE* policy_file = write_to_pipe(double_token);
+ FILE* policy_file = write_policy_to_pipe(double_token);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1767,7 +1815,7 @@
struct sock_fprog actual;
std::string no_file = "@include\n";
- FILE* policy_file = write_to_pipe(no_file);
+ FILE* policy_file = write_policy_to_pipe(no_file);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1778,7 +1826,7 @@
struct sock_fprog actual;
std::string space_no_file = "@include \n";
- FILE* policy_file = write_to_pipe(space_no_file);
+ FILE* policy_file = write_policy_to_pipe(space_no_file);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1789,7 +1837,7 @@
struct sock_fprog actual;
std::string implicit_relative_path = "@include test/seccomp.policy\n";
- FILE* policy_file = write_to_pipe(implicit_relative_path);
+ FILE* policy_file = write_policy_to_pipe(implicit_relative_path);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1800,7 +1848,7 @@
struct sock_fprog actual;
std::string extra_text = "@include /some/file: sneaky comment\n";
- FILE* policy_file = write_to_pipe(extra_text);
+ FILE* policy_file = write_policy_to_pipe(extra_text);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1811,7 +1859,7 @@
struct sock_fprog actual;
std::string split_filename = "@include /some/file:colon.policy\n";
- FILE* policy_file = write_to_pipe(split_filename);
+ FILE* policy_file = write_policy_to_pipe(split_filename);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
@@ -1822,7 +1870,7 @@
struct sock_fprog actual;
std::string include_policy = "@include ./nonexistent.policy\n";
- FILE* policy_file = write_to_pipe(include_policy);
+ FILE* policy_file = write_policy_to_pipe(include_policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1845,7 +1893,7 @@
"rt_sigreturn: 1\n"
"exit: 1\n";
- FILE* file_plain = write_to_pipe(policy_plain);
+ FILE* file_plain = write_policy_to_pipe(policy_plain);
ASSERT_NE(file_plain, nullptr);
int res_plain = test_compile_filter("policy", file_plain, &compiled_plain,
ACTION_RET_KILL);
@@ -1854,7 +1902,7 @@
std::string policy_with_include =
"@include " + source_path("test/seccomp.policy") + "\n";
- FILE* file_with_include = write_to_pipe(policy_with_include);
+ FILE* file_with_include = write_policy_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);
@@ -1901,7 +1949,7 @@
"exit: 1\n"
"@include " + source_path("test/seccomp.policy") + "\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1923,7 +1971,7 @@
"exit: 1\n"
"@include " + source_path("test/seccomp.policy") + "\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual,
@@ -1940,7 +1988,7 @@
"@include " + source_path("test/seccomp.policy") + "\n" +
"@include " + source_path("test/seccomp.policy") + "\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
@@ -1962,7 +2010,7 @@
"exit: 1\n"
"@include ./test/invalid_syscall_name.policy\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
/* Ensure the included (invalid) policy file exists. */
@@ -1981,7 +2029,7 @@
struct sock_fprog actual;
std::string policy = "@include ./test/nested.policy\n";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
/* Ensure the policy file exists. */
@@ -2003,7 +2051,7 @@
"read:&&\n"
"read:&&";
- FILE* policy_file = write_to_pipe(policy);
+ FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
int res = test_compile_filter("policy", policy_file, &actual);
fclose(policy_file);
diff --git a/system.c b/system.c
index 711e29e..10e1499 100644
--- a/system.c
+++ b/system.c
@@ -111,8 +111,7 @@
int write_proc_file(pid_t pid, const char *content, const char *basename)
{
- attribute_cleanup_fd int fd = -1;
- int ret;
+ int fd, ret;
size_t sz, len;
ssize_t written;
char filename[32];
@@ -141,6 +140,7 @@
warn("failed to write %zu bytes to '%s'", len, filename);
return -1;
}
+ close(fd);
return 0;
}
@@ -167,10 +167,8 @@
last_valid_cap--;
}
} else {
- static const char cap_file[] = "/proc/sys/kernel/cap_last_cap";
+ 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);
@@ -187,7 +185,7 @@
int config_net_loopback(void)
{
const char ifname[] = "lo";
- attribute_cleanup_fd int sock = -1;
+ int sock;
struct ifreq ifr;
/* Make sure people don't try to add really long names. */
@@ -216,6 +214,7 @@
return -1;
}
+ close(sock);
return 0;
}
@@ -230,7 +229,6 @@
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)) {
@@ -367,13 +365,13 @@
if (rc)
return rc;
if (!domkdir) {
- attribute_cleanup_fd int fd = open(
- dest, O_RDWR | O_CREAT | O_CLOEXEC, 0700);
+ int fd = open(dest, O_RDWR | O_CREAT | O_CLOEXEC, 0700);
if (fd < 0) {
rc = errno;
pwarn("open(%s) failed", dest);
return -rc;
}
+ close(fd);
}
if (chown(dest, uid, gid)) {
rc = errno;
@@ -500,10 +498,11 @@
return false;
}
- attribute_cleanup_str char *actions_avail = NULL;
+ char *actions_avail = NULL;
size_t buf_size = 0;
if (getline(&actions_avail, &buf_size, f) < 0) {
pwarn("getline() failed");
+ free(actions_avail);
return false;
}
@@ -513,7 +512,9 @@
* 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;
+ bool available = strstr(actions_avail, wanted) != NULL;
+ free(actions_avail);
+ return available;
}
int seccomp_ret_log_available(void)
diff --git a/test/invalid.conf b/test/invalid.conf
deleted file mode 100644
index d15d289..0000000
--- a/test/invalid.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-% minijail-config-file v0
-# Comments
-bad-key = whatever
diff --git a/test/valid.conf b/test/valid.conf
deleted file mode 100644
index a574e6c..0000000
--- a/test/valid.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-% minijail-config-file v0
-# Comments
-# enable mount namespace
-ns-mount
-# mounts and bind-mounts
-mount = none,/,none
-bind-mount = /,/
-mount-dev
diff --git a/test_util.cc b/test_util.cc
deleted file mode 100644
index cb751ff..0000000
--- a/test_util.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Copyright 2021 The Chromium OS Authors. All rights reserved.
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#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;
-}
diff --git a/test_util.h b/test_util.h
deleted file mode 100644
index e915086..0000000
--- a/test_util.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* test_util.h
- * Copyright 2021 The Chromium OS Authors. All rights reserved.
- * 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 <stdio.h>
-
-#include <memory>
-#include <string>
-
-#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);
- }
- }
-};
-
-} // namespace internal
-
-} // namespace mj
-
-using ScopedFILE = std::unique_ptr<FILE, mj::internal::ScopedFILECloser>;
-using ScopedConfigEntry =
- std::unique_ptr<config_entry, mj::internal::ScopedConfigEntryDeleter>;
-
-/*
- * 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_ */
diff --git a/tools/compile_seccomp_policy.py b/tools/compile_seccomp_policy.py
index 2219ae5..f2b714b 100755
--- a/tools/compile_seccomp_policy.py
+++ b/tools/compile_seccomp_policy.py
@@ -40,25 +40,6 @@
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 <stdint.h>
-
-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."""
@@ -70,10 +51,6 @@
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 '
@@ -86,22 +63,12 @@
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.')
+ help='The BPF program.',
+ type=argparse.FileType('wb'))
return arg_parser.parse_args(argv), arg_parser
@@ -117,21 +84,7 @@
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:
+ if opts.use_kill_process:
kill_action = bpf.KillProcess()
else:
kill_action = bpf.KillThread()
@@ -141,29 +94,14 @@
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') 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)
+ with opts.output as outf:
+ outf.write(
+ 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).opcodes)
return 0
diff --git a/tools/compiler.py b/tools/compiler.py
index f239740..161eadf 100644
--- a/tools/compiler.py
+++ b/tools/compiler.py
@@ -270,32 +270,24 @@
optimization_strategy,
kill_action,
include_depth_limit=10,
- override_default_action=None,
- denylist=False,
- ret_log=False):
+ override_default_action=None):
"""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)
+ override_default_action=override_default_action)
parsed_policy = policy_parser.parse_file(policy_filename)
entries = [
self.compile_filter_statement(
- filter_statement, kill_action=kill_action, denylist=denylist)
+ filter_statement, kill_action=kill_action)
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
+ 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,
@@ -312,11 +304,7 @@
bpf.ValidateArch(reject_action).accept(visitor)
return visitor.result
- def compile_filter_statement(self,
- filter_statement,
- *,
- kill_action,
- denylist=False):
+ def compile_filter_statement(self, filter_statement, *, kill_action):
"""Compile one parser.FilterStatement into BPF."""
policy_entry = SyscallPolicyEntry(filter_statement.syscall.name,
filter_statement.syscall.number,
@@ -326,7 +314,7 @@
# 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():
+ if 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
diff --git a/tools/parser.py b/tools/parser.py
index 87e1493..0db0f62 100644
--- a/tools/parser.py
+++ b/tools/parser.py
@@ -31,12 +31,6 @@
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'])
@@ -48,9 +42,8 @@
('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}'),
+ ('NUMERIC_CONSTANT', r'-?0[xX][0-9a-fA-F]+|-?0[Oo][0-7]+|-?[0-9]+'),
('COLON', r':'),
('SEMICOLON', r';'),
('COMMA', r','),
@@ -226,21 +219,14 @@
*,
kill_action,
include_depth_limit=10,
- override_default_action=None,
- denylist=False,
- ret_log=False):
+ override_default_action=None):
self._parser_states = [ParserState("<memory>")]
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._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):
@@ -255,21 +241,8 @@
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)
+ single_constant = int(token.value, base=0)
except ValueError:
self._parser_state.error('invalid constant', token=token)
else:
@@ -317,7 +290,7 @@
Constants can be:
- - A number that can be parsed with strtol() in C.
+ - A number that can be parsed with int(..., base=0)
- A named constant expression.
- A parenthesized, valid constant expression.
- A valid constant expression prefixed with the unary bitwise
@@ -433,11 +406,6 @@
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()
@@ -462,22 +430,17 @@
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 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.
+ # 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)
@@ -734,7 +697,6 @@
self._parser_states.append(ParserState(filename))
try:
statements = []
- denylist_header = False
with open(filename) as policy_file:
for tokens in self._parser_state.tokenize(policy_file):
if tokens[0].type == 'INCLUDE':
@@ -748,14 +710,6 @@
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:
@@ -767,9 +721,6 @@
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()
diff --git a/tools/parser_unittest.py b/tools/parser_unittest.py
index 9e7d6d8..1570e51 100755
--- a/tools/parser_unittest.py
+++ b/tools/parser_unittest.py
@@ -60,7 +60,7 @@
])
self.assertEqual(
[(token.type, token.value) for token in TokenizerTests._tokenize(
- 'read: arg0 in ~0xffff || arg0 & (1|2) && arg0 == 0755; '
+ 'read: arg0 in ~0xffff || arg0 & (1|2) && arg0 == 0o755; '
'return ENOSYS # ignored')], [
('IDENTIFIER', 'read'),
('COLON', ':'),
@@ -79,7 +79,7 @@
('AND', '&&'),
('ARGUMENT', 'arg0'),
('OP', '=='),
- ('NUMERIC_CONSTANT', '0755'),
+ ('NUMERIC_CONSTANT', '0o755'),
('SEMICOLON', ';'),
('RETURN', 'return'),
('IDENTIFIER', 'ENOSYS'),
@@ -276,27 +276,6 @@
parser.Atom(1, '==', 2)],
])
- def test_parse_number_argument_expression(self):
- """Accept valid 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,
@@ -416,29 +395,6 @@
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."""
@@ -912,112 +868,6 @@
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') 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()
diff --git a/tools/seccomp_policy_lint.py b/tools/seccomp_policy_lint.py
deleted file mode 100755
index f7621b0..0000000
--- a/tools/seccomp_policy_lint.py
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-"""A linter for the Minijail seccomp policy file."""
-
-import argparse
-import re
-import sys
-
-from typing import List, NamedTuple
-
-# The syscalls we have determined are more dangerous and need justification
-# for inclusion in a policy.
-DANGEROUS_SYSCALLS = (
- 'clone',
- 'mount',
- 'setns',
- 'kill',
- 'execve',
- 'execveat',
- 'bpf',
- 'socket',
- 'ptrace',
- 'swapon',
- 'swapoff',
- # TODO(b/193169195): Add argument granularity for the below syscalls.
- 'prctl',
- 'ioctl',
-# 'mmap',
-# 'mprotect',
-# 'mmap2',
-)
-
-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('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):
- """Fail if the seccomp policy file has dangerous, undocumented syscalls.
-
- Takes in a file object and a set of dangerous syscalls as arguments.
- """
-
- 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(fr'^\s*(\w*)\s*:', line)
- if match:
- syscall = match.group(1)
- if syscall in found_syscalls:
- errors.append(f'{check_file.name}, line {line_num}: repeat '
- f'syscall: {syscall}')
- else:
- found_syscalls.add(syscall)
- for dangerous in dangerous_syscalls:
- if dangerous == syscall:
- # Dangerous syscalls must be preceded with a
- # comment.
- contains_dangerous_syscall = True
- if not prev_line_comment:
- errors.append(f'{check_file.name}, line '
- f'{line_num}: {syscall} syscall '
- 'is a dangerous syscall so '
- '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: {check_file.name} contains dangerous syscalls, so'
- ' requires review from chromeos-security@')
- else:
- msg = (f'seccomp: {check_file.name} 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)
-
- check = check_seccomp_policy(opts.policy,
- set(opts.dangerous_syscalls.split(',')))
-
- 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:]))
diff --git a/tools/seccomp_policy_lint_unittest.py b/tools/seccomp_policy_lint_unittest.py
deleted file mode 100644
index 192739f..0000000
--- a/tools/seccomp_policy_lint_unittest.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-"""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()}, line 5: clone syscall is a dangerous '
- 'syscall so 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()}, line 5: repeat syscall: clone')])
-
- 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()
diff --git a/util.c b/util.c
index f715b19..0926866 100644
--- a/util.c
+++ b/util.c
@@ -35,37 +35,34 @@
*/
#if defined(__x86_64__)
#if defined(__ANDROID__)
-const char *const log_syscalls[] = {"socket", "connect", "fcntl", "writev"};
+const char *log_syscalls[] = {"socket", "connect", "fcntl", "writev"};
#else
-const char *const log_syscalls[] = {"socket", "connect", "sendto", "writev"};
+const char *log_syscalls[] = {"socket", "connect", "sendto", "writev"};
#endif
#elif defined(__i386__)
#if defined(__ANDROID__)
-const char *const log_syscalls[] = {"socketcall", "writev", "fcntl64",
- "clock_gettime"};
+const char *log_syscalls[] = {"socketcall", "writev", "fcntl64",
+ "clock_gettime"};
#else
-const char *const log_syscalls[] = {"socketcall", "time", "writev"};
+const char *log_syscalls[] = {"socketcall", "time", "writev"};
#endif
#elif defined(__arm__)
#if defined(__ANDROID__)
-const char *const log_syscalls[] = {"clock_gettime", "connect", "fcntl64",
- "socket", "writev"};
+const char *log_syscalls[] = {"clock_gettime", "connect", "fcntl64", "socket",
+ "writev"};
#else
-const char *const log_syscalls[] = {"socket", "connect", "gettimeofday", "send",
- "writev"};
+const char *log_syscalls[] = {"socket", "connect", "gettimeofday", "send",
+ "writev"};
#endif
#elif defined(__aarch64__)
#if defined(__ANDROID__)
-const char *const log_syscalls[] = {"connect", "fcntl", "sendto", "socket",
- "writev"};
+const char *log_syscalls[] = {"connect", "fcntl", "sendto", "socket", "writev"};
#else
-const char *const log_syscalls[] = {"socket", "connect", "send", "writev"};
+const char *log_syscalls[] = {"socket", "connect", "send", "writev"};
#endif
#elif defined(__powerpc__) || defined(__ia64__) || defined(__hppa__) || \
- defined(__sparc__) || defined(__mips__)
-const char *const log_syscalls[] = {"socket", "connect", "send"};
-#elif defined(__riscv)
-const char *const log_syscalls[] = {"socket", "connect", "sendto"};
+ defined(__sparc__) || defined(__mips__)
+const char *log_syscalls[] = {"socket", "connect", "send"};
#else
#error "Unsupported platform"
#endif
@@ -443,10 +440,16 @@
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;
+ char *path;
+ size_t pathlen;
+
+ /* One extra char for '/' and one for '\0', hence + 2. */
+ pathlen = strlen(external_path) + strlen(internal_path) + 2;
+ path = malloc(pathlen);
+ if (path)
+ snprintf(path, pathlen, "%s/%s", external_path, internal_path);
+
+ return path;
}
void *consumebytes(size_t length, char **buf, size_t *buflength)
@@ -511,46 +514,24 @@
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;
+ size_t name_len = strlen(name);
- /* 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 **dest = NULL;
+ size_t env_len = 0;
+ for (char **entry = *env; *entry; ++entry, ++env_len) {
+ if (!dest && strncmp(name, *entry, name_len) == 0 &&
+ (*entry)[name_len] == '=') {
+ if (!overwrite)
+ return 0;
+
+ dest = entry;
+ }
}
char *new_entry = NULL;
@@ -563,87 +544,15 @@
return 0;
}
- /* getenv_index has set |i| to the length of |env|. */
- ++i;
- char **new_env = realloc(*env, (i + 1) * sizeof(char *));
+ env_len++;
+ char **new_env = realloc(*env, (env_len + 1) * sizeof(char *));
if (!new_env) {
free(new_entry);
return ENOMEM;
}
- new_env[i - 1] = new_entry;
- new_env[i] = NULL;
+ new_env[env_len - 1] = new_entry;
+ new_env[env_len] = 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;
-}
diff --git a/util.h b/util.h
index 5ed9f94..359ce95 100644
--- a/util.h
+++ b/util.h
@@ -55,72 +55,6 @@
#define attribute_printf(format_idx, check_idx) \
__attribute__((__format__(__printf__, format_idx, check_idx)))
-#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__)
@@ -140,7 +74,7 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/* clang-format on */
-extern const char *const log_syscalls[];
+extern const char *log_syscalls[];
extern const size_t log_syscalls_len;
enum logging_system_t {
@@ -202,8 +136,7 @@
return compiled_with_asan() || &__asan_init != 0 || &__hwasan_init != 0;
}
-static inline bool debug_logging_allowed(void)
-{
+static inline bool debug_logging_allowed(void) {
#if defined(ALLOW_DEBUG_LOGGING)
return true;
#else
@@ -211,15 +144,6 @@
#endif
}
-static inline bool seccomp_default_ret_log(void)
-{
-#if defined(SECCOMP_DEFAULT_RET_LOG)
- return true;
-#else
- return false;
-#endif
-}
-
static inline size_t get_num_syscalls(void)
{
return syscall_table_size;
@@ -274,7 +198,7 @@
* @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.
+ priority parameter. Ignored unless @logger = LOG_TO_FD.
*/
void init_logging(enum logging_system_t logger, int fd, int min_priority);
@@ -318,45 +242,6 @@
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);
-
-/*
- * 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
diff --git a/util_unittest.cc b/util_unittest.cc
index b9e6dfc..35a99e5 100644
--- a/util_unittest.cc
+++ b/util_unittest.cc
@@ -14,7 +14,6 @@
#include <gtest/gtest.h>
#include "bpf.h"
-#include "test_util.h"
#include "util.h"
namespace {
@@ -158,42 +157,6 @@
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);
}
@@ -395,36 +358,3 @@
ASSERT_EQ(-EINVAL, parse_size(&size, "-1G"));
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(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);
-}