diff options
171 files changed, 2865 insertions, 2314 deletions
diff --git a/.github/workflows/build-test-ci.yml b/.github/workflows/build-test-ci.yml new file mode 100644 index 0000000..5c95baa --- /dev/null +++ b/.github/workflows/build-test-ci.yml @@ -0,0 +1,51 @@ +# GitHub actions workflow. +# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions + +name: Build+Test CI + +on: [pull_request, push] + +jobs: + glibc: + strategy: + matrix: + cc: [gcc, clang] + fail-fast: false + runs-on: ubuntu-latest + env: + CC: ${{ matrix.cc }} + steps: + - name: Install dependencies + run: | + sudo apt-get update -qq + sudo apt-get install build-essential gcc clang automake autoconf autoconf-archive libtool pax-utils -qy + + - uses: actions/checkout@v3 + name: Checkout + + - name: Build + run: | + ./autogen.sh + ./configure || { cat config.log; false; } + make V=1 + make V=1 check || { cat tests/testsuite.log; false; } + make V=1 distcheck + + musl: + runs-on: ubuntu-latest + container: + image: alpine:latest + options: --cap-add=SYS_PTRACE + steps: + - name: Install dependencies + run: apk add bash coreutils build-base automake autoconf autoconf-archive libtool pax-utils gawk sed + + - name: Checkout + uses: actions/checkout@v3 + + - name: Build + run: | + ./autogen.sh + ./configure || { cat config.log; false; } + make V=1 + make V=1 check || { cat tests/testsuite.log; false; } @@ -7,8 +7,8 @@ a.out .deps .libs .dirstamp -Makefile -Makefile.in +/Makefile +/Makefile.in f f2 @@ -42,7 +42,6 @@ core /ltmain.sh /missing /stamp-h1 -/test.sh /m4/libtool.m4 /m4/ltoptions.m4 @@ -1,8 +1,8 @@ Installation Instructions ************************* - Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software -Foundation, Inc. + Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright @@ -225,7 +225,7 @@ order to use an ANSI C compiler: and if that doesn't work, install pre-built binaries of GCC for HP-UX. - HP-UX 'make' updates targets which have the same time stamps as their + HP-UX 'make' updates targets which have the same timestamps as their prerequisites, which makes it generally unusable when shipped generated files such as 'configure' are involved. Use GNU 'make' instead. diff --git a/Makefile.am b/Makefile.am index 309a41e..07b1c6d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,40 +1,36 @@ ACLOCAL_AMFLAGS = -I m4 MAKEFLAGS = --no-print-directory -AM_CPPFLAGS = $(SANDBOX_DEFINES) - -SUBDIRS = \ - libsbutil \ - libsandbox \ - src \ - tests +AM_CPPFLAGS = \ + $(SANDBOX_DEFINES) \ + -I$(top_srcdir) confdir = $(sysconfdir) confddir = $(sysconfdir)/sandbox.d -pixmapdir = $(datadir)/pixmaps -desktopdir = $(datadir)/applications +bin_PROGRAMS = +check_PROGRAMS = +check_SCRIPTS = +dist_check_SCRIPTS = dist_conf_DATA = etc/sandbox.conf confd_DATA = etc/sandbox.d/00default dist_pkgdata_DATA = data/sandbox.bashrc -dist_pixmap_DATA = data/sandbox.svg -dist_desktop_DATA = data/sandbox.desktop +lib_LTLIBRARIES = noinst_LTLIBRARIES = -libsandbox: libsbutil -src: libsbutil -tests: src +CLEANFILES = +DISTCLEANFILES = EXTRA_DIST = \ headers.h \ localdecls.h \ ChangeLog.0 \ + data/sandbox.desktop \ + data/sandbox.svg \ etc/sandbox.d/00default.in \ scripts/gen_symbol_version_map.awk \ scripts/gen_symbol_header.awk \ scripts/gen_trace_header.awk -DISTCLEANFILES = $(CLEANFILES) - ChangeLog: touch ChangeLog @@ -50,11 +46,9 @@ dist-hook: fi ; \ fi -install-exec-hook: - set -e ; \ - for f in $(bindir)/sandbox $(libdir)/libsandbox.so ; do \ - sed -i.tmp \ - 's:__SANDBOX_TESTING:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:' \ - $(DESTDIR)$$f ; \ - rm -f $(DESTDIR)$$f.tmp ; \ - done +include libsandbox/local.mk +include libsbutil/local.mk +include src/local.mk +include tests/local.mk + +DISTCLEANFILES += $(CLEANFILES) @@ -1,28 +0,0 @@ ---------- - about ---------- - -Sandbox is a library (and helper utility) to run programs in a "sandboxed" -environment. This is used as a QA measure to try and prevent applications from -modifying files they should not. - -For example, in the Gentoo world we use it so we can build applications as root -and make sure that the build system does not do crazy things outside of its -build directory. Such as install files to the live root file system or modify -config files on the fly. - -For people who are familiar with the Debian "fakeroot" project or the RPM based -"InstallWatch", sandbox is in the same vein of projects. - ----------- - method ----------- - -The way sandbox works is that you prime a few environment variables (in order -to control the sandbox's behavior) and then stick it into the LD_PRELOAD -variable. Then when the ELF loader runs, it will first load the sandbox -library. Whenever an applications makes a library call that we have wrapped, -we'll check the arguments against the environment settings. Based on that, any -access that is not permitted is logged and we return an error to the -application. Any access that is permitted is of course forwarded along to the -real C library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..750c0fe --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# Sandbox + +Sandbox is a library (and helper utility) to run programs in a "sandboxed" +environment. This is used as a QA measure to try and prevent applications from +modifying files they should not. + +For example, in the Gentoo world we use it so we can build applications as root +and make sure that the build system does not do crazy things outside of its +build directory. Such as install files to the live root file system or modify +config files on the fly. + +For people who are familiar with the Debian "fakeroot" project or the RPM based +"InstallWatch", sandbox is in the same vein of projects. + +## Method + +The way sandbox works is that you prime a few environment variables (in order +to control the sandbox's behavior) and then stick it into the LD_PRELOAD +variable. Then when the ELF loader runs, it will first load the sandbox +library. Whenever an applications makes a library call that we have wrapped, +we'll check the arguments against the environment settings. Based on that, any +access that is not permitted is logged and we return an error to the +application. Any access that is permitted is of course forwarded along to the +real C library. + +Static ELFs and setuid/setgid programs are executed with +[ptrace()](https://man7.org/linux/man-pages/man2/ptrace.2.html) instead. + +## Availability + +Sandbox supports multiple monitoring methods, but not all are available in all +system configurations. + +### preload + +The in-process LD_PRELOAD method should be available on any reasonable ELF-based +system as long as it uses dynamic linking. Statically linked programs will run, +but will not be monitored, nor will set*id programs (because the C library will +clear LD_PRELOAD first). + +Multiple ABIs are supported (e.g. x86 32-bit & 64-bit). + +It has been tested & known to work with: +* Architecture + * They all should work! +* Operating system + * [Linux](https://kernel.org/) 2.4+ +* C library + * [GNU C library (glibc)](https://www.gnu.org/software/libc/) 2.2+ + * [uClibc](https://uclibc.org/) 0.9.26+ + * [musl](https://musl.libc.org/) 0.9.9+ + +### ptrace + +The out-of-process ptrace method is available on Linux systems, works with +dynamic & static linking, and supports set*id programs (by forcing them to run +without any elevated privileges). + +Multiple personalities are supported (e.g. PowerPC 32-bit & 64-bit). + +NB: Does not work in userland emulators (e.g. QEMU) which do not provide ptrace +emulation. + +It requires: +* Architecture + * Alpha + * ARM (32-bit EABI) + * Blackfin + * HPPA/PA-RISC (32-bit) + * Itanium + * PowerPC (32-bit & 64-bit) + * s390 (32-bit & 64-bit) + * SPARC (32-bit & 64-bit) + * x86 (32-bit & 64-bit & x32) +* Operating system + * [Linux](https://kernel.org/) 3.8+ +* C library + * They all should work! @@ -9,6 +9,26 @@ review erealpath vs realpath usage wrappers for execl{,l,p} ... unfortunately, we'll probably have to basically reimplement the functions (building up argv[] and then call the execv* ver) +wrappers for open funcs: + - freopen + - freopen64 + - name_to_handle_at + - open_by_handle_at + - __open + - __open64 + +wrappers for 64-bit time funcs +https://bugs.gentoo.org/751241 + +wrappers for syscalls that modify non-filesystem resources ? +how would we `addpredict` these ? +is it worth checking for these in the first place ? unittests sometimes do +terrible things to systems. + - clock_settime + - create_module + - setdomainname + - settimeofday + erealpath() might deref symlinks when working with unreadable paths as non-root even when working on funcs that do not deref funcs themselves ... this isnt a real big issue though @@ -26,8 +46,6 @@ handle multiple processing writing to log simultaneously doesnt seem to work quite right: echo $(./vfork-0 ./mkdir_static-0 2>&1) -handle env var modification inside of traced apps - messaging still needs a little work. consider: - user is running as root - user does `emerge foo` @@ -40,3 +58,6 @@ really only way around this would be to have sandbox set up a named pipe in $T and set the message path to that. then it would poll that for data and take care of writing it to its open stderr. + +sparc32 tracing under sparc64 doesn't work quite right. we need to reload the +syscall table after the exec call finishes. not sure any other port needs this. @@ -7,14 +7,21 @@ touch ChangeLog # whatever updated version is on the host rm -f m4/*.m4 -# not everyone has sys-devel/autoconf-archive installed +# not everyone has dev-build/autoconf-archive installed has() { [[ " ${*:2} " == *" $1 "* ]] ; } import_ax() { - local macro content m4 lm4s=() + local macro content m4 found lm4s=() content=$(sed -e '/^[[:space:]]*#/d' -e 's:\<dnl\>.*::' "$@") for macro in $(echo "${content}" | grep -o '\<AX[A-Z_]*\>' | sort -u) ; do - for m4 in $(grep -rl "\[${macro}\]" /usr/share/aclocal/) ; do - has ${m4} "${m4s[@]}" || lm4s+=( ${m4} ) + if ! found=$(grep -rl "AC_DEFUN(\[${macro}\]" /usr/share/aclocal/) ; then + echo "error: ${macro}: unable to locate m4 definition" + exit 1 + fi + for m4 in ${found} ; do + if ! has ${m4} "${m4s[@]}" "${lm4s[@]}" ; then + echo "$*: ${macro}: ${m4}" + lm4s+=( ${m4} ) + fi done done if [[ ${#lm4s[@]} -gt 0 ]] ; then diff --git a/configure.ac b/configure.ac index 8c1866c..7d6fce4 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.69]) -AC_INIT([sandbox], [2.15], [sandbox@gentoo.org]) -AM_INIT_AUTOMAKE([1.14 dist-xz no-dist-gzip silent-rules subdir-objects -Wall]) +AC_INIT([sandbox], [3.2], [sandbox@gentoo.org]) +AM_INIT_AUTOMAKE([1.15 dist-xz foreign no-dist-gzip silent-rules subdir-objects -Wall]) AM_SILENT_RULES([yes]) # AM_INIT_AUTOMAKE([silent-rules]) is broken atm AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) @@ -20,10 +20,18 @@ env 1>&AS_MESSAGE_LOG_FD AC_MSG_RESULT([ok]) dnl Check for compiler and features first. -AC_PROG_CC +AC_PROG_CC_C99 +AS_IF([test "$ac_cv_prog_cc_c99" = "no"], [AC_MSG_ERROR([A C99+ compiler is required])]) AM_PROG_CC_C_O AC_ISC_POSIX AC_USE_SYSTEM_EXTENSIONS +dnl http://www.gnu.org/s/libc/manual/html_node/Feature-Test-Macros.html +dnl _LARGEFILE_SOURCE: enable support for new LFS funcs (ftello/etc...) +dnl _LARGEFILE64_SOURCE: enable support for 64-bit variants (off64_t/fseeko64/etc...) +dnl NB: We do not want -D_FILE_OFFSET_BITS=64 because we need to interpose both 32-bit +dnl and 64-bit FS interfaces, and having the C library rewrite them makes that difficult. +dnl Along those lines, we do not use AC_SYS_LARGEFILE. +AS_VAR_APPEND([CPPFLAGS], [" -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE"]) dnl Checks for programs. AM_PROG_AR @@ -31,7 +39,7 @@ AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_AWK AC_PROG_EGREP -AC_CHECK_PROGS([READELF], [readelf eu-readelf], [false]) +AC_CHECK_TOOLS([READELF], [readelf eu-readelf], [false]) AM_MISSING_PROG([AUTOM4TE], [autom4te]) LT_INIT([disable-static]) @@ -40,12 +48,12 @@ AC_PREFIX_DEFAULT([/usr]) dnl multiple personality support (x86 & x86_64: multilib) AC_MSG_CHECKING([for multiple personalities]) -AC_ARG_ENABLE([schizo], - [AS_HELP_STRING([--enable-schizo],[Support multiple personalities])], - [],[enable_schizo="auto"]) -AC_MSG_RESULT([$enable_schizo]) -SB_SCHIZO_SETTINGS= -AC_DEFUN([SB_CHECK_SCHIZO],[dnl +AC_ARG_ENABLE([personalities], + [AS_HELP_STRING([--enable-personalities],[Support multiple Linux personalities using ptrace])], + [],[enable_personalities="auto"]) +AC_MSG_RESULT([$enable_personalities]) +SB_PERSONALITIES_SETTINGS= +AC_DEFUN([SB_CHECK_PERSONALITIES],[dnl AC_MSG_CHECKING([checking for $1/$2 compiler support]) ac_save_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $2" @@ -54,43 +62,46 @@ AC_DEFUN([SB_CHECK_SCHIZO],[dnl ], [ return 0 ], [ - enable_schizo=yes - AS_VAR_APPEND([SB_SCHIZO_SETTINGS], " $1:$2") - AS_VAR_APPEND([SB_SCHIZO_HEADERS], " trace_syscalls_$1.h") + enable_personalities=yes + AS_VAR_APPEND([SB_PERSONALITIES_SETTINGS], " $1:$2") + AS_VAR_APPEND([SB_PERSONALITIES_HEADERS], " libsandbox/trace_syscalls_$1.h") AC_MSG_RESULT([yes]) - AC_DEFINE_UNQUOTED([SB_SCHIZO_$1], 1, [Support for $1/$2 is available]) + AC_DEFINE_UNQUOTED([SB_PERSONALITIES_$1], 1, [Support for $1/$2 is available]) ], [ AC_MSG_RESULT([no]) ]) CFLAGS=$ac_save_CFLAGS ]) -if test "x$enable_schizo" != "xno" ; then - enable_schizo=no +if test "x$enable_personalities" != "xno" ; then + enable_personalities=no case $host in i686*linux*|\ x86_64*linux*) - SB_CHECK_SCHIZO([x86_64], [-m64]) - SB_CHECK_SCHIZO([x86], [-m32]) - SB_CHECK_SCHIZO([x32], [-mx32]) + SB_CHECK_PERSONALITIES([x86_64], [-m64]) + SB_CHECK_PERSONALITIES([x86], [-m32]) + SB_CHECK_PERSONALITIES([x32], [-mx32]) ;; s390*linux*) - SB_CHECK_SCHIZO([s390x], [-m64]) - SB_CHECK_SCHIZO([s390], [-m31]) + SB_CHECK_PERSONALITIES([s390x], [-m64]) + SB_CHECK_PERSONALITIES([s390], [-m31]) + ;; + sparc*linux*) + SB_CHECK_PERSONALITIES([sparc64], [-m64]) + SB_CHECK_PERSONALITIES([sparc], [-m32]) ;; esac - SB_SCHIZO_SETTINGS=${SB_SCHIZO_SETTINGS# } - if test "x$enable_schizo" != "xno" ; then - AC_DEFINE_UNQUOTED([SB_SCHIZO], ["$SB_SCHIZO_SETTINGS"], [Enable multiple personalities support]) + SB_PERSONALITIES_SETTINGS=${SB_PERSONALITIES_SETTINGS# } + if test "x$enable_personalities" != "xno" ; then + AC_DEFINE_UNQUOTED([SB_PERSONALITIES], ["$SB_PERSONALITIES_SETTINGS"], [Enable multiple personalities support]) fi fi -AC_SUBST(SB_SCHIZO_SETTINGS) -AC_SUBST(SB_SCHIZO_HEADERS) -AM_CONDITIONAL([SB_SCHIZO], [test "x$enable_schizo" != "xno"]) +AC_SUBST(SB_PERSONALITIES_SETTINGS) +AC_SUBST(SB_PERSONALITIES_HEADERS) +AM_CONDITIONAL([SB_PERSONALITIES], [test "x$enable_personalities" != "xno"]) dnl this test fills up the stack and then triggers a segfault ... dnl but it's hard to wrap things without a stack, so let's ignore dnl this test for now ... -ac_cv_header_sigsegv_h=no ac_cv_lib_sigsegv_stackoverflow_install_handler=false dnl Checks for libraries. @@ -134,6 +145,7 @@ AC_CHECK_HEADERS_ONCE(m4_flatten([ sys/mman.h sys/mount.h sys/param.h + sys/prctl.h sys/ptrace.h sys/reg.h sys/socket.h @@ -144,6 +156,7 @@ AC_CHECK_HEADERS_ONCE(m4_flatten([ sys/uio.h sys/user.h sys/wait.h + sys/xattr.h asm/ptrace.h linux/ptrace.h ])) @@ -160,6 +173,7 @@ AC_CHECK_TYPES([sighandler_t, sig_t, __sighandler_t],,,[#include <signal.h>]) save_CPPFLAGS=$CPPFLAGS CPPFLAGS="-I$srcdir $CPPFLAGS" +AC_CHECK_TYPES([struct ptrace_syscall_info],,,[#include "headers.h"]) AC_CHECK_TYPES([struct user_regs_struct, struct pt_regs],,,[#include "headers.h"]) AC_CHECK_SIZEOF([struct user_regs_struct],,[#include "headers.h"]) AC_CHECK_SIZEOF([struct pt_regs],,[#include "headers.h"]) @@ -186,6 +200,8 @@ AC_CHECK_FUNCS_ONCE(m4_flatten([ getcwd lchown linkat + lremovexattr + lsetxattr lutimes memmove memcpy @@ -207,13 +223,17 @@ AC_CHECK_FUNCS_ONCE(m4_flatten([ openat openat64 pathconf + prctl process_vm_readv ptrace realpath remove + removexattr renameat + renameat2 rmdir setenv + setxattr strcasecmp strchr strdup @@ -274,18 +294,18 @@ if test x"$va_copy" != xva_copy ; then ) fi -dnl Verify people aren't doing stupid shit +dnl Avoid footguns. if test x"$enable_static" != xno ; then - AC_MSG_ERROR([dont be a Kumba, building a libsandbox.a is stupid]) + AC_MSG_ERROR([Building a static libsandbox.a is not supported]) fi if test x"$enable_shared" != xyes ; then - AC_MSG_ERROR([dont be a Kumba, omitting a libsandbox.so is stupid]) + AC_MSG_ERROR([Omitting a libsandbox.so is not supported]) fi if echo " $CFLAGS " | $EGREP ' -static ' >/dev/null 2>&1; then - AC_MSG_ERROR([dont be a Kumba, using -static in CFLAGS is stupid]) + AC_MSG_ERROR([Using -static in CFLAGS is not supported]) fi if echo " $LDFLAGS " | $EGREP ' -static ' >/dev/null 2>&1; then - AC_MSG_ERROR([dont be a Kumba, using -static in LDFLAGS is stupid]) + AC_MSG_ERROR([Using -static in LDFLAGS is not supported]) fi dnl Some libc's like those on bsd have dlopen() in libc, and not libdl @@ -295,18 +315,6 @@ dnl uClibc doesn't currently provide dlvsym() so lets dnl verify the toolchain supports it AC_CHECK_FUNCS([dlvsym]) -dnl when using libc5, (f)trucate's offset argument type is size_t with -dnl libc5, but it's off_t with libc6 (glibc2). -AC_MSG_CHECKING([truncate argument type]) -AC_EGREP_HEADER([truncate.*size_t], [unistd.h], - [dnl - AC_MSG_RESULT([size_t]) - AC_DEFINE([TRUNCATE_T], [size_t], [truncate arg type]) - ],[dnl - AC_MSG_RESULT([off_t]) - AC_DEFINE([TRUNCATE_T], [off_t], [truncate arg type]) -]) - dnl Check if libc provides RTLD_NEXT AC_MSG_CHECKING([for RTLD_NEXT]) AC_TRY_COMPILE([ @@ -324,7 +332,7 @@ if test x"$have_rtld_next" = xyes ; then AC_DEFINE([HAVE_RTLD_NEXT], [1], [Have RTLD_NEXT enabled libc]) fi -dnl we need to handle symbols differently based upon their version, +dnl we need to handle symbols differently based upon their version, dnl but we have to know which symbols the libc supports first AC_MSG_CHECKING([libc path]) echo "int main(void) { return 0; }" > libctest.c @@ -344,9 +352,12 @@ try_link() { ) 1>&AS_MESSAGE_LOG_FD } LIBC_PATH=$(AS_IF( - dnl GNU linker (bfd & gold) - [try_link -Wl,--verbose], - [$AWK '/ttempt to open/ { if (($(NF-1) ~ /\/libc\.so/) && ($NF == "succeeded")) LIBC = $(NF-1); }; END {print LIBC}' libctest.log], + dnl GNU linkers (bfd, gold), LLVM lld, mold - searching for latest entry of: + dnl "/usr/lib/gcc/x86_64-pc-linux-gnu/8.3.0/../../../../lib64/libc.so" + dnl "/lib64/libc.so.6" + dnl Note that mold prefixes output with "trace: " whereas others do not. + [try_link -Wl,--trace], + [$EGREP -o '/[[^ ]]*/libc.so.*' libctest.log | tail -n1], dnl Solaris linker [try_link -Wl,-m], [set -- `$EGREP -o '/[[^ ]]*/libc.so' libctest.log`; echo $1] @@ -359,7 +370,7 @@ AC_DEFINE_UNQUOTED([LIBC_PATH], ["$LIBC_PATH"], [Full path to the libc]) AC_MSG_RESULT([$LIBC_PATH]) AC_SUBST([LIBC_PATH]) -dnl when intercepting libc calls, we have to know the name of the +dnl when intercepting libc calls, we have to know the name of the dnl libc to load and search with dl*() calls AC_MSG_CHECKING([libc version]) dnl the sed script at the end here looks funny but it's ok ... @@ -367,7 +378,7 @@ echo "int main(void) { return 0; }" > libctest.c $CC $CFLAGS $CPPFLAGS $LDFLAGS -o libctest libctest.c LIBC_VERSION=$( $READELF -d libctest | \ - $EGREP 'NEEDED.* \@<:@libc\.so' | \ + $EGREP 'NEEDED.* \@<:@libc\..*so' | \ $AWK '{print $NF}' | [sed -e 's:\[::' -e 's:\]::'] ) rm -f libctest* @@ -457,11 +468,7 @@ AC_CONFIG_FILES([src/sandbox.sh], [chmod +x src/sandbox.sh]) AC_CONFIG_FILES([ Makefile etc/sandbox.d/00default - libsandbox/Makefile - libsbutil/Makefile - src/Makefile tests/atlocal - tests/Makefile tests/package.m4 ]) AC_OUTPUT diff --git a/data/sandbox.bashrc b/data/sandbox.bashrc index 7601d35..3b953b8 100644 --- a/data/sandbox.bashrc +++ b/data/sandbox.bashrc @@ -9,7 +9,6 @@ fi export BASH_ENV="${SANDBOX_BASHRC}" -alias make="make LD_PRELOAD=${LD_PRELOAD}" alias su="su -c '/bin/bash -rcfile ${SANDBOX_BASHRC}'" declare -r SANDBOX_ACTIVE @@ -29,8 +28,8 @@ if [[ ${SANDBOX_INTRACTV} == "1" && -t 1 ]] || [[ ${__SANDBOX_TESTING} == "yes" ( [[ ${NOCOLOR} == "true" || ${NOCOLOR} == "yes" || ${NOCOLOR} == "1" ]] && \ export RC_NOCOLOR="yes" - source /etc/init.d/functions.sh - if [ $? -ne 0 ] ; then + source /lib/gentoo/functions.sh + if [[ $? -ne 0 ]] ; then einfo() { echo " INFO: $*"; } ewarn() { echo " WARN: $*"; } eerror() { echo " ERR: $*"; } @@ -71,7 +70,7 @@ if [[ ${SANDBOX_INTRACTV} == "1" && -t 1 ]] || [[ ${__SANDBOX_TESTING} == "yes" sbs_tmpenvfile=${sbs_pdir}${sbs_bdir}/temp/environment if [[ -e ${sbs_tmpenvfile} ]] ; then echo "Found environment at ${sbs_tmpenvfile}" - printf " * Would you like to enter the portage environment ? " + printf " * Would you like to enter the portage environment (y/N) ? " read env sbs_PREPWD=${PWD} if [[ ${env} == "y" ]] ; then @@ -80,7 +79,11 @@ if [[ ${SANDBOX_INTRACTV} == "1" && -t 1 ]] || [[ ${__SANDBOX_TESTING} == "yes" -e '/^[[:alnum:]_-]* ()/Q' "${sbs_tmpenvfile}") 2>/dev/null # Then grab everything (including functions) source "${sbs_tmpenvfile}" 2> /dev/null - export SANDBOX_WRITE=${SANDBOX_WRITE}:${sbs_pdir}${sbs_bdir}:${sbs_pdir}/homedir + # Some variables portage does not write out to th environment. + : "${T:=${sbs_tmpenvfile%/*}}" + HOME=${sbs_pdir}/homedir + SANDBOX_WRITE+=:${sbs_pdir}${sbs_bdir}:${HOME} + export T HOME SANDBOX_WRITE fi PWD=${sbs_PREPWD} fi @@ -103,7 +106,7 @@ if [[ ${SANDBOX_INTRACTV} == "1" && -t 1 ]] || [[ ${__SANDBOX_TESTING} == "yes" sandboxon() { export SANDBOX_ON="1" ; } sandboxoff() { export SANDBOX_ON="0" ; } - [[ -z ${CCACHE_DIR} ]] && [[ -w /root/.ccache ]] && export CCACHE_DIR=/root/.ccache + [[ -z ${CCACHE_DIR} ]] && [[ -w $HOME/.ccache ]] && export CCACHE_DIR=$HOME/.ccache for var in CCACHE_DIR DISTCC_DIR ; do [[ ${!var+set} == "set" ]] && addwrite ${!var} done diff --git a/etc/sandbox.conf b/etc/sandbox.conf index 5f09ee4..d8a6550 100644 --- a/etc/sandbox.conf +++ b/etc/sandbox.conf @@ -27,6 +27,17 @@ # Determine the use of color in the output. Default is "false" (ie, use color) #NOCOLOR="false" +# SANDBOX_METHOD +# +# Control how processes are monitored. See the README for system requirements +# for each setting, as well as particular limitations. Changing this setting +# is not recommended. +# +# Possible values: +# any: (default) Use any method of tracing available on the system. +# preload: Only use in-process LD_PRELOAD symbol interposing. +#SANDBOX_METHOD="any" + # # Namespace Section (Linux-only) @@ -39,11 +50,13 @@ # particular type, it will be automatically skipped. Default to off as these # are currently experimental. # For more details on each type, see the namespaces(7) manpage. +#NAMESPACE_CGROUP_ENABLE="no" #NAMESPACE_IPC_ENABLE="no" #NAMESPACE_MNT_ENABLE="no" #NAMESPACE_NET_ENABLE="no" #NAMESPACE_PID_ENABLE="no" #NAMESPACE_SYSV_ENABLE="no" +#NAMESPACE_TIME_ENABLE="no" #NAMESPACE_USER_ENABLE="no" #NAMESPACE_UTS_ENABLE="no" @@ -86,7 +99,7 @@ SANDBOX_WRITE="/dev/console:/dev/tty:/dev/vc/:/dev/pty:/dev/tts" # Device filesystems SANDBOX_WRITE="/dev/ptmx:/dev/pts/:/dev/shm" # Tempory storage -SANDBOX_WRITE="/tmp/:/var/tmp/" +SANDBOX_WRITE="/tmp/:/var/tmp/:/usr/tmp/" # Needed for shells SANDBOX_WRITE="${HOME}/.bash_history" @@ -113,6 +113,9 @@ #ifdef HAVE_SYS_PARAM_H # include <sys/param.h> #endif +#ifdef HAVE_SYS_PRCTL_H +# include <sys/prctl.h> +#endif #ifdef HAVE_SYS_PTRACE_H # include <sys/ptrace.h> #endif @@ -143,6 +146,9 @@ #ifdef HAVE_SYS_WAIT_H # include <sys/wait.h> #endif +#ifdef HAVE_SYS_XATTR_H +# include <sys/xattr.h> +#endif #ifdef __ia64__ /* what a pos */ # define ia64_fpreg FU_ia64_fpreg diff --git a/libsandbox/Makefile b/libsandbox/Makefile new file mode 100644 index 0000000..2db82ff --- /dev/null +++ b/libsandbox/Makefile @@ -0,0 +1,4 @@ +# Helper for developers. +all libsandbox libsandbox.la: libsandbox/libsandbox.la ; +clean: ; rm -f *.o *.l[ao] .libs/* +%: ; $(MAKE) -C .. $@ diff --git a/libsandbox/Makefile.am b/libsandbox/Makefile.am deleted file mode 100644 index ac9a548..0000000 --- a/libsandbox/Makefile.am +++ /dev/null @@ -1,93 +0,0 @@ -AUTOMAKE_OPTIONS = foreign - -lib_LTLIBRARIES = libsandbox.la - -AM_CPPFLAGS = \ - -I$(top_srcdir) \ - -I$(top_srcdir)/libsbutil \ - -I$(top_srcdir)/libsbutil/include \ - $(SANDBOX_DEFINES) - -libsandbox_la_CFLAGS = $(CFLAG_EXCEPTIONS) -# Could use the following to libsandbox_la_LIBADD, but then libtool links it -# with --whole-archive, and libsandbox.so increase with a few KB in size: -# $(top_builddir)/libsbutil/libsbutil.la -libsandbox_la_LIBSBLIB = $(top_builddir)/libsbutil/.libs/libsbutil.a -libsandbox_la_LIBADD = \ - -lc $(LIBDL) \ - $(libsandbox_la_LIBSBLIB) -# Do not add -nostdlib or -nostartfiles, as then our constructor -# and destructor will not be executed ... -libsandbox_la_LDFLAGS = \ - -no-undefined \ - -avoid-version \ - $(LDFLAG_VER),libsandbox.map -libsandbox_la_SOURCES = \ - libsandbox.h \ - libsandbox.c \ - lock.c \ - memory.c \ - trace.c \ - wrappers.h \ - wrappers.c \ - canonicalize.c - -install-exec-hook: - rm -f $(DESTDIR)$(libdir)/libsandbox.la -# Since we removed the .la file, libtool uninstall doesn't work, -# so we have to manually uninstall libsandbox.so ourselves. -uninstall-hook: - rm -f $(DESTDIR)$(libdir)/libsandbox.so - -libsandbox.c: libsandbox.map sb_nr.h -trace.c: trace_syscalls.h sb_nr.h $(TRACE_FILES) -wrappers.c: symbols.h - -TRACE_FILES = $(wildcard $(srcdir)/trace/*.[ch] $(srcdir)/trace/*/*.[ch]) - -SCRIPT_DIR = $(top_srcdir)/scripts - -SYMBOLS_FILE = $(srcdir)/symbols.h.in -SYMBOLS_LIST = $(shell $(SED) -n '/^[^\#]/p' $(SYMBOLS_FILE)) -SYMBOLS_WRAPPERS = $(wildcard $(srcdir)/wrapper-funcs/*.[ch]) -GEN_VERSION_MAP_SCRIPT = $(SCRIPT_DIR)/gen_symbol_version_map.awk -GEN_HEADER_SCRIPT = $(SCRIPT_DIR)/gen_symbol_header.awk -GEN_TRACE_SCRIPT = $(SCRIPT_DIR)/gen_trace_header.awk -SB_AWK = LC_ALL=C $(AWK) -v SYMBOLS_LIST="$(SYMBOLS_LIST)" -v srcdir="$(srcdir)" -f - -libsandbox.map: $(SYMBOLS_FILE) $(GEN_VERSION_MAP_SCRIPT) - $(AM_V_GEN)$(READELF) -s $(LIBC_PATH) | $(SB_AWK) $(GEN_VERSION_MAP_SCRIPT) > $@ - -symbols.h: $(SYMBOLS_FILE) $(GEN_HEADER_SCRIPT) - $(AM_V_GEN)$(READELF) -s $(LIBC_PATH) | $(SB_AWK) $(GEN_HEADER_SCRIPT) > $@ - -SB_NR_FILE = $(srcdir)/sb_nr.h.in -sb_nr.h: symbols.h $(SB_NR_FILE) - $(AM_V_GEN)$(EGREP) -h '^\#define SB_' $^ > $@ - -TRACE_MAKE_HEADER = \ - $(SB_AWK) $(GEN_TRACE_SCRIPT) -v MODE=gen | \ - $(COMPILE) -E -P -include $(top_srcdir)/headers.h - $$f | \ - $(SB_AWK) $(GEN_TRACE_SCRIPT) -v syscall_prefix=$$t > $$header -trace_syscalls.h: $(GEN_TRACE_SCRIPT) $(SB_SCHIZO_HEADERS) -if SB_SCHIZO - $(AM_V_GEN)touch $@ -else - $(AM_V_GEN)t= f= header=$@; $(TRACE_MAKE_HEADER) -endif - -$(SB_SCHIZO_HEADERS): $(GEN_TRACE_SCRIPT) - $(AM_V_GEN)for pers in $(SB_SCHIZO_SETTINGS) ; do \ - t=_$${pers%:*}; \ - f=$${pers#*:}; \ - header="trace_syscalls$${t}.h"; \ - if [ "$$header" = "$@" ]; then \ - $(TRACE_MAKE_HEADER) || exit $$?; \ - break; \ - fi; \ - done - -EXTRA_DIST = $(SYMBOLS_FILE) $(SYMBOLS_WRAPPERS) $(SB_NR_FILE) $(TRACE_FILES) headers.h - -CLEANFILES = libsandbox.map sb_nr.h symbols.h trace_syscalls*.h -DISTCLEANFILES = $(CLEANFILES) diff --git a/libsandbox/canonicalize.c b/libsandbox/canonicalize.c index 6519340..f8d32f0 100644 --- a/libsandbox/canonicalize.c +++ b/libsandbox/canonicalize.c @@ -49,7 +49,6 @@ erealpath(const char *name, char *resolved) { char *rpath, *dest, *recover; const char *start, *end, *rpath_limit; - long int path_max; if (name == NULL) { /* As per Single Unix Specification V2 we must return an error if @@ -66,16 +65,9 @@ erealpath(const char *name, char *resolved) __set_errno(ENOENT); return NULL; } -#ifdef SB_PATH_MAX - path_max = SB_PATH_MAX; -#else - path_max = pathconf(name, _PC_PATH_MAX); - if (path_max <= 0) - path_max = 1024; -#endif if (resolved == NULL) { - rpath = xmalloc(path_max); + rpath = xmalloc(SB_PATH_MAX); } else { /* We can't handle resolving a buffer inline, so demand * separate read and write strings. @@ -83,16 +75,16 @@ erealpath(const char *name, char *resolved) sb_assert(name != resolved); rpath = resolved; } - rpath_limit = rpath + path_max; + rpath_limit = rpath + SB_PATH_MAX; recover = NULL; if (name[0] != '/') { - if (!egetcwd(rpath, path_max)) { + if (!egetcwd(rpath, SB_PATH_MAX)) { rpath[0] = '\0'; goto error; } - /* This stat() business uses relative paths atm */ + /* This stat business uses relative paths atm. */ if (trace_pid) goto no_recover; @@ -100,26 +92,28 @@ erealpath(const char *name, char *resolved) * If not, try a little harder to consume this path in * case it has symlinks out into a better world ... */ - struct stat st; - if (lstat(rpath, &st) == -1 && errno == EACCES) { + struct stat64 st; + if (lstat64(rpath, &st) == -1 && errno == EACCES) { char *p = rpath; strcpy(rpath, name); do { p = strchr(p, '/'); if (p) *p = '\0'; - if (lstat(rpath, &st)) + if (lstat64(rpath, &st)) break; if (S_ISLNK(st.st_mode)) { - ssize_t cnt = readlink(rpath, rpath, path_max); + char buffer[SB_PATH_MAX]; + ssize_t cnt = readlink(rpath, buffer, SB_PATH_MAX - 1); if (cnt == -1) break; - rpath[cnt] = '\0'; + buffer[cnt] = '\0'; + strcpy(rpath, buffer); if (p) { size_t bytes_left = strlen(p); - if (bytes_left >= path_max) + if (bytes_left >= SB_PATH_MAX) break; strncat(rpath, name + (p - rpath + 1), - path_max - bytes_left - 1); + SB_PATH_MAX - bytes_left - 1); } /* Ok, we have a chance at something better. If @@ -187,10 +181,10 @@ erealpath(const char *name, char *resolved) goto error; } new_size = rpath_limit - rpath; - if (end - start + 1 > path_max) + if (end - start + 1 > SB_PATH_MAX) new_size += end - start + 1; else - new_size += path_max; + new_size += SB_PATH_MAX; new_rpath = (char *) xrealloc(rpath, new_size); rpath = new_rpath; rpath_limit = rpath + new_size; @@ -213,7 +207,7 @@ erealpath(const char *name, char *resolved) error: if (resolved) - snprintf(resolved, path_max, "%s", rpath); + snprintf(resolved, SB_PATH_MAX, "%s", rpath); else free(rpath); free(recover); diff --git a/libsandbox/headers.h b/libsandbox/headers.h deleted file mode 100644 index 7fcc3b2..0000000 --- a/libsandbox/headers.h +++ /dev/null @@ -1 +0,0 @@ -#include "../headers.h" diff --git a/libsandbox/libsandbox.c b/libsandbox/libsandbox.c index 77e9959..acd8585 100644 --- a/libsandbox/libsandbox.c +++ b/libsandbox/libsandbox.c @@ -29,6 +29,7 @@ char sandbox_lib[SB_PATH_MAX]; typedef struct { bool show_access_violation, on, active, testing, verbose, debug; + sandbox_method_t method; char *ld_library_path; char **prefixes[5]; int num_prefixes[5]; @@ -53,6 +54,7 @@ static char message_path[SB_PATH_MAX]; bool sandbox_on = true; static bool sb_init = false; static bool sb_env_init = false; +int (*sbio_faccessat)(int, const char *, int, int) = sb_unwrapped_faccessat; int (*sbio_open)(const char *, int, mode_t) = sb_unwrapped_open; FILE *(*sbio_popen)(const char *, const char *) = sb_unwrapped_popen; @@ -94,6 +96,7 @@ void libsb_init(void) sbcontext.verbose = is_env_on(ENV_SANDBOX_VERBOSE); sbcontext.debug = is_env_on(ENV_SANDBOX_DEBUG); sbcontext.testing = is_env_on(ENV_SANDBOX_TESTING); + sbcontext.method = get_sandbox_method(); if (sbcontext.testing) { const char *ldpath = getenv("LD_LIBRARY_PATH"); if (ldpath) @@ -101,6 +104,11 @@ void libsb_init(void) } } +sandbox_method_t get_sandbox_method(void) +{ + return parse_sandbox_method(getenv(ENV_SANDBOX_METHOD)); +} + /* resolve_dirfd_path - get the path relative to a dirfd * * return value: @@ -124,26 +132,31 @@ int resolve_dirfd_path(int dirfd, const char *path, char *resolved_path, save_errno(); + char *fd_path = xmalloc(SB_PATH_MAX * sizeof(char)); + size_t at_len = resolved_path_len - 1 - 1 - (path ? strlen(path) : 0); - if (trace_pid) - sprintf(resolved_path, "/proc/%i/fd/%i", trace_pid, dirfd); - else - /* If /proc was mounted by a process in a different pid namespace, - * getpid cannot be used to create a valid /proc/<pid> path. Instead - * use sb_get_fd_dir() which works in any case. - */ - sprintf(resolved_path, "%s/%i", sb_get_fd_dir(), dirfd); - ssize_t ret = readlink(resolved_path, resolved_path, at_len); + if (trace_pid) { + sprintf(fd_path, "/proc/%i/fd/%i", trace_pid, dirfd); + } else { + /* If /proc was mounted by a process in a different pid namespace, + * getpid cannot be used to create a valid /proc/<pid> path. Instead + * use sb_get_fd_dir() which works in any case. + */ + sprintf(fd_path, "%s/%i", sb_get_fd_dir(), dirfd); + } + ssize_t ret = readlink(fd_path, resolved_path, at_len); if (ret == -1) { /* see comments at end of check_syscall() */ if (errno_is_too_long()) { restore_errno(); + free(fd_path); return 2; } - sb_debug_dyn("AT_FD LOOKUP fail: %s: %s\n", resolved_path, strerror(errno)); + sb_debug_dyn("AT_FD LOOKUP fail: %s: %s\n", fd_path, strerror(errno)); /* If the fd isn't found, some guys (glibc) expect errno */ if (errno == ENOENT) errno = EBADF; + free(fd_path); return -1; } resolved_path[ret] = '/'; @@ -152,6 +165,7 @@ int resolve_dirfd_path(int dirfd, const char *path, char *resolved_path, strcat(resolved_path, path); restore_errno(); + free(fd_path); return 0; } @@ -276,7 +290,7 @@ static char *resolve_path(const char *path, int follow_link) } if (!ret) { - char tmp_str1[SB_PATH_MAX]; + char *tmp_str1 = xmalloc(SB_PATH_MAX * sizeof(char)); snprintf(tmp_str1, SB_PATH_MAX, "%s", path); dname = dirname(tmp_str1); @@ -294,7 +308,7 @@ static char *resolve_path(const char *path, int follow_link) filtered_path = NULL; } } else { - char tmp_str2[SB_PATH_MAX]; + char *tmp_str2 = xmalloc(SB_PATH_MAX * sizeof(char)); /* OK, now add the basename to keep our access * checking happy (don't want '/usr/lib' if we * tried to do something with non-existing @@ -306,7 +320,10 @@ static char *resolve_path(const char *path, int follow_link) snprintf(filtered_path + len, SB_PATH_MAX - len, "%s%s", (filtered_path[len - 1] != '/') ? "/" : "", bname); + free(tmp_str2); } + + free(tmp_str1); } } @@ -325,7 +342,7 @@ static char *resolve_path(const char *path, int follow_link) char *egetcwd(char *buf, size_t size) { - struct stat st; + struct stat64 st; char *tmpbuf; /* We can't let the C lib allocate memory for us since we have our @@ -339,14 +356,14 @@ char *egetcwd(char *buf, size_t size) /* If tracing a child, our cwd may not be the same as the child's */ if (trace_pid) { - char proc[20]; - sprintf(proc, "/proc/%i/cwd", trace_pid); - ssize_t ret = readlink(proc, buf, size); - if (ret == -1) { + char proc[22]; + snprintf(proc, sizeof(proc), "/proc/%i/cwd", trace_pid); + ssize_t link_len = readlink(proc, buf, size - 1); + if (link_len == -1) { errno = ESRCH; return NULL; } - buf[ret] = '\0'; + buf[link_len] = '\0'; return buf; } @@ -368,12 +385,12 @@ char *egetcwd(char *buf, size_t size) */ if ((tmpbuf) && (errno == 0)) { save_errno(); - if (!lstat(buf, &st)) + if (!lstat64(buf, &st)) /* errno is set only on failure */ errno = 0; if (errno == ENOENT) - /* If lstat() failed with eerror = ENOENT, then its + /* If lstat failed with eerror = ENOENT, then its * possible that we are running on an older kernel * which had issues with returning invalid paths if * they got too long. Return with errno = ENAMETOOLONG, @@ -388,8 +405,8 @@ char *egetcwd(char *buf, size_t size) free(buf); /* Not sure if we should quit here, but I guess if - * lstat() fails, getcwd could have messed up. Not - * sure what to do about errno - use lstat()'s for + * lstat fails, getcwd could have messed up. Not + * sure what to do about errno - use lstat's for * now. */ return NULL; @@ -427,12 +444,12 @@ void __sb_dump_backtrace(void) static bool write_logfile(const char *logfile, const char *func, const char *path, const char *apath, const char *rpath, bool access) { - struct stat log_stat; + struct stat64 log_stat; int stat_ret; int logfd; bool ret = false; - stat_ret = lstat(logfile, &log_stat); + stat_ret = lstat64(logfile, &log_stat); /* Do not care about failure */ errno = 0; if (stat_ret == 0 && S_ISREG(log_stat.st_mode) == 0) @@ -663,33 +680,32 @@ static int check_prefixes(char **prefixes, int num_prefixes, const char *path) } /* Is this a func that works on symlinks, and is the file a symlink ? */ -static bool symlink_func(int sb_nr, int flags, const char *abs_path) +static bool symlink_func(int sb_nr, int flags) { - struct stat st; - /* These funcs always operate on symlinks */ - if (!(sb_nr == SB_NR_UNLINK || - sb_nr == SB_NR_UNLINKAT || - sb_nr == SB_NR_LCHOWN || - sb_nr == SB_NR_REMOVE || - sb_nr == SB_NR_RENAME || - sb_nr == SB_NR_RENAMEAT || - sb_nr == SB_NR_RMDIR || - sb_nr == SB_NR_SYMLINK || - sb_nr == SB_NR_SYMLINKAT)) - { - /* These funcs sometimes operate on symlinks */ - if (!((sb_nr == SB_NR_FCHOWNAT || - sb_nr == SB_NR_FCHMODAT || - sb_nr == SB_NR_UTIMENSAT) && - (flags & AT_SYMLINK_NOFOLLOW))) - return false; - } + if (sb_nr == SB_NR_UNLINK || + sb_nr == SB_NR_UNLINKAT || + sb_nr == SB_NR_LCHOWN || + sb_nr == SB_NR_LREMOVEXATTR || + sb_nr == SB_NR_LSETXATTR || + sb_nr == SB_NR_LUTIMES || + sb_nr == SB_NR_REMOVE || + sb_nr == SB_NR_RENAME || + sb_nr == SB_NR_RENAMEAT || + sb_nr == SB_NR_RENAMEAT2 || + sb_nr == SB_NR_RMDIR || + sb_nr == SB_NR_SYMLINK || + sb_nr == SB_NR_SYMLINKAT) + return true; - if (-1 != lstat(abs_path, &st) && S_ISLNK(st.st_mode)) + /* These funcs sometimes operate on symlinks */ + if ((sb_nr == SB_NR_FCHOWNAT || + sb_nr == SB_NR_FCHMODAT || + sb_nr == SB_NR_UTIMENSAT) && + (flags & AT_SYMLINK_NOFOLLOW)) return true; - else - return false; + + return false; } static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, @@ -698,7 +714,7 @@ static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, int old_errno = errno; int result = 0; int retval; - bool sym_func = symlink_func(sb_nr, flags, abs_path); + bool sym_func = symlink_func(sb_nr, flags); retval = check_prefixes(sbcontext->deny_prefixes, sbcontext->num_deny_prefixes, abs_path); @@ -706,6 +722,12 @@ static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, /* Fall in a read/write denied path, Deny Access */ goto out; + if (!strncmp(resolv_path, "/memfd:", strlen("/memfd:"))) { + /* Allow operations on memfd objects #910561 */ + result = 1; + goto out; + } + if (!sym_func) { retval = check_prefixes(sbcontext->deny_prefixes, sbcontext->num_deny_prefixes, resolv_path); @@ -714,15 +736,6 @@ static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, goto out; } - /* Hardcode denying write to the whole log dir. While this is a - * parial match and so rejects paths that also start with this - * string, that isn't going to happen in real life so live with - * it. We can't append a slash to this path either as that would - * allow people to open the dir itself for writing. - */ - if (!strncmp(resolv_path, SANDBOX_LOG_LOCATION, strlen(SANDBOX_LOG_LOCATION))) - goto out; - if (sbcontext->read_prefixes && (sb_nr == SB_NR_ACCESS_RD || sb_nr == SB_NR_OPEN_RD || @@ -754,12 +767,23 @@ static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, } } + /* Hardcode denying write to the whole log dir. While this is a + * parial match and so rejects paths that also start with this + * string, that isn't going to happen in real life so live with + * it. We can't append a slash to this path either as that would + * allow people to open the dir itself for writing. + */ + if (!strncmp(resolv_path, SANDBOX_LOG_LOCATION, strlen(SANDBOX_LOG_LOCATION))) + goto out; + if (sb_nr == SB_NR_ACCESS_WR || sb_nr == SB_NR_CHMOD || sb_nr == SB_NR_CHOWN || sb_nr == SB_NR_CREAT || sb_nr == SB_NR_CREAT64 || + sb_nr == SB_NR_FCHMOD || sb_nr == SB_NR_FCHMODAT || + sb_nr == SB_NR_FCHOWN || sb_nr == SB_NR_FCHOWNAT || /*sb_nr == SB_NR_FTRUNCATE || sb_nr == SB_NR_FTRUNCATE64 ||*/ @@ -767,6 +791,8 @@ static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, sb_nr == SB_NR_LCHOWN || sb_nr == SB_NR_LINK || sb_nr == SB_NR_LINKAT || + sb_nr == SB_NR_LREMOVEXATTR|| + sb_nr == SB_NR_LSETXATTR || sb_nr == SB_NR_LUTIMES || sb_nr == SB_NR_MKDIR || sb_nr == SB_NR_MKDIRAT || @@ -785,9 +811,12 @@ static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, sb_nr == SB_NR_MKSTEMPS64 || sb_nr == SB_NR_OPEN_WR || sb_nr == SB_NR_REMOVE || + sb_nr == SB_NR_REMOVEXATTR || sb_nr == SB_NR_RENAME || sb_nr == SB_NR_RENAMEAT || + sb_nr == SB_NR_RENAMEAT2 || sb_nr == SB_NR_RMDIR || + sb_nr == SB_NR_SETXATTR || sb_nr == SB_NR_SYMLINK || sb_nr == SB_NR_SYMLINKAT || sb_nr == SB_NR_TRUNCATE || @@ -852,20 +881,6 @@ static int check_access(sbcontext_t *sbcontext, int sb_nr, const char *func, goto out; } - /* A very common bug (apparently) is for .py[co] files to fall out - * of sync with their .py source files. Rather than trigger a hard - * failure, let's just whine about it. Once python itself gets - * sorted out, we can drop this #256953. - */ - size_t len = strlen(resolv_path); - if (len > 4) { - const char *py = resolv_path + len - 4; - if (!strcmp(py, ".pyc") || !strcmp(py, ".pyo")) { - sbcontext->show_access_violation = false; - goto out; - } - } - /* If we are here, and still no joy, and its the access() call, * do not log it, but just return -1 */ if (sb_nr == SB_NR_ACCESS_WR) { @@ -895,11 +910,14 @@ static int check_syscall(sbcontext_t *sbcontext, int sb_nr, const char *func, bool access, debug, verbose, set; absolute_path = resolve_path(file, 0); + if (!absolute_path) + goto error; + /* Do not bother dereferencing symlinks when we are using a function that * itself does not dereference. This speeds things up and avoids updating * the atime implicitly. #415475 */ - if (symlink_func(sb_nr, flags, absolute_path)) + if (symlink_func(sb_nr, flags)) resolved_path = absolute_path; else resolved_path = resolve_path(file, 1); @@ -970,9 +988,24 @@ static int check_syscall(sbcontext_t *sbcontext, int sb_nr, const char *func, if (trace_pid && errno == ESRCH) return 2; + /* Underlying directory we operate on went away: #590084 */ + if (!absolute_path && !resolved_path && errno == ENOENT) { + int sym_len = SB_MAX_STRING_LEN + 1 - strlen(func); + if (sbcontext->show_access_violation) + sb_eerror("%sACCESS DENIED%s: %s:%*s'%s' (from deleted directory, see https://bugs.gentoo.org/590084)\n", + COLOR_RED, COLOR_NORMAL, func, sym_len, "", file); + return 0; + } + /* If we get here, something bad happened */ - sb_ebort("ISE: %s(%s)\n\tabs_path: %s\n\tres_path: %s\n", - func, file, absolute_path, resolved_path); + sb_ebort("ISE: %s('%s')\n" + "\tabs_path: %s\n" + "\tres_path: %s\n" + "\terrno=%i: %s\n", + func, file, + absolute_path, + resolved_path, + errno, strerror(errno)); } bool is_sandbox_on(void) @@ -1008,10 +1041,24 @@ bool is_sandbox_on(void) return result; } +static int resolve_dirfd_path_alloc(int dirfd, const char *path, char **resolved_path) +{ + size_t resolved_path_size = SB_PATH_MAX * sizeof(char); + *resolved_path = xmalloc(resolved_path_size); + int result = resolve_dirfd_path(dirfd, path, *resolved_path, resolved_path_size); + + if (result) { + free(*resolved_path); + *resolved_path = NULL; + } + + return result; +} + bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, int flags) { int result; - char at_file_buf[SB_PATH_MAX]; + char *at_file_buf; /* Some funcs operate on a fd directly and so filename is NULL, but * the rest should get rejected as "file/directory does not exist". @@ -1030,7 +1077,7 @@ bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, in } } - switch (resolve_dirfd_path(dirfd, file, at_file_buf, sizeof(at_file_buf))) { + switch (resolve_dirfd_path_alloc(dirfd, file, &at_file_buf)) { case -1: return false; case 0: file = at_file_buf; break; case 2: return true; @@ -1053,6 +1100,9 @@ bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, in result = check_syscall(&sbcontext, sb_nr, func, file, flags); + if (at_file_buf) + free(at_file_buf); + sb_unlock(); if (0 == result) { @@ -1071,8 +1121,11 @@ bool before_syscall_access(int dirfd, int sb_nr, const char *func, const char *f const char *ext_func; if (flags & W_OK) sb_nr = SB_NR_ACCESS_WR, ext_func = "access_wr"; - else + else if (flags & R_OK) sb_nr = SB_NR_ACCESS_RD, ext_func = "access_rd"; + else + /* Must be F_OK or X_OK; we do not need to check either. */ + return true; return before_syscall(dirfd, sb_nr, ext_func, file, flags); } @@ -1086,6 +1139,21 @@ bool before_syscall_open_int(int dirfd, int sb_nr, const char *func, const char return before_syscall(dirfd, sb_nr, ext_func, file, flags); } +bool before_syscall_fd(int sb_nr, const char *func, int fd) { +#ifdef SANDBOX_PROC_SELF_FD + /* We only know how to handle e.g. fchmod() and fchown() on + * linux, where it's possible to (eventually) get a path out + * of the given file descriptor. The "64" below accounts for + * the length of an integer string, and is probably + * overkill. */ + char path[sizeof("/proc/self/fd/") + 64]; + snprintf(path, sizeof("/proc/self/fd/") + 64, "/proc/self/fd/%i", fd); + return before_syscall(AT_FDCWD, sb_nr, func, path, 0); +#else + return true; +#endif +} + bool before_syscall_open_char(int dirfd, int sb_nr, const char *func, const char *file, const char *mode) { if (NULL == mode) @@ -1104,7 +1172,7 @@ bool before_syscall_open_char(int dirfd, int sb_nr, const char *func, const char typedef struct { const char *name; size_t len; - char *value; + const char *value; } env_pair; #define ENV_PAIR(x, n, v) [x] = { .name = n, .len = sizeof(n) - 1, .value = v, } @@ -1152,6 +1220,7 @@ struct sb_envp_ctx sb_new_envp(char **envp, bool insert) ENV_PAIR(11, ENV_SANDBOX_DEBUG, NULL), ENV_PAIR(12, "LD_LIBRARY_PATH", NULL), ENV_PAIR(13, ENV_SANDBOX_TESTING, NULL), + ENV_PAIR(14, ENV_SANDBOX_METHOD, NULL), }; size_t num_vars = ARRAY_SIZE(vars); char *found_vars[num_vars]; @@ -1166,6 +1235,7 @@ struct sb_envp_ctx sb_new_envp(char **envp, bool insert) found_var_cnt = 0; memset(found_vars, 0, sizeof(found_vars)); + /* Iterate through user's environment and check against expected. */ str_list_for_each_item(envp, entry, count) { for (i = 0; i < num_vars; ++i) { if (found_vars[i]) @@ -1177,6 +1247,14 @@ struct sb_envp_ctx sb_new_envp(char **envp, bool insert) } } + /* Treat unset and expected-unset variables as found. This will allow us + * to keep existing environment. */ + for (i = 0; i < num_vars; ++i) { + if (vars[i].value == NULL && found_vars[i] == NULL) { + ++found_var_cnt; + } + } + /* Now specially handle merging of LD_PRELOAD */ char *ld_preload; bool merge_ld_preload = found_vars[0] && !strstr(found_vars[0], sandbox_lib); @@ -1215,6 +1293,8 @@ struct sb_envp_ctx sb_new_envp(char **envp, bool insert) vars[12].value = sbcontext.ld_library_path; vars[13].value = "1"; } + if (sbcontext.method != SANDBOX_METHOD_ANY) + vars[14].value = str_sandbox_method(sbcontext.method); char ** my_env = NULL; if (!insert) { diff --git a/libsandbox/libsandbox.h b/libsandbox/libsandbox.h index 70fc422..01a4c6c 100644 --- a/libsandbox/libsandbox.h +++ b/libsandbox/libsandbox.h @@ -46,11 +46,23 @@ #define SB_SAFE_OPEN_CHAR(_path, _mode) \ SB_SAFE_OPEN_CHAR_AT(AT_FDCWD, _path, _mode) +#define _SB_SAFE_FD(_nr, _name, _fd) \ + __SB_SAFE(before_syscall_fd(_nr, _name, fd)) +#define SB_SAFE_FD(_fd) \ + _SB_SAFE_FD(WRAPPER_NR, STRING_NAME, _fd) + +/* Symbols that don't exist in the C library will be <= this value. */ +#define SB_NR_UNDEF -99999 +#define SB_NR_IS_DEFINED(nr) (nr > SB_NR_UNDEF) + bool is_sandbox_on(void); bool before_syscall(int, int, const char *, const char *, int); bool before_syscall_access(int, int, const char *, const char *, int); bool before_syscall_open_int(int, int, const char *, const char *, int); bool before_syscall_open_char(int, int, const char *, const char *, const char *); +bool before_syscall_fd(int, const char *, int); + +enum sandbox_method_t get_sandbox_method(void); void *get_dlsym(const char *symname, const char *symver); @@ -77,7 +89,7 @@ extern void sb_lock(void); extern void sb_unlock(void); bool trace_possible(const char *filename, char *const argv[], const void *data); -void trace_main(const char *filename, char *const argv[]); +void trace_main(void); /* glibc modified realpath() function */ char *erealpath(const char *, char *); diff --git a/libsandbox/local.mk b/libsandbox/local.mk new file mode 100644 index 0000000..dd78a76 --- /dev/null +++ b/libsandbox/local.mk @@ -0,0 +1,105 @@ +lib_LTLIBRARIES += %D%/libsandbox.la + +%C%_libsandbox_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I%D% \ + -I$(top_srcdir)/%D% \ + -I$(top_srcdir)/libsbutil \ + -I$(top_srcdir)/libsbutil/include + +%C%_libsandbox_la_CFLAGS = $(CFLAG_EXCEPTIONS) +# Could use the following to libsandbox_la_LIBADD, but then libtool links it +# with --whole-archive, and libsandbox.so increase with a few KB in size: +# libsbutil/libsbutil.la +libsbutil/.libs/libsbutil.a: libsbutil/libsbutil.la +%C%_libsandbox_la_LIBSBLIB = libsbutil/.libs/libsbutil.a +%C%_libsandbox_la_LIBADD = \ + -lc $(LIBDL) \ + $(%C%_libsandbox_la_LIBSBLIB) +# Do not add -nostdlib or -nostartfiles, as then our constructor +# and destructor will not be executed ... +%C%_libsandbox_la_LDFLAGS = \ + -no-undefined \ + -avoid-version \ + $(LDFLAG_VER),%D%/libsandbox.map +%C%_libsandbox_la_SOURCES = \ + %D%/libsandbox.h \ + %D%/libsandbox.c \ + %D%/lock.c \ + %D%/memory.c \ + %D%/pre_check_at.c \ + %D%/pre_check_mkdirat.c \ + %D%/pre_check_openat64.c \ + %D%/pre_check_openat.c \ + %D%/pre_check_unlinkat.c \ + %D%/trace.c \ + %D%/wrappers.h \ + %D%/wrappers.c \ + %D%/canonicalize.c + +install-exec-hook: + rm -f $(DESTDIR)$(libdir)/libsandbox.la +# Since we removed the .la file, libtool uninstall doesn't work, +# so we have to manually uninstall libsandbox.so ourselves. +uninstall-hook: + rm -f $(DESTDIR)$(libdir)/libsandbox.so + +%D%/libsandbox.c: %D%/libsandbox.map %D%/sb_nr.h +%D%/trace.c: %D%/trace_syscalls.h %D%/sb_nr.h $(TRACE_FILES) +%D%/wrappers.c: %D%/symbols.h + +TRACE_FILES = $(wildcard $(top_srcdir)/%D%/trace/*.[ch] $(top_srcdir)/%D%/trace/*/*.[ch]) + +SCRIPT_DIR = $(top_srcdir)/scripts + +SYMBOLS_FILE = $(top_srcdir)/%D%/symbols.h.in +SYMBOLS_WRAPPERS = $(wildcard $(top_srcdir)/%D%/wrapper-funcs/*.[ch]) +GEN_VERSION_MAP_SCRIPT = $(SCRIPT_DIR)/gen_symbol_version_map.awk +GEN_HEADER_SCRIPT = $(SCRIPT_DIR)/gen_symbol_header.awk +GEN_TRACE_SCRIPT = $(SCRIPT_DIR)/gen_trace_header.awk +SB_AWK = LC_ALL=C $(AWK) -v SYMBOLS_FILE="$(SYMBOLS_FILE)" -v srcdir="$(top_srcdir)/%D%" -f + +%D%/libsandbox.map: $(SYMBOLS_FILE) $(GEN_VERSION_MAP_SCRIPT) + @$(MKDIR_P) %D% + $(AM_V_GEN)$(READELF) -sW $(LIBC_PATH) | $(SB_AWK) $(GEN_VERSION_MAP_SCRIPT) > $@ + +%D%/symbols.h: $(SYMBOLS_FILE) $(GEN_HEADER_SCRIPT) + @$(MKDIR_P) %D% + $(AM_V_GEN)$(READELF) -sW $(LIBC_PATH) | $(SB_AWK) $(GEN_HEADER_SCRIPT) > $@ + +SB_NR_FILE = %D%/sb_nr.h.in +%D%/sb_nr.h: %D%/symbols.h $(SB_NR_FILE) + @$(MKDIR_P) %D% + $(AM_V_GEN)$(EGREP) -h '^\#define SB_' $^ > $@ + +TRACE_MAKE_HEADER = \ + $(SB_AWK) $(GEN_TRACE_SCRIPT) -v MODE=gen | \ + $(COMPILE) -E -P -include $(top_srcdir)/headers.h - $$f | \ + $(SB_AWK) $(GEN_TRACE_SCRIPT) -v syscall_prefix=$$t > $$header +%D%/trace_syscalls.h: $(SYMBOLS_FILE) $(GEN_TRACE_SCRIPT) $(SB_PERSONALITIES_HEADERS) + @$(MKDIR_P) %D% +if SB_PERSONALITIES + $(AM_V_GEN)touch $@ +else + $(AM_V_GEN)t= f= header=$@; $(TRACE_MAKE_HEADER) +endif + +$(SB_PERSONALITIES_HEADERS): $(SYMBOLS_FILE) $(GEN_TRACE_SCRIPT) + @$(MKDIR_P) %D% + $(AM_V_GEN)for pers in $(SB_PERSONALITIES_SETTINGS) ; do \ + t=_$${pers%:*}; \ + f=$${pers#*:}; \ + header="%D%/trace_syscalls$${t}.h"; \ + if [ "$$header" = "$@" ]; then \ + $(TRACE_MAKE_HEADER) || exit $$?; \ + break; \ + fi; \ + done + +EXTRA_DIST += $(SYMBOLS_FILE) $(SYMBOLS_WRAPPERS) $(SB_NR_FILE) $(TRACE_FILES) + +CLEANFILES += \ + %D%/libsandbox.map \ + %D%/sb_nr.h \ + %D%/symbols.h \ + %D%/trace_syscalls*.h diff --git a/libsandbox/wrapper-funcs/__pre_at_check.c b/libsandbox/pre_check_at.c index f72c40c..be6e634 100644 --- a/libsandbox/wrapper-funcs/__pre_at_check.c +++ b/libsandbox/pre_check_at.c @@ -5,6 +5,11 @@ * Licensed under the GPL-2 */ +#include "headers.h" +#include "sbutil.h" +#include "libsandbox.h" +#include "wrappers.h" + /* We assume the parent has nested use with save/restore errno */ bool sb_common_at_pre_check(const char *func, const char **pathname, int dirfd, char *dirfd_path, size_t dirfd_path_len) diff --git a/libsandbox/wrapper-funcs/mkdirat_pre_check.c b/libsandbox/pre_check_mkdirat.c index 0b48d1f..49c382a 100644 --- a/libsandbox/wrapper-funcs/mkdirat_pre_check.c +++ b/libsandbox/pre_check_mkdirat.c @@ -5,6 +5,11 @@ * Licensed under the GPL-2 */ +#include "headers.h" +#include "sbutil.h" +#include "libsandbox.h" +#include "wrappers.h" + bool sb_mkdirat_pre_check(const char *func, const char *pathname, int dirfd) { char canonic[SB_PATH_MAX]; @@ -31,8 +36,8 @@ bool sb_mkdirat_pre_check(const char *func, const char *pathname, int dirfd) * not want to pass this attempt up to the higher levels as those * will trigger a sandbox violation. */ - struct stat st; - if (0 == lstat(canonic, &st)) { + struct stat64 st; + if (0 == lstat64(pathname, &st)) { int new_errno; sb_debug_dyn("EARLY FAIL: %s(%s[%s]) @ lstat: %s\n", func, pathname, canonic, strerror(errno)); @@ -40,7 +45,7 @@ bool sb_mkdirat_pre_check(const char *func, const char *pathname, int dirfd) new_errno = EEXIST; /* Hmm, is this a broken symlink we're trying to extend ? */ - if (S_ISLNK(st.st_mode) && stat(pathname, &st) != 0) { + if (S_ISLNK(st.st_mode) && stat64(pathname, &st) != 0) { /* XXX: This awful hack should probably be turned into a * common func that does a better job. For now, we have * enough crap to catch gnulib tests #297026. diff --git a/libsandbox/pre_check_openat.c b/libsandbox/pre_check_openat.c new file mode 100644 index 0000000..99c03eb --- /dev/null +++ b/libsandbox/pre_check_openat.c @@ -0,0 +1,30 @@ +/* + * open*() pre-check. + * + * Copyright 1999-2012 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#include "headers.h" +#include "sbutil.h" +#include "libsandbox.h" +#include "wrappers.h" + +bool sb_openat_pre_check(const char *func, const char *pathname, int dirfd, int flags) +{ + /* If we're not trying to create, fail normally if file does not stat */ + if (flags & O_CREAT) + return true; + + save_errno(); + + /* Doesn't exist -> skip permission checks */ + if (sb_exists(dirfd, pathname, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) == -1) { + sb_debug_dyn("EARLY FAIL: %s(%s): %s\n", func, pathname, strerror(errno)); + return false; + } + + restore_errno(); + + return true; +} diff --git a/libsandbox/wrapper-funcs/openat64_pre_check.c b/libsandbox/pre_check_openat64.c index 67dc0dc..d4dbe97 100644 --- a/libsandbox/wrapper-funcs/openat64_pre_check.c +++ b/libsandbox/pre_check_openat64.c @@ -5,8 +5,11 @@ * Licensed under the GPL-2 */ -#include "__64_pre.h" +#include "headers.h" +#include "sbutil.h" +#include "libsandbox.h" +#include "wrappers.h" + #define sb_openat_pre_check sb_openat64_pre_check -#include "openat_pre_check.c" +#include "pre_check_openat.c" #undef sb_openat_pre_check -#include "__64_post.h" diff --git a/libsandbox/wrapper-funcs/unlinkat_pre_check.c b/libsandbox/pre_check_unlinkat.c index c004d15..93a0dd9 100644 --- a/libsandbox/wrapper-funcs/unlinkat_pre_check.c +++ b/libsandbox/pre_check_unlinkat.c @@ -5,6 +5,11 @@ * Licensed under the GPL-2 */ +#include "headers.h" +#include "sbutil.h" +#include "libsandbox.h" +#include "wrappers.h" + bool sb_unlinkat_pre_check(const char *func, const char *pathname, int dirfd) { char canonic[SB_PATH_MAX]; diff --git a/libsandbox/symbols.h.in b/libsandbox/symbols.h.in index bdbce08..5805592 100644 --- a/libsandbox/symbols.h.in +++ b/libsandbox/symbols.h.in @@ -7,8 +7,10 @@ # before 'creat()' as 'creat()' uses 'open()' ... chmod +fchmod fchmodat chown +fchown fchownat open __open_2 @@ -34,6 +36,7 @@ faccessat remove rename renameat +renameat2 rmdir symlink symlinkat @@ -68,9 +71,23 @@ execvpe fexecve system popen +removexattr +lremovexattr +setxattr +lsetxattr utime +__utime64 utimes +__utimes64 +__utimes_time64 utimensat +__utimensat64 utimensat_time64 +__utimensat_time64 futimesat +__futimesat64 +__futimesat_time64 lutimes +__lutimes64 +__lutimes_time64 fork +vfork diff --git a/libsandbox/trace.c b/libsandbox/trace.c index fb1fc32..75a749e 100644 --- a/libsandbox/trace.c +++ b/libsandbox/trace.c @@ -10,7 +10,16 @@ #include "sb_nr.h" static long do_peekdata(long offset); -static long _do_ptrace(enum __ptrace_request request, const char *srequest, void *addr, void *data); +/* Note on _do_ptrace argument types: + glibc defines ptrace as: + long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); + musl defines ptrace as: + long ptrace(int, ...); + + Let's clobber to 'int' lowest common denominator. + */ +typedef int sb_ptrace_req_t; +static long _do_ptrace(sb_ptrace_req_t request, const char *srequest, void *addr, void *data); #define do_ptrace(request, addr, data) _do_ptrace(request, #request, addr, data) #define _trace_possible(data) true @@ -20,7 +29,7 @@ static long _do_ptrace(enum __ptrace_request request, const char *srequest, void # define SBDEBUG 0 #endif #define __sb_debug(fmt, args...) do { if (SBDEBUG) sb_eraw(fmt, ## args); } while (0) -#define _sb_debug(fmt, args...) do { if (SBDEBUG) sb_ewarn("TRACE (pid=%i):%s: " fmt, getpid(), __func__, ## args); } while (0) +#define _sb_debug(fmt, args...) do { if (SBDEBUG) sb_ewarn("TRACE (pid=%i<%i):%s: " fmt, getpid(), trace_pid, __func__, ## args); } while (0) #define sb_debug(fmt, args...) _sb_debug(fmt "\n", ## args) #include "trace/os.c" @@ -37,6 +46,38 @@ pid_t trace_pid; # define sb_openat_pre_check sb_openat64_pre_check #endif +static int trace_yama_level(void) +{ + char ch; + int fd, level; + + fd = open64("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY | O_CLOEXEC); + if (fd == -1) + return 0; + + RETRY_EINTR(read(fd, &ch, 1)); + close(fd); + level = ch - '0'; + + switch (level) { + case 0: + /* Normal levels work fine. */ + return 0; + + case 1: + case 2: + /* ptrace scope binds access to specific capabilities. Lets use uid==0 as a + * lazy proxy for "we have all capabilities" until we can refine this. + */ + return getuid() == 0 ? 0 : level; + + case 3: + default: + /* Level 3+ is not supported. */ + sb_ebort("YAMA ptrace_scope=%i+ is not supported as it makes tracing impossible.\n", level); + } +} + static void trace_exit(int status) { /* if we were vfork-ed, clear trace_pid and exit */ @@ -44,7 +85,7 @@ static void trace_exit(int status) _exit(status); } -static long _do_ptrace(enum __ptrace_request request, const char *srequest, void *addr, void *data) +static long _do_ptrace(sb_ptrace_req_t request, const char *srequest, void *addr, void *data) { long ret; try_again: @@ -169,20 +210,6 @@ static char *do_peekstr(unsigned long lptr) } } -static const char *strcld_chld(int cld) -{ - switch (cld) { -#define C(c) case CLD_##c: return "CLD_"#c; - C(CONTINUED) - C(DUMPED) - C(EXITED) - C(KILLED) - C(TRAPPED) - C(STOPPED) -#undef C - default: return "CLD_???"; - } -} /* strsignal() translates the string when i want C define */ static const char *strsig(int sig) { @@ -205,47 +232,6 @@ static const char *strsig(int sig) } } -static void trace_child_signal(int signo, siginfo_t *info, void *context) -{ - sb_debug("got sig %s(%i): code:%s(%i) status:%s(%i)", - strsig(signo), signo, - strcld_chld(info->si_code), info->si_code, - strsig(info->si_status), info->si_status); - - switch (info->si_code) { - case CLD_DUMPED: - case CLD_KILLED: - trace_exit(128 + info->si_status); - - case CLD_EXITED: - __sb_debug(" = %i\n", info->si_status); - trace_exit(info->si_status); - - case CLD_TRAPPED: - switch (info->si_status) { - case SIGSTOP: - kill(trace_pid, SIGCONT); - case SIGTRAP: - case SIGCONT: - return; - } - - /* For whatever signal the child caught, let's ignore it and - * continue on. If it aborted, segfaulted, whatever, that's - * its problem, not ours, so don't whine about it. We just - * have to be sure to bubble it back up. #265072 - */ - do_ptrace(PTRACE_CONT, NULL, (void *)(long)info->si_status); - return; - } - - sb_eerror("ISE:trace_child_signal: child (%i) signal %s(%i), code %s(%i), status %s(%i)\n", - trace_pid, - strsig(signo), signo, - strcld_chld(info->si_code), info->si_code, - strsig(info->si_status), info->si_status); -} - static const struct syscall_entry *lookup_syscall_in_tbl(const struct syscall_entry *tbl, int nr) { while (tbl->name) @@ -263,6 +249,7 @@ struct syscall_state { bool (*pre_check)(const char *func, const char *pathname, int dirfd); }; +/* Check syscall that only takes a path as its |ibase| argument. */ static bool _trace_check_syscall_C(struct syscall_state *state, int ibase) { char *path = do_peekstr(trace_arg(state->regs, ibase)); @@ -279,6 +266,7 @@ static bool _trace_check_syscall_C(struct syscall_state *state, int ibase) free(path); return ret; } +/* Check syscall that only takes a path as its first argument. */ static bool trace_check_syscall_C(struct syscall_state *state) { return _trace_check_syscall_C(state, 1); @@ -301,20 +289,24 @@ static bool __trace_check_syscall_DCF(struct syscall_state *state, int ibase, in free(path); return ret; } -static bool _trace_check_syscall_DCF(struct syscall_state *state, int ibase) +/* Check syscall that takes a dirfd & path starting at |ibase| argument, and flags at |fbase|. */ +static bool _trace_check_syscall_DCF(struct syscall_state *state, int ibase, int fbase) { - int flags = trace_arg(state->regs, ibase + 2); + int flags = trace_arg(state->regs, fbase); return __trace_check_syscall_DCF(state, ibase, flags); } +/* Check syscall that takes a dirfd, path, and flags as its first 3 arguments. */ static bool trace_check_syscall_DCF(struct syscall_state *state) { - return _trace_check_syscall_DCF(state, 1); + return _trace_check_syscall_DCF(state, 1, 3); } +/* Check syscall that takes a dirfd & path starting at |ibase| argument. */ static bool _trace_check_syscall_DC(struct syscall_state *state, int ibase) { return __trace_check_syscall_DCF(state, ibase, 0); } +/* Check syscall that takes a dirfd & path as its first 2 arguments (but no flags). */ static bool trace_check_syscall_DC(struct syscall_state *state) { return _trace_check_syscall_DC(state, 1); @@ -345,7 +337,7 @@ static bool trace_check_syscall(const struct syscall_entry *se, void *regs) state.regs = regs; state.nr = nr = se->sys; state.func = name = se->name; - if (nr == SB_NR_UNDEF) goto done; + if (!SB_NR_IS_DEFINED(se->nr)) goto done; else if (nr == SB_NR_MKDIR) state.pre_check = sb_mkdirat_pre_check; else if (nr == SB_NR_MKDIRAT) state.pre_check = sb_mkdirat_pre_check; else if (nr == SB_NR_UNLINK) state.pre_check = sb_unlinkat_pre_check; @@ -353,22 +345,24 @@ static bool trace_check_syscall(const struct syscall_entry *se, void *regs) else state.pre_check = NULL; /* Hmm, add these functions to the syscall table and avoid this if() ? */ - if (nr == SB_NR_UNDEF) goto done; - else if (nr == SB_NR_CHMOD) return trace_check_syscall_C (&state); + if (nr == SB_NR_CHMOD) return trace_check_syscall_C (&state); else if (nr == SB_NR_CHOWN) return trace_check_syscall_C (&state); else if (nr == SB_NR_CREAT) return trace_check_syscall_C (&state); - else if (nr == SB_NR_FCHMODAT) return trace_check_syscall_DCF(&state); - else if (nr == SB_NR_FCHOWNAT) return trace_check_syscall_DCF(&state); + /* NB: Linux syscall does not have a flags argument. */ + else if (nr == SB_NR_FCHMODAT) return trace_check_syscall_DC (&state); + else if (nr == SB_NR_FCHOWNAT) return _trace_check_syscall_DCF(&state, 1, 5); else if (nr == SB_NR_FUTIMESAT) return trace_check_syscall_DC (&state); else if (nr == SB_NR_LCHOWN) return trace_check_syscall_C (&state); else if (nr == SB_NR_LINK) return _trace_check_syscall_C (&state, 2); - else if (nr == SB_NR_LINKAT) return _trace_check_syscall_DCF(&state, 3); + else if (nr == SB_NR_LINKAT) return _trace_check_syscall_DCF(&state, 3, 5); else if (nr == SB_NR_MKDIR) return trace_check_syscall_C (&state); else if (nr == SB_NR_MKDIRAT) return trace_check_syscall_DC (&state); else if (nr == SB_NR_MKNOD) return trace_check_syscall_C (&state); else if (nr == SB_NR_MKNODAT) return trace_check_syscall_DC (&state); else if (nr == SB_NR_RENAME) return trace_check_syscall_C (&state) && _trace_check_syscall_C (&state, 2); + else if (nr == SB_NR_RENAMEAT2) return trace_check_syscall_DC (&state) && + _trace_check_syscall_DC (&state, 3); else if (nr == SB_NR_RENAMEAT) return trace_check_syscall_DC (&state) && _trace_check_syscall_DC (&state, 3); else if (nr == SB_NR_RMDIR) return trace_check_syscall_C (&state); @@ -380,7 +374,14 @@ static bool trace_check_syscall(const struct syscall_entry *se, void *regs) else if (nr == SB_NR_UNLINKAT) return trace_check_syscall_DCF(&state); else if (nr == SB_NR_UTIME) return trace_check_syscall_C (&state); else if (nr == SB_NR_UTIMES) return trace_check_syscall_C (&state); - else if (nr == SB_NR_UTIMENSAT) return _trace_check_syscall_DCF(&state, 1); + + else if (nr == SB_NR_UTIMENSAT) { + utimensat: + return _trace_check_syscall_DCF(&state, 1, 4); + } else if (nr == SB_NR___UTIMENSAT64) { + state.nr = SB_NR_UTIMENSAT; + goto utimensat; + } else if (nr == SB_NR_ACCESS) { char *path = do_peekstr(trace_arg(regs, 1)); @@ -421,47 +422,218 @@ static bool trace_check_syscall(const struct syscall_entry *se, void *regs) ret = 1; free(path); return ret; + + } else if (nr == SB_NR_EXECVE || nr == SB_NR_EXECVEAT) { + /* Try to extract environ and merge with our own. */ + char *path; + unsigned long environ, i = 0; + + if (nr == SB_NR_EXECVEAT) { + int dirfd = do_peekdata(trace_arg(regs, 1)); + unsigned long argv = trace_arg(regs, 3); + environ = trace_arg(regs, 4); + path = do_peekstr(trace_arg(regs, 2)); + __sb_debug("(%i, \"%s\", %lx, %lx{", dirfd, path, argv, environ); + } else { + path = do_peekstr(trace_arg(regs, 1)); + unsigned long argv = trace_arg(regs, 2); + environ = trace_arg(regs, 3); + __sb_debug("(\"%s\", %lx, %lx{", path, argv, environ); + } + + while (1) { + unsigned long envp = do_peekdata(environ + i); + if (!envp) + break; + + char *env = do_peekstr(envp); + if (strncmp(env, "SANDBOX_", 8) == 0) { + __sb_debug("\"%s\" ", env); + putenv(env); + } + i += sizeof(long); + } + __sb_debug("})"); + return 1; + } else if (nr == SB_NR_FCHMOD) { + int fd = trace_arg(regs, 1); + mode_t mode = trace_arg(regs, 2); + __sb_debug("(%i, %o)", fd, mode); + return _SB_SAFE_FD(nr, name, fd); + + } else if (nr == SB_NR_FCHOWN) { + int fd = trace_arg(regs, 1); + uid_t uid = trace_arg(regs, 2); + gid_t gid = trace_arg(regs, 3); + __sb_debug("(%i, %i, %i)", fd, uid, gid); + return _SB_SAFE_FD(nr, name, fd); } + done: __sb_debug("(...)"); return ret; } +static void trace_init_tracee(void) +{ + do_ptrace(PTRACE_SETOPTIONS, NULL, (void *)(uintptr_t)( + PTRACE_O_EXITKILL | + PTRACE_O_TRACECLONE | + PTRACE_O_TRACEEXEC | + PTRACE_O_TRACEEXIT | + PTRACE_O_TRACEFORK | + PTRACE_O_TRACEVFORK | + PTRACE_O_TRACESYSGOOD + )); +} + static void trace_loop(void) { trace_regs regs; bool before_exec, before_syscall, fake_syscall_ret; + unsigned event; long ret; - int nr, status; - const struct syscall_entry *se, *tbl_after_fork; + int status, sig; + const struct syscall_entry *tbl_after_fork; + void *data; before_exec = true; before_syscall = false; fake_syscall_ret = false; tbl_after_fork = NULL; + data = NULL; do { - ret = do_ptrace(PTRACE_SYSCALL, NULL, NULL); + ret = do_ptrace(PTRACE_SYSCALL, NULL, data); + data = NULL; waitpid(trace_pid, &status, 0); - if (before_exec) { - unsigned event = ((unsigned)status >> 16); - if (event == PTRACE_EVENT_EXEC) { - _sb_debug("hit exec!"); - before_exec = false; - } else + event = (unsigned)status >> 16; + + if (WIFSIGNALED(status)) { + int sig = WTERMSIG(status); + sb_debug("signaled %s %i", strsig(sig), sig); + kill(getpid(), sig); + trace_exit(128 + sig); + + } else if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + sb_debug("exited %li", ret); + trace_exit(ret); + + } + + sb_assert(WIFSTOPPED(status)); + sig = WSTOPSIG(status); + + switch (event) { + case 0: + if (sig != (SIGTRAP | 0x80)) { + /* For whatever signal the child caught, let's ignore it and + * continue on. If it aborted, segfaulted, whatever, that's + * its problem, not ours, so don't whine about it. We just + * have to be sure to bubble it back up. #265072 + * + * The next run of this loop should see the WIFSIGNALED status + * and we'll exit then. + */ + sb_debug("passing signal through %s (%i)", strsig(sig), sig); + data = (void *)(uintptr_t)(sig); + continue; + } + + if (before_exec) { _sb_debug("waiting for exec; status: %#x", status); + continue; + } + break; + + case PTRACE_EVENT_EXEC: + __sb_debug("hit exec!"); + before_exec = false; ret = trace_get_regs(®s); tbl_after_fork = trace_check_personality(®s); continue; + + case PTRACE_EVENT_EXIT: + /* We'll tell the process to resume, which should make it exit, + * and then we'll pick up its exit status and exit above. + */ + __sb_debug(" exit event!\n"); + continue; + + case PTRACE_EVENT_CLONE: + case PTRACE_EVENT_FORK: + case PTRACE_EVENT_VFORK: { + /* The tracee is forking, so fork a new tracer to handle it. */ + long newpid; + do_ptrace(PTRACE_GETEVENTMSG, NULL, &newpid); + sb_debug("following forking event %i; pid=%li %i\n", + event, newpid, before_syscall); + + /* If YAMA ptrace_scope is active, then we can't hand off the child + * to a new tracer. Give up. #821403 + */ + int yama = trace_yama_level(); + if (yama >= 1) { + sb_eqawarn("Unable to trace children due to YAMA ptrace_scope=%i\n", yama); + ptrace(PTRACE_DETACH, newpid, NULL, NULL); + continue; + } + + /* Pipe for synchronizing detach & attach events. */ + int fds[2]; + ret = pipe(fds); + sb_assert(ret == 0); + if (fork() == 0) { + /* New tracer needs to take control of new tracee. */ + char ch; + close(fds[1]); + RETRY_EINTR(read(fds[0], &ch, 1)); + close(fds[0]); + trace_pid = newpid; + retry_attach: + ret = do_ptrace(PTRACE_ATTACH, NULL, NULL); + if (ret) { + if (errno == EPERM) + goto retry_attach; + sb_ebort("ISE:PTRACE_ATTACH %s", strerror(errno)); + } + trace_init_tracee(); + before_syscall = true; + continue; + } else { + /* Existing tracer needs to release new tracee. */ + retry_detach: + ret = ptrace(PTRACE_DETACH, newpid, NULL, (void *)SIGSTOP); + if (ret) { + if (errno == ESRCH) { + /* The kernel might not have the proc ready yet. */ + struct timespec ts = {0, 500 * 1000 /* 0.5 millisec */}; + nanosleep(&ts, NULL); + goto retry_detach; + } + sb_ebort("ISE:PTRACE_DETACH %s", strerror(errno)); + } + close(fds[0]); + RETRY_EINTR(write(fds[1], "", 1)); + close(fds[1]); + } + continue; } - ret = trace_get_regs(®s); - nr = trace_get_sysnum(®s); + default: + sb_ebort("ISE: unhandle ptrace signal %s (%i) event %u\n", + strsig(sig), sig, event); + } - se = lookup_syscall_in_tbl(tbl_after_fork, nr); ret = trace_get_regs(®s); + if (before_syscall) { + /* NB: The kernel guarantees syscall NR is valid only on entry. */ + int nr = trace_get_sysnum(®s); + const struct syscall_entry *se = lookup_syscall_in_tbl(tbl_after_fork, nr); + _sb_debug("%s:%i", se ? se->name : "IDK", nr); if (!trace_check_syscall(se, ®s)) { sb_debug_dyn("trace_loop: forcing EPERM after %s\n", se->name); @@ -489,27 +661,21 @@ static void trace_loop(void) } while (1); } -void trace_main(const char *filename, char *const argv[]) +void trace_main(void) { - struct sigaction sa, old_sa; - - sa.sa_flags = SA_RESTART | SA_SIGINFO; - sa.sa_sigaction = trace_child_signal; - sigaction(SIGCHLD, &sa, &old_sa); - - sb_debug_dyn("trace_main: tracing: %s\n", filename); + struct sigaction old_sa, sa = { .sa_handler = SIG_DFL, }; if (trace_pid) sb_ebort("ISE: trace code assumes multiple threads are not forking\n"); + sigaction(SIGCHLD, &sa, &old_sa); trace_pid = fork(); if (unlikely(trace_pid == -1)) { sb_ebort("ISE: vfork() failed: %s\n", strerror(errno)); } else if (trace_pid) { sb_debug("parent waiting for child (pid=%i) to signal", trace_pid); waitpid(trace_pid, NULL, 0); - do_ptrace(PTRACE_SETOPTIONS, NULL, - (void *)(PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC)); + trace_init_tracee(); sb_close_all_fds(); trace_loop(); sb_ebort("ISE: child should have quit, as should we\n"); @@ -527,7 +693,7 @@ void trace_main(const char *filename, char *const argv[]) #undef _trace_possible #define _trace_possible(data) false -void trace_main(const char *filename, char *const argv[]) +void trace_main(void) { /* trace_possible() triggers a warning for us */ } @@ -563,10 +729,26 @@ static char *flatten_args(char *const argv[]) bool trace_possible(const char *filename, char *const argv[], const void *data) { - if (_trace_possible(data)) - return true; + char *args; + + /* If YAMA ptrace_scope is very high, then we can't trace at all. #771360 */ + int yama = trace_yama_level(); + if (yama >= 2) { + sb_eqawarn("YAMA ptrace_scope=%i is not currently supported\n", yama); + goto fail; + } + + if (_trace_possible(data)) { + /* If we're in an environment like QEMU where ptrace doesn't work, then + * don't try to use it. If ptrace does work, this should fail with ESRCH. + */ + errno = 0; + ptrace(PTRACE_CONT, 0, NULL, NULL); + return errno == ENOSYS ? false : true; + } - char *args = flatten_args(argv); + fail: + args = flatten_args(argv); sb_eqawarn("Unable to trace static ELF: %s: %s\n", filename, args); free(args); return false; diff --git a/libsandbox/trace/common.c b/libsandbox/trace/common.c index f426887..767839a 100644 --- a/libsandbox/trace/common.c +++ b/libsandbox/trace/common.c @@ -7,7 +7,7 @@ static int trace_get_sysnum(void *vregs); static long trace_raw_ret(void *vregs); static unsigned long trace_arg(void *vregs, int num); -#ifndef SB_SCHIZO +#ifndef SB_PERSONALITIES static const struct syscall_entry syscall_table[] = { #define S(s) { SB_SYS_##s, SB_NR_##s, #s }, #include "trace_syscalls.h" diff --git a/libsandbox/trace/linux/aarch64.c b/libsandbox/trace/linux/aarch64.c new file mode 100644 index 0000000..82e829c --- /dev/null +++ b/libsandbox/trace/linux/aarch64.c @@ -0,0 +1,50 @@ +#define trace_reg_ret regs[0] /* x0 */ + +#undef trace_get_regs +static long trace_get_regs(void *vregs) +{ + struct iovec iov_regs = { + .iov_base = vregs, + .iov_len = sizeof(trace_regs), + }; + return do_ptrace(PTRACE_GETREGSET, (void *)(uintptr_t)NT_PRSTATUS, &iov_regs); +} + +#undef trace_set_regs +static long trace_set_regs(void *vregs) +{ + struct iovec iov_regs = { + .iov_base = vregs, + .iov_len = sizeof(trace_regs), + }; + return do_ptrace(PTRACE_SETREGSET, (void *)(uintptr_t)NT_PRSTATUS, &iov_regs); +} + +static unsigned long trace_arg(void *vregs, int num) +{ + trace_regs *regs = vregs; + if (num < 7) + return regs->regs[num - 1]; /* x0 - x5 */ + else + return -1; +} + +static int trace_get_sysnum(void *vregs) +{ + int nr; + struct iovec iov_nr = { + .iov_base = &nr, + .iov_len = sizeof(nr), + }; + do_ptrace(PTRACE_GETREGSET, (void *)(uintptr_t)NT_ARM_SYSTEM_CALL, &iov_nr); + return nr; +} + +static void trace_set_sysnum(void *vregs, int nr) +{ + struct iovec iov_nr = { + .iov_base = &nr, + .iov_len = sizeof(nr), + }; + do_ptrace(PTRACE_SETREGSET, (void *)(uintptr_t)NT_ARM_SYSTEM_CALL, &iov_nr); +} diff --git a/libsandbox/trace/linux/arch.c b/libsandbox/trace/linux/arch.c index 4b3d615..16cdf9b 100644 --- a/libsandbox/trace/linux/arch.c +++ b/libsandbox/trace/linux/arch.c @@ -7,6 +7,8 @@ #if !defined(HAVE_PTRACE) || !defined(HAVE_SYS_PTRACE_H) || \ !defined(HAVE_SYS_USER_H) || !defined(PTRACE_SETOPTIONS) # define SB_NO_TRACE_ARCH +#elif defined(__aarch64__) +# include "aarch64.c" #elif defined(__alpha__) # include "alpha.c" #elif defined(__arm__) @@ -27,6 +29,8 @@ # include "sparc.c" #elif defined(__x86_64__) # include "x86_64.c" +#elif defined(HAVE_STRUCT_PTRACE_SYSCALL_INFO) +# include "syscall_info.c" #else # define SB_NO_TRACE_ARCH #endif diff --git a/libsandbox/trace/linux/i386.c b/libsandbox/trace/linux/i386.c index f9476aa..722efb4 100644 --- a/libsandbox/trace/linux/i386.c +++ b/libsandbox/trace/linux/i386.c @@ -8,7 +8,7 @@ static bool _trace_possible(const void *data) (ehdr->e_machine == EM_386); } -#ifdef SB_SCHIZO +#ifdef SB_PERSONALITIES static const struct syscall_entry syscall_table[] = { #define S(s) { SB_SYS_x86_##s, SB_NR_##s, #s }, #include "trace_syscalls_x86.h" diff --git a/libsandbox/trace/linux/powerpc.c b/libsandbox/trace/linux/powerpc.c index 6e9152c..b7fb76a 100644 --- a/libsandbox/trace/linux/powerpc.c +++ b/libsandbox/trace/linux/powerpc.c @@ -13,8 +13,14 @@ static long trace_raw_ret(void *vregs) static void trace_set_ret(void *vregs, int err) { trace_regs *regs = vregs; - regs->gpr[0] = -1; - regs->gpr[3] = err; + if ((regs->trap & 0xfff0) == 0x3000) { + /* ppc64 */ + regs->gpr[3] = -err; + } else { + /* ppc32 */ + regs->gpr[3] = err; + regs->ccr |= 0x10000000; + } trace_set_regs(regs); } diff --git a/libsandbox/trace/linux/s390.c b/libsandbox/trace/linux/s390.c index acbf894..2c7a9be 100644 --- a/libsandbox/trace/linux/s390.c +++ b/libsandbox/trace/linux/s390.c @@ -1,10 +1,10 @@ #undef _trace_possible #define _trace_possible _trace_possible -#ifdef SB_SCHIZO +#ifdef SB_PERSONALITIES static const struct syscall_entry syscall_table_32[] = { -#ifdef SB_SCHIZO_s390 +#ifdef SB_PERSONALITIES_s390 #define S(s) { SB_SYS_s390_##s, SB_NR_##s, #s }, #include "trace_syscalls_s390.h" #undef S @@ -12,7 +12,7 @@ static const struct syscall_entry syscall_table_32[] = { { SB_NR_UNDEF, SB_NR_UNDEF, NULL }, }; static const struct syscall_entry syscall_table_64[] = { -#ifdef SB_SCHIZO_s390x +#ifdef SB_PERSONALITIES_s390x #define S(s) { SB_SYS_s390x_##s, SB_NR_##s, #s }, #include "trace_syscalls_s390x.h" #undef S diff --git a/libsandbox/trace/linux/sparc.c b/libsandbox/trace/linux/sparc.c index b59a036..6050dc9 100644 --- a/libsandbox/trace/linux/sparc.c +++ b/libsandbox/trace/linux/sparc.c @@ -1,6 +1,3 @@ -#define SB_NO_TRACE_ARCH -#if 0 /* XXX: broken sometimes #293632 */ - /* Since sparc's g0 register is hardcoded to 0 in the ISA, the kernel does not * bother copying it out when using the regs ptrace. Instead it shifts things * by one and stores [g1..g7] in [0..6] and [o0..o7] in [7..14] (leaving the @@ -16,11 +13,86 @@ #define U_REG_G1 0 #define U_REG_O0 7 +#undef _trace_possible +#define _trace_possible _trace_possible + +#ifdef SB_PERSONALITIES + +static const struct syscall_entry syscall_table_32[] = { +#ifdef SB_PERSONALITIES_sparc +#define S(s) { SB_SYS_sparc_##s, SB_NR_##s, #s }, +#include "trace_syscalls_sparc.h" +#undef S +#endif + { SB_NR_UNDEF, SB_NR_UNDEF, NULL }, +}; +static const struct syscall_entry syscall_table_64[] = { +#ifdef SB_PERSONALITIES_sparc64 +#define S(s) { SB_SYS_sparc64_##s, SB_NR_##s, #s }, +#include "trace_syscalls_sparc64.h" +#undef S +#endif + { SB_NR_UNDEF, SB_NR_UNDEF, NULL }, +}; + +static bool pers_is_32(trace_regs *regs) +{ +#ifdef __arch64__ + /* Sparc does not make it easy to detect 32-bit vs 64-bit. + * Inspect the syscall trap insn to see which one it is. + */ + unsigned long ret = do_ptrace(PTRACE_PEEKTEXT, (void *)regs->tpc, NULL); + return (ret >> 32) == 0x91d02010; +#else + return true; +#endif +} + +static const struct syscall_entry *trace_check_personality(void *vregs) +{ + trace_regs *regs = vregs; + if (pers_is_32(regs)) + return syscall_table_32; + else + return syscall_table_64; +} + +static bool _trace_possible(const void *data) +{ +#ifdef __arch64__ + /* sparc64 can trace sparc32. */ + return true; +#else + /* sparc32 can only trace sparc32 :(. */ + const Elf64_Ehdr *ehdr = data; + return ehdr->e_ident[EI_CLASS] == ELFCLASS32; +#endif +} + +#else + +static bool _trace_possible(const void *data) +{ + const Elf64_Ehdr *ehdr = data; +#ifdef __arch64__ + return ehdr->e_ident[EI_CLASS] == ELFCLASS64; +#else + return ehdr->e_ident[EI_CLASS] == ELFCLASS32; +#endif +} + +#endif + /* Sparc systems have swapped the addr/data args. */ #undef trace_get_regs -#define trace_get_regs(regs) do_ptrace(PTRACE_GETREGS, regs, NULL) #undef trace_set_regs -#define trace_set_regs(regs) do_ptrace(PTRACE_SETREGS, regs, NULL) +#ifdef __arch64__ +# define trace_get_regs(regs) do_ptrace(PTRACE_GETREGS64, regs, NULL) +# define trace_set_regs(regs) do_ptrace(PTRACE_SETREGS64, regs, NULL) +#else +# define trace_get_regs(regs) do_ptrace(PTRACE_GETREGS, regs, NULL) +# define trace_set_regs(regs) do_ptrace(PTRACE_SETREGS, regs, NULL) +#endif #define trace_reg_sysnum u_regs[U_REG_G1] @@ -33,8 +105,12 @@ static long trace_raw_ret(void *vregs) static void trace_set_ret(void *vregs, int err) { trace_regs *regs = vregs; +#ifndef __arch64__ /* The carry bit is used to flag errors. */ regs->psr |= PSR_C; +#else + regs->tstate |= 0x1100000000; +#endif /* Userland negates the value on sparc. */ regs->u_regs[U_REG_O0] = err; trace_set_regs(regs); @@ -48,5 +124,3 @@ static unsigned long trace_arg(void *vregs, int num) else return -1; } - -#endif diff --git a/libsandbox/trace/linux/syscall_info.c b/libsandbox/trace/linux/syscall_info.c new file mode 100644 index 0000000..23cd509 --- /dev/null +++ b/libsandbox/trace/linux/syscall_info.c @@ -0,0 +1,24 @@ +#undef trace_regs +#define trace_regs struct ptrace_syscall_info + +#define trace_reg_sysnum entry.nr +#define trace_reg_ret exit.rval + +#undef trace_get_regs +#define trace_get_regs(regs) do_ptrace(PTRACE_GET_SYSCALL_INFO, (void *)(uintptr_t)sizeof(trace_regs), regs) + +static unsigned long trace_arg(void *vregs, int num) +{ + trace_regs *regs = vregs; + if (num < 7) + return regs->entry.args[num - 1]; + else + return -1; +} + +#undef trace_set_regs +static long trace_set_regs(void *vregs) +{ + sb_ewarn("sandbox: Unable to block violation\n"); + return 0; +} diff --git a/libsandbox/trace/linux/x86_64.c b/libsandbox/trace/linux/x86_64.c index aff1edb..d01e00f 100644 --- a/libsandbox/trace/linux/x86_64.c +++ b/libsandbox/trace/linux/x86_64.c @@ -1,10 +1,10 @@ #undef _trace_possible #define _trace_possible _trace_possible -#ifdef SB_SCHIZO +#ifdef SB_PERSONALITIES static const struct syscall_entry syscall_table_32[] = { -#ifdef SB_SCHIZO_x86 +#ifdef SB_PERSONALITIES_x86 #define S(s) { SB_SYS_x86_##s, SB_NR_##s, #s }, #include "trace_syscalls_x86.h" #undef S @@ -12,7 +12,7 @@ static const struct syscall_entry syscall_table_32[] = { { SB_NR_UNDEF, SB_NR_UNDEF, NULL }, }; static const struct syscall_entry syscall_table_64[] = { -#ifdef SB_SCHIZO_x86_64 +#ifdef SB_PERSONALITIES_x86_64 #define S(s) { SB_SYS_x86_64_##s, SB_NR_##s, #s }, #include "trace_syscalls_x86_64.h" #undef S @@ -20,7 +20,7 @@ static const struct syscall_entry syscall_table_64[] = { { SB_NR_UNDEF, SB_NR_UNDEF, NULL }, }; static const struct syscall_entry syscall_table_x32[] = { -#ifdef SB_SCHIZO_x32 +#ifdef SB_PERSONALITIES_x32 #define S(s) { SB_SYS_x32_##s, SB_NR_##s, #s }, #include "trace_syscalls_x32.h" #undef S diff --git a/libsandbox/wrapper-funcs/__64_post.h b/libsandbox/wrapper-funcs/__64_post.h deleted file mode 100644 index 82d2a16..0000000 --- a/libsandbox/wrapper-funcs/__64_post.h +++ /dev/null @@ -1,4 +0,0 @@ -#undef SB64 -#undef stat -#undef lstat -#undef off_t diff --git a/libsandbox/wrapper-funcs/__64_pre.h b/libsandbox/wrapper-funcs/__64_pre.h deleted file mode 100644 index 0b34b25..0000000 --- a/libsandbox/wrapper-funcs/__64_pre.h +++ /dev/null @@ -1,4 +0,0 @@ -#define SB64 -#define stat stat64 -#define lstat lstat64 -#define off_t off64_t diff --git a/libsandbox/wrapper-funcs/__futimesat64.c b/libsandbox/wrapper-funcs/__futimesat64.c new file mode 100644 index 0000000..9ad791e --- /dev/null +++ b/libsandbox/wrapper-funcs/__futimesat64.c @@ -0,0 +1,13 @@ +/* + * __futimesat64() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +/* + * NB: Reusing the 32-bit time interface isn't entirely correct as the 64-bit time interface uses a + * different structure, but we never decode the time values in sandbox, so it doesn't matter to use. + */ +#define WRAPPER_SAFE() _SB_SAFE_AT(SB_NR_FUTIMESAT, STRING_NAME, dirfd, filename, 0) +#include "futimesat.c" diff --git a/libsandbox/wrapper-funcs/__futimesat_time64.c b/libsandbox/wrapper-funcs/__futimesat_time64.c new file mode 120000 index 0000000..c3a9b23 --- /dev/null +++ b/libsandbox/wrapper-funcs/__futimesat_time64.c @@ -0,0 +1 @@ +__futimesat64.c
\ No newline at end of file diff --git a/libsandbox/wrapper-funcs/__lutimes64.c b/libsandbox/wrapper-funcs/__lutimes64.c new file mode 100644 index 0000000..edab47c --- /dev/null +++ b/libsandbox/wrapper-funcs/__lutimes64.c @@ -0,0 +1,13 @@ +/* + * __lutimes64() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +/* + * NB: Reusing the 32-bit time interface isn't entirely correct as the 64-bit time interface uses a + * different structure, but we never decode the time values in sandbox, so it doesn't matter to use. + */ +#define WRAPPER_SAFE() _SB_SAFE(SB_NR_LUTIMES, STRING_NAME, filename) +#include "lutimes.c" diff --git a/libsandbox/wrapper-funcs/__lutimes_time64.c b/libsandbox/wrapper-funcs/__lutimes_time64.c new file mode 120000 index 0000000..1819ce7 --- /dev/null +++ b/libsandbox/wrapper-funcs/__lutimes_time64.c @@ -0,0 +1 @@ +__lutimes64.c
\ No newline at end of file diff --git a/libsandbox/wrapper-funcs/__open64_2.c b/libsandbox/wrapper-funcs/__open64_2.c index bdbd5d8..52daff1 100644 --- a/libsandbox/wrapper-funcs/__open64_2.c +++ b/libsandbox/wrapper-funcs/__open64_2.c @@ -5,6 +5,6 @@ * Licensed under the GPL-2 */ -#include "__64_pre.h" +#define sb_openat_pre_check sb_openat64_pre_check #include "__open_2.c" -#include "__64_post.h" +#undef sb_openat_pre_check diff --git a/libsandbox/wrapper-funcs/__openat64_2.c b/libsandbox/wrapper-funcs/__openat64_2.c index 445164a..ccc4fdd 100644 --- a/libsandbox/wrapper-funcs/__openat64_2.c +++ b/libsandbox/wrapper-funcs/__openat64_2.c @@ -5,6 +5,6 @@ * Licensed under the GPL-2 */ -#include "__64_pre.h" +#define sb_openat_pre_check sb_openat64_pre_check #include "__openat_2.c" -#include "__64_post.h" +#undef sb_openat_pre_check diff --git a/libsandbox/wrapper-funcs/__openat_2.c b/libsandbox/wrapper-funcs/__openat_2.c index 4549a23..f2e85ea 100644 --- a/libsandbox/wrapper-funcs/__openat_2.c +++ b/libsandbox/wrapper-funcs/__openat_2.c @@ -13,11 +13,7 @@ # define dirfd AT_FDCWD #endif -#ifdef SB64 -# define WRAPPER_PRE_CHECKS() sb_openat64_pre_check(STRING_NAME, pathname, dirfd, flags) -#else -# define WRAPPER_PRE_CHECKS() sb_openat_pre_check(STRING_NAME, pathname, dirfd, flags) -#endif +#define WRAPPER_PRE_CHECKS() sb_openat_pre_check(STRING_NAME, pathname, dirfd, flags) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/__pre_check.c b/libsandbox/wrapper-funcs/__pre_check.c deleted file mode 100644 index 28ad91f..0000000 --- a/libsandbox/wrapper-funcs/__pre_check.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * make sure some pre-checks are pulled in when needed - * - * Copyright 1999-2009 Gentoo Foundation - * Licensed under the GPL-2 - */ - -#if SB_NR_MKDIR != SB_NR_UNDEF && SB_NR_MKDIRAT == SB_NR_UNDEF -# include "mkdirat_pre_check.c" -#endif - -#if SB_NR_OPEN != SB_NR_UNDEF && SB_NR_OPENAT == SB_NR_UNDEF -# include "openat_pre_check.c" -#endif - -#if SB_NR_OPEN64 != SB_NR_UNDEF && SB_NR_OPENAT64 == SB_NR_UNDEF -# include "openat64_pre_check.c" -#endif - -#if SB_NR_UNLINK != SB_NR_UNDEF && SB_NR_UNLINKAT == SB_NR_UNDEF -# include "unlinkat_pre_check.c" -#endif - -#include "__pre_at_check.c" diff --git a/libsandbox/wrapper-funcs/__utime64.c b/libsandbox/wrapper-funcs/__utime64.c new file mode 100644 index 0000000..4e1b573 --- /dev/null +++ b/libsandbox/wrapper-funcs/__utime64.c @@ -0,0 +1,13 @@ +/* + * __utime64() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +/* + * NB: Reusing the 32-bit time interface isn't entirely correct as the 64-bit time interface uses a + * different structure, but we never decode the time values in sandbox, so it doesn't matter to use. + */ +#define WRAPPER_SAFE() _SB_SAFE(SB_NR_UTIME, STRING_NAME, filename) +#include "utime.c" diff --git a/libsandbox/wrapper-funcs/__utimensat64.c b/libsandbox/wrapper-funcs/__utimensat64.c new file mode 100644 index 0000000..4ef1c69 --- /dev/null +++ b/libsandbox/wrapper-funcs/__utimensat64.c @@ -0,0 +1,13 @@ +/* + * __utimensat64() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +/* + * NB: Reusing the 32-bit time interface isn't entirely correct as the 64-bit time interface uses a + * different structure, but we never decode the time values in sandbox, so it doesn't matter to use. + */ +#define WRAPPER_SAFE() _SB_SAFE_AT(SB_NR_UTIMENSAT, STRING_NAME, dirfd, filename, flags) +#include "utimensat.c" diff --git a/libsandbox/wrapper-funcs/__utimensat_time64.c b/libsandbox/wrapper-funcs/__utimensat_time64.c new file mode 120000 index 0000000..2dceb14 --- /dev/null +++ b/libsandbox/wrapper-funcs/__utimensat_time64.c @@ -0,0 +1 @@ +__utimensat64.c
\ No newline at end of file diff --git a/libsandbox/wrapper-funcs/__utimes64.c b/libsandbox/wrapper-funcs/__utimes64.c new file mode 100644 index 0000000..3fa6688 --- /dev/null +++ b/libsandbox/wrapper-funcs/__utimes64.c @@ -0,0 +1,13 @@ +/* + * __utimes64() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +/* + * NB: Reusing the 32-bit time interface isn't entirely correct as the 64-bit time interface uses a + * different structure, but we never decode the time values in sandbox, so it doesn't matter to use. + */ +#define WRAPPER_SAFE() _SB_SAFE(SB_NR_UTIMES, STRING_NAME, filename) +#include "utimes.c" diff --git a/libsandbox/wrapper-funcs/__utimes_time64.c b/libsandbox/wrapper-funcs/__utimes_time64.c new file mode 120000 index 0000000..3dea445 --- /dev/null +++ b/libsandbox/wrapper-funcs/__utimes_time64.c @@ -0,0 +1 @@ +__utimes64.c
\ No newline at end of file diff --git a/libsandbox/wrapper-funcs/__wrapper_exec.c b/libsandbox/wrapper-funcs/__wrapper_exec.c index 974156e..f603257 100644 --- a/libsandbox/wrapper-funcs/__wrapper_exec.c +++ b/libsandbox/wrapper-funcs/__wrapper_exec.c @@ -27,14 +27,18 @@ static bool sb_check_exec(const char *filename, char *const argv[]) { int fd; unsigned char *elf; - struct stat st; + struct stat64 st; bool do_trace = false; bool run_in_process = true; + sandbox_method_t method = get_sandbox_method(); + + if (unlikely(method == SANDBOX_METHOD_PRELOAD)) + return true; fd = sb_unwrapped_open_DEFAULT(filename, O_RDONLY|O_CLOEXEC, 0); if (fd == -1) return true; - if (fstat(fd, &st)) + if (fstat64(fd, &st)) goto out_fd; if (st.st_size < sizeof(Elf64_Ehdr)) goto out_fd; @@ -65,7 +69,7 @@ static bool sb_check_exec(const char *filename, char *const argv[]) run_in_process = false; /* We also need to ptrace programs that interpose their own allocator. - * http://crbug.com/586444 + * https://crbug.com/586444 */ if (run_in_process) { static const char * const libc_alloc_syms[] = { @@ -83,8 +87,8 @@ static bool sb_check_exec(const char *filename, char *const argv[]) ({ \ Elf##n##_Ehdr *ehdr = (void *)elf; \ Elf##n##_Phdr *phdr = (void *)(elf + ehdr->e_phoff); \ - Elf##n##_Addr vaddr, filesz, vsym = 0, vstr = 0, vhash = 0, vliblist = 0; \ - Elf##n##_Off offset, symoff = 0, stroff = 0, hashoff = 0, liblistoff = 0; \ + Elf##n##_Addr vaddr, filesz, vsym = 0, vstr = 0, vhash = 0, vgnuhash = 0; \ + Elf##n##_Off offset, symoff = 0, stroff = 0, hashoff = 0, gnuhashoff = 0; \ Elf##n##_Dyn *dyn; \ Elf##n##_Sym *sym, *symend; \ uint##n##_t ent_size = 0, str_size = 0; \ @@ -107,7 +111,7 @@ static bool sb_check_exec(const char *filename, char *const argv[]) case DT_STRTAB: vstr = dyn->d_un.d_val; break; \ case DT_STRSZ: str_size = dyn->d_un.d_val; break; \ case DT_HASH: vhash = dyn->d_un.d_val; break; \ - case DT_GNU_LIBLIST: vliblist = dyn->d_un.d_val; break; \ + case DT_GNU_HASH: vgnuhash = dyn->d_un.d_val; break; \ } \ ++dyn; \ } \ @@ -127,8 +131,8 @@ static bool sb_check_exec(const char *filename, char *const argv[]) stroff = offset + (vstr - vaddr); \ if (vhash >= vaddr && vhash < vaddr + filesz) \ hashoff = offset + (vhash - vaddr); \ - if (vliblist >= vaddr && vliblist < vaddr + filesz) \ - liblistoff = offset + (vliblist - vaddr); \ + if (vgnuhash >= vaddr && vgnuhash < vaddr + filesz) \ + gnuhashoff = offset + (vgnuhash - vaddr); \ } \ \ /* Finally walk the symbol table. This should generally be fast as \ @@ -137,47 +141,81 @@ static bool sb_check_exec(const char *filename, char *const argv[]) */ \ if (symoff && stroff) { \ /* Nowhere is the # of symbols recorded, or the size of the symbol \ - * table. Instead, we do what glibc does: use the sysv hash table \ - * if it exists, else assume that the string table always directly \ + * table. Instead, we do what glibc does: use the gnu or sysv hash \ + * table if it exists, else assume that the string table always directly \ * follows the symbol table. This seems like a poor assumption to \ * make, but glibc has gotten by this long. See determine_info in \ * glibc's elf/dl-addr.c. \ * \ - * Turns out prelink will violate that assumption. Fortunately it \ - * will insert its liblist at the same location all the time -- it \ - * replaces the string table with its liblist table. \ - * \ - * Long term, we should behave the same as glibc and walk the gnu \ - * hash table first before falling back to the raw symbol table. \ - * \ * We don't sanity check the ranges here as you aren't executing \ * corrupt programs in the sandbox. \ */ \ sym = (void *)(elf + symoff); \ - if (vhash) { \ - /* Hash entries are always 32-bits. */ \ - uint32_t *hashes = (void *)(elf + hashoff); \ - symend = sym + hashes[1]; \ - } else if (vliblist) \ - symend = (void *)(elf + liblistoff); \ - else \ - symend = (void *)(elf + stroff); \ - \ - while (sym < symend) { \ - char *symname = (void *)(elf + stroff + sym->st_name); \ - if (ELF##n##_ST_VISIBILITY(sym->st_other) == STV_DEFAULT && \ - sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE && \ - sym->st_name && \ - /* Minor optimization to avoid strcmp. */ \ - symname[0] == '_' && symname[1] == '_') { \ - /* Blacklist internal C library symbols. */ \ - for (i = 0; i < ARRAY_SIZE(libc_alloc_syms); ++i) \ - if (!strcmp(symname, libc_alloc_syms[i])) { \ - run_in_process = false; \ - goto use_trace; \ - } \ + if (vgnuhash) { \ + uint32_t *hash32 = (void *)(elf + gnuhashoff); \ + /* use glibc's elf/dl-lookup.c:_dl_setup_hash() as a reference */ \ + /* DT_GNU_HASH header: */ \ + uint32_t nbuckets = *hash32++; \ + uint32_t symbias = *hash32++; \ + uint32_t bitmask_nwords = *hash32++; \ + hash32++; /* gnu_shift */ \ + hash32 += n / 32 * bitmask_nwords; /* gnu_bitmask */ \ + uint32_t *gnu_buckets = hash32; \ + hash32 += nbuckets; \ + uint32_t *gnu_chain_zero = hash32 - symbias; \ + \ + uint32_t bucket; \ + \ + for (bucket = 0; bucket < nbuckets; bucket++) { \ + uint32_t symndx = gnu_buckets[bucket]; \ + if (symndx != 0) { \ + const uint32_t *hasharr = &gnu_chain_zero[symndx]; \ + do { \ + Elf##n##_Sym * s = &sym[symndx]; \ + \ + /* keep in sync with 'vhash' case */ \ + char *symname = (void *)(elf + stroff + s->st_name); \ + if (ELF##n##_ST_VISIBILITY(s->st_other) == STV_DEFAULT && \ + s->st_shndx != SHN_UNDEF && s->st_shndx < SHN_LORESERVE && \ + s->st_name && \ + /* Minor optimization to avoid strcmp. */ \ + symname[0] == '_' && symname[1] == '_') { \ + /* Blacklist internal C library symbols. */ \ + for (i = 0; i < ARRAY_SIZE(libc_alloc_syms); ++i) \ + if (!strcmp(symname, libc_alloc_syms[i])) { \ + run_in_process = false; \ + goto use_trace; \ + } \ + } \ + ++symndx; \ + } while ((*hasharr++ & 1u) == 0); \ + } \ + } \ + } else { \ + if (vhash) { \ + /* Hash entries are always 32-bits. */ \ + uint32_t *hashes = (void *)(elf + hashoff); \ + symend = sym + hashes[1]; \ + } else \ + symend = (void *)(elf + stroff); \ + \ + while (sym < symend) { \ + /* keep insync with 'vgnuhash' case */ \ + char *symname = (void *)(elf + stroff + sym->st_name); \ + if (ELF##n##_ST_VISIBILITY(sym->st_other) == STV_DEFAULT && \ + sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE && \ + sym->st_name && \ + /* Minor optimization to avoid strcmp. */ \ + symname[0] == '_' && symname[1] == '_') { \ + /* Blacklist internal C library symbols. */ \ + for (i = 0; i < ARRAY_SIZE(libc_alloc_syms); ++i) \ + if (!strcmp(symname, libc_alloc_syms[i])) { \ + run_in_process = false; \ + goto use_trace; \ + } \ + } \ + ++sym; \ } \ - ++sym; \ } \ } \ \ @@ -204,8 +242,10 @@ static bool sb_check_exec(const char *filename, char *const argv[]) out_fd: close(fd); - if (do_trace) - trace_main(filename, argv); + if (do_trace) { + sb_debug_dyn("tracing: %s\n", filename); + trace_main(); + } return run_in_process; } diff --git a/libsandbox/wrapper-funcs/fchmod.c b/libsandbox/wrapper-funcs/fchmod.c new file mode 100644 index 0000000..04bfcea --- /dev/null +++ b/libsandbox/wrapper-funcs/fchmod.c @@ -0,0 +1,11 @@ +/* + * fchmod() wrapper. + * + * Copyright 1999-2018 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#define WRAPPER_ARGS_PROTO int fd, mode_t mode +#define WRAPPER_ARGS fd, mode +#define WRAPPER_SAFE() SB_SAFE_FD(fd) +#include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/fchown.c b/libsandbox/wrapper-funcs/fchown.c new file mode 100644 index 0000000..ab79d5c --- /dev/null +++ b/libsandbox/wrapper-funcs/fchown.c @@ -0,0 +1,11 @@ +/* + * fchown() wrapper. + * + * Copyright 1999-2018 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#define WRAPPER_ARGS_PROTO int fd, uid_t owner, gid_t group +#define WRAPPER_ARGS fd, owner, group +#define WRAPPER_SAFE() SB_SAFE_FD(fd) +#include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/fopen.c b/libsandbox/wrapper-funcs/fopen.c index ce2fdf3..5d36ffa 100644 --- a/libsandbox/wrapper-funcs/fopen.c +++ b/libsandbox/wrapper-funcs/fopen.c @@ -10,11 +10,6 @@ #define WRAPPER_SAFE() SB_SAFE_OPEN_CHAR(pathname, mode) #define WRAPPER_RET_TYPE FILE * #define WRAPPER_RET_DEFAULT NULL - -#ifdef SB64 -# define WRAPPER_PRE_CHECKS() sb_fopen64_pre_check(STRING_NAME, pathname, mode) -#else -# define WRAPPER_PRE_CHECKS() sb_fopen_pre_check(STRING_NAME, pathname, mode) -#endif +#define WRAPPER_PRE_CHECKS() sb_fopen_pre_check(STRING_NAME, pathname, mode) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/fopen64.c b/libsandbox/wrapper-funcs/fopen64.c index 8e0cdb0..c9b42ef 100644 --- a/libsandbox/wrapper-funcs/fopen64.c +++ b/libsandbox/wrapper-funcs/fopen64.c @@ -5,6 +5,6 @@ * Licensed under the GPL-2 */ -#include "__64_pre.h" +#define sb_fopen_pre_check sb_fopen64_pre_check #include "fopen.c" -#include "__64_post.h" +#undef sb_fopen_pre_check diff --git a/libsandbox/wrapper-funcs/fopen64_pre_check.c b/libsandbox/wrapper-funcs/fopen64_pre_check.c index 3f7a737..4dbd171 100644 --- a/libsandbox/wrapper-funcs/fopen64_pre_check.c +++ b/libsandbox/wrapper-funcs/fopen64_pre_check.c @@ -5,8 +5,6 @@ * Licensed under the GPL-2 */ -#include "__64_pre.h" #define sb_fopen_pre_check sb_fopen64_pre_check #include "fopen_pre_check.c" #undef sb_fopen_pre_check -#include "__64_post.h" diff --git a/libsandbox/wrapper-funcs/fopen_pre_check.c b/libsandbox/wrapper-funcs/fopen_pre_check.c index 765526e..e3ed2c6 100644 --- a/libsandbox/wrapper-funcs/fopen_pre_check.c +++ b/libsandbox/wrapper-funcs/fopen_pre_check.c @@ -11,8 +11,7 @@ bool sb_fopen_pre_check(const char *func, const char *pathname, const char *mode save_errno(); /* If we're trying to read, fail normally if file does not stat */ - struct stat st; - if (-1 == stat(pathname, &st)) { + if (sb_exists(AT_FDCWD, pathname, 0) == -1) { sb_debug_dyn("EARLY FAIL: %s(%s): %s\n", func, pathname, strerror(errno)); return false; diff --git a/libsandbox/wrapper-funcs/futimesat.c b/libsandbox/wrapper-funcs/futimesat.c index d549e6a..bc1a966 100644 --- a/libsandbox/wrapper-funcs/futimesat.c +++ b/libsandbox/wrapper-funcs/futimesat.c @@ -5,7 +5,9 @@ * Licensed under the GPL-2 */ -#define WRAPPER_ARGS_PROTO int dirfd, const char *filename, const struct timeval times[] +#define WRAPPER_ARGS_PROTO int dirfd, const char *filename, const struct timeval times[2] #define WRAPPER_ARGS dirfd, filename, times -#define WRAPPER_SAFE() SB_SAFE_AT(dirfd, filename, 0) +#ifndef WRAPPER_SAFE +# define WRAPPER_SAFE() SB_SAFE_AT(dirfd, filename, 0) +#endif #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/lremovexattr.c b/libsandbox/wrapper-funcs/lremovexattr.c new file mode 100644 index 0000000..bfcb931 --- /dev/null +++ b/libsandbox/wrapper-funcs/lremovexattr.c @@ -0,0 +1,11 @@ +/* + * lremovexattr() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#define WRAPPER_ARGS_PROTO const char *path, const char *name +#define WRAPPER_ARGS path, name +#define WRAPPER_SAFE() SB_SAFE(path) +#include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/lsetxattr.c b/libsandbox/wrapper-funcs/lsetxattr.c new file mode 100644 index 0000000..2369e51 --- /dev/null +++ b/libsandbox/wrapper-funcs/lsetxattr.c @@ -0,0 +1,11 @@ +/* + * setxattr() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#define WRAPPER_ARGS_PROTO const char *path, const char *name, const void *value, size_t size, int flags +#define WRAPPER_ARGS path, name, value, size, flags +#define WRAPPER_SAFE() SB_SAFE(path) +#include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/lutimes.c b/libsandbox/wrapper-funcs/lutimes.c index c608231..fb9ae68 100644 --- a/libsandbox/wrapper-funcs/lutimes.c +++ b/libsandbox/wrapper-funcs/lutimes.c @@ -5,7 +5,9 @@ * Licensed under the GPL-2 */ -#define WRAPPER_ARGS_PROTO const char *filename, const struct timeval times[] +#define WRAPPER_ARGS_PROTO const char *filename, const struct timeval times[2] #define WRAPPER_ARGS filename, times -#define WRAPPER_SAFE() SB_SAFE(filename) +#ifndef WRAPPER_SAFE +# define WRAPPER_SAFE() SB_SAFE(filename) +#endif #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/open64.c b/libsandbox/wrapper-funcs/open64.c index 622b8ea..8b03ea8 100644 --- a/libsandbox/wrapper-funcs/open64.c +++ b/libsandbox/wrapper-funcs/open64.c @@ -5,6 +5,6 @@ * Licensed under the GPL-2 */ -#include "__64_pre.h" +#define sb_openat_pre_check sb_openat64_pre_check #include "open.c" -#include "__64_post.h" +#undef sb_openat_pre_check diff --git a/libsandbox/wrapper-funcs/openat.c b/libsandbox/wrapper-funcs/openat.c index 846c63f..d09e63d 100644 --- a/libsandbox/wrapper-funcs/openat.c +++ b/libsandbox/wrapper-funcs/openat.c @@ -16,11 +16,7 @@ # define dirfd AT_FDCWD #endif -#ifdef SB64 -# define WRAPPER_PRE_CHECKS() sb_openat64_pre_check(STRING_NAME, pathname, dirfd, flags) -#else -# define WRAPPER_PRE_CHECKS() sb_openat_pre_check(STRING_NAME, pathname, dirfd, flags) -#endif +#define WRAPPER_PRE_CHECKS() sb_openat_pre_check(STRING_NAME, pathname, dirfd, flags) #define WRAPPER_SAFE_POST_EXPAND \ int mode = 0; \ diff --git a/libsandbox/wrapper-funcs/openat64.c b/libsandbox/wrapper-funcs/openat64.c index b410af2..66c2089 100644 --- a/libsandbox/wrapper-funcs/openat64.c +++ b/libsandbox/wrapper-funcs/openat64.c @@ -5,6 +5,6 @@ * Licensed under the GPL-2 */ -#include "__64_pre.h" +#define sb_openat_pre_check sb_openat64_pre_check #include "openat.c" -#include "__64_post.h" +#undef sb_openat_pre_check diff --git a/libsandbox/wrapper-funcs/openat_pre_check.c b/libsandbox/wrapper-funcs/openat_pre_check.c deleted file mode 100644 index 5fd5eaa..0000000 --- a/libsandbox/wrapper-funcs/openat_pre_check.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * open*() pre-check. - * - * Copyright 1999-2012 Gentoo Foundation - * Licensed under the GPL-2 - */ - -bool sb_openat_pre_check(const char *func, const char *pathname, int dirfd, int flags) -{ - /* If we're not trying to create, fail normally if - * file does not stat - */ - if (flags & O_CREAT) - return true; - - save_errno(); - - /* Check incoming args against common *at issues */ - char dirfd_path[SB_PATH_MAX]; - if (!sb_common_at_pre_check(func, &pathname, dirfd, dirfd_path, sizeof(dirfd_path))) - return false; - - /* Doesn't exist -> skip permission checks */ - struct stat st; - if (((flags & O_NOFOLLOW) ? lstat(pathname, &st) : stat(pathname, &st)) == -1) { - sb_debug_dyn("EARLY FAIL: %s(%s): %s\n", - func, pathname, strerror(errno)); - return false; - } - - restore_errno(); - - return true; -} diff --git a/libsandbox/wrapper-funcs/removexattr.c b/libsandbox/wrapper-funcs/removexattr.c new file mode 100644 index 0000000..4e33fe6 --- /dev/null +++ b/libsandbox/wrapper-funcs/removexattr.c @@ -0,0 +1,11 @@ +/* + * removexattr() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#define WRAPPER_ARGS_PROTO const char *path, const char *name +#define WRAPPER_ARGS path, name +#define WRAPPER_SAFE() SB_SAFE(path) +#include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/renameat2.c b/libsandbox/wrapper-funcs/renameat2.c new file mode 100644 index 0000000..4a2e29b --- /dev/null +++ b/libsandbox/wrapper-funcs/renameat2.c @@ -0,0 +1,11 @@ +/* + * renameat2() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#define WRAPPER_ARGS_PROTO int olddirfd, const char *oldpath, int newdirfd, const char *newpath, unsigned int flags +#define WRAPPER_ARGS olddirfd, oldpath, newdirfd, newpath, flags +#define WRAPPER_SAFE() (SB_SAFE_AT(olddirfd, oldpath, 0) && SB_SAFE_AT(newdirfd, newpath, 0)) +#include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/setxattr.c b/libsandbox/wrapper-funcs/setxattr.c new file mode 100644 index 0000000..2369e51 --- /dev/null +++ b/libsandbox/wrapper-funcs/setxattr.c @@ -0,0 +1,11 @@ +/* + * setxattr() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#define WRAPPER_ARGS_PROTO const char *path, const char *name, const void *value, size_t size, int flags +#define WRAPPER_ARGS path, name, value, size, flags +#define WRAPPER_SAFE() SB_SAFE(path) +#include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/truncate.c b/libsandbox/wrapper-funcs/truncate.c index b0b5674..2bfbec7 100644 --- a/libsandbox/wrapper-funcs/truncate.c +++ b/libsandbox/wrapper-funcs/truncate.c @@ -5,7 +5,9 @@ * Licensed under the GPL-2 */ -#define WRAPPER_ARGS_PROTO const char *path, off_t length +#ifndef WRAPPER_ARGS_PROTO +# define WRAPPER_ARGS_PROTO const char *path, off_t length +#endif #define WRAPPER_ARGS path, length #define WRAPPER_SAFE() SB_SAFE(path) #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/truncate64.c b/libsandbox/wrapper-funcs/truncate64.c index a06b895..6e2266d 100644 --- a/libsandbox/wrapper-funcs/truncate64.c +++ b/libsandbox/wrapper-funcs/truncate64.c @@ -5,6 +5,5 @@ * Licensed under the GPL-2 */ -#include "__64_pre.h" +#define WRAPPER_ARGS_PROTO const char *path, off64_t length #include "truncate.c" -#include "__64_post.h" diff --git a/libsandbox/wrapper-funcs/utime.c b/libsandbox/wrapper-funcs/utime.c index f0a6814..4bbf374 100644 --- a/libsandbox/wrapper-funcs/utime.c +++ b/libsandbox/wrapper-funcs/utime.c @@ -7,5 +7,7 @@ #define WRAPPER_ARGS_PROTO const char *filename, const struct utimbuf *times #define WRAPPER_ARGS filename, times -#define WRAPPER_SAFE() SB_SAFE(filename) +#ifndef WRAPPER_SAFE +# define WRAPPER_SAFE() SB_SAFE(filename) +#endif #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/utimensat.c b/libsandbox/wrapper-funcs/utimensat.c index e0d8ee1..3baf89c 100644 --- a/libsandbox/wrapper-funcs/utimensat.c +++ b/libsandbox/wrapper-funcs/utimensat.c @@ -5,7 +5,9 @@ * Licensed under the GPL-2 */ -#define WRAPPER_ARGS_PROTO int dirfd, const char *filename, const struct timespec times[], int flags +#define WRAPPER_ARGS_PROTO int dirfd, const char *filename, const struct timespec times[2], int flags #define WRAPPER_ARGS dirfd, filename, times, flags -#define WRAPPER_SAFE() SB_SAFE_AT(dirfd, filename, flags) +#ifndef WRAPPER_SAFE +# define WRAPPER_SAFE() SB_SAFE_AT(dirfd, filename, flags) +#endif #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/utimes.c b/libsandbox/wrapper-funcs/utimes.c index c361c36..902c6f5 100644 --- a/libsandbox/wrapper-funcs/utimes.c +++ b/libsandbox/wrapper-funcs/utimes.c @@ -5,7 +5,9 @@ * Licensed under the GPL-2 */ -#define WRAPPER_ARGS_PROTO const char *filename, const struct timeval times[] +#define WRAPPER_ARGS_PROTO const char *filename, const struct timeval times[2] #define WRAPPER_ARGS filename, times -#define WRAPPER_SAFE() SB_SAFE(filename) +#ifndef WRAPPER_SAFE +# define WRAPPER_SAFE() SB_SAFE(filename) +#endif #include "__wrapper_simple.c" diff --git a/libsandbox/wrapper-funcs/vfork.c b/libsandbox/wrapper-funcs/vfork.c new file mode 100644 index 0000000..b28e74c --- /dev/null +++ b/libsandbox/wrapper-funcs/vfork.c @@ -0,0 +1,28 @@ +/* + * vfork() wrapper. + * + * Copyright 1999-2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +/* We're only wrapping vfork() as a poor man's pthread_atfork(). That would + * require dedicated linkage against libpthread. So here we force the locks + * to a consistent state before forking. + * + * We also implement vfork() as fork() because sandbox does not meet vfork() + * requirements bet ween vfork()/exec("some-static-bianary") because we launch + * ptrace in the middle. + */ + +#define WRAPPER_ARGS_PROTO +#define WRAPPER_ARGS +#define WRAPPER_SAFE() 0 +#define WRAPPER_PRE_CHECKS() \ +({ \ + /* pthread_atfork(sb_lock, sb_unlock, sb_unlock); */ \ + sb_lock(); \ + result = sb_unwrapped_fork_DEFAULT(WRAPPER_ARGS_FULL); \ + sb_unlock(); \ + false; \ +}) +#include "__wrapper_simple.c" diff --git a/libsandbox/wrappers.h b/libsandbox/wrappers.h index bf5bf64..3237397 100644 --- a/libsandbox/wrappers.h +++ b/libsandbox/wrappers.h @@ -15,6 +15,8 @@ */ #define sb_unwrapped_access sb_unwrapped_access_DEFAULT attribute_hidden int sb_unwrapped_access (const char *, int); +#define sb_unwrapped_faccessat sb_unwrapped_faccessat_DEFAULT +attribute_hidden int sb_unwrapped_faccessat (int, const char *, int, int); #define sb_unwrapped_getcwd sb_unwrapped_getcwd_DEFAULT attribute_hidden char *sb_unwrapped_getcwd (char *, size_t); #define sb_unwrapped_open sb_unwrapped_open_DEFAULT diff --git a/libsbutil/Makefile b/libsbutil/Makefile new file mode 100644 index 0000000..f5638c7 --- /dev/null +++ b/libsbutil/Makefile @@ -0,0 +1,4 @@ +# Helper for developers. +all libsbutil libsbutil.la: libsbutil/libsbutil.la ; +clean: ; rm -f *.o *.l[ao] .libs/* +%: ; $(MAKE) -C .. $@ diff --git a/libsbutil/Makefile.am b/libsbutil/Makefile.am deleted file mode 100644 index 684d126..0000000 --- a/libsbutil/Makefile.am +++ /dev/null @@ -1,70 +0,0 @@ -AUTOMAKE_OPTIONS = foreign - -AM_CPPFLAGS = \ - -I$(top_srcdir) \ - -I$(srcdir)/include \ - $(SANDBOX_DEFINES) - -LOCAL_INCLUDES = $(top_srcdir)/localdecls.h - -noinst_LTLIBRARIES = libsbutil.la - -libsbutil_la_LDFLAGS = -no-undefined -libsbutil_la_SOURCES = \ - sbutil.h \ - get_sandbox_conf.c \ - get_sandbox_confd.c \ - get_sandbox_lib.c \ - get_sandbox_rc.c \ - get_sandbox_log.c \ - get_tmp_dir.c \ - environment.c \ - sb_backtrace.c \ - sb_efuncs.c \ - sb_gdb.c \ - sb_open.c \ - sb_read.c \ - sb_write.c \ - sb_write_fd.c \ - sb_close.c \ - sb_printf.c \ - sb_proc.c \ - sb_memory.c \ - include/rcscripts/rcutil.h \ - include/rcscripts/util/str_list.h \ - include/rcscripts/util/debug.h \ - src/debug.c \ - include/rcscripts/util/string.h \ - src/string.c \ - include/rcscripts/util/file.h \ - src/file.c \ - include/rcscripts/util/config.h \ - src/config.c \ - include/rcscripts/util/dynbuf.h \ - src/dynbuf.c \ - gnulib/areadlink.h \ - gnulib/areadlink-with-size.c \ - gnulib/bitrotate.c \ - gnulib/bitrotate.h \ - gnulib/canonicalize.c \ - gnulib/canonicalize.h \ - gnulib/careadlinkat.h \ - gnulib/dosname.h \ - gnulib/file-set.c \ - gnulib/file-set.h \ - gnulib/gl-inline.h \ - gnulib/glue.h \ - gnulib/hash.c \ - gnulib/hash.h \ - gnulib/hash-pjw.c \ - gnulib/hash-pjw.h \ - gnulib/hash-triple.c \ - gnulib/hash-triple.h \ - gnulib/pathmax.h \ - gnulib/same-inode.h \ - gnulib/xalloc.h \ - gnulib/xalloc-oversized.h \ - gnulib/xgetcwd.h \ - $(LOCAL_INCLUDES) - -EXTRA_DIST = headers.h diff --git a/libsbutil/get_sandbox_conf.c b/libsbutil/get_sandbox_conf.c index af0140e..1178f8a 100644 --- a/libsbutil/get_sandbox_conf.c +++ b/libsbutil/get_sandbox_conf.c @@ -19,6 +19,7 @@ char *get_sandbox_conf(void) save_errno(); if (is_env_on(ENV_SANDBOX_TESTING)) { char *abs = getenv("abs_top_srcdir"); + sb_assert(abs != NULL); ret = xmalloc(strlen(abs) + strlen(LOCAL_SANDBOX_CONF_FILE) + 1); sprintf(ret, "%s%s", abs, LOCAL_SANDBOX_CONF_FILE); } diff --git a/libsbutil/headers.h b/libsbutil/headers.h deleted file mode 100644 index 7fcc3b2..0000000 --- a/libsbutil/headers.h +++ /dev/null @@ -1 +0,0 @@ -#include "../headers.h" diff --git a/libsbutil/include/rcscripts/util/file.h b/libsbutil/include/rcscripts/util/file.h index 197f211..8bbde00 100644 --- a/libsbutil/include/rcscripts/util/file.h +++ b/libsbutil/include/rcscripts/util/file.h @@ -23,7 +23,7 @@ bool rc_is_dir (const char *pathname, bool follow_link); /* The following functions do not care about errors - it only returns * the size/mtime of 'pathname' if it exists, and is the type requested, * or else 0. */ -off_t rc_get_size (const char *pathname, bool follow_link); +off64_t rc_get_size (const char *pathname, bool follow_link); /* The following return a pointer on success, or NULL with errno set on error. * If it returned NULL, but errno is not set, then there was no error, but diff --git a/libsbutil/local.mk b/libsbutil/local.mk new file mode 100644 index 0000000..1cb5de7 --- /dev/null +++ b/libsbutil/local.mk @@ -0,0 +1,64 @@ +noinst_LTLIBRARIES += %D%/libsbutil.la + +%C%_libsbutil_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/%D% \ + -I$(top_srcdir)/%D%/include +%C%_libsbutil_la_LDFLAGS = -no-undefined +%C%_libsbutil_la_SOURCES = \ + %D%/sbutil.h \ + %D%/get_sandbox_conf.c \ + %D%/get_sandbox_confd.c \ + %D%/get_sandbox_lib.c \ + %D%/get_sandbox_rc.c \ + %D%/get_sandbox_log.c \ + %D%/get_tmp_dir.c \ + %D%/environment.c \ + %D%/sb_backtrace.c \ + %D%/sb_efuncs.c \ + %D%/sb_exists.c \ + %D%/sb_gdb.c \ + %D%/sb_method.c \ + %D%/sb_open.c \ + %D%/sb_read.c \ + %D%/sb_write.c \ + %D%/sb_write_fd.c \ + %D%/sb_close.c \ + %D%/sb_printf.c \ + %D%/sb_proc.c \ + %D%/sb_memory.c \ + %D%/include/rcscripts/rcutil.h \ + %D%/include/rcscripts/util/str_list.h \ + %D%/include/rcscripts/util/debug.h \ + %D%/src/debug.c \ + %D%/include/rcscripts/util/string.h \ + %D%/src/string.c \ + %D%/include/rcscripts/util/file.h \ + %D%/src/file.c \ + %D%/include/rcscripts/util/config.h \ + %D%/src/config.c \ + %D%/include/rcscripts/util/dynbuf.h \ + %D%/src/dynbuf.c \ + %D%/gnulib/areadlink.h \ + %D%/gnulib/areadlink-with-size.c \ + %D%/gnulib/bitrotate.c \ + %D%/gnulib/bitrotate.h \ + %D%/gnulib/canonicalize.c \ + %D%/gnulib/canonicalize.h \ + %D%/gnulib/careadlinkat.h \ + %D%/gnulib/dosname.h \ + %D%/gnulib/file-set.c \ + %D%/gnulib/file-set.h \ + %D%/gnulib/gl-inline.h \ + %D%/gnulib/glue.h \ + %D%/gnulib/hash.c \ + %D%/gnulib/hash.h \ + %D%/gnulib/hash-pjw.c \ + %D%/gnulib/hash-pjw.h \ + %D%/gnulib/hash-triple.c \ + %D%/gnulib/hash-triple.h \ + %D%/gnulib/pathmax.h \ + %D%/gnulib/same-inode.h \ + %D%/gnulib/xalloc.h \ + %D%/gnulib/xalloc-oversized.h \ + %D%/gnulib/xgetcwd.h diff --git a/libsbutil/sb_close.c b/libsbutil/sb_close.c index 5379197..113deab 100644 --- a/libsbutil/sb_close.c +++ b/libsbutil/sb_close.c @@ -34,7 +34,7 @@ int sb_close(int fd) void sb_close_all_fds(void) { DIR *dirp; - struct dirent *de; + struct dirent64 *de; int dfd, fd; const char *fd_dir = sb_get_fd_dir(); @@ -43,7 +43,7 @@ void sb_close_all_fds(void) sb_ebort("could not process %s\n", fd_dir); dfd = dirfd(dirp); - while ((de = readdir(dirp)) != NULL) { + while ((de = readdir64(dirp)) != NULL) { if (de->d_name[0] == '.') continue; fd = atoi(de->d_name); diff --git a/libsbutil/sb_exists.c b/libsbutil/sb_exists.c new file mode 100644 index 0000000..c2171fe --- /dev/null +++ b/libsbutil/sb_exists.c @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Gentoo Authors + * Distributed under the terms of the GNU General Public License v2 + */ + +#include "headers.h" +#include "sbutil.h" + +/* Wrapper for faccessat to work around buggy behavior on musl */ +int sb_exists(int dirfd, const char *pathname, int flags) +{ + struct stat64 buf; + + if (sbio_faccessat(dirfd, pathname, F_OK, flags|AT_EACCESS) == 0) + return 0; + + /* musl's faccessat gives EINVAL when the kernel does not support + * faccessat2 and AT_SYMLINK_NOFOLLOW is set. + * https://www.openwall.com/lists/musl/2023/06/19/1 */ + if (errno != EINVAL) + return -1; + + return fstatat64(dirfd, pathname, &buf, flags); +} diff --git a/libsbutil/sb_method.c b/libsbutil/sb_method.c new file mode 100644 index 0000000..b2d62a7 --- /dev/null +++ b/libsbutil/sb_method.c @@ -0,0 +1,34 @@ +/* + * sb_method.c + * + * Util functions for sandbox method settings. + * + * Copyright 2021 Gentoo Foundation + * Licensed under the GPL-2 + */ + +#include "headers.h" +#include "sbutil.h" + +sandbox_method_t parse_sandbox_method(const char *method) +{ + if (method == NULL || streq(method, "") || streq(method, "any")) + return SANDBOX_METHOD_ANY; + + if (streq(method, "preload")) + return SANDBOX_METHOD_PRELOAD; + + return SANDBOX_METHOD_ANY; +} + +const char *str_sandbox_method(sandbox_method_t method) +{ + switch (method) { + case SANDBOX_METHOD_PRELOAD: + return "preload"; + case SANDBOX_METHOD_ANY: + return "any"; + default: + return ""; + } +} diff --git a/libsbutil/sbutil.h b/libsbutil/sbutil.h index 66c6f73..6d284f1 100644 --- a/libsbutil/sbutil.h +++ b/libsbutil/sbutil.h @@ -54,8 +54,11 @@ #define ENV_SANDBOX_WRITE "SANDBOX_WRITE" #define ENV_SANDBOX_PREDICT "SANDBOX_PREDICT" +#define ENV_SANDBOX_METHOD "SANDBOX_METHOD" #define ENV_SANDBOX_ON "SANDBOX_ON" +#define ENV_SANDBOX_INTRACTV "SANDBOX_INTRACTV" + #define ENV_SANDBOX_ACTIVE "SANDBOX_ACTIVE" #define SANDBOX_ACTIVE "armedandready" @@ -84,12 +87,20 @@ static inline bool is_env_var(const char *env, const char *var, size_t vlen) return !strncmp(env, var, vlen) && env[vlen] == '='; } +typedef enum sandbox_method_t { + SANDBOX_METHOD_ANY = 0, + SANDBOX_METHOD_PRELOAD, +} sandbox_method_t; +sandbox_method_t parse_sandbox_method(const char *); +const char *str_sandbox_method(sandbox_method_t); + /* proc helpers */ extern const char sb_fd_dir[]; #define sb_get_fd_dir() sb_fd_dir const char *sb_get_cmdline(pid_t pid); /* libsandbox need to use a wrapper for open */ +attribute_hidden extern int (*sbio_faccessat)(int, const char *, int, int); attribute_hidden extern int (*sbio_open)(const char *, int, mode_t); attribute_hidden extern FILE *(*sbio_popen)(const char *, const char *); extern const char *sbio_message_path; @@ -101,6 +112,7 @@ size_t sb_write(int fd, const void *buf, size_t count); int sb_close(int fd); void sb_close_all_fds(void); int sb_copy_file_to_fd(const char *file, int ofd); +int sb_exists(int dirfd, const char *pathname, int flags); /* Reliable output */ __printf(1, 2) void sb_printf(const char *format, ...); @@ -130,7 +142,14 @@ void sb_maybe_gdb(void); #define sb_fprintf(fp, ...) sb_fdprintf(fileno(fp), __VA_ARGS__) #define sb_vfprintf(fp, ...) sb_vfdprintf(fileno(fp), __VA_ARGS__) -/* Memory functions */ +/* + * Memory functions. + * + * NB: These are wrappers around libsbutil functions that build off memory calls that we + * implement directly (see libsandbox/memory.c). Do not add any helpers here that cannot + * be mirrored in libsandbox as attempts to pass memory between the two allocators will + * lead to corruption & crashes. + */ void *__xcalloc(size_t nmemb, size_t size, const char *file, const char *func, size_t line); void *__xmalloc(size_t size, const char *file, const char *func, size_t line); void *__xzalloc(size_t size /*, const char *file, const char *func, size_t line */); @@ -145,11 +164,23 @@ char *__xstrndup(const char *str, size_t size, const char *file, const char *fun #define xstrndup(_str, _size) __xstrndup(_str, _size, __FILE__, __func__, __LINE__) #define xalloc_die() __sb_ebort(__FILE__, __func__, __LINE__, "out of memory") +/* string helpers */ +#define streq(s1, s2) (strcmp(s1, s2) == 0) + /* errno helpers */ #define save_errno() int old_errno = errno; #define restore_errno() errno = old_errno; #define saved_errno old_errno +#define RETRY_EINTR(call) \ +({ \ + long result; \ + do { \ + result = (call); \ + } while (result == -1 && errno == EINTR); \ + result; \ +}) + #include "gnulib/canonicalize.h" #endif /* __SBUTIL_H__ */ diff --git a/libsbutil/src/debug.c b/libsbutil/src/debug.c index 42652a3..b901fe8 100644 --- a/libsbutil/src/debug.c +++ b/libsbutil/src/debug.c @@ -11,7 +11,7 @@ #include "headers.h" #include "rcscripts/rcutil.h" -volatile static int debug_errno = 0; +static volatile int debug_errno = 0; #define log_domain "sandbox" diff --git a/libsbutil/src/file.c b/libsbutil/src/file.c index 4542ae5..64a6f0e 100644 --- a/libsbutil/src/file.c +++ b/libsbutil/src/file.c @@ -15,31 +15,19 @@ bool rc_file_exists (const char *pathname) { - struct stat buf; - int retval; - - if (!check_str (pathname)) - return false; - - retval = lstat (pathname, &buf); - if (-1 != retval) - retval = true; - else - retval = false; - - return retval; + return sb_exists(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW) == 0; } bool rc_is_file (const char *pathname, bool follow_link) { - struct stat buf; + struct stat64 buf; int retval; if (!check_str (pathname)) return false; - retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf); + retval = follow_link ? stat64 (pathname, &buf) : lstat64 (pathname, &buf); if ((-1 != retval) && (S_ISREG (buf.st_mode))) retval = true; else @@ -51,13 +39,13 @@ rc_is_file (const char *pathname, bool follow_link) bool rc_is_dir (const char *pathname, bool follow_link) { - struct stat buf; + struct stat64 buf; int retval; if (!check_str (pathname)) return false; - retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf); + retval = follow_link ? stat64 (pathname, &buf) : lstat64 (pathname, &buf); if ((-1 != retval) && (S_ISDIR (buf.st_mode))) retval = true; else @@ -66,16 +54,16 @@ rc_is_dir (const char *pathname, bool follow_link) return retval; } -off_t +off64_t rc_get_size (const char *pathname, bool follow_link) { - struct stat buf; + struct stat64 buf; int retval; if (!check_str (pathname)) return 0; - retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf); + retval = follow_link ? stat64 (pathname, &buf) : lstat64 (pathname, &buf); if (-1 != retval) retval = buf.st_size; else @@ -88,7 +76,7 @@ char ** rc_ls_dir (const char *pathname, bool hidden, bool sort) { DIR *dp; - struct dirent *dir_entry; + struct dirent64 *dir_entry; char **dirlist = NULL; if (!check_arg_str (pathname)) @@ -114,7 +102,7 @@ rc_ls_dir (const char *pathname, bool hidden, bool sort) { /* Clear errno to distinguish between EOF and error */ errno = 0; - dir_entry = readdir (dp); + dir_entry = readdir64 (dp); /* Only an error if 'errno' != 0, else EOF */ if ((NULL == dir_entry) && (0 != errno)) { @@ -196,10 +184,10 @@ error: int rc_file_map (const char *filename, char **buf, size_t * bufsize) { - struct stat stats; + struct stat64 stats; int fd; - fd = open (filename, O_RDONLY); + fd = open64 (filename, O_RDONLY); if (fd < 0) { rc_errno_set (errno); @@ -207,7 +195,7 @@ rc_file_map (const char *filename, char **buf, size_t * bufsize) return -1; } - if (fstat (fd, &stats) < 0) + if (fstat64 (fd, &stats) < 0) { rc_errno_set (errno); DBG_MSG ("Failed to stat file!\n"); diff --git a/localdecls.h b/localdecls.h index ecc5856..6b727ff 100644 --- a/localdecls.h +++ b/localdecls.h @@ -11,9 +11,8 @@ /* take care of broken ld loading */ #if defined(__GLIBC__) && !defined(__UCLIBC__) -# if __GLIBC__ <= 2 && __GLIBC_MINOR__ <= 2 -# define BROKEN_RTLD_NEXT -# define LIBC 5 +# if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 2) +# error "glibc-2.3+ required" # endif # if !defined(BROKEN_RTLD_NEXT) @@ -22,14 +21,6 @@ # endif # endif -#else - -#if 0 -# if defined(__FreeBSD__) -# define BROKEN_RTLD_NEXT -# endif -#endif - #endif #ifdef PATH_MAX diff --git a/m4/ax_append_compile_flags.m4 b/m4/ax_append_compile_flags.m4 deleted file mode 100644 index 5b6f1af..0000000 --- a/m4/ax_append_compile_flags.m4 +++ /dev/null @@ -1,67 +0,0 @@ -# ============================================================================ -# https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html -# ============================================================================ -# -# SYNOPSIS -# -# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) -# -# DESCRIPTION -# -# For every FLAG1, FLAG2 it is checked whether the compiler works with the -# flag. If it does, the flag is added FLAGS-VARIABLE -# -# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. -# CFLAGS) is used. During the check the flag is always added to the -# current language's flags. -# -# If EXTRA-FLAGS is defined, it is added to the current language's default -# flags (e.g. CFLAGS) when the check is done. The check is thus made with -# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to -# force the compiler to issue an error when a bad flag is given. -# -# INPUT gives an alternative input source to AC_COMPILE_IFELSE. -# -# NOTE: This macro depends on the AX_APPEND_FLAG and -# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with -# AX_APPEND_LINK_FLAGS. -# -# LICENSE -# -# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see <https://www.gnu.org/licenses/>. -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 6 - -AC_DEFUN([AX_APPEND_COMPILE_FLAGS], -[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) -AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) -for flag in $1; do - AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4]) -done -])dnl AX_APPEND_COMPILE_FLAGS diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4 deleted file mode 100644 index e8c5312..0000000 --- a/m4/ax_append_flag.m4 +++ /dev/null @@ -1,71 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) -# -# DESCRIPTION -# -# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space -# added in between. -# -# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. -# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains -# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly -# FLAG. -# -# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. -# -# LICENSE -# -# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> -# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see <https://www.gnu.org/licenses/>. -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 7 - -AC_DEFUN([AX_APPEND_FLAG], -[dnl -AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF -AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) -AS_VAR_SET_IF(FLAGS,[ - AS_CASE([" AS_VAR_GET(FLAGS) "], - [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], - [ - AS_VAR_APPEND(FLAGS,[" $1"]) - AC_RUN_LOG([: FLAGS="$FLAGS"]) - ]) - ], - [ - AS_VAR_SET(FLAGS,[$1]) - AC_RUN_LOG([: FLAGS="$FLAGS"]) - ]) -AS_VAR_POPDEF([FLAGS])dnl -])dnl AX_APPEND_FLAG diff --git a/m4/ax_append_link_flags.m4 b/m4/ax_append_link_flags.m4 deleted file mode 100644 index 6f7f174..0000000 --- a/m4/ax_append_link_flags.m4 +++ /dev/null @@ -1,65 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) -# -# DESCRIPTION -# -# For every FLAG1, FLAG2 it is checked whether the linker works with the -# flag. If it does, the flag is added FLAGS-VARIABLE -# -# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is -# used. During the check the flag is always added to the linker's flags. -# -# If EXTRA-FLAGS is defined, it is added to the linker's default flags -# when the check is done. The check is thus made with the flags: "LDFLAGS -# EXTRA-FLAGS FLAG". This can for example be used to force the linker to -# issue an error when a bad flag is given. -# -# INPUT gives an alternative input source to AC_COMPILE_IFELSE. -# -# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG. -# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS. -# -# LICENSE -# -# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see <https://www.gnu.org/licenses/>. -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 6 - -AC_DEFUN([AX_APPEND_LINK_FLAGS], -[AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) -AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) -for flag in $1; do - AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3], [$4]) -done -])dnl AX_APPEND_LINK_FLAGS diff --git a/m4/ax_cflags_force_c89.m4 b/m4/ax_cflags_force_c89.m4 deleted file mode 100644 index 59b33c1..0000000 --- a/m4/ax_cflags_force_c89.m4 +++ /dev/null @@ -1,93 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_cflags_force_c89.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_CFLAGS_FORCE_C89 [(shellvar [,default, [A/NA]])] -# -# DESCRIPTION -# -# Try to find a compiler option that enables strict C89 mode. -# -# For the GNU CC compiler it will be -ansi -pedantic. The result is added -# to the shellvar being CFLAGS by default. -# -# Currently this macro knows about GCC, Solaris C compiler, Digital Unix C -# compiler, C for AIX Compiler, HP-UX C compiler, IRIX C compiler, NEC -# SX-5 (Super-UX 10) C compiler, and Cray J90 (Unicos 10.0.0.8) C -# compiler. -# -# - $1 shell-variable-to-add-to : CFLAGS -# - $2 add-value-if-not-found : nothing -# - $3 action-if-found : add value to shellvariable -# - $4 action-if-not-found : nothing -# -# NOTE: These macros depend on AX_APPEND_FLAG. -# -# LICENSE -# -# Copyright (c) 2009 Guido U. Draheim <guidod@gmx.de> -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see <https://www.gnu.org/licenses/>. -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 10 - -AC_DEFUN([AX_CFLAGS_FORCE_C89],[dnl -AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl -AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_force_c89])dnl -AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for C89 mode], -VAR,[VAR="no, unknown" - AC_LANG_SAVE - AC_LANG_C - ac_save_[]FLAGS="$[]FLAGS" -for ac_arg dnl -in "-pedantic % -ansi -pedantic" dnl GCC - "-xstrconst % -v -Xc" dnl Solaris C - "-std1 % -std1" dnl Digital Unix - " % -qlanglvl=ansi" dnl AIX - " % -ansi -ansiE" dnl IRIX - "+ESlit % -Aa" dnl HP-UX C - "-Xc % -Xc" dnl NEC SX-5 (Super-UX 10) - "-h conform % -h conform" dnl Cray C (Unicos) - # -do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` - AC_TRY_COMPILE([],[return 0;], - [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) -done - FLAGS="$ac_save_[]FLAGS" - AC_LANG_RESTORE -]) -AS_VAR_POPDEF([FLAGS])dnl -AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) -case ".$VAR" in - .ok|.ok,*) m4_ifvaln($3,$3) ;; - .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;; - *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;; -esac -AS_VAR_POPDEF([VAR])dnl -]) diff --git a/m4/ax_cflags_no_writable_strings.m4 b/m4/ax_cflags_no_writable_strings.m4 deleted file mode 100644 index 48831fa..0000000 --- a/m4/ax_cflags_no_writable_strings.m4 +++ /dev/null @@ -1,121 +0,0 @@ -# ================================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_cflags_no_writable_strings.html -# ================================================================================== -# -# SYNOPSIS -# -# AX_CFLAGS_NO_WRITABLE_STRINGS [(shellvar [,default, [A/NA]])] -# -# DESCRIPTION -# -# Try to find a compiler option that makes all string literals readonly. -# -# The sanity check is done by looking at string.h which has a set of -# strcpy definitions that should be defined with const-modifiers to not -# emit a warning in all so many places. -# -# For the GNU CC compiler it will be -fno-writable-strings -Wwrite-strings -# The result is added to the shellvar being CFLAGS by default. -# -# DEFAULTS: -# -# - $1 shell-variable-to-add-to : CFLAGS -# - $2 add-value-if-not-found : nothing -# - $3 action-if-found : add value to shellvariable -# - $4 action-if-not-found : nothing -# -# NOTE: These macros depend on AX_APPEND_FLAG. -# -# LICENSE -# -# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see <https://www.gnu.org/licenses/>. -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 14 - -AC_DEFUN([AX_FLAGS_NO_WRITABLE_STRINGS],[dnl -AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl -AS_VAR_PUSHDEF([VAR],[ax_cv_[]_AC_LANG_ABBREV[]flags_no_writable_strings])dnl -AC_CACHE_CHECK([m4_ifval([$1],[$1],FLAGS) making strings readonly], -VAR,[VAR="no, unknown" -ac_save_[]FLAGS="$[]FLAGS" -# IRIX C compiler: -# -use_readonly_const is the default for IRIX C, -# puts them into .rodata, but they are copied later. -# need to be "-G0 -rdatashared" for strictmode but -# I am not sure what effect that has really. - guidod -for ac_arg dnl -in "-pedantic -Werror % -fno-writable-strings -Wwrite-strings" dnl GCC - "-pedantic -Werror % -fconst-strings -Wwrite-strings" dnl newer GCC - "-pedantic % -fconst-strings %% no, const-strings is default" dnl newer GCC - "-v -Xc % -xstrconst" dnl Solaris C - strings go into readonly segment - "+w1 -Aa % +ESlit" dnl HP-UX C - strings go into readonly segment - "-w0 -std1 % -readonly_strings" dnl Digital Unix - again readonly segment - "-fullwarn -use_readonly_const %% ok, its the default" dnl IRIX C - # -do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` - AC_TRY_COMPILE([],[return 0;], - [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) -done -case ".$VAR" in - .|.no|.no,*) ;; - *) # sanity check - testing strcpy() from string.h - cp config.log config.tmp - AC_TRY_COMPILE([#include <string.h>],[ - char test[16]; - if (strcpy (test, "test")) return 1;], - dnl the original did use test -n `$CC testprogram.c` - [if test `diff config.log config.tmp | grep -i warning | wc -l` != 0 - then VAR="no, suppressed, string.h," ; fi], - [VAR="no, suppressed, string.h"]) - rm config.tmp - ;; -esac -FLAGS="$ac_save_[]FLAGS" -]) -AS_VAR_POPDEF([FLAGS])dnl -AC_REQUIRE([AX_APPEND_FLAG]) -case ".$VAR" in - .ok|.ok,*) m4_ifvaln($3,$3) ;; - .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;; - *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;; -esac -AS_VAR_POPDEF([VAR])dnl -])dnl AX_FLAGS_NO_WRITABLE_STRINGS - -AC_DEFUN([AX_CFLAGS_NO_WRITABLE_STRINGS],[dnl -AC_LANG_PUSH([C]) -AX_FLAGS_NO_WRITABLE_STRINGS([$1], [$2], [$3], [$4]) -AC_LANG_POP([C]) -]) - -AC_DEFUN([AX_CXXFLAGS_NO_WRITABLE_STRINGS],[dnl -AC_LANG_PUSH([C++]) -AX_FLAGS_NO_WRITABLE_STRINGS([$1], [$2], [$3], [$4]) -AC_LANG_POP([C++]) -]) diff --git a/m4/ax_cflags_strict_prototypes.m4 b/m4/ax_cflags_strict_prototypes.m4 deleted file mode 100644 index 1bc5db4..0000000 --- a/m4/ax_cflags_strict_prototypes.m4 +++ /dev/null @@ -1,118 +0,0 @@ -# ================================================================================ -# https://www.gnu.org/software/autoconf-archive/ax_cflags_strict_prototypes.html -# ================================================================================ -# -# SYNOPSIS -# -# AX_CFLAGS_STRICT_PROTOTYPES [(shellvar [,default, [A/NA]] -# -# DESCRIPTION -# -# Try to find a compiler option that requires strict prototypes. -# -# The sanity check is done by looking at sys/signal.h which has a set of -# macro-definitions SIG_DFL and SIG_IGN that are cast to the local -# signal-handler type. If that signal-handler type is not fully qualified -# then the system headers are not seen as strictly prototype clean. -# -# For the GNU CC compiler it will be -fstrict-prototypes -# -Wstrict-prototypes The result is added to the shellvar being CFLAGS by -# default. -# -# DEFAULTS: -# -# - $1 shell-variable-to-add-to : CFLAGS -# - $2 add-value-if-not-found : nothing -# - $3 action-if-found : add value to shellvariable -# - $4 action-if-not-found : nothing -# -# NOTE: These macros depend on AX_APPEND_FLAG. -# -# LICENSE -# -# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see <https://www.gnu.org/licenses/>. -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 16 - -AC_DEFUN([AX_FLAGS_STRICT_PROTOTYPES],[dnl -AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl -AS_VAR_PUSHDEF([VAR],[ac_cv_[]_AC_LANG_ABBREV[]flags_strict_prototypes])dnl -AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for strict prototypes], -VAR,[VAR="no, unknown" -ac_save_[]FLAGS="$[]FLAGS" -for ac_arg dnl -in "-pedantic -Werror % -fstrict-prototypes -Wstrict-prototypes" dnl GCC - "-pedantic -Werror % -Wstrict-prototypes" dnl try to warn at least - "-pedantic -Werror % -Wmissing-prototypes" dnl try to warn at least - "-pedantic -Werror % -Werror-implicit-function-declaration" dnl - "-pedantic -Werror % -Wimplicit-function-declaration" dnl - "-pedantic % -Wstrict-prototypes %% no, unsupported" dnl oops - # -do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` - AC_TRY_COMPILE([],[return 0;], - [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) -done -case ".$VAR" in - .|.no|.no,*) ;; - *) # sanity check with signal() from sys/signal.h - cp config.log config.tmp - AC_TRY_COMPILE([#include <signal.h>],[ - if (signal (SIGINT, SIG_IGN) == SIG_DFL) return 1; - if (signal (SIGINT, SIG_IGN) != SIG_DFL) return 2;], - dnl the original did use test -n `$CC testprogram.c` - [if test `diff config.log config.tmp | grep -i warning | wc -l` != 0 -then if test `diff config.log config.tmp | grep -i warning | wc -l` != 1 -then VAR="no, suppressed, signal.h," ; fi ; fi], - [VAR="no, suppressed, signal.h"]) - rm config.tmp - ;; -esac -FLAGS="$ac_save_[]FLAGS" -]) -AS_VAR_POPDEF([FLAGS])dnl -AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) -case ".$VAR" in - .ok|.ok,*) m4_ifvaln($3,$3) ;; - .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;; - *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;; -esac -AS_VAR_POPDEF([VAR])dnl -])dnl AX_FLAGS_STRICT_PROTOTYPES - -AC_DEFUN([AX_CFLAGS_STRICT_PROTOTYPES],[dnl -AC_LANG_PUSH([C]) -AX_FLAGS_STRICT_PROTOTYPES([$1], [$2], [$3], [$4]) -AC_LANG_POP([C]) -]) - -AC_DEFUN([AX_CXXFLAGS_STRICT_PROTOTYPES],[dnl -AC_LANG_PUSH([C++]) -AX_FLAGS_STRICT_PROTOTYPES([$1], [$2], [$3], [$4]) -AC_LANG_POP([C++]) -]) diff --git a/m4/ax_cflags_warn_all.m4 b/m4/ax_cflags_warn_all.m4 index 094577e..9235a18 100644 --- a/m4/ax_cflags_warn_all.m4 +++ b/m4/ax_cflags_warn_all.m4 @@ -4,33 +4,54 @@ # # SYNOPSIS # -# AX_CFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] -# AX_CXXFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] -# AX_FCFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] +# AX_CFLAGS_WARN_ALL [(shellvar[, default[, action-if-found[, action-if-not-found]]])] +# AX_CXXFLAGS_WARN_ALL [(shellvar[, default[, action-if-found[, action-if-not-found]]])] +# AX_FCFLAGS_WARN_ALL [(shellvar[, default[, action-if-found[, action-if-not-found]]])] # # DESCRIPTION # -# Try to find a compiler option that enables most reasonable warnings. +# Specify compiler options that enable most reasonable warnings. For the +# GNU Compiler Collection (GCC), for example, it will be "-Wall". The +# result is added to shellvar, one of CFLAGS, CXXFLAGS or FCFLAGS if the +# first parameter is not specified. # -# For the GNU compiler it will be -Wall (and -ansi -pedantic) The result -# is added to the shellvar being CFLAGS, CXXFLAGS, or FCFLAGS by default. +# Each of these macros accepts the following optional arguments: # -# Currently this macro knows about the GCC, Solaris, Digital Unix, AIX, -# HP-UX, IRIX, NEC SX-5 (Super-UX 10), Cray J90 (Unicos 10.0.0.8), and -# Intel compilers. For a given compiler, the Fortran flags are much more -# experimental than their C equivalents. +# - $1 - shellvar +# shell variable to use (CFLAGS, CXXFLAGS or FCFLAGS if not +# specified, depending on macro) # -# - $1 shell-variable-to-add-to : CFLAGS, CXXFLAGS, or FCFLAGS -# - $2 add-value-if-not-found : nothing -# - $3 action-if-found : add value to shellvariable -# - $4 action-if-not-found : nothing +# - $2 - default +# value to use for flags if compiler vendor cannot be determined (by +# default, "") # -# NOTE: These macros depend on AX_APPEND_FLAG. +# - $3 - action-if-found +# action to take if the compiler vendor has been successfully +# determined (by default, add the appropriate compiler flags to +# shellvar) +# +# - $4 - action-if-not-found +# action to take if the compiler vendor has not been determined or +# is unknown (by default, add the default flags, or "" if not +# specified, to shellvar) +# +# These macros use AX_COMPILER_VENDOR to determine which flags should be +# returned for a given compiler. Not all compilers currently have flags +# defined for them; patches are welcome. If need be, compiler flags may +# be made language-dependent: use a construct like the following: +# +# [vendor_name], [m4_if(_AC_LANG_PREFIX,[C], VAR="--relevant-c-flags",dnl +# m4_if(_AC_LANG_PREFIX,[CXX], VAR="--relevant-c++-flags",dnl +# m4_if(_AC_LANG_PREFIX,[FC], VAR="--relevant-fortran-flags",dnl +# VAR="$2"; FOUND="no")))], +# +# Note: These macros also depend on AX_PREPEND_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> # Copyright (c) 2010 Rhys Ulerich <rhys.ulerich@gmail.com> +# Copyright (c) 2018 John Zaitseff <J.Zaitseff@zap.org.au> # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -58,65 +79,80 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 16 +#serial 25 + +AC_DEFUN([AX_FLAGS_WARN_ALL], [ + AX_REQUIRE_DEFINED([AX_PREPEND_FLAG])dnl + AC_REQUIRE([AX_COMPILER_VENDOR])dnl + + AS_VAR_PUSHDEF([FLAGS], [m4_default($1,_AC_LANG_PREFIX[]FLAGS)])dnl + AS_VAR_PUSHDEF([VAR], [ac_cv_[]_AC_LANG_ABBREV[]flags_warn_all])dnl + AS_VAR_PUSHDEF([FOUND], [ac_save_[]_AC_LANG_ABBREV[]flags_warn_all_found])dnl + + AC_CACHE_CHECK([FLAGS for most reasonable warnings], VAR, [ + VAR="" + FOUND="yes" + dnl Cases are listed in the order found in ax_compiler_vendor.m4 + AS_CASE("$ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor", + [intel], [VAR="-w2"], + [ibm], [VAR="-qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd"], + [pathscale], [], + [clang], [VAR="-Wall"], + [cray], [VAR="-h msglevel 2"], + [fujitsu], [], + [sdcc], [], + [sx], [VAR="-pvctl[,]fullmsg"], + [portland], [], + [gnu], [VAR="-Wall"], + [sun], [VAR="-v"], + [hp], [VAR="+w1"], + [dec], [VAR="-verbose -w0 -warnprotos"], + [borland], [], + [comeau], [], + [kai], [], + [lcc], [], + [sgi], [VAR="-fullwarn"], + [microsoft], [], + [metrowerks], [], + [watcom], [], + [tcc], [], + [unknown], [ + VAR="$2" + FOUND="no" + ], + [ + AC_MSG_WARN([Unknown compiler vendor returned by [AX_COMPILER_VENDOR]]) + VAR="$2" + FOUND="no" + ] + ) + + AS_IF([test "x$FOUND" = "xyes"], [dnl + m4_default($3, [AS_IF([test "x$VAR" != "x"], [AX_PREPEND_FLAG([$VAR], [FLAGS])])]) + ], [dnl + m4_default($4, [m4_ifval($2, [AX_PREPEND_FLAG([$VAR], [FLAGS])], [true])]) + ])dnl + ])dnl -AC_DEFUN([AX_FLAGS_WARN_ALL],[dnl -AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl -AS_VAR_PUSHDEF([VAR],[ac_cv_[]_AC_LANG_ABBREV[]flags_warn_all])dnl -AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings], -VAR,[VAR="no, unknown" -ac_save_[]FLAGS="$[]FLAGS" -for ac_arg dnl -in "-warn all % -warn all" dnl Intel - "-pedantic % -Wall" dnl GCC - "-xstrconst % -v" dnl Solaris C - "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix - "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX - "-ansi -ansiE % -fullwarn" dnl IRIX - "+ESlit % +w1" dnl HP-UX C - "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10) - "-h conform % -h msglevel 2" dnl Cray C (Unicos) - # -do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` - AC_COMPILE_IFELSE([AC_LANG_PROGRAM], - [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) -done -FLAGS="$ac_save_[]FLAGS" -]) -AS_VAR_POPDEF([FLAGS])dnl -AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) -case ".$VAR" in - .ok|.ok,*) m4_ifvaln($3,$3) ;; - .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;; - *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;; -esac -AS_VAR_POPDEF([VAR])dnl + AS_VAR_POPDEF([FOUND])dnl + AS_VAR_POPDEF([VAR])dnl + AS_VAR_POPDEF([FLAGS])dnl ])dnl AX_FLAGS_WARN_ALL -dnl implementation tactics: -dnl the for-argument contains a list of options. The first part of -dnl these does only exist to detect the compiler - usually it is -dnl a global option to enable -ansi or -extrawarnings. All other -dnl compilers will fail about it. That was needed since a lot of -dnl compilers will give false positives for some option-syntax -dnl like -Woption or -Xoption as they think of it is a pass-through -dnl to later compile stages or something. The "%" is used as a -dnl delimiter. A non-option comment can be given after "%%" marks -dnl which will be shown but not added to the respective C/CXXFLAGS. -AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl -AC_LANG_PUSH([C]) -AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) -AC_LANG_POP([C]) -]) +AC_DEFUN([AX_CFLAGS_WARN_ALL], [dnl + AC_LANG_PUSH([C]) + AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) + AC_LANG_POP([C]) +])dnl -AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl -AC_LANG_PUSH([C++]) -AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) -AC_LANG_POP([C++]) -]) +AC_DEFUN([AX_CXXFLAGS_WARN_ALL], [dnl + AC_LANG_PUSH([C++]) + AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) + AC_LANG_POP([C++]) +])dnl -AC_DEFUN([AX_FCFLAGS_WARN_ALL],[dnl -AC_LANG_PUSH([Fortran]) -AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) -AC_LANG_POP([Fortran]) -]) +AC_DEFUN([AX_FCFLAGS_WARN_ALL], [dnl + AC_LANG_PUSH([Fortran]) + AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) + AC_LANG_POP([Fortran]) +])dnl diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 index dcabb92..bd753b3 100644 --- a/m4/ax_check_compile_flag.m4 +++ b/m4/ax_check_compile_flag.m4 @@ -29,33 +29,12 @@ # Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> # Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> # -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see <https://www.gnu.org/licenses/>. -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. -#serial 5 +#serial 6 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4 index 819409a..03a30ce 100644 --- a/m4/ax_check_link_flag.m4 +++ b/m4/ax_check_link_flag.m4 @@ -29,33 +29,12 @@ # Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> # Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> # -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see <https://www.gnu.org/licenses/>. -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. -#serial 5 +#serial 6 AC_DEFUN([AX_CHECK_LINK_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF diff --git a/m4/ax_compiler_flags.m4 b/m4/ax_compiler_flags.m4 deleted file mode 100644 index ddb0456..0000000 --- a/m4/ax_compiler_flags.m4 +++ /dev/null @@ -1,158 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_COMPILER_FLAGS([CFLAGS-VARIABLE], [LDFLAGS-VARIABLE], [IS-RELEASE], [EXTRA-BASE-CFLAGS], [EXTRA-YES-CFLAGS], [UNUSED], [UNUSED], [UNUSED], [EXTRA-BASE-LDFLAGS], [EXTRA-YES-LDFLAGS], [UNUSED], [UNUSED], [UNUSED]) -# -# DESCRIPTION -# -# Check for the presence of an --enable-compile-warnings option to -# configure, defaulting to "error" in normal operation, or "yes" if -# IS-RELEASE is equal to "yes". Return the value in the variable -# $ax_enable_compile_warnings. -# -# Depending on the value of --enable-compile-warnings, different compiler -# warnings are checked to see if they work with the current compiler and, -# if so, are appended to CFLAGS-VARIABLE and LDFLAGS-VARIABLE. This -# allows a consistent set of baseline compiler warnings to be used across -# a code base, irrespective of any warnings enabled locally by individual -# developers. By standardising the warnings used by all developers of a -# project, the project can commit to a zero-warnings policy, using -Werror -# to prevent compilation if new warnings are introduced. This makes -# catching bugs which are flagged by warnings a lot easier. -# -# By providing a consistent --enable-compile-warnings argument across all -# projects using this macro, continuous integration systems can easily be -# configured the same for all projects. Automated systems or build -# systems aimed at beginners may want to pass the --disable-Werror -# argument to unconditionally prevent warnings being fatal. -# -# --enable-compile-warnings can take the values: -# -# * no: Base compiler warnings only; not even -Wall. -# * yes: The above, plus a broad range of useful warnings. -# * error: The above, plus -Werror so that all warnings are fatal. -# Use --disable-Werror to override this and disable fatal -# warnings. -# -# The set of base and enabled flags can be augmented using the -# EXTRA-*-CFLAGS and EXTRA-*-LDFLAGS variables, which are tested and -# appended to the output variable if --enable-compile-warnings is not -# "no". Flags should not be disabled using these arguments, as the entire -# point of AX_COMPILER_FLAGS is to enforce a consistent set of useful -# compiler warnings on code, using warnings which have been chosen for low -# false positive rates. If a compiler emits false positives for a -# warning, a #pragma should be used in the code to disable the warning -# locally. See: -# -# https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas -# -# The EXTRA-* variables should only be used to supply extra warning flags, -# and not general purpose compiler flags, as they are controlled by -# configure options such as --disable-Werror. -# -# IS-RELEASE can be used to disable -Werror when making a release, which -# is useful for those hairy moments when you just want to get the release -# done as quickly as possible. Set it to "yes" to disable -Werror. By -# default, it uses the value of $ax_is_release, so if you are using the -# AX_IS_RELEASE macro, there is no need to pass this parameter. For -# example: -# -# AX_IS_RELEASE([git-directory]) -# AX_COMPILER_FLAGS() -# -# CFLAGS-VARIABLE defaults to WARN_CFLAGS, and LDFLAGS-VARIABLE defaults -# to WARN_LDFLAGS. Both variables are AC_SUBST-ed by this macro, but must -# be manually added to the CFLAGS and LDFLAGS variables for each target in -# the code base. -# -# If C++ language support is enabled with AC_PROG_CXX, which must occur -# before this macro in configure.ac, warning flags for the C++ compiler -# are AC_SUBST-ed as WARN_CXXFLAGS, and must be manually added to the -# CXXFLAGS variables for each target in the code base. EXTRA-*-CFLAGS can -# be used to augment the base and enabled flags. -# -# Warning flags for g-ir-scanner (from GObject Introspection) are -# AC_SUBST-ed as WARN_SCANNERFLAGS. This variable must be manually added -# to the SCANNERFLAGS variable for each GIR target in the code base. If -# extra g-ir-scanner flags need to be enabled, the AX_COMPILER_FLAGS_GIR -# macro must be invoked manually. -# -# AX_COMPILER_FLAGS may add support for other tools in future, in addition -# to the compiler and linker. No extra EXTRA-* variables will be added -# for those tools, and all extra support will still use the single -# --enable-compile-warnings configure option. For finer grained control -# over the flags for individual tools, use AX_COMPILER_FLAGS_CFLAGS, -# AX_COMPILER_FLAGS_LDFLAGS and AX_COMPILER_FLAGS_* for new tools. -# -# The UNUSED variables date from a previous version of this macro, and are -# automatically appended to the preceding non-UNUSED variable. They should -# be left empty in new uses of the macro. -# -# LICENSE -# -# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk> -# Copyright (c) 2015 David King <amigadave@amigadave.com> -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 14 - -# _AX_COMPILER_FLAGS_LANG([LANGNAME]) -m4_defun([_AX_COMPILER_FLAGS_LANG], -[m4_ifdef([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [], - [m4_define([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [])dnl - AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_]$1[FLAGS])])dnl -]) - -AC_DEFUN([AX_COMPILER_FLAGS],[ - # C support is enabled by default. - _AX_COMPILER_FLAGS_LANG([C]) - # Only enable C++ support if AC_PROG_CXX is called. The redefinition of - # AC_PROG_CXX is so that a fatal error is emitted if this macro is called - # before AC_PROG_CXX, which would otherwise cause no C++ warnings to be - # checked. - AC_PROVIDE_IFELSE([AC_PROG_CXX], - [_AX_COMPILER_FLAGS_LANG([CXX])], - [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[_AX_COMPILER_FLAGS_LANG([CXX])])]) - AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_LDFLAGS]) - - # Default value for IS-RELEASE is $ax_is_release - ax_compiler_flags_is_release=m4_tolower(m4_normalize(ifelse([$3],, - [$ax_is_release], - [$3]))) - - AC_ARG_ENABLE([compile-warnings], - AS_HELP_STRING([--enable-compile-warnings=@<:@no/yes/error@:>@], - [Enable compiler warnings and errors]),, - [AS_IF([test "$ax_compiler_flags_is_release" = "yes"], - [enable_compile_warnings="yes"], - [enable_compile_warnings="error"])]) - AC_ARG_ENABLE([Werror], - AS_HELP_STRING([--disable-Werror], - [Unconditionally make all compiler warnings non-fatal]),, - [enable_Werror=maybe]) - - # Return the user's chosen warning level - AS_IF([test "$enable_Werror" = "no" -a \ - "$enable_compile_warnings" = "error"],[ - enable_compile_warnings="yes" - ]) - - ax_enable_compile_warnings=$enable_compile_warnings - - AX_COMPILER_FLAGS_CFLAGS([$1],[$ax_compiler_flags_is_release], - [$4],[$5 $6 $7 $8]) - m4_ifdef([_AX_COMPILER_FLAGS_LANG_CXX_enabled], - [AX_COMPILER_FLAGS_CXXFLAGS([WARN_CXXFLAGS], - [$ax_compiler_flags_is_release], - [$4],[$5 $6 $7 $8])]) - AX_COMPILER_FLAGS_LDFLAGS([$2],[$ax_compiler_flags_is_release], - [$9],[$10 $11 $12 $13]) - AX_COMPILER_FLAGS_GIR([WARN_SCANNERFLAGS],[$ax_compiler_flags_is_release]) -])dnl AX_COMPILER_FLAGS diff --git a/m4/ax_compiler_flags_cflags.m4 b/m4/ax_compiler_flags_cflags.m4 deleted file mode 100644 index aeb16e3..0000000 --- a/m4/ax_compiler_flags_cflags.m4 +++ /dev/null @@ -1,140 +0,0 @@ -# ============================================================================= -# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_cflags.html -# ============================================================================= -# -# SYNOPSIS -# -# AX_COMPILER_FLAGS_CFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS]) -# -# DESCRIPTION -# -# Add warning flags for the C compiler to VARIABLE, which defaults to -# WARN_CFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be -# manually added to the CFLAGS variable for each target in the code base. -# -# This macro depends on the environment set up by AX_COMPILER_FLAGS. -# Specifically, it uses the value of $ax_enable_compile_warnings to decide -# which flags to enable. -# -# LICENSE -# -# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk> -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 14 - -AC_DEFUN([AX_COMPILER_FLAGS_CFLAGS],[ - AC_REQUIRE([AC_PROG_SED]) - AX_REQUIRE_DEFINED([AX_APPEND_COMPILE_FLAGS]) - AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) - AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) - - # Variable names - m4_define([ax_warn_cflags_variable], - [m4_normalize(ifelse([$1],,[WARN_CFLAGS],[$1]))]) - - AC_LANG_PUSH([C]) - - # Always pass -Werror=unknown-warning-option to get Clang to fail on bad - # flags, otherwise they are always appended to the warn_cflags variable, and - # Clang warns on them for every compilation unit. - # If this is passed to GCC, it will explode, so the flag must be enabled - # conditionally. - AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[ - ax_compiler_flags_test="-Werror=unknown-warning-option" - ],[ - ax_compiler_flags_test="" - ]) - - # Check that -Wno-suggest-attribute=format is supported - AX_CHECK_COMPILE_FLAG([-Wno-suggest-attribute=format],[ - ax_compiler_no_suggest_attribute_flags="-Wno-suggest-attribute=format" - ],[ - ax_compiler_no_suggest_attribute_flags="" - ]) - - # Base flags - AX_APPEND_COMPILE_FLAGS([ dnl - -fno-strict-aliasing dnl - $3 dnl - ],ax_warn_cflags_variable,[$ax_compiler_flags_test]) - - AS_IF([test "$ax_enable_compile_warnings" != "no"],[ - # "yes" flags - AX_APPEND_COMPILE_FLAGS([ dnl - -Wall dnl - -Wextra dnl - -Wundef dnl - -Wnested-externs dnl - -Wwrite-strings dnl - -Wpointer-arith dnl - -Wmissing-declarations dnl - -Wmissing-prototypes dnl - -Wstrict-prototypes dnl - -Wredundant-decls dnl - -Wno-unused-parameter dnl - -Wno-missing-field-initializers dnl - -Wdeclaration-after-statement dnl - -Wformat=2 dnl - -Wold-style-definition dnl - -Wcast-align dnl - -Wformat-nonliteral dnl - -Wformat-security dnl - -Wsign-compare dnl - -Wstrict-aliasing dnl - -Wshadow dnl - -Winline dnl - -Wpacked dnl - -Wmissing-format-attribute dnl - -Wmissing-noreturn dnl - -Winit-self dnl - -Wredundant-decls dnl - -Wmissing-include-dirs dnl - -Wunused-but-set-variable dnl - -Warray-bounds dnl - -Wimplicit-function-declaration dnl - -Wreturn-type dnl - -Wswitch-enum dnl - -Wswitch-default dnl - $4 dnl - $5 dnl - $6 dnl - $7 dnl - ],ax_warn_cflags_variable,[$ax_compiler_flags_test]) - ]) - AS_IF([test "$ax_enable_compile_warnings" = "error"],[ - # "error" flags; -Werror has to be appended unconditionally because - # it's not possible to test for - # - # suggest-attribute=format is disabled because it gives too many false - # positives - AX_APPEND_FLAG([-Werror],ax_warn_cflags_variable) - - AX_APPEND_COMPILE_FLAGS([ dnl - [$ax_compiler_no_suggest_attribute_flags] dnl - ],ax_warn_cflags_variable,[$ax_compiler_flags_test]) - ]) - - # In the flags below, when disabling specific flags, always add *both* - # -Wno-foo and -Wno-error=foo. This fixes the situation where (for example) - # we enable -Werror, disable a flag, and a build bot passes CFLAGS=-Wall, - # which effectively turns that flag back on again as an error. - for flag in $ax_warn_cflags_variable; do - AS_CASE([$flag], - [-Wno-*=*],[], - [-Wno-*],[ - AX_APPEND_COMPILE_FLAGS([-Wno-error=$(AS_ECHO([$flag]) | $SED 's/^-Wno-//')], - ax_warn_cflags_variable, - [$ax_compiler_flags_test]) - ]) - done - - AC_LANG_POP([C]) - - # Substitute the variables - AC_SUBST(ax_warn_cflags_variable) -])dnl AX_COMPILER_FLAGS diff --git a/m4/ax_compiler_flags_cxxflags.m4 b/m4/ax_compiler_flags_cxxflags.m4 deleted file mode 100644 index 3067d9b..0000000 --- a/m4/ax_compiler_flags_cxxflags.m4 +++ /dev/null @@ -1,136 +0,0 @@ -# =============================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_cxxflags.html -# =============================================================================== -# -# SYNOPSIS -# -# AX_COMPILER_FLAGS_CXXFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS]) -# -# DESCRIPTION -# -# Add warning flags for the C++ compiler to VARIABLE, which defaults to -# WARN_CXXFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be -# manually added to the CXXFLAGS variable for each target in the code -# base. -# -# This macro depends on the environment set up by AX_COMPILER_FLAGS. -# Specifically, it uses the value of $ax_enable_compile_warnings to decide -# which flags to enable. -# -# LICENSE -# -# Copyright (c) 2015 David King <amigadave@amigadave.com> -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 10 - -AC_DEFUN([AX_COMPILER_FLAGS_CXXFLAGS],[ - AC_REQUIRE([AC_PROG_SED]) - AX_REQUIRE_DEFINED([AX_APPEND_COMPILE_FLAGS]) - AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) - AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) - - # Variable names - m4_define([ax_warn_cxxflags_variable], - [m4_normalize(ifelse([$1],,[WARN_CXXFLAGS],[$1]))]) - - AC_LANG_PUSH([C++]) - - # Always pass -Werror=unknown-warning-option to get Clang to fail on bad - # flags, otherwise they are always appended to the warn_cxxflags variable, - # and Clang warns on them for every compilation unit. - # If this is passed to GCC, it will explode, so the flag must be enabled - # conditionally. - AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[ - ax_compiler_flags_test="-Werror=unknown-warning-option" - ],[ - ax_compiler_flags_test="" - ]) - - # Check that -Wno-suggest-attribute=format is supported - AX_CHECK_COMPILE_FLAG([-Wno-suggest-attribute=format],[ - ax_compiler_no_suggest_attribute_flags="-Wno-suggest-attribute=format" - ],[ - ax_compiler_no_suggest_attribute_flags="" - ]) - - # Base flags - AX_APPEND_COMPILE_FLAGS([ dnl - -fno-strict-aliasing dnl - $3 dnl - ],ax_warn_cxxflags_variable,[$ax_compiler_flags_test]) - - AS_IF([test "$ax_enable_compile_warnings" != "no"],[ - # "yes" flags - AX_APPEND_COMPILE_FLAGS([ dnl - -Wall dnl - -Wextra dnl - -Wundef dnl - -Wwrite-strings dnl - -Wpointer-arith dnl - -Wmissing-declarations dnl - -Wredundant-decls dnl - -Wno-unused-parameter dnl - -Wno-missing-field-initializers dnl - -Wformat=2 dnl - -Wcast-align dnl - -Wformat-nonliteral dnl - -Wformat-security dnl - -Wsign-compare dnl - -Wstrict-aliasing dnl - -Wshadow dnl - -Winline dnl - -Wpacked dnl - -Wmissing-format-attribute dnl - -Wmissing-noreturn dnl - -Winit-self dnl - -Wredundant-decls dnl - -Wmissing-include-dirs dnl - -Wunused-but-set-variable dnl - -Warray-bounds dnl - -Wreturn-type dnl - -Wno-overloaded-virtual dnl - -Wswitch-enum dnl - -Wswitch-default dnl - $4 dnl - $5 dnl - $6 dnl - $7 dnl - ],ax_warn_cxxflags_variable,[$ax_compiler_flags_test]) - ]) - AS_IF([test "$ax_enable_compile_warnings" = "error"],[ - # "error" flags; -Werror has to be appended unconditionally because - # it's not possible to test for - # - # suggest-attribute=format is disabled because it gives too many false - # positives - AX_APPEND_FLAG([-Werror],ax_warn_cxxflags_variable) - - AX_APPEND_COMPILE_FLAGS([ dnl - [$ax_compiler_no_suggest_attribute_flags] dnl - ],ax_warn_cxxflags_variable,[$ax_compiler_flags_test]) - ]) - - # In the flags below, when disabling specific flags, always add *both* - # -Wno-foo and -Wno-error=foo. This fixes the situation where (for example) - # we enable -Werror, disable a flag, and a build bot passes CXXFLAGS=-Wall, - # which effectively turns that flag back on again as an error. - for flag in $ax_warn_cxxflags_variable; do - AS_CASE([$flag], - [-Wno-*=*],[], - [-Wno-*],[ - AX_APPEND_COMPILE_FLAGS([-Wno-error=$(AS_ECHO([$flag]) | $SED 's/^-Wno-//')], - ax_warn_cxxflags_variable, - [$ax_compiler_flags_test]) - ]) - done - - AC_LANG_POP([C++]) - - # Substitute the variables - AC_SUBST(ax_warn_cxxflags_variable) -])dnl AX_COMPILER_FLAGS_CXXFLAGS diff --git a/m4/ax_compiler_flags_gir.m4 b/m4/ax_compiler_flags_gir.m4 deleted file mode 100644 index 5b4924a..0000000 --- a/m4/ax_compiler_flags_gir.m4 +++ /dev/null @@ -1,60 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_gir.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_COMPILER_FLAGS_GIR([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS]) -# -# DESCRIPTION -# -# Add warning flags for the g-ir-scanner (from GObject Introspection) to -# VARIABLE, which defaults to WARN_SCANNERFLAGS. VARIABLE is AC_SUBST-ed -# by this macro, but must be manually added to the SCANNERFLAGS variable -# for each GIR target in the code base. -# -# This macro depends on the environment set up by AX_COMPILER_FLAGS. -# Specifically, it uses the value of $ax_enable_compile_warnings to decide -# which flags to enable. -# -# LICENSE -# -# Copyright (c) 2015 Philip Withnall <philip@tecnocode.co.uk> -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 6 - -AC_DEFUN([AX_COMPILER_FLAGS_GIR],[ - AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) - - # Variable names - m4_define([ax_warn_scannerflags_variable], - [m4_normalize(ifelse([$1],,[WARN_SCANNERFLAGS],[$1]))]) - - # Base flags - AX_APPEND_FLAG([$3],ax_warn_scannerflags_variable) - - AS_IF([test "$ax_enable_compile_warnings" != "no"],[ - # "yes" flags - AX_APPEND_FLAG([ dnl - --warn-all dnl - $4 dnl - $5 dnl - $6 dnl - $7 dnl - ],ax_warn_scannerflags_variable) - ]) - AS_IF([test "$ax_enable_compile_warnings" = "error"],[ - # "error" flags - AX_APPEND_FLAG([ dnl - --warn-error dnl - ],ax_warn_scannerflags_variable) - ]) - - # Substitute the variables - AC_SUBST(ax_warn_scannerflags_variable) -])dnl AX_COMPILER_FLAGS diff --git a/m4/ax_compiler_flags_ldflags.m4 b/m4/ax_compiler_flags_ldflags.m4 deleted file mode 100644 index 842e329..0000000 --- a/m4/ax_compiler_flags_ldflags.m4 +++ /dev/null @@ -1,92 +0,0 @@ -# ============================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_ldflags.html -# ============================================================================== -# -# SYNOPSIS -# -# AX_COMPILER_FLAGS_LDFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS]) -# -# DESCRIPTION -# -# Add warning flags for the linker to VARIABLE, which defaults to -# WARN_LDFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be -# manually added to the LDFLAGS variable for each target in the code base. -# -# This macro depends on the environment set up by AX_COMPILER_FLAGS. -# Specifically, it uses the value of $ax_enable_compile_warnings to decide -# which flags to enable. -# -# LICENSE -# -# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk> -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 8 - -AC_DEFUN([AX_COMPILER_FLAGS_LDFLAGS],[ - AX_REQUIRE_DEFINED([AX_APPEND_LINK_FLAGS]) - AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) - AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) - AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) - - # Variable names - m4_define([ax_warn_ldflags_variable], - [m4_normalize(ifelse([$1],,[WARN_LDFLAGS],[$1]))]) - - # Always pass -Werror=unknown-warning-option to get Clang to fail on bad - # flags, otherwise they are always appended to the warn_ldflags variable, - # and Clang warns on them for every compilation unit. - # If this is passed to GCC, it will explode, so the flag must be enabled - # conditionally. - AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[ - ax_compiler_flags_test="-Werror=unknown-warning-option" - ],[ - ax_compiler_flags_test="" - ]) - - # macOS linker does not have --as-needed - AX_CHECK_LINK_FLAG([-Wl,--no-as-needed], [ - ax_compiler_flags_as_needed_option="-Wl,--no-as-needed" - ], [ - ax_compiler_flags_as_needed_option="" - ]) - - # macOS linker speaks with a different accent - ax_compiler_flags_fatal_warnings_option="" - AX_CHECK_LINK_FLAG([-Wl,--fatal-warnings], [ - ax_compiler_flags_fatal_warnings_option="-Wl,--fatal-warnings" - ]) - AX_CHECK_LINK_FLAG([-Wl,-fatal_warnings], [ - ax_compiler_flags_fatal_warnings_option="-Wl,-fatal_warnings" - ]) - - # Base flags - AX_APPEND_LINK_FLAGS([ dnl - $ax_compiler_flags_as_needed_option dnl - $3 dnl - ],ax_warn_ldflags_variable,[$ax_compiler_flags_test]) - - AS_IF([test "$ax_enable_compile_warnings" != "no"],[ - # "yes" flags - AX_APPEND_LINK_FLAGS([$4 $5 $6 $7], - ax_warn_ldflags_variable, - [$ax_compiler_flags_test]) - ]) - AS_IF([test "$ax_enable_compile_warnings" = "error"],[ - # "error" flags; -Werror has to be appended unconditionally because - # it's not possible to test for - # - # suggest-attribute=format is disabled because it gives too many false - # positives - AX_APPEND_LINK_FLAGS([ dnl - $ax_compiler_flags_fatal_warnings_option dnl - ],ax_warn_ldflags_variable,[$ax_compiler_flags_test]) - ]) - - # Substitute the variables - AC_SUBST(ax_warn_ldflags_variable) -])dnl AX_COMPILER_FLAGS diff --git a/m4/ax_compiler_vendor.m4 b/m4/ax_compiler_vendor.m4 new file mode 100644 index 0000000..039f99d --- /dev/null +++ b/m4/ax_compiler_vendor.m4 @@ -0,0 +1,119 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_VENDOR +# +# DESCRIPTION +# +# Determine the vendor of the C, C++ or Fortran compiler. The vendor is +# returned in the cache variable $ax_cv_c_compiler_vendor for C, +# $ax_cv_cxx_compiler_vendor for C++ or $ax_cv_fc_compiler_vendor for +# (modern) Fortran. The value is one of "intel", "ibm", "pathscale", +# "clang" (LLVM), "cray", "fujitsu", "sdcc", "sx", "nvhpc" (NVIDIA HPC +# Compiler), "portland" (PGI), "gnu" (GCC), "sun" (Oracle Developer +# Studio), "hp", "dec", "borland", "comeau", "kai", "lcc", "sgi", +# "microsoft", "metrowerks", "watcom", "tcc" (Tiny CC) or "unknown" (if +# the compiler cannot be determined). +# +# To check for a Fortran compiler, you must first call AC_FC_PP_SRCEXT +# with an appropriate preprocessor-enabled extension. For example: +# +# AC_LANG_PUSH([Fortran]) +# AC_PROG_FC +# AC_FC_PP_SRCEXT([F]) +# AX_COMPILER_VENDOR +# AC_LANG_POP([Fortran]) +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> +# Copyright (c) 2008 Matteo Frigo +# Copyright (c) 2018-19 John Zaitseff <J.Zaitseff@zap.org.au> +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see <https://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 32 + +AC_DEFUN([AX_COMPILER_VENDOR], [dnl + AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, [dnl + dnl If you modify this list of vendors, please add similar support + dnl to ax_compiler_version.m4 if at all possible. + dnl + dnl Note: Do NOT check for GCC first since some other compilers + dnl define __GNUC__ to remain compatible with it. Compilers that + dnl are very slow to start (such as Intel) are listed first. + + vendors=" + intel: __ICC,__ECC,__INTEL_COMPILER + ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__,__ibmxl__ + pathscale: __PATHCC__,__PATHSCALE__ + clang: __clang__ + cray: _CRAYC + fujitsu: __FUJITSU + sdcc: SDCC,__SDCC + sx: _SX + nvhpc: __NVCOMPILER + portland: __PGI + gnu: __GNUC__ + sun: __SUNPRO_C,__SUNPRO_CC,__SUNPRO_F90,__SUNPRO_F95 + hp: __HP_cc,__HP_aCC + dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER + borland: __BORLANDC__,__CODEGEARC__,__TURBOC__ + comeau: __COMO__ + kai: __KCC + lcc: __LCC__ + sgi: __sgi,sgi + microsoft: _MSC_VER + metrowerks: __MWERKS__ + watcom: __WATCOMC__ + tcc: __TINYC__ + unknown: UNKNOWN + " + for ventest in $vendors; do + case $ventest in + *:) + vendor=$ventest + continue + ;; + *) + vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" + ;; + esac + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[ +#if !($vencpp) + thisisanerror; +#endif + ]])], [break]) + done + + ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1` + ]) +])dnl diff --git a/m4/ax_prepend_flag.m4 b/m4/ax_prepend_flag.m4 new file mode 100644 index 0000000..adac8c5 --- /dev/null +++ b/m4/ax_prepend_flag.m4 @@ -0,0 +1,51 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_prepend_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PREPEND_FLAG(FLAG, [FLAGS-VARIABLE]) +# +# DESCRIPTION +# +# FLAG is added to the front of the FLAGS-VARIABLE shell variable, with a +# space added in between. +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains +# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly +# FLAG. +# +# NOTE: Implementation based on AX_APPEND_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> +# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> +# Copyright (c) 2018 John Zaitseff <J.Zaitseff@zap.org.au> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 2 + +AC_DEFUN([AX_PREPEND_FLAG], +[dnl +AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF +AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) +AS_VAR_SET_IF(FLAGS,[ + AS_CASE([" AS_VAR_GET(FLAGS) "], + [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], + [ + FLAGS="$1 $FLAGS" + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) + ], + [ + AS_VAR_SET(FLAGS,[$1]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) +AS_VAR_POPDEF([FLAGS])dnl +])dnl AX_PREPEND_FLAG diff --git a/scripts/gen_symbol_header.awk b/scripts/gen_symbol_header.awk index c9af7f9..0180f6c 100644 --- a/scripts/gen_symbol_header.awk +++ b/scripts/gen_symbol_header.awk @@ -1,5 +1,21 @@ +# Read the symbols list and create regexs to use for processing readelf output. BEGIN { - COUNT = split(" " SYMBOLS_LIST, SYMBOLS); + COUNT = 0; + + sym_regex = ""; + while ((getline line < SYMBOLS_FILE) > 0) { + if (line ~ /^ *#/ || line ~ /^$/) + continue; + split(line, fields); + symbol = fields[1]; + + SYMBOLS[++COUNT] = symbol; + if (sym_regex) + sym_regex = sym_regex "|"; + sym_regex = sym_regex symbol; + } + SYMBOL_REGEX = "^(" sym_regex ")(@|$)"; + WEAK_SYMBOL_REGEX = "^__(" sym_regx ")(@@|$)"; } /^ OS\/ABI:/ { @@ -12,73 +28,69 @@ BEGIN { if ($0 ~ "^Symbol (.*)table '.symtab'") nextfile; - for (x in SYMBOLS) { - sym_regex = "^" SYMBOLS[x] "(@|$)"; - # On x86, x86_64 and others, $8 is the symbol name, but on - # alpha, its $10, so rather use $NF, as it should be the - # last field - if ($NF ~ sym_regex) { - split($NF, symbol_array, /@|@@/); - - # Don't add local symbols of versioned libc's - if (VERSIONED_LIBC && !symbol_array[2]) - continue; - - # We have a versioned libc - if (symbol_array[2] && !VERSIONED_LIBC) - VERSIONED_LIBC = 1; - - ADD = 1; - # Check that we do not add duplicates - for (y in PROCESSED_SYMBOLS) { - if (y == $NF) { - ADD = 0; - break; - } + # On x86, x86_64 and others, $8 is the symbol name, but on + # alpha, its $10, so rather use $NF, as it should be the + # last field + if ($NF ~ SYMBOL_REGEX) { + split($NF, symbol_array, /@|@@/); + + # Don't add local symbols of versioned libc's + if (VERSIONED_LIBC && !symbol_array[2]) + next; + + # We have a versioned libc + if (symbol_array[2] && !VERSIONED_LIBC) + VERSIONED_LIBC = 1; + + ADD = 1; + # Check that we do not add duplicates + for (y in PROCESSED_SYMBOLS) { + if (y == $NF) { + ADD = 0; + break; } + } - if (ADD) { - SYMBOL_LIST[symbol_array[1]] = SYMBOL_LIST[symbol_array[1]] " " $NF; - PROCESSED_SYMBOLS[$NF] = $NF; - } + if (ADD) { + SYMBOL_LIST[symbol_array[1]] = SYMBOL_LIST[symbol_array[1]] " " $NF; + PROCESSED_SYMBOLS[$NF] = $NF; } + } - # No apparent need to handle weak __XXX symbols ... so disable - # until we have documentation on why ... - # If we do re-add this, need to update the `readelf` call in - # libsandbox/ to include the -h flag again. - continue; - - sym_regex = "^__" SYMBOLS[x] "(@@|$)"; - if (($5 == "WEAK") && ($NF ~ sym_regex)) { - split($NF, symbol_array, /@@/); - - # Don't add local symbols of versioned libc's - if (VERSIONED_LIBC && !symbol_array[2]) - continue; - - # Blacklist __getcwd on FreeBSD - # Unleashed - May 2006 - if ((symbol_array[1] == "__getcwd") && (ABI == "FreeBSD")) - continue; - - # We have a versioned libc - if (symbol_array[2] && !VERSIONED_LIBC) - VERSIONED_LIBC = 1; - - ADD = 1; - # Check that we do not add duplicates - for (y in PROCESSED_SYMBOLS) { - if (y == $NF) { - ADD = 0; - break; - } + # No apparent need to handle weak __XXX symbols ... so disable + # until we have documentation on why ... + # If we do re-add this, need to update the `readelf` call in + # libsandbox/ to include the -h flag again. + next; + + if (($5 == "WEAK") && ($NF ~ WEAK_SYMBOL_REGEX)) { + split($NF, symbol_array, /@@/); + + # Don't add local symbols of versioned libc's + if (VERSIONED_LIBC && !symbol_array[2]) + next; + + # Blacklist __getcwd on FreeBSD + # Unleashed - May 2006 + if ((symbol_array[1] == "__getcwd") && (ABI == "FreeBSD")) + next; + + # We have a versioned libc + if (symbol_array[2] && !VERSIONED_LIBC) + VERSIONED_LIBC = 1; + + ADD = 1; + # Check that we do not add duplicates + for (y in PROCESSED_SYMBOLS) { + if (y == $NF) { + ADD = 0; + break; } + } - if (ADD) { - WEAK_SYMBOLS[SYMBOLS[x]] = WEAK_SYMBOLS[SYMBOLS[x]] " " $NF; - PROCESSED_SYMBOLS[$NF] = $NF; - } + if (ADD) { + WEAK_SYMBOLS[SYMBOLS[x]] = WEAK_SYMBOLS[SYMBOLS[x]] " " $NF; + PROCESSED_SYMBOLS[$NF] = $NF; } } } @@ -86,7 +98,6 @@ BEGIN { END { printf("#ifndef __symbols_h\n"); printf("#define __symbols_h\n\n"); - printf("#define SB_NR_UNDEF -99999\n\n"); SB_MAX_STRING_LEN = 0 @@ -99,7 +110,7 @@ END { SB_MAX_STRING_LEN = length(sym_index); if (full_count == 0) - printf("#define SB_NR_%s SB_NR_UNDEF\n", toupper(sym_index)); + printf("#define SB_NR_%s (SB_NR_UNDEF - %i)\n", toupper(sym_index), i); for (x in sym_full_names) { split(sym_full_names[x], symbol_array, /@|@@/); @@ -117,6 +128,10 @@ END { gsub(/@|\./, "_", sym_real_name); } + # Avoid libc's symbol rename via #define. musl defines aliases as: + # #define mkstemp64 mkstemp + # #define mkstemps64 mkstemps + printf("#undef %s\n", sym_index); printf("#define symname_%s \"%s\"\n", sym_real_name, sym_index); # We handle non-versioned libc's by setting symver_* @@ -186,8 +201,6 @@ END { } } - printf("#include \"wrapper-funcs/__pre_check.c\"\n"); - printf("#define SB_MAX_STRING_LEN %i\n\n", SB_MAX_STRING_LEN); printf("#endif /* __symbols_h */\n"); diff --git a/scripts/gen_symbol_version_map.awk b/scripts/gen_symbol_version_map.awk index a0a43c0..661cb1d 100644 --- a/scripts/gen_symbol_version_map.awk +++ b/scripts/gen_symbol_version_map.awk @@ -1,5 +1,18 @@ +# Read the symbols list and create regexs to use for processing readelf output. BEGIN { - split(" " SYMBOLS_LIST, SYMBOLS); + sym_regex = ""; + while ((getline line < SYMBOLS_FILE) > 0) { + if (line ~ /^ *#/ || line ~ /^$/) + continue; + split(line, fields); + symbol = fields[1]; + + if (sym_regex) + sym_regex = sym_regex "|"; + sym_regex = sym_regex symbol; + } + SYMBOL_REGEX = "^(" sym_regex ")(@|$)"; + WEAK_SYMBOL_REGEX = "^__(" sym_regx ")(@@|$)"; } /^ OS\/ABI:/ { @@ -17,81 +30,77 @@ BEGIN { if ($4 != "FUNC" || $5 == "LOCAL" || $6 != "DEFAULT") next; - for (x in SYMBOLS) { - sym_regex = "^" SYMBOLS[x] "(@|$)"; - # On x86, x86_64 and others, $8 is the symbol name, but on - # alpha, its $10, so rather use $NF, as it should be the - # last field - if ($NF ~ sym_regex) { - split($NF, symbol_array, /@|@@/); + # On x86, x86_64 and others, $8 is the symbol name, but on + # alpha, its $10, so rather use $NF, as it should be the + # last field + if ($NF ~ SYMBOL_REGEX) { + split($NF, symbol_array, /@|@@/); - # Don't add local symbols of versioned libc's - if (VERSIONED_LIBC && !symbol_array[2]) - continue; + # Don't add local symbols of versioned libc's + if (VERSIONED_LIBC && !symbol_array[2]) + next; - # Handle non-versioned libc's like uClibc ... - if (!symbol_array[2]) - symbol_array[2] = ""; - - # We have a versioned libc - if (symbol_array[2] && !VERSIONED_LIBC) - VERSIONED_LIBC = 1; - - ADD = 1; - # Check that we do not add duplicates - for (y in PROCESSED_SYMBOLS) { - if (y == $NF) { - ADD = 0; - break; - } + # Handle non-versioned libc's like uClibc ... + if (!symbol_array[2]) + symbol_array[2] = ""; + + # We have a versioned libc + if (symbol_array[2] && !VERSIONED_LIBC) + VERSIONED_LIBC = 1; + + ADD = 1; + # Check that we do not add duplicates + for (y in PROCESSED_SYMBOLS) { + if (y == $NF) { + ADD = 0; + break; } + } - if (ADD) { - SYMBOL_LIST[symbol_array[2]] = SYMBOL_LIST[symbol_array[2]] " " symbol_array[1]; - PROCESSED_SYMBOLS[$NF] = $NF; - } + if (ADD) { + SYMBOL_LIST[symbol_array[2]] = SYMBOL_LIST[symbol_array[2]] " " symbol_array[1]; + PROCESSED_SYMBOLS[$NF] = $NF; } + } - # No apparent need to handle weak __XXX symbols ... so disable - # until we have documentation on why ... - # If we do re-add this, need to update the `readelf` call in - # libsandbox/ to include the -h flag again. - continue; + # No apparent need to handle weak __XXX symbols ... so disable + # until we have documentation on why ... + # If we do re-add this, need to update the `readelf` call in + # libsandbox/ to include the -h flag again. + next; - sym_regex = "^__" SYMBOLS[x] "(@@|$)"; - if (($5 == "WEAK") && ($NF ~ sym_regex)) { - split($NF, symbol_array, /@@/); + if (($5 == "WEAK") && ($NF ~ WEAK_SYMBOL_REGEX)) { + split($NF, symbol_array, /@@/); - # Don't add local symbols of versioned libc's - if (VERSIONED_LIBC && !symbol_array[2]) - continue; + # Don't add local symbols of versioned libc's + if (VERSIONED_LIBC && !symbol_array[2]) + next; - # Blacklist __getcwd on FreeBSD - # Unleashed - May 2006 - if ((symbol_array[1] == "__getcwd") && (ABI == "FreeBSD")) - continue; + # Blacklist __getcwd on FreeBSD + # Unleashed - May 2006 + if ((symbol_array[1] == "__getcwd") && (ABI == "FreeBSD")) + next; - # Handle non-versioned libc's like uClibc ... - if (!symbol_array[2]) - symbol_array[2] = ""; - - # We have a versioned libc - if (symbol_array[2] && !VERSIONED_LIBC) - VERSIONED_LIBC = 1; - - ADD = 1; - # Check that we do not add duplicates - for (y in PROCESSED_SYMBOLS) { - if (y == $NF) { - ADD = 0; - break; - } + # Handle non-versioned libc's like uClibc ... + if (!symbol_array[2]) + symbol_array[2] = ""; + + # We have a versioned libc + if (symbol_array[2] && !VERSIONED_LIBC) + VERSIONED_LIBC = 1; + + ADD = 1; + # Check that we do not add duplicates + for (y in PROCESSED_SYMBOLS) { + if (y == $NF) { + ADD = 0; + break; } + } - if (ADD) { - SYMBOL_LIST[symbol_array[2]] = SYMBOL_LIST[symbol_array[2]] " " symbol_array[1]; - PROCESSED_SYMBOLS[$NF] = $NF; - } + if (ADD) { + SYMBOL_LIST[symbol_array[2]] = SYMBOL_LIST[symbol_array[2]] " " symbol_array[1]; + PROCESSED_SYMBOLS[$NF] = $NF; } } } diff --git a/scripts/gen_trace_header.awk b/scripts/gen_trace_header.awk index 846294c..0bb53b1 100644 --- a/scripts/gen_trace_header.awk +++ b/scripts/gen_trace_header.awk @@ -1,20 +1,39 @@ +# Read the symbols list and create regexs to use for processing readelf output. +function read_symbols() { + COUNT = 0; + while ((getline line < SYMBOLS_FILE) > 0) { + if (line ~ /^ *#/ || line ~ /^$/) + continue; + nfields = split(line, fields); + symbol = fields[1]; + syscall = nfields > 1 ? fields[2] : symbol; + + c = ++COUNT + SYMBOLS[c] = symbol; + SYSCALLS[c] = syscall; + } +} + BEGIN { - COUNT = split(" " SYMBOLS_LIST, SYMBOLS); + read_symbols(); if (MODE == "gen") { - for (x in SYMBOLS) { - s = SYMBOLS[x] - print "SB_" s " = SYS_" s + for (x in SYSCALLS) { + print "SB_" SYMBOLS[x] " = SYS_" SYSCALLS[x]; } exit(0); } } -function out(name, val) +function out(name, syscall, val) { - name = toupper(name) - print "#define SB_SYS" syscall_prefix "_" name " " val; - print "S(" name ")"; + uname = toupper(name) + syscall_define = "SB_SYS" syscall_prefix "_" uname + print "#define " syscall_define " " val; + if (name == syscall) + print "S(" uname ")"; + else + print "{ " syscall_define ", SB_NR_" uname ", \"" uname "\" },"; } { @@ -31,21 +50,23 @@ function out(name, val) # a straight number or an expression (a syscall base) sub(/^[^=]*= /, ""); - for (i = 1; i <= COUNT; ++i) + syscall = "<awk_script_error>"; + for (i = 1; i <= COUNT; ++i) { if (SYMBOLS[i] == name) { - SYMBOLS[i] = ""; + FOUND[i] = 1; + syscall = SYSCALLS[i]; break; } + } - out(name, $0); + out(name, syscall, $0); } END { if (MODE != "gen") { for (x in SYMBOLS) { - s = SYMBOLS[x]; - if (s != "") - out(s, "SB_NR_UNDEF"); + if (!FOUND[x]) + out(SYMBOLS[x], SYSCALLS[x], "SB_NR_UNDEF"); } } } diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..4b2bc35 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,4 @@ +# Helper for developers. +all sandbox: src/sandbox ; +clean: ; rm -f *.o *.l[ao] .libs/* sandbox +%: ; $(MAKE) -C .. $@ diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index e7aeb30..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -AUTOMAKE_OPTIONS = foreign - -bin_PROGRAMS = sandbox - -AM_CPPFLAGS = \ - -I$(top_srcdir) \ - -I$(top_srcdir)/libsbutil \ - -I$(top_srcdir)/libsbutil/include \ - $(SANDBOX_DEFINES) - -sandbox_LDADD = $(top_builddir)/libsbutil/libsbutil.la $(LIBDL) -sandbox_SOURCES = \ - environ.c \ - namespaces.c \ - options.c \ - sandbox.h \ - sandbox.c diff --git a/src/environ.c b/src/environ.c index 346bc26..df8595b 100644 --- a/src/environ.c +++ b/src/environ.c @@ -96,7 +96,7 @@ static void setup_cfg_var(const char *env_var) * environment if not already present. */ config = rc_get_cnf_entry(sb_conf_file(), env_var, NULL); if (NULL != config) { - setenv(ENV_SANDBOX_VERBOSE, config, 0); + setenv(env_var, config, 0); free(config); } } @@ -195,6 +195,7 @@ static int setup_cfg_vars(struct sandbox_info_t *sandbox_info) setup_cfg_var(ENV_SANDBOX_VERBOSE); setup_cfg_var(ENV_SANDBOX_DEBUG); setup_cfg_var(ENV_NOCOLOR); + setup_cfg_var(ENV_SANDBOX_METHOD); if (-1 == setup_access_var(ENV_SANDBOX_DENY)) return -1; @@ -207,7 +208,7 @@ static int setup_cfg_vars(struct sandbox_info_t *sandbox_info) if (-1 == setup_access_var(ENV_SANDBOX_WRITE)) return -1; if ((NULL == getenv(ENV_SANDBOX_WRITE)) && - (NULL != sandbox_info->work_dir)) + strlen(sandbox_info->work_dir)) setenv(ENV_SANDBOX_WRITE, sandbox_info->work_dir, 1); if (-1 == setup_access_var(ENV_SANDBOX_PREDICT)) @@ -240,7 +241,7 @@ static void sb_setenv(char ***envp, const char *name, const char *val) /* We setup the environment child side only to prevent issues with * setting LD_PRELOAD parent side */ -char **setup_environ(struct sandbox_info_t *sandbox_info) +char **setup_environ(struct sandbox_info_t *sandbox_info, bool interactive) { int have_ld_preload = 0; @@ -263,6 +264,7 @@ char **setup_environ(struct sandbox_info_t *sandbox_info) unsetenv(ENV_SANDBOX_MESSAGE_PATH); unsetenv(ENV_SANDBOX_WORKDIR); unsetenv(ENV_SANDBOX_ACTIVE); + unsetenv(ENV_SANDBOX_INTRACTV); unsetenv(ENV_BASH_ENV); orig_ld_preload_envvar = getenv(ENV_LD_PRELOAD); @@ -294,13 +296,18 @@ char **setup_environ(struct sandbox_info_t *sandbox_info) sb_setenv(&new_environ, ENV_SANDBOX_LOG, sandbox_info->sandbox_log); sb_setenv(&new_environ, ENV_SANDBOX_DEBUG_LOG, sandbox_info->sandbox_debug_log); sb_setenv(&new_environ, ENV_SANDBOX_MESSAGE_PATH, sandbox_info->sandbox_message_path); + /* Is this an interactive session? */ + if (interactive) + sb_setenv(&new_environ, ENV_SANDBOX_INTRACTV, "1"); /* Just set the these if not already set so that is_env_on() work */ if (!getenv(ENV_SANDBOX_VERBOSE)) sb_setenv(&new_environ, ENV_SANDBOX_VERBOSE, "1"); if (!getenv(ENV_SANDBOX_DEBUG)) - sb_setenv(&new_environ, ENV_SANDBOX_DEBUG, "0"); + sb_setenv(&new_environ, ENV_SANDBOX_DEBUG, opt_debug ? "1" : "0"); if (!getenv(ENV_NOCOLOR)) sb_setenv(&new_environ, ENV_NOCOLOR, "no"); + if (!getenv(ENV_SANDBOX_METHOD)) + sb_setenv(&new_environ, ENV_SANDBOX_METHOD, "any"); /* If LD_PRELOAD was not set, set it here, else do it below */ if (!have_ld_preload) sb_setenv(&new_environ, ENV_LD_PRELOAD, ld_preload_envvar); diff --git a/src/local.mk b/src/local.mk new file mode 100644 index 0000000..61877ae --- /dev/null +++ b/src/local.mk @@ -0,0 +1,14 @@ +bin_PROGRAMS += %D%/sandbox + +%C%_sandbox_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/libsbutil \ + -I$(top_srcdir)/libsbutil/include + +%C%_sandbox_LDADD = libsbutil/libsbutil.la $(LIBDL) +%C%_sandbox_SOURCES = \ + %D%/environ.c \ + %D%/namespaces.c \ + %D%/options.c \ + %D%/sandbox.h \ + %D%/sandbox.c diff --git a/src/namespaces.c b/src/namespaces.c index 5be42f6..290111a 100644 --- a/src/namespaces.c +++ b/src/namespaces.c @@ -26,16 +26,9 @@ #define xchmod(...) sb_assert(chmod(__VA_ARGS__) == 0) #define xsymlink(...) sb_assert(symlink(__VA_ARGS__) == 0) -#define xasprintf(fmt, ...) \ -({ \ - int _ret = asprintf(fmt, __VA_ARGS__); \ - if (_ret == 0) \ - sb_perr("asprintf(%s) failed", #fmt); \ - _ret; \ -}) #define xfopen(path, ...) \ ({ \ - FILE *_ret = fopen(path, __VA_ARGS__); \ + FILE *_ret = fopen64(path, __VA_ARGS__); \ if (_ret == 0) \ sb_perr("fopen(%s) failed", #path); \ _ret; \ @@ -114,7 +107,7 @@ static void ns_mount_setup(void) /* Now map in all the files/dirs we do want to expose. */ int fd; #define bind_file(node) \ - fd = open("/dev/shm/" node, O_CREAT, 0); \ + fd = open64("/dev/shm/" node, O_CREAT, 0); \ sb_assert(fd != -1); \ close(fd); \ xmount("/dev/" node, "/dev/shm/" node, NULL, MS_BIND, NULL) @@ -182,6 +175,10 @@ pid_t setup_namespaces(void) if (opt_use_ns_user) ns_user_switch(uid, gid, 0, 0); +#ifdef CLONE_NEWCGROUP + if (opt_use_ns_cgroup) + unshare(CLONE_NEWCGROUP); +#endif #ifdef CLONE_NEWIPC if (opt_use_ns_ipc) unshare(CLONE_NEWIPC); @@ -190,6 +187,10 @@ pid_t setup_namespaces(void) if (opt_use_ns_sysv) unshare(CLONE_SYSVSEM); #endif +#ifdef CLONE_NEWTIME + if (opt_use_ns_time) + unshare(CLONE_NEWTIME); +#endif #ifdef CLONE_NEWUTS if (opt_use_ns_uts && unshare(CLONE_NEWUTS) == 0) { diff --git a/src/options.c b/src/options.c index 295ee75..2105fdc 100644 --- a/src/options.c +++ b/src/options.c @@ -11,13 +11,17 @@ /* Setting to -1 will load defaults from the config file. */ int opt_use_namespaces = -1; +int opt_use_ns_cgroup = -1; int opt_use_ns_ipc = -1; int opt_use_ns_mnt = -1; int opt_use_ns_net = -1; int opt_use_ns_pid = -1; int opt_use_ns_sysv = -1; +int opt_use_ns_time = -1; int opt_use_ns_user = -1; int opt_use_ns_uts = -1; +bool opt_use_bash = false; +int opt_debug = -1; static const struct { const char *name; @@ -25,14 +29,17 @@ static const struct { int default_val; } config_opts[] = { /* Default these to off until they can get more testing. */ - { "NAMESPACES_ENABLE", &opt_use_namespaces, false, }, - { "NAMESPACE_IPC_ENABLE", &opt_use_ns_ipc, false, }, - { "NAMESPACE_MNT_ENABLE", &opt_use_ns_mnt, false, }, - { "NAMESPACE_NET_ENABLE", &opt_use_ns_net, false, }, - { "NAMESPACE_PID_ENABLE", &opt_use_ns_pid, false, }, - { "NAMESPACE_SYSV_ENABLE", &opt_use_ns_sysv, false, }, - { "NAMESPACE_USER_ENABLE", &opt_use_ns_user, false, }, - { "NAMESPACE_UTS_ENABLE", &opt_use_ns_uts, false, }, + { "NAMESPACES_ENABLE", &opt_use_namespaces, false, }, + { "NAMESPACE_CGROUP_ENABLE", &opt_use_ns_cgroup, false, }, + { "NAMESPACE_IPC_ENABLE", &opt_use_ns_ipc, false, }, + { "NAMESPACE_MNT_ENABLE", &opt_use_ns_mnt, false, }, + { "NAMESPACE_NET_ENABLE", &opt_use_ns_net, false, }, + { "NAMESPACE_PID_ENABLE", &opt_use_ns_pid, false, }, + { "NAMESPACE_SYSV_ENABLE", &opt_use_ns_sysv, false, }, + { "NAMESPACE_TIME_ENABLE", &opt_use_ns_time, false, }, + { "NAMESPACE_USER_ENABLE", &opt_use_ns_user, false, }, + { "NAMESPACE_UTS_ENABLE", &opt_use_ns_uts, false, }, + { "SANDBOX_DEBUG", &opt_debug, false, }, }; static void read_config(void) @@ -46,35 +53,39 @@ static void read_config(void) } } +static const char sb_sonfigure_opts[] = SANDBOX_CONFIGURE_OPTS; + static void show_version(void) { - puts( + printf( "Gentoo path sandbox\n" " version: " PACKAGE_VERSION "\n" " C lib: " LIBC_VERSION " (" LIBC_PATH ")\n" " build: " __DATE__ " " __TIME__ "\n" - " contact: " PACKAGE_BUGREPORT " via http://bugs.gentoo.org/\n" + " contact: " PACKAGE_BUGREPORT " via https://bugs.gentoo.org/\n" " rtld: " #ifdef BROKEN_RTLD_NEXT "next is broken ;(\n" #else "next is OK! :D\n" #endif -#ifndef SB_SCHIZO -# define SB_SCHIZO "no" +#ifndef SB_PERSONALITIES +# define SB_PERSONALITIES "no" #endif - " schizo: " SB_SCHIZO "\n" - "\nconfigured with these options:\n" - SANDBOX_CONFIGURE_OPTS + " personalities: " SB_PERSONALITIES "\n" + "\nconfigured with these options:\n%s\n", + sb_sonfigure_opts ); exit(0); } -#define PARSE_FLAGS "+hV" +#define PARSE_FLAGS "+cdhV" #define a_argument required_argument static struct option const long_opts[] = { {"ns-on", no_argument, &opt_use_namespaces, true}, {"ns-off", no_argument, &opt_use_namespaces, false}, + {"ns-cgroup-on", no_argument, &opt_use_ns_cgroup, true}, + {"ns-cgroup-off", no_argument, &opt_use_ns_cgroup, false}, {"ns-ipc-on", no_argument, &opt_use_ns_ipc, true}, {"ns-ipc-off", no_argument, &opt_use_ns_ipc, false}, {"ns-mnt-on", no_argument, &opt_use_ns_mnt, true}, @@ -85,17 +96,24 @@ static struct option const long_opts[] = { {"ns-pid-off", no_argument, &opt_use_ns_pid, false}, {"ns-sysv-on", no_argument, &opt_use_ns_sysv, true}, {"ns-sysv-off", no_argument, &opt_use_ns_sysv, false}, + {"ns-time-on", no_argument, &opt_use_ns_time, true}, + {"ns-time-off", no_argument, &opt_use_ns_time, false}, {"ns-user-on", no_argument, &opt_use_ns_user, true}, {"ns-user-off", no_argument, &opt_use_ns_user, false}, {"ns-uts-on", no_argument, &opt_use_ns_uts, true}, {"ns-uts-off", no_argument, &opt_use_ns_uts, false}, + {"bash", no_argument, NULL, 'c'}, + {"debug", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, + {"run-configure", no_argument, NULL, 0x800}, {NULL, no_argument, NULL, 0x0} }; static const char * const opts_help[] = { "Enable the use of namespaces", "Disable the use of namespaces", + "Enable the use of cgroup namespaces", + "Disable the use of cgroup namespaces", "Enable the use of IPC (and System V) namespaces", "Disable the use of IPC (and System V) namespaces", "Enable the use of mount namespaces", @@ -106,12 +124,17 @@ static const char * const opts_help[] = { "Disable the use of process (pid) namespaces", "Enable the use of System V namespaces", "Disable the use of System V namespaces", + "Enable the use of time namespaces", + "Disable the use of time namespaces", "Enable the use of user namespaces", "Disable the use of user namespaces", "Enable the use of UTS (hostname/uname) namespaces", "Disable the use of UTS (hostname/uname) namespaces", + "Run command through bash shell", + "Enable debug output", "Print this help and exit", "Print version and exit", + "Run local sandbox configure in same way and exit (developer only)", NULL }; @@ -163,23 +186,47 @@ static void show_usage(int status) printf(" * %s\n", opts_help[i]); } - fprintf(fp, "\nContact: " PACKAGE_BUGREPORT " via http://bugs.gentoo.org/\n"); + fprintf(fp, "\nContact: " PACKAGE_BUGREPORT " via https://bugs.gentoo.org/\n"); exit(status); } +static void run_configure(int argc, char *argv[]) +{ + int i; + char *cmd; + xasprintf(&cmd, "set -x; ./configure %s", sb_sonfigure_opts); + /* This doesn't need to be fast, so keep it simple. */ + for (i = optind; i < argc; ++i) + xasprintf(&cmd, "%s %s", cmd, argv[i]); + exit(system(cmd)); +} + void parseargs(int argc, char *argv[]) { int i; while ((i = getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { switch (i) { + case 'c': + opt_use_bash = true; + break; + case 'd': + if (opt_debug <= 0) + opt_debug = 1; + else + ++opt_debug; + break; case 'V': show_version(); case 'h': show_usage(0); + case 0x800: + run_configure(argc, argv); case '?': show_usage(1); + default: + sb_ebort("ISE: unhandled CLI option %c\n", i); } } diff --git a/src/sandbox.c b/src/sandbox.c index 503ad0b..071cad0 100644 --- a/src/sandbox.c +++ b/src/sandbox.c @@ -15,14 +15,18 @@ #include "sbutil.h" #include "sandbox.h" +/* The C library might have a macro for this. */ +#undef dprintf + static int print_debug = 0; #define dprintf(fmt, args...) do { if (print_debug) printf(fmt, ## args); } while (0) #define dputs(str) do { if (print_debug) puts(str); } while (0) +int (*sbio_faccessat)(int, const char *, int, int) = faccessat; int (*sbio_open)(const char *, int, mode_t) = (void *)open; FILE *(*sbio_popen)(const char *, const char *) = popen; -volatile static int stop_called = 0; -volatile static pid_t child_pid = 0; +static volatile int stop_called = 0; +static volatile pid_t child_pid = 0; static const char sandbox_banner[] = "============================= Gentoo path sandbox =============================="; static const char sandbox_footer[] = "--------------------------------------------------------------------------------"; @@ -111,7 +115,7 @@ static void print_sandbox_log(char *sandbox_log) return; } - sb_eerror("--------------------------- ACCESS VIOLATION SUMMARY ---------------------------\n"); + sb_eerror("----------------------- SANDBOX ACCESS VIOLATION SUMMARY -----------------------\n"); sb_eerror("LOG FILE: \"%s\"\n", sandbox_log); while (1) { @@ -172,7 +176,9 @@ static int spawn_shell(char *argv_bash[], char **env, int debug) /* Child's process */ if (0 == child_pid) { - int ret = execve(argv_bash[0], argv_bash, env); + /* Would be nice if execvpe were in POSIX. */ + environ = env; + int ret = execvp(argv_bash[0], argv_bash); sb_pwarn("failed to exec child"); _exit(ret); } else if (child_pid < 0) { @@ -205,7 +211,7 @@ int main(int argc, char **argv) { int sandbox_log_presence = 0; - struct sandbox_info_t sandbox_info; + struct sandbox_info_t sandbox_info = {}; char **sandbox_environ; char **argv_bash = NULL; @@ -229,19 +235,22 @@ int main(int argc, char **argv) sb_err("not launching a new sandbox as one is already running in this process hierarchy"); /* determine the location of all the sandbox support files */ - dputs("Detection of the support files."); + if (opt_debug) + dputs("Detection of the support files."); if (-1 == setup_sandbox(&sandbox_info, print_debug)) sb_err("failed to setup sandbox"); /* verify the existance of required files */ - dputs("Verification of the required files."); + if (opt_debug) + dputs("Verification of the required files."); if (!rc_file_exists(sandbox_info.sandbox_rc)) sb_perr("could not open the sandbox rc file: %s", sandbox_info.sandbox_rc); /* set up the required environment variables */ - dputs("Setting up the required environment variables."); + if (opt_debug) + dputs("Setting up the required environment variables."); /* If not in portage, cd into it work directory */ if ('\0' != sandbox_info.work_dir[0]) @@ -250,33 +259,61 @@ int main(int argc, char **argv) /* Setup the child environment stuff. * XXX: We free this in spawn_shell(). */ - sandbox_environ = setup_environ(&sandbox_info); + sandbox_environ = setup_environ(&sandbox_info, print_debug); if (NULL == sandbox_environ) goto oom_error; /* Setup bash argv */ - str_list_add_item_copy(argv_bash, "/bin/bash", oom_error); - str_list_add_item_copy(argv_bash, "-rcfile", oom_error); - str_list_add_item_copy(argv_bash, sandbox_info.sandbox_rc, oom_error); - if (argc >= 2) { - int i; - - str_list_add_item_copy(argv_bash, run_str, oom_error); - str_list_add_item_copy(argv_bash, argv[1], oom_error); - for (i = 2; i < argc; i++) { - char *tmp_ptr; - - tmp_ptr = xrealloc(argv_bash[4], - (strlen(argv_bash[4]) + - strlen(argv[i]) + 2) * - sizeof(char)); - argv_bash[4] = tmp_ptr; + if (!opt_use_bash && argc == 2) { + /* Backwards compatibility hack: if there's only one argument, and it + * appears to be a shell command (not an absolute path to a program), + * then fallback to running through the shell. + */ + if (access(argv[1], X_OK)) + opt_use_bash = true; + } - snprintf(argv_bash[4] + strlen(argv_bash[4]), - strlen(argv[i]) + 2, " %s", - argv[i]); + if (opt_use_bash || argc == 1) { + str_list_add_item_copy(argv_bash, "/bin/bash", oom_error); + str_list_add_item_copy(argv_bash, "-rcfile", oom_error); + str_list_add_item_copy(argv_bash, sandbox_info.sandbox_rc, oom_error); + if (argc >= 2) { + int i; + size_t cmdlen; + + str_list_add_item_copy(argv_bash, run_str, oom_error); + str_list_add_item_copy(argv_bash, argv[1], oom_error); + cmdlen = strlen(argv_bash[4]); + for (i = 2; i < argc; i++) { + size_t arglen = strlen(argv[i]); + argv_bash[4] = xrealloc(argv_bash[4], cmdlen + arglen + 2); + argv_bash[4][cmdlen] = ' '; + memcpy(argv_bash[4] + cmdlen + 1, argv[i], arglen); + cmdlen += arglen + 1; + argv_bash[4][cmdlen] = '\0'; + } } + } else { + int i; + for (i = 1; i < argc; ++i) + str_list_add_item_copy(argv_bash, argv[i], oom_error); + } + +#ifdef HAVE_PRCTL + /* Lock down access to elevated privileges. In practice, this will block + * use of tools like su and sudo, and will allow use of seccomp bpf. + */ +# ifdef PR_SET_NO_NEW_PRIVS + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { + /* Ignore EINVAL in case we're on old kernels. Unfortunately we can't + * differentiate between EINVAL due to unsupported PR_xxx and EINVAL + * due to bad 2nd/3rd/4th/5th args. + */ + if (errno != EINVAL) + sb_eerror("prctl(PR_SET_NO_NEW_PRIVS) failed"); } +# endif +#endif /* Set up the required signal handlers */ int sigs[] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, }; @@ -313,9 +350,8 @@ int main(int argc, char **argv) sigaction(SIGHUP, &act_old[0], NULL); /* STARTING PROTECTED ENVIRONMENT */ - dputs("The protected environment has been started."); - dputs(sandbox_footer); - dputs("Process being started in forked instance."); + if (opt_debug) + dputs("The protected environment has been started."); /* Start Bash */ int shell_exit = spawn_shell(argv_bash, sandbox_environ, print_debug); @@ -326,8 +362,6 @@ int main(int argc, char **argv) argv_bash = NULL; sandbox_environ = NULL; - dputs("Cleaning up sandbox process"); - dputs(sandbox_banner); dputs("The protected environment has been shut down."); if (rc_file_exists(sandbox_info.sandbox_log)) { @@ -337,7 +371,7 @@ int main(int argc, char **argv) dputs(sandbox_footer); /* Do the right thing and pass the signal back up. See: - * http://www.cons.org/cracauer/sigint.html + * https://www.cons.org/cracauer/sigint.html */ if (stop_called != SIGUSR1 && WIFSIGNALED(shell_exit)) { int signum = WTERMSIG(shell_exit); diff --git a/src/sandbox.h b/src/sandbox.h index 303dac4..477973a 100644 --- a/src/sandbox.h +++ b/src/sandbox.h @@ -24,7 +24,7 @@ struct sandbox_info_t { char *home_dir; }; -extern char **setup_environ(struct sandbox_info_t *sandbox_info); +extern char **setup_environ(struct sandbox_info_t *sandbox_info, bool interactive); extern bool sb_get_cnf_bool(const char *, bool); @@ -40,15 +40,27 @@ extern pid_t setup_namespaces(void); #define sb_err(fmt, args...) _sb_err(warn, fmt, ## args) #define sb_perr(fmt, args...) _sb_err(pwarn, fmt, ## args) +#define xasprintf(fmt, ...) \ +({ \ + int _ret = asprintf(fmt, __VA_ARGS__); \ + if (_ret == 0) \ + sb_perr("asprintf(%s) failed", #fmt); \ + _ret; \ +}) + /* Option parsing related code */ extern void parseargs(int argc, char *argv[]); extern int opt_use_namespaces; +extern int opt_use_ns_cgroup; extern int opt_use_ns_ipc; extern int opt_use_ns_mnt; extern int opt_use_ns_net; extern int opt_use_ns_pid; extern int opt_use_ns_sysv; +extern int opt_use_ns_time; extern int opt_use_ns_user; extern int opt_use_ns_uts; +extern bool opt_use_bash; +extern int opt_debug; #endif diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..2eed23e --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,4 @@ +# Helper for developers. +all: tests ; +clean: clean-checkPROGRAMS ; rm -f *.o *.l[ao] .libs/* +%: ; $(MAKE) -C .. $@ diff --git a/tests/Makefile.am b/tests/Makefile.am deleted file mode 100644 index 3baf5b1..0000000 --- a/tests/Makefile.am +++ /dev/null @@ -1,123 +0,0 @@ -AT_FILES = $(wildcard $(srcdir)/*.at) -TESTSUITE = $(srcdir)/testsuite -DISTCLEANFILES = atconfig -EXTRA_DIST = atlocal.in package.m4.in $(AT_FILES) $(TESTSUITE) \ - test-skel-0.c \ - tests.h - -AM_CPPFLAGS = -I$(top_srcdir) $(SANDBOX_DEFINES) - -check_PROGRAMS = \ - get-group \ - get-user \ - sb_true \ - sb_true_static \ - \ - access-0 \ - chmod-0 \ - chown-0 \ - creat-0 \ - creat64-0 \ - execv-0 \ - execvp-0 \ - faccessat-0 \ - fchmodat-0 \ - fchownat-0 \ - fopen-0 \ - fopen64-0 \ - futimesat-0 \ - lchown-0 \ - link-0 \ - linkat-0 \ - linkat_static-0 \ - lutimes-0 \ - mkdtemp-0 \ - mkdir-0 \ - mkdir_static-0 \ - mkdirat-0 \ - mkfifo-0 \ - mkfifoat-0 \ - mknod-0 \ - mknodat-0 \ - mkostemp-0 \ - mkostemp64-0 \ - mkostemps-0 \ - mkostemps64-0 \ - mkstemp-0 \ - mkstemp64-0 \ - mkstemps-0 \ - mkstemps64-0 \ - open-0 \ - open_static-0 \ - open64-0 \ - openat-0 \ - openat_static-0 \ - openat64-0 \ - opendir-0 \ - remove-0 \ - rename-0 \ - renameat-0 \ - rmdir-0 \ - signal_static-0 \ - symlink-0 \ - symlinkat-0 \ - truncate-0 \ - truncate64-0 \ - unlink-0 \ - unlink_static-0 \ - unlinkat-0 \ - utime-0 \ - utimensat-0 \ - utimensat_static-0 \ - utimes-0 \ - vfork-0 \ - \ - getcwd-gnulib_tst \ - libsigsegv_tst \ - malloc_hooked_tst \ - malloc_mmap_tst \ - pipe-fork_tst \ - pipe-fork_static_tst \ - sb_printf_tst \ - sigsuspend-zsh_tst \ - sigsuspend-zsh_static_tst \ - trace-memory_static_tst - -dist_check_SCRIPTS = \ - $(wildcard $(srcdir)/*-[0-9]*.sh) \ - malloc-0 \ - script-0 \ - trace-0 - -AM_LDFLAGS = `expr $@ : .*_static >/dev/null && echo -all-static` - -sb_printf_tst_CFLAGS = -I$(top_srcdir)/libsbutil -I$(top_srcdir)/libsbutil/include -sb_printf_tst_LDADD = $(top_builddir)/libsbutil/libsbutil.la - -malloc_hooked_tst_LDFLAGS = $(AM_LDFLAGS) -pthread - -if HAVE_LIBSIGSEGV -libsigsegv_tst_LDADD = -lsigsegv -endif - -TESTSUITEFLAGS = --jobs=`getconf _NPROCESSORS_ONLN || echo 1` - -check-local: atconfig atlocal $(TESTSUITE) - $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='src:tests' $(TESTSUITEFLAGS) - -installcheck-local: atconfig atlocal $(TESTSUITE) - $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='src:tests:$(bindir)' $(TESTSUITEFLAGS) - -clean-local: - test ! -f '$(TESTSUITE)' || \ - $(SHELL) '$(TESTSUITE)' --clean - -AUTOTEST = $(AUTOM4TE) --language=autotest -$(TESTSUITE): $(AT_FILES) testsuite.list.at - $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at - mv $@.tmp $@ - -testsuite.list.at: $(AT_FILES) - ( echo "dnl DO NOT EDIT: GENERATED BY MAKEFILE.AM"; \ - $(GREP) -l -e '^SB_CHECK' -e '^AT_CHECK' $(AT_FILES) | LC_ALL=C sort | \ - $(SED) -e 's:^[.]/:sb_inc([:' -e 's:[.]at$$:]):' ) > $@ diff --git a/tests/atlocal.in b/tests/atlocal.in index b9a631b..adf3bad 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -2,6 +2,7 @@ export abs_top_srcdir abs_top_builddir abs_srcdir abs_builddir export AWK="@AWK@" +export HOST="@host@" if ! ${at_clean} ; then export SB_UID=$(./get-user) @@ -23,5 +24,32 @@ export SANDBOX_VERBOSE=0 # If the terminal has this flag set, the tests get all messed up. stty -tostop 2>/dev/null || : +# Some tests want this internal path. +for devfd in /proc/self/fd /dev/fd ; do + [ -e "${devfd}" ] && break +done + +# GNU make likes to leak fds when using jobservers (i.e. using -j). +case "${MAKEFLAGS}" in +*--jobserver-auth=*) + flags=${MAKEFLAGS#*--jobserver-auth=} + flags=${flags%% *} + for fd in $(echo "${flags}" | tr ',' ' ') ; do + if [ -e "${devfd}/${fd}" ] ; then + eval "exec ${fd}>&-" + fi + done + ;; +esac + +# Figure out currently YAMA ptrace_scope restriction level. +at_yama_ptrace_scope=$(cat /proc/sys/kernel/yama/ptrace_scope 2>/dev/null || echo 0) +if [ ${at_yama_ptrace_scope} -gt 0 ] ; then + if [ "$(id -u)" -eq 0 ] ; then + at_yama_ptrace_scope=0 + fi +fi +export at_yama_ptrace_scope + # This script must finish with ($? == 0) else the autotest runner gets upset. : diff --git a/tests/fchmod-0.c b/tests/fchmod-0.c new file mode 100644 index 0000000..de0c237 --- /dev/null +++ b/tests/fchmod-0.c @@ -0,0 +1,35 @@ +/* + * https://bugs.gentoo.org/599706 + * + */ + +#include "headers.h" + +int main(int argc, char *argv[]) +{ + if (argc < 2) + return -2; + + int mode = 0; + sscanf(argv[1], "%i", &mode); + /* The sandbox catches this: + * + * int fd = open(argv[2], O_RDWR); + * + * And it /should/ catch this: + * + * int fd = open(argv[2], O_RDONLY); + * + * ...but the latter only works when /proc/self/fd/%i + * is available. + * + */ +#ifdef SANDBOX_PROC_SELF_FD + int fd = open(argv[2], O_RDONLY); +#else + int fd = open(argv[2], O_RDWR); +#endif + int fchmod_result = fchmod(fd, (mode_t)mode); + close(fd); + return fchmod_result; +} diff --git a/tests/fchmod-1.sh b/tests/fchmod-1.sh new file mode 100755 index 0000000..140d84f --- /dev/null +++ b/tests/fchmod-1.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# +# https://bugs.gentoo.org/599706 +# + +addwrite $PWD +rm -f deny || exit 1 +touch deny || exit 1 +adddeny $PWD/deny + +# The sandbox doesn't log anything when it returns a junk file +# descriptor? It doesn't look like we can test the contents of +# sandbox.log here... instead, we just have to count on fchmod +# failing, which it does if you use O_RDWR, and it *should* if you use +# O_RDONLY (because that won't stop the change of permissions). +fchmod-0 $(stat --format='%#04a' $PWD/deny) $PWD/deny && exit 1 + +exit 0 diff --git a/tests/fchmod-2.sh b/tests/fchmod-2.sh new file mode 100755 index 0000000..96d7cc9 --- /dev/null +++ b/tests/fchmod-2.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Ensure that fchmod() doesn't trigger spurious violations in the most +# basic of cases. +# +addwrite $PWD + +# This should not trigger a violation. +rm -f file +touch file +fchmod-0 0644 file || exit 1 diff --git a/tests/fchmod.at b/tests/fchmod.at new file mode 100644 index 0000000..d364b4b --- /dev/null +++ b/tests/fchmod.at @@ -0,0 +1,2 @@ +SB_CHECK(1) +SB_CHECK(2) diff --git a/tests/fchown-0.c b/tests/fchown-0.c new file mode 100644 index 0000000..7fdca73 --- /dev/null +++ b/tests/fchown-0.c @@ -0,0 +1,34 @@ +/* + * https://bugs.gentoo.org/599706 + * + */ + +#include "headers.h" + +int main(int argc, char *argv[]) +{ + if (argc < 3) + return -2; + + uid_t uid = atoi(argv[1]); + gid_t gid = atoi(argv[2]); + /* The sandbox catches this: + * + * int fd = open(argv[3], O_RDWR); + * + * And it /should/ catch this: + * + * int fd = open(argv[3], O_RDONLY); + * + * ...but the latter only works when /proc/self/fd/%i + * is available. + */ +#ifdef SANDBOX_PROC_SELF_FD + int fd = open(argv[3], O_RDONLY); +#else + int fd = open(argv[3], O_RDWR); +#endif + int fchown_result = fchown(fd, uid, gid); + close(fd); + return fchown_result; +} diff --git a/tests/fchown-1.sh b/tests/fchown-1.sh new file mode 100755 index 0000000..6c1178e --- /dev/null +++ b/tests/fchown-1.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# +# https://bugs.gentoo.org/599706 +# + +addwrite $PWD +rm -f deny || exit 1 +touch deny || exit 1 +adddeny $PWD/deny + +# The sandbox doesn't log anything when it returns a junk file +# descriptor? It doesn't look like we can test the contents of +# sandbox.log here... instead, we just have to count on fchown +# failing, which it does if you use O_RDWR, and it *should* if you use +# O_RDONLY (because that won't stop the change of ownership). +fchown-0 ${SB_UID} ${SB_GID} $PWD/deny && exit 1 + +exit 0 diff --git a/tests/fchown-2.sh b/tests/fchown-2.sh new file mode 100755 index 0000000..dedfbe4 --- /dev/null +++ b/tests/fchown-2.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Ensure that fchown() doesn't trigger spurious violations in the most +# basic of cases. +# +addwrite $PWD + +# This should not trigger a violation. +rm -f file +touch file +fchown-0 ${SB_UID} ${SB_GID} file || exit 1 diff --git a/tests/fchown.at b/tests/fchown.at new file mode 100644 index 0000000..d364b4b --- /dev/null +++ b/tests/fchown.at @@ -0,0 +1,2 @@ +SB_CHECK(1) +SB_CHECK(2) diff --git a/tests/fork-follow_static_tst.c b/tests/fork-follow_static_tst.c new file mode 100644 index 0000000..363384e --- /dev/null +++ b/tests/fork-follow_static_tst.c @@ -0,0 +1 @@ +#include "fork-follow_tst.c" diff --git a/tests/fork-follow_tst.c b/tests/fork-follow_tst.c new file mode 100644 index 0000000..2e3bb95 --- /dev/null +++ b/tests/fork-follow_tst.c @@ -0,0 +1,34 @@ +/* + * Make sure violations in children are caught. + */ + +#include "tests.h" + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + printf("usage: %s <number forks> <path to remove>\n", argv[0]); + exit(1); + } + + int i, forks = atoi(argv[1]); + const char *path = argv[2]; + + for (i = 0; i < forks; ++i) { + pid_t pid = fork(); + if (pid < 0) + errp("unable to fork"); + + if (pid > 0) { + /* parent -- wait for child */ + int status; + if (waitpid(pid, &status, 0) == pid) + exit(WEXITSTATUS(status)); + errp("waitpid failed"); + } + /* child -- keep looping */ + } + + /* final child -- try to create the path */ + exit(creat(path, 0666) < 0 ? 0 : 1); +} diff --git a/tests/get-group.c b/tests/get-group.c index 8138967..30cdfc9 100644 --- a/tests/get-group.c +++ b/tests/get-group.c @@ -31,8 +31,8 @@ int main(int argc, char *argv[]) printf("%i\n", grp->gr_gid); } else { const char *file = argv[1]; - struct stat st; - if (lstat(file, &st)) + struct stat64 st; + if (lstat64(file, &st)) errp("lstat(%s) failed", file); printf("%i\n", st.st_gid); } diff --git a/tests/get-user.c b/tests/get-user.c index f85e299..be448d7 100644 --- a/tests/get-user.c +++ b/tests/get-user.c @@ -31,8 +31,8 @@ int main(int argc, char *argv[]) printf("%i\n", pwd->pw_uid); } else { const char *file = argv[1]; - struct stat st; - if (lstat(file, &st)) + struct stat64 st; + if (lstat64(file, &st)) errp("lstat(%s) failed", file); printf("%i\n", st.st_uid); } diff --git a/tests/git-bisector.sh b/tests/git-bisector.sh index c45db6e..b64dff6 100755 --- a/tests/git-bisector.sh +++ b/tests/git-bisector.sh @@ -21,10 +21,21 @@ make="make -s -j" cat << EOF > git-run.sh #!/bin/sh ./autogen.sh -./configure -q -C $(sandbox -V | tail -n1) +# Newer versions of sandbox can run configure for us. +# Should drop old support around Jan 2023. +if sandbox --help | grep -q -e--run-configure ; then + sandbox --run-configure -q -C +else + ./configure -q -C $(sandbox -V | tail -n1) +fi ${make} clean ${make} -./src/sandbox.sh . ./data/sandbox.bashrc \; . ./git-run-sandbox.sh +opt= +# Older versions of sandbox implied -c all the time. +if ./src/sandbox.sh --help | grep -q -e--bash ; then + opt="-c" +fi +./src/sandbox.sh ${opt} . ./data/sandbox.bashrc \; . ./git-run-sandbox.sh EOF chmod a+rx git-run.sh diff --git a/tests/libsigsegv_tst.c b/tests/libsigsegv_tst.c index 82ed21b..2b17fa4 100644 --- a/tests/libsigsegv_tst.c +++ b/tests/libsigsegv_tst.c @@ -11,7 +11,7 @@ #define WRITE(msg) ({ ssize_t w = write(1, msg, sizeof(msg) - 1); w; }) -#ifdef HAVE_SIGSEGV_H +#if defined(HAVE_SIGSEGV_H) && defined(HAVE_LIBSIGSEGV) #include <sigsegv.h> static int segv_handler(void *address, int serious) diff --git a/tests/local.at b/tests/local.at index 95db774..028961d 100644 --- a/tests/local.at +++ b/tests/local.at @@ -6,7 +6,7 @@ dnl due to the default PM test env having that predict. m4_defun([SB_RUN],[\ env \ SANDBOX_LOG="$PWD/sandbox.log" \ - sandbox.sh \ + sandbox.sh -c \ addpredict / \; \ addwrite "${PWD%/*}" \; \ set -x \; \ diff --git a/tests/local.mk b/tests/local.mk new file mode 100644 index 0000000..f1f4ac0 --- /dev/null +++ b/tests/local.mk @@ -0,0 +1,150 @@ +AT_FILES = $(wildcard $(top_srcdir)/%D%/*.at) +DISTCLEANFILES += %D%/atconfig + +# Use top_srcdir for dependencies, and abs_top_srcdir to execute it. +TESTSUITE = $(top_srcdir)/%D%/testsuite +ABS_TESTSUITE = $(abs_top_srcdir)/%D%/testsuite + +EXTRA_DIST += \ + $(AT_FILES) \ + $(TESTSUITE) \ + $(TESTSUITE_LIST) \ + %D%/atlocal.in \ + %D%/package.m4.in \ + %D%/test-skel-0.c \ + %D%/tests.h \ + %D%/xattr-0 + +check_PROGRAMS += \ + %D%/get-group \ + %D%/get-user \ + %D%/sb_true \ + %D%/sb_true_static \ + \ + %D%/access-0 \ + %D%/chmod-0 \ + %D%/chown-0 \ + %D%/creat-0 \ + %D%/creat64-0 \ + %D%/execv-0 \ + %D%/execvp-0 \ + %D%/faccessat-0 \ + %D%/fchmod-0 \ + %D%/fchmodat-0 \ + %D%/fchown-0 \ + %D%/fchownat-0 \ + %D%/fopen-0 \ + %D%/fopen64-0 \ + %D%/futimesat-0 \ + %D%/lchown-0 \ + %D%/link-0 \ + %D%/linkat-0 \ + %D%/linkat_static-0 \ + %D%/lremovexattr-0 \ + %D%/lsetxattr-0 \ + %D%/lutimes-0 \ + %D%/mkdtemp-0 \ + %D%/mkdir-0 \ + %D%/mkdir_static-0 \ + %D%/mkdirat-0 \ + %D%/mkfifo-0 \ + %D%/mkfifoat-0 \ + %D%/mknod-0 \ + %D%/mknodat-0 \ + %D%/mkostemp-0 \ + %D%/mkostemp64-0 \ + %D%/mkostemps-0 \ + %D%/mkostemps64-0 \ + %D%/mkstemp-0 \ + %D%/mkstemp64-0 \ + %D%/mkstemps-0 \ + %D%/mkstemps64-0 \ + %D%/open-0 \ + %D%/open_static-0 \ + %D%/open64-0 \ + %D%/openat-0 \ + %D%/openat_static-0 \ + %D%/openat64-0 \ + %D%/opendir-0 \ + %D%/remove-0 \ + %D%/removexattr-0 \ + %D%/rename-0 \ + %D%/renameat-0 \ + %D%/renameat2-0 \ + %D%/rmdir-0 \ + %D%/setxattr-0 \ + %D%/signal_static-0 \ + %D%/symlink-0 \ + %D%/symlinkat-0 \ + %D%/truncate-0 \ + %D%/truncate64-0 \ + %D%/unlink-0 \ + %D%/unlink_static-0 \ + %D%/unlinkat-0 \ + %D%/utime-0 \ + %D%/utimensat-0 \ + %D%/utimensat64-0 \ + %D%/utimensat_static-0 \ + %D%/utimensat64_static-0 \ + %D%/utimes-0 \ + %D%/vfork-0 \ + \ + %D%/fork-follow_tst \ + %D%/fork-follow_static_tst \ + %D%/getcwd-gnulib_tst \ + %D%/libsigsegv_tst \ + %D%/malloc_hooked_tst \ + %D%/malloc_mmap_tst \ + %D%/pipe-fork_tst \ + %D%/pipe-fork_static_tst \ + %D%/sb_printf_tst \ + %D%/sigsuspend-zsh_tst \ + %D%/sigsuspend-zsh_static_tst \ + %D%/trace-memory_static_tst + +dist_check_SCRIPTS += \ + $(wildcard $(top_srcdir)/%D%/*-[0-9]*.sh) \ + %D%/malloc-0 \ + %D%/script-0 \ + %D%/trace-0 + +# This will be used by all programs, not just tests/ ... +AM_LDFLAGS = `expr $@ : .*_static >/dev/null && echo -all-static` + +%C%_sb_printf_tst_CFLAGS = -I$(top_srcdir)/libsbutil -I$(top_srcdir)/libsbutil/include +%C%_sb_printf_tst_LDADD = libsbutil/libsbutil.la + +%C%_malloc_hooked_tst_LDFLAGS = $(AM_LDFLAGS) -pthread + +%C%_libsigsegv_tst_CPPFLAGS = ${AM_CPPFLAGS} +if HAVE_LIBSIGSEGV +%C%_libsigsegv_tst_CPPFLAGS += -DHAVE_LIBSIGSEGV +%C%_libsigsegv_tst_LDADD = -lsigsegv +endif + +TESTSUITEFLAGS = --jobs=`getconf _NPROCESSORS_ONLN || echo 1` + +# Helper target for devs to precompile. +tests: $(check_PROGRAMS) $(TESTSUITE) + +check-local: %D%/atconfig %D%/atlocal $(TESTSUITE) + cd %D% && $(SHELL) '$(ABS_TESTSUITE)' AUTOTEST_PATH='src:tests' $(TESTSUITEFLAGS) + +installcheck-local: %D%/atconfig %D%/atlocal $(TESTSUITE) + cd %D% && $(SHELL) '$(ABS_TESTSUITE)' AUTOTEST_PATH='src:tests:$(bindir)' $(TESTSUITEFLAGS) + +clean-local: + test ! -f '$(TESTSUITE)' || { cd %D% && $(SHELL) '$(ABS_TESTSUITE)' --clean; } + +TESTSUITE_LIST = $(top_srcdir)/%D%/testsuite.list.at +AUTOTEST = $(AUTOM4TE) --language=autotest +$(TESTSUITE): $(AT_FILES) $(TESTSUITE_LIST) + @$(MKDIR_P) $(top_srcdir)/%D% + $(AM_V_GEN)cd $(top_srcdir)/%D% && $(AUTOTEST) -I. -o testsuite.tmp testsuite.at + $(AM_V_at)mv $@.tmp $@ + +$(TESTSUITE_LIST): $(AT_FILES) + @$(MKDIR_P) $(top_srcdir)/%D% + $(AM_V_GEN)( echo "dnl DO NOT EDIT: GENERATED BY MAKEFILE.AM"; \ + $(GREP) -l -e '^SB_CHECK' -e '^AT_CHECK' $(AT_FILES) | LC_ALL=C sort | \ + $(SED) -e 's:^[^/]*/%D%/:sb_inc([:' -e 's:[.]at$$:]):' ) > $@ diff --git a/tests/lremovexattr-0.c b/tests/lremovexattr-0.c new file mode 100644 index 0000000..ca925f1 --- /dev/null +++ b/tests/lremovexattr-0.c @@ -0,0 +1,15 @@ +#define FUNC lremovexattr +#define SFUNC "lremovexattr" +#define FUNC_STR "\"%s\", \"%s\"" +#define FUNC_IMP path, name +#define ARG_CNT 2 +#define ARG_USE "<path> <name>" + +#define process_args() \ + s = argv[i++]; \ + char *path = s; \ + \ + s = argv[i++]; \ + char *name = s; + +#include "test-skel-0.c" diff --git a/tests/lsetxattr-0.c b/tests/lsetxattr-0.c new file mode 100644 index 0000000..b1ed475 --- /dev/null +++ b/tests/lsetxattr-0.c @@ -0,0 +1,24 @@ +#define FUNC lsetxattr +#define SFUNC "lsetxattr" +#define FUNC_STR "\"%s\", \"%s\", \"%s\", %zu, %i" +#define FUNC_IMP path, name, value, size, flags +#define ARG_CNT 5 +#define ARG_USE "<path> <name> <value> <size> <flags>" + +#define process_args() \ + s = argv[i++]; \ + char *path = s; \ + \ + s = argv[i++]; \ + char *name = s; \ + \ + s = argv[i++]; \ + char *value = s; \ + \ + s = argv[i++]; \ + size_t size = atoi(s); \ + \ + s = argv[i++]; \ + int flags = atoi(s); + +#include "test-skel-0.c" diff --git a/tests/lutimes-1.sh b/tests/lutimes-1.sh new file mode 100755 index 0000000..8638bb2 --- /dev/null +++ b/tests/lutimes-1.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +addwrite "${PWD}" + +sym="lutimes-1.sym" +ln -s /bad/path "${sym}" + +lutimes-0 0 "${sym}" NULL || exit 1 +lutimes-0 -1,EACCES /bin/sh NULL || exit 1 diff --git a/tests/lutimes.at b/tests/lutimes.at new file mode 100644 index 0000000..081d7d2 --- /dev/null +++ b/tests/lutimes.at @@ -0,0 +1 @@ +SB_CHECK(1) diff --git a/tests/malloc_hooked_tst.c b/tests/malloc_hooked_tst.c index 18737fe..8d0922e 100644 --- a/tests/malloc_hooked_tst.c +++ b/tests/malloc_hooked_tst.c @@ -9,7 +9,7 @@ * libsandbox tries to initialize itself (since it never finished originally) -> * libsandbox's malloc() -> * dlsym() -> deadlock - * http://crbug.com/586444 + * https://crbug.com/586444 */ #include "headers.h" diff --git a/tests/removexattr-0.c b/tests/removexattr-0.c new file mode 100644 index 0000000..4abdfff --- /dev/null +++ b/tests/removexattr-0.c @@ -0,0 +1,15 @@ +#define FUNC removexattr +#define SFUNC "removexattr" +#define FUNC_STR "\"%s\", \"%s\"" +#define FUNC_IMP path, name +#define ARG_CNT 2 +#define ARG_USE "<path> <name>" + +#define process_args() \ + s = argv[i++]; \ + char *path = s; \ + \ + s = argv[i++]; \ + char *name = s; + +#include "test-skel-0.c" diff --git a/tests/removexattr-1.sh b/tests/removexattr-1.sh new file mode 100755 index 0000000..327f4dd --- /dev/null +++ b/tests/removexattr-1.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Make sure we catch removexattr. +[ "${at_xfail}" = "yes" ] && exit 77 # see trace-0 +xattr-0 ; ret=$? ; [ ${ret} -eq 0 ] || exit ${ret} + +# Set it to something to make sure it works. +touch f +setxattr-0 0 f user.sandbox test 4 0 || exit 1 + +# Try to remove it and get rejected. +adddeny "${PWD}" +removexattr-0 0 f user.sandbox +test -e sandbox.log diff --git a/tests/removexattr.at b/tests/removexattr.at new file mode 100644 index 0000000..081d7d2 --- /dev/null +++ b/tests/removexattr.at @@ -0,0 +1 @@ +SB_CHECK(1) diff --git a/tests/renameat2-0.c b/tests/renameat2-0.c new file mode 100644 index 0000000..6041d69 --- /dev/null +++ b/tests/renameat2-0.c @@ -0,0 +1,22 @@ +#define CONFIG HAVE_RENAMEAT2 +#define FUNC renameat2 +#define SFUNC "renameat2" +#define FUNC_STR "%i, \"%s\", %i, \"%s\", %i" +#define FUNC_IMP olddirfd, oldpath, newdirfd, newpath, 0 +#define ARG_CNT 4 +#define ARG_USE "<dirfd>(old) <path>(old) <dirfd>(new) <path>(new)" + +#define process_args() \ + s = argv[i++]; \ + int olddirfd = at_get_fd(s); \ + \ + s = argv[i++]; \ + char *oldpath = s; \ + \ + s = argv[i++]; \ + int newdirfd = at_get_fd(s); \ + \ + s = argv[i++]; \ + char *newpath = s; + +#include "test-skel-0.c" diff --git a/tests/renameat2-1.sh b/tests/renameat2-1.sh new file mode 100755 index 0000000..9f91c05 --- /dev/null +++ b/tests/renameat2-1.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# basic functionality check + +addwrite $PWD + +touch old || exit 1 +renameat2-0 0 AT_FDCWD old AT_FDCWD new || exit 1 +[ ! -e old -a -e new ] diff --git a/tests/renameat2-2.sh b/tests/renameat2-2.sh new file mode 100755 index 0000000..420b36d --- /dev/null +++ b/tests/renameat2-2.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# make sure we can clobber symlinks #612202 + +addwrite $PWD + +ln -s /asdf sym || exit 1 +touch file +renameat2-0 0 AT_FDCWD file AT_FDCWD sym || exit 1 +[ ! -e file ] +[ ! -L sym ] +[ -e sym ] +test ! -s "${SANDBOX_LOG}" diff --git a/tests/renameat2-3.sh b/tests/renameat2-3.sh new file mode 100755 index 0000000..ca945a5 --- /dev/null +++ b/tests/renameat2-3.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# make sure we reject bad renames #612202 + +addwrite $PWD +mkdir deny +adddeny $PWD/deny + +touch file +renameat2-0 -1,EACCES AT_FDCWD file AT_FDCWD deny/file || exit 1 +[ -e file ] +test -s "${SANDBOX_LOG}" diff --git a/tests/renameat2.at b/tests/renameat2.at new file mode 100644 index 0000000..eec4638 --- /dev/null +++ b/tests/renameat2.at @@ -0,0 +1,3 @@ +SB_CHECK(1) +SB_CHECK(2) +SB_CHECK(3) diff --git a/tests/script-1.sh b/tests/script-1.sh index 3ac6252..8eb46bf 100755 --- a/tests/script-1.sh +++ b/tests/script-1.sh @@ -1,5 +1,8 @@ #!/bin/sh -# http://bugs.gentoo.org/257418 +# https://bugs.gentoo.org/257418 [ "${at_xfail}" = "yes" ] && exit 77 # see script-0 -(>/dev/fd/3) +( +cd "${devfd}" +>3 +) exit 0 diff --git a/tests/script-14.sh b/tests/script-14.sh index 6fa55a0..6fa55a0 100644..100755 --- a/tests/script-14.sh +++ b/tests/script-14.sh diff --git a/tests/script-15.sh b/tests/script-15.sh index b2acddc..b2acddc 100644..100755 --- a/tests/script-15.sh +++ b/tests/script-15.sh diff --git a/tests/script-16.sh b/tests/script-16.sh new file mode 100755 index 0000000..73b7803 --- /dev/null +++ b/tests/script-16.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# https://bugs.gentoo.org/139591 +[ "${at_xfail}" = "yes" ] && exit 77 # see script-0 +addwrite $PWD + +mkdir -p to-be/deleted +cd to-be/deleted +rmdir ../deleted + +# In https://bugs.gentoo.org/590084 sanbox should deny +# access here and touch should fail: +! touch ../foo diff --git a/tests/script-17.sh b/tests/script-17.sh new file mode 100755 index 0000000..83c51f9 --- /dev/null +++ b/tests/script-17.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Make sure forked children are caught. Historically, dynamic worked fine, but +# static missed forks. +[ "${at_xfail}" = "yes" ] && exit 77 # see script-0 + +# Setup scratch path. +mkdir subdir +adddeny "${PWD}/subdir" + +for child in 0 1 2 3 4 5 ; do + fork-follow_tst ${child} subdir/dyn${child} || exit $? +done + +depth="0" +# We can't trace static children currently with YAMA ptrace_scope 1+. +if [ ${at_yama_ptrace_scope} -eq 0 ] ; then + depth="${depth} 1 2 3 4 5" +fi +for child in ${depth} ; do + fork-follow_static_tst ${child} subdir/static${child} || exit $? +done + +exit 0 diff --git a/tests/script-2.sh b/tests/script-2.sh index ec10fa9..3c7d66e 100755 --- a/tests/script-2.sh +++ b/tests/script-2.sh @@ -1,5 +1,5 @@ #!/bin/sh -# http://bugs.gentoo.org/139591 +# https://bugs.gentoo.org/139591 [ "${at_xfail}" = "yes" ] && exit 77 # see script-0 addwrite $PWD diff --git a/tests/script-3.sh b/tests/script-3.sh index be7f7a3..60ca5ce 100755 --- a/tests/script-3.sh +++ b/tests/script-3.sh @@ -1,5 +1,5 @@ #!/bin/sh -# http://bugs.gentoo.org/260765 +# https://bugs.gentoo.org/260765 [ "${at_xfail}" = "yes" ] && exit 77 # see script-0 addwrite $PWD diff --git a/tests/script-8.sh b/tests/script-8.sh index 6d9de55..9d8ca11 100755 --- a/tests/script-8.sh +++ b/tests/script-8.sh @@ -6,6 +6,9 @@ sigsuspend-zsh_tst d=$? echo "ret = $d" +# We can't trace static children currently with YAMA ptrace_scope 1+. +[ ${at_yama_ptrace_scope} -gt 0 ] && exit ${d} + sigsuspend-zsh_static_tst s=$? echo "ret = $s" diff --git a/tests/script.at b/tests/script.at index 8837bda..037d27e 100644 --- a/tests/script.at +++ b/tests/script.at @@ -13,3 +13,5 @@ SB_CHECK(12) SB_CHECK(13) SB_CHECK(14) SB_CHECK(15) +SB_CHECK(16) +SB_CHECK(17) diff --git a/tests/setxattr-0.c b/tests/setxattr-0.c new file mode 100644 index 0000000..2717b85 --- /dev/null +++ b/tests/setxattr-0.c @@ -0,0 +1,24 @@ +#define FUNC setxattr +#define SFUNC "setxattr" +#define FUNC_STR "\"%s\", \"%s\", \"%s\", %zu, %i" +#define FUNC_IMP path, name, value, size, flags +#define ARG_CNT 5 +#define ARG_USE "<path> <name> <value> <size> <flags>" + +#define process_args() \ + s = argv[i++]; \ + char *path = s; \ + \ + s = argv[i++]; \ + char *name = s; \ + \ + s = argv[i++]; \ + char *value = s; \ + \ + s = argv[i++]; \ + size_t size = atoi(s); \ + \ + s = argv[i++]; \ + int flags = atoi(s); + +#include "test-skel-0.c" diff --git a/tests/setxattr-1.sh b/tests/setxattr-1.sh new file mode 100755 index 0000000..6bbe1df --- /dev/null +++ b/tests/setxattr-1.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Make sure we catch setxattr. +[ "${at_xfail}" = "yes" ] && exit 77 # see trace-0 +xattr-0 ; ret=$? ; [ ${ret} -eq 0 ] || exit ${ret} + +# Set it to something to make sure it works. +touch f +setxattr-0 0 f user.sandbox test 4 0 || exit 1 + +# Try to set it again and get rejected. +adddeny "${PWD}" +setxattr-0 0 f user.sandbox test 4 0 +test -e sandbox.log diff --git a/tests/setxattr.at b/tests/setxattr.at new file mode 100644 index 0000000..081d7d2 --- /dev/null +++ b/tests/setxattr.at @@ -0,0 +1 @@ +SB_CHECK(1) diff --git a/tests/test-skel-0.c b/tests/test-skel-0.c index 96e42ae..91128d3 100644 --- a/tests/test-skel-0.c +++ b/tests/test-skel-0.c @@ -128,7 +128,7 @@ int at_get_fd(const char *str_dirfd) } str_mode = strtok(NULL, ":"); - return open(str_path, f_get_flags(str_flags), sscanf_mode_t(str_mode)); + return open64(str_path, f_get_flags(str_flags), sscanf_mode_t(str_mode)); } #define V_TIMESPEC "NULL | NOW | #[,#]" @@ -144,8 +144,8 @@ struct timespec *parse_timespec(const char *s) if (!strcmp(s, "NOW")) { times->tv_sec = time(0); } else { - long sec = 0, nsec = 0; - sscanf(s, "%li,%li", &sec, &nsec); + int64_t sec = 0, nsec = 0; + sscanf(s, "%" PRIi64 ",%" PRIi64, &sec, &nsec); times->tv_sec = sec; times->tv_nsec = nsec; } diff --git a/tests/tests.h b/tests/tests.h index 22733ca..610388d 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -73,6 +73,7 @@ const value_pair tbl_errno[] = { PAIR(EMLINK) PAIR(ENAMETOOLONG) PAIR(ENOBUFS) + PAIR(ENODATA) PAIR(ENODEV) PAIR(ENOENT) PAIR(ENOEXEC) diff --git a/tests/trace-0 b/tests/trace-0 index 5a91c7a..99f3037 100755 --- a/tests/trace-0 +++ b/tests/trace-0 @@ -1,6 +1,6 @@ #!/bin/sh # make sure trace support exists -if grep -q trace_child_signal "$abs_top_builddir"/libsandbox/.libs/libsandbox.so ; then +if grep -q trace_loop "$abs_top_builddir"/libsandbox/.libs/libsandbox.so ; then # see comment at top of script-0 -- same issue applies here because # the ld.so isn't around to load the correct sandbox lib for us exec script-0 diff --git a/tests/trace-memory_static_tst.c b/tests/trace-memory_static_tst.c index 14c6477..86a47fe 100644 --- a/tests/trace-memory_static_tst.c +++ b/tests/trace-memory_static_tst.c @@ -26,7 +26,7 @@ volatile uintptr_t offset = 0; #define check_ptr(addr) \ ({ \ printf(" open(%p)\n", addr); \ - ret = open(non_const_ptr(addr), O_RDONLY); \ + ret = open64(non_const_ptr(addr), O_RDONLY); \ assert(ret == -1 && errno == EFAULT); \ }) @@ -53,7 +53,7 @@ int main(int argc, char *argv[]) printf(" open(%p -> %p [+%#zx])\n", p, p + len, len); memset(p, 'a', len); path[end] = '\0'; - ret = open(p, O_RDONLY); + ret = open64(p, O_RDONLY); assert(ret == -1 && (errno == ENOENT || errno == ENAMETOOLONG)); } } diff --git a/tests/utimensat-3.sh b/tests/utimensat-3.sh index 4ecd4b3..a7b9843 100755 --- a/tests/utimensat-3.sh +++ b/tests/utimensat-3.sh @@ -1,6 +1,17 @@ #!/bin/sh # make sure NULL filename is handled correctly +# Note: this test is dependent on glibc internals +# other libcs chose not to validate invalid parameters: +# https://bugs.gentoo.org/549108#c28 +# https://www.openwall.com/lists/musl/2019/06/25/1 +# Run this test only on glibc systems. + +case $HOST in + *-linux-gnu);; + *) exit 77;; +esac + addwrite $PWD exec utimensat-0 -1:22 'f:O_WRONLY|O_CREAT:0666' NULL NULL 0 diff --git a/tests/utimensat64-0.c b/tests/utimensat64-0.c new file mode 100644 index 0000000..bbacef5 --- /dev/null +++ b/tests/utimensat64-0.c @@ -0,0 +1,3 @@ +#define _TIME_BITS 64 +#define _FILE_OFFSET_BITS 64 +#include "utimensat-0.c" diff --git a/tests/utimensat64-1.sh b/tests/utimensat64-1.sh new file mode 100755 index 0000000..2aebc5f --- /dev/null +++ b/tests/utimensat64-1.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# basic functionality check + +addwrite $PWD + +touch -r / file || exit 1 +utimensat64-0 0 AT_FDCWD . NULL 0 || exit 1 +utimensat64-0 0 AT_FDCWD file NULL 0 || exit 1 +[ file -nt / ] diff --git a/tests/utimensat64.at b/tests/utimensat64.at new file mode 100644 index 0000000..081d7d2 --- /dev/null +++ b/tests/utimensat64.at @@ -0,0 +1 @@ +SB_CHECK(1) diff --git a/tests/utimensat64_static-0.c b/tests/utimensat64_static-0.c new file mode 100644 index 0000000..73e7602 --- /dev/null +++ b/tests/utimensat64_static-0.c @@ -0,0 +1 @@ +#include "utimensat64-0.c" diff --git a/tests/utimensat64_static-1.sh b/tests/utimensat64_static-1.sh new file mode 100755 index 0000000..7b0355c --- /dev/null +++ b/tests/utimensat64_static-1.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# basic functionality check +[ "${at_xfail}" = "yes" ] && exit 77 # see trace-0 + +addwrite $PWD + +touch -r / file || exit 1 +utimensat64_static-0 0 AT_FDCWD . NULL 0 || exit 1 +utimensat64_static-0 0 AT_FDCWD file NULL 0 || exit 1 +[ file -nt / ] diff --git a/tests/utimensat64_static.at b/tests/utimensat64_static.at new file mode 100644 index 0000000..081d7d2 --- /dev/null +++ b/tests/utimensat64_static.at @@ -0,0 +1 @@ +SB_CHECK(1) diff --git a/tests/xattr-0 b/tests/xattr-0 new file mode 100755 index 0000000..5504443 --- /dev/null +++ b/tests/xattr-0 @@ -0,0 +1,7 @@ +#!/bin/sh +# Make sure the filesystem supports xattrs. +file=".test.xattrs" +touch "${file}" +setxattr-0 0 "${file}" user.sandbox test 4 0 && ret=0 || ret=77 +rm -f "${file}" +exit "${ret}" |