diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60ddecb43..f049d76d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,6 +139,7 @@ jobs: - { target: arm-unknown-linux-gnueabihf, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1 } - { target: armv7-unknown-linux-gnueabi, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: qemu-user qemu-system } - { target: armv7-unknown-linux-gnueabihf, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: qemu-user qemu-system } + - { target: thumbv7neon-unknown-linux-gnueabihf, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: qemu-user qemu-system } - { target: i586-unknown-linux-gnu, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1 } - { target: i686-unknown-linux-gnu, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: native qemu-user qemu-system } - { target: mips-unknown-linux-gnu, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: qemu-user qemu-system } @@ -167,6 +168,7 @@ jobs: - { target: aarch64-linux-android, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: arm-linux-androideabi, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: armv7-linux-androideabi, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } + - { target: thumbv7neon-linux-androideabi, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: i686-linux-android, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: x86_64-linux-android, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: x86_64-pc-windows-gnu, os: ubuntu-latest, cpp: 1, std: 1, run: 1 } diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ebf1293e..c74ac2b6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - #767 - added the `cross-util` and `cross-dev` commands. +- #745 - added `thumbv7neon-*` targets. - #741 - added `armv7-unknown-linux-gnueabi` and `armv7-unknown-linux-musleabi` targets. - #721 - add support for running doctests on nightly if `CROSS_UNSTABLE_ENABLE_DOCTESTS=true`. - #719 - add `--list` to known subcommands. @@ -23,6 +24,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #762 - re-enabled `x86_64-unknown-dragonfly` target. - #747 - reduced android image sizes. +- #746 - limit image permissions for android images. - #377 - update WINE versions to 7.0. - #734 - patch `arm-unknown-linux-gnueabihf` to build for ARMv6, and add architecture for crosstool-ng-based images. - #709 - Update Emscripten targets to `emcc` version 3.1.10 diff --git a/docker/Dockerfile.thumbv7neon-linux-androideabi b/docker/Dockerfile.thumbv7neon-linux-androideabi new file mode 100644 index 000000000..203392d52 --- /dev/null +++ b/docker/Dockerfile.thumbv7neon-linux-androideabi @@ -0,0 +1,55 @@ +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive + +COPY common.sh lib.sh / +RUN /common.sh + +COPY cmake.sh / +RUN /cmake.sh + +COPY xargo.sh / +RUN /xargo.sh + +COPY android-ndk.sh / +RUN /android-ndk.sh arm 28 +ENV PATH=$PATH:/android-ndk/bin + +COPY android-system.sh / +RUN /android-system.sh arm + +COPY qemu.sh / +RUN /qemu.sh arm + +RUN cp /android-ndk/sysroot/usr/lib/arm-linux-androideabi/28/libz.so /system/lib/ + +COPY android-runner / + +# Libz is distributed in the android ndk, but for some unknown reason it is not +# found in the build process of some crates, so we explicit set the DEP_Z_ROOT +# likewise, the toolchains expect the prefix `thumbv7neon-linux-androideabi`, +# which we don't have, so just export every possible variable, such as AR. +# Also export all target binutils just in case required. +ENV CARGO_TARGET_THUMBV7NEON_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \ + CARGO_TARGET_THUMBV7NEON_LINUX_ANDROIDEABI_RUNNER="/android-runner arm" \ + AR_thumbv7neon_linux_androideabi=arm-linux-androideabi-ar \ + AS_thumbv7neon_linux_androideabi=arm-linux-androideabi-as \ + CC_thumbv7neon_linux_androideabi=arm-linux-androideabi-gcc \ + CXX_thumbv7neon_linux_androideabi=arm-linux-androideabi-g++ \ + LD_thumbv7neon_linux_androideabi=arm-linux-androideabi-ld \ + NM_thumbv7neon_linux_androideabi=arm-linux-androideabi-nm \ + OBJCOPY_thumbv7neon_linux_androideabi=arm-linux-androideabi-objcopy \ + OBJDUMP_thumbv7neon_linux_androideabi=arm-linux-androideabi-objdump \ + RANLIB_thumbv7neon_linux_androideabi=arm-linux-androideabi-ranlib \ + READELF_thumbv7neon_linux_androideabi=arm-linux-androideabi-readelf \ + SIZE_thumbv7neon_linux_androideabi=arm-linux-androideabi-size \ + STRINGS_thumbv7neon_linux_androideabi=arm-linux-androideabi-strings \ + STRIP_thumbv7neon_linux_androideabi=arm-linux-androideabi-strip \ + BINDGEN_EXTRA_CLANG_ARGS_thumbv7neon_linux_androideabi="--sysroot=/android-ndk/sysroot" \ + DEP_Z_INCLUDE=/android-ndk/sysroot/usr/include/ \ + RUST_TEST_THREADS=1 \ + HOME=/tmp/ \ + TMPDIR=/tmp/ \ + ANDROID_DATA=/ \ + ANDROID_DNS_MODE=local \ + ANDROID_ROOT=/system + diff --git a/docker/Dockerfile.thumbv7neon-unknown-linux-gnueabihf b/docker/Dockerfile.thumbv7neon-unknown-linux-gnueabihf new file mode 100644 index 000000000..7468bdac4 --- /dev/null +++ b/docker/Dockerfile.thumbv7neon-unknown-linux-gnueabihf @@ -0,0 +1,47 @@ +FROM ubuntu:16.04 +ARG DEBIAN_FRONTEND=noninteractive + +COPY common.sh lib.sh / +RUN /common.sh + +COPY cmake.sh / +RUN /cmake.sh + +COPY xargo.sh / +RUN /xargo.sh + +RUN apt-get update && apt-get install --assume-yes --no-install-recommends \ + g++-arm-linux-gnueabihf \ + libc6-dev-armhf-cross + +COPY qemu.sh / +RUN /qemu.sh arm softmmu + +COPY dropbear.sh / +RUN /dropbear.sh + +COPY linux-image.sh / +RUN /linux-image.sh armv7 + +COPY linux-runner / + +# Export all target binutils just in case required. +ENV CARGO_TARGET_THUMBV7NEON_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ + CARGO_TARGET_THUMBV7NEON_UNKNOWN_LINUX_GNUEABIHF_RUNNER="/linux-runner armv7" \ + AR_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-ar \ + AS_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-as \ + CC_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \ + CXX_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ \ + LD_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-ld \ + NM_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-nm \ + OBJCOPY_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-objcopy \ + OBJDUMP_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-objdump \ + RANLIB_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-ranlib \ + READELF_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-readelf \ + SIZE_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-size \ + STRINGS_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-strings \ + STRIP_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-strip \ + BINDGEN_EXTRA_CLANG_ARGS_thumbv7neon_unknown_linux_gnueabihf="--sysroot=/usr/arm-linux-gnueabihf" \ + QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ + RUST_TEST_THREADS=1 \ + PKG_CONFIG_PATH="/usr/lib/arm-linux-gnueabihf/pkgconfig/:${PKG_CONFIG_PATH}" diff --git a/src/docker.rs b/src/docker.rs index cc9011898..430e8731b 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -1,9 +1,11 @@ +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; use std::{env, fs}; use crate::cargo::CargoMetadata; use crate::extensions::{CommandExt, SafeCommand}; +use crate::file::write_file; use crate::id; use crate::{errors::*, file}; use crate::{Config, Target}; @@ -14,16 +16,34 @@ const DOCKER_IMAGES: &[&str] = &include!(concat!(env!("OUT_DIR"), "/docker-image const CROSS_IMAGE: &str = "ghcr.io/cross-rs"; const DOCKER: &str = "docker"; const PODMAN: &str = "podman"; +// secured profile based off the docker documentation for denied syscalls: +// https://docs.docker.com/engine/security/seccomp/#significant-syscalls-blocked-by-the-default-profile +// note that we've allow listed `clone` and `clone3`, which is necessary +// to fork the process, and which podman allows by default. +const SECCOMP: &str = include_str!("seccomp.json"); + +#[derive(Debug, PartialEq, Eq)] +enum EngineType { + Docker, + Podman, + Other, +} // determine if the container engine is docker. this fixes issues with // any aliases (#530), and doesn't fail if an executable suffix exists. -fn get_is_docker(ce: std::path::PathBuf, verbose: bool) -> Result { +fn get_engine_type(ce: std::path::PathBuf, verbose: bool) -> Result { let stdout = Command::new(ce) .arg("--help") .run_and_get_stdout(verbose)? .to_lowercase(); - Ok(stdout.contains("docker") && !stdout.contains("emulate")) + if stdout.contains("podman") { + Ok(EngineType::Podman) + } else if stdout.contains("docker") && !stdout.contains("emulate") { + Ok(EngineType::Docker) + } else { + Ok(EngineType::Other) + } } pub fn get_container_engine() -> Result { @@ -172,7 +192,7 @@ pub fn run( let runner = config.runner(target)?; let mut docker = docker_command("run")?; - let is_docker = get_is_docker(get_container_engine().unwrap(), verbose)?; + let engine_type = get_engine_type(get_container_engine().unwrap(), verbose)?; for ref var in config.env_passthrough(target)? { validate_env_var(var)?; @@ -233,12 +253,37 @@ pub fn run( docker.arg("--rm"); - if target.needs_docker_privileged() { - docker.arg("--privileged"); + // docker uses seccomp now on all installations + if target.needs_docker_seccomp() { + let seccomp = if engine_type == EngineType::Docker && cfg!(target_os = "windows") { + // docker on windows fails due to a bug in reading the profile + // https://github.com/docker/for-win/issues/12760 + "unconfined".to_string() + } else { + #[allow(unused_mut)] // target_os = "windows" + let mut path = env::current_dir() + .wrap_err("couldn't get current directory")? + .canonicalize() + .wrap_err_with(|| "when canonicalizing current_dir".to_string())? + .join("target") + .join(target.triple()) + .join("seccomp.json"); + if !path.exists() { + write_file(&path, false)?.write_all(SECCOMP.as_bytes())?; + } + #[cfg(target_os = "windows")] + if engine_type == EngineType::Podman { + // podman weirdly expects a WSL path here, and fails otherwise + path = wslpath(&path, verbose)?; + } + path.display().to_string() + }; + + docker.args(&["--security-opt", &format!("seccomp={}", seccomp)]); } // We need to specify the user for Docker, but not for Podman. - if is_docker { + if engine_type == EngineType::Docker { docker.args(&[ "--user", &format!( diff --git a/src/file.rs b/src/file.rs index f8a0181ef..55468df81 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,4 +1,4 @@ -use std::fs::File; +use std::fs::{self, File}; use std::io::Read; use std::path::{Path, PathBuf}; @@ -35,3 +35,18 @@ fn _canonicalize(path: &Path) -> Result { Path::new(&path).canonicalize().map_err(Into::into) } } + +pub fn write_file(path: impl AsRef, overwrite: bool) -> Result { + let path = path.as_ref(); + fs::create_dir_all( + &path.parent().ok_or_else(|| { + eyre::eyre!("could not find parent directory for `{}`", path.display()) + })?, + ) + .wrap_err_with(|| format!("couldn't create directory `{}`", path.display()))?; + fs::OpenOptions::new() + .write(true) + .create_new(!overwrite) + .open(&path) + .wrap_err(format!("could't write to file `{}`", path.display())) +} diff --git a/src/lib.rs b/src/lib.rs index 367e32750..c4c9808c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,8 +228,9 @@ impl Target { !native && (self.is_linux() || self.is_windows() || self.is_bare_metal()) } - fn needs_docker_privileged(&self) -> bool { + fn needs_docker_seccomp(&self) -> bool { let arch_32bit = self.triple().starts_with("arm") + || self.triple().starts_with("thumb") || self.triple().starts_with("i586") || self.triple().starts_with("i686"); diff --git a/src/seccomp.json b/src/seccomp.json new file mode 100644 index 000000000..b3991bd5b --- /dev/null +++ b/src/seccomp.json @@ -0,0 +1,166 @@ +{ + "defaultAction": "SCMP_ACT_ALLOW", + "syscalls": [ + { + "names": [ + "add_key", + "get_kernel_syms", + "keyctl", + "move_pages", + "nfsservctl", + "perf_event_open", + "pivot_root", + "query_module", + "request_key", + "sysfs", + "_sysctl", + "uselib", + "userfaultfd", + "ustat" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1 + }, + { + "names": [ + "acct" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_PACCT" + ] + } + }, + { + "names": [ + "bpf", + "lookup_dcookie", + "mount", + "quotactl", + "quotactl_fd", + "setns", + "swapon", + "swapoff", + "umount", + "umount2", + "unshare", + "vm86", + "vm86old", + "pciconfig_read", + "pciconfig_write", + "salinfo_log_open", + "salinfo_event_open", + "sys_cacheflush", + "rtas" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_ADMIN" + ] + } + }, + { + "names": [ + "clock_adjtime", + "clock_settime", + "settimeofday", + "stime" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_TIME" + ] + } + }, + { + "names": [ + "create_module", + "delete_module", + "finit_module", + "init_module" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_MODULE" + ] + } + }, + { + "names": [ + "get_mempolicy", + "mbind", + "set_mempolicy" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_NICE" + ] + } + }, + { + "names": [ + "ioperm", + "iopl" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_RAWIO" + ] + } + }, + { + "names": [ + "kcmp", + "process_vm_readv", + "process_vm_writev", + "ptrace" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_PTRACE" + ] + } + }, + { + "names": [ + "kexec_file_load", + "kexec_load", + "reboot" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_BOOT" + ] + } + }, + { + "names": [ + "name_to_handle_at", + "open_by_handle_at" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_DAC_READ_SEARCH" + ] + } + } + ] +}