diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..47f7ad9b18 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,27 @@ +version: 2.1 +jobs: + test-local-gcc: + machine: + image: ubuntu-2004:202010-01 + working_directory: ~/criu + steps: + - checkout + - run: + name: "Test local with GCC" + command: sudo -E make -C scripts/ci local + test-local-clang: + machine: + image: ubuntu-2004:202010-01 + working_directory: ~/criu + steps: + - checkout + - run: + name: "Test local with CLANG" + command: sudo -E make -C scripts/ci local CLANG=1 + +workflows: + version: 2 + builds: + jobs: + - test-local-gcc + - test-local-clang diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000000..5574b15835 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,53 @@ +task: + name: Vagrant Fedora based test (no VDSO) + environment: + HOME: "/root" + CIRRUS_WORKING_DIR: "/tmp/criu" + + compute_engine_instance: + image_project: cirrus-images + image: family/docker-kvm + platform: linux + cpu: 4 + memory: 16G + nested_virtualization: true + + setup_script: | + scripts/ci/apt-install make gcc pkg-config git perl-modules iproute2 kmod wget cpu-checker + sudo kvm-ok + ln -sf /usr/include/google/protobuf/descriptor.proto images/google/protobuf/descriptor.proto + build_script: | + make -C scripts/ci vagrant-fedora-no-vdso + +task: + name: CentOS 8 based test + environment: + HOME: "/root" + CIRRUS_WORKING_DIR: "/tmp/criu" + + compute_engine_instance: + image_project: centos-cloud + image: family/centos-8 + platform: linux + cpu: 4 + memory: 8G + + setup_script: | + ln -sf /usr/include/google/protobuf/descriptor.proto images/google/protobuf/descriptor.proto + yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm dnf-plugins-core + yum config-manager --set-enabled powertools + yum install -y --allowerasing asciidoc gcc git gnutls-devel libaio-devel libasan libcap-devel libnet-devel libnl3-devel libselinux-devel make protobuf-c-devel protobuf-devel python3-devel python3-flake8 python3-PyYAML python3-future python3-protobuf xmlto + alternatives --set python /usr/bin/python3 + systemctl stop sssd + # Even with selinux in permissive mode the selinux tests will be executed + # The Cirrus CI user runs as a service from selinux point of view and is + # much more restricted than a normal shell (system_u:system_r:unconfined_service_t:s0) + # The test case above (vagrant-fedora-no-vdso) should run selinux tests in enforcing mode + setenforce 0 + # netns-nft fails with + # 4: FAIL: netns-nft.c:51: Can't get nft table (errno = 11 (Resource temporarily unavailable)) + mv /usr/sbin/nft /usr/sbin/nft.away + pip3 install junit_xml + + build_script: | + make -C scripts/ci local SKIP_CI_PREP=1 CC=gcc CD_TO_TOP=1 diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000000..07eb8be653 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,82 @@ +--- +kind: pipeline +type: docker +name: aarch64 build GCC (native) + +platform: + os: linux + arch: arm64 + +steps: +- name: build + image: ubuntu:focal + commands: + - scripts/ci/apt-install make + - make -C scripts/ci local + +--- +kind: pipeline +type: docker +name: aarch64 build CLANG (native) + +platform: + os: linux + arch: arm64 + +steps: +- name: build + image: ubuntu:focal + commands: + - scripts/ci/apt-install make + - make -C scripts/ci local CLANG=1 + +--- +kind: pipeline +type: docker +name: armhf build GCC (native) + +platform: + os: linux + arch: arm + +steps: +- name: build + # At the time of setting up focal did not work + image: ubuntu:bionic + commands: + - scripts/ci/apt-install make + - make -C scripts/ci local + +--- +kind: pipeline +type: docker +name: armhf build CLANG (native) + +platform: + os: linux + arch: arm + +steps: +- name: build + # At the time of setting up focal did not work + image: ubuntu:bionic + commands: + - scripts/ci/apt-install make + - make -C scripts/ci local CLANG=1 + +--- +kind: pipeline +type: docker +name: aarch64 Fedora Rawhide + +platform: + os: linux + arch: arm64 + +steps: +- name: build + image: registry.fedoraproject.org/fedora:rawhide + commands: + - scripts/ci/prepare-for-fedora-rawhide.sh + - make -C scripts/ci/ local CC=gcc SKIP_CI_PREP=1 SKIP_CI_TEST=1 CD_TO_TOP=1 + - make -C test/zdtm -j 4 diff --git a/.fmf/all.fmf b/.fmf/all.fmf new file mode 100644 index 0000000000..e61ef7b965 --- /dev/null +++ b/.fmf/all.fmf @@ -0,0 +1 @@ +aa diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.github/workflows/alpine-test.yml b/.github/workflows/alpine-test.yml new file mode 100644 index 0000000000..6fc546ff54 --- /dev/null +++ b/.github/workflows/alpine-test.yml @@ -0,0 +1,15 @@ +name: Alpine Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + strategy: + matrix: + target: [GCC=1, CLANG=1] + + steps: + - uses: actions/checkout@v2 + - name: Run Alpine ${{ matrix.target }} Test + run: sudo -E make -C scripts/ci alpine ${{ matrix.target }} diff --git a/.github/workflows/centos-test.yml b/.github/workflows/centos-test.yml new file mode 100644 index 0000000000..41b1f3a6ea --- /dev/null +++ b/.github/workflows/centos-test.yml @@ -0,0 +1,15 @@ +name: CentOS Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + strategy: + matrix: + target: [centos7] + + steps: + - uses: actions/checkout@v2 + - name: Run CentOS ${{ matrix.target }} Test + run: sudo -E make -C scripts/ci ${{ matrix.target }} diff --git a/.github/workflows/compat-test.yml b/.github/workflows/compat-test.yml new file mode 100644 index 0000000000..5ae25fb73e --- /dev/null +++ b/.github/workflows/compat-test.yml @@ -0,0 +1,16 @@ +name: Compat Tests + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + strategy: + matrix: + target: [GCC, CLANG] + + + steps: + - uses: actions/checkout@v2 + - name: Run Compat Tests (${{ matrix.target }}) + run: sudo -E make -C scripts/ci local COMPAT_TEST=y ${{ matrix.target }}=1 diff --git a/.github/workflows/cross-compile-daily-mips.yml b/.github/workflows/cross-compile-daily-mips.yml deleted file mode 100644 index b372a23ae3..0000000000 --- a/.github/workflows/cross-compile-daily-mips.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Daily Cross Compile Tests - -on: - schedule: - - cron: '30 * * * *' - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - target: [mips64el-cross] - branches: [criu-dev] - - steps: - - uses: actions/checkout@v2 - with: - ref: ${{ matrix.branches }} - - name: Run Cross Compilation Targets - run: > - sudo make -C scripts/travis ${{ matrix.target }} diff --git a/.github/workflows/cross-compile-daily.yml b/.github/workflows/cross-compile-daily.yml index 78e9c1619c..7012132766 100644 --- a/.github/workflows/cross-compile-daily.yml +++ b/.github/workflows/cross-compile-daily.yml @@ -2,7 +2,7 @@ name: Daily Cross Compile Tests on: schedule: - - cron: '30 * * * *' + - cron: '30 12 * * *' jobs: build: @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - target: [armv7-cross, aarch64-cross, ppc64-cross] + target: [armv7-cross, aarch64-cross, ppc64-cross, mips64el-cross] branches: [criu-dev, master] steps: @@ -19,4 +19,4 @@ jobs: ref: ${{ matrix.branches }} - name: Run Cross Compilation Targets run: > - sudo make -C scripts/travis ${{ matrix.target }} + sudo make -C scripts/ci ${{ matrix.target }} diff --git a/.github/workflows/cross-compile-mips.yml b/.github/workflows/cross-compile-mips.yml deleted file mode 100644 index 4f6c6dbd39..0000000000 --- a/.github/workflows/cross-compile-mips.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Cross Compile Tests - -on: - push: - branches: [criu-dev] - pull_request: - branches: [criu-dev] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - target: [mips64el-cross] - - steps: - - uses: actions/checkout@v2 - - name: Run Cross Compilation Targets - run: > - sudo make -C scripts/travis ${{ matrix.target }} diff --git a/.github/workflows/cross-compile.yml b/.github/workflows/cross-compile.yml index 3b007cdd09..90862e7abd 100644 --- a/.github/workflows/cross-compile.yml +++ b/.github/workflows/cross-compile.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - target: [armv7-cross, aarch64-cross, ppc64-cross] + target: [armv7-cross, aarch64-cross, ppc64-cross, mips64el-cross] steps: - uses: actions/checkout@v2 - name: Run Cross Compilation Targets run: > - sudo make -C scripts/travis ${{ matrix.target }} + sudo make -C scripts/ci ${{ matrix.target }} diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml new file mode 100644 index 0000000000..701b646fbf --- /dev/null +++ b/.github/workflows/docker-test.yml @@ -0,0 +1,14 @@ +name: Docker Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-18.04, ubuntu-20.04] + steps: + - uses: actions/checkout@v2 + - name: Run Docker Test (${{ matrix.os }}) + run: sudo make -C scripts/ci docker-test diff --git a/.github/workflows/fedora-asan-test.yml b/.github/workflows/fedora-asan-test.yml new file mode 100644 index 0000000000..44b0f16d6e --- /dev/null +++ b/.github/workflows/fedora-asan-test.yml @@ -0,0 +1,12 @@ +name: Fedora ASAN Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + - name: Run Fedora ASAN Test + run: sudo -E make -C scripts/ci fedora-asan diff --git a/.github/workflows/fedora-rawhide-test.yml b/.github/workflows/fedora-rawhide-test.yml new file mode 100644 index 0000000000..2ff8c1371c --- /dev/null +++ b/.github/workflows/fedora-rawhide-test.yml @@ -0,0 +1,12 @@ +name: Fedora Rawhide Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + - name: Run Fedora Rawhide Test + run: sudo -E make -C scripts/ci fedora-rawhide diff --git a/.github/workflows/gcov-test.yml b/.github/workflows/gcov-test.yml new file mode 100644 index 0000000000..f1b38e77e6 --- /dev/null +++ b/.github/workflows/gcov-test.yml @@ -0,0 +1,14 @@ +name: Coverage Tests + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + - name: Run Coverage Tests + run: sudo -E make -C scripts/ci local GCOV=1 + - name: Run Coverage Analysis + run: sudo -E make codecov diff --git a/.github/workflows/openj9-test.yml b/.github/workflows/openj9-test.yml new file mode 100644 index 0000000000..1d7a1eb6b7 --- /dev/null +++ b/.github/workflows/openj9-test.yml @@ -0,0 +1,11 @@ +name: OpenJ9 Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Run OpenJ9 Test + run: sudo make -C scripts/ci openj9-test diff --git a/.github/workflows/podman-test.yml b/.github/workflows/podman-test.yml new file mode 100644 index 0000000000..447cbf0b69 --- /dev/null +++ b/.github/workflows/podman-test.yml @@ -0,0 +1,11 @@ +name: Podman Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Run Podman Test + run: sudo make -C scripts/ci podman-test diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..beb6774e4b --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,27 @@ +name: Mark stale issues and pull requests + +# Please refer to https://github.com/actions/stale/blob/master/action.yml +# to see all config knobs of the stale action. + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'A friendly reminder that this issue had no activity for 30 days.' + stale-pr-message: 'A friendly reminder that this PR had no activity for 30 days.' + stale-issue-label: 'stale-issue' + stale-pr-label: 'stale-pr' + days-before-stale: 30 + days-before-close: 365 + remove-stale-when-updated: true + exempt-pr-labels: 'no-auto-close' + exempt-issue-labels: 'no-auto-close,new feature,enhancement' diff --git a/.github/workflows/stream-test.yml b/.github/workflows/stream-test.yml new file mode 100644 index 0000000000..ecdd81e0a3 --- /dev/null +++ b/.github/workflows/stream-test.yml @@ -0,0 +1,12 @@ +name: CRIU Image Streamer Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + - name: Run CRIU Image Streamer Test + run: sudo -E make -C scripts/ci local STREAM_TEST=1 diff --git a/.github/workflows/x86-64-clang-test.yml b/.github/workflows/x86-64-clang-test.yml new file mode 100644 index 0000000000..e6e84ef524 --- /dev/null +++ b/.github/workflows/x86-64-clang-test.yml @@ -0,0 +1,11 @@ +name: X86_64 CLANG Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Run X86_64 CLANG Test + run: sudo make -C scripts/ci x86_64 CLANG=1 diff --git a/.github/workflows/x86-64-gcc-test.yml b/.github/workflows/x86-64-gcc-test.yml new file mode 100644 index 0000000000..b8b81ef15b --- /dev/null +++ b/.github/workflows/x86-64-gcc-test.yml @@ -0,0 +1,11 @@ +name: X86_64 GCC Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Run X86_64 GCC Test + run: sudo make -C scripts/ci x86_64 diff --git a/.gitignore b/.gitignore index 23cd703be2..3b3da545cc 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ criu/pie/parasite-blob.h criu/protobuf-desc-gen.h lib/build/ lib/c/criu.pc -scripts/build/qemu-user-static/* lib/.crit-setup.files compel/include/asm include/common/asm diff --git a/.packit.yaml b/.packit.yaml new file mode 100644 index 0000000000..20b639800b --- /dev/null +++ b/.packit.yaml @@ -0,0 +1,32 @@ +# See the documentation for more information: +# https://packit.dev/docs/configuration/ + +specfile_path: criu.spec + +# add or remove files that should be synced +synced_files: + - criu.spec + - .packit.yaml + - criu-tmpfiles.conf + +# name in upstream package repository/registry (e.g. in PyPI) +upstream_package_name: criu +# downstream (Fedora) RPM package name +downstream_package_name: criu +actions: + post-upstream-clone: + - "wget https://src.fedoraproject.org/rpms/criu/raw/rawhide/f/criu.spec -O criu.spec" + - "wget https://src.fedoraproject.org/rpms/criu/raw/rawhide/f/criu-tmpfiles.conf -O criu-tmpfiles.conf" + +jobs: +- job: copr_build + trigger: pull_request + metadata: + targets: + - fedora-all-x86_64 + - fedora-all-aarch64 +- job: tests + trigger: pull_request + metadata: + targets: + - fedora-all-x86_64 diff --git a/.travis.yml b/.travis.yml index 08d0b2ed6d..94841b3f3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,10 @@ language: c os: linux dist: bionic -cache: ccache services: - docker -env: - - TR_ARCH=local - - TR_ARCH=local CLANG=1 - - TR_ARCH=local COMPAT_TEST=y - - TR_ARCH=local CLANG=1 COMPAT_TEST=y - - TR_ARCH=x86_64 - - TR_ARCH=x86_64 CLANG=1 - - TR_ARCH=openj9-test - - TR_ARCH=vagrant-fedora-no-vdso jobs: include: - - os: linux - arch: amd64 - env: TR_ARCH=local - dist: focal - os: linux arch: ppc64le env: TR_ARCH=local @@ -32,69 +18,18 @@ jobs: env: TR_ARCH=local dist: bionic - os: linux - arch: arm64 - env: TR_ARCH=local - dist: bionic - - os: linux - arch: arm64 - env: TR_ARCH=local CLANG=1 - dist: bionic - - os: linux - arch: arm64 - # This runs on aarch64 with 'setarch linux32' - env: TR_ARCH=armv7hf - dist: bionic - - os: linux - arch: arm64 - # This runs on aarch64 with 'setarch linux32' - env: TR_ARCH=armv7hf CLANG=1 - dist: bionic - - os: linux - arch: arm64 - env: TR_ARCH=fedora-rawhide - dist: bionic - - os: linux - arch: amd64 - env: TR_ARCH=fedora-rawhide - dist: bionic - - os: linux - arch: amd64 - env: TR_ARCH=podman-test - dist: bionic - - os: linux - arch: amd64 - env: TR_ARCH=docker-test - dist: bionic - - os: linux - arch: amd64 - env: TR_ARCH=docker-test DIST=xenial - # On xenial it should be possible to test overlayfs; - # broken on the latest bionic kernel - dist: xenial - - os: linux - arch: amd64 - env: TR_ARCH=alpine CLANG=1 - dist: bionic - - os: linux - arch: amd64 - env: TR_ARCH=alpine - dist: bionic - - os: linux - arch: amd64 - env: TR_ARCH=centos - dist: bionic + arch: arm64-graviton2 + env: TR_ARCH=local RUN_TESTS=1 + dist: focal + group: edge + virt: vm - os: linux - arch: amd64 - env: TR_ARCH=fedora-asan + arch: arm64-graviton2 + env: TR_ARCH=local CLANG=1 RUN_TESTS=1 + group: edge + virt: vm dist: bionic - - env: TR_ARCH=local STREAM_TEST=1 - allow_failures: - - env: TR_ARCH=docker-test - - env: TR_ARCH=docker-test DIST=xenial - - env: TR_ARCH=fedora-rawhide - - env: TR_ARCH=local GCOV=1 script: - - sudo make CCACHE=1 -C scripts/travis $TR_ARCH + - sudo make -C scripts/ci $TR_ARCH after_success: - - ccache -s - - make -C scripts/travis after_success + - make -C scripts/ci after_success diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d803d0208..96972296e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -126,6 +126,19 @@ If your change address an issue listed in GitHub, please use `Fixes:` tag with t The `Fixes:` tags should be put at the end of the detailed description. +Please add a prefix to your commit subject line describing the part of the +project your change is related to. This can be either the name of the file or +directory you changed, or just a general word. If your patch is touching +multiple components you may separate prefixes with "/"-es. Here are some good +examples of subject lines from git log: + +``` +criu-ns: Convert to python3 style print() syntax +compel: Calculate sh_addr if not provided by linker +style: Enforce kernel style -Wstrict-prototypes +rpc/libcriu: Add lsm-profile option +``` + You may refer to [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) article for recommendations for good commit message. @@ -203,7 +216,7 @@ commit message. To append such line to a commit you already made, use ``` From: Random J Developer - Subject: [PATCH] Short patch description + Subject: [PATCH] component: Short patch description Long patch description (could be skipped if patch is trivial enough) diff --git a/Makefile b/Makefile index c33494bec8..28c23cf673 100644 --- a/Makefile +++ b/Makefile @@ -147,7 +147,7 @@ HOSTCFLAGS += $(WARNINGS) $(DEFINES) -iquote include/ export AFLAGS CFLAGS USERCLFAGS HOSTCFLAGS # Default target -all: criu lib crit +all: flog criu lib crit .PHONY: all # @@ -202,7 +202,7 @@ criu-deps += include/common/asm # # Configure variables. export CONFIG_HEADER := include/common/config.h -ifeq ($(filter tags etags cscope clean mrproper,$(MAKECMDGOALS)),) +ifeq ($(filter tags etags cscope clean mrproper show-version help,$(MAKECMDGOALS)),) include Makefile.config else # To clean all files, enable make/build options here @@ -233,6 +233,15 @@ soccr/built-in.o: $(CONFIG_HEADER) .FORCE $(SOCCR_A): |soccr/built-in.o criu-deps += $(SOCCR_A) +#flog gets used by criu, build it earlier + +flogMakefile: ; +flog%: + $(Q) $(MAKE) $(build)=flog $@ +flog: + $(Q) $(MAKE) $(build)=flog all +.PHONY: flog + # # CRIU building done in own directory # with slightly different rules so we @@ -271,6 +280,7 @@ lib: crit clean mrproper: $(Q) $(MAKE) $(build)=images $@ + $(Q) $(MAKE) $(build)=flog $@ $(Q) $(MAKE) $(build)=criu $@ $(Q) $(MAKE) $(build)=soccr $@ $(Q) $(MAKE) $(build)=lib $@ @@ -339,6 +349,10 @@ criu-$(tar-name).tar.bz2: dist tar: criu-$(tar-name).tar.bz2 ; .PHONY: dist tar +show-version: + @echo $(head-name) +.PHONY: show-version + TAGS_FILES_REGEXP := . -name '*.[hcS]' ! -path './.*' \( ! -path './test/*' -o -path './test/zdtm/lib/*' \) tags: $(call msg-gen, $@) @@ -392,6 +406,7 @@ help: @echo ' cscope - Generate cscope database' @echo ' test - Run zdtm test-suite' @echo ' gcov - Make code coverage report' + @echo ' show-version - Show current CRIU version' .PHONY: help lint: @@ -401,7 +416,13 @@ lint: flake8 --config=scripts/flake8.cfg test/others/rpc/config_file.py flake8 --config=scripts/flake8.cfg lib/py/images/pb2dict.py shellcheck scripts/*.sh - shellcheck scripts/travis/*.sh scripts/travis/travis* scripts/travis/apt-install + shellcheck scripts/ci/*.sh scripts/ci/apt-install + shellcheck test/others/crit/*.sh + +codecov: SHELL := $(shell which bash) +codecov: + bash <(curl -s https://codecov.io/bash) +.PHONY: codecov include Makefile.install diff --git a/README.md b/README.md index d703638ec4..fd86b2c159 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,18 @@ -[![master](https://travis-ci.org/checkpoint-restore/criu.svg?branch=master)](https://travis-ci.org/checkpoint-restore/criu) -[![development](https://travis-ci.org/checkpoint-restore/criu.svg?branch=criu-dev)](https://travis-ci.org/checkpoint-restore/criu) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/55251ec7db28421da4481fc7c1cb0cee)](https://www.codacy.com/app/xemul/criu?utm_source=github.com&utm_medium=referral&utm_content=xemul/criu&utm_campaign=Badge_Grade) +![X86_64 GCC Test](https://github.com/checkpoint-restore/criu/workflows/X86_64%20GCC%20Test/badge.svg) +![Podman Test](https://github.com/checkpoint-restore/criu/workflows/Podman%20Test/badge.svg) +[![CircleCI](https://circleci.com/gh/checkpoint-restore/criu.svg?style=svg)](https://circleci.com/gh/checkpoint-restore/criu) +

## CRIU -- A project to implement checkpoint/restore functionality for Linux CRIU (stands for Checkpoint and Restore in Userspace) is a utility to checkpoint/restore Linux tasks. -Using this tool, you can freeze a running application (or part of it) and checkpoint +Using this tool, you can freeze a running application (or part of it) and checkpoint it to a hard drive as a collection of files. You can then use the files to restore and run the application from the point it was frozen at. The distinctive feature of the CRIU project is that it is mainly implemented in user space. There are some more projects -doing C/R for Linux, and so far CRIU [appears to be](https://criu.org/Comparison_to_other_CR_projects) +doing C/R for Linux, and so far CRIU [appears to be](https://criu.org/Comparison_to_other_CR_projects) the most feature-rich and up-to-date with the kernel. CRIU project is (almost) the never-ending story, because we have to always keep up with the @@ -20,8 +21,8 @@ looking for contributors of all kinds -- feedback, bug reports, testing, coding, Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) if you would like to get involved. The project [started](https://criu.org/History) as the way to do live migration for OpenVZ -Linux containers, but later grew to more sophisticated and flexible tool. It is currently -used by (integrated into) OpenVZ, LXC/LXD, Docker, and other software, project gets tremendous +Linux containers, but later grew to more sophisticated and flexible tool. It is currently +used by (integrated into) OpenVZ, LXC/LXD, Docker, and other software, project gets tremendous help from the community, and its packages are included into many Linux distributions. The project home is at http://criu.org. This wiki contains all the knowledge base for CRIU we have. @@ -31,13 +32,13 @@ Pages worth starting with are: - [Examples of more advanced usage](https://criu.org/Category:HOWTO) - Troubleshooting can be hard, some help can be found [here](https://criu.org/When_C/R_fails), [here](https://criu.org/What_cannot_be_checkpointed) and [here](https://criu.org/FAQ) -### Checkpoint and restore of simple loop process +### Checkpoint and restore of simple loop process [

](https://asciinema.org/a/232445) ## Advanced features As main usage for CRIU is live migration, there's a library for it called P.Haul. Also the -project exposes two cool core features as standalone libraries. These are libcompel for parasite code +project exposes two cool core features as standalone libraries. These are libcompel for parasite code injection and libsoccr for TCP connections checkpoint-restore. ### Live migration @@ -64,3 +65,6 @@ itself, and we have it available as the [libsoccr library](https://criu.org/Libs ## Licence The project is licensed under GPLv2 (though files sitting in the lib/ directory are LGPLv2.1). + +All files in the images/ directory are licensed under the Expat license (so-called MIT). +See the images/LICENSE file. diff --git a/compel/Makefile b/compel/Makefile index de9318c42d..b79aee6871 100644 --- a/compel/Makefile +++ b/compel/Makefile @@ -28,6 +28,10 @@ lib-y += src/lib/infect-util.o lib-y += src/lib/infect.o lib-y += src/lib/ptrace.o +ifeq ($(ARCH),x86) +lib-y += arch/$(ARCH)/src/lib/thread_area.o +endif + # handle_elf() has no support of ELF relocations on ARM (yet?) ifneq ($(filter arm aarch64,$(ARCH)),) CFLAGS += -DNO_RELOCS diff --git a/compel/arch/aarch64/src/lib/include/uapi/asm/infect-types.h b/compel/arch/aarch64/src/lib/include/uapi/asm/infect-types.h index 7a33baa8ef..b52f7a766c 100644 --- a/compel/arch/aarch64/src/lib/include/uapi/asm/infect-types.h +++ b/compel/arch/aarch64/src/lib/include/uapi/asm/infect-types.h @@ -18,6 +18,11 @@ typedef struct user_pt_regs user_regs_struct_t; typedef struct user_fpsimd_state user_fpregs_struct_t; +#define __compel_arch_fetch_thread_area(tid, th) 0 +#define compel_arch_fetch_thread_area(tctl) 0 +#define compel_arch_get_tls_task(ctl, tls) +#define compel_arch_get_tls_thread(tctl, tls) + #define REG_RES(r) ((uint64_t)(r).regs[0]) #define REG_IP(r) ((uint64_t)(r).pc) #define REG_SP(r) ((uint64_t)((r).sp)) diff --git a/compel/arch/aarch64/src/lib/infect.c b/compel/arch/aarch64/src/lib/infect.c index 4b5939022c..586eedac04 100644 --- a/compel/arch/aarch64/src/lib/infect.c +++ b/compel/arch/aarch64/src/lib/infect.c @@ -60,11 +60,12 @@ int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe, return 0; } -int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, +int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, + user_fpregs_struct_t *ext_regs, save_regs_t save, void *arg, __maybe_unused unsigned long flags) { + user_fpregs_struct_t tmp, *fpsimd = ext_regs ? ext_regs : &tmp; struct iovec iov; - user_fpregs_struct_t fpsimd; int ret; pr_info("Dumping GP/FPU registers for %d\n", pid); @@ -76,18 +77,33 @@ int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, goto err; } - iov.iov_base = &fpsimd; - iov.iov_len = sizeof(fpsimd); + iov.iov_base = fpsimd; + iov.iov_len = sizeof(*fpsimd); if ((ret = ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov))) { pr_perror("Failed to obtain FPU registers for %d", pid); goto err; } - ret = save(arg, regs, &fpsimd); + ret = save(arg, regs, fpsimd); err: return ret; } +int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs) +{ + struct iovec iov; + + pr_info("Restoring GP/FPU registers for %d\n", pid); + + iov.iov_base = ext_regs; + iov.iov_len = sizeof(*ext_regs); + if (ptrace(PTRACE_SETREGSET, pid, NT_PRFPREG, &iov)) { + pr_perror("Failed to set FPU registers for %d", pid); + return -1; + } + return 0; +} + int compel_syscall(struct parasite_ctl *ctl, int nr, long *ret, unsigned long arg1, unsigned long arg2, diff --git a/compel/arch/arm/src/lib/include/uapi/asm/infect-types.h b/compel/arch/arm/src/lib/include/uapi/asm/infect-types.h index 69222b251f..5e05ef53d4 100644 --- a/compel/arch/arm/src/lib/include/uapi/asm/infect-types.h +++ b/compel/arch/arm/src/lib/include/uapi/asm/infect-types.h @@ -17,6 +17,11 @@ typedef struct { long uregs[18]; } user_regs_struct_t; +#define __compel_arch_fetch_thread_area(tid, th) 0 +#define compel_arch_fetch_thread_area(tctl) 0 +#define compel_arch_get_tls_task(ctl, tls) +#define compel_arch_get_tls_thread(tctl, tls) + typedef struct user_vfp user_fpregs_struct_t; #define ARM_cpsr uregs[16] diff --git a/compel/arch/arm/src/lib/infect.c b/compel/arch/arm/src/lib/infect.c index 0053bef581..1ddb486541 100644 --- a/compel/arch/arm/src/lib/infect.c +++ b/compel/arch/arm/src/lib/infect.c @@ -4,6 +4,8 @@ #include #include #include +#include + #include "common/page.h" #include "uapi/compel/asm/infect-types.h" #include "log.h" @@ -67,15 +69,16 @@ int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe, } #define PTRACE_GETVFPREGS 27 -int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, +int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, + user_fpregs_struct_t *ext_regs, save_regs_t save, void *arg, __maybe_unused unsigned long flags) { - user_fpregs_struct_t vfp; + user_fpregs_struct_t tmp, *vfp = ext_regs ? ext_regs : &tmp; int ret = -1; pr_info("Dumping GP/FPU registers for %d\n", pid); - if (ptrace(PTRACE_GETVFPREGS, pid, NULL, &vfp)) { + if (ptrace(PTRACE_GETVFPREGS, pid, NULL, vfp)) { pr_perror("Can't obtain FPU registers for %d", pid); goto err; } @@ -91,17 +94,28 @@ int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, regs->ARM_pc -= 4; break; case -ERESTART_RESTARTBLOCK: - regs->ARM_r0 = __NR_restart_syscall; - regs->ARM_pc -= 4; + pr_warn("Will restore %d with interrupted system call\n", pid); + regs->ARM_r0 = -EINTR; break; } } - ret = save(arg, regs, &vfp); + ret = save(arg, regs, vfp); err: return ret; } +int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs) +{ + pr_info("Restoring GP/FPU registers for %d\n", pid); + + if (ptrace(PTRACE_SETVFPREGS, pid, NULL, ext_regs)) { + pr_perror("Can't set FPU registers for %d", pid); + return -1; + } + return 0; +} + int compel_syscall(struct parasite_ctl *ctl, int nr, long *ret, unsigned long arg1, unsigned long arg2, diff --git a/compel/arch/mips/src/lib/include/uapi/asm/infect-types.h b/compel/arch/mips/src/lib/include/uapi/asm/infect-types.h index 423880821e..07f3936a9c 100755 --- a/compel/arch/mips/src/lib/include/uapi/asm/infect-types.h +++ b/compel/arch/mips/src/lib/include/uapi/asm/infect-types.h @@ -54,6 +54,10 @@ static inline bool user_regs_native(user_regs_struct_t *pregs) return true; } +#define __compel_arch_fetch_thread_area(tid, th) 0 +#define compel_arch_fetch_thread_area(tctl) 0 +#define compel_arch_get_tls_task(ctl, tls) +#define compel_arch_get_tls_thread(tctl, tls) #define REG_RES(regs) ((regs).MIPS_v0) #define REG_IP(regs) ((regs).cp0_epc) diff --git a/compel/arch/mips/src/lib/infect.c b/compel/arch/mips/src/lib/infect.c index 521528f765..0c8067f88b 100755 --- a/compel/arch/mips/src/lib/infect.c +++ b/compel/arch/mips/src/lib/infect.c @@ -2,6 +2,8 @@ #include #include #include +#include + #include #include #include "errno.h" @@ -120,13 +122,16 @@ int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe, return 0; } -int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, +int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, + user_fpregs_struct_t *ext_regs, save_regs_t save, void *arg, __maybe_unused unsigned long flags) { - user_fpregs_struct_t xsave = { }, *xs = NULL; + user_fpregs_struct_t xsave = { }, *xs = ext_regs ? ext_regs : &xsave; int ret = -1; - if (ptrace(PTRACE_GETFPREGS, pid, NULL, &xsave)) { + pr_info("Dumping GP/FPU registers for %d\n", pid); + + if (ptrace(PTRACE_GETFPREGS, pid, NULL, xs)) { pr_perror("Can't obtain FPU registers for %d", pid); return ret; } @@ -142,19 +147,28 @@ int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, regs->cp0_epc -= 4; break; case ERESTART_RESTARTBLOCK: - regs->regs[2] = __NR_restart_syscall; - regs->regs[7] = regs->regs[26]; - regs->cp0_epc -= 4; + pr_warn("Will restore %d with interrupted system call\n", pid); + regs->regs[2] = -EINTR; break; } regs->regs[0] = 0; } - xs = &xsave; ret = save(arg, regs, xs); return ret; } +int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs) +{ + pr_info("Restoring GP/FPU registers for %d\n", pid); + + if (ptrace(PTRACE_SETFPREGS, pid, NULL, ext_regs)) { + pr_perror("Can't set FPU registers for %d", pid); + return -1; + } + return 0; +} + int compel_syscall(struct parasite_ctl *ctl, int nr, long *ret, unsigned long arg1, unsigned long arg2, diff --git a/compel/arch/ppc64/scripts/compel-pack.lds.S b/compel/arch/ppc64/scripts/compel-pack.lds.S index 572f1c42dd..f197fb9994 100644 --- a/compel/arch/ppc64/scripts/compel-pack.lds.S +++ b/compel/arch/ppc64/scripts/compel-pack.lds.S @@ -12,7 +12,7 @@ SECTIONS *(.compel.init) } - .data : { + .data : ALIGN(0x10000) { *(.data*) *(.bss*) } diff --git a/compel/arch/ppc64/src/lib/include/uapi/asm/infect-types.h b/compel/arch/ppc64/src/lib/include/uapi/asm/infect-types.h index 126fa2ea31..b65c942ee7 100644 --- a/compel/arch/ppc64/src/lib/include/uapi/asm/infect-types.h +++ b/compel/arch/ppc64/src/lib/include/uapi/asm/infect-types.h @@ -83,4 +83,9 @@ typedef struct { #define __NR(syscall, compat) ({ (void)compat; __NR_##syscall; }) +#define __compel_arch_fetch_thread_area(tid, th) 0 +#define compel_arch_fetch_thread_area(tctl) 0 +#define compel_arch_get_tls_task(ctl, tls) +#define compel_arch_get_tls_thread(tctl, tls) + #endif /* UAPI_COMPEL_ASM_TYPES_H__ */ diff --git a/compel/arch/ppc64/src/lib/infect.c b/compel/arch/ppc64/src/lib/infect.c index 637acd46d4..5797fb16d6 100644 --- a/compel/arch/ppc64/src/lib/infect.c +++ b/compel/arch/ppc64/src/lib/infect.c @@ -330,8 +330,8 @@ static int __get_task_regs(pid_t pid, user_regs_struct_t *regs, regs->nip -= 4; break; case ERESTART_RESTARTBLOCK: - regs->gpr[0] = __NR_restart_syscall; - regs->nip -= 4; + pr_warn("Will restore %d with interrupted system call\n", pid); + regs->gpr[3] = EINTR; break; } } @@ -372,17 +372,46 @@ static int __get_task_regs(pid_t pid, user_regs_struct_t *regs, return 0; } -int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, +int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, + user_fpregs_struct_t *ext_regs, save_regs_t save, void *arg, __maybe_unused unsigned long flags) { - user_fpregs_struct_t fpregs; + user_fpregs_struct_t tmp, *fpregs = ext_regs ? ext_regs : &tmp; int ret; - ret = __get_task_regs(pid, regs, &fpregs); + ret = __get_task_regs(pid, regs, fpregs); if (ret) return ret; - return save(arg, regs, &fpregs); + return save(arg, regs, fpregs); +} + +int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs) +{ + int ret = 0; + + pr_info("Restoring GP/FPU registers for %d\n", pid); + + /* XXX: should restore TM registers somehow? */ + if (ext_regs->flags & USER_FPREGS_FL_FP) { + if (ptrace(PTRACE_SETFPREGS, pid, 0, (void *)&ext_regs->fpregs) < 0) { + pr_perror("Couldn't set floating-point registers"); + ret = -1; + } + } + + if (ext_regs->flags & USER_FPREGS_FL_ALTIVEC) { + if (ptrace(PTRACE_SETVRREGS, pid, 0, (void*)&ext_regs->vrregs) < 0) { + pr_perror("Couldn't set Altivec registers"); + ret = -1; + } + if (ptrace(PTRACE_SETVSRREGS, pid, 0, (void*)ext_regs->vsxregs) < 0) { + pr_perror("Couldn't set VSX registers"); + ret = -1; + } + } + + return ret; } int compel_syscall(struct parasite_ctl *ctl, int nr, long *ret, diff --git a/compel/arch/s390/scripts/compel-pack.lds.S b/compel/arch/s390/scripts/compel-pack.lds.S index 2571ac852c..a821189833 100644 --- a/compel/arch/s390/scripts/compel-pack.lds.S +++ b/compel/arch/s390/scripts/compel-pack.lds.S @@ -12,7 +12,7 @@ SECTIONS *(.compel.init) } - .data : { + .data : ALIGN(0x1000) { *(.data*) *(.bss*) } diff --git a/compel/arch/s390/src/lib/include/uapi/asm/infect-types.h b/compel/arch/s390/src/lib/include/uapi/asm/infect-types.h index 8171d33951..807be5a9a8 100644 --- a/compel/arch/s390/src/lib/include/uapi/asm/infect-types.h +++ b/compel/arch/s390/src/lib/include/uapi/asm/infect-types.h @@ -84,4 +84,9 @@ struct mmap_arg_struct { unsigned long offset; }; +#define __compel_arch_fetch_thread_area(tid, th) 0 +#define compel_arch_fetch_thread_area(tctl) 0 +#define compel_arch_get_tls_task(ctl, tls) +#define compel_arch_get_tls_thread(tctl, tls) + #endif /* UAPI_COMPEL_ASM_TYPES_H__ */ diff --git a/compel/arch/s390/src/lib/infect.c b/compel/arch/s390/src/lib/infect.c index 5a4675449d..9fad614684 100644 --- a/compel/arch/s390/src/lib/infect.c +++ b/compel/arch/s390/src/lib/infect.c @@ -310,31 +310,32 @@ static int s390_disable_ri_bit(pid_t pid, user_regs_struct_t *regs) /* * Prepare task registers for restart */ -int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, +int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, + user_fpregs_struct_t *ext_regs, save_regs_t save, void *arg, __maybe_unused unsigned long flags) { - user_fpregs_struct_t fpregs; + user_fpregs_struct_t tmp, *fpregs = ext_regs ? ext_regs : &tmp; struct iovec iov; int rewind; - print_user_regs_struct("get_task_regs", pid, regs); + print_user_regs_struct("compel_get_task_regs", pid, regs); - memset(&fpregs, 0, sizeof(fpregs)); - iov.iov_base = &fpregs.prfpreg; - iov.iov_len = sizeof(fpregs.prfpreg); + memset(fpregs, 0, sizeof(*fpregs)); + iov.iov_base = &fpregs->prfpreg; + iov.iov_len = sizeof(fpregs->prfpreg); if (ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov) < 0) { pr_perror("Couldn't get floating-point registers"); return -1; } - if (get_vx_regs(pid, &fpregs)) { + if (get_vx_regs(pid, fpregs)) { pr_perror("Couldn't get vector registers"); return -1; } - if (get_gs_cb(pid, &fpregs)) { + if (get_gs_cb(pid, fpregs)) { pr_perror("Couldn't get guarded-storage"); return -1; } - if (get_ri_cb(pid, &fpregs)) { + if (get_ri_cb(pid, fpregs)) { pr_perror("Couldn't get runtime-instrumentation"); return -1; } @@ -343,10 +344,10 @@ int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, * before we execute parasite code. Otherwise parasite operations * would be recorded. */ - if (fpregs.flags & USER_RI_ON) + if (fpregs->flags & USER_RI_ON) s390_disable_ri_bit(pid, regs); - print_user_fpregs_struct("get_task_regs", pid, &fpregs); + print_user_fpregs_struct("compel_get_task_regs", pid, fpregs); /* Check for system call restarting. */ if (regs->system_call) { rewind = regs->system_call >> 16; @@ -366,7 +367,62 @@ int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, } } /* Call save_task_regs() */ - return save(arg, regs, &fpregs); + return save(arg, regs, fpregs); +} + +int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs) +{ + struct iovec iov; + int ret = 0; + + iov.iov_base = &ext_regs->prfpreg; + iov.iov_len = sizeof(ext_regs->prfpreg); + if (ptrace(PTRACE_SETREGSET, pid, NT_PRFPREG, &iov) < 0) { + pr_perror("Couldn't set floating-point registers"); + ret = -1; + } + + if (ext_regs->flags & USER_FPREGS_VXRS) { + iov.iov_base = &ext_regs->vxrs_low; + iov.iov_len = sizeof(ext_regs->vxrs_low); + if (ptrace(PTRACE_SETREGSET, pid, NT_S390_VXRS_LOW, &iov) < 0) { + pr_perror("Couldn't set VXRS_LOW\n"); + ret = -1; + } + + iov.iov_base = &ext_regs->vxrs_high; + iov.iov_len = sizeof(ext_regs->vxrs_high); + if (ptrace(PTRACE_SETREGSET, pid, NT_S390_VXRS_HIGH, &iov) < 0) { + pr_perror("Couldn't set VXRS_HIGH\n"); + ret = -1; + } + } + + if (ext_regs->flags & USER_GS_CB) { + iov.iov_base = &ext_regs->gs_cb; + iov.iov_len = sizeof(ext_regs->gs_cb); + if (ptrace(PTRACE_SETREGSET, pid, NT_S390_GS_CB, &iov) < 0) { + pr_perror("Couldn't set GS_CB\n"); + ret = -1; + } + iov.iov_base = &ext_regs->gs_bc; + iov.iov_len = sizeof(ext_regs->gs_bc); + if (ptrace(PTRACE_SETREGSET, pid, NT_S390_GS_BC, &iov) < 0) { + pr_perror("Couldn't set GS_BC\n"); + ret = -1; + } + } + + if (ext_regs->flags & USER_RI_CB) { + iov.iov_base = &ext_regs->ri_cb; + iov.iov_len = sizeof(ext_regs->ri_cb); + if (ptrace(PTRACE_SETREGSET, pid, NT_S390_RI_CB, &iov) < 0) { + pr_perror("Couldn't set RI_CB\n"); + ret = -1; + } + } + + return ret; } /* @@ -661,14 +717,6 @@ unsigned long compel_task_size(void) /* * Get task registers (overwrites weak function) - * - * We don't store floating point and vector registers here because we - * assue that compel/pie code does not change them. - * - * For verification issue: - * - * $ objdump -S criu/pie/parasite.built-in.bin.o | grep "%f" - * $ objdump -S criu/pie/restorer.built-in.bin.o | grep "%f" */ int ptrace_get_regs(int pid, user_regs_struct_t *regs) { diff --git a/compel/arch/x86/plugins/std/syscalls/Makefile.syscalls b/compel/arch/x86/plugins/std/syscalls/Makefile.syscalls index 4ba4b56c84..62c25f3e03 100644 --- a/compel/arch/x86/plugins/std/syscalls/Makefile.syscalls +++ b/compel/arch/x86/plugins/std/syscalls/Makefile.syscalls @@ -39,6 +39,10 @@ $(sys-proto): $(sys-def) $(sys-proto-types) $(Q) echo "/* Autogenerated, don't edit */" > $$@ $(Q) echo "#ifndef ASM_SYSCALL_PROTO_H_$(1)__" >> $$@ $(Q) echo "#define ASM_SYSCALL_PROTO_H_$(1)__" >> $$@ + $(Q) echo "/* musl defines loff_t as off_t */" >> $$@ + $(Q) echo '#ifndef loff_t' >> $$@ + $(Q) echo '#define loff_t off_t' >> $$@ + $(Q) echo '#endif' >> $$@ $(Q) echo '#include ' >> $$@ $(Q) echo '#include ' >> $$@ ifeq ($(1),32) @@ -71,6 +75,10 @@ $(sys-codes-generic): $(PLUGIN_ARCH_DIR)/std/syscalls/syscall_32.tbl $(sys-proto $(Q) echo "/* Autogenerated, don't edit */" > $@ $(Q) echo "#ifndef __ASM_CR_SYSCALL_CODES_H__" >> $@ $(Q) echo "#define __ASM_CR_SYSCALL_CODES_H__" >> $@ + $(Q) echo "/* musl defines loff_t as off_t */" >> $@ + $(Q) echo '#ifndef loff_t' >> $@ + $(Q) echo '#define loff_t off_t' >> $@ + $(Q) echo '#endif' >> $@ $(Q) echo '#include ' >> $@ $(Q) cat $< | awk '/^__NR/{NR32=$$1; \ sub("^__NR", "__NR32", NR32); \ diff --git a/compel/arch/x86/scripts/compel-pack.lds.S b/compel/arch/x86/scripts/compel-pack.lds.S index 68ddb36226..44e705e299 100644 --- a/compel/arch/x86/scripts/compel-pack.lds.S +++ b/compel/arch/x86/scripts/compel-pack.lds.S @@ -13,7 +13,7 @@ SECTIONS *(.compel.init) } - .data : { + .data : ALIGN(0x1000) { *(.data*) *(.bss*) } diff --git a/compel/arch/x86/src/lib/cpu.c b/compel/arch/x86/src/lib/cpu.c index 6175121673..c96f013534 100644 --- a/compel/arch/x86/src/lib/cpu.c +++ b/compel/arch/x86/src/lib/cpu.c @@ -125,7 +125,7 @@ static int compel_fpuid(compel_cpuinfo_t *c) c->xfeatures_mask &= ~(1 << i); } - c->xfeatures_mask &= XCNTXT_MASK; + c->xfeatures_mask &= XFEATURE_MASK_USER; c->xfeatures_mask &= ~XFEATURE_MASK_SUPERVISOR; /* diff --git a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h index 4ff531fb9c..8985ad7f62 100644 --- a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h +++ b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h @@ -76,7 +76,7 @@ enum xfeature { #define XFEATURE_MASK_SUPERVISOR (XFEATURE_MASK_PT | XFEATURE_HDC) /* All currently supported features */ -#define XCNTXT_MASK \ +#define XFEATURE_MASK_USER \ (XFEATURE_MASK_FP | XFEATURE_MASK_SSE | \ XFEATURE_MASK_YMM | XFEATURE_MASK_OPMASK | \ XFEATURE_MASK_ZMM_Hi256 | XFEATURE_MASK_Hi16_ZMM | \ @@ -232,7 +232,7 @@ struct pkru_state { * can vary quite a bit between CPUs. * * - * One page should be enough for the whole xsave state. + * One page should be enough for the whole xsave state ;-) */ #define EXTENDED_STATE_AREA_SIZE (4096 - sizeof(struct i387_fxsave_struct) - sizeof(struct xsave_hdr_struct)) diff --git a/compel/arch/x86/src/lib/include/uapi/asm/infect-types.h b/compel/arch/x86/src/lib/include/uapi/asm/infect-types.h index e6d394989b..470a1b0fd2 100644 --- a/compel/arch/x86/src/lib/include/uapi/asm/infect-types.h +++ b/compel/arch/x86/src/lib/include/uapi/asm/infect-types.h @@ -9,6 +9,29 @@ #define SIGMAX 64 #define SIGMAX_OLD 31 +#define ARCH_HAS_PTRACE_GET_THREAD_AREA + +/* + * Linux preserves three TLS segments in GDT. + * Offsets in GDT differ between 32-bit and 64-bit machines. + * For 64-bit x86 those GDT offsets are the same + * for native and compat tasks. + */ +#define GDT_ENTRY_TLS_MIN 12 +#define GDT_ENTRY_TLS_MAX 14 +#define GDT_ENTRY_TLS_NUM 3 +typedef struct { + user_desc_t desc[GDT_ENTRY_TLS_NUM]; +} tls_t; + +struct thread_ctx; +struct parasite_ctl; +struct parasite_thread_ctl; +extern int __compel_arch_fetch_thread_area(int tid, struct thread_ctx *th); +extern int compel_arch_fetch_thread_area(struct parasite_thread_ctl *tctl); +extern void compel_arch_get_tls_thread(struct parasite_thread_ctl *tctl, tls_t *out); +extern void compel_arch_get_tls_task(struct parasite_ctl *ctl, tls_t *out); + typedef struct { uint64_t r15; uint64_t r14; diff --git a/compel/arch/x86/src/lib/infect.c b/compel/arch/x86/src/lib/infect.c index 9c4abb60c2..566238d2d2 100644 --- a/compel/arch/x86/src/lib/infect.c +++ b/compel/arch/x86/src/lib/infect.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include @@ -258,10 +260,85 @@ static int get_task_fpregs(pid_t pid, user_fpregs_struct_t *xsave) return 0; } -int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, +/* See arch/x86/kernel/fpu/xstate.c */ +static void validate_random_xstate(struct xsave_struct *xsave) +{ + struct xsave_hdr_struct *hdr = &xsave->xsave_hdr; + unsigned int i; + + /* No unknown or supervisor features may be set */ + hdr->xstate_bv &= XFEATURE_MASK_USER; + hdr->xstate_bv &= ~XFEATURE_MASK_SUPERVISOR; + + for (i = 0; i < XFEATURE_MAX; i++) { + if (!compel_fpu_has_feature(i)) + hdr->xstate_bv &= ~(1 << i); + } + + /* Userspace must use the uncompacted format */ + hdr->xcomp_bv = 0; + + /* + * If 'reserved' is shrunken to add a new field, make sure to validate + * that new field here! + */ + BUILD_BUG_ON(sizeof(hdr->reserved) != 48); + + /* No reserved bits may be set */ + memset(&hdr->reserved, 0, sizeof(hdr->reserved)); +} + +/* + * TODO: Put fault-injection under CONFIG_* and move + * extended regset corruption to generic code + */ +static int corrupt_extregs(pid_t pid) +{ + bool use_xsave = compel_cpu_has_feature(X86_FEATURE_OSXSAVE); + user_fpregs_struct_t ext_regs; + int *rand_to = (int *)&ext_regs; + unsigned int seed; + size_t i; + + seed = time(NULL); + for (i = 0; i < sizeof(ext_regs) / sizeof(int); i++) + *rand_to++ = rand_r(&seed); + + /* + * Error log-level as: + * - not intended to be used outside of testing, + * - zdtm.py will grep it auto-magically from logs + * (and the seed will be known from an automatical testing) + */ + pr_err("Corrupting %s for %d, seed %u\n", + use_xsave ? "xsave" : "fpuregs", pid, seed); + + if (!use_xsave) { + if (ptrace(PTRACE_SETFPREGS, pid, NULL, &ext_regs)) { + pr_perror("Can't set FPU registers for %d", pid); + return -1; + } + } else { + struct iovec iov; + + validate_random_xstate((void *)&ext_regs); + + iov.iov_base = &ext_regs; + iov.iov_len = sizeof(ext_regs); + + if (ptrace(PTRACE_SETREGSET, pid, (unsigned int)NT_X86_XSTATE, &iov) < 0) { + pr_perror("Can't set xstate for %d", pid); + return -1; + } + } + return 0; +} + +int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, + user_fpregs_struct_t *ext_regs, save_regs_t save, void *arg, unsigned long flags) { - user_fpregs_struct_t xsave = { }, *xs = NULL; + user_fpregs_struct_t xsave = { }, *xs = ext_regs ? ext_regs : &xsave; int ret = -1; pr_info("Dumping general registers for %d in %s mode\n", pid, @@ -295,30 +372,57 @@ int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, pr_info("Dumping GP/FPU registers for %d\n", pid); if (!compel_cpu_has_feature(X86_FEATURE_OSXSAVE)) { - ret = get_task_fpregs(pid, &xsave); + ret = get_task_fpregs(pid, xs); } else if (unlikely(flags & INFECT_X86_PTRACE_MXCSR_BUG)) { /* * get_task_fpregs() will fill FP state, * get_task_xsave() will overwrite rightly sse/mmx/etc */ pr_warn("Skylake xsave fpu bug workaround used\n"); - ret = get_task_fpregs(pid, &xsave); + ret = get_task_fpregs(pid, xs); if (!ret) - ret = get_task_xsave(pid, &xsave); + ret = get_task_xsave(pid, xs); } else { - ret = get_task_xsave(pid, &xsave); + ret = get_task_xsave(pid, xs); } + if (!ret && unlikely(flags & INFECT_CORRUPT_EXTREGS)) + ret = corrupt_extregs(pid); + if (ret) goto err; - xs = &xsave; out: ret = save(arg, regs, xs); err: return ret; } +int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs) +{ + struct iovec iov; + + pr_info("Restoring GP/FPU registers for %d\n", pid); + + if (!compel_cpu_has_feature(X86_FEATURE_OSXSAVE)) { + if (ptrace(PTRACE_SETFPREGS, pid, NULL, ext_regs)) { + pr_perror("Can't set FPU registers for %d", pid); + return -1; + } + return 0; + } + + iov.iov_base = ext_regs; + iov.iov_len = sizeof(*ext_regs); + + if (ptrace(PTRACE_SETREGSET, pid, (unsigned int)NT_X86_XSTATE, &iov) < 0) { + pr_perror("Can't set FPU registers for %d", pid); + return -1; + } + + return 0; +} + int compel_syscall(struct parasite_ctl *ctl, int nr, long *ret, unsigned long arg1, unsigned long arg2, diff --git a/compel/arch/x86/src/lib/thread_area.c b/compel/arch/x86/src/lib/thread_area.c new file mode 100644 index 0000000000..f581496a78 --- /dev/null +++ b/compel/arch/x86/src/lib/thread_area.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include "log.h" +#include "asm/infect-types.h" +#include "infect.h" +#include "infect-priv.h" + +#ifndef PTRACE_GET_THREAD_AREA +# define PTRACE_GET_THREAD_AREA 25 +#endif + +/* + * For 64-bit applications, TLS (fs_base for Glibc) is in MSR, + * which are dumped with the help of ptrace() and restored with + * arch_prctl(ARCH_SET_FS/ARCH_SET_GS). + * + * But SET_FS_BASE will update GDT if base pointer fits in 4 bytes. + * Otherwise it will set only MSR, which allows for mixed 64/32-bit + * code to use: 2 MSRs as TLS base _and_ 3 GDT entries. + * Having in sum 5 TLS pointers, 3 of which are four bytes and + * other two eight bytes: + * struct thread_struct { + * struct desc_struct tls_array[3]; + * ... + * #ifdef CONFIG_X86_64 + * unsigned long fsbase; + * unsigned long gsbase; + * #endif + * ... + * }; + * + * Most x86_64 applications don't use GDT, but mixed code (i.e. Wine) + * can use it. Be pessimistic and dump it for 64-bit applications too. + */ +int __compel_arch_fetch_thread_area(int tid, struct thread_ctx *th) +{ + bool native_mode = user_regs_native(&th->regs); + tls_t *ptls = &th->tls; + int err, i; + + /* Initialise as not present by default */ + for (i = 0; i < GDT_ENTRY_TLS_NUM; i++) { + user_desc_t *d = &ptls->desc[i]; + + memset(d, 0, sizeof(user_desc_t)); + d->seg_not_present = 1; + d->entry_number = GDT_ENTRY_TLS_MIN + i; + } + + for (i = 0; i < GDT_ENTRY_TLS_NUM; i++) + { + user_desc_t *d = &ptls->desc[i]; + + err = ptrace(PTRACE_GET_THREAD_AREA, tid, + GDT_ENTRY_TLS_MIN + i, d); + /* + * Ignoring absent syscall on !CONFIG_IA32_EMULATION + * where such mixed code can't run. + * XXX: Add compile CONFIG_X86_IGNORE_64BIT_TLS + * (for x86_64 systems with CONFIG_IA32_EMULATION) + */ + if (err == -EIO && native_mode) + return 0; + if (err) { + pr_perror("get_thread_area failed for %d\n", tid); + return err; + } + } + + return 0; +} + +int compel_arch_fetch_thread_area(struct parasite_thread_ctl *tctl) +{ + return __compel_arch_fetch_thread_area(tctl->tid, &tctl->th); +} + +void compel_arch_get_tls_task(struct parasite_ctl *ctl, tls_t *out) +{ + memcpy(out, &ctl->orig.tls, sizeof(tls_t)); +} + +void compel_arch_get_tls_thread(struct parasite_thread_ctl *tctl, tls_t *out) +{ + memcpy(out, &tctl->th.tls, sizeof(tls_t)); +} diff --git a/compel/include/infect-priv.h b/compel/include/infect-priv.h index 8e1f990d5e..d4f19307d3 100644 --- a/compel/include/infect-priv.h +++ b/compel/include/infect-priv.h @@ -8,6 +8,10 @@ struct thread_ctx { k_rtsigset_t sigmask; user_regs_struct_t regs; +#ifdef ARCH_HAS_PTRACE_GET_THREAD_AREA + tls_t tls; +#endif + user_fpregs_struct_t ext_regs; }; /* parasite control block */ @@ -58,8 +62,18 @@ extern void *remote_mmap(struct parasite_ctl *ctl, void *addr, size_t length, int prot, int flags, int fd, off_t offset); extern bool arch_can_dump_task(struct parasite_ctl *ctl); -extern int get_task_regs(pid_t pid, user_regs_struct_t *regs, save_regs_t save, +/* + * @regs: general purpose registers + * @ext_regs: extended register set (fpu/mmx/sse/etc) + * for task that is NULL, restored by sigframe on rt_sigreturn() + * @save: callback to dump all info + * @flags: see INFECT_* in infect_ctx::flags + * @pid: mystery + */ +extern int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, + user_fpregs_struct_t *ext_regs, save_regs_t save, void *arg, unsigned long flags); +extern int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs); extern int arch_fetch_sas(struct parasite_ctl *ctl, struct rt_sigframe *s); extern int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe, user_regs_struct_t *regs, diff --git a/compel/include/uapi/infect-rpc.h b/compel/include/uapi/infect-rpc.h index 180dedf1f6..5fcdb56ff8 100644 --- a/compel/include/uapi/infect-rpc.h +++ b/compel/include/uapi/infect-rpc.h @@ -5,6 +5,8 @@ #include #include +#include + struct parasite_ctl; extern int __must_check compel_rpc_sync(unsigned int cmd, struct parasite_ctl *ctl); extern int __must_check compel_rpc_call(unsigned int cmd, struct parasite_ctl *ctl); diff --git a/compel/include/uapi/infect.h b/compel/include/uapi/infect.h index 257658a70d..6cf3daf467 100644 --- a/compel/include/uapi/infect.h +++ b/compel/include/uapi/infect.h @@ -136,6 +136,8 @@ extern struct infect_ctx *compel_infect_ctx(struct parasite_ctl *); #define INFECT_COMPATIBLE (1UL << 3) /* Workaround for ptrace bug on Skylake CPUs with kernels older than v4.14 */ #define INFECT_X86_PTRACE_MXCSR_BUG (1UL << 4) +/* After infecting - corrupt extended registers (fault-injection) */ +#define INFECT_CORRUPT_EXTREGS (1UL << 5) /* * There are several ways to describe a blob to compel @@ -155,6 +157,7 @@ struct parasite_blob_desc { unsigned long args_ptr_off; unsigned long got_off; unsigned long args_off; + unsigned long data_off; compel_reloc_t *relocs; unsigned int nr_relocs; } hdr; diff --git a/compel/plugins/std/log.c b/compel/plugins/std/log.c index f9be432ea8..0053482467 100644 --- a/compel/plugins/std/log.c +++ b/compel/plugins/std/log.c @@ -225,6 +225,22 @@ static void print_num_l(long num, struct simple_buf *b) print_string(s, b); } +static void print_num_u(unsigned long num, struct simple_buf *b) +{ + char buf[22], *s; + + buf[21] = '\0'; + s = &buf[21]; + + do { + s--; + *s = (num % 10) + '0'; + num /= 10; + } while (num > 0); + + print_string(s, b); +} + static void hexdigit(unsigned int v, char *to, char **z) { *to = "0123456789abcdef"[v & 0xf]; @@ -329,10 +345,17 @@ static void sbuf_printf(struct simple_buf *b, const char *format, va_list args) case 'p': print_hex_l((unsigned long)va_arg(args, void *), b); break; + case 'u': + if (along) + print_num_u(va_arg(args, unsigned long), b); + else + print_num_u(va_arg(args, unsigned), b); + break; default: - print_string("UNKNOWN FORMAT ", b); + print_string("\nError: Unknown printf format %", b); sbuf_putc(b, *s); - break; + sbuf_putc(b, '\n'); + return; } s++; } diff --git a/compel/src/lib/handle-elf.c b/compel/src/lib/handle-elf.c index 2319170ae0..2d643ce438 100644 --- a/compel/src/lib/handle-elf.c +++ b/compel/src/lib/handle-elf.c @@ -155,6 +155,7 @@ int __handle_elf(void *mem, size_t size) int64_t toc_offset = 0; #endif int ret = -EINVAL; + unsigned long data_off = 0; pr_debug("Header\n"); pr_debug("------------\n"); @@ -211,13 +212,11 @@ int __handle_elf(void *mem, size_t size) if (sh->sh_addralign > 0 && k % sh->sh_addralign != 0) { k += sh->sh_addralign - k % sh->sh_addralign; } - if (sh->sh_addr && sh->sh_addr != k) { - pr_err("Unexpected precalculated address of section (section %s addr 0x%lx expected 0x%lx)\n", - &secstrings[sh->sh_name], - (unsigned long) sh->sh_addr, - (unsigned long) k); - goto err; - } + if (sh->sh_addr && sh->sh_addr != k) + pr_info("Overriding unexpected precalculated address of section (section %s addr 0x%lx expected 0x%lx)\n", + &secstrings[sh->sh_name], + (unsigned long) sh->sh_addr, + (unsigned long) k); sh->sh_addr = k; k += sh->sh_size; } @@ -698,6 +697,9 @@ int __handle_elf(void *mem, size_t size) pr_out("\n\t"); pr_out("0x%02x,", shdata[j]); } + + if (!strcmp(&secstrings[sh->sh_name], ".data")) + data_off = sh->sh_addr; } pr_out("};\n"); pr_out("\n"); @@ -722,6 +724,7 @@ int __handle_elf(void *mem, size_t size) pr_out("\tpbd->hdr.args_ptr_off = %s_sym__export_parasite_service_args_ptr;\n", opts.prefix); pr_out("\tpbd->hdr.got_off = round_up(pbd->hdr.bsize, sizeof(long));\n"); pr_out("\tpbd->hdr.args_off = pbd->hdr.got_off + %zd*sizeof(long);\n", nr_gotpcrel); + pr_out("\tpbd->hdr.data_off = %#lx;\n", data_off); pr_out("\tpbd->hdr.relocs = %s_relocs;\n", opts.prefix); pr_out("\tpbd->hdr.nr_relocs = " "sizeof(%s_relocs) / sizeof(%s_relocs[0]);\n", diff --git a/compel/src/lib/infect.c b/compel/src/lib/infect.c index 38846c21f1..1bb07ace54 100644 --- a/compel/src/lib/infect.c +++ b/compel/src/lib/infect.c @@ -105,7 +105,7 @@ static int parse_pid_status(int pid, struct seize_task_status *ss, void *data) int compel_stop_task(int pid) { int ret; - struct seize_task_status ss; + struct seize_task_status ss = {}; ret = compel_interrupt_task(pid); if (ret == 0) @@ -475,7 +475,8 @@ static int parasite_run(pid_t pid, int cmd, unsigned long ip, void *stack, return -1; } -static int restore_thread_ctx(int pid, struct thread_ctx *ctx) +static int restore_thread_ctx(int pid, struct thread_ctx *ctx, + bool restore_ext_regs) { int ret = 0; @@ -483,6 +484,10 @@ static int restore_thread_ctx(int pid, struct thread_ctx *ctx) pr_perror("Can't restore registers (pid: %d)", pid); ret = -1; } + + if (restore_ext_regs && compel_set_task_ext_regs(pid, &ctx->ext_regs)) + ret = -1; + if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &ctx->sigmask)) { pr_perror("Can't block signals"); ret = -1; @@ -491,11 +496,11 @@ static int restore_thread_ctx(int pid, struct thread_ctx *ctx) return ret; } - /* we run at @regs->ip */ static int parasite_trap(struct parasite_ctl *ctl, pid_t pid, user_regs_struct_t *regs, - struct thread_ctx *octx) + struct thread_ctx *octx, + bool may_use_extended_regs) { siginfo_t siginfo; int status; @@ -540,7 +545,7 @@ static int parasite_trap(struct parasite_ctl *ctl, pid_t pid, */ ret = 0; err: - if (restore_thread_ctx(pid, octx)) + if (restore_thread_ctx(pid, octx, may_use_extended_regs)) ret = -1; return ret; @@ -567,7 +572,7 @@ int compel_execute_syscall(struct parasite_ctl *ctl, err = parasite_run(pid, PTRACE_CONT, ctl->ictx.syscall_ip, 0, regs, &ctl->orig); if (!err) - err = parasite_trap(ctl, pid, regs, &ctl->orig); + err = parasite_trap(ctl, pid, regs, &ctl->orig, false); if (ptrace_poke_area(pid, (void *)code_orig, (void *)ctl->ictx.syscall_ip, sizeof(code_orig))) { @@ -585,7 +590,7 @@ int compel_run_at(struct parasite_ctl *ctl, unsigned long ip, user_regs_struct_t ret = parasite_run(ctl->rpid, PTRACE_CONT, ip, 0, ®s, &ctl->orig); if (!ret) - ret = parasite_trap(ctl, ctl->rpid, ret_regs ? ret_regs : ®s, &ctl->orig); + ret = parasite_trap(ctl, ctl->rpid, ret_regs ? ret_regs : ®s, &ctl->orig, false); return ret; } @@ -671,16 +676,21 @@ static int parasite_start_daemon(struct parasite_ctl *ctl) /* * Get task registers before going daemon, since the - * compel_get_task_regs needs to call ptrace on _stopped_ task, + * compel_get_task_regs() needs to call ptrace on _stopped_ task, * while in daemon it is not such. */ - if (get_task_regs(pid, &ctl->orig.regs, ictx->save_regs, + if (compel_get_task_regs(pid, &ctl->orig.regs, NULL, ictx->save_regs, ictx->regs_arg, ictx->flags)) { pr_err("Can't obtain regs for thread %d\n", pid); return -1; } + if (__compel_arch_fetch_thread_area(pid, &ctl->orig)) { + pr_err("Can't get thread area of %d\n", pid); + return -1; + } + if (ictx->make_sigframe(ictx->regs_arg, ctl->sigframe, ctl->rsigframe, &ctl->orig.sigmask)) return -1; @@ -690,12 +700,11 @@ static int parasite_start_daemon(struct parasite_ctl *ctl) return 0; } -static int parasite_mmap_exchange(struct parasite_ctl *ctl, unsigned long size) +static int parasite_mmap_exchange(struct parasite_ctl *ctl, unsigned long size, int remote_prot) { int fd; - ctl->remote_map = remote_mmap(ctl, NULL, size, - PROT_READ | PROT_WRITE | PROT_EXEC, + ctl->remote_map = remote_mmap(ctl, NULL, size, remote_prot, MAP_ANONYMOUS | MAP_SHARED, -1, 0); if (!ctl->remote_map) { pr_err("Can't allocate memory for parasite blob (pid: %d)\n", ctl->rpid); @@ -733,7 +742,7 @@ static void parasite_memfd_close(struct parasite_ctl *ctl, int fd) pr_err("Can't close memfd\n"); } -static int parasite_memfd_exchange(struct parasite_ctl *ctl, unsigned long size) +static int parasite_memfd_exchange(struct parasite_ctl *ctl, unsigned long size, int remote_prot) { void *where = (void *)ctl->ictx.syscall_ip + BUILTIN_SYSCALL_SIZE; bool compat_task = !compel_mode_native(ctl); @@ -785,8 +794,7 @@ static int parasite_memfd_exchange(struct parasite_ctl *ctl, unsigned long size) goto err_cure; } - ctl->remote_map = remote_mmap(ctl, NULL, size, - PROT_READ | PROT_WRITE | PROT_EXEC, + ctl->remote_map = remote_mmap(ctl, NULL, size, remote_prot, MAP_FILE | MAP_SHARED, fd, 0); if (!ctl->remote_map) { pr_err("Can't rmap memfd for parasite blob\n"); @@ -856,15 +864,47 @@ void compel_relocs_apply(void *mem, void *vbase, struct parasite_blob_desc *pbd) #endif } +long remote_mprotect(struct parasite_ctl *ctl, void *addr, size_t len, int prot) +{ + long ret; + int err; + bool compat_task = !user_regs_native(&ctl->orig.regs); + + err = compel_syscall(ctl, __NR(mprotect, compat_task), &ret, + (unsigned long)addr, len, prot, 0, 0, 0); + if (err < 0) { + pr_err("compel_syscall for mprotect failed\n"); + return -1; + } + return ret; +} + static int compel_map_exchange(struct parasite_ctl *ctl, unsigned long size) { - int ret; + int ret, remote_prot; + + if (ctl->pblob.hdr.data_off) + remote_prot = PROT_READ | PROT_EXEC; + else + remote_prot = PROT_READ | PROT_WRITE | PROT_EXEC; - ret = parasite_memfd_exchange(ctl, size); + ret = parasite_memfd_exchange(ctl, size, remote_prot); if (ret == 1) { pr_info("MemFD parasite doesn't work, goto legacy mmap\n"); - ret = parasite_mmap_exchange(ctl, size); + ret = parasite_mmap_exchange(ctl, size, remote_prot); + if (ret) + return ret; } + + if (!ctl->pblob.hdr.data_off) + return 0; + + ret = remote_mprotect(ctl, ctl->remote_map + ctl->pblob.hdr.data_off, + size - ctl->pblob.hdr.data_off, + PROT_READ | PROT_WRITE); + if (ret) + pr_err("remote_mprotect failed\n"); + return ret; } @@ -1436,7 +1476,7 @@ int compel_run_in_thread(struct parasite_thread_ctl *tctl, unsigned int cmd) ret = parasite_run(pid, PTRACE_CONT, ctl->parasite_ip, stack, ®s, octx); if (ret == 0) - ret = parasite_trap(ctl, pid, ®s, octx); + ret = parasite_trap(ctl, pid, ®s, octx, true); if (ret == 0) ret = (int)REG_RES(regs); @@ -1464,7 +1504,11 @@ int compel_unmap(struct parasite_ctl *ctl, unsigned long addr) ret = compel_stop_on_syscall(1, __NR(munmap, 0), __NR(munmap, 1), TRACE_ENTER); - if (restore_thread_ctx(pid, &ctl->orig)) + /* + * Don't touch extended registers here: they were restored + * with rt_sigreturn from sigframe. + */ + if (restore_thread_ctx(pid, &ctl->orig, false)) ret = -1; err: return ret; @@ -1639,7 +1683,8 @@ k_rtsigset_t *compel_task_sigmask(struct parasite_ctl *ctl) int compel_get_thread_regs(struct parasite_thread_ctl *tctl, save_regs_t save, void * arg) { - return get_task_regs(tctl->tid, &tctl->th.regs, save, arg, tctl->ctl->ictx.flags); + return compel_get_task_regs(tctl->tid, &tctl->th.regs, &tctl->th.ext_regs, + save, arg, tctl->ctl->ictx.flags); } struct infect_ctx *compel_infect_ctx(struct parasite_ctl *ctl) diff --git a/compel/src/main.c b/compel/src/main.c index c5f6e57ed2..2c98659cc7 100644 --- a/compel/src/main.c +++ b/compel/src/main.c @@ -19,7 +19,7 @@ #define CFLAGS_DEFAULT_SET \ "-Wstrict-prototypes " \ - "-fno-stack-protector -nostdlib -fomit-frame-pointer -ffreestanding " + "-fno-stack-protector -nostdlib -fomit-frame-pointer " #define COMPEL_CFLAGS_PIE CFLAGS_DEFAULT_SET "-fpie" #define COMPEL_CFLAGS_NOPIC CFLAGS_DEFAULT_SET "-fno-pic" diff --git a/compel/test/Makefile b/compel/test/Makefile new file mode 100644 index 0000000000..63fb76f80d --- /dev/null +++ b/compel/test/Makefile @@ -0,0 +1,17 @@ +all: fdspy infect rsys + +fdspy: + $(Q) $(MAKE) -C fdspy + $(Q) $(MAKE) -C fdspy run +.PHONY: fdspy + +infect: + $(Q) $(MAKE) -C infect + $(Q) $(MAKE) -C infect run +.PHONY: infect + + +rsys: + $(Q) $(MAKE) -C rsys + $(Q) $(MAKE) -C rsys run +.PHONY: rsys diff --git a/compel/test/fdspy/Makefile b/compel/test/fdspy/Makefile index 027c373fe8..82d9fdc0b2 100644 --- a/compel/test/fdspy/Makefile +++ b/compel/test/fdspy/Makefile @@ -5,6 +5,10 @@ COMPEL := ../../../compel/compel-host all: victim spy +run: + ./spy +.PHONY: run + clean: rm -f victim rm -f spy diff --git a/compel/test/fdspy/parasite.c b/compel/test/fdspy/parasite.c index c14064b362..7933d29ae2 100644 --- a/compel/test/fdspy/parasite.c +++ b/compel/test/fdspy/parasite.c @@ -1,7 +1,7 @@ #include -#include #include +#include /* * Stubs for std compel plugin. @@ -15,6 +15,6 @@ void parasite_cleanup(void) { } int parasite_daemon_cmd(int cmd, void *args) { if (cmd == PARASITE_CMD_GETFD) - fds_send_fd(2); + return (fds_send_fd(2) < 0); return 0; } diff --git a/compel/test/fdspy/spy.c b/compel/test/fdspy/spy.c index 1a373b6bb0..bbb9eb4183 100644 --- a/compel/test/fdspy/spy.c +++ b/compel/test/fdspy/spy.c @@ -5,6 +5,10 @@ #include #include +#include +#include +#include + #include "parasite.h" #define PARASITE_CMD_GETFD PARASITE_USER_CMDS @@ -100,8 +104,14 @@ static int check_pipe_ends(int wfd, int rfd) } printf("Check pipe ends are connected\n"); - write(wfd, "1", 2); - read(rfd, aux, sizeof(aux)); + if (write(wfd, "1", 2) != 2) { + fprintf(stderr, "write to pipe failed\n"); + return -1; + } + if (read(rfd, aux, sizeof(aux)) != sizeof(aux)) { + fprintf(stderr, "read from pipe failed\n"); + return -1; + } if (aux[0] != '1' || aux[1] != '\0') { fprintf(stderr, "Pipe connectivity lost\n"); return 0; diff --git a/compel/test/infect/Makefile b/compel/test/infect/Makefile index 4dedf33c95..bacfad9624 100644 --- a/compel/test/infect/Makefile +++ b/compel/test/infect/Makefile @@ -5,6 +5,10 @@ COMPEL := ../../../compel/compel-host all: victim spy +run: + ./spy +.PHONY: run + clean: rm -f victim rm -f spy diff --git a/compel/test/infect/spy.c b/compel/test/infect/spy.c index b5f8b25593..8be6925c58 100644 --- a/compel/test/infect/spy.c +++ b/compel/test/infect/spy.c @@ -3,6 +3,9 @@ #include #include +#include +#include + #include "parasite.h" #define PARASITE_CMD_INC PARASITE_USER_CMDS diff --git a/compel/test/rsys/Makefile b/compel/test/rsys/Makefile index 3babda18f2..53400498e4 100644 --- a/compel/test/rsys/Makefile +++ b/compel/test/rsys/Makefile @@ -5,6 +5,10 @@ COMPEL := ../../../compel/compel-host all: victim spy +run: + ./spy +.PHONY: run + clean: rm -f victim rm -f spy diff --git a/compel/test/rsys/spy.c b/compel/test/rsys/spy.c index 98654efcf3..dd89005f12 100644 --- a/compel/test/rsys/spy.c +++ b/compel/test/rsys/spy.c @@ -4,6 +4,9 @@ #include #include +#include +#include + static void print_vmsg(unsigned int lvl, const char *fmt, va_list parms) { printf("\tLC%u: ", lvl); @@ -61,7 +64,9 @@ static inline int chk(int fd, int val) { int v = 0; - read(fd, &v, sizeof(v)); + if (read(fd, &v, sizeof(v)) != sizeof(v)) { + fprintf(stderr, "read failed\n"); + } printf("%d, want %d\n", v, val); return v == val; } @@ -94,7 +99,10 @@ int main(int argc, char **argv) * Kick the victim once */ i = 0; - write(p_in[1], &i, sizeof(i)); + if (write(p_in[1], &i, sizeof(i)) != sizeof(i)) { + fprintf(stderr, "write to pipe failed\n"); + return -1; + } printf("Checking the victim session to be %d\n", sid); pass = chk(p_out[0], sid); @@ -112,7 +120,10 @@ int main(int argc, char **argv) /* * Kick the victim again so it tells new session */ - write(p_in[1], &i, sizeof(i)); + if (write(p_in[1], &i, sizeof(i)) != sizeof(i)) { + fprintf(stderr, "write to pipe failed\n"); + return -1; + } /* * Stop the victim and check the intrusion went well diff --git a/compel/test/rsys/victim.c b/compel/test/rsys/victim.c index 2f1943d0c6..85cb7cb890 100644 --- a/compel/test/rsys/victim.c +++ b/compel/test/rsys/victim.c @@ -9,7 +9,8 @@ int main(int argc, char **argv) break; i = getsid(0); - write(1, &i, sizeof(i)); + if (write(1, &i, sizeof(i)) != sizeof(i)) + break; } return 0; diff --git a/coredump/criu_coredump/coredump.py b/coredump/criu_coredump/coredump.py index 68dc16bf2f..b37ef22913 100644 --- a/coredump/criu_coredump/coredump.py +++ b/coredump/criu_coredump/coredump.py @@ -523,7 +523,6 @@ class mmaped_file_info: continue shmid = vma["shmid"] - size = vma["end"] - vma["start"] off = vma["pgoff"] / PAGESIZE files = self.reg_files @@ -719,7 +718,7 @@ def _gen_mem_chunk(self, pid, vma, size): # than one from maped file on disk. page = page_mem - if page == None: + if page is None: # Hole page = PAGESIZE * "\0" @@ -822,8 +821,6 @@ class vma_class: for vma in mm["vmas"]: size = self._get_vma_dump_size(vma) - chunk = self._gen_mem_chunk(pid, vma, size) - v = vma_class() v.filesz = self._get_vma_dump_size(vma) v.data = self._gen_mem_chunk(pid, vma, v.filesz) diff --git a/criu/arch/aarch64/include/asm/vdso.h b/criu/arch/aarch64/include/asm/vdso.h index 8a65e0947c..97a2440eed 100644 --- a/criu/arch/aarch64/include/asm/vdso.h +++ b/criu/arch/aarch64/include/asm/vdso.h @@ -16,15 +16,16 @@ * Workaround for VDSO array symbol table's relocation. * XXX: remove when compel/piegen will support aarch64. */ -static const char* __maybe_unused aarch_vdso_symbol1 = "__kernel_clock_getres"; -static const char* __maybe_unused aarch_vdso_symbol2 = "__kernel_clock_gettime"; -static const char* __maybe_unused aarch_vdso_symbol3 = "__kernel_gettimeofday"; -static const char* __maybe_unused aarch_vdso_symbol4 = "__kernel_rt_sigreturn"; +#define ARCH_VDSO_SYMBOLS_LIST \ + const char* aarch_vdso_symbol1 = "__kernel_clock_getres"; \ + const char* aarch_vdso_symbol2 = "__kernel_clock_gettime"; \ + const char* aarch_vdso_symbol3 = "__kernel_gettimeofday"; \ + const char* aarch_vdso_symbol4 = "__kernel_rt_sigreturn"; -#define ARCH_VDSO_SYMBOLS \ - aarch_vdso_symbol1, \ - aarch_vdso_symbol2, \ - aarch_vdso_symbol3, \ +#define ARCH_VDSO_SYMBOLS \ + aarch_vdso_symbol1, \ + aarch_vdso_symbol2, \ + aarch_vdso_symbol3, \ aarch_vdso_symbol4 extern void write_intraprocedure_branch(unsigned long to, unsigned long from); diff --git a/criu/arch/arm/include/asm/parasite.h b/criu/arch/arm/include/asm/parasite.h index 0ed320ba6b..7f1e219c6f 100644 --- a/criu/arch/arm/include/asm/parasite.h +++ b/criu/arch/arm/include/asm/parasite.h @@ -1,6 +1,7 @@ #ifndef __ASM_PARASITE_H__ #define __ASM_PARASITE_H__ +/* kuser_get_tls() kernel-provided user-helper, the address is emulated */ static inline void arch_get_tls(tls_t *ptls) { *ptls = ((tls_t (*)(void))0xffff0fe0)(); diff --git a/criu/arch/arm/include/asm/vdso.h b/criu/arch/arm/include/asm/vdso.h index f57790ac28..e96514e0e6 100644 --- a/criu/arch/arm/include/asm/vdso.h +++ b/criu/arch/arm/include/asm/vdso.h @@ -11,8 +11,11 @@ */ #define VDSO_SYMBOL_MAX 2 #define VDSO_SYMBOL_GTOD 1 -#define ARCH_VDSO_SYMBOLS \ - "__vdso_clock_gettime", \ - "__vdso_gettimeofday" +#define ARCH_VDSO_SYMBOLS_LIST \ + const char* aarch_vdso_symbol1 = "__vdso_clock_gettime"; \ + const char* aarch_vdso_symbol2 = "__vdso_gettimeofday"; +#define ARCH_VDSO_SYMBOLS \ + aarch_vdso_symbol1, \ + aarch_vdso_symbol2, #endif /* __CR_ASM_VDSO_H__ */ diff --git a/criu/arch/mips/include/asm/vdso.h b/criu/arch/mips/include/asm/vdso.h index 0e5da159e4..c3e9b01763 100755 --- a/criu/arch/mips/include/asm/vdso.h +++ b/criu/arch/mips/include/asm/vdso.h @@ -14,10 +14,14 @@ */ #define VDSO_SYMBOL_MAX 3 #define VDSO_SYMBOL_GTOD 0 -#define ARCH_VDSO_SYMBOLS \ - "__vdso_clock_gettime", \ - "__vdso_gettimeofday", \ - "__vdso_clock_getres" +#define ARCH_VDSO_SYMBOLS_LIST \ + const char* aarch_vdso_symbol1 = "__vdso_clock_gettime"; \ + const char* aarch_vdso_symbol2 = "__vdso_gettimeofday"; \ + const char* aarch_vdso_symbol3 = "__vdso_clock_getres"; +#define ARCH_VDSO_SYMBOLS \ + aarch_vdso_symbol1, \ + aarch_vdso_symbol2, \ + aarch_vdso_symbol3, #endif /* __CR_ASM_VDSO_H__ */ diff --git a/criu/arch/ppc64/include/asm/vdso.h b/criu/arch/ppc64/include/asm/vdso.h index 6c92348d6d..fe04336650 100644 --- a/criu/arch/ppc64/include/asm/vdso.h +++ b/criu/arch/ppc64/include/asm/vdso.h @@ -14,16 +14,28 @@ */ #define VDSO_SYMBOL_MAX 10 #define VDSO_SYMBOL_GTOD 5 -#define ARCH_VDSO_SYMBOLS \ - "__kernel_clock_getres", \ - "__kernel_clock_gettime", \ - "__kernel_get_syscall_map", \ - "__kernel_get_tbfreq", \ - "__kernel_getcpu", \ - "__kernel_gettimeofday", \ - "__kernel_sigtramp_rt64", \ - "__kernel_sync_dicache", \ - "__kernel_sync_dicache_p5", \ - "__kernel_time" +#define ARCH_VDSO_SYMBOLS_LIST \ + const char* aarch_vdso_symbol1 = "__kernel_clock_getres"; \ + const char* aarch_vdso_symbol2 = "__kernel_clock_gettime"; \ + const char* aarch_vdso_symbol3 = "__kernel_get_syscall_map"; \ + const char* aarch_vdso_symbol4 = "__kernel_get_tbfreq"; \ + const char* aarch_vdso_symbol5 = "__kernel_getcpu"; \ + const char* aarch_vdso_symbol6 = "__kernel_gettimeofday"; \ + const char* aarch_vdso_symbol7 = "__kernel_sigtramp_rt64"; \ + const char* aarch_vdso_symbol8 = "__kernel_sync_dicache"; \ + const char* aarch_vdso_symbol9 = "__kernel_sync_dicache_p5"; \ + const char* aarch_vdso_symbol10 = "__kernel_time"; + +#define ARCH_VDSO_SYMBOLS \ + aarch_vdso_symbol1, \ + aarch_vdso_symbol2, \ + aarch_vdso_symbol3, \ + aarch_vdso_symbol4, \ + aarch_vdso_symbol5, \ + aarch_vdso_symbol6, \ + aarch_vdso_symbol7, \ + aarch_vdso_symbol8, \ + aarch_vdso_symbol9, \ + aarch_vdso_symbol10 #endif /* __CR_ASM_VDSO_H__ */ diff --git a/criu/arch/s390/include/asm/vdso.h b/criu/arch/s390/include/asm/vdso.h index c54d848ad2..ac71f59811 100644 --- a/criu/arch/s390/include/asm/vdso.h +++ b/criu/arch/s390/include/asm/vdso.h @@ -12,13 +12,18 @@ #define VDSO_SYMBOL_GTOD 0 /* - * This definition is used in pie/util-vdso.c to initialize the vdso symbol + * These definitions are used in pie/util-vdso.c to initialize the vdso symbol * name string table 'vdso_symbols' */ -#define ARCH_VDSO_SYMBOLS \ - "__kernel_gettimeofday", \ - "__kernel_clock_gettime", \ - "__kernel_clock_getres", \ - "__kernel_getcpu" +#define ARCH_VDSO_SYMBOLS_LIST \ + const char* aarch_vdso_symbol1 = "__kernel_gettimeofday"; \ + const char* aarch_vdso_symbol2 = "__kernel_clock_gettime"; \ + const char* aarch_vdso_symbol3 = "__kernel_clock_getres"; \ + const char* aarch_vdso_symbol4 = "__kernel_getcpu"; +#define ARCH_VDSO_SYMBOLS \ + aarch_vdso_symbol1, \ + aarch_vdso_symbol2, \ + aarch_vdso_symbol3, \ + aarch_vdso_symbol4 #endif /* __CR_ASM_VDSO_H__ */ diff --git a/criu/arch/x86/include/asm/parasite.h b/criu/arch/x86/include/asm/parasite.h index 6b4d4ac59a..678fc75e22 100644 --- a/criu/arch/x86/include/asm/parasite.h +++ b/criu/arch/x86/include/asm/parasite.h @@ -1,77 +1,10 @@ #ifndef __ASM_PARASITE_H__ #define __ASM_PARASITE_H__ -#include -#include -#include "asm/compat.h" - -static int arch_get_user_desc(user_desc_t *desc) -{ - int ret = __NR32_get_thread_area; - /* - * For 64-bit applications, TLS (fs_base for Glibc) is - * in MSR, which are dumped with the help of arch_prctl(). - * - * But SET_FS_BASE will update GDT if base pointer fits in 4 bytes. - * Otherwise it will set only MSR, which allows for mixed 64/32-bit - * code to use: 2 MSRs as TLS base _and_ 3 GDT entries. - * Having in sum 5 TLS pointers, 3 of which are four bytes and - * other two bigger than four bytes: - * struct thread_struct { - * struct desc_struct tls_array[3]; - * ... - * #ifdef CONFIG_X86_64 - * unsigned long fsbase; - * unsigned long gsbase; - * #endif - * ... - * }; - */ - asm volatile ( - " mov %0,%%eax \n" - " mov %1,%%rbx \n" - " int $0x80 \n" - " mov %%eax,%0 \n" - : "+m"(ret) - : "m"(desc) - : "rax", "rbx", "r8", "r9", "r10", "r11", "memory"); - - if (ret) - pr_err("Failed to dump TLS descriptor #%d: %d\n", - desc->entry_number, ret); - return ret; -} - -static void arch_get_tls(tls_t *ptls) -{ - void *syscall_mem; - int i; - - syscall_mem = alloc_compat_syscall_stack(); - if (!syscall_mem) { - pr_err("Failed to allocate memory <4Gb for compat syscall\n"); - - for (i = 0; i < GDT_ENTRY_TLS_NUM; i++) { - user_desc_t *d = &ptls->desc[i]; - - d->seg_not_present = 1; - d->entry_number = GDT_ENTRY_TLS_MIN + i; - } - return; - } - - for (i = 0; i < GDT_ENTRY_TLS_NUM; i++) - { - user_desc_t *d = syscall_mem; - - memset(d, 0, sizeof(user_desc_t)); - d->seg_not_present = 1; - d->entry_number = GDT_ENTRY_TLS_MIN + i; - arch_get_user_desc(d); - memcpy(&ptls->desc[i], d, sizeof(user_desc_t)); - } - - free_compat_syscall_stack(syscall_mem); -} +/* + * TLS is accessed through PTRACE_GET_THREAD_AREA, + * see compel_arch_fetch_thread_area(). + */ +static inline void arch_get_tls(tls_t *ptls) { (void)ptls; } #endif diff --git a/criu/arch/x86/include/asm/restorer.h b/criu/arch/x86/include/asm/restorer.h index 731477ec99..6ba0740206 100644 --- a/criu/arch/x86/include/asm/restorer.h +++ b/criu/arch/x86/include/asm/restorer.h @@ -3,6 +3,7 @@ #include "asm/types.h" #include +#include #include "images/core.pb-c.h" #include #include diff --git a/criu/arch/x86/include/asm/types.h b/criu/arch/x86/include/asm/types.h index 3ff7fc630a..012358949b 100644 --- a/criu/arch/x86/include/asm/types.h +++ b/criu/arch/x86/include/asm/types.h @@ -36,17 +36,4 @@ static inline void *decode_pointer(u64 v) { return (void*)(long)v; } #define AT_VECTOR_SIZE 44 typedef uint64_t auxv_t; -/* - * Linux preserves three TLS segments in GDT. - * Offsets in GDT differ between 32-bit and 64-bit machines. - * For 64-bit x86 those GDT offsets are the same - * for native and compat tasks. - */ -#define GDT_ENTRY_TLS_MIN 12 -#define GDT_ENTRY_TLS_MAX 14 -#define GDT_ENTRY_TLS_NUM 3 -typedef struct { - user_desc_t desc[GDT_ENTRY_TLS_NUM]; -} tls_t; - #endif /* __CR_ASM_TYPES_H__ */ diff --git a/criu/arch/x86/include/asm/vdso.h b/criu/arch/x86/include/asm/vdso.h index 28ae2d15a4..54d1fba695 100644 --- a/criu/arch/x86/include/asm/vdso.h +++ b/criu/arch/x86/include/asm/vdso.h @@ -35,13 +35,22 @@ * vsyscall will be patched again when addressing: * https://github.com/checkpoint-restore/criu/issues/512 */ -#define ARCH_VDSO_SYMBOLS \ - "__vdso_clock_gettime", \ - "__vdso_getcpu", \ - "__vdso_gettimeofday", \ - "__vdso_time", \ - "__kernel_sigreturn", \ - "__kernel_rt_sigreturn" + +#define ARCH_VDSO_SYMBOLS_LIST \ + const char* aarch_vdso_symbol1 = "__vdso_clock_gettime"; \ + const char* aarch_vdso_symbol2 = "__vdso_getcpu"; \ + const char* aarch_vdso_symbol3 = "__vdso_gettimeofday"; \ + const char* aarch_vdso_symbol4 = "__vdso_time"; \ + const char* aarch_vdso_symbol5 = "__kernel_sigreturn"; \ + const char* aarch_vdso_symbol6 = "__kernel_rt_sigreturn"; + +#define ARCH_VDSO_SYMBOLS \ + aarch_vdso_symbol1, \ + aarch_vdso_symbol2, \ + aarch_vdso_symbol3, \ + aarch_vdso_symbol4, \ + aarch_vdso_symbol5, \ + aarch_vdso_symbol6 /* "__kernel_vsyscall", */ diff --git a/criu/autofs.c b/criu/autofs.c index 3e93b7b851..8b25308d97 100644 --- a/criu/autofs.c +++ b/criu/autofs.c @@ -330,11 +330,11 @@ static int parse_options(char *options, AutofsEntry *entry, long *pipe_ino) static int autofs_revisit_options(struct mount_info *pm) { FILE *f; - char *str; + char *buf; int ret = -ENOMEM; - str = xmalloc(1024); - if (!str) { + buf = xmalloc(1024); + if (!buf) { return -ENOMEM; } @@ -342,8 +342,9 @@ static int autofs_revisit_options(struct mount_info *pm) if (!f) goto free_str; - while (fgets(str, 1024, f)) { + while (fgets(buf, 1024, f)) { int mnt_id = -1; + char *str = buf; char *token; /* Removing '/n' */ @@ -376,7 +377,7 @@ static int autofs_revisit_options(struct mount_info *pm) close_proc: fclose(f); free_str: - free(str); + free(buf); return ret; } @@ -432,6 +433,7 @@ static int access_autofs_mount(struct mount_info *pm) case 0: /* We don't care about results. * All we need is to "touch" */ + /* coverity[check_return] */ openat(autofs_mnt, mnt_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY); _exit(0); diff --git a/criu/bfd.c b/criu/bfd.c index 05824551b6..535c4d3549 100644 --- a/criu/bfd.c +++ b/criu/bfd.c @@ -198,8 +198,7 @@ char *breadchr(struct bfd *f, char c) if (b->sz == BUFSIZE) { pr_err("The bfd buffer is too small\n"); - ERR_PTR(-EIO); - return NULL; + return ERR_PTR(-EIO); } /* * Last bytes may lack the \n at the diff --git a/criu/cgroup.c b/criu/cgroup.c index e7e15bc512..8576ee2f13 100644 --- a/criu/cgroup.c +++ b/criu/cgroup.c @@ -1940,6 +1940,7 @@ static int rewrite_cgroup_roots(CgroupEntry *cge) list_for_each_entry(o, &opts.new_cgroup_roots, node) { unsigned old_mask = ctrl_mask; + /* coverity[check_return] */ cgroup_contains(ctrl->cnames, ctrl->n_cnames, o->controller, &ctrl_mask); if (old_mask != ctrl_mask) { diff --git a/criu/cr-dump.c b/criu/cr-dump.c index b9d29145cc..193a49c4d8 100644 --- a/criu/cr-dump.c +++ b/criu/cr-dump.c @@ -936,8 +936,10 @@ static int dump_signal_queue(pid_t tid, SignalQueueEntry **sqe, bool group) } nr = ret = ptrace(PTRACE_PEEKSIGINFO, tid, &arg, si); - if (ret == 0) + if (ret == 0) { + xfree(si); break; /* Finished */ + } if (ret < 0) { if (errno == EIO) { @@ -946,6 +948,7 @@ static int dump_signal_queue(pid_t tid, SignalQueueEntry **sqe, bool group) } else pr_perror("ptrace"); + xfree(si); break; } @@ -953,6 +956,7 @@ static int dump_signal_queue(pid_t tid, SignalQueueEntry **sqe, bool group) queue->signals = xrealloc(queue->signals, sizeof(*queue->signals) * queue->n_signals); if (!queue->signals) { ret = -1; + xfree(si); break; } @@ -1095,8 +1099,16 @@ static int dump_zombies(void) int ret = -1; int pidns = root_ns_mask & CLONE_NEWPID; - if (pidns && set_proc_fd(get_service_fd(CR_PROC_FD_OFF))) - return -1; + if (pidns) { + int fd; + + fd = get_service_fd(CR_PROC_FD_OFF); + if (fd < 0) + return -1; + + if (set_proc_fd(fd)) + return -1; + } /* * We dump zombies separately because for pid-ns case diff --git a/criu/cr-restore.c b/criu/cr-restore.c index 589087f9fb..53eddba6bf 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -418,6 +418,10 @@ static int populate_pid_proc(void) pr_err("Can't open PROC_SELF\n"); return -1; } + if (open_pid_proc(PROC_SELF) < 0) { + pr_err("Can't open PROC_SELF\n"); + return -1; + } return 0; } @@ -1363,9 +1367,9 @@ static int set_next_pid(void *arg) static inline int fork_with_pid(struct pstree_item *item) { - unsigned long clone_flags; struct cr_clone_arg ca; struct ns_id *pid_ns = NULL; + bool external_pidns = false; int ret = -1; pid_t pid = vpid(item); @@ -1404,20 +1408,13 @@ static inline int fork_with_pid(struct pstree_item *item) ca.core = NULL; } - ret = -1; - - ca.item = item; - ca.clone_flags = rsti(item)->clone_flags; - - BUG_ON(ca.clone_flags & CLONE_VM); - - pr_info("Forking task with %d pid (flags 0x%lx)\n", pid, ca.clone_flags); + if (item->ids) + pid_ns = lookup_ns_by_id(item->ids->pid_ns_id, &pid_ns_desc); - if (ca.item->ids) - pid_ns = lookup_ns_by_id(ca.item->ids->pid_ns_id, &pid_ns_desc); + if (!current && pid_ns && pid_ns->ext_key) + external_pidns = true; - clone_flags = ca.clone_flags; - if (pid_ns && pid_ns->ext_key) { + if (external_pidns) { int fd; /* Not possible to restore into an empty PID namespace. */ @@ -1426,13 +1423,6 @@ static inline int fork_with_pid(struct pstree_item *item) return -1; } - /* - * Restoring into an existing namespace means that CLONE_NEWPID - * needs to be removed during clone() as the process will be - * created in the correct PID namespace thanks to switch_ns_by_fd(). - */ - clone_flags &= ~CLONE_NEWPID; - fd = inherit_fd_lookup_id(pid_ns->ext_key); if (fd < 0) { pr_err("Unable to find an external pidns: %s\n", pid_ns->ext_key); @@ -1446,20 +1436,21 @@ static inline int fork_with_pid(struct pstree_item *item) return -1; } - /* - * If a process without a PID namespace is restored into - * a PID namespace this tells CRIU to still handle the - * process as if using CLONE_NEWPID. - */ - root_ns_mask |= CLONE_NEWPID; - rsti(item)->clone_flags |= CLONE_NEWPID; + pr_info("Inheriting external pidns %s for %d\n", pid_ns->ext_key, pid); } - if (!(clone_flags & CLONE_NEWPID)) { + ca.item = item; + ca.clone_flags = rsti(item)->clone_flags; + + BUG_ON(ca.clone_flags & CLONE_VM); + + pr_info("Forking task with %d pid (flags 0x%lx)\n", pid, ca.clone_flags); + + if (!(ca.clone_flags & CLONE_NEWPID)) { lock_last_pid(); if (!kdat.has_clone3_set_tid) { - if (pid_ns && pid_ns->ext_key) { + if (external_pidns) { /* * Restoring into another namespace requires a helper * to write to LAST_PID_PATH. Using clone3() this is @@ -1476,7 +1467,7 @@ static inline int fork_with_pid(struct pstree_item *item) } } } else { - if (!(pid_ns && pid_ns->ext_key)) { + if (!external_pidns) { if (pid != INIT_PID) { pr_err("First PID in a PID namespace needs to be %d and not %d\n", pid, INIT_PID); @@ -1487,7 +1478,7 @@ static inline int fork_with_pid(struct pstree_item *item) if (kdat.has_clone3_set_tid) { ret = clone3_with_pid_noasan(restore_task_with_children, - &ca, (clone_flags & + &ca, (ca.clone_flags & ~(CLONE_NEWNET | CLONE_NEWCGROUP | CLONE_NEWTIME)), SIGCHLD, pid); } else { @@ -1505,7 +1496,7 @@ static inline int fork_with_pid(struct pstree_item *item) */ close_pid_proc(); ret = clone_noasan(restore_task_with_children, - (clone_flags & + (ca.clone_flags & ~(CLONE_NEWNET | CLONE_NEWCGROUP | CLONE_NEWTIME)) | SIGCHLD, &ca); } @@ -1525,7 +1516,7 @@ static inline int fork_with_pid(struct pstree_item *item) } err_unlock: - if (!(clone_flags & CLONE_NEWPID)) + if (!(ca.clone_flags & CLONE_NEWPID)) unlock_last_pid(); if (ca.core) @@ -1533,35 +1524,53 @@ static inline int fork_with_pid(struct pstree_item *item) return ret; } -static void sigchld_handler(int signal, siginfo_t *siginfo, void *data) +/* Returns 0 if restore can be continued */ +static int sigchld_process(int status, pid_t pid) { - int status, pid, exit; + int sig; + + if (WIFEXITED(status)) { + pr_err("%d exited, status=%d\n", pid, WEXITSTATUS(status)); + return -1; + } else if (WIFSIGNALED(status)) { + sig = WTERMSIG(status); + pr_err("%d killed by signal %d: %s\n", pid, sig, strsignal(sig)); + return -1; + } else if (WIFSTOPPED(status)) { + sig = WSTOPSIG(status); + /* The root task is ptraced. Allow it to handle SIGCHLD */ + if (sig == SIGCHLD && !current) { + if (ptrace(PTRACE_CONT, pid, 0, SIGCHLD)) { + pr_perror("Unable to resume %d", pid); + return -1; + } + return 0; + } + pr_err("%d stopped by signal %d: %s\n", pid, sig, strsignal(sig)); + return -1; + } else if (WIFCONTINUED(status)) { + pr_err("%d unexpectedly continued\n", pid); + return -1; + } + pr_err("wait for %d resulted in %x status\n", pid, status); + return -1; +} +static void sigchld_handler(int signal, siginfo_t *siginfo, void *data) +{ while (1) { + int status; + pid_t pid; + pid = waitpid(-1, &status, WNOHANG); if (pid <= 0) return; - if (!current && WIFSTOPPED(status) && - WSTOPSIG(status) == SIGCHLD) { - /* The root task is ptraced. Allow it to handle SIGCHLD */ - if (ptrace(PTRACE_CONT, pid, 0, SIGCHLD)) - pr_perror("Unable to resume %d", pid); - return; - } - - exit = WIFEXITED(status); - status = exit ? WEXITSTATUS(status) : WTERMSIG(status); - - break; + if (sigchld_process(status, pid) < 0) + goto err_abort; } - if (exit) - pr_err("%d exited, status=%d\n", pid, status); - else - pr_err("%d killed by signal %d: %s\n", - pid, status, strsignal(status)); - +err_abort: futex_abort_and_wake(&task_entries->nr_in_progress); } @@ -2192,6 +2201,18 @@ static int write_restored_pid(void) return 0; } +static void reap_zombies(void) +{ + while (1) { + pid_t pid = wait(NULL); + if (pid == -1) { + if (errno != ECHILD) + pr_perror("Error while waiting for pids"); + return; + } + } +} + static int restore_root_task(struct pstree_item *init) { enum trace_flags flag = TRACE_ALL; @@ -2236,9 +2257,18 @@ static int restore_root_task(struct pstree_item *init) "\"--namespace pid\" option.\n"); return -1; } - } else if (root_ns_mask & CLONE_NEWPID) { - pr_err("Can't restore pid namespace without the process init\n"); - return -1; + } else if (root_ns_mask & CLONE_NEWPID) { + struct ns_id *ns; + /* + * Restoring into an existing PID namespace. This disables + * the check to require a PID 1 when restoring a process + * which used to be in a PID namespace. + */ + ns = lookup_ns_by_id(init->ids->pid_ns_id, &pid_ns_desc); + if (!ns || !ns->ext_key) { + pr_err("Can't restore pid namespace without the process init\n"); + return -1; + } } __restore_switch_stage_nw(CR_STATE_ROOT_TASK); @@ -2445,8 +2475,9 @@ static int restore_root_task(struct pstree_item *init) if (ret != 0) pr_err("Post-resume script ret code %d\n", ret); - if (!opts.restore_detach && !opts.exec_cmd) - wait(NULL); + if (!opts.restore_detach && !opts.exec_cmd) { + reap_zombies(); + } return 0; @@ -2457,7 +2488,7 @@ static int restore_root_task(struct pstree_item *init) * The processes can be killed only when all of them have been created, * otherwise an external processes can be killed. */ - if (root_ns_mask & CLONE_NEWPID) { + if (vpid(root_item) == INIT_PID) { int status; /* Kill init */ @@ -3229,7 +3260,7 @@ static bool groups_match(gid_t* groups, int n_groups) n = getgroups(0, NULL); if (n == -1) { pr_perror("Failed to get number of supplementary groups"); - ret = false; + return false; } if (n != n_groups) return false; diff --git a/criu/cr-service.c b/criu/cr-service.c index 56be6bcd37..474f598aca 100644 --- a/criu/cr-service.c +++ b/criu/cr-service.c @@ -1069,8 +1069,7 @@ static int handle_feature_check(int sk, CriuReq * msg) } if (pid == 0) { - /* kerndat_init() is called from setup_opts_from_req() */ - if (setup_opts_from_req(sk, msg->opts)) + if (kerndat_init()) exit(1); setproctitle("feature-check --rpc"); @@ -1104,6 +1103,8 @@ static int handle_feature_check(int sk, CriuReq * msg) if (status != 0) goto out; + return 0; + /* * The child process was not able to send an answer. Tell * the RPC client that something did not work as expected. diff --git a/criu/fdstore.c b/criu/fdstore.c index a4583fdf44..7c8eb72081 100644 --- a/criu/fdstore.c +++ b/criu/fdstore.c @@ -107,8 +107,13 @@ int fdstore_add(int fd) int fdstore_get(int id) { - int sk = get_service_fd(FDSTORE_SK_OFF); - int fd; + int sk, fd; + + sk = get_service_fd(FDSTORE_SK_OFF); + if (sk < 0) { + pr_err("Cannot get FDSTORE_SK_OFF fd\n"); + return -1; + } mutex_lock(&desc->lock); if (setsockopt(sk, SOL_SOCKET, SO_PEEK_OFF, &id, sizeof(id))) { diff --git a/criu/file-lock.c b/criu/file-lock.c index 8be7589df3..050954cdec 100644 --- a/criu/file-lock.c +++ b/criu/file-lock.c @@ -108,6 +108,12 @@ int dump_file_locks(void) continue; } + if (!opts.handle_file_locks) { + pr_err("Some file locks are hold by dumping tasks! " + "You can try --" OPT_FILE_LOCKS " to dump them.\n"); + return -1; + } + file_lock_entry__init(&fle); fle.pid = fl->real_owner; fle.fd = fl->owners_fd; @@ -195,7 +201,7 @@ static int lock_check_fd(int lfd, struct file_lock *fl) } else { /* * The ret == 0 means, that new lock doesn't conflict - * with any others on the file. But since we do know, + * with any others on the file. But since we do know, * that there should be some other one (file is found * in /proc/locks), it means that the lock is already * on file pointed by fd. diff --git a/criu/files-reg.c b/criu/files-reg.c index aed1e73dc2..f4f41193b2 100644 --- a/criu/files-reg.c +++ b/criu/files-reg.c @@ -1150,40 +1150,6 @@ static inline bool nfs_silly_rename(char *rpath, const struct fd_parms *parms) return (parms->fs_type == NFS_SUPER_MAGIC) && is_sillyrename_name(rpath); } -int strip_deleted(struct fd_link *link) -{ - struct dcache_prepends { - const char *str; - size_t len; - } static const prepends[] = { - { - .str = " (deleted)", - .len = 10, - }, { - .str = "//deleted", - .len = 9, - } - }; - size_t i; - - for (i = 0; i < ARRAY_SIZE(prepends); i++) { - size_t at; - - if (link->len <= prepends[i].len) - continue; - - at = link->len - prepends[i].len; - if (!strcmp(&link->name[at], prepends[i].str)) { - pr_debug("Strip '%s' tag from '%s'\n", - prepends[i].str, link->name); - link->name[at] = '\0'; - link->len -= prepends[i].len; - return 1; - } - } - return 0; -} - static int check_path_remap(struct fd_link *link, const struct fd_parms *parms, int lfd, u32 id, struct ns_id *nsid) { @@ -1219,7 +1185,7 @@ static int check_path_remap(struct fd_link *link, const struct fd_parms *parms, * cases. */ if (pid != 0) { - bool is_dead = strip_deleted(link); + bool is_dead = link_strip_deleted(link); mntns_root = mntns_get_root_fd(nsid); if (mntns_root < 0) return -1; @@ -1252,7 +1218,7 @@ static int check_path_remap(struct fd_link *link, const struct fd_parms *parms, * this FS and can't have a valid " (deleted)" * postfix as a part of not deleted filename. */ - strip_deleted(link); + link_strip_deleted(link); /* * Devpts devices/files are generated by the * kernel itself so we should not try to generate @@ -1269,7 +1235,7 @@ static int check_path_remap(struct fd_link *link, const struct fd_parms *parms, * be careful whether anybody still has any of its hardlinks * also open. */ - strip_deleted(link); + link_strip_deleted(link); return dump_ghost_remap(rpath + 1, ost, lfd, id, nsid); } @@ -1301,9 +1267,11 @@ static int check_path_remap(struct fd_link *link, const struct fd_parms *parms, * name. */ - if (errno == ENOENT) + if (errno == ENOENT) { + link_strip_deleted(link); return dump_linked_remap(rpath + 1, plen - 1, ost, lfd, id, nsid); + } pr_perror("Can't stat path"); return -1; diff --git a/criu/files.c b/criu/files.c index 0912d1a4d6..f10527b591 100644 --- a/criu/files.c +++ b/criu/files.c @@ -1256,6 +1256,14 @@ int close_old_fds(void) struct dirent *de; int fd, ret; + /** + * Close previous /proc/self/ service fd, as we don't wan't to reuse it + * from a different task. Also there can be some junk fd in it's place + * after we've moved our service fds (e.g. from other task of parents + * shared fdtable), we need to close it before opendir_proc() below. + */ + __close_service_fd(PROC_SELF_FD_OFF); + dir = opendir_proc(PROC_SELF, "fd"); if (dir == NULL) return -1; @@ -1297,7 +1305,6 @@ int prepare_fds(struct pstree_item *me) sfds_protected = false; close_service_fd(CGROUP_YARD); sfds_protected = true; - set_proc_self_fd(-1); /* flush any proc cached fds we may have */ if (rsti(me)->fdt) { struct fdt *fdt = rsti(me)->fdt; diff --git a/criu/fsnotify.c b/criu/fsnotify.c index 09093c0bed..00a6a32c11 100644 --- a/criu/fsnotify.c +++ b/criu/fsnotify.c @@ -104,12 +104,19 @@ static int open_by_handle(void *arg, int fd, int pid) return syscall(__NR_open_by_handle_at, fd, arg, O_PATH); } +enum { + ERR_NO_MOUNT = -1, + ERR_NO_PATH_IN_MOUNT = -2, + ERR_GENERIC = -3 +}; + static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_handle) { struct mount_info *m; fh_t handle; int fd = -1; char *path; + char suitable_mount_found = 0; decode_handle(&handle, f_handle); @@ -142,6 +149,7 @@ static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_ close(mntfd); if (fd < 0) continue; + suitable_mount_found = 1; if (read_fd_link(fd, buf, sizeof(buf)) < 0) { close(fd); @@ -164,7 +172,7 @@ static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_ if (fstat(openable_fd, &st)) { pr_perror("Can't stat on %s", __path); close(openable_fd); - return ERR_PTR(-errno); + goto err; } close(openable_fd); @@ -175,7 +183,7 @@ static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_ if (st.st_ino == i_ino) { path = xstrdup(buf); if (path == NULL) - return ERR_PTR(-ENOMEM); + return ERR_PTR(ERR_GENERIC); if (root_ns_mask & CLONE_NEWNS) { f_handle->has_mnt_id = true; f_handle->mnt_id = m->mnt_id; @@ -186,9 +194,10 @@ static char *alloc_openable(unsigned int s_dev, unsigned long i_ino, FhEntry *f_ pr_debug("\t\t\tnot openable as %s (%m)\n", __path); } - return ERR_PTR(-ENOENT); err: - return ERR_PTR(-1); + if (suitable_mount_found) + return ERR_PTR(ERR_NO_PATH_IN_MOUNT); + return ERR_PTR(ERR_NO_MOUNT); } static int open_handle(unsigned int s_dev, unsigned long i_ino, @@ -209,7 +218,7 @@ static int open_handle(unsigned int s_dev, unsigned long i_ino, mntfd = __open_mountpoint(m, -1); if (mntfd < 0) { - pr_err("Can't open mount for s_dev %x, continue\n", s_dev); + pr_warn("Can't open mount for s_dev %x, continue\n", s_dev); continue; } @@ -228,63 +237,59 @@ int check_open_handle(unsigned int s_dev, unsigned long i_ino, FhEntry *f_handle) { char *path, *irmap_path; - int fd = -1; + struct mount_info *mi; - if (fault_injected(FI_CHECK_OPEN_HANDLE)) { - fd = -1; + if (fault_injected(FI_CHECK_OPEN_HANDLE)) goto fault; - } - fd = open_handle(s_dev, i_ino, f_handle); -fault: - if (fd >= 0) { - struct mount_info *mi; + /* + * Always try to fetch watchee path first. There are several reasons: + * + * - tmpfs/devtmps do not save inode numbers between mounts, + * so it is critical to have the complete path under our + * hands for restore purpose; + * + * - in case of migration the inodes might be changed as well + * so the only portable solution is to carry the whole path + * to the watchee inside image. + */ + path = alloc_openable(s_dev, i_ino, f_handle); + if (!IS_ERR_OR_NULL(path)) { pr_debug("\tHandle 0x%x:0x%lx is openable\n", s_dev, i_ino); + goto out; + } else if (IS_ERR(path) && PTR_ERR(path) == ERR_NO_MOUNT) { + goto fault; + } else if (IS_ERR(path) && PTR_ERR(path) == ERR_GENERIC) { + goto err; + } - mi = lookup_mnt_sdev(s_dev); - if (mi == NULL) { - pr_err("Unable to lookup a mount by dev 0x%x\n", s_dev); - goto err; - } + mi = lookup_mnt_sdev(s_dev); + if (mi == NULL) { + pr_err("Unable to lookup a mount by dev 0x%x\n", s_dev); + goto err; + } + + if ((mi->fstype->code == FSTYPE__TMPFS) || + (mi->fstype->code == FSTYPE__DEVTMPFS)) { + pr_err("Can't find suitable path for handle (dev %#x ino %#lx): %d\n", + s_dev, i_ino, (int)PTR_ERR(path)); + goto err; + } + if (!opts.force_irmap) /* - * Always try to fetch watchee path first. There are several reasons: + * If we're not forced to do irmap, then + * say we have no path for watch. Otherwise + * do irmap scan even if the handle is + * working. * - * - tmpfs/devtmps do not save inode numbers between mounts, - * so it is critical to have the complete path under our - * hands for restore purpose; - * - * - in case of migration the inodes might be changed as well - * so the only portable solution is to carry the whole path - * to the watchee inside image. + * FIXME -- no need to open-by-handle if + * we are in force-irmap and not on tempfs */ - path = alloc_openable(s_dev, i_ino, f_handle); - if (!IS_ERR_OR_NULL(path)) - goto out; - else if (IS_ERR(path) && PTR_ERR(path) == -ENOMEM) - goto err; - - if ((mi->fstype->code == FSTYPE__TMPFS) || - (mi->fstype->code == FSTYPE__DEVTMPFS)) { - pr_err("Can't find suitable path for handle (dev %#x ino %#lx): %d\n", - s_dev, i_ino, (int)PTR_ERR(path)); - goto err; - } - - if (!opts.force_irmap) - /* - * If we're not forced to do irmap, then - * say we have no path for watch. Otherwise - * do irmap scan even if the handle is - * working. - * - * FIXME -- no need to open-by-handle if - * we are in force-irmap and not on tempfs - */ - goto out_nopath; - } + goto out_nopath; +fault: pr_warn("\tHandle 0x%x:0x%lx cannot be opened\n", s_dev, i_ino); irmap_path = irmap_lookup(s_dev, i_ino); if (!irmap_path) { @@ -298,10 +303,8 @@ int check_open_handle(unsigned int s_dev, unsigned long i_ino, pr_debug("\tDumping %s as path for handle\n", path); f_handle->path = path; out_nopath: - close_safe(&fd); return 0; err: - close_safe(&fd); return -1; } diff --git a/criu/image.c b/criu/image.c index 6ba2ffc7d0..84101b610f 100644 --- a/criu/image.c +++ b/criu/image.c @@ -149,8 +149,7 @@ InventoryEntry *get_parent_inventory(void) InventoryEntry *ie; int dir; - dir = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); - if (dir == -1) { + if (open_parent(get_service_fd(IMG_FD_OFF), &dir)) { /* * We print the warning below to be notified that we had some * unexpected problem on open. For instance we have a parent @@ -158,10 +157,11 @@ InventoryEntry *get_parent_inventory(void) * when also having no parent directory is an expected case of * first dump iteration. */ - if (errno != ENOENT) - pr_warn("Failed to open parent directory\n"); + pr_warn("Failed to open parent directory\n"); return NULL; } + if (dir < 0) + return NULL; img = open_image_at(dir, CR_FD_INVENTORY, O_RSTR); if (!img) { @@ -561,6 +561,11 @@ int open_image_dir(char *dir, int mode) if (img_streamer_init(dir, mode) < 0) goto err; } else if (opts.img_parent) { + if (faccessat(fd, opts.img_parent, R_OK, 0)) { + pr_perror("Invalid parent image directory provided"); + goto err; + } + ret = symlinkat(opts.img_parent, fd, CR_PARENT_LINK); if (ret < 0 && errno != EEXIST) { pr_perror("Can't link parent snapshot"); @@ -586,6 +591,26 @@ void close_image_dir(void) close_service_fd(IMG_FD_OFF); } +int open_parent(int dfd, int *pfd) +{ + struct stat st; + + *pfd = -1; + /* Check if the parent symlink exists */ + if (fstatat(dfd, CR_PARENT_LINK, &st, AT_SYMLINK_NOFOLLOW) && errno == ENOENT) { + pr_debug("No parent images directory provided\n"); + return 0; + } + + *pfd = openat(dfd, CR_PARENT_LINK, O_RDONLY); + if (*pfd < 0) { + pr_perror("Can't open parent path"); + return -1; + } + + return 0; +} + static unsigned long page_ids = 1; void up_page_ids_base(void) diff --git a/criu/include/fault-injection.h b/criu/include/fault-injection.h index 31fe161784..0fd9b512d9 100644 --- a/criu/include/fault-injection.h +++ b/criu/include/fault-injection.h @@ -18,6 +18,7 @@ enum faults { FI_PARTIAL_PAGES = 131, FI_HUGE_ANON_SHMEM_ID = 132, FI_CANNOT_MAP_VDSO = 133, + FI_CORRUPT_EXTREGS = 134, FI_MAX, }; diff --git a/criu/include/files-reg.h b/criu/include/files-reg.h index 016d76a9fc..0cb67c51ad 100644 --- a/criu/include/files-reg.h +++ b/criu/include/files-reg.h @@ -2,6 +2,7 @@ #define __CR_FILES_REG_H__ #include "files.h" +#include "util.h" #include "images/regfile.pb-c.h" #include "images/ghost-file.pb-c.h" @@ -51,7 +52,10 @@ extern void free_link_remaps(void); extern int prepare_remaps(void); extern int try_clean_remaps(bool only_ghosts); -extern int strip_deleted(struct fd_link *link); +static inline int link_strip_deleted(struct fd_link *link) +{ + return strip_deleted(link->name, link->len); +} extern int dead_pid_conflict(void); diff --git a/criu/include/image.h b/criu/include/image.h index 62c8d7ba0e..7c45381db3 100644 --- a/criu/include/image.h +++ b/criu/include/image.h @@ -147,6 +147,12 @@ extern off_t img_raw_size(struct cr_img *img); extern int open_image_dir(char *dir, int mode); extern void close_image_dir(void); +/* + * Return -1 -- parent symlink points to invalid target + * Return 0 && pfd < 0 -- parent symlink does not exist + * Return 0 && pfd >= 0 -- opened + */ +extern int open_parent(int dfd, int *pfd); extern struct cr_img *open_image_at(int dfd, int type, unsigned long flags, ...); #define open_image(typ, flags, ...) open_image_at(-1, typ, flags, ##__VA_ARGS__) diff --git a/criu/include/servicefd.h b/criu/include/servicefd.h index c11f89d373..b03bdaf59b 100644 --- a/criu/include/servicefd.h +++ b/criu/include/servicefd.h @@ -17,6 +17,7 @@ enum sfd_type { IMG_STREAMER_FD_OFF, PROC_FD_OFF, /* fd with /proc for all proc_ calls */ PROC_PID_FD_OFF, + PROC_SELF_FD_OFF, CR_PROC_FD_OFF, /* some other's proc fd: * - For dump -- target ns' proc * - For restore -- CRIU ns' proc @@ -44,6 +45,7 @@ extern bool is_service_fd(int fd, enum sfd_type type); extern int service_fd_min_fd(struct pstree_item *item); extern int install_service_fd(enum sfd_type type, int fd); extern int close_service_fd(enum sfd_type type); +extern void __close_service_fd(enum sfd_type type); extern int clone_service_fd(struct pstree_item *me); #endif /* __CR_SERVICE_FD_H__ */ diff --git a/criu/include/util.h b/criu/include/util.h index c2baf2788e..f000e97a74 100644 --- a/criu/include/util.h +++ b/criu/include/util.h @@ -180,7 +180,7 @@ extern int cr_daemon(int nochdir, int noclose, int close_fd); extern int status_ready(void); extern int is_root_user(void); -extern void set_proc_self_fd(int fd); +extern int set_proc_self_fd(int fd); static inline bool dir_dots(const struct dirent *de) { @@ -252,6 +252,9 @@ static inline bool issubpath(const char *path, const char *sub_path) (end == '/' || end == '\0'); } +int strip_deleted(char *path, int len); +int cut_path_ending(char *path, char *sub_path); + /* * mkdir -p */ diff --git a/criu/ipc_ns.c b/criu/ipc_ns.c index 915aea20b2..e7049599f9 100644 --- a/criu/ipc_ns.c +++ b/criu/ipc_ns.c @@ -760,6 +760,10 @@ static int restore_content(void *data, struct cr_img *img, const IpcShmEntry *sh ssize_t size, off; ifd = img_raw_fd(img); + if (ifd < 0) { + pr_err("Failed getting raw image fd\n"); + return -1; + } size = round_up(shm->size, sizeof(u32)); off = 0; do { diff --git a/criu/irmap.c b/criu/irmap.c index 58bc055d56..c9bfdbe223 100644 --- a/criu/irmap.c +++ b/criu/irmap.c @@ -425,12 +425,10 @@ static int open_irmap_cache(struct cr_img **img) close_image(*img); if (dir == AT_FDCWD) { pr_info("Searching irmap cache in parent\n"); - dir = openat(get_service_fd(IMG_FD_OFF), - CR_PARENT_LINK, O_RDONLY); + if (open_parent(get_service_fd(IMG_FD_OFF), &dir)) + return -1; if (dir >= 0) goto in; - if (errno != ENOENT) - return -1; } pr_info("No irmap cache\n"); diff --git a/criu/libnetlink.c b/criu/libnetlink.c index 18a323b8d3..f9da58e9a4 100644 --- a/criu/libnetlink.c +++ b/criu/libnetlink.c @@ -20,9 +20,9 @@ static int nlmsg_receive(char *buf, int len, if (hdr->nlmsg_seq != CR_NLMSG_SEQ) continue; if (hdr->nlmsg_type == NLMSG_DONE) { - int *len = (int *)NLMSG_DATA(hdr); - if (*len < 0) - return err_cb(*len, ns, arg); + int *length = (int *)NLMSG_DATA(hdr); + if (*length < 0) + return err_cb(*length, ns, arg); return 0; } if (hdr->nlmsg_type == NLMSG_ERROR) { diff --git a/criu/memfd.c b/criu/memfd.c index 4419b4bf53..48245eb752 100644 --- a/criu/memfd.c +++ b/criu/memfd.c @@ -157,7 +157,7 @@ static int dump_one_memfd(int lfd, u32 id, const struct fd_parms *p) } else link = p->link; - strip_deleted(link); + link_strip_deleted(link); /* link->name is always started with "." which has to be skipped. */ if (strncmp(link->name + 1, MEMFD_PREFIX, MEMFD_PREFIX_LEN) == 0) name = &link->name[1 + MEMFD_PREFIX_LEN]; @@ -332,7 +332,7 @@ int memfd_open(struct file_desc *d, u32 *fdflags) * O_LARGEFILE file flag with regular open(). It doesn't seem that * important though. */ - _fd = __open_proc(getpid(), 0, flags, "fd/%d", fd); + _fd = __open_proc(PROC_SELF, 0, flags, "fd/%d", fd); if (_fd < 0) { pr_perror("Can't reopen memfd id=%d", mfe->id); goto err; diff --git a/criu/mount.c b/criu/mount.c index 25ef7f09eb..d20054aef3 100644 --- a/criu/mount.c +++ b/criu/mount.c @@ -235,6 +235,7 @@ struct mount_info *lookup_mnt_sdev(unsigned int s_dev) if (m->s_dev == s_dev && mnt_is_dir(m)) return m; + pr_err("Unable to find suitable mount point for s_dev %x\n", s_dev); return NULL; } @@ -1016,12 +1017,12 @@ int mnt_is_dir(struct mount_info *pm) mntns_root = mntns_get_root_fd(pm->nsid); if (mntns_root < 0) { - pr_perror("Can't get root fd of mntns for %d", pm->mnt_id); + pr_warn("Can't get root fd of mntns for %d: %s\n", pm->mnt_id, strerror(errno)); return 0; } if (fstatat(mntns_root, pm->ns_mountpoint, &st, 0)) { - pr_perror("Can't fstatat on %s", pm->ns_mountpoint); + pr_warn("Can't fstatat on %s: %s\n", pm->ns_mountpoint, strerror(errno)); return 0; } @@ -1111,8 +1112,8 @@ static char *get_clean_mnt(struct mount_info *mi, char *mnt_path_tmp, char *mnt_ } if (mount(mi->mountpoint, mnt_path, NULL, MS_BIND, NULL)) { - pr_perror("Can't bind-mount %d:%s to %s", - mi->mnt_id, mi->mountpoint, mnt_path); + pr_warn("Can't bind-mount %d:%s to %s: %s\n", + mi->mnt_id, mi->mountpoint, mnt_path, strerror(errno)); rmdir(mnt_path); return NULL; } @@ -1188,6 +1189,7 @@ bool mnt_is_overmounted(struct mount_info *mi) static int set_is_overmounted(struct mount_info *mi) { + /* coverity[check_return] */ mnt_is_overmounted(mi); return 0; } @@ -1444,6 +1446,7 @@ int open_mountpoint(struct mount_info *pm) return __open_mountpoint(pm, fd); err: if (ns_old >= 0) + /* coverity[check_return] */ restore_ns(ns_old, &mnt_ns_desc); close_safe(&fd); if (fchdir(cwd_fd)) @@ -3508,6 +3511,7 @@ int prepare_mnt_ns(void) return ret; err: if (rst >= 0) + /* coverity[check_return] */ restore_ns(rst, &mnt_ns_desc); return -1; } diff --git a/criu/namespaces.c b/criu/namespaces.c index 796f412c56..aecc82f66e 100644 --- a/criu/namespaces.c +++ b/criu/namespaces.c @@ -247,11 +247,11 @@ int switch_ns(int pid, struct ns_desc *nd, int *rst) int switch_ns_by_fd(int nsfd, struct ns_desc *nd, int *rst) { - int ret = -1; + int ret = -1, old_ns = -1; if (rst) { - *rst = open_proc(PROC_SELF, "ns/%s", nd->str); - if (*rst < 0) + old_ns = open_proc(PROC_SELF, "ns/%s", nd->str); + if (old_ns < 0) goto err_ns; } @@ -261,11 +261,12 @@ int switch_ns_by_fd(int nsfd, struct ns_desc *nd, int *rst) goto err_set; } + if (rst) + *rst = old_ns; return 0; err_set: - if (rst) - close(*rst); + close_safe(&old_ns); err_ns: return -1; } @@ -537,7 +538,10 @@ static int open_ns_fd(struct file_desc *d, int *new_fd) else break; fd = fdstore_get(nsfd_id); - goto check_open; + if (fd < 0) { + return -1; + } + goto out; } /* @@ -597,12 +601,11 @@ static int open_ns_fd(struct file_desc *d, int *new_fd) path[sizeof(path) - 1] = '\0'; fd = open(path, nfi->nfe->flags); -check_open: if (fd < 0) { pr_perror("Can't open file %s on restore", path); return fd; } - +out: *new_fd = fd; return 0; } @@ -1080,21 +1083,18 @@ int dump_namespaces(struct pstree_item *item, unsigned int ns_flags) pr_info("Dumping %d(%d)'s namespaces\n", ns_pid->ns[0].virt, ns_pid->real); - if ((ns_flags & CLONE_NEWPID) && ns_pid->ns[0].virt != 1) { + if ((ns_flags & CLONE_NEWPID) && ns_pid->ns[0].virt != INIT_PID) { char *val = NULL; - for (ns = ns_ids; ns; ns = ns->next) { - if (ns->nd->cflag == CLONE_NEWPID) { - char id[64]; - snprintf(id, sizeof(id), "pid[%u]", ns->kid); - val = external_lookup_by_key(id); - if (IS_ERR_OR_NULL(val)) { - val = NULL; - continue; - } - if (val) - break; - } + + ns = lookup_ns_by_id(item->ids->pid_ns_id, &pid_ns_desc); + if (ns) { + char id[64]; + snprintf(id, sizeof(id), "pid[%u]", ns->kid); + val = external_lookup_by_key(id); + if (IS_ERR_OR_NULL(val)) + val = NULL; } + if (!val) { pr_err("Can't dump a pid namespace without the process init\n"); return -1; @@ -1163,11 +1163,22 @@ static int write_id_map(pid_t pid, UidGidExtent **extents, int n, char *id_map) * We can perform only a single write (that may contain multiple * newline-delimited records) to a uid_map and a gid_map files. */ - for (i = 0; i < n; i++) - off += snprintf(buf + off, sizeof(buf) - off, + for (i = 0; i < n; i++) { + int len; + + len = snprintf(buf + off, sizeof(buf) - off, "%u %u %u\n", extents[i]->first, extents[i]->lower_first, extents[i]->count); + if (len < 0) { + pr_perror("Unable to form the user/group mappings buffer"); + return -1; + } else if (len >= sizeof(buf) - off) { + pr_err("The user/group mappings buffer truncated\n"); + return -1; + } + off += len; + } fd = open_proc_rw(pid, "%s", id_map); if (fd < 0) @@ -1364,6 +1375,10 @@ int __userns_call(const char *func_name, uns_call_t call, int flags, return call(arg, fd, getpid()); sk = get_service_fd(USERNSD_SK); + if (sk < 0) { + pr_err("Cannot get USERNSD_SK fd\n"); + return -1; + } pr_debug("uns: calling %s (%d, %x)\n", func_name, fd, flags); if (!async) @@ -1799,17 +1814,8 @@ static int read_pid_ns_img(void) pr_err("Can not read pidns object\n"); return -1; } - if (ret > 0) { + if (ret > 0) ns->ext_key = e->ext_key; - /* - * Restoring into an existing PID namespace. This disables - * the check to require a PID 1 when restoring a process - * which used to be in a PID namespace. - * To keep the PID namespace code paths enabled this bit - * will be set after having clone()ed the process. - */ - root_ns_mask &= ~CLONE_NEWPID; - } } return 0; diff --git a/criu/net.c b/criu/net.c index 4f1f7d47f4..91990412f9 100644 --- a/criu/net.c +++ b/criu/net.c @@ -2930,6 +2930,7 @@ void network_unlock(void) rst_unlock_tcp_connections(); if (root_ns_mask & CLONE_NEWNET) { + /* coverity[check_return] */ run_scripts(ACT_NET_UNLOCK); network_unlock_internal(); } diff --git a/criu/page-xfer.c b/criu/page-xfer.c index 98e9c6b6e1..34f6a09678 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -364,10 +364,8 @@ static int open_page_local_xfer(struct page_xfer *xfer, int fd_type, unsigned lo return -1; xfer->pi = open_pages_image(O_DUMP, xfer->pmi, &pages_id); - if (!xfer->pi) { - close_image(xfer->pmi); - return -1; - } + if (!xfer->pi) + goto err_pmi; /* * Open page-read for parent images (if it exists). It will @@ -386,14 +384,15 @@ static int open_page_local_xfer(struct page_xfer *xfer, int fd_type, unsigned lo if (opts.stream) goto out; - pfd = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); - if (pfd < 0 && errno == ENOENT) + if (open_parent(get_service_fd(IMG_FD_OFF), &pfd)) + goto err_pi; + if (pfd < 0) goto out; xfer->parent = xmalloc(sizeof(*xfer->parent)); if (!xfer->parent) { close(pfd); - return -1; + goto err_pi; } ret = open_page_read_at(pfd, img_id, xfer->parent, pr_flags); @@ -412,6 +411,12 @@ static int open_page_local_xfer(struct page_xfer *xfer, int fd_type, unsigned lo xfer->write_pages = write_pages_loc; xfer->close = close_page_xfer; return 0; + +err_pi: + close_image(xfer->pi); +err_pmi: + close_image(xfer->pmi); + return -1; } int open_page_xfer(struct page_xfer *xfer, int fd_type, unsigned long img_id) @@ -936,8 +941,9 @@ int check_parent_local_xfer(int fd_type, unsigned long img_id) if (opts.stream) return 0; - pfd = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY); - if (pfd < 0 && errno == ENOENT) + if (open_parent(get_service_fd(IMG_FD_OFF), &pfd)) + return -1; + if (pfd < 0) return 0; snprintf(path, sizeof(path), imgset_template[fd_type].fmt, img_id); diff --git a/criu/pagemap.c b/criu/pagemap.c index 33bce10e37..1d849a1b99 100644 --- a/criu/pagemap.c +++ b/criu/pagemap.c @@ -242,10 +242,15 @@ static int read_parent_page(struct page_read *pr, unsigned long vaddr, static int read_local_page(struct page_read *pr, unsigned long vaddr, unsigned long len, void *buf) { - int fd = img_raw_fd(pr->pi); + int fd; ssize_t ret; size_t curr = 0; + fd = img_raw_fd(pr->pi); + if (fd < 0) { + pr_err("Failed getting raw image fd\n"); + return -1; + } /* * Flush any pending async requests if any not to break the * linear reading from the pages.img file. @@ -656,8 +661,9 @@ static int try_open_parent(int dfd, unsigned long id, struct page_read *pr, int if (opts.stream) goto out; - pfd = openat(dfd, CR_PARENT_LINK, O_RDONLY); - if (pfd < 0 && errno == ENOENT) + if (open_parent(dfd, &pfd)) + goto err; + if (pfd < 0) goto out; parent = xmalloc(sizeof(*parent)); @@ -682,6 +688,7 @@ static int try_open_parent(int dfd, unsigned long id, struct page_read *pr, int xfree(parent); err_cl: close(pfd); +err: return -1; } diff --git a/criu/parasite-syscall.c b/criu/parasite-syscall.c index c7074c7c46..04364e5bf1 100644 --- a/criu/parasite-syscall.c +++ b/criu/parasite-syscall.c @@ -156,6 +156,8 @@ int parasite_dump_thread_leader_seized(struct parasite_ctl *ctl, int pid, CoreEn return -1; } + compel_arch_get_tls_task(ctl, &args->tls); + return dump_thread_core(pid, core, args); } @@ -190,6 +192,14 @@ int parasite_dump_thread_seized(struct parasite_thread_ctl *tctl, goto err_rth; } + ret = compel_arch_fetch_thread_area(tctl); + if (ret) { + pr_err("Can't obtain thread area of %d\n", pid); + goto err_rth; + } + + compel_arch_get_tls_thread(tctl, &args->tls); + ret = compel_run_in_thread(tctl, PARASITE_CMD_DUMP_THREAD); if (ret) { pr_err("Can't init thread in parasite %d\n", pid); @@ -565,6 +575,8 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item, ictx->flags |= INFECT_COMPATIBLE; if (kdat.x86_has_ptrace_fpu_xsave_bug) ictx->flags |= INFECT_X86_PTRACE_MXCSR_BUG; + if (fault_injected(FI_CORRUPT_EXTREGS)) + ictx->flags |= INFECT_CORRUPT_EXTREGS; ictx->log_fd = log_get_fd(); diff --git a/criu/path.c b/criu/path.c index 22a89a4aa6..637d0fae2f 100644 --- a/criu/path.c +++ b/criu/path.c @@ -88,11 +88,11 @@ char *mnt_get_sibling_path(struct mount_info *m, len -= off; path += off; } else { - int len = strlen(cut_root); - if (strncmp(rpath, cut_root, len)) + int length = strlen(cut_root); + if (strncmp(rpath, cut_root, length)) return NULL; rpath += strlen(cut_root); - if (len > 0 && (rpath[0] && rpath[0] != '/')) + if (length > 0 && (rpath[0] && rpath[0] != '/')) return NULL; } if (rpath[0] == '/') diff --git a/criu/pie/util-vdso.c b/criu/pie/util-vdso.c index 58b27680c8..5e1f128e84 100644 --- a/criu/pie/util-vdso.c +++ b/criu/pie/util-vdso.c @@ -219,10 +219,12 @@ static void parse_elf_symbols(uintptr_t mem, size_t size, Phdr_t *load, struct vdso_symtable *t, uintptr_t dynsymbol_names, Hash_t *hash, Dyn_t *dyn_symtab) { + ARCH_VDSO_SYMBOLS_LIST + const char *vdso_symbols[VDSO_SYMBOL_MAX] = { ARCH_VDSO_SYMBOLS }; - const size_t vdso_symbol_length = sizeof(t->symbols[0].name); + const size_t vdso_symbol_length = sizeof(t->symbols[0].name) - 1; Hash_t nbucket, nchain; Hash_t *bucket, *chain; @@ -265,6 +267,7 @@ static void parse_elf_symbols(uintptr_t mem, size_t size, Phdr_t *load, if (std_strncmp(name, symbol, vdso_symbol_length)) continue; + /* XXX: provide strncpy() implementation for PIE */ memcpy(t->symbols[i].name, name, vdso_symbol_length); t->symbols[i].offset = (unsigned long)sym->st_value - load->p_vaddr; break; diff --git a/criu/plugin.c b/criu/plugin.c index b97d3763a9..f35a04fc95 100644 --- a/criu/plugin.c +++ b/criu/plugin.c @@ -243,7 +243,11 @@ int cr_plugin_init(int stage) if (len < 3 || strncmp(de->d_name + len - 3, ".so", 3)) continue; - snprintf(path, sizeof(path), "%s/%s", opts.libdir, de->d_name); + if (snprintf(path, sizeof(path), "%s/%s", opts.libdir, de->d_name) >= + sizeof(path)) { + pr_err("Unable to build plugin path\n"); + goto err; + } if (cr_lib_load(stage, path)) goto err; diff --git a/criu/proc_parse.c b/criu/proc_parse.c index ba60832f45..7b0b32e2e7 100644 --- a/criu/proc_parse.c +++ b/criu/proc_parse.c @@ -35,7 +35,6 @@ #include "seccomp.h" #include "string.h" #include "namespaces.h" -#include "files-reg.h" #include "cgroup.h" #include "cgroup-props.h" #include "timerfd.h" @@ -306,16 +305,15 @@ static int vma_get_mapfile_user(const char *fname, struct vma_area *vma, vfi_dev = makedev(vfi->dev_maj, vfi->dev_min); if (is_memfd(vfi_dev)) { - struct fd_link link; - link.len = strlen(fname); - strlcpy(link.name, fname, sizeof(link.name)); - strip_deleted(&link); + char tmp[PATH_MAX]; + strlcpy(tmp, fname, PATH_MAX); + strip_deleted(tmp, strlen(tmp)); /* * The error EPERM will be shown in the following pr_perror(). * It comes from the previous open() call. */ - pr_perror("Can't open mapped [%s]", link.name); + pr_perror("Can't open mapped [%s]", tmp); /* * TODO Perhaps we could do better than failing and dump the @@ -1058,6 +1056,7 @@ int parse_pid_status(pid_t pid, struct seize_task_status *ss, void *data) cr->s.sigpnd = 0; cr->s.shdpnd = 0; + cr->s.seccomp_mode = SECCOMP_MODE_DISABLED; if (bfdopenr(&f)) return -1; @@ -1351,10 +1350,10 @@ static void cure_path(char *path) static int parse_mountinfo_ent(char *str, struct mount_info *new, char **fsname) { - struct fd_link root_link; unsigned int kmaj, kmin; - int ret, n; + int ret, n, len; char *sub, *opt = NULL; + char link_path[PATH_MAX]; new->mountpoint = xmalloc(PATH_MAX); if (new->mountpoint == NULL) @@ -1371,14 +1370,14 @@ static int parse_mountinfo_ent(char *str, struct mount_info *new, char **fsname) cure_path(new->mountpoint); cure_path(new->root); - root_link.len = strlen(new->root); - if (root_link.len >= sizeof(root_link.name) - 1) { - pr_err("new root path (%s) exceeds %zu\n", new->root, sizeof(root_link.name)); + len = strlen(new->root); + if (len >= PATH_MAX - 1) { + pr_err("new root path (%s) exceeds %d\n", new->root, PATH_MAX); goto err; } - strcpy(root_link.name, new->root); - if (strip_deleted(&root_link)) { - strcpy(new->root, root_link.name); + strcpy(link_path, new->root); + if (strip_deleted(link_path, len)) { + strcpy(new->root, link_path); new->deleted = true; } diff --git a/criu/pstree.c b/criu/pstree.c index a876615fb0..3ca8908b47 100644 --- a/criu/pstree.c +++ b/criu/pstree.c @@ -339,6 +339,7 @@ static int prepare_pstree_for_shell_job(pid_t pid) pid_t current_gid = getpgid(pid); struct pstree_item *pi; + struct pid *tmp; pid_t old_sid; pid_t old_gid; @@ -346,6 +347,7 @@ static int prepare_pstree_for_shell_job(pid_t pid) if (!opts.shell_job) return 0; + /* root_item is a session leader */ if (root_item->sid == vpid(root_item)) return 0; @@ -367,23 +369,42 @@ static int prepare_pstree_for_shell_job(pid_t pid) */ old_sid = root_item->sid; + if (old_sid != current_sid) { + pr_info("Migrating process tree (SID %d->%d)\n", + old_sid, current_sid); + + tmp = pstree_pid_by_virt(current_sid); + if (tmp) { + pr_err("Current sid %d intersects with pid (%d) in images", + current_sid, tmp->state); + return -1; + } - pr_info("Migrating process tree (SID %d->%d)\n", - old_sid, current_sid); - - for_each_pstree_item(pi) { - if (pi->sid == old_sid) - pi->sid = current_sid; - } + for_each_pstree_item(pi) { + if (pi->sid == old_sid) + pi->sid = current_sid; + } - old_gid = root_item->pgid; - if (old_gid != vpid(root_item)) { if (lookup_create_item(current_sid) == NULL) return -1; + } + /* root_item is a group leader */ + if (root_item->pgid == vpid(root_item)) + return 0; + + old_gid = root_item->pgid; + if (old_gid != current_gid) { pr_info("Migrating process tree (GID %d->%d)\n", old_gid, current_gid); + tmp = pstree_pid_by_virt(current_gid); + if (tmp) { + pr_err("Current gid %d intersects with pid (%d) in images", + current_gid, tmp->state); + return -1; + } + for_each_pstree_item(pi) { if (pi->pgid == old_gid) pi->pgid = current_gid; @@ -674,12 +695,12 @@ static int prepare_pstree_ids(pid_t pid) leader = pstree_item_by_virt(item->sid); BUG_ON(leader == NULL); if (leader->pid->state != TASK_UNDEF) { - pid_t pid; + pid_t helper_pid; - pid = get_free_pid(); - if (pid < 0) + helper_pid = get_free_pid(); + if (helper_pid < 0) break; - helper = lookup_create_item(pid); + helper = lookup_create_item(helper_pid); if (helper == NULL) return -1; @@ -771,15 +792,15 @@ static int prepare_pstree_ids(pid_t pid) /* Add a process group leader if it is absent */ for_each_pstree_item(item) { - struct pid *pid; + struct pid *pgid; if (!item->pgid || vpid(item) == item->pgid) continue; - pid = pstree_pid_by_virt(item->pgid); - if (pid->state != TASK_UNDEF) { - BUG_ON(pid->state == TASK_THREAD); - rsti(item)->pgrp_leader = pid->item; + pgid = pstree_pid_by_virt(item->pgid); + if (pgid->state != TASK_UNDEF) { + BUG_ON(pgid->state == TASK_THREAD); + rsti(item)->pgrp_leader = pgid->item; continue; } @@ -791,7 +812,7 @@ static int prepare_pstree_ids(pid_t pid) if (current_pgid == item->pgid) continue; - helper = pid->item; + helper = pgid->item; helper->sid = item->sid; helper->pgid = item->pgid; @@ -906,6 +927,12 @@ static int prepare_pstree_kobj_ids(void) */ rsti(item)->clone_flags &= ~CLONE_NEWNS; + /** + * Only child reaper can clone with CLONE_NEWPID + */ + if (vpid(item) != INIT_PID) + rsti(item)->clone_flags &= ~CLONE_NEWPID; + cflags &= CLONE_ALLNS; if (item == root_item) { diff --git a/criu/servicefd.c b/criu/servicefd.c index 2533f61680..952410a8d3 100644 --- a/criu/servicefd.c +++ b/criu/servicefd.c @@ -46,6 +46,7 @@ const char *sfd_type_name(enum sfd_type type) [IMG_FD_OFF] = __stringify_1(IMG_FD_OFF), [PROC_FD_OFF] = __stringify_1(PROC_FD_OFF), [PROC_PID_FD_OFF] = __stringify_1(PROC_PID_FD_OFF), + [PROC_SELF_FD_OFF] = __stringify_1(PROC_SELF_FD_OFF), [CR_PROC_FD_OFF] = __stringify_1(CR_PROC_FD_OFF), [ROOT_FD_OFF] = __stringify_1(ROOT_FD_OFF), [CGROUP_YARD] = __stringify_1(CGROUP_YARD), @@ -206,6 +207,15 @@ int close_service_fd(enum sfd_type type) return 0; } +void __close_service_fd(enum sfd_type type) +{ + int fd; + + fd = __get_service_fd(type, service_fd_id); + close(fd); + clear_bit(type, sfd_map); +} + static int move_service_fd(struct pstree_item *me, int type, int new_id, int new_base) { int old = get_service_fd(type); @@ -290,7 +300,8 @@ int clone_service_fd(struct pstree_item *me) if (new_base == -1) return -1; - if (service_fd_base == new_base && service_fd_id == id) + + if (get_service_fd(LOG_FD_OFF) == new_base - LOG_FD_OFF - SERVICE_FD_MAX * id) return 0; /* Dup sfds in memmove() style: they may overlap */ diff --git a/criu/sk-unix.c b/criu/sk-unix.c index 00d09cc114..e589da8deb 100644 --- a/criu/sk-unix.c +++ b/criu/sk-unix.c @@ -131,7 +131,8 @@ static struct unix_sk_listen_icon *lookup_unix_listen_icons(unsigned int peer_in static void show_one_unix(char *act, const struct unix_sk_desc *sk) { pr_debug("\t%s: ino %u peer_ino %u family %4d type %4d state %2d name %s\n", - act, sk->sd.ino, sk->peer_ino, sk->sd.family, sk->type, sk->state, sk->name); + act, sk->sd.ino, sk->peer_ino, sk->sd.family, sk->type, sk->state, + sk->name ? : "null"); if (sk->nr_icons) { int i; @@ -559,7 +560,7 @@ const struct fdtype_ops unix_dump_ops = { .dump = dump_one_unix_fd, }; -static int unix_resolve_name(int lfd, uint32_t id, struct unix_sk_desc *d, +static int unix_resolve_name_old(int lfd, uint32_t id, struct unix_sk_desc *d, UnixSkEntry *ue, const struct fd_parms *p) { char *name = d->name; @@ -644,6 +645,84 @@ static int unix_resolve_name(int lfd, uint32_t id, struct unix_sk_desc *d, goto out; } +static int unix_resolve_name(int lfd, uint32_t id, struct unix_sk_desc *d, + UnixSkEntry *ue, const struct fd_parms *p) +{ + char *name = d->name; + char path[PATH_MAX], tmp[PATH_MAX]; + struct stat st; + int fd, proc_fd, mnt_id, ret; + + if (d->namelen == 0 || name[0] == '\0') + return 0; + + if (kdat.sk_unix_file && (root_ns_mask & CLONE_NEWNS)) { + if (get_mnt_id(lfd, &mnt_id)) + return -1; + ue->mnt_id = mnt_id; + ue->has_mnt_id = true; + } + + fd = ioctl(lfd, SIOCUNIXFILE); + if (fd < 0) { + pr_warn("Unable to get a socket file descriptor with SIOCUNIXFILE ioctl."); + goto fallback; + } + + ret = fstat(fd, &st); + if (ret) { + pr_perror("Unable to fstat socket fd"); + return -1; + } + d->mode = st.st_mode; + d->uid = st.st_uid; + d->gid = st.st_gid; + + proc_fd = get_service_fd(PROC_FD_OFF); + if (proc_fd < 0) { + pr_err("Unable to get service fd for proc\n"); + return -1; + } + + snprintf(tmp, sizeof(tmp), "self/fd/%d", fd); + ret = readlinkat(proc_fd, tmp, path, PATH_MAX); + if (ret < 0 && ret >= PATH_MAX) { + pr_perror("Unable to readlink %s", tmp); + goto out; + } + path[ret] = 0; + + d->deleted = strip_deleted(path, ret); + + if (name[0] != '/') { + ret = cut_path_ending(path, name); + if (ret) { + pr_err("Unable too resolve %s from %s\n", name, path); + goto out; + } + + ue->name_dir = xstrdup(path); + if (!ue->name_dir) { + ret = -ENOMEM; + goto out; + } + + pr_debug("Resolved socket relative name %s to %s/%s\n", name, ue->name_dir, name); + } + + ret = 0; +out: + close(fd); + return ret; + +fallback: + pr_warn("Trying to resolve unix socket with obsolete method"); + ret = unix_resolve_name_old(lfd, id, d, ue, p); + if (ret < 0) + pr_err("Unable to resolve unix socket name with obsolete method. Try a linux kernel newer than 4.10\n"); + return ret; +} + /* * Returns: < 0 on error, 0 if OK, 1 to skip the socket */ @@ -1170,6 +1249,7 @@ static int revert_unix_sk_cwd(struct unix_sk_info *ui, int *prev_cwd_fd, int *ro if (*ns_fd >= 0 && restore_ns(*ns_fd, &mnt_ns_desc)) ret = -1; if (*root_fd >= 0) { + /* coverity[chroot_call] */ if (fchdir(*root_fd) || chroot(".")) pr_perror("Can't revert root directory"); close_safe(root_fd); @@ -1210,7 +1290,6 @@ static int prep_unix_sk_cwd(struct unix_sk_info *ui, int *prev_cwd_fd, if (switch_ns_by_fd(ns_fd, &mnt_ns_desc, prev_mntns_fd)) return -1; - set_proc_self_fd(-1); close(ns_fd); } @@ -1248,6 +1327,7 @@ static int prep_unix_sk_cwd(struct unix_sk_info *ui, int *prev_cwd_fd, goto err; } close(fd); + /* coverity[chroot_call] */ if (chroot(".")) { pr_perror("Unable to change root directory"); goto err; @@ -1469,56 +1549,49 @@ static int bind_on_deleted(int sk, struct unix_sk_info *ui) addr.sun_family = AF_UNIX; memcpy(&addr.sun_path, ui->name, ui->ue->name.len); - ret = bind(sk, (struct sockaddr *)&addr, - sizeof(addr.sun_family) + ui->ue->name.len); - if (ret < 0) { + ret = access(ui->name, F_OK); + if (!ret) { + snprintf(path_parked, sizeof(path_parked), UNIX_GHOST_FMT, ui->name); /* - * In case if there some real living socket - * with same name just move it aside for a - * while, we will move it back once ghost - * socket is processed. + * In case if there exist any file with same name + * just move it aside for a while, we will move it back + * once ghost socket is processed. */ - if (errno == EADDRINUSE) { - snprintf(path_parked, sizeof(path_parked), UNIX_GHOST_FMT, ui->name); - /* - * Say previous restore get killed in a middle due to - * any reason, be ready the file might already exist, - * clean it up. - */ - if (unlinkat(AT_FDCWD, path_parked, 0) == 0) - pr_debug("ghost: Unlinked stale socket id %#x ino %u name %s\n", - ui->ue->id, ui->ue->ino, path_parked); - if (rename(ui->name, path_parked)) { - ret = -errno; - pr_perror("ghost: Can't rename id %#x ino %u addr %s -> %s", - ui->ue->id, ui->ue->ino, ui->name, path_parked); - return ret; - } - pr_debug("ghost: id %#x ino %u renamed %s -> %s\n", - ui->ue->id, ui->ue->ino, ui->name, path_parked); - renamed = true; - ret = bind(sk, (struct sockaddr *)&addr, - sizeof(addr.sun_family) + ui->ue->name.len); - } - if (ret < 0) { + if (unlinkat(AT_FDCWD, path_parked, 0) == 0) + pr_debug("ghost: Unlinked stale socket id %#x ino %d name %s\n", + ui->ue->id, ui->ue->ino, path_parked); + if (rename(ui->name, path_parked)) { ret = -errno; - pr_perror("ghost: Can't bind on socket id %#x ino %u addr %s", - ui->ue->id, ui->ue->ino, ui->name); + pr_perror("ghost: Can't rename id %#x ino %u addr %s -> %s", + ui->ue->id, ui->ue->ino, ui->name, path_parked); return ret; } + pr_debug("ghost: id %#x ino %d renamed %s -> %s\n", + ui->ue->id, ui->ue->ino, ui->name, path_parked); + renamed = true; + } + + ret = bind(sk, (struct sockaddr *)&addr, + sizeof(addr.sun_family) + ui->ue->name.len); + if (ret < 0) { + ret = -errno; + pr_perror("ghost: Can't bind on socket id %#x ino %d addr %s", + ui->ue->id, ui->ue->ino, ui->name); + goto out_rename; } ret = restore_file_perms(ui); if (ret < 0) - return ret; + goto out; ret = keep_deleted(ui); if (ret < 0) { pr_err("ghost: Can't save socket %#x ino %u addr %s into fdstore\n", ui->ue->id, ui->ue->ino, ui->name); - return -EIO; + ret = -EIO; } +out: /* * Once everything is ready, just remove the socket from the * filesystem and rename back the original one if it were here. @@ -1528,19 +1601,18 @@ static int bind_on_deleted(int sk, struct unix_sk_info *ui) ret = -errno; pr_perror("ghost: Can't unlink socket %#x ino %u addr %s", ui->ue->id, ui->ue->ino, ui->name); - return ret; } +out_rename: if (renamed) { if (rename(path_parked, ui->name)) { ret = -errno; pr_perror("ghost: Can't rename id %#x ino %u addr %s -> %s", ui->ue->id, ui->ue->ino, path_parked, ui->name); - return ret; + } else { + pr_debug("ghost: id %#x ino %d renamed %s -> %s\n", + ui->ue->id, ui->ue->ino, path_parked, ui->name); } - - pr_debug("ghost: id %#x ino %u renamed %s -> %s\n", - ui->ue->id, ui->ue->ino, path_parked, ui->name); } /* @@ -1987,6 +2059,7 @@ static struct file_desc_ops unix_desc_ops = { static int unlink_sk(struct unix_sk_info *ui) { int ret = 0, cwd_fd = -1, root_fd = -1, ns_fd = -1; + struct stat statbuf; if (!ui->name || ui->name[0] == '\0' || (ui->ue->uflags & USK_EXTERN)) return 0; @@ -1994,6 +2067,15 @@ static int unlink_sk(struct unix_sk_info *ui) if (prep_unix_sk_cwd(ui, &cwd_fd, &root_fd, NULL)) return -1; + ret = stat(ui->name, &statbuf); + if (!ret) { + /* Do not cleanup non-socket files */ + if (!S_ISSOCK(statbuf.st_mode)) { + ret = 0; + goto out; + } + } + ret = unlinkat(AT_FDCWD, ui->name, 0) ? -1 : 0; if (ret < 0 && errno != ENOENT) { pr_warn("Can't unlink socket %u peer %u (name %s dir %s)\n", @@ -2118,10 +2200,10 @@ static int collect_one_unixsk(void *o, ProtobufCMessage *base, struct cr_img *i) if (memrchr(uname, 0, ulen)) { /* replace zero characters */ char *s = alloca(ulen + 1); - int i; + int j; - for (i = 0; i < ulen; i++) - s[i] = uname[i] ? : '@'; + for (j = 0; j < ulen; j++) + s[j] = uname[j] ? : '@'; uname = s; } } else if (ulen == 0) { diff --git a/criu/stats.c b/criu/stats.c index 891c378000..ed5a191415 100644 --- a/criu/stats.c +++ b/criu/stats.c @@ -102,6 +102,10 @@ void timing_stop(int t) struct timing *tm; struct timeval now; + /* stats haven't been initialized. */ + if (!dstats && !rstats) + return; + tm = get_timing(t); gettimeofday(&now, NULL); timeval_accumulate(&tm->start, &now, &tm->total); diff --git a/criu/tls.c b/criu/tls.c index f7b94dee8c..e865368c4c 100644 --- a/criu/tls.c +++ b/criu/tls.c @@ -303,13 +303,25 @@ static int tls_x509_setup_creds(void) static ssize_t _tls_push_cb(void *p, const void* data, size_t sz) { int fd = *(int *)(p); - return send(fd, data, sz, tls_sk_flags); + int ret = send(fd, data, sz, tls_sk_flags); + if (ret < 0 && errno != EAGAIN) { + int _errno = errno; + pr_perror("Push callback send failed"); + errno = _errno; + } + return ret; } static ssize_t _tls_pull_cb(void *p, void* data, size_t sz) { int fd = *(int *)(p); - return recv(fd, data, sz, tls_sk_flags); + int ret = recv(fd, data, sz, tls_sk_flags); + if (ret < 0 && errno != EAGAIN) { + int _errno = errno; + pr_perror("Pull callback recv failed"); + errno = _errno; + } + return ret; } static int tls_x509_setup_session(unsigned int flags) diff --git a/criu/uffd.c b/criu/uffd.c index a6886ca108..e460423445 100644 --- a/criu/uffd.c +++ b/criu/uffd.c @@ -1131,6 +1131,7 @@ static int handle_fork(struct lazy_pages_info *parent_lpi, struct uffd_msg *msg) static int complete_forks(int epollfd, struct epoll_event **events, int *nr_fds) { struct lazy_pages_info *lpi, *n; + struct epoll_event *tmp; if (list_empty(&pending_lpis)) return 1; @@ -1138,9 +1139,10 @@ static int complete_forks(int epollfd, struct epoll_event **events, int *nr_fds) list_for_each_entry(lpi, &pending_lpis, l) (*nr_fds)++; - *events = xrealloc(*events, sizeof(struct epoll_event) * (*nr_fds)); - if (!*events) + tmp = xrealloc(*events, sizeof(struct epoll_event) * (*nr_fds)); + if (!tmp) return -1; + *events = tmp; list_for_each_entry_safe(lpi, n, &pending_lpis, l) { if (epoll_add_rfd(epollfd, &lpi->lpfd)) @@ -1207,17 +1209,20 @@ static int handle_uffd_event(struct epoll_rfd *lpfd) lpi = container_of(lpfd, struct lazy_pages_info, lpfd); ret = read(lpfd->fd, &msg, sizeof(msg)); - if (!ret) - return 1; - - if (ret != sizeof(msg)) { + if (ret < 0) { /* we've already handled the page fault for another thread */ if (errno == EAGAIN) return 0; - if (ret < 0) - lp_perror(lpi, "Can't read uffd message"); - else - lp_err(lpi, "Can't read uffd message: short read"); + if (errno == EBADF && lpi->exited) { + lp_debug(lpi, "excess message in queue: %d", msg.event); + return 0; + } + lp_perror(lpi, "Can't read uffd message"); + return -1; + } else if (ret == 0) { + return 1; + } else if (ret != sizeof(msg)) { + lp_err(lpi, "Can't read uffd message: short read"); return -1; } @@ -1254,18 +1259,18 @@ static void lazy_pages_summary(struct lazy_pages_info *lpi) #endif } -static int handle_requests(int epollfd, struct epoll_event *events, int nr_fds) +static int handle_requests(int epollfd, struct epoll_event **events, int nr_fds) { struct lazy_pages_info *lpi, *n; int poll_timeout = -1; int ret; for (;;) { - ret = epoll_run_rfds(epollfd, events, nr_fds, poll_timeout); + ret = epoll_run_rfds(epollfd, *events, nr_fds, poll_timeout); if (ret < 0) goto out; if (ret > 0) { - ret = complete_forks(epollfd, &events, &nr_fds); + ret = complete_forks(epollfd, events, &nr_fds); if (ret < 0) goto out; if (restore_finished) @@ -1481,7 +1486,7 @@ int cr_lazy_pages(bool daemon) } } - ret = handle_requests(epollfd, events, nr_fds); + ret = handle_requests(epollfd, &events, nr_fds); tls_terminate_session(); diff --git a/criu/util.c b/criu/util.c index 27751880c8..76624dc5ac 100644 --- a/criu/util.c +++ b/criu/util.c @@ -281,15 +281,18 @@ int move_fd_from(int *img_fd, int want_fd) static pid_t open_proc_pid = PROC_NONE; static pid_t open_proc_self_pid; -static int open_proc_self_fd = -1; -void set_proc_self_fd(int fd) +int set_proc_self_fd(int fd) { - if (open_proc_self_fd >= 0) - close(open_proc_self_fd); + int ret; + + if (fd < 0) + return close_service_fd(PROC_SELF_FD_OFF); - open_proc_self_fd = fd; open_proc_self_pid = getpid(); + ret = install_service_fd(PROC_SELF_FD_OFF, fd); + + return ret; } static inline int set_proc_pid_fd(int pid, int fd) @@ -308,10 +311,18 @@ static inline int set_proc_pid_fd(int pid, int fd) static inline int get_proc_fd(int pid) { if (pid == PROC_SELF) { - if (open_proc_self_fd != -1 && open_proc_self_pid != getpid()) { - close(open_proc_self_fd); + int open_proc_self_fd; + + open_proc_self_fd = get_service_fd(PROC_SELF_FD_OFF); + /** + * FIXME in case two processes from different pidnses have the + * same pid from getpid() and one inherited service fds from + * another or they share them by shared fdt - this check will + * not detect that one of them reuses /proc/self of another. + * Everything proc related may break in this case. + */ + if (open_proc_self_fd >= 0 && open_proc_self_pid != getpid()) open_proc_self_fd = -1; - } return open_proc_self_fd; } else if (pid == open_proc_pid) return get_service_fd(PROC_PID_FD_OFF); @@ -402,7 +413,7 @@ inline int open_pid_proc(pid_t pid) } if (pid == PROC_SELF) - set_proc_self_fd(fd); + fd = set_proc_self_fd(fd); else fd = set_proc_pid_fd(pid, fd); @@ -534,6 +545,7 @@ static int close_fds(int minfd) continue; if (fd < minfd) continue; + /* coverity[double_close] */ close(fd); } closedir(dir); @@ -1311,6 +1323,7 @@ int epoll_prepare(int nr_fds, struct epoll_event **events) free_events: xfree(*events); + *events = NULL; return -1; } @@ -1401,3 +1414,76 @@ int mount_detached_fs(const char *fsname) return fd; } +int strip_deleted(char *name, int len) +{ + struct dcache_prepends { + const char *str; + size_t len; + } static const prepends[] = { + { + .str = " (deleted)", + .len = 10, + }, { + .str = "//deleted", + .len = 9, + } + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(prepends); i++) { + size_t at; + + if (len <= prepends[i].len) + continue; + + at = len - prepends[i].len; + if (!strcmp(&name[at], prepends[i].str)) { + pr_debug("Strip '%s' tag from '%s'\n", + prepends[i].str, name); + name[at] = '\0'; + len -= prepends[i].len; + return 1; + } + } + return 0; +} + +/* + * This function check if path ends with ending and cuts it from path. + * Return 0 if path is cut. -1 otherwise, leaving path unchanged. + * Example: + * path = "/foo/bar", ending = "bar" + * cut(path, ending) -> path becomes "/foo" + * + * 1. Skip leading "./" in subpath. + * 2. Respect root: ("/a/b", "b") -> "/a" but ("/a", "a") -> "/" + * 3. Refuse to cut identical strings, e.g. ("abc", "abc") will result in -1 + * 4. Do not handle "..", "//", "./" (with exception "./" as leading symbols) + */ + +int cut_path_ending(char *path, char *ending) +{ + int ending_pos; + + if (ending[0] == '.' && ending[1] == '/') + ending = ending + 2; + + ending_pos = strlen(path) - strlen(ending); + + if (ending_pos < 1) + return -1; + + if (strcmp(path + ending_pos, ending)) + return -1; + + if (path[ending_pos - 1] != '/') + return -1; + + if (ending_pos == 1) { + path[ending_pos] = 0; + return 0; + } + + path[ending_pos - 1] = 0; + return 0; +} \ No newline at end of file diff --git a/criu/vdso-compat.c b/criu/vdso-compat.c index a68c0bacea..8f5d19e102 100644 --- a/criu/vdso-compat.c +++ b/criu/vdso-compat.c @@ -13,6 +13,7 @@ static void exit_on(int ret, int err_fd, char *reason) if (ret) { syscall(__NR_write, err_fd, reason, strlen(reason)); syscall(__NR_exit, ret); + __builtin_unreachable(); } } /* diff --git a/flog/Makefile b/flog/Makefile new file mode 100644 index 0000000000..12255af719 --- /dev/null +++ b/flog/Makefile @@ -0,0 +1,29 @@ +OPTS=-ggdb3 -Wall -Werror +export OPTS + +CFLAGS += -iquote include +CFLAGS += -iquote flog/include +CFLAGS += -iquote flog/include/uapi + +include $(__nmk_dir)msg.mk + +$(eval $(call gen-built-in,src)) + +flog: + $(Q) $(MAKE) $(build)=$(obj)/src all +.PHONY: flog + +clean-flog: + $(call msg-gen, $@) + $(Q) $(MAKE) $(build)=$(obj)/src clean + $(Q) $(RM) built-in.o +.PHONY: clean-flog + +clean: clean-flog +mrproper: clean + +test: + ./tests/test00 + +all-y += flog + diff --git a/flog/built-in.S b/flog/built-in.S new file mode 100644 index 0000000000..26627d0544 --- /dev/null +++ b/flog/built-in.S @@ -0,0 +1,4 @@ +SECTIONS +{ + .rodata : { _rodata_start = . ; *(.rodata*) ; _rodata_end = . ;} +} diff --git a/flog/include/compiler.h b/flog/include/compiler.h new file mode 100644 index 0000000000..3e56eb0e64 --- /dev/null +++ b/flog/include/compiler.h @@ -0,0 +1,71 @@ +#ifndef __COMPILER_H__ +#define __COMPILER_H__ + +/* + * Various definitions for success build, + * picked from various places, mostly from + * the linux kernel. + */ + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x) + +#define NORETURN __attribute__((__noreturn__)) +#define __packed __attribute__((__packed__)) +#define __used __attribute__((__used__)) +#define __maybe_unused __attribute__((unused)) +#define __always_unused __attribute__((unused)) + +#define __section(S) __attribute__ ((__section__(#S))) + +#ifndef __always_inline +# define __always_inline inline __attribute__((always_inline)) +#endif + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#ifndef always_inline +# define always_inline __always_inline +#endif + +#ifndef noinline +# define noinline __attribute__((noinline)) +#endif + +#define __aligned(x) __attribute__((aligned(x))) + +#ifndef offsetof +# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define barrier() asm volatile("" ::: "memory") + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define __round_mask(x, y) ((__typeof__(x))((y) - 1)) +#define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +#define min(x, y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) + +#define max(x, y) ({ \ + typeof(x) _max1 = (x); \ + typeof(y) _max2 = (y); \ + (void) (&_max1 == &_max2); \ + _max1 > _max2 ? _max1 : _max2; }) + +#define is_log2(v) (((v) & ((v) - 1)) == 0) + +#endif /* __COMPILER_H__ */ diff --git a/flog/include/flog.h b/flog/include/flog.h new file mode 100644 index 0000000000..f00c20541f --- /dev/null +++ b/flog/include/flog.h @@ -0,0 +1,9 @@ +#ifndef __FLOG_H__ +#define __FLOG_H__ + +#include +#include + +#include "uapi/flog.h" + +#endif /* __FLOG_H__ */ diff --git a/flog/include/log.h b/flog/include/log.h new file mode 100644 index 0000000000..1a165ea9fb --- /dev/null +++ b/flog/include/log.h @@ -0,0 +1,17 @@ +#ifndef __LOG_H__ +#define __LOG_H__ + +#include + +#define pr_out(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__) + +#if 1 +# define pr_debug(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) +#else +# define pr_debug(fmt, ...) +#endif + +#define pr_err(fmt, ...) fprintf(stderr, "Error (%s:%d): "fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#define pr_perror(fmt, ...) fprintf(stderr, "Error (%s:%d): "fmt "%m\n", __FILE__, __LINE__, ##__VA_ARGS__) + +#endif /* __LOG_H__ */ diff --git a/flog/include/types.h b/flog/include/types.h new file mode 100644 index 0000000000..0e15bfbff5 --- /dev/null +++ b/flog/include/types.h @@ -0,0 +1,16 @@ +#ifndef __FLOG_TYPES_H__ +#define __FLOG_TYPES_H__ + +#include +#include + +typedef uint64_t u64; +typedef int64_t s64; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint8_t u8; +typedef int8_t s8; + +#endif /* __FLOG_TYPES_H__ */ diff --git a/flog/include/uapi/flog.h b/flog/include/uapi/flog.h new file mode 100644 index 0000000000..2d879110fc --- /dev/null +++ b/flog/include/uapi/flog.h @@ -0,0 +1,149 @@ +#ifndef __UAPI_FLOG_H__ +#define __UAPI_FLOG_H__ + +#include +#include +#include + +/* + * We work with up to 32 arguments in macros here. + * If more provided -- behaviour is undefined. + */ + +/* + * By Laurent Deniau at https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s + */ +#define FLOG_PP_NARG_(...) FLOG_PP_ARG_N(__VA_ARGS__) +#define FLOG_PP_NARG(...) FLOG_PP_NARG_(1, ##__VA_ARGS__, FLOG_PP_RSEQ_N()) + +#define FLOG_PP_ARG_N( _0, _1, _2, _3, _4, \ + _5, _6, _7, _8, _9, \ + _10,_11,_12,_13,_14, \ + _15,_16,_17,_18,_19, \ + _20,_21,_22,_23,_24, \ + _25,_26,_27,_28,_29, \ + _30,_31, N, ...) N + +#define FLOG_PP_RSEQ_N() \ + 31, 30, 29, 28, 27, \ + 26, 25, 24, 23, 22, \ + 21, 20, 19, 18, 17, \ + 16, 15, 14, 13, 12, \ + 11, 10, 9, 8, 7, \ + 6, 5, 4, 3, 2, \ + 1, 0 + +#define FLOG_GENMASK_0(N, x) 0 +#define FLOG_GENMASK_1(N, op, x, ...) (op(N, 0, x)) +#define FLOG_GENMASK_2(N, op, x, ...) ((op(N, 1, x)) | FLOG_GENMASK_1(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_3(N, op, x, ...) ((op(N, 2, x)) | FLOG_GENMASK_2(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_4(N, op, x, ...) ((op(N, 3, x)) | FLOG_GENMASK_3(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_5(N, op, x, ...) ((op(N, 4, x)) | FLOG_GENMASK_4(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_6(N, op, x, ...) ((op(N, 5, x)) | FLOG_GENMASK_5(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_7(N, op, x, ...) ((op(N, 6, x)) | FLOG_GENMASK_6(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_8(N, op, x, ...) ((op(N, 7, x)) | FLOG_GENMASK_7(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_9(N, op, x, ...) ((op(N, 8, x)) | FLOG_GENMASK_8(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_10(N, op, x, ...) ((op(N, 9, x)) | FLOG_GENMASK_9(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_11(N, op, x, ...) ((op(N, 10, x)) | FLOG_GENMASK_10(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_12(N, op, x, ...) ((op(N, 11, x)) | FLOG_GENMASK_11(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_13(N, op, x, ...) ((op(N, 12, x)) | FLOG_GENMASK_12(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_14(N, op, x, ...) ((op(N, 13, x)) | FLOG_GENMASK_13(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_15(N, op, x, ...) ((op(N, 14, x)) | FLOG_GENMASK_14(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_16(N, op, x, ...) ((op(N, 15, x)) | FLOG_GENMASK_15(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_17(N, op, x, ...) ((op(N, 16, x)) | FLOG_GENMASK_16(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_18(N, op, x, ...) ((op(N, 17, x)) | FLOG_GENMASK_17(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_19(N, op, x, ...) ((op(N, 18, x)) | FLOG_GENMASK_18(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_20(N, op, x, ...) ((op(N, 19, x)) | FLOG_GENMASK_19(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_21(N, op, x, ...) ((op(N, 20, x)) | FLOG_GENMASK_20(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_22(N, op, x, ...) ((op(N, 21, x)) | FLOG_GENMASK_21(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_23(N, op, x, ...) ((op(N, 22, x)) | FLOG_GENMASK_22(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_24(N, op, x, ...) ((op(N, 23, x)) | FLOG_GENMASK_23(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_25(N, op, x, ...) ((op(N, 24, x)) | FLOG_GENMASK_24(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_26(N, op, x, ...) ((op(N, 25, x)) | FLOG_GENMASK_25(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_27(N, op, x, ...) ((op(N, 26, x)) | FLOG_GENMASK_26(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_28(N, op, x, ...) ((op(N, 27, x)) | FLOG_GENMASK_27(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_29(N, op, x, ...) ((op(N, 28, x)) | FLOG_GENMASK_28(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_30(N, op, x, ...) ((op(N, 29, x)) | FLOG_GENMASK_29(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_31(N, op, x, ...) ((op(N, 30, x)) | FLOG_GENMASK_30(N, op, __VA_ARGS__)) +#define FLOG_GENMASK_32(N, op, x, ...) ((op(N, 31, x)) | FLOG_GENMASK_31(N, op, __VA_ARGS__)) + +#define FLOG_CONCAT(arg1, arg2) FLOG_CONCAT1(arg1, arg2) +#define FLOG_CONCAT1(arg1, arg2) FLOG_CONCAT2(arg1, arg2) +#define FLOG_CONCAT2(arg1, arg2) arg1##arg2 + +#define FLOG_GENMASK_(N, op, ...) FLOG_CONCAT(FLOG_GENMASK_, N)(N, op, ##__VA_ARGS__) +#define FLOG_GENMASK(op, ...) FLOG_GENMASK_(FLOG_PP_NARG(__VA_ARGS__), op, ##__VA_ARGS__) + +#define flog_genbit(ord, n, v, ...) \ + _Generic((v), \ + \ + /* Basic types */ \ + char: 0, \ + signed char: 0, \ + unsigned char: 0, \ + signed short int: 0, \ + unsigned short int: 0, \ + signed int: 0, \ + unsigned int: 0, \ + signed long: 0, \ + unsigned long: 0, \ + signed long long: 0, \ + unsigned long long: 0, \ + \ + /* Not used for a while */ \ + /* float: 12, */ \ + /* double: 13, */ \ + /* long double: 14, */ \ + \ + /* Basic poniters */ \ + char *: (1u << (ord - n - 1)), \ + signed char *: (1u << (ord - n - 1)), \ + unsigned char *: (1u << (ord - n - 1)), \ + signed short int *: 0, \ + unsigned short int *: 0, \ + signed int *: 0, \ + unsigned int *: 0, \ + signed long *: 0, \ + unsigned long *: 0, \ + signed long long *: 0, \ + unsigned long long *: 0, \ + void *: 0, \ + \ + /* Const basic pointers */ \ + const char *: (1u << (ord - n - 1)), \ + const signed char *: (1u << (ord - n - 1)), \ + const unsigned char *: (1u << (ord - n - 1)), \ + const signed short int *: 0, \ + const unsigned short int *: 0, \ + const signed int *: 0, \ + const unsigned int *: 0, \ + const signed long *: 0, \ + const unsigned long *: 0, \ + const signed long long *: 0, \ + const unsigned long long *: 0, \ + const void *: 0, \ + \ + /* Systypes and pointers */ \ + default: -1) + +typedef struct { + unsigned int magic; + unsigned int size; + unsigned int nargs; + unsigned int mask; + long fmt; + long args[0]; +} flog_msg_t; + +extern int flog_encode_msg(int fdout, unsigned int nargs, unsigned int mask, const char *format, ...); +void flog_decode_msg(int fdout, const char *format, ...); +extern int flog_decode_all(int fdin, int fdout); + +#define flog_encode(fdout, fmt, ...) \ + flog_encode_msg(fdout, FLOG_PP_NARG(__VA_ARGS__), \ + FLOG_GENMASK(flog_genbit, ##__VA_ARGS__), fmt, ##__VA_ARGS__) + +int flog_map_buf(int fdout); +int flog_close(int fdout); + +#endif /* __UAPI_FLOG_H__ */ diff --git a/flog/include/util.h b/flog/include/util.h new file mode 100644 index 0000000000..17a4d77997 --- /dev/null +++ b/flog/include/util.h @@ -0,0 +1,37 @@ +#ifndef __UTIL_H__ +#define __UTIL_H__ + +#include +#include + +#include "log.h" +#include "types.h" + +#define __xalloc(op, size, ...) \ + ({ \ + void *___p = op(__VA_ARGS__); \ + ___p; \ + }) + +#define xstrdup(str) __xalloc(strdup, strlen(str) + 1, str) +#define xmalloc(size) __xalloc(malloc, size, size) +#define xzalloc(size) __xalloc(calloc, size, 1, size) +#define xrealloc(p, size) __xalloc(realloc, size, p, size) + +#define xfree(p) do { if (p) free(p); } while (0) + +#define xrealloc_safe(pptr, size) \ + ({ \ + int __ret = -ENOMEM; \ + void *new = xrealloc(*pptr, size); \ + if (new) { \ + *pptr = new; \ + __ret = 0; \ + } \ + __ret; \ + }) + +#define memzero_p(p) memset(p, 0, sizeof(*p)) +#define memzero(p, size) memset(p, 0, size) + +#endif /* __UTIL_H__ */ diff --git a/flog/src/Makefile b/flog/src/Makefile new file mode 100644 index 0000000000..ee73ea7252 --- /dev/null +++ b/flog/src/Makefile @@ -0,0 +1,5 @@ +ccflags-y += -DCONFIG_X86_64 -iquote ./include $(OPTS) +ldflags-y += -r + +#obj-y += main.o +obj-y += flog.o diff --git a/flog/src/flog.c b/flog/src/flog.c new file mode 100644 index 0000000000..40cce3fedc --- /dev/null +++ b/flog/src/flog.c @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include + +//#include + +#include "uapi/flog.h" +#include "util.h" + +#define MAGIC 0xABCDABCD + +#define BUF_SIZE (1<<20) +static char _mbuf[BUF_SIZE]; +static char *mbuf = _mbuf; +static char *fbuf; +static uint64_t fsize; +static uint64_t mbuf_size = sizeof(_mbuf); + +/*int flog_decode_all(int fdin, int fdout) +{ + flog_msg_t *m = (void *)mbuf; + ffi_type *args[34] = { + [0] = &ffi_type_sint, + [1] = &ffi_type_pointer, + [2 ... 33] = &ffi_type_slong + }; + void *values[34]; + ffi_cif cif; + ffi_arg rc; + size_t i, ret; + char *fmt; + + values[0] = (void *)&fdout; + + while (1) { + ret = read(fdin, mbuf, sizeof(m)); + if (ret == 0) + break; + if (ret < 0) { + fprintf(stderr, "Unable to read a message: %m"); + return -1; + } + if (m->magic != MAGIC) { + fprintf(stderr, "The log file was not properly closed\n"); + break; + } + ret = m->size - sizeof(m); + if (m->size > mbuf_size) { + fprintf(stderr, "The buffer is too small"); + return -1; + } + if (read(fdin, mbuf + sizeof(m), ret) != ret) { + fprintf(stderr, "Unable to read a message: %m"); + return -1; + } + + fmt = mbuf + m->fmt; + values[1] = &fmt; + + for (i = 0; i < m->nargs; i++) { + values[i + 2] = (void *)&m->args[i]; + if (m->mask & (1u << i)) { + m->args[i] = (long)(mbuf + m->args[i]); + } + } + + if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, m->nargs + 2, + &ffi_type_sint, args) == FFI_OK) + ffi_call(&cif, FFI_FN(dprintf), &rc, values); + } + return 0; +}*/ + +static int flog_enqueue(flog_msg_t *m) +{ + if (write(1, m, m->size) != m->size) { + fprintf(stderr, "Unable to write a message\n"); + return -1; + } + return 0; +} + +/*extern char *rodata_start; +extern char *rodata_end; +*/ +/* Pre-allocate a buffer in a file and map it into memory. */ +int flog_map_buf(int fdout) +{ + uint64_t off = 0; + void *addr; + + /* + * Two buffers are mmaped into memory. A new one is mapped when a first + * one is completly filled. + */ + if (fbuf && (mbuf - fbuf < BUF_SIZE)) + return 0; + + if (fbuf) { + if (munmap(fbuf, BUF_SIZE * 2)) { + fprintf(stderr, "Unable to unmap a buffer: %m"); + return -1; + } + off = mbuf - fbuf - BUF_SIZE; + fbuf = NULL; + } + + if (fsize == 0) + fsize += BUF_SIZE; + fsize += BUF_SIZE; + + if (ftruncate(fdout, fsize)) { + fprintf(stderr, "Unable to truncate a file: %m"); + return -1; + } + + if (!fbuf) + addr = mmap(NULL, BUF_SIZE * 2, PROT_WRITE | PROT_READ, + MAP_FILE | MAP_SHARED, fdout, fsize - 2 * BUF_SIZE); + else + addr = mremap(fbuf + BUF_SIZE, BUF_SIZE, + BUF_SIZE * 2, MREMAP_FIXED, fbuf); + if (addr == MAP_FAILED) { + fprintf(stderr, "Unable to map a buffer: %m"); + return -1; + } + + fbuf = addr; + mbuf = fbuf + off; + mbuf_size = 2 * BUF_SIZE; + + return 0; +} + +int flog_close(int fdout) +{ + if (mbuf == _mbuf) + return 0; + + munmap(fbuf, BUF_SIZE * 2); + + if (ftruncate(fdout, fsize - 2 * BUF_SIZE + mbuf - fbuf)) { + fprintf(stderr, "Unable to truncate a file: %m"); + return -1; + } + return 0; +} + +int flog_encode_msg(int fdout, unsigned int nargs, unsigned int mask, const char *format, ...) +{ + flog_msg_t *m; + va_list argptr; + char *str_start, *p; + size_t i; + + if (mbuf != _mbuf && flog_map_buf(fdout)) + return -1; + + m = (void *) mbuf; + + m->nargs = nargs; + m->mask = mask; + + str_start = (void *)m->args + sizeof(m->args[0]) * nargs; + p = memccpy(str_start, format, 0, mbuf_size - (str_start - mbuf)); + if (p == NULL) { + fprintf(stderr, "No memory for string argument\n"); + return -1; + } + m->fmt = str_start - mbuf; + str_start = p; + + va_start(argptr, format); + for (i = 0; i < nargs; i++) { + m->args[i] = (long)va_arg(argptr, long); + /* + * If we got a string, we should either + * reference it when in rodata, or make + * a copy (FIXME implement rodata refs). + */ + if (mask & (1u << i)) { + p = memccpy(str_start, (void *)m->args[i], 0, mbuf_size - (str_start - mbuf)); + if (p == NULL) { + fprintf(stderr, "No memory for string argument\n"); + va_end(argptr); + return -1; + } + m->args[i] = str_start - mbuf; + str_start = p; + } + } + va_end(argptr); + m->size = str_start - mbuf; + + /* + * A magic is required to know where we stop writing into a log file, + * if it was not properly closed. The file is mapped into memory, so a + * space in the file is allocated in advance and at the end it can have + * some unused tail. + */ + m->magic = MAGIC; + + m->size = roundup(m->size, 8); + if (mbuf == _mbuf) { + if (flog_enqueue(m)) + return -1; + } else { + mbuf += m->size; + mbuf_size -= m->size; + } + return 0; +} diff --git a/flog/src/main.c b/flog/src/main.c new file mode 100644 index 0000000000..c84e774781 --- /dev/null +++ b/flog/src/main.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "flog.h" + +extern char _rodata_start, _rodata_end; +char *rodata_start = &_rodata_start; +char *rodata_end = &_rodata_end; + +enum { + MODE_BINARY, + MODE_FPRINTF, + MODE_SPRINTF, + MODE_DPRINTF, +}; + +int main(int argc, char *argv[]) +{ + static const char str1[] = "String1 String1"; + static const char str2[] = "string2 string2 string2"; + int fdout = STDOUT_FILENO; + bool use_decoder = false; + int mode = MODE_BINARY; + size_t niter = 100; + int opt, idx; + size_t i; + + static const char short_opts[] = "m:o:di:h"; + static struct option long_opts[] = { + { "mode", required_argument, 0, 'm' }, + { "output", required_argument, 0, 'o' }, + { "decode", no_argument, 0, 'd' }, + { "iter", required_argument, 0, 'i' }, + { "help", no_argument, 0, 'h' }, + { }, + }; + + while (1) { + idx = -1; + opt = getopt_long(argc, argv, short_opts, long_opts, &idx); + if (opt == -1) + break; + + switch (opt) { + case 'm': + if (strcmp(optarg, "binary") == 0) { + mode = MODE_BINARY; + } else if (strcmp(optarg, "fprintf") == 0) { + mode = MODE_FPRINTF; + } else if (strcmp(optarg, "sprintf") == 0) { + mode = MODE_SPRINTF; + } else if (strcmp(optarg, "dprintf") == 0) { + mode = MODE_DPRINTF; + } else + goto usage; + break; + case 'o': + if (strcmp(optarg, "stdout") == 0) { + fdout = fileno(stdout); + } else if (strcmp(optarg, "stderr") == 0) { + fdout = fileno(stderr); + } else { + fdout = open(optarg, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (fdout < 0) { + fprintf(stderr, "Can't open %s: %s\n", + optarg, strerror(errno)); + exit(1); + } + } + break; + case 'i': + niter = atoi(optarg); + break; + case 'd': + use_decoder = true; + break; + case 'h': + default: + goto usage; + } + } + + switch (mode) { + case MODE_BINARY: + if (use_decoder) + return flog_decode_all(STDIN_FILENO, fdout); + + if (fdout != STDOUT_FILENO && flog_map_buf(fdout)) + return 1; + for (i = 0; i < niter; i++) + if (flog_encode(fdout, "Some message %s %s %c %li %d %lu\n", + str1, str2, 'c', (long)-4, (short)2, + (unsigned long)2)) + return 1; + if (flog_close(fdout)) + return 1; + break; + case MODE_DPRINTF: + { + for (i = 0; i < niter; i++) { + dprintf(fdout, "Some message %s %s %c %li %d %lu\n", + str1, str2, 'c', (long)-4, (short)2, + (unsigned long)2); + } + break; + } + case MODE_FPRINTF: + { + FILE *f = fdopen(fdout, "w"); + + for (i = 0; i < niter; i++) { + fprintf(f, "Some message %s %s %c %li %d %lu\n", + str1, str2, 'c', (long)-4, (short)2, + (unsigned long)2); + fflush(f); + } + fclose(f); + break; + } + case MODE_SPRINTF: + { + static char buf[4096]; + + for (i = 0; i < niter; i++) { + sprintf(buf, "Some message %s %s %c %li %d %lu\n", + str1, str2, 'c', (long)-4, (short)2, + (unsigned long)2); + } + break; + } + default: + return 1; + } + + return 0; +usage: + fprintf(stderr, + "flog [--mode binary|dprintf] [--output stdout|stderr|filename] [--decode] [--iter number]\n" + "\n" + + "Examples:\n" + "\n" + + " - run 100000 iterations of instant message processing (immediate dprintf calls)\n" + "\n" + " flog -m dprintf -i 100000\n" + "\n" + + " - run 100000 iterations in binary mode without processing (queue messages only)\n" + "\n" + " flog -i 100000\n" + "\n" + + " - run 100000 iterations in binary mode with decoding after\n" + "\n" + " flog -i 100000 -d\n" + "\n" + + " - run 100000 iterations in binary mode with decoding after, writting results into 'out' file\n" + "\n" + " flog -i 100000 -d -o out\n" + "\n"); + return 1; +} diff --git a/flog/tests/test00 b/flog/tests/test00 new file mode 100755 index 0000000000..a7937e4a18 --- /dev/null +++ b/flog/tests/test00 @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e -x + +echo Map a log file into memory +time ./flog run -i 1000000 -o /tmp/flog.raw.map +echo Write into a log file +time ./flog run -i 1000000 > /tmp/flog.raw +echo Use fprintf +time ./flog run -m fprintf -i 1000000 -o /tmp/flog.fprintf.txt +echo Use dprintf +time ./flog run -m dprintf -i 1000000 -o /tmp/flog.dprintf.txt +echo Use sprintf +time ./flog run -m sprintf -i 1000000 + +time ./flog run -d < /tmp/flog.raw > /tmp/flog.raw.txt +cmp /tmp/flog.raw.txt /tmp/flog.fprintf.txt + +time ./flog run -d < /tmp/flog.raw.map > /tmp/flog.raw.map.txt +cmp /tmp/flog.raw.map.txt /tmp/flog.fprintf.txt + +cmp /tmp/flog.dprintf.txt /tmp/flog.fprintf.txt diff --git a/images/LICENSE b/images/LICENSE new file mode 100644 index 0000000000..3c6395a105 --- /dev/null +++ b/images/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 The CRIU developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/images/autofs.proto b/images/autofs.proto index 2146ca89f0..5c8c216c84 100644 --- a/images/autofs.proto +++ b/images/autofs.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message autofs_entry { diff --git a/images/binfmt-misc.proto b/images/binfmt-misc.proto index 82a86c8c94..a48d8724c5 100644 --- a/images/binfmt-misc.proto +++ b/images/binfmt-misc.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message binfmt_misc_entry { diff --git a/images/bpfmap-data.proto b/images/bpfmap-data.proto index dc3b0db017..b9502bb452 100644 --- a/images/bpfmap-data.proto +++ b/images/bpfmap-data.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message bpfmap_data_entry { diff --git a/images/bpfmap-file.proto b/images/bpfmap-file.proto index 6560bb646e..34a6c1dd29 100644 --- a/images/bpfmap-file.proto +++ b/images/bpfmap-file.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/cgroup.proto b/images/cgroup.proto index b8a545e3ce..ee03541240 100644 --- a/images/cgroup.proto +++ b/images/cgroup.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message cgroup_perms { diff --git a/images/core-aarch64.proto b/images/core-aarch64.proto index 83fdd64372..3356e6b757 100644 --- a/images/core-aarch64.proto +++ b/images/core-aarch64.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/core-arm.proto b/images/core-arm.proto index 3004346bb9..f9c9e80cb6 100644 --- a/images/core-arm.proto +++ b/images/core-arm.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/core-mips.proto b/images/core-mips.proto index 6391b1e86f..ec06d69511 100755 --- a/images/core-mips.proto +++ b/images/core-mips.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/core-ppc64.proto b/images/core-ppc64.proto index aca1c2faee..6a27f90128 100644 --- a/images/core-ppc64.proto +++ b/images/core-ppc64.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/core-s390.proto b/images/core-s390.proto index 497c73b206..44130f2060 100644 --- a/images/core-s390.proto +++ b/images/core-s390.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/core-x86.proto b/images/core-x86.proto index 2ed2ad35d9..ee7be8ff11 100644 --- a/images/core-x86.proto +++ b/images/core-x86.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/core.proto b/images/core.proto index 9e9e39388f..b713119f27 100644 --- a/images/core.proto +++ b/images/core.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "core-x86.proto"; diff --git a/images/cpuinfo.proto b/images/cpuinfo.proto index 8ee629c2c9..15860a90c5 100644 --- a/images/cpuinfo.proto +++ b/images/cpuinfo.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message cpuinfo_x86_entry { diff --git a/images/creds.proto b/images/creds.proto index 23b84c7e50..0007fb46db 100644 --- a/images/creds.proto +++ b/images/creds.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message creds_entry { diff --git a/images/eventfd.proto b/images/eventfd.proto index ff9ced3934..225462f763 100644 --- a/images/eventfd.proto +++ b/images/eventfd.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "fown.proto"; diff --git a/images/eventpoll.proto b/images/eventpoll.proto index 4a8d1b834e..0f3e8a870b 100644 --- a/images/eventpoll.proto +++ b/images/eventpoll.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "fown.proto"; diff --git a/images/ext-file.proto b/images/ext-file.proto index f820ffb07a..8b4f825681 100644 --- a/images/ext-file.proto +++ b/images/ext-file.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "fown.proto"; diff --git a/images/fdinfo.proto b/images/fdinfo.proto index f5e18954bb..88f1c11860 100644 --- a/images/fdinfo.proto +++ b/images/fdinfo.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "regfile.proto"; diff --git a/images/fh.proto b/images/fh.proto index 2da7e9deb0..7a2ce484ba 100644 --- a/images/fh.proto +++ b/images/fh.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/fifo.proto b/images/fifo.proto index f5b3283ffa..ae6f48162a 100644 --- a/images/fifo.proto +++ b/images/fifo.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message fifo_entry { diff --git a/images/file-lock.proto b/images/file-lock.proto index 5dd8847cc3..dcf3d871ca 100644 --- a/images/file-lock.proto +++ b/images/file-lock.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message file_lock_entry { diff --git a/images/fown.proto b/images/fown.proto index 9956b98656..b2e20b6572 100644 --- a/images/fown.proto +++ b/images/fown.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message fown_entry { diff --git a/images/fs.proto b/images/fs.proto index 5b940a1c1a..158501ac9d 100644 --- a/images/fs.proto +++ b/images/fs.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message fs_entry { diff --git a/images/fsnotify.proto b/images/fsnotify.proto index 399a449a02..df6a667f86 100644 --- a/images/fsnotify.proto +++ b/images/fsnotify.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/ghost-file.proto b/images/ghost-file.proto index 0576089fdd..9ecee41d24 100644 --- a/images/ghost-file.proto +++ b/images/ghost-file.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/img-streamer.proto b/images/img-streamer.proto index d1bd4cc191..48a3a93b0e 100644 --- a/images/img-streamer.proto +++ b/images/img-streamer.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; // This message is sent from CRIU to the streamer. diff --git a/images/inventory.proto b/images/inventory.proto index d7d0622c40..56e85a80a9 100644 --- a/images/inventory.proto +++ b/images/inventory.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "core.proto"; diff --git a/images/ipc-desc.proto b/images/ipc-desc.proto index b400bd7d2a..8b4c7f5baa 100644 --- a/images/ipc-desc.proto +++ b/images/ipc-desc.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message ipc_desc_entry { diff --git a/images/ipc-msg.proto b/images/ipc-msg.proto index 5260ea8626..5b3103182f 100644 --- a/images/ipc-msg.proto +++ b/images/ipc-msg.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "ipc-desc.proto"; diff --git a/images/ipc-sem.proto b/images/ipc-sem.proto index bffb581aec..71a2beb948 100644 --- a/images/ipc-sem.proto +++ b/images/ipc-sem.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "ipc-desc.proto"; diff --git a/images/ipc-shm.proto b/images/ipc-shm.proto index 31e172eb8e..7865dad8dd 100644 --- a/images/ipc-shm.proto +++ b/images/ipc-shm.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "ipc-desc.proto"; diff --git a/images/ipc-var.proto b/images/ipc-var.proto index f46fcdeebd..a5e2df9dec 100644 --- a/images/ipc-var.proto +++ b/images/ipc-var.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message ipc_var_entry { diff --git a/images/macvlan.proto b/images/macvlan.proto index 0ca2652f03..6f78076d8e 100644 --- a/images/macvlan.proto +++ b/images/macvlan.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message macvlan_link_entry { diff --git a/images/memfd.proto b/images/memfd.proto index ad5373d10a..a944f145d7 100644 --- a/images/memfd.proto +++ b/images/memfd.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/mm.proto b/images/mm.proto index e0f14c62f6..b37668c4b3 100644 --- a/images/mm.proto +++ b/images/mm.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/mnt.proto b/images/mnt.proto index 8983395aea..4abb7d1a95 100644 --- a/images/mnt.proto +++ b/images/mnt.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/netdev.proto b/images/netdev.proto index ae9c995316..748fd02004 100644 --- a/images/netdev.proto +++ b/images/netdev.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "macvlan.proto"; diff --git a/images/ns.proto b/images/ns.proto index 5ff0001652..19ec641f62 100644 --- a/images/ns.proto +++ b/images/ns.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message ns_file_entry { diff --git a/images/opts.proto b/images/opts.proto index 70c7fd481f..95304a8c60 100644 --- a/images/opts.proto +++ b/images/opts.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "google/protobuf/descriptor.proto"; diff --git a/images/packet-sock.proto b/images/packet-sock.proto index 25875b4702..d4b38cf154 100644 --- a/images/packet-sock.proto +++ b/images/packet-sock.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/pagemap.proto b/images/pagemap.proto index 42ed5ebb9c..e6d341b0f6 100644 --- a/images/pagemap.proto +++ b/images/pagemap.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/pidns.proto b/images/pidns.proto index 7ff0497494..f7e92e3ecd 100644 --- a/images/pidns.proto +++ b/images/pidns.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message pidns_entry { diff --git a/images/pipe-data.proto b/images/pipe-data.proto index 78d53a8902..040479e7cb 100644 --- a/images/pipe-data.proto +++ b/images/pipe-data.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message pipe_data_entry { diff --git a/images/pipe.proto b/images/pipe.proto index a9a213b90b..2c0360e852 100644 --- a/images/pipe.proto +++ b/images/pipe.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/pstree.proto b/images/pstree.proto index 23e88aa74f..fca284cb73 100644 --- a/images/pstree.proto +++ b/images/pstree.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message pstree_entry { diff --git a/images/regfile.proto b/images/regfile.proto index 49884ddc9b..bf22d2026d 100644 --- a/images/regfile.proto +++ b/images/regfile.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/remap-file-path.proto b/images/remap-file-path.proto index 3cc78a25e7..8635370c0c 100644 --- a/images/remap-file-path.proto +++ b/images/remap-file-path.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; enum remap_type { diff --git a/images/rlimit.proto b/images/rlimit.proto index 773a8df9ce..3a3aeda306 100644 --- a/images/rlimit.proto +++ b/images/rlimit.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message rlimit_entry { diff --git a/images/rpc.proto b/images/rpc.proto index df1b5aed2a..c968cc9918 100644 --- a/images/rpc.proto +++ b/images/rpc.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message criu_page_server_info { diff --git a/images/sa.proto b/images/sa.proto index 07fd4ffd3f..07f71c3a03 100644 --- a/images/sa.proto +++ b/images/sa.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/seccomp.proto b/images/seccomp.proto index 177e5fd4a5..e56cea3a1d 100644 --- a/images/seccomp.proto +++ b/images/seccomp.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message seccomp_filter { diff --git a/images/siginfo.proto b/images/siginfo.proto index e0d141e6c6..6e696c7fd8 100644 --- a/images/siginfo.proto +++ b/images/siginfo.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message siginfo_entry { diff --git a/images/signalfd.proto b/images/signalfd.proto index 31d0d9f006..83546ae218 100644 --- a/images/signalfd.proto +++ b/images/signalfd.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/sit.proto b/images/sit.proto index 7ca91ccd0e..5396458581 100644 --- a/images/sit.proto +++ b/images/sit.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/sk-inet.proto b/images/sk-inet.proto index 75d565dcdd..594e29c662 100644 --- a/images/sk-inet.proto +++ b/images/sk-inet.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/sk-netlink.proto b/images/sk-netlink.proto index 97fa445860..cfcc88daad 100644 --- a/images/sk-netlink.proto +++ b/images/sk-netlink.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/sk-opts.proto b/images/sk-opts.proto index 5bdc5063ff..2377f6b629 100644 --- a/images/sk-opts.proto +++ b/images/sk-opts.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message sk_opts_entry { diff --git a/images/sk-packet.proto b/images/sk-packet.proto index e15dd382a5..b60a8870a7 100644 --- a/images/sk-packet.proto +++ b/images/sk-packet.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message scm_entry { diff --git a/images/sk-unix.proto b/images/sk-unix.proto index 2a3a7cc320..8ddbccde00 100644 --- a/images/sk-unix.proto +++ b/images/sk-unix.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/stats.proto b/images/stats.proto index 68d2f1bbbb..64e46181da 100644 --- a/images/stats.proto +++ b/images/stats.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; // This one contains statistics about dump/restore process diff --git a/images/sysctl.proto b/images/sysctl.proto index 4ecdf27011..0922b87ab0 100644 --- a/images/sysctl.proto +++ b/images/sysctl.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; enum SysctlType { diff --git a/images/tcp-stream.proto b/images/tcp-stream.proto index 1740783052..c2244ba3bf 100644 --- a/images/tcp-stream.proto +++ b/images/tcp-stream.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/time.proto b/images/time.proto index 4bb2b94596..5e5e7eee47 100644 --- a/images/time.proto +++ b/images/time.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message timeval { diff --git a/images/timens.proto b/images/timens.proto index a8272609b0..79097a18d4 100644 --- a/images/timens.proto +++ b/images/timens.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message timespec { diff --git a/images/timer.proto b/images/timer.proto index a254a6f809..4eb5452b35 100644 --- a/images/timer.proto +++ b/images/timer.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message itimer_entry { diff --git a/images/timerfd.proto b/images/timerfd.proto index 2432815492..0bdf125388 100644 --- a/images/timerfd.proto +++ b/images/timerfd.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/tty.proto b/images/tty.proto index ed664ef513..14bc543ece 100644 --- a/images/tty.proto +++ b/images/tty.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/tun.proto b/images/tun.proto index b70c9ed67d..ad61037db1 100644 --- a/images/tun.proto +++ b/images/tun.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/images/userns.proto b/images/userns.proto index 16be6b161d..3a23cbbf87 100644 --- a/images/userns.proto +++ b/images/userns.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message uid_gid_extent { diff --git a/images/utsns.proto b/images/utsns.proto index a29aea1c27..efc689fa55 100644 --- a/images/utsns.proto +++ b/images/utsns.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; message utsns_entry { diff --git a/images/vma.proto b/images/vma.proto index 7085f4237e..0c07d51c6b 100644 --- a/images/vma.proto +++ b/images/vma.proto @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + syntax = "proto2"; import "opts.proto"; diff --git a/include/common/arch/x86/asm/asm.h b/include/common/arch/x86/asm/asm.h new file mode 100644 index 0000000000..af324c6e2a --- /dev/null +++ b/include/common/arch/x86/asm/asm.h @@ -0,0 +1,28 @@ +#ifndef __CR_ASM_H__ +#define __CR_ASM_H__ + +#ifdef __GCC_ASM_FLAG_OUTPUTS__ +# define CC_SET(c) "\n\t/* output condition code " #c "*/\n" +# define CC_OUT(c) "=@cc" #c +#else +# define CC_SET(c) "\n\tset" #c " %[_cc_" #c "]\n" +# define CC_OUT(c) [_cc_ ## c] "=qm" +#endif + +#ifdef __ASSEMBLY__ +# define __ASM_FORM(x) x +#else +# define __ASM_FORM(x) " " #x " " +#endif + +#ifndef __x86_64__ +/* 32 bit */ +# define __ASM_SEL(a,b) __ASM_FORM(a) +#else +/* 64 bit */ +# define __ASM_SEL(a,b) __ASM_FORM(b) +#endif + +#define __ASM_SIZE(inst, ...) __ASM_SEL(inst##l##__VA_ARGS__, inst##q##__VA_ARGS__) + +#endif /* __CR_ASM_H__ */ diff --git a/include/common/arch/x86/asm/bitops.h b/include/common/arch/x86/asm/bitops.h index c69e0ed029..9c7aeb5d73 100644 --- a/include/common/arch/x86/asm/bitops.h +++ b/include/common/arch/x86/asm/bitops.h @@ -3,16 +3,9 @@ #include #include "common/arch/x86/asm/cmpxchg.h" +#include "common/arch/x86/asm/asm.h" #include "common/asm/bitsperlong.h" -#ifdef __GCC_ASM_FLAG_OUTPUTS__ -# define CC_SET(c) "\n\t/* output condition code " #c "*/\n" -# define CC_OUT(c) "=@cc" #c -#else -# define CC_SET(c) "\n\tset" #c " %[_cc_" #c "]\n" -# define CC_OUT(c) [_cc_ ## c] "=qm" -#endif - #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_LONG) @@ -29,21 +22,21 @@ #define ADDR BITOP_ADDR(addr) -static inline void set_bit(int nr, volatile unsigned long *addr) +static inline void set_bit(long nr, volatile unsigned long *addr) { - asm volatile("btsl %1,%0" : ADDR : "Ir" (nr) : "memory"); + asm volatile(__ASM_SIZE(bts) " %1,%0" : ADDR : "Ir" (nr) : "memory"); } -static inline void change_bit(int nr, volatile unsigned long *addr) +static inline void change_bit(long nr, volatile unsigned long *addr) { - asm volatile("btcl %1,%0" : ADDR : "Ir" (nr)); + asm volatile(__ASM_SIZE(btc) " %1,%0" : ADDR : "Ir" (nr)); } static inline bool test_bit(long nr, volatile const unsigned long *addr) { bool oldbit; - asm volatile("btq %2,%1" + asm volatile(__ASM_SIZE(bt) " %2,%1" CC_SET(c) : CC_OUT(c) (oldbit) : "m" (*(unsigned long *)addr), "Ir" (nr) : "memory"); @@ -51,9 +44,9 @@ static inline bool test_bit(long nr, volatile const unsigned long *addr) return oldbit; } -static inline void clear_bit(int nr, volatile unsigned long *addr) +static inline void clear_bit(long nr, volatile unsigned long *addr) { - asm volatile("btrl %1,%0" : ADDR : "Ir" (nr)); + asm volatile(__ASM_SIZE(btr) " %1,%0" : ADDR : "Ir" (nr)); } /** @@ -64,11 +57,11 @@ static inline void clear_bit(int nr, volatile unsigned long *addr) * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -static inline bool test_and_set_bit(int nr, volatile unsigned long *addr) +static inline bool test_and_set_bit(long nr, volatile unsigned long *addr) { bool oldbit; - asm("btsq %2,%1" + asm(__ASM_SIZE(bts) " %2,%1" CC_SET(c) : CC_OUT(c) (oldbit) : "m" (*(unsigned long *)addr), "Ir" (nr) : "memory"); diff --git a/include/common/bug.h b/include/common/bug.h index 4622911040..bacbf566b8 100644 --- a/include/common/bug.h +++ b/include/common/bug.h @@ -24,6 +24,7 @@ pr_err("BUG at %s:%d\n", __FILE__, __LINE__); \ __raise(); \ *(volatile unsigned long *)NULL = 0xdead0000 + __LINE__; \ + __builtin_unreachable(); \ } \ } while (0) #else diff --git a/include/common/lock.h b/include/common/lock.h index 4782b638f2..6baece25fa 100644 --- a/include/common/lock.h +++ b/include/common/lock.h @@ -9,10 +9,13 @@ #include "common/asm/atomic.h" #include "common/compiler.h" +/* scan-build complains about derefencing a NULL pointer here. */ +#ifndef __clang_analyzer__ #define LOCK_BUG_ON(condition) \ if ((condition)) \ *(volatile unsigned long *)NULL = 0xdead0000 + __LINE__ #define LOCK_BUG() LOCK_BUG_ON(1) +#endif /* __clang_analyzer__ */ #ifdef CR_NOGLIBC # include diff --git a/lib/py/cli.py b/lib/py/cli.py index da343022e6..cb125aab87 100755 --- a/lib/py/cli.py +++ b/lib/py/cli.py @@ -11,14 +11,28 @@ def inf(opts): if opts['in']: return open(opts['in'], 'rb') else: - return sys.stdin + if (sys.version_info < (3, 0)): + return sys.stdin + if sys.stdin.isatty(): + # If we are reading from a terminal (not a pipe) we want text input and not binary + return sys.stdin + return sys.stdin.buffer -def outf(opts): +def outf(opts, decode): + # Decode means from protobuf to JSON. + # Use text when writing to JSON else use binaray mode if opts['out']: - return open(opts['out'], 'w+') + mode = 'wb+' + if decode: + mode = 'w+' + return open(opts['out'], mode) else: - return sys.stdout + if (sys.version_info < (3, 0)): + return sys.stdout + if decode: + return sys.stdout + return sys.stdout.buffer def dinf(opts, name): @@ -39,15 +53,21 @@ def decode(opts): if opts['pretty']: indent = 4 - f = outf(opts) + f = outf(opts, True) json.dump(img, f, indent=indent) if f == sys.stdout: f.write("\n") def encode(opts): - img = json.load(inf(opts)) - pycriu.images.dump(img, outf(opts)) + try: + img = json.load(inf(opts)) + except UnicodeDecodeError: + print("Cannot read JSON.\n"\ + "Maybe you are feeding me an image with protobuf data? "\ + "Encode expects JSON input.", file=sys.stderr) + sys.exit(1) + pycriu.images.dump(img, outf(opts, False)) def info(opts): @@ -133,7 +153,7 @@ def ftype_find_in_image(opts, ft, fid, img): if f: return f[ft['field']] - if ft['img'] == None: + if ft['img'] is None: ft['img'] = pycriu.images.load(dinf(opts, img))['entries'] for f in ft['img']: if f['id'] == fid: diff --git a/lib/py/images/images.py b/lib/py/images/images.py index 9c8e1447de..956932e03d 100644 --- a/lib/py/images/images.py +++ b/lib/py/images/images.py @@ -42,6 +42,7 @@ import struct import os import array +import sys from . import magic from . import pb @@ -284,9 +285,15 @@ def dump(self, entries, f): size = len(pb_str) f.write(struct.pack('i', size)) f.write(pb_str) - f.write(base64.decodebytes(item['extra'])) + if (sys.version_info > (3, 0)): + f.write(base64.decodebytes(str.encode(item['extra']))) + else: + f.write(base64.decodebytes(item['extra'])) else: - f.write(base64.decodebytes(item['extra'])) + if (sys.version_info > (3, 0)): + f.write(base64.decodebytes(str.encode(item['extra']))) + else: + f.write(base64.decodebytes(item['extra'])) def dumps(self, entries): f = io.BytesIO('') @@ -304,10 +311,13 @@ class pipes_data_extra_handler: def load(self, f, pload): size = pload.bytes data = f.read(size) - return base64.encodebytes(data) + return base64.encodebytes(data).decode() def dump(self, extra, f, pload): - data = base64.decodebytes(extra) + if (sys.version_info > (3, 0)): + data = base64.decodebytes(str.encode(extra)) + else: + data = base64.decodebytes(extra) f.write(data) def skip(self, f, pload): @@ -319,10 +329,13 @@ class sk_queues_extra_handler: def load(self, f, pload): size = pload.length data = f.read(size) - return base64.encodebytes(data) + return base64.encodebytes(data).decode() def dump(self, extra, f, _unused): - data = base64.decodebytes(extra) + if (sys.version_info > (3, 0)): + data = base64.decodebytes(str.encode(extra)) + else: + data = base64.decodebytes(extra) f.write(data) def skip(self, f, pload): @@ -375,7 +388,7 @@ def load(self, f, pbuff): s = array.array('H') if s.itemsize != sizeof_u16: raise Exception("Array size mismatch") - s.fromstring(f.read(size)) + s.frombytes(f.read(size)) f.seek(rounded - size, 1) return s.tolist() @@ -389,8 +402,8 @@ def dump(self, extra, f, pbuff): s.fromlist(extra) if len(s) != entry['nsems']: raise Exception("Number of semaphores mismatch") - f.write(s.tostring()) - f.write('\0' * (rounded - size)) + f.write(s.tobytes()) + f.write(b'\0' * (rounded - size)) def skip(self, f, pbuff): entry = pb2dict.pb2dict(pbuff) @@ -418,7 +431,6 @@ def load(self, f, pbuff): return messages def dump(self, extra, f, pbuff): - entry = pb2dict.pb2dict(pbuff) for i in range(0, len(extra), 2): msg = pb.ipc_msg() pb2dict.dict2pb(extra[i], msg) @@ -429,7 +441,7 @@ def dump(self, extra, f, pbuff): rounded = round_up(msg.msize, sizeof_u64) data = base64.decodebytes(extra[i + 1]) f.write(data[:msg.msize]) - f.write('\0' * (rounded - msg.msize)) + f.write(b'\0' * (rounded - msg.msize)) def skip(self, f, pbuff): entry = pb2dict.pb2dict(pbuff) @@ -463,7 +475,7 @@ def dump(self, extra, f, pbuff): data = base64.decodebytes(extra) rounded = round_up(size, sizeof_u32) f.write(data[:size]) - f.write('\0' * (rounded - size)) + f.write(b'\0' * (rounded - size)) def skip(self, f, pbuff): entry = pb2dict.pb2dict(pbuff) diff --git a/lib/py/images/pb2dict.py b/lib/py/images/pb2dict.py index eecdc05223..cfaff6c7d0 100644 --- a/lib/py/images/pb2dict.py +++ b/lib/py/images/pb2dict.py @@ -3,6 +3,7 @@ import os import quopri import socket +import sys from ipaddress import IPv4Address, IPv6Address, ip_address from google.protobuf.descriptor import FieldDescriptor as FD @@ -246,11 +247,17 @@ def encode_dev(field, value): def encode_base64(value): - return base64.encodebytes(value) + if (sys.version_info > (3, 0)): + return base64.encodebytes(value).decode() + else: + return base64.encodebytes(value) def decode_base64(value): - return base64.decodebytes(value) + if (sys.version_info > (3, 0)): + return base64.decodebytes(str.encode(value)) + else: + return base64.decodebytes(value) def encode_unix(value): diff --git a/scripts/build/Dockerfile.aarch64-cross b/scripts/build/Dockerfile.aarch64-cross index ab5d9299fc..d372b2c91f 100644 --- a/scripts/build/Dockerfile.aarch64-cross +++ b/scripts/build/Dockerfile.aarch64-cross @@ -1,6 +1,6 @@ FROM dockcross/base:latest -COPY scripts/travis/apt-install /bin/apt-install +COPY scripts/ci/apt-install /bin/apt-install # Add the cross compiler sources RUN echo "deb http://ftp.us.debian.org/debian/ buster main" >> /etc/apt/sources.list && \ diff --git a/scripts/build/Dockerfile.alpine b/scripts/build/Dockerfile.alpine index dbf3c2bf16..058b46ad60 100644 --- a/scripts/build/Dockerfile.alpine +++ b/scripts/build/Dockerfile.alpine @@ -6,7 +6,6 @@ RUN apk update && apk add \ $CC \ bash \ build-base \ - ccache \ coreutils \ git \ gnutls-dev \ @@ -26,9 +25,8 @@ RUN apk update && apk add \ COPY . /criu WORKDIR /criu -ENV CC="ccache $CC" CCACHE_DIR=/tmp/.ccache CCACHE_NOCOMPRESS=1 $ENV1=yes -RUN mv .ccache /tmp && make mrproper && ccache -sz && \ - date && make -j $(nproc) CC="$CC" && date && ccache -s +ENV $ENV1=yes +RUN make mrproper && date && make -j $(nproc) CC="$CC" && date RUN apk add \ ip6tables \ diff --git a/scripts/build/Dockerfile.armv7-cross b/scripts/build/Dockerfile.armv7-cross index 99321e456c..b3a18d0ec7 100644 --- a/scripts/build/Dockerfile.armv7-cross +++ b/scripts/build/Dockerfile.armv7-cross @@ -1,6 +1,6 @@ FROM dockcross/base:latest -COPY scripts/travis/apt-install /bin/apt-install +COPY scripts/ci/apt-install /bin/apt-install # Add the cross compiler sources RUN echo "deb http://ftp.us.debian.org/debian/ buster main" >> /etc/apt/sources.list && \ diff --git a/scripts/build/Dockerfile.centos b/scripts/build/Dockerfile.centos7 similarity index 79% rename from scripts/build/Dockerfile.centos rename to scripts/build/Dockerfile.centos7 index 213be694fb..d10ec48e14 100644 --- a/scripts/build/Dockerfile.centos +++ b/scripts/build/Dockerfile.centos7 @@ -5,7 +5,6 @@ ARG ENV1=FOOBAR RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm RUN yum install -y \ - ccache \ findutils \ gcc \ git \ @@ -39,9 +38,8 @@ RUN yum install -y \ COPY . /criu WORKDIR /criu -ENV CCACHE_DIR=/tmp/.ccache CCACHE_NOCOMPRESS=1 $ENV1=yes -RUN mv .ccache /tmp && make mrproper && ccache -sz && \ - date && make -j $(nproc) CC="$CC" && date && ccache -s +ENV $ENV1=yes +RUN make mrproper && date && make -j $(nproc) CC="$CC" && date # The rpc test cases are running as user #1000, let's add the user RUN adduser -u 1000 test diff --git a/scripts/build/Dockerfile.centos8 b/scripts/build/Dockerfile.centos8 new file mode 100644 index 0000000000..ff6c15f7fd --- /dev/null +++ b/scripts/build/Dockerfile.centos8 @@ -0,0 +1,54 @@ +FROM registry.centos.org/centos/centos:8 + +ARG CC=gcc +ARG ENV1=FOOBAR + +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm dnf-plugins-core +RUN yum config-manager --set-enabled powertools +RUN yum install -y --allowerasing \ + asciidoc \ + coreutils \ + chkconfig \ + diffutils \ + findutils \ + gcc \ + git \ + gnutls-devel \ + iproute \ + iptables \ + libaio-devel \ + libasan \ + libcap-devel \ + libnet-devel \ + libnl3-devel \ + libselinux-devel \ + make \ + procps-ng \ + protobuf-c-devel \ + protobuf-devel \ + python3-devel \ + python3-flake8 \ + python3-PyYAML \ + python3-future \ + python3-protobuf \ + python3-pip \ + sudo \ + tar \ + which \ + xmlto + +RUN alternatives --set python /usr/bin/python3 +ENV PYTHON=python3 + +COPY . /criu +WORKDIR /criu + +ENV $ENV1=yes +RUN make mrproper && date && make -j $(nproc) CC="$CC" && date + +# The rpc test cases are running as user #1000, let's add the user +RUN adduser -u 1000 test + +RUN pip3 install junit_xml + +RUN make -C test/zdtm -j $(nproc) diff --git a/scripts/build/Dockerfile.fedora-asan.hdr b/scripts/build/Dockerfile.fedora-asan.hdr index 3ec09c1c00..7a20edd243 100644 --- a/scripts/build/Dockerfile.fedora-asan.hdr +++ b/scripts/build/Dockerfile.fedora-asan.hdr @@ -1,2 +1,2 @@ -FROM fedora:29 +FROM registry.fedoraproject.org/fedora:latest ENV ASAN=1 diff --git a/scripts/build/Dockerfile.fedora.tmpl b/scripts/build/Dockerfile.fedora.tmpl index 184f0019af..2ee20ae381 100644 --- a/scripts/build/Dockerfile.fedora.tmpl +++ b/scripts/build/Dockerfile.fedora.tmpl @@ -1,52 +1,16 @@ ARG CC=gcc ARG ENV1=FOOBAR -RUN dnf install -y \ - ccache \ - diffutils \ - findutils \ - gcc \ - git \ - gnutls-devel \ - gzip \ - iproute \ - iptables \ - nftables \ - nftables-devel \ - libaio-devel \ - libasan \ - libcap-devel \ - libnet-devel \ - libnl3-devel \ - make \ - procps-ng \ - protobuf-c-devel \ - protobuf-devel \ - python3-flake8 \ - python3-PyYAML \ - python3-future \ - python3-protobuf \ - python3-junit_xml \ - redhat-rpm-config \ - sudo \ - tar \ - which \ - e2fsprogs \ - rubygem-asciidoctor \ - kmod - -RUN ln -sf python3 /usr/bin/python -ENV PYTHON=python3 +COPY scripts/ci/prepare-for-fedora-rawhide.sh /bin/prepare-for-fedora-rawhide.sh +RUN /bin/prepare-for-fedora-rawhide.sh COPY . /criu WORKDIR /criu -ENV CCACHE_DIR=/tmp/.ccache CCACHE_NOCOMPRESS=1 $ENV1=yes -RUN mv .ccache /tmp && make mrproper && ccache -sz && \ - date && make -j $(nproc) CC="$CC" && date && ccache -s +ENV $ENV1=yes +RUN make mrproper && date && make -j $(nproc) CC="$CC" && date # The rpc test cases are running as user #1000, let's add the user RUN adduser -u 1000 test RUN make -C test/zdtm -j $(nproc) - diff --git a/scripts/build/Dockerfile.linux32.tmpl b/scripts/build/Dockerfile.linux32.tmpl index 3b3007e9c2..cf26b27561 100644 --- a/scripts/build/Dockerfile.linux32.tmpl +++ b/scripts/build/Dockerfile.linux32.tmpl @@ -1,10 +1,9 @@ ARG CC=gcc ARG ENV1=FOOBAR -COPY scripts/travis/apt-install /bin/apt-install +COPY scripts/ci/apt-install /bin/apt-install RUN apt-install \ - ccache \ libnet-dev \ libnl-route-3-dev \ $CC \ @@ -28,12 +27,11 @@ RUN apt-install \ COPY . /criu WORKDIR /criu -ENV CC="ccache $CC" CCACHE_DIR=/tmp/.ccache CCACHE_NOCOMPRESS=1 $ENV1=yes +ENV $ENV1=yes RUN uname -m && setarch linux32 uname -m && setarch --list -RUN mv .ccache /tmp && make mrproper && ccache -s && \ - date && \ +RUN make mrproper && date && \ # Check single object build setarch linux32 make -j $(nproc) CC="$CC" criu/parasite-syscall.o && \ # Compile criu diff --git a/scripts/build/Dockerfile.mips64el-cross b/scripts/build/Dockerfile.mips64el-cross index 0458ee887e..6f0f32820f 100644 --- a/scripts/build/Dockerfile.mips64el-cross +++ b/scripts/build/Dockerfile.mips64el-cross @@ -1,6 +1,6 @@ FROM dockcross/base:latest -COPY scripts/travis/apt-install /bin/apt-install +COPY scripts/ci/apt-install /bin/apt-install # Add the cross compiler sources RUN echo "deb http://ftp.us.debian.org/debian/ buster main" >> /etc/apt/sources.list && \ diff --git a/scripts/build/Dockerfile.openj9-alpine b/scripts/build/Dockerfile.openj9-alpine index 39ea4d08e6..5f2f08df46 100644 --- a/scripts/build/Dockerfile.openj9-alpine +++ b/scripts/build/Dockerfile.openj9-alpine @@ -3,7 +3,6 @@ FROM adoptopenjdk/openjdk8-openj9:alpine RUN apk update && apk add \ bash \ build-base \ - ccache \ coreutils \ git \ gnutls-dev \ @@ -26,5 +25,5 @@ WORKDIR /criu RUN make -ENTRYPOINT mvn -f test/javaTests/pom.xml test +ENTRYPOINT mvn -q -f test/javaTests/pom.xml test diff --git a/scripts/build/Dockerfile.openj9-ubuntu b/scripts/build/Dockerfile.openj9-ubuntu index 80af38b1a2..abcd8cc1a1 100644 --- a/scripts/build/Dockerfile.openj9-ubuntu +++ b/scripts/build/Dockerfile.openj9-ubuntu @@ -1,6 +1,6 @@ FROM adoptopenjdk/openjdk8-openj9:latest -COPY scripts/travis/apt-install /bin/apt-install +COPY scripts/ci/apt-install /bin/apt-install RUN apt-install protobuf-c-compiler \ libprotobuf-c-dev \ @@ -29,5 +29,5 @@ WORKDIR /criu RUN make -ENTRYPOINT mvn -f test/javaTests/pom.xml test +ENTRYPOINT mvn -q -f test/javaTests/pom.xml test diff --git a/scripts/build/Dockerfile.ppc64-cross b/scripts/build/Dockerfile.ppc64-cross index fb93a5824e..eaa4e4ea22 100644 --- a/scripts/build/Dockerfile.ppc64-cross +++ b/scripts/build/Dockerfile.ppc64-cross @@ -1,6 +1,6 @@ FROM dockcross/base:latest -COPY scripts/travis/apt-install /bin/apt-install +COPY scripts/ci/apt-install /bin/apt-install # Add the cross compiler sources RUN echo "deb http://ftp.us.debian.org/debian/ buster main" >> /etc/apt/sources.list && \ diff --git a/scripts/build/Dockerfile.tmpl b/scripts/build/Dockerfile.tmpl index f4213cea04..c259796242 100644 --- a/scripts/build/Dockerfile.tmpl +++ b/scripts/build/Dockerfile.tmpl @@ -1,10 +1,9 @@ ARG CC=gcc ARG ENV1=FOOBAR -COPY scripts/travis/apt-install /bin/apt-install +COPY scripts/ci/apt-install /bin/apt-install RUN apt-install \ - ccache \ libnet-dev \ libnl-route-3-dev \ $CC \ @@ -13,9 +12,11 @@ RUN apt-install \ git-core \ iptables \ libaio-dev \ + libbsd-dev \ libcap-dev \ libgnutls28-dev \ libgnutls30 \ + libnftables-dev \ libnl-3-dev \ libprotobuf-c-dev \ libprotobuf-dev \ @@ -28,10 +29,9 @@ RUN apt-install \ COPY . /criu WORKDIR /criu -ENV CC="ccache $CC" CCACHE_DIR=/tmp/.ccache CCACHE_NOCOMPRESS=1 $ENV1=yes +ENV $ENV1=yes -RUN mv .ccache /tmp && make mrproper && ccache -s && \ - date && \ +RUN make mrproper && date && \ # Check single object build make -j $(nproc) CC="$CC" criu/parasite-syscall.o && \ # Compile criu diff --git a/scripts/build/Dockerfile.x86_64.hdr b/scripts/build/Dockerfile.x86_64.hdr index 2f87c4e8cf..32fc2978a5 100644 --- a/scripts/build/Dockerfile.x86_64.hdr +++ b/scripts/build/Dockerfile.x86_64.hdr @@ -1,5 +1,5 @@ -FROM ubuntu:xenial +FROM ubuntu:focal -COPY scripts/travis/apt-install /bin/apt-install +COPY scripts/ci/apt-install /bin/apt-install RUN apt-install gcc-multilib diff --git a/scripts/build/Makefile b/scripts/build/Makefile index 974d1455f4..b6c6477f3e 100644 --- a/scripts/build/Makefile +++ b/scripts/build/Makefile @@ -1,4 +1,4 @@ -ARCHES := x86_64 fedora-asan fedora-rawhide centos armv7hf +ARCHES := x86_64 fedora-asan fedora-rawhide centos7 armv7hf centos8 TARGETS := $(ARCHES) alpine TARGETS_CLANG := $(addsuffix $(TARGETS),-clang) CONTAINER_RUNTIME := docker @@ -19,10 +19,7 @@ Dockerfile.%: Dockerfile.%.hdr Dockerfile.%.tmpl cat $^ > $@ $(TARGETS): - mkdir -p $(HOME)/.ccache - mv $(HOME)/.ccache ../../ - $(CONTAINER_RUNTIME) build -t criu-$@ -f Dockerfile.$@ $(DB_CC) $(DB_ENV) ../.. - $(CONTAINER_RUNTIME) run criu-$@ tar c -C /tmp .ccache | tar x -C $(HOME) + $(CONTAINER_RUNTIME) build -t criu-$@ -f Dockerfile.$@ $(DB_CC) ../.. .PHONY: $(TARGETS) # Clang builds add some Docker build env @@ -32,6 +29,5 @@ endef $(foreach t,$(TARGETS),$(eval $(call CLANG_DEP,$(t)))) %-clang: DB_CC=--build-arg CC=clang -%-clang: DB_ENV=--build-arg ENV1=CCACHE_CPP2 s390x-clang: DB_CC=--build-arg CC=clang-3.8 .PHONY: $(TARGETS_CLANG) diff --git a/scripts/travis/Makefile b/scripts/ci/Makefile similarity index 75% rename from scripts/travis/Makefile rename to scripts/ci/Makefile index 1af60fe8dc..79637db0b3 100644 --- a/scripts/travis/Makefile +++ b/scripts/ci/Makefile @@ -1,9 +1,9 @@ local: - ./travis-tests + ./run-ci-tests.sh .PHONY: local after_success: - ./travis-after_success + ./ci-after-success.sh .PHONY: after_success target-suffix = @@ -11,7 +11,7 @@ ifdef CLANG target-suffix = -clang endif -TARGETS := alpine fedora-rawhide centos +TARGETS := alpine fedora-rawhide centos7 centos8 ZDTM_OPTIONS := UNAME := $(shell uname -m) @@ -27,10 +27,20 @@ endef export DOCKER_JSON +ifeq ($(GITHUB_ACTIONS),true) + # GitHub Actions does not give us a real TTY and errors out with + # 'the input device is not a TTY' if using '-t' + CONTAINER_TERMINAL := -i +else + CONTAINER_TERMINAL := -it +endif + +export CONTAINER_TERMINAL + ifeq ($(UNAME),x86_64) # On anything besides x86_64 Travis is running unprivileged LXD # containers which do not support running docker with '--privileged'. - CONTAINER_OPTS := --rm -it --privileged -v /lib/modules:/lib/modules --tmpfs /run + CONTAINER_OPTS := --rm $(CONTAINER_TERMINAL) --privileged -v /lib/modules:/lib/modules --tmpfs /run else CONTAINER_OPTS := --rm -v /lib/modules:/lib/modules --tmpfs /run endif @@ -46,11 +56,11 @@ restart-docker: $(TARGETS): restart-docker $(MAKE) -C ../build $@$(target-suffix) - docker run --env-file docker.env $(CONTAINER_OPTS) criu-$@ scripts/travis/travis-tests + docker run --env-file docker.env $(CONTAINER_OPTS) criu-$@ scripts/ci/run-ci-tests.sh fedora-asan: restart-docker $(MAKE) -C ../build $@$(target-suffix) - docker run -it $(CONTAINER_OPTS) criu-$@ ./scripts/travis/asan.sh $(ZDTM_OPTIONS) + docker run $(CONTAINER_OPTS) criu-$@ ./scripts/ci/asan.sh $(ZDTM_OPTIONS) docker-test: ./docker-test.sh diff --git a/scripts/travis/apt-install b/scripts/ci/apt-install similarity index 100% rename from scripts/travis/apt-install rename to scripts/ci/apt-install diff --git a/scripts/travis/asan.sh b/scripts/ci/asan.sh similarity index 100% rename from scripts/travis/asan.sh rename to scripts/ci/asan.sh diff --git a/scripts/travis/travis-after_success b/scripts/ci/ci-after-success.sh similarity index 100% rename from scripts/travis/travis-after_success rename to scripts/ci/ci-after-success.sh diff --git a/scripts/ci/ci.fmf b/scripts/ci/ci.fmf new file mode 100644 index 0000000000..d252389b95 --- /dev/null +++ b/scripts/ci/ci.fmf @@ -0,0 +1,13 @@ +summary: + Run CI test suite +environment: + CD_TO_TOP: 1 + SKIP_CI_PREP: 1 + CC: gcc +test: + ./prepare-for-fedora-rawhide.sh; + dnf install -y libselinux-devel; + systemctl stop sssd; + ln -sf /usr/include/google/protobuf/descriptor.proto ../../images/google/protobuf/descriptor.proto; + ./run-ci-tests.sh +duration: 60m diff --git a/scripts/travis/docker-test.sh b/scripts/ci/docker-test.sh similarity index 67% rename from scripts/travis/docker-test.sh rename to scripts/ci/docker-test.sh index a6990b1801..466a2c4c70 100755 --- a/scripts/travis/docker-test.sh +++ b/scripts/ci/docker-test.sh @@ -21,20 +21,25 @@ add-apt-repository \ . /etc/lsb-release -if [ "$DISTRIB_RELEASE" = "18.04" ]; then - # overlayfs behaves differently on Ubuntu (18.04) and breaks CRIU - # https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1857257 - # Switch to devicemapper - echo '{ "experimental": true, "storage-driver": "devicemapper" }' > /etc/docker/daemon.json -else - echo '{ "experimental": true }' > /etc/docker/daemon.json -fi +# overlayfs behaves differently on Ubuntu (18.04) and breaks CRIU +# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1857257 +# Switch to devicemapper +echo '{ "experimental": true, "storage-driver": "devicemapper" }' > /etc/docker/daemon.json + +CRIU_LOG='/criu.log' +mkdir -p /etc/criu +echo "log-file=$CRIU_LOG" > /etc/criu/runc.conf + +service docker stop + +# Restore with containerd versions after v1.2.14 and before v1.5.0-beta.0 is broken. +wget -nv https://github.com/containerd/containerd/releases/download/v1.5.0-beta.0/containerd-1.5.0-beta.0-linux-amd64.tar.gz -O - | tar -xz -C /usr/bin/ service docker restart -export SKIP_TRAVIS_TEST=1 +export SKIP_CI_TEST=1 -./travis-tests +./run-ci-tests.sh cd ../../ @@ -61,7 +66,7 @@ for i in $(seq 50); do docker start --checkpoint checkpoint"$i" cr 2>&1 | tee log || { cat "$(grep log 'log file:' | sed 's/log file:\s*//')" || true docker logs cr || true - cat /tmp/zdtm-core-* || true + cat $CRIU_LOG || true dmesg docker ps exit 1 diff --git a/scripts/travis/docker.env b/scripts/ci/docker.env similarity index 83% rename from scripts/travis/docker.env rename to scripts/ci/docker.env index 36154df0d6..399ced6941 100644 --- a/scripts/travis/docker.env +++ b/scripts/ci/docker.env @@ -1,4 +1,4 @@ -SKIP_TRAVIS_PREP=1 +SKIP_CI_PREP=1 ZDTM_OPTS=-x zdtm/static/binfmt_misc -x zdtm/static/sched_policy00 CC=gcc SKIP_EXT_DEV_TEST=1 diff --git a/scripts/travis/openj9-test.sh b/scripts/ci/openj9-test.sh similarity index 100% rename from scripts/travis/openj9-test.sh rename to scripts/ci/openj9-test.sh diff --git a/scripts/travis/podman-test.sh b/scripts/ci/podman-test.sh similarity index 71% rename from scripts/travis/podman-test.sh rename to scripts/ci/podman-test.sh index 509113994f..5d8a6f8400 100755 --- a/scripts/travis/podman-test.sh +++ b/scripts/ci/podman-test.sh @@ -1,13 +1,21 @@ #!/bin/bash set -x -e -o pipefail -echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_18.04/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list +if [ ! -e /etc/lsb-release ]; then + # This expects to run on Ubuntu + exit 1 +fi -wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/xUbuntu_18.04/Release.key -O- | apt-key add - +#shellcheck disable=SC1091 +. /etc/lsb-release + +echo "deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${DISTRIB_RELEASE}/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list + +curl -sL "https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/xUbuntu_${DISTRIB_RELEASE}/Release.key" | apt-key add - # podman conflicts with a man page from docker-ce # this is a podman packaging bug (https://github.com/containers/libpod/issues/4747) -apt-get -y purge docker-ce +apt-get -y purge docker-ce || : ./apt-install \ apt-transport-https \ @@ -15,11 +23,14 @@ apt-get -y purge docker-ce curl \ software-properties-common -./apt-install podman containernetworking-plugins +# explicitly install runc. crun is not compiled with criu support +./apt-install cri-o-runc podman containernetworking-plugins + +echo -e '[engine]\nruntime="runc"' > /etc/containers/containers.conf -export SKIP_TRAVIS_TEST=1 +export SKIP_CI_TEST=1 -./travis-tests +./run-ci-tests.sh cd ../../ @@ -27,6 +38,7 @@ make install # overlaysfs behaves differently on Ubuntu and breaks CRIU # https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1857257 +export STORAGE_DRIVER=vfs podman --storage-driver vfs info criu --version diff --git a/scripts/ci/prepare-for-fedora-rawhide.sh b/scripts/ci/prepare-for-fedora-rawhide.sh new file mode 100755 index 0000000000..c28a684dcf --- /dev/null +++ b/scripts/ci/prepare-for-fedora-rawhide.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e -x + +dnf install -y \ + diffutils \ + findutils \ + gcc \ + git \ + gnutls-devel \ + gzip \ + iproute \ + iptables \ + nftables \ + nftables-devel \ + libaio-devel \ + libasan \ + libbsd-devel \ + libcap-devel \ + libnet-devel \ + libnl3-devel \ + make \ + procps-ng \ + protobuf-c-devel \ + protobuf-devel \ + python3-flake8 \ + python3-PyYAML \ + python3-future \ + python3-protobuf \ + python3-junit_xml \ + python-unversioned-command \ + redhat-rpm-config \ + sudo \ + tar \ + which \ + e2fsprogs \ + rubygem-asciidoctor \ + kmod + +# /tmp is no longer 755 in the rawhide container image and breaks CI - fix it +chmod 1777 /tmp diff --git a/scripts/travis/travis-tests b/scripts/ci/run-ci-tests.sh similarity index 52% rename from scripts/travis/travis-tests rename to scripts/ci/run-ci-tests.sh index 9e11054494..53ee10bbd5 100755 --- a/scripts/travis/travis-tests +++ b/scripts/ci/run-ci-tests.sh @@ -1,10 +1,11 @@ #!/bin/bash set -x -e -TRAVIS_PKGS="protobuf-c-compiler libprotobuf-c-dev libaio-dev libgnutls28-dev +CI_PKGS="protobuf-c-compiler libprotobuf-c-dev libaio-dev libgnutls28-dev libgnutls30 libprotobuf-dev protobuf-compiler libcap-dev libnl-3-dev gdb bash libnet-dev util-linux asciidoctor - libnl-route-3-dev time ccache flake8 libbsd-dev" + libnl-route-3-dev time flake8 libbsd-dev + libperl-dev pkg-config" if [ -e /etc/lsb-release ]; then @@ -15,11 +16,11 @@ if [ -e /etc/lsb-release ]; then # There is one last test running on 16.04 because of the broken # overlayfs in 18.04. Once that is fixed we can remove the last # 16.04 based test and this if clause. - TRAVIS_PKGS="$TRAVIS_PKGS python-future python-protobuf python-yaml - python-junit.xml python-ipaddress" + CI_PKGS="$CI_PKGS python-future python-protobuf python-yaml + python-junit.xml python-ipaddress" else - TRAVIS_PKGS="$TRAVIS_PKGS python3-future python3-protobuf python3-yaml - python3-junit.xml" + CI_PKGS="$CI_PKGS python3-future python3-protobuf python3-yaml + python3-junit.xml" fi fi @@ -31,11 +32,15 @@ if [ "$UNAME_M" != "x86_64" ]; then # For Travis only x86_64 seems to be baremetal. Other # architectures are running in unprivileged LXD containers. # That seems to block most of CRIU's interfaces. - SKIP_TRAVIS_TEST=1 + + # But with the introduction of baremetal aarch64 systems in + # Travis (arch: arm64-graviton2) we can override this using + # an evironment variable + [ -n "$RUN_TESTS" ] || SKIP_CI_TEST=1 fi -travis_prep () { - [ -n "$SKIP_TRAVIS_PREP" ] && return +ci_prep () { + [ -n "$SKIP_CI_PREP" ] && return cd ../../ @@ -47,35 +52,24 @@ travis_prep () { # This can fail on aarch64 travis service apport stop || : - CC=gcc - # clang support if [ "$CLANG" = "1" ]; then - TRAVIS_PKGS="$TRAVIS_PKGS clang" + # clang support CC=clang + # If this is running in an environment without gcc installed + # compel-host-bin will fail as it is using HOSTCC. Also + # set HOSTCC to clang to build compel-host-bin with it. + export HOSTCC=clang + else + CC=gcc fi - - [ -n "$GCOV" ] && { - apt-add-repository -y "ppa:ubuntu-toolchain-r/test" - scripts/travis/apt-install --no-install-suggests g++-7 - CC=gcc-7 - } - - # ccache support, only enable for non-GCOV case - if [ "$CCACHE" = "1" ] && [ -z "$GCOV" ]; then - # ccache is installed by default, need to set it up - export CCACHE_DIR=$HOME/.ccache - [ "$CC" = "clang" ] && export CCACHE_CPP2=yes - # uncomment the following to get detailed ccache logs - #export CCACHE_LOGFILE=$HOME/ccache.log - CC="ccache $CC" - fi + CI_PKGS="$CI_PKGS $CC" # Do not install x86_64 specific packages on other architectures if [ "$UNAME_M" = "x86_64" ]; then - TRAVIS_PKGS="$TRAVIS_PKGS $X86_64_PKGS" + CI_PKGS="$CI_PKGS $X86_64_PKGS" fi - scripts/travis/apt-install "$TRAVIS_PKGS" + scripts/ci/apt-install "$CI_PKGS" chmod a+x "$HOME" # zdtm uses an unversioned python binary to run the tests. @@ -91,11 +85,71 @@ test_stream() { ./test/zdtm.py run --stream -p 2 --keep-going -T "$STREAM_TEST_PATTERN" $ZDTM_OPTS } -travis_prep +print_header() { + echo "############### $1 ###############" +} + +print_env() { + set +x + # As this script can run on multiple different CI systems + # the following lines should give some context to the + # evnvironment of this CI run. + print_header "Environment variables" + printenv + print_header "uname -a" + uname -a || : + print_header "Mounted file systems" + mount || : + print_header "Kernel command line" + cat /proc/cmdline || : + print_header "Distribution information" + [ -e /etc/lsb-release ] && cat /etc/lsb-release + [ -e /etc/redhat-release ] && cat /etc/redhat-release + [ -e /etc/alpine-release ] && cat /etc/alpine-release + if [ -e /etc/os-release ]; then + # shellcheck disable=SC1091 + . /etc/os-release + if [ "${NAME}" = "Fedora" ] && [ "${VERSION_ID}" = "33" ]; then + # The tun_ns test fails only on Fedora 33 with + # Error (criu/net.c:1818): IP tool failed on route save' + # Skip it + ZDTM_OPTS="$ZDTM_OPTS -x zdtm/static/tun_ns" + fi + if [ "${NAME}" = "Fedora" ] && [ "${VERSION_ID}" = "35" ]; then + # Error injection fails on Fedora 35 + KERN_MAJ=$(uname -r | cut -d. -f1) + KERN_MIN=$(uname -r | cut -d. -f2) + if [ "$KERN_MAJ" = "5" ] && [ "$KERN_MIN" -gt "11" ]; then + SKIP_CRIU_FAULT=1 + fi + fi + fi + + print_header "ulimit -a" + ulimit -a + print_header "Available memory" + if [ -e /etc/alpine-release ]; then + # Alpine's busybox based free does not understand -h + free + else + free -h + fi + print_header "Available CPUs" + lscpu || : + set -x +} + +print_env + +ci_prep + +if [ "${CD_TO_TOP}" = "1" ]; then + cd ../../ +fi -export GCOV +export GCOV CC $CC --version -time make CC="$CC" -j4 +time make CC="$CC" -j4 V=1 ./criu/criu -v4 cpuinfo dump || : ./criu/criu -v4 cpuinfo check || : @@ -107,7 +161,7 @@ if [ "$WIDTH" -gt 80 ]; then exit 1 fi -[ -n "$SKIP_TRAVIS_TEST" ] && exit 0 +[ -n "$SKIP_CI_TEST" ] && exit 0 ulimit -c unlimited @@ -133,22 +187,29 @@ if [ "${COMPAT_TEST}x" = "yx" ] ; then done # shellcheck disable=SC2086 apt-get remove $INCOMPATIBLE_LIBS - scripts/travis/apt-install "$IA32_PKGS" + dpkg --add-architecture i386 + scripts/ci/apt-install "$IA32_PKGS" mkdir -p /usr/lib/x86_64-linux-gnu/ mv "$REFUGE"/* /usr/lib/x86_64-linux-gnu/ fi -time make CC="$CC" -j4 -C test/zdtm +time make CC="$CC" -j4 -C test/zdtm V=1 -[ -f "$CCACHE_LOGFILE" ] && cat "$CCACHE_LOGFILE" +if [ "${COMPAT_TEST}x" = "yx" ] ; then + # Cross-verify that zdtm tests are 32-bit + file test/zdtm/static/env00 | grep 'ELF 32-bit' -q +fi # umask has to be called before a first criu run, so that .gcda (coverage data) # files are created with read-write permissions for all. umask 0000 ./criu/criu check ./criu/criu check --all || echo $? -./criu/criu cpuinfo dump -./criu/criu cpuinfo check +if [ "$UNAME_M" == "x86_64" ]; then + # This fails on aarch64 (aws-graviton2) + ./criu/criu cpuinfo dump + ./criu/criu cpuinfo check +fi export SKIP_PREP=1 # The 3.19 kernel (from Ubuntu 14.04) has a bug. When /proc/PID/pagemap @@ -172,34 +233,45 @@ fi # shellcheck disable=SC2086 ./test/zdtm.py run -a -p 2 --keep-going $ZDTM_OPTS -KERN_MAJ=$(uname -r | cut -d. -f1) -KERN_MIN=$(uname -r | cut -d. -f2) -if [ "$KERN_MAJ" -ge "4" ] && [ "$KERN_MIN" -ge "18" ]; then - LAZY_EXCLUDE="-x cmdlinenv00 -x maps007" -else - LAZY_EXCLUDE="-x maps007 -x fork -x fork2 -x uffd-events -x cgroupns - -x socket_listen -x socket_listen6 -x cmdlinenv00 - -x socket_close_data01 -x file_read -x lazy-thp -x futex" -fi -LAZY_EXCLUDE="$LAZY_EXCLUDE -x maps04" +LAZY_EXCLUDE="-x maps04 -x cmdlinenv00 -x maps007" -LAZY_TESTS='.*\(maps0\|uffd-events\|lazy-thp\|futex\|fork\).*' +LAZY_TESTS='.*(maps0|uffd-events|lazy-thp|futex|fork).*' LAZY_OPTS="-p 2 -T $LAZY_TESTS $LAZY_EXCLUDE $ZDTM_OPTS" -./test/zdtm.py run "$LAZY_OPTS" --lazy-pages -./test/zdtm.py run "$LAZY_OPTS" --remote-lazy-pages -./test/zdtm.py run "$LAZY_OPTS" --remote-lazy-pages --tls +if [ -e /proc/sys/vm/unprivileged_userfaultfd ]; then + # Starting with 5.2 to use usefaultfd in a user space + # this needs to be set. + echo 1 > /proc/sys/vm/unprivileged_userfaultfd +fi + +# shellcheck disable=SC2086 +./test/zdtm.py run $LAZY_OPTS --lazy-pages --keep-going +# shellcheck disable=SC2086 +./test/zdtm.py run $LAZY_OPTS --remote-lazy-pages --keep-going +# shellcheck disable=SC2086 +./test/zdtm.py run $LAZY_OPTS --remote-lazy-pages --tls --keep-going + +if [ -n "$SKIP_CRIU_FAULT" ]; then + bash -x ./test/jenkins/criu-fault.sh +fi -bash ./test/jenkins/criu-fault.sh -bash ./test/jenkins/criu-fcg.sh -bash ./test/jenkins/criu-inhfd.sh +if [ "$UNAME_M" == "x86_64" ]; then + # This fails on aarch64 (aws-graviton2) with: + # 33: ERR: thread-bomb.c:49: pthread_attr_setstacksize(): 22 + bash -x ./test/jenkins/criu-fcg.sh +fi +bash -x ./test/jenkins/criu-inhfd.sh if [ -z "$SKIP_EXT_DEV_TEST" ]; then make -C test/others/mnt-ext-dev/ run fi #make -C test/others/exec/ run make -C test/others/make/ run CC="$CC" -make -C test/others/shell-job/ run +if [ -n "$TRAVIS" ] || [ -n "$CIRCLECI" ]; then + # GitHub Actions (and Cirrus CI) does not provide a real TTY and CRIU will fail with: + # Error (criu/tty.c:1014): tty: Don't have tty to inherit session from, aborting + make -C test/others/shell-job/ run +fi make -C test/others/rpc/ run ./test/zdtm.py run -t zdtm/static/env00 --sibling @@ -209,6 +281,8 @@ make -C test/others/rpc/ run ./test/zdtm.py run -t zdtm/transition/maps007 --pre 2 --page-server ./test/zdtm.py run -t zdtm/transition/maps007 --pre 2 --page-server --dedup +./test/zdtm.py run -t zdtm/transition/pid_reuse --pre 2 + ./test/zdtm.py run -t zdtm/static/socket-tcp-local --norst ip net add test @@ -221,13 +295,22 @@ ip net add test ./test/zdtm.py run --empty-ns -T zdtm/static/socket-tcp*-local --iter 2 -./test/zdtm.py run -t zdtm/static/env00 -k always +./test/zdtm.py run -t zdtm/static/env00 -t zdtm/transition/fork -t zdtm/static/ghost_holes00 -k always ./test/crit-recode.py +# more crit testing +make -C test/others/crit run + # libcriu testing make -C test/others/libcriu run # external namespace testing make -C test/others/ns_ext run -make -C test/others/shell-job +# Skip all further tests when running with GCOV=1 +# The one test which currently cannot handle GCOV testing is compel/test +# Probably because the GCOV Makefile infrastructure does not exist in compel +[ -n "$GCOV" ] && exit 0 + +# compel testing +make -C compel/test diff --git a/scripts/travis/vagrant.sh b/scripts/ci/vagrant.sh similarity index 62% rename from scripts/travis/vagrant.sh rename to scripts/ci/vagrant.sh index bc10693bb8..3ed1a26754 100755 --- a/scripts/travis/vagrant.sh +++ b/scripts/ci/vagrant.sh @@ -6,31 +6,36 @@ set -e set -x -VAGRANT_VERSION=2.2.7 -FEDORA_VERSION=32 -FEDORA_BOX_VERSION=32.20200422.0 +VAGRANT_VERSION=2.2.14 +FEDORA_VERSION=33 +FEDORA_BOX_VERSION=33.20201019.0 setup() { - apt-get -qq update - # Load the kvm modules for vagrant to use qemu - modprobe kvm kvm_intel + if [ -n "$TRAVIS" ]; then + # Load the kvm modules for vagrant to use qemu + modprobe kvm kvm_intel + fi # Tar up the git checkout to have vagrant rsync it to the VM tar cf criu.tar ../../../criu - wget https://releases.hashicorp.com/vagrant/${VAGRANT_VERSION}/vagrant_${VAGRANT_VERSION}_"$(uname -m)".deb -O /tmp/vagrant.deb && \ + # Cirrus has problems with the following certificate. + wget --no-check-certificate https://releases.hashicorp.com/vagrant/${VAGRANT_VERSION}/vagrant_${VAGRANT_VERSION}_"$(uname -m)".deb -O /tmp/vagrant.deb && \ dpkg -i /tmp/vagrant.deb - ./apt-install libvirt-bin libvirt-dev qemu-utils qemu - systemctl restart libvirt-bin + ./apt-install libvirt-clients libvirt-daemon-system libvirt-dev qemu-utils qemu \ + ruby build-essential libxml2-dev qemu-kvm rsync ebtables dnsmasq-base \ + openssh-client + systemctl restart libvirtd vagrant plugin install vagrant-libvirt vagrant init fedora/${FEDORA_VERSION}-cloud-base --box-version ${FEDORA_BOX_VERSION} # The default libvirt Vagrant VM uses 512MB. # Travis VMs should have around 7.5GB. # Increasing it to 4GB should work. sed -i Vagrantfile -e 's,^end$, config.vm.provider :libvirt do |libvirt|'"\n"' libvirt.memory = 4096;end'"\n"'end,g' - vagrant up --provider=libvirt + vagrant up --provider=libvirt --no-tty mkdir -p /root/.ssh vagrant ssh-config >> /root/.ssh/config + ssh default sudo dnf upgrade -y ssh default sudo dnf install -y gcc git gnutls-devel nftables-devel libaio-devel \ libasan libcap-devel libnet-devel libnl3-devel make protobuf-c-devel \ protobuf-devel python3-flake8 python3-future python3-protobuf \ @@ -45,8 +50,9 @@ fedora-no-vdso() { vagrant reload ssh default cat /proc/cmdline ssh default 'cd /vagrant; tar xf criu.tar; cd criu; make -j 4' - # Excluding two cgroup tests which seem to fail because of cgroup2 - ssh default 'cd /vagrant/criu/test; sudo ./zdtm.py run -a --keep-going' + # BPF tests are failing see: https://github.com/checkpoint-restore/criu/issues/1354 + # Needs to be fixed, skip for now + ssh default 'cd /vagrant/criu/test; sudo ./zdtm.py run -a --keep-going -x zdtm/static/bpf_hash -x zdtm/static/bpf_array' } $1 diff --git a/scripts/criu-ns b/scripts/criu-ns new file mode 100755 index 0000000000..3ee6564e93 --- /dev/null +++ b/scripts/criu-ns @@ -0,0 +1,230 @@ +#!/usr/bin/env python +import ctypes +import ctypes.util +import errno +import sys +import os + +# constants for unshare +CLONE_NEWNS = 0x00020000 +CLONE_NEWPID = 0x20000000 + +# - constants for mount +MS_REC = 16384 +MS_PRIVATE = 1 << 18 +MS_SLAVE = 1 << 19 + +# Load libc bindings +_libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) + +try: + _unshare = _libc.unshare +except AttributeError: + raise OSError(errno.EINVAL, "unshare is not supported on this platform") +else: + _unshare.argtypes = [ctypes.c_int] + _unshare.restype = ctypes.c_int + +try: + _setns = _libc.setns +except AttributeError: + raise OSError(errno.EINVAL, "setns is not supported on this platform") +else: + _setns.argtypes = [ctypes.c_int, ctypes.c_int] + _setns.restype = ctypes.c_int + +try: + _mount = _libc.mount +except AttributeError: + raise OSError(errno.EINVAL, "mount is not supported on this platform") +else: + _mount.argtypes = [ + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_ulong, + ctypes.c_void_p + ] + _mount.restype = ctypes.c_int + + +def _mount_new_proc(): + """ + Mount new /proc filesystem. + """ + if _mount(None, b"/", None, MS_SLAVE|MS_REC, None): + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + if _mount(b'proc', b'/proc', b'proc', 0, None): + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + + +def _wait_for_process_status(criu_pid): + """ + Wait for CRIU to exit and report the status back. + """ + while True: + try: + (pid, status) = os.wait() + if pid == criu_pid: + return os.WEXITSTATUS(status) + except OSError: + return -251 + + +def run_criu(args): + """ + Spawn CRIU binary + """ + print(sys.argv) + os.execlp('criu', *['criu'] + args) + raise OSError(errno.ENOENT, "No such command") + + +def wrap_restore(): + restore_args = sys.argv[1:] + if '--restore-sibling' in restore_args: + raise OSError(errno.EINVAL, "--restore-sibling is not supported") + + # Unshare pid and mount namespaces + if _unshare(CLONE_NEWNS | CLONE_NEWPID) != 0: + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + + restore_detached = False + if '-d' in restore_args: + restore_detached = True + restore_args.remove('-d') + if '--restore-detached' in restore_args: + restore_detached = True + restore_args.remove('--restore-detached') + + criu_pid = os.fork() + if criu_pid == 0: + _mount_new_proc() + run_criu(restore_args) + + if restore_detached: + return 0 + + return _wait_for_process_status(criu_pid) + + +def get_varg(args): + for i in range(1, len(sys.argv)): + if not sys.argv[i] in args: + continue + + if i + 1 >= len(sys.argv): + break + + return (sys.argv[i + 1], i + 1) + + return (None, None) + + +def _set_namespace(fd): + """Join namespace referred to by fd""" + if _setns(fd, 0) != 0: + _errno = ctypes.get_errno() + raise OSError(_errno, errno.errorcode[_errno]) + + +def is_my_namespace(fd): + """Returns True if fd refers to current namespace""" + return os.stat('/proc/self/ns/pid').st_ino != os.fstat(fd).st_ino + + +def set_pidns(tpid, pid_idx): + """ + Join pid namespace. Note, that the given pid should + be changed in -t option, as task lives in different + pid namespace. + """ + ns_fd = os.open('/proc/%s/ns/pid' % tpid, os.O_RDONLY) + if is_my_namespace(ns_fd): + for l in open('/proc/%s/status' % tpid): + if not l.startswith('NSpid:'): + continue + ls = l.split() + if ls[1] != tpid: + os.close(ns_fd) + raise OSError(errno.ESRCH, 'No such pid') + + print('Replace pid {} with {}'.format(tpid, ls[2])) + sys.argv[pid_idx] = ls[2] + break + else: + os.close(ns_fd) + raise OSError(errno.ENOENT, 'Cannot find NSpid field in proc') + _set_namespace(ns_fd) + os.close(ns_fd) + + +def set_mntns(tpid): + """ + Join mount namespace. Trick here too -- check / and . + will be the same in target mntns. + """ + ns_fd = os.open('/proc/%s/ns/mnt' % tpid, os.O_RDONLY) + if is_my_namespace(ns_fd): + root_st = os.stat('/') + cwd_st = os.stat('.') + cwd_path = os.path.realpath('.') + + _set_namespace(ns_fd) + + os.chdir(cwd_path) + root_nst = os.stat('/') + cwd_nst = os.stat('.') + + def steq(st, nst): + return (st.st_dev, st.st_ino) == (nst.st_dev, nst.st_ino) + + if not steq(root_st, root_nst): + os.close(ns_fd) + raise OSError(errno.EXDEV, 'Target ns / is not as current') + if not steq(cwd_st, cwd_nst): + os.close(ns_fd) + raise OSError(errno.EXDEV, 'Target ns . is not as current') + os.close(ns_fd) + + +def wrap_dump(): + (pid, pid_idx) = get_varg(('-t', '--tree')) + if pid is None: + raise OSError(errno.EINVAL, 'No --tree option given') + + set_pidns(pid, pid_idx) + set_mntns(pid) + + criu_pid = os.fork() + if criu_pid == 0: + run_criu(sys.argv[1:]) + return _wait_for_process_status(pid) + + +if len(sys.argv) == 1: + print(""" +Usage: + {0} dump|pre-dump -t PID [] + {0} restore [] +\nCommands: + dump checkpoint a process/tree identified by pid + pre-dump pre-dump task(s) minimizing their frozen time + restore restore a process/tree +""".format(sys.argv[0])) + exit(1) + +action = sys.argv[1] + +if action == 'restore': + res = wrap_restore() +elif action in ['dump', 'pre-dump']: + res = wrap_dump() +else: + print('Unsupported action {} for nswrap'.format(action)) + res = -1 + +sys.exit(res) diff --git a/test/.gitignore b/test/.gitignore index 6a735ba7ae..17b4d8635f 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -14,3 +14,5 @@ /zdtm_mount_cgroups.lock /compel/handle_binary /umount2 +/javaTests/output/ +/javaTests/target/ diff --git a/test/all.fmf b/test/all.fmf new file mode 100644 index 0000000000..2bce34a64a --- /dev/null +++ b/test/all.fmf @@ -0,0 +1,6 @@ +summary: + Run all tests +discover: + how: fmf +execute: + how: tmt diff --git a/test/crit-recode.py b/test/crit-recode.py index adaf337336..4135681e11 100755 --- a/test/crit-recode.py +++ b/test/crit-recode.py @@ -1,6 +1,4 @@ #!/usr/bin/env python -# vim: noet ts=8 sw=8 sts=8 - import pycriu import sys import os diff --git a/test/inhfd/tty.py b/test/inhfd/tty.py index c11a57117e..984f60c64e 100755 --- a/test/inhfd/tty.py +++ b/test/inhfd/tty.py @@ -1,4 +1,3 @@ -# vim: noet ts=8 sw=8 sts=8 import fcntl import os import pty diff --git a/test/javaTests/src/org/criu/java/tests/CheckpointRestore.java b/test/javaTests/src/org/criu/java/tests/CheckpointRestore.java index 9d61e126f1..860619c267 100644 --- a/test/javaTests/src/org/criu/java/tests/CheckpointRestore.java +++ b/test/javaTests/src/org/criu/java/tests/CheckpointRestore.java @@ -361,7 +361,7 @@ String copyFiles() throws IOException { private void checkpoint(String pid, String checkpointOpt) throws IOException, InterruptedException { ProcessBuilder builder; System.out.println("Checkpointing process " + pid); - String command = "../../criu/criu dump --shell-job -t " + pid + " -vvv -D " + logFolder + " -o dump.log"; + String command = "../../criu/criu dump --shell-job -t " + pid + " --file-locks -v4 -D " + logFolder + " -o dump.log"; if (0 == checkpointOpt.length()) { String[] cmd = command.split(" "); builder = new ProcessBuilder(cmd); @@ -411,7 +411,7 @@ private void checkpoint(String pid, String checkpointOpt) throws IOException, In private void restore(String restoreOpt) throws IOException, InterruptedException { ProcessBuilder builder; System.out.println("Restoring process"); - String command = "../../criu/criu restore -d -vvv --shell-job -D " + logFolder + " -o restore.log"; + String command = "../../criu/criu restore -d --file-locks -v4 --shell-job -D " + logFolder + " -o restore.log"; if (0 == restoreOpt.length()) { String[] cmd = command.split(" "); builder = new ProcessBuilder(cmd); diff --git a/test/jenkins/criu-fault.sh b/test/jenkins/criu-fault.sh index f871a140b2..a8c3a5cf7f 100755 --- a/test/jenkins/criu-fault.sh +++ b/test/jenkins/criu-fault.sh @@ -28,3 +28,8 @@ fi ./test/zdtm.py run -t zdtm/static/maps04 --fault 131 --keep-going --report report --pre 2:1 || fail ./test/zdtm.py run -t zdtm/transition/maps008 --fault 131 --keep-going --report report --pre 2:1 || fail ./test/zdtm.py run -t zdtm/static/maps01 --fault 132 -f h || fail +# 134 is corrupting extended registers set, should run in a sub-thread (fpu03) +# without restore (that will check if parasite corrupts extended registers) +./test/zdtm.py run -t zdtm/static/fpu03 --fault 134 -f h --norst || fail +# also check for the main thread corruption +./test/zdtm.py run -t zdtm/static/fpu00 --fault 134 -f h --norst || fail diff --git a/test/others/crit/test.sh b/test/others/crit/test.sh index a85b4c3b2d..9d30575c48 100755 --- a/test/others/crit/test.sh +++ b/test/others/crit/test.sh @@ -1,8 +1,14 @@ +#!/bin/bash +# shellcheck disable=SC1091,SC2002 + +set -x + source ../env.sh images_list="" function _exit { + # shellcheck disable=SC2181 if [ $? -ne 0 ]; then echo "FAIL" exit 1 @@ -13,37 +19,89 @@ function gen_imgs { setsid ./loop.sh < /dev/null &> /dev/null & PID=$! $CRIU dump -v4 -o dump.log -D ./ -t $PID + # shellcheck disable=SC2181 if [ $? -ne 0 ]; then kill -9 $PID _exit 1 fi - images_list=$(ls -1 *.img) + images_list=$(ls -1 ./*.img) if [ -z "$images_list" ]; then echo "Failed to generate images" _exit 1 fi } -function run_test { +function run_test1 { for x in $images_list do echo "=== $x" - if [[ $x == pages* ]]; then + if [[ $x == *pages* ]]; then echo "skip" continue fi echo " -- to json" - $CRIT decode -o "$x"".json" --pretty < $x || _exit $? + $CRIT decode -o "$x"".json" --pretty < "$x" || _exit $? echo " -- to img" $CRIT encode -i "$x"".json" > "$x"".json.img" || _exit $? echo " -- cmp" - cmp $x "$x"".json.img" || _exit $? + cmp "$x" "$x"".json.img" || _exit $? echo "=== done" done } + +function run_test2 { + mapfile -t array <<< "$images_list" + + PROTO_IN=${array[0]} + JSON_IN=$(mktemp -p ./ tmp.XXXXXXXXXX.json) + OUT=$(mktemp -p ./ tmp.XXXXXXXXXX.log) + + # prepare + ${CRIT} decode -i "${PROTO_IN}" -o "${JSON_IN}" + + # proto in - json out decode + cat "${PROTO_IN}" | ${CRIT} decode || _exit 1 + cat "${PROTO_IN}" | ${CRIT} decode -o "${OUT}" || _exit 1 + cat "${PROTO_IN}" | ${CRIT} decode > "${OUT}" || _exit 1 + ${CRIT} decode -i "${PROTO_IN}" || _exit 1 + ${CRIT} decode -i "${PROTO_IN}" -o "${OUT}" || _exit 1 + ${CRIT} decode -i "${PROTO_IN}" > "${OUT}" || _exit 1 + ${CRIT} decode < "${PROTO_IN}" || _exit 1 + ${CRIT} decode -o "${OUT}" < "${PROTO_IN}" || _exit 1 + ${CRIT} decode < "${PROTO_IN}" > "${OUT}" || _exit 1 + + # proto in - json out encode -> should fail + cat "${PROTO_IN}" | ${CRIT} encode || true + cat "${PROTO_IN}" | ${CRIT} encode -o "${OUT}" || true + cat "${PROTO_IN}" | ${CRIT} encode > "${OUT}" || true + ${CRIT} encode -i "${PROTO_IN}" || true + ${CRIT} encode -i "${PROTO_IN}" -o "${OUT}" || true + ${CRIT} encode -i "${PROTO_IN}" > "${OUT}" || true + + # json in - proto out encode + cat "${JSON_IN}" | ${CRIT} encode || _exit 1 + cat "${JSON_IN}" | ${CRIT} encode -o "${OUT}" || _exit 1 + cat "${JSON_IN}" | ${CRIT} encode > "${OUT}" || _exit 1 + ${CRIT} encode -i "${JSON_IN}" || _exit 1 + ${CRIT} encode -i "${JSON_IN}" -o "${OUT}" || _exit 1 + ${CRIT} encode -i "${JSON_IN}" > "${OUT}" || _exit 1 + ${CRIT} encode < "${JSON_IN}" || _exit 1 + ${CRIT} encode -o "${OUT}" < "${JSON_IN}" || _exit 1 + ${CRIT} encode < "${JSON_IN}" > "${OUT}" || _exit 1 + + # json in - proto out decode -> should fail + cat "${JSON_IN}" | ${CRIT} decode || true + cat "${JSON_IN}" | ${CRIT} decode -o "${OUT}" || true + cat "${JSON_IN}" | ${CRIT} decode > "${OUT}" || true + ${CRIT} decode -i "${JSON_IN}" || true + ${CRIT} decode -i "${JSON_IN}" -o "${OUT}" || true + ${CRIT} decode -i "${JSON_IN}" > "${OUT}" || true +} + gen_imgs -run_test +run_test1 +run_test2 diff --git a/test/others/env.sh b/test/others/env.sh index 73369ad1bd..b514e87d9e 100755 --- a/test/others/env.sh +++ b/test/others/env.sh @@ -2,7 +2,16 @@ CRIU=$(readlink -f `dirname ${BASH_SOURCE[0]}`/../../criu/criu) criu=$CRIU -CRIT=$(readlink -f `dirname ${BASH_SOURCE[0]}`/../../crit/crit) +if [ $(which python3) ]; then + PYTHON=python3 +elif [ $(which python2) ]; then + PYTHON=python2 +else + echo "FAIL: Neither python3 nor python2" + exit 1 +fi +#export PYTHON +CRIT=$(readlink -f `dirname ${BASH_SOURCE[0]}`/../../crit/crit-"${PYTHON}") crit=$CRIT CRIU_COREDUMP=$(readlink -f `dirname ${BASH_SOURCE[0]}`/../../criu-coredump/criu-coredump) criu_coredump=$CRIU_COREDUMP diff --git a/test/others/libcriu/Makefile b/test/others/libcriu/Makefile index 226396e6a0..c63c224bd6 100644 --- a/test/others/libcriu/Makefile +++ b/test/others/libcriu/Makefile @@ -1,6 +1,6 @@ -include ../../../../criu/Makefile.versions +include ../../../Makefile.versions -TESTS += test_sub +TESTS += test_sub TESTS += test_self TESTS += test_notify TESTS += test_iters @@ -15,13 +15,13 @@ run: all define genb $(1): $(1).o lib.o - gcc $$^ -L ../../../../criu/lib/c/ -L ../../../../criu/images/ -lcriu -o $$@ + gcc $$^ -L ../../../lib/c/ -L ../../../images/ -lcriu -o $$@ endef $(foreach t, $(TESTS), $(eval $(call genb, $(t)))) %.o: %.c - gcc -c $^ -iquote ../../../../criu/criu/include -I../../../../criu/lib/c/ -I../../../../criu/images/ -o $@ -Werror + gcc -c $^ -iquote ../../../criu/include -I../../../lib/c/ -I../../../images/ -o $@ -Werror clean: libcriu_clean rm -rf $(TESTS) $(TESTS:%=%.o) lib.o @@ -32,5 +32,5 @@ libcriu_clean: .PHONY: libcriu_clean libcriu: - ln -s ../../../../criu/lib/c/libcriu.so libcriu.so.${CRIU_SO_VERSION_MAJOR} + ln -s ../../../lib/c/libcriu.so libcriu.so.${CRIU_SO_VERSION_MAJOR} .PHONY: libcriu diff --git a/test/others/libcriu/run.sh b/test/others/libcriu/run.sh index bd92f8544b..5a0dca46be 100755 --- a/test/others/libcriu/run.sh +++ b/test/others/libcriu/run.sh @@ -48,7 +48,10 @@ function run_test { run_test test_sub run_test test_self run_test test_notify -run_test test_iters +if [ "$(uname -m)" == "x86_64" ]; then + # Skip this on aarch64 as aarch64 has no dirty page tracking + run_test test_iters +fi run_test test_errno echo "== Tests done" diff --git a/test/others/ns_ext/Makefile b/test/others/ns_ext/Makefile index f859b89bca..d15224d52c 100644 --- a/test/others/ns_ext/Makefile +++ b/test/others/ns_ext/Makefile @@ -1,7 +1,14 @@ -run: +run: run_pidns run_net run_pid + +run_net: ./run.sh net + +run_pid: ./run.sh pid +run_pidns: + unshare -fp -m --mount-proc --propagation private ./run_pidns.sh + clean: rm -rf images output pidfile pidfile2 pidfile3 .PHONY: clean run diff --git a/test/others/ns_ext/__run_pidns.sh b/test/others/ns_ext/__run_pidns.sh new file mode 100644 index 0000000000..50cdb6b760 --- /dev/null +++ b/test/others/ns_ext/__run_pidns.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e + +status_fd=$1 + +exec 0/dev/null +exec 1>/dev/null + +# Sending our real pid to run_pidns.sh over pipe + +exec {fd}&- +echo $pid >&$status_fd +exec {status_fd}>&- + +while :; do + sleep 10000 +done diff --git a/test/others/ns_ext/_run_pidns.sh b/test/others/ns_ext/_run_pidns.sh new file mode 100644 index 0000000000..b0b266ad6d --- /dev/null +++ b/test/others/ns_ext/_run_pidns.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +status_fd=$1 + +# Note that with this block we only guaranty that pid of +# __run_pidns.sh would be somewhere around 1000, not +# exactly 1000. + +mkdir -p pidns_proc +mount -t proc proc pidns_proc +echo 1000 > pidns_proc/sys/kernel/ns_last_pid +umount -l pidns_proc +rmdir pidns_proc + +# Here we create a pipe to wait for __run_pidns.sh to die, +# when it dies the pipe_w is closed and read from pipe_r +# is unblocked. + +exec {pipe}<> <(:) +exec {pipe_r}/proc/self/fd/$pipe +exec {pipe}>&- + +setsid bash __run_pidns.sh $status_fd & +exec {pipe_w}>&- +exec {status_fd}>&- + +# Waiting for __run_pidns.sh to be checkpointed + +cat <&$pipe_r +echo "Temporary pidns init is exiting..." diff --git a/test/others/ns_ext/run_pidns.sh b/test/others/ns_ext/run_pidns.sh new file mode 100755 index 0000000000..b4c7b88a8f --- /dev/null +++ b/test/others/ns_ext/run_pidns.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +set -e + +# This test creates a process in non-host pidns and then dumps it and restores +# it into host pidns. We use pid >100000 in non-host pidns to make sure it does +# not intersect with some host pid on restore but it is potentially racy so +# please run this test only in manualy. + +CRIU=../../../criu/criu + +# This is a status pipe to report the pid of __run_pidns.sh +exec {pipe}<> <(:) +exec {pipe_r}/proc/self/fd/$pipe +exec {pipe}>&- + +unshare -p sh -c "bash _run_pidns.sh $pipe_w &" +exec {pipe_w}>&- + +PID=$(cat <&$pipe_r) +echo PID: $PID + +PIDNS=$(readlink /proc/$PID/ns/pid | sed 's/://') +echo PIDNS: $PIDNS + +BEFORE=$(grep NSpid /proc/$PID/status) +echo "before c/r: $BEFORE" + +rm -rf images_pidns || : +mkdir -p images_pidns + +echo "$CRIU dump -v4 -o dump.log -t $PID -D images_pidns --external $PIDNS:exti" +$CRIU dump -v4 -o dump.log -t $PID -D images_pidns --external $PIDNS:exti +RESULT=$? +cat images_pidns/dump.log | grep -B 5 Error || echo ok +[ "$RESULT" != "0" ] && { + echo "CRIU dump failed" + echo FAIL + exit 1 +} + +exec {pidns_fd}< /proc/self/ns/pid + +echo "$CRIU restore -v4 -o restore.log -D images_pidns --restore-detached --inherit-fd fd[$pidns_fd]:exti" +$CRIU restore -v4 -o restore.log -D images_pidns --restore-detached --inherit-fd fd[$pidns_fd]:exti --pidfile test.pidfile +RESULT=$? +cat images_pidns/restore.log | grep -B 5 Error || echo ok +[ "$RESULT" != "0" ] && { + echo "CRIU restore failed" + echo FAIL + exit 1 +} + +PID=$(cat images_pidns/test.pidfile) +AFTER=$(grep NSpid /proc/$PID/status) +echo "after c/r: $AFTER" +echo PASS +exit 0 diff --git a/test/zdtm.py b/test/zdtm.py index dff64d4ed5..9488c6edb9 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# vim: noet ts=8 sw=8 sts=8 from __future__ import absolute_import, division, print_function, unicode_literals import argparse @@ -1043,19 +1042,19 @@ def fini(self): ret = self.__dump_process.wait() if self.__lazy_pages_p: ret = self.__lazy_pages_p.wait() - grep_errors(os.path.join(self.__ddir(), "lazy-pages.log")) + grep_errors(os.path.join(self.__ddir(), "lazy-pages.log"), err=ret) self.__lazy_pages_p = None if ret: raise test_fail_exc("criu lazy-pages exited with %s" % ret) if self.__page_server_p: ret = self.__page_server_p.wait() - grep_errors(os.path.join(self.__ddir(), "page-server.log")) + grep_errors(os.path.join(self.__ddir(), "page-server.log"), err=ret) self.__page_server_p = None if ret: raise test_fail_exc("criu page-server exited with %s" % ret) if self.__dump_process: ret = self.__dump_process.wait() - grep_errors(os.path.join(self.__ddir(), "dump.log")) + grep_errors(os.path.join(self.__ddir(), "dump.log"), err=ret) self.__dump_process = None if ret: raise test_fail_exc("criu dump exited with %s" % ret) @@ -1357,7 +1356,7 @@ def dump(self, action, opts=[]): if self.__page_server_p: ret = self.__page_server_p.wait() - grep_errors(os.path.join(self.__ddir(), "page-server.log")) + grep_errors(os.path.join(self.__ddir(), "page-server.log"), err=ret) self.__page_server_p = None if ret: raise test_fail_exc("criu page-server exited with %d" % ret) @@ -2165,7 +2164,7 @@ def print_error(line): return False -def grep_errors(fname): +def grep_errors(fname, err=False): first = True print_next = False before = [] @@ -2186,6 +2185,17 @@ def grep_errors(fname): if print_next: print_next = print_error(line) before = [] + + # If process failed but there are no errors in log, + # let's just print the log tail, probably it would + # be helpful. + if err and first: + print_fname(fname, 'log') + print_sep("grep Error (no)", "-", 60) + first = False + for i in before: + print_next = print_error(i) + if not first: print_sep("ERROR OVER", "-", 60) diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile index aae49832a7..53dee92c5c 100644 --- a/test/zdtm/static/Makefile +++ b/test/zdtm/static/Makefile @@ -14,6 +14,7 @@ TST_NOFILE := \ fpu00 \ fpu01 \ fpu02 \ + fpu03 \ arm-neon00 \ futex \ futex-rl \ @@ -302,6 +303,7 @@ TST_FILE = \ file_lease03 \ file_lease04 \ file_locks00 \ + file_locks00_fail \ file_locks01 \ file_locks02 \ file_locks03 \ @@ -314,6 +316,8 @@ TST_FILE = \ netns-nft \ maps_file_prot \ socket_close_data01 \ + fifo_upon_unix_socket00 \ + fifo_upon_unix_socket01 \ TST_DIR = \ cwd00 \ @@ -493,6 +497,8 @@ $(TST): | $(LIB) aio00: LDLIBS += -laio different_creds: LDLIBS += -lcap file_locks06 file_locks07 file_locks08: ofd_file_locks.o +fpu03: CFLAGS += -pthread -D ZDTM_FPU00_RUN_IN_THREAD +fpu03: LDFLAGS += -pthread futex: CFLAGS += -pthread futex: LDFLAGS += -pthread futex-rl: CFLAGS += -pthread @@ -591,6 +597,8 @@ thread_different_uid_gid: LDLIBS += -pthread -lcap bpf_hash: LDLIBS += -lbpf bpf_array: LDLIBS += -lbpf +fifo_upon_unix_socket01: CFLAGS += -DFIFO_UPON_UNIX01 + $(LIB): force $(Q) $(MAKE) -C $(LIBDIR) diff --git a/test/zdtm/static/fifo_upon_unix_socket00.c b/test/zdtm/static/fifo_upon_unix_socket00.c new file mode 100644 index 0000000000..f75965fdd7 --- /dev/null +++ b/test/zdtm/static/fifo_upon_unix_socket00.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zdtmtst.h" + +const char *test_doc = "Check that fifo upon ghost socket configuration is restored"; +const char *test_author = "Andrey Zhadchenko "; + +char *filename; +TEST_OPTION(filename, string, "socket name", 1); + +#ifndef FIFO_UPON_UNIX01 +static int fill_sock_name(struct sockaddr_un *name, const char *filename) +{ + char *cwd; + + cwd = get_current_dir_name(); + if (strlen(filename) + strlen(cwd) + 1 >= sizeof(name->sun_path)) { + pr_err("Name %s/%s is too long for socket\n", + cwd, filename); + return -1; + } + + name->sun_family = AF_LOCAL; + ssprintf(name->sun_path, "%s/%s", cwd, filename); + return 0; +} +#else +static int fill_sock_name(struct sockaddr_un *name, const char *filename) +{ + if (strlen(filename) + 1 >= sizeof(name->sun_path)) { + pr_err("Name %s is too long for socket\n", filename); + return -1; + } + + name->sun_family = AF_LOCAL; + ssprintf(name->sun_path, "%s", filename); + return 0; +} +#endif + +static int sk_alloc_bind(int type, struct sockaddr_un *addr) +{ + int sk; + + sk = socket(PF_UNIX, type, 0); + if (sk < 0) { + pr_perror("socket"); + return -1; + } + + if (addr && bind(sk, (const struct sockaddr *)addr, sizeof(*addr))) { + pr_perror("bind %s", addr->sun_path); + close(sk); + return -1; + } + + return sk; +} + +static int sk_alloc_connect(int type, struct sockaddr_un *addr) +{ + int sk; + + sk = socket(PF_UNIX, type, 0); + if (sk < 0) { + pr_perror("socket"); + return -1; + } + + if (connect(sk, (const struct sockaddr *)addr, sizeof(*addr))) { + pr_perror("connect %s", addr->sun_path); + close(sk); + return -1; + } + + return sk; +} + +static int check_fd(int fdin, int fdout) +{ + int ret; + char c = 0; + + ret = write(fdin, &c, 1); + if (ret != 1) + goto err; + + ret = read(fdout, &c, 1); + if (ret != 1) + goto err; + + return 0; + +err: + pr_perror("broken fd pair %d %d", fdin, fdout); + return -1; +} + +int main(int argc, char **argv) +{ + int sk1, sk2, fd1, err; + struct sockaddr_un addr; + + test_init(argc, argv); + + unlink(filename); + + if (fill_sock_name(&addr, filename)) + return 1; + + sk1 = sk_alloc_bind(SOCK_DGRAM, &addr); + if (sk1 < 0) { + pr_perror("Can't create sk"); + return 1; + } + + sk2 = sk_alloc_connect(SOCK_DGRAM, &addr); + if (sk2 < 0) { + pr_perror("Can't connect to sk"); + return 1; + } + + if (unlink(filename) < 0) { + fail("can't unlink %s", filename); + return 1; + } + + if (mkfifo(filename, 0666)) { + pr_perror("can't make fifo \"%s\"", filename); + return 1; + } + + fd1 = open(filename, O_RDWR); + if (fd1 < 0) { + pr_perror("can't open %s", filename); + return 1; + } + +#ifdef FIFO_UPON_UNIX01 + chdir("/"); +#endif + + test_daemon(); + test_waitsig(); + + unlink(filename); + + err = check_fd(sk2, sk1) || check_fd(fd1, fd1); + if (err) + fail(); + else + pass(); + + close(sk1); + close(sk2); + close(fd1); + + return 0; +} diff --git a/test/zdtm/static/fifo_upon_unix_socket00.desc b/test/zdtm/static/fifo_upon_unix_socket00.desc new file mode 100644 index 0000000000..2eac7e654b --- /dev/null +++ b/test/zdtm/static/fifo_upon_unix_socket00.desc @@ -0,0 +1 @@ +{'flags': 'suid'} diff --git a/test/zdtm/static/fifo_upon_unix_socket01.c b/test/zdtm/static/fifo_upon_unix_socket01.c new file mode 120000 index 0000000000..235241eb30 --- /dev/null +++ b/test/zdtm/static/fifo_upon_unix_socket01.c @@ -0,0 +1 @@ +fifo_upon_unix_socket00.c \ No newline at end of file diff --git a/test/zdtm/static/fifo_upon_unix_socket01.desc b/test/zdtm/static/fifo_upon_unix_socket01.desc new file mode 120000 index 0000000000..d29414c1d5 --- /dev/null +++ b/test/zdtm/static/fifo_upon_unix_socket01.desc @@ -0,0 +1 @@ +fifo_upon_unix_socket00.desc \ No newline at end of file diff --git a/test/zdtm/static/file_locks00_fail.c b/test/zdtm/static/file_locks00_fail.c new file mode 120000 index 0000000000..d523748259 --- /dev/null +++ b/test/zdtm/static/file_locks00_fail.c @@ -0,0 +1 @@ +file_locks00.c \ No newline at end of file diff --git a/test/zdtm/static/file_locks00_fail.desc b/test/zdtm/static/file_locks00_fail.desc new file mode 100644 index 0000000000..6626fefa80 --- /dev/null +++ b/test/zdtm/static/file_locks00_fail.desc @@ -0,0 +1 @@ +{'flags': 'excl crfail'} diff --git a/test/zdtm/static/fpu00.c b/test/zdtm/static/fpu00.c index 04aa738fdf..6f2e1c2bf7 100644 --- a/test/zdtm/static/fpu00.c +++ b/test/zdtm/static/fpu00.c @@ -1,4 +1,5 @@ #include +#include #include "zdtmtst.h" @@ -44,21 +45,11 @@ int chk_proc_fpu(void) return edx & CPUID_FEAT_EDX_FPU; } -#endif -int main(int argc, char ** argv) +void *run_fpu_test(void *unused) { -#if defined(__i386__) || defined(__x86_64__) float a, b, c, d; float res1, res2; -#endif - - test_init(argc, argv); -#if defined(__i386__) || defined(__x86_64__) - if (!chk_proc_fpu()) { - skip("FPU not supported"); - return 1; - } a = drand48(); b = drand48(); @@ -80,8 +71,50 @@ int main(int argc, char ** argv) fail("%f != %f\n", res1, res2); else pass(); + + return (void *)(uintptr_t)(res1 != res2); +} + +int main(int argc, char ** argv) +{ + test_init(argc, argv); + + if (!chk_proc_fpu()) { + skip("FPU not supported"); + return 1; + } + + +#ifdef ZDTM_FPU00_RUN_IN_THREAD + /* Check if thread's fpu state is preserved */ + { + pthread_t child; + void *ret; + + if (pthread_create(&child, NULL, &run_fpu_test, NULL)) { + pr_perror("Can't create pthread\n"); + exit(1); + } + + if (pthread_join(child, &ret)) { + pr_perror("Can't join pthread\n"); + exit(1); + } + + exit(!!ret); + } #else - skip("Unsupported arch"); + return !!run_fpu_test(NULL); #endif +} + +#else + +int main(int argc, char *argv[]) +{ + test_init(argc, argv); + skip("Unsupported arch"); return 0; } + +#endif diff --git a/test/zdtm/static/fpu03.c b/test/zdtm/static/fpu03.c new file mode 120000 index 0000000000..158732c022 --- /dev/null +++ b/test/zdtm/static/fpu03.c @@ -0,0 +1 @@ +fpu00.c \ No newline at end of file diff --git a/test/zdtm/static/fpu03.desc b/test/zdtm/static/fpu03.desc new file mode 100644 index 0000000000..d2f501de20 --- /dev/null +++ b/test/zdtm/static/fpu03.desc @@ -0,0 +1 @@ +{'arch': 'x86_64'} diff --git a/test/zdtm/static/get_smaps_bits.c b/test/zdtm/static/get_smaps_bits.c index 9253f4db6d..3debaa7527 100644 --- a/test/zdtm/static/get_smaps_bits.c +++ b/test/zdtm/static/get_smaps_bits.c @@ -109,7 +109,7 @@ int get_smaps_bits(unsigned long where, unsigned long *flags, unsigned long *mad while (fgets(buf, sizeof(buf), smaps)) { is_vma_range_fmt(buf, &start, &end); - if (!strncmp(buf, "VmFlags: ", 9) && start == where) { + if (!strncmp(buf, "VmFlags: ", 9) && start <= where && where < end) { found = 1; parse_vmflags(buf, flags, madv); break; diff --git a/test/zdtm/static/netns-nf.desc b/test/zdtm/static/netns-nf.desc index 496477aa7a..67c294888c 100644 --- a/test/zdtm/static/netns-nf.desc +++ b/test/zdtm/static/netns-nf.desc @@ -1,6 +1,6 @@ { 'deps': [ '/bin/sh', - '/sbin/iptables', - '/usr/lib64/xtables/libxt_standard.so|/usr/lib/iptables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so', + '/sbin/iptables|/usr/sbin/iptables', + '/usr/lib64/xtables/libxt_standard.so|/usr/lib/iptables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so|/usr/lib/aarch64-linux-gnu/xtables/libxt_standard.so', '/usr/bin/diff'], 'flags': 'suid', 'flavor': 'ns uns'} diff --git a/test/zdtm/static/overmount_sock.desc b/test/zdtm/static/overmount_sock.desc index 1ba68c314d..cc9d23ab9f 100644 --- a/test/zdtm/static/overmount_sock.desc +++ b/test/zdtm/static/overmount_sock.desc @@ -1 +1 @@ -{'flavor' : 'ns uns', 'flags': 'suid crfail'} +{'flavor' : 'ns uns', 'flags': 'suid'} diff --git a/test/zdtm/static/sigaltstack.c b/test/zdtm/static/sigaltstack.c index f36d409f5e..13602d3a1b 100644 --- a/test/zdtm/static/sigaltstack.c +++ b/test/zdtm/static/sigaltstack.c @@ -14,8 +14,10 @@ const char *test_doc = "Check for alternate signal stack"; const char *test_author = "Cyrill Gorcunov "; -static char stack_thread[SIGSTKSZ + TEST_MSG_BUFFER_SIZE] __stack_aligned__; -static char stack_main[SIGSTKSZ + TEST_MSG_BUFFER_SIZE] __stack_aligned__; +#define TESTSIGSTKSZ 16384 + +static char stack_thread[TESTSIGSTKSZ + TEST_MSG_BUFFER_SIZE] __stack_aligned__; +static char stack_main[TESTSIGSTKSZ + TEST_MSG_BUFFER_SIZE] __stack_aligned__; enum { SAS_MAIN_OLD, diff --git a/test/zdtm/static/socket-tcp-closed-last-ack.desc b/test/zdtm/static/socket-tcp-closed-last-ack.desc index 8a9df7fc40..8a83de6fdc 100644 --- a/test/zdtm/static/socket-tcp-closed-last-ack.desc +++ b/test/zdtm/static/socket-tcp-closed-last-ack.desc @@ -1,7 +1,7 @@ { 'deps': [ '/bin/sh', - '/sbin/iptables', - '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/s390x-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so', - '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so', + '/sbin/iptables|/usr/sbin/iptables', + '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/s390x-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so|/usr/lib/aarch64-linux-gnu/xtables/libxt_tcp.so', + '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so|/usr/lib/aarch64-linux-gnu/xtables/libxt_standard.so', ], 'opts': '--tcp-established', 'flags': 'suid nouser samens', diff --git a/test/zdtm/static/socket-tcp-reseted.desc b/test/zdtm/static/socket-tcp-reseted.desc index 94425b44e7..a5c1a209de 100644 --- a/test/zdtm/static/socket-tcp-reseted.desc +++ b/test/zdtm/static/socket-tcp-reseted.desc @@ -1,8 +1,8 @@ { 'deps': [ '/bin/sh', - '/sbin/iptables', - '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so|/usr/lib/s390x-linux-gnu/xtables/libxt_tcp.so', - '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so', - '/usr/lib64/xtables/libipt_REJECT.so|/lib/xtables/libipt_REJECT.so|/usr/lib/powerpc64le-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/x86_64-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/xtables/libipt_REJECT.so|/usr/lib/s390x-linux-gnu/xtables/libipt_REJECT.so', + '/sbin/iptables|/usr/sbin/iptables', + '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so|/usr/lib/s390x-linux-gnu/xtables/libxt_tcp.so|/usr/lib/aarch64-linux-gnu/xtables/libxt_tcp.so', + '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so|/usr/lib/aarch64-linux-gnu/xtables/libxt_standard.so', + '/usr/lib64/xtables/libipt_REJECT.so|/lib/xtables/libipt_REJECT.so|/usr/lib/powerpc64le-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/x86_64-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/xtables/libipt_REJECT.so|/usr/lib/s390x-linux-gnu/xtables/libipt_REJECT.so|/usr/lib/aarch64-linux-gnu/xtables/libipt_REJECT.so', ], 'opts': '--tcp-established', 'flags': 'suid nouser samens', diff --git a/test/zdtm/static/socket-tcp-syn-sent.desc b/test/zdtm/static/socket-tcp-syn-sent.desc index b9f3d5e6db..d59e59717d 100644 --- a/test/zdtm/static/socket-tcp-syn-sent.desc +++ b/test/zdtm/static/socket-tcp-syn-sent.desc @@ -1,7 +1,7 @@ { 'deps': [ '/bin/sh', - '/sbin/iptables', - '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so|/usr/lib/s390x-linux-gnu/xtables/libxt_tcp.so', - '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so', + '/sbin/iptables|/usr/sbin/iptables', + '/usr/lib64/xtables/libxt_tcp.so|/lib/xtables/libxt_tcp.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_tcp.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_tcp.so|/usr/lib/xtables/libxt_tcp.so|/usr/lib/s390x-linux-gnu/xtables/libxt_tcp.so|/usr/lib/aarch64-linux-gnu/xtables/libxt_tcp.so', + '/usr/lib64/xtables/libxt_standard.so|/lib/xtables/libxt_standard.so|/usr/lib/powerpc64le-linux-gnu/xtables/libxt_standard.so|/usr/lib/x86_64-linux-gnu/xtables/libxt_standard.so|/usr/lib/xtables/libxt_standard.so|/usr/lib/s390x-linux-gnu/xtables/libxt_standard.so|/usr/lib/aarch64-linux-gnu/xtables/libxt_standard.so', ], 'opts': '--tcp-established', 'flags': 'suid nouser samens', diff --git a/test/zdtm/static/socket_aio.c b/test/zdtm/static/socket_aio.c index b276adb2b8..1dcdfec42c 100644 --- a/test/zdtm/static/socket_aio.c +++ b/test/zdtm/static/socket_aio.c @@ -5,7 +5,7 @@ const char *test_author = "Andrew Vagin "; /* Description: * Create two tcp socket, server send asynchronous request on - * read data and clietn write data after migration + * read data and client write data after migration */ #include diff --git a/test/zdtm/static/socket_listen.c b/test/zdtm/static/socket_listen.c index d0acfd9118..a4d3e66bd7 100644 --- a/test/zdtm/static/socket_listen.c +++ b/test/zdtm/static/socket_listen.c @@ -16,7 +16,7 @@ const char *test_author = "Stanislav Kinsbursky "; /* Description: * Create two tcp socket, server send asynchronous request on - * read data and clietn write data after migration + * read data and client write data after migration */ #include diff --git a/test/zdtm/static/socket_udp-corked.c b/test/zdtm/static/socket_udp-corked.c index 30cfac5ec6..540b9456a0 100644 --- a/test/zdtm/static/socket_udp-corked.c +++ b/test/zdtm/static/socket_udp-corked.c @@ -5,7 +5,7 @@ const char *test_author = "Pavel Emelyanov \n"; /* Description: * Create two tcp socket, server send asynchronous request on - * read data and clietn write data after migration + * read data and client write data after migration */ #include diff --git a/test/zdtm/static/socket_udp.c b/test/zdtm/static/socket_udp.c index 36a954237c..cce29fc96e 100644 --- a/test/zdtm/static/socket_udp.c +++ b/test/zdtm/static/socket_udp.c @@ -5,7 +5,7 @@ const char *test_author = "Pavel Emelyanov \n"; /* Description: * Create two tcp socket, server send asynchronous request on - * read data and clietn write data after migration + * read data and client write data after migration */ #include diff --git a/test/zdtm/static/socket_udplite.c b/test/zdtm/static/socket_udplite.c index 229005a106..763df319bb 100644 --- a/test/zdtm/static/socket_udplite.c +++ b/test/zdtm/static/socket_udplite.c @@ -5,7 +5,7 @@ const char *test_author = "Pavel Emelyanov \n"; /* Description: * Create two tcp socket, server send asynchronous request on - * read data and clietn write data after migration + * read data and client write data after migration */ #include diff --git a/test/zdtm/static/vt.c b/test/zdtm/static/vt.c index 0d843c4cf2..6c96fceba9 100644 --- a/test/zdtm/static/vt.c +++ b/test/zdtm/static/vt.c @@ -16,7 +16,7 @@ char *filename; TEST_OPTION(filename, string, "file name", 1); #ifdef __s390x__ -#define MINOR 64 /* ttyS0 */ +#define MINOR 65 /* ttysclp0 */ #else #define MINOR 5 #endif diff --git a/test/zdtm/transition/thread-bomb.c b/test/zdtm/transition/thread-bomb.c index 6621b18ed2..c1b825be7b 100644 --- a/test/zdtm/transition/thread-bomb.c +++ b/test/zdtm/transition/thread-bomb.c @@ -18,17 +18,31 @@ static const size_t stack_size = 64 * 1024; static void *thread_fn(void *arg) { pthread_t t, p, *self; + int err; if (arg) { p = *(pthread_t *)arg; - pthread_join(p, NULL); + err = pthread_join(p, NULL); free(arg); + if (err) { + pr_err("pthread_join(): %d\n", err); + return NULL; + } } self = malloc(sizeof(*self)); + if (!self) { + pr_perror("malloc()"); + return NULL; + } + *self = pthread_self(); - pthread_create(&t, &attr, thread_fn, self); + err = pthread_create(&t, &attr, thread_fn, self); + if (err) { + pr_err("pthread_create(): %d\n", err); + free(self); + } return NULL; } @@ -38,6 +52,8 @@ int main(int argc, char **argv) char *val; int err; + test_init(argc, argv); + err = pthread_attr_init(&attr); if (err) { pr_err("pthread_attr_init(): %d\n", err); @@ -56,8 +72,6 @@ int main(int argc, char **argv) test_msg("%d\n", max_nr); - test_init(argc, argv); - for (i = 0; i < max_nr; i++) { pthread_t p; err = pthread_create(&p, &attr, thread_fn, NULL); diff --git a/test/zdtm_ct.c b/test/zdtm_ct.c index 5495d61eb7..d9823da3fc 100644 --- a/test/zdtm_ct.c +++ b/test/zdtm_ct.c @@ -9,6 +9,7 @@ #include #include #include +#include #ifndef CLONE_NEWTIME #define CLONE_NEWTIME 0x00000080 /* New time namespace */ @@ -42,7 +43,28 @@ static inline int _settime(clockid_t clk_id, time_t offset) static int create_timens() { - int fd; + struct utsname buf; + unsigned major, minor; + int fd, ret; + + /* + * Before the 5.11 kernel, there is a known issue. + * start_time in /proc/pid/stat is printed in the host time + * namespace, but /proc/uptime is shown in the current time + * namespace, so criu can't compare them to detect tasks that + * reuse old pids. + */ + ret = uname(&buf); + if (ret) + return -1; + + if (sscanf(buf.release, "%u.%u", &major, &minor) != 2) + return -1; + + if (major == 5 && minor < 11) { + fprintf(stderr, "timens isn't supported on %s\n", buf.release); + return 0; + } if (unshare(CLONE_NEWTIME)) { if (errno == EINVAL) {