mnt_id was declared as uint32_t but assigned -1 when absent, which yields
UINT32_MAX. When passed to mntns_get_root_by_mnt_id(int), the conversion
from UINT32_MAX to int is implementation-defined and can become a large
positive value, potentially causing lookup_nsid_by_mnt_id() to return NULL
and trigger BUG_ON().
Change mnt_id to int with an explicit cast from f_handle->mnt_id when
present, so the sentinel -1 is preserved reliably across platforms.
Signed-off-by: 3idey <elaidya225@gmail.com>
Add checks for mntns_get_root_by_mnt_id() return value before using it
in openat() calls. This prevents using an invalid file descriptor if
the mount namespace root lookup fails.
Add error logging when mntns_get_root_by_mnt_id() fails to provide
diagnostic context about which mount namespace and path were being
accessed.
Also rename local variable 'path' to 'fpath' to avoid shadowing the
outer scope variable of the same name.
Signed-off-by: 3idey <elaidya225@gmail.com>
Before that patch we always had exactly the same magic and extentsion
patterns generated.
While on it let's fix the data restrictions:
- The length of extension and magic should be non-zero.
- Let's explicitly wrap extension characters with 256.
Fixes: #2886
Co-developed-by: Andrei Vagin <avagin@google.com>
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
In ns flavor for instance the binfmt_misc is not c/r-ed, but without
this cleanup the test will not detect that as it would inherit correct
unchanged binfmt_misc step which was there on dump.
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
In zdtm/static/binfmt_misc we print the string which contains random
bytes, some of which may not map to utf-8. So let's ignore those bytes
we can't show in utf-8.
Else we can get:
File ".../criu/test/zdtm.py", line 660, in print_output
print(output.read())
~~~~~~~~~~~^^
File "<frozen codecs>", line 325, in decode
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe3 in position 180: invalid continuation byte
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
The Linux kernel already supports per user namespace sandboxed mounts:
21ca59b365c0 ("binfmt_misc: enable sandboxed mounts")
The overall dump/restore logic is based on the fact that a binfmt_misc
superblock is static and is allocated once per a binfmt_misc mount
within a given user namespace. So the code makes a temporary mount to
read/write the entries and then removes it. The binfmt_misc data is
stored to the USERNS image.
Signed-off-by: Dmitry Sepp <dmitry.sepp@virtuozzo.com>
Detect if the kernel supports binfmt_misc sandboxing and store the
result in kerndat.
The result will be used by the code that dumps and restores binfmt_misc
entries.
Signed-off-by: Dmitry Sepp <dmitry.sepp@virtuozzo.com>
Remove the OpenVZ specific code that was not used outside of the OpenVZ
infrastructure in preparation for implementing support for upstream
Linux kernel binfmt_misc sandboxing.
Signed-off-by: Dmitry Sepp <dmitry.sepp@virtuozzo.com>
Change compel_task_size() to probe the actual kernel address space
limit at runtime via mmap() with MAP_FIXED_NOREPLACE flag.
On systems where the CPU supports 5-level paging but the kernel
doesn't have it enabled, we need to detect the actual task size the
kernel is using.
On x86-64, task_size is either (1UL << 47) for 4-level paging or
(1UL << 56) for 5-level paging. The function probes by attempting
to mmap at the 4-level boundary and if it succeeds or the address
is already occupied, the kernel supports 5-level paging.
Fixes: #2877
Signed-off-by: Tushar Mohapatra <tusharmohapatra.gig@gmail.com>
Add a test that creates a SIGEV_THREAD_ID posix timer targeting the
thread leader in a multithreaded process. This exercises the code path
where encode_notify_thread_id() must look up the thread leader's vtid
in threads[0].ns[0].virt.
Without the fix in the previous commit, this test fails during dump
with:
Error (criu/timer.c:329): Unable to convert the notify thread id <pid>
The test creates a worker thread (so the process is multithreaded, which
triggers the collect_threads() path) and a SIGEV_THREAD_ID timer on the
main thread, then verifies the timer still fires after C/R.
Signed-off-by: 3idey <elaidya225@gmail.com>
In collect_threads(), threads[0] (the thread leader) gets its .real pid
set but .ns[0].virt is left uninitialized. Later in dump_one_task(),
item->pid->ns[0].virt is set from the parasite misc data, but
threads[0].ns[0].virt is not populated until dump_task_threads() runs.
The problem is that parasite_dump_posix_timers_seized() is called
*before* dump_task_threads(). When a posix timer has SIGEV_THREAD_ID
notification targeting the thread leader, encode_notify_thread_id()
looks up threads[i].ns[0].virt and finds an uninitialized (garbage)
value for threads[0]. This causes:
- On dump: the garbage vtid is written into the image as
notify_thread_id.
- On restore: timer_create() fails with -EINVAL because the
notify_thread_id is invalid.
Fix this by initializing threads[0].ns[0].virt right after
item->pid->ns[0].virt is set, before the posix timer dump.
Fixes: #2887
Signed-off-by: 3idey <elaidya225@gmail.com>
page_pipe_from_pagemap() opens a page_read via open_page_read() but
never calls pr.close() on error or success paths. This leaks the
pagemap and pages image file descriptors, and any parent page_read
chain.
Add proper cleanup using goto err pattern to ensure pr.close() is
always called before returning. Also destroy the page_pipe on
fill_page_pipe() failure to avoid leaking its memory and pipe FDs.
Signed-off-by: 3idey <elaidya225@gmail.com>
In container setups, particularly k8s, cgroup may be used to
restrict what GPUs a process has access to. CRIU may then be used
to checkpoint that process while CRIU itself has full access to
all GPUs.
In this case, the kfd CRIU ioctl will return only a subset of the
ioctls visible through sysfs.
Don't write these devices or any links connected to them to the
tology dump files.
Signed-off-by: David Francis <David.Francis@amd.com>
When irmap_queue_cache fails, the remaining fanotify mark entries
and the mark array are not freed, causing a memory leak.
Add cleanup label to ensure proper deallocation of remaining entries
and the mark array on error paths.
Signed-off-by: 3idey <elaidya225@gmail.com>
When irmap_queue_cache fails, the remaining inotify watch descriptors
and the wd array are not freed, causing a memory leak.
Add cleanup label to ensure proper deallocation of remaining entries
and the wd array on error paths.
Signed-off-by: 3idey <elaidya225@gmail.com>
Error messages now include additional context to aid debugging:
- Device and inode numbers for file handles
- Watch descriptor (wd) for inotify operations
- Mask values for fanotify marks
- Mount ID for mount-related operations
This makes it much easier to diagnose checkpoint/restore failures
by providing complete information about which watch or mark failed
and why, without needing to add debug logging.
Signed-off-by: 3idey <elaidya225@gmail.com>
When restoring inotify watches, the restore_one_inotify() function opens
a file descriptor via get_mark_path() and stores it in the 'target'
variable. However, in two error paths, the function returns directly
without properly closing this file descriptor:
1. When INOTIFY_IOC_SETNEXTWD ioctl fails (line 556)
2. When has_inotify_setnextwd is true but watch descriptor mismatch
occurs (line 577)
Both cases bypass the cleanup code at the 'err' label which calls
close_safe(&target), resulting in a file descriptor leak.
Fix this by using 'goto err' instead of 'return -1' in both error paths,
ensuring proper cleanup of the target file descriptor.
Signed-off-by: 3idey <elaidya225@gmail.com>
Add repository-specific guidance for GitHub Copilot in
.github/copilot-instructions.md. This file includes information about:
- Coding style (Linux Kernel Coding Style)
- Architectural overview of the project
- PIE code requirements (must be self-contained and depend on compel)
- Descriptions of CRIU commands
- ZDTM test suite details
- Commit message formatting guidelines
This is just initial skeleton designed to optimize GitHub Copilot
reviews.
Signed-off-by: Andrei Vagin <avagin@gmail.com>
When `opts.pid` is explicitly set to `os.getpid()`, `pycriu` fails to
daemonize the `criu` process. This causes `criu` to run as a child of
the dumped process, leading to the error "The criu itself is within
dumped tree".
This can be fixed by modifying `_send_req_and_recv_resp` to check if the
target PID matches the current process PID. If so, it enables daemon
mode, ensuring `criu` is detached and the dump succeeds.
Signed-off-by: unichronic <ishuvam.pal@gmail.com>
In a simple case where the parent process and the child one are in one
pid namespace we can safely use vpid(item) to prace the child. But, for
the cases where the child is a pid namespace init, or the child is put
into external pid namespace, the parent and the child have different pid
namespaces and using pid vpid(item) (which e.g. for init will always be
1 here) to ptrace the child process is inorrect.
Let's use the pid reported to us from clone as it's always the right pid
of the child from the parent's point of view.
Fixes: 7dd583002 ("restore: add infrastructure to enable shadow stack")
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
The return value of sys_rseq was previously ignored during
unregistration, under the assumption that it would not fail if the rseq
structure was properly registered.
However, if sys_rseq fails, the kernel retains the registration. If the
memory containing the rseq structure is subsequently unmapped or reused,
kernel updates to the rseq area can cause the process to crash (e.g.,
via SIGSEGV).
Check the return value of sys_rseq. If it fails, log the error code and
abort the restoration process. This makes rseq unregistration failures
fatal and explicit, aiding in debugging and preventing later obscure
crashes.
Signed-off-by: liqiang2020 <liqiang64@huawei.com>
Fedora rawhide ships a pre-release of GCC 16 which produces following
error:
uprobes.c:34:22: error: variable ‘dummy’ set but not used [-Werror=unused-but-set-variable=]
34 | volatile int dummy = 0;
| ^~~~~
Marking this variable as "__maybe_unused" to fix the error.
Signed-off-by: Adrian Reber <areber@redhat.com>
In some cases, CRIU can observe tasks that exit during checkpointing,
and sets the state of these tasks to COMPEL_TASK_DEAD.
This patch adds a string representation of this value that can be used
by CRIT when decoding the images.
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
CRIU defines the following constants for task state in compel/include/uapi/task-state.h
COMPEL_TASK_ALIVE = 0x01
COMPEL_TASK_STOPPED = 0x03
COMPEL_TASK_ZOMBIE = 0x06
Thus, we need to swap the values for "zombie" and "stopped" used in CRIT.
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
When running the command 'make docker-test', almost all zdtm tests fail,
logging 'ip: not found'. 'ip' command of the iproute2 package was missing.
So added the package to the list of dependencies in 'apt-packages.sh'. Now
tests run
Signed-off-by: ImranullahKhann <imranullahkhann2004@gmail.com>
As of Alpine Linux 3.19, musl libc no longer contains separate
fopen64(), fseeko64(), or ftello64() functions. This causes building
CRIU with amdgpu plugin to fail with the following error:
amdgpu_plugin.c: In function 'parallel_restore_bo_contents':
amdgpu_plugin.c:2286:17: error: implicit declaration of function 'fseeko64'; did you mean 'fseeko'? [-Wimplicit-function-declaration]
2286 | fseeko64(bo_contents_fp, entry->read_offset + offset, SEEK_SET);
| ^~~~~~~~
| fseeko
make[2]: *** [Makefile:31: amdgpu_plugin.so] Error 1
make[1]: *** [Makefile:363: amdgpu_plugin] Error 2
To fix this, add the missing $(DEFINES) to plugin builds, and since we
always compile with _FILE_OFFSET_BITS=64, we don't need the 64 suffix.
Fixes: #2826
Suggested-by: Andrei Vagin <avagin@google.com>
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
The initialization of the struct timespec used as clockid input
parameter was removed in commit:
b4441d1bd8 ("restorer.c: rm unneded struct init")
This causes the build to fail on Alpine with clang version 21.1.2:
GEN criu/pie/parasite-blob.h
criu/pie/restorer.c:1230:39: error: variable 'ts' is uninitialized when passed as a const pointer argument here [-Werror,-Wuninitialized-const-pointer]
1230 | if (sys_clock_gettime(t->clockid, &ts)) {
| ^~
1 error generated.
make[2]: *** [/criu/scripts/nmk/scripts/build.mk:118: criu/pie/restorer.o] Error 1
make[1]: *** [criu/Makefile:59: pie] Error 2
make: *** [Makefile:278: criu] Error 2
To fix this, we remove the "const" from the declaration of
clock_gettime. Since the kernel writes the current time into
the struct timespec provided by the caller, the pointer must
be writable.
Suggested-by: Andrei Vagin <avagin@google.com>
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
After commit [1] we accidentally stopped reporting the errors from
kerndat_has_timer_cr_ids(), let's fix that.
Fixes: 1eaa870cc ("kerndat: check that hardware breakpoints work") [1]
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
The "man 2 close":"Dealing with error returns from close()" says:
"Retrying the close() after a failure return is the wrong thing to do"
We should not leave the fd there, attempting to close it again on next
close()/close_safe() may lead to accidentally closing something else.
It confirms with the kernel code where sys_close() removes fd from
fdtable in this stack:
+-> sys_close
+-> file_close_fd
+-> file_close_fd_locked
+-> rcu_assign_pointer(fdt->fd[fd], NULL)
If there was an fd this stack is always reached and fd is always
removed.
Let's replace the fd with -1 after close no matter what.
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
The README currently uses an external link to criu.org for the embedded
CRIU logo. Loading this URL when viewing the README on GitHub sometimes
fails with "Error Fetching Resource". Using a local copy of the logo
fixes this issue.
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
Docker version 28 broke container restore in combination with network
namespaces. The workaround in the CI script was excluding Docker version
28. Now that there is also Docker version 29, which is still broken,
this also excludes Docker version 29.
Signed-off-by: Adrian Reber <areber@redhat.com>
Commit "plugin: Add DUMP_DEVICES_LATE callback" introduced a new plugin
callback that is invoked in cr_dump_tasks(). The return value of this
callback was assigned to the variable ret. However, this variable is later
used as the return value when goto err is triggered in subsequent
conditions. As a result, CRIU exits with "Dumping finished successfully" even
when some actions have failed and inventory.img has not been created.
To fix this, we replace ret with exit_code and use it only when it is
actually needed.
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
Introduce an opt-in mode for building and running ZDTM static tests
with Guarded Control Stack (GCS) enabled on AArch64.
Changes:
- Support `GCS_ENABLE=1` builds, adding `-mbranch-protection=standard`
and `-z experimental-gcs=check` to CFLAGS/LDFLAGS.
- Export required GLIBC_TUNABLES at runtime via `TEST_ENV`.
- %.pid rules to prefix test binaries with `$(TEST_ENV)`
so the tunables are set when running tests.
- Makefile rules for selectively enabling GCS in tests
Usage:
# Build and run with GCS enabled
make -C zdtm/static GCS_ENABLE=1 posix_timers
GCS_ENABLE=1 ./zdtm.py run --keep-img=always \
-t zdtm/static/posix_timers
By default (`GCS_ENABLE` unset or 0), test builds and runs are
unchanged.
NOTE: This assumes that the test victim was compiled also using
GCS_ENABLE=1 so that the proper GCS AArch64 ELF headers are present
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
Reviewed-by: Alexander Mikhalitsyn aleksandr.mikhalitsyn@canonical.com
This commit finalizes AArch64 Guarded Control Stack (GCS)
support by wiring the full dump and restore flow.
The restore path adds the following steps:
- Define shared AArch64 GCS types and constants in a dedicated header
for both compel and CRIU inclusion
- compel: add get/set NT_ARM_GCS via ptrace, enabling user-space
GCS state save and restore.
- During restore switch to the new GCS (via GCSSTR) to place capability
token sa_restorer address
- arch_shstk_trampoline() — We enable GCS in a trampoline that using
prctl(PR_SET_SHADOW_STACK_STATUS, ...) via inline SVC. The trampoline
ineeded because we can’t RET without a valid GCS.
- restorer: map the recorded GCS VMA, populate contents top-down with
GCSSTR, write the signal capability at GCSPR_EL0 and the valid token at
GCSPR_EL0-8, then switch to the rebuilt GCS (GCSSS1)
- Save and restore registers via ptrace
- Extend restorer argument structures to carry GCS state
into post-restore execution
- Add shstk_set_restorer_stack(): sets tmp_gcs to temporary restorer
shadow stack start
- Add gcs_vma_restore implementation (required for mremap of the GCS VMA)
Tested with:
GCS_ENABLE=1 ./zdtm.py run -t zdtm/static/env00
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
Add debug and info messages to log Guarded Control Stack state when
dumping AArch64 threads. This includes the following values:
- gcspr_el0
- features_enabled
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
[ alex: cleanup fixes ]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Acked-by: Mike Rapoport <rppt@kernel.org>
- Define user_aarch64_gcs_entry in core-aarch64.proto to store
Guarded Control Stack state (gcspr_el0, features_enabled).
- Extend thread_info_aarch64 with an optional gcs field
Also extend thread_info_aarch64 with an optional gcs field
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
Introduce an opt-in mode for building and running compel tests
with Guarded Control Stack (GCS) enabled on AArch64.
Changes:
- Extend compel/test/infect to support `GCS_ENABLE=1` builds,
adding `-mbranch-protection=standard` and
`-z experimental-gcs=check` to CFLAGS/LDFLAGS.
- Export required GLIBC_TUNABLES at runtime via `TEST_ENV`.
Usage:
make -C compel/test/infect GCS_ENABLE=1
make -C compel/test/infect GCS_ENABLE=1 run
By default (`GCS_ENABLE` unset or 0), builds and runs are unchanged.
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
When GCS is enabled, the kernel expects a capability token at GCSPR_EL0-8
and sa_restorer at GCSPR_EL0-16 on rt_sigreturn. The sigframe must be
consistent with the kernel’s expectations, with GCSPR_EL0 advanced by -8
having it point to the token on signal entry. On rt_sigreturn, the kernel
verifies the cap at GCSPR_EL0, invalidates it and increments GCSPR_EL0 by 8
at the end of gcs_restore_signal() .
Implement parasite_setup_gcs() to:
- read NT_ARM_GCS via ptrace(PTRACE_GETREGSET)
- write (via ptrace) the computed capability token and restorer address
- update GCSPR_EL0 to point to the token's location
Call parasite_setup_gcs() into parasite_start_daemon() so the sigreturn
frame satisfies kernel's expectation
Tests with GCS remain opt‑in:
make -C compel/test/infect GCS_ENABLE=1 && make -C compel/test/infect run
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
[ alex: cleanup fixes ]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Acked-by: Mike Rapoport <rppt@kernel.org>
Add basic prerequisites for Guarded Control Stack (GCS) state on AArch64.
This adds a gcs_context to the signal frame and extends user_fpregs_struct_t to
carry GCS metadata, preparing the groundwork for GCS in the parasite.
For now, the GCS fields are zeroed during compel_get_task_regs(), technically
ignoring GCS since it does not reach the control logic yet; that will be
introduced in the next commit.
The code path is gated and does not affect normal tests. Can be explicitly
enabled and tested via:
make -C infect GCS_ENABLE=1 && make -C infect run
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
[ alex: clean up fixes ]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Acked-by: Mike Rapoport <rppt@kernel.org>
Introduce ARM64 Guarded Control Stack (GCS) constants and macros
in a new uapi header for use in both CRIU and compel.
Includes:
- NT_ARM_GCS type
- prctl(2) constants for GCS enable/write/push modes
- Capability token helpers (GCS_CAP, GCS_SIGNAL_CAP)
- HWCAP_GCS definition
These are based on upstream Linux definitions
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Acked-by: Mike Rapoport <rppt@kernel.org>
Refactor user_fpregs_struct_t to wrap user_fpsimd_state in a
dedicated struct, preparing for future extending by just
adding new members
Signed-off-by: Igor Svilenkov Bozic <svilenkov@gmail.com>
[ alex: fixes ]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Acked-by: Mike Rapoport <rppt@kernel.org>
At least on tests running on Fedora rawhide following error could be
seen:
```
criu/tty.c: In function 'pts_fd_get_index':
criu/tty.c:262:21: error: initialization discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers]
262 | char *pos = strrchr(link->name, '/');
|
```
This fixes it.
Signed-off-by: Adrian Reber <areber@redhat.com>
Static code analysis reported:
1. criu/cr-restore.c:2438:2: var_decl: Declaring variable "end_vma"
without initializer.
4. criu/cr-restore.c:2451:5: assign: Assigning: "s_vma" = "&end_vma",
which points to uninitialized data.
7. criu/cr-restore.c:2449:4: uninit_use: Using uninitialized value
"s_vma->list.next".
This tries to fix it by initializing the variable.
Signed-off-by: Adrian Reber <areber@redhat.com>
Static code analysis reported:
criu/cr-dump.c:2328:2: identical_branches: The same code is executed
when the condition "ret" is true or false, because the code in the
if-then branch and after the if statement is identical. Should the if
statement be removed?
This is a fix for the warning.
Signed-off-by: Adrian Reber <areber@redhat.com>
The syntax of the inherit-fd functionality for unix socket and pipe
includes a colon.
Fixes: 0df3f79fc0 ("criu(8): fix --inherit-fd description")
Fixes: c37324b6d0 ("crtools: describe the inherit-fd option")
Signed-off-by: Mark Polyakov <mark@thundercompute.com>
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>