Compel
Compel is a utility to execute arbitrary code in a context of a foreign process. Compel is part of CRIU, and its sources are available from the criu-dev branch of CRIU repo, subdirectory compel.
The code to be executed is called parasite code. Once compiled with compel flags and packed, it can be executed in other task's context. Note the code is run in environment without glibc, thus it can not call the usual stdio/stdlib/etc. functions.
A set of compel plugins are provided for your convenience. Plugins get linked to the parasite binary during the pack stage.
Writing parasite code
Execution of parasite code always starts with a function in compel std plugin that should be linked with parasite binary (see below). From the parasite code these symbols should be available for libcompel to work
parasite_trap_cmd(int cmd, void *arg);
- This routine gets called by
compel_run_in_thread()
parasite_daemon_cmd(int cmd, void *arg);
- This routine gets called by
compel_rpc_call()
andcompel_rpc_call_sync()
. Thearg
points to the memory with arguments, see the #Arguments passing section below.
parasite_cleanup(void);
- This gets called on parasite unload by
compel_cure()
Compiling and packing
Compile the source of your parasite code with compel flags:
$ gcc -c foo1.c -o foo1.o $(compel cflags)
Then link the parasite binary. Include all the .o files needed.
$ ld $(compel ldflags) foo1.o foo2.o $(compel plugins) -o parasite.po
The .po blob can now be loaded as parasite.
Loading blob
Using CLI
This functionality is in plans and not implemented yet.
Using libcompel.a library
Currently there is only one way to load the blob into victim task using libcompel.a, called c-header [1]. First you should make a header out of your .po file with the hgen
action of compel tool:
compel hgen -f parasite.po -o parasite.h
Options meaning is the following:
-f
tells which binary to turn into header-o
tells where to write the resulting header
Once parasite.h
file is ready, it should be included into the infecting program source code to be compiled with it.
Running parasite code
So, in order to infect a task with parasite one must do the following.
- Stop the task. This is done by calling
compel_stop_task(int pid)
. Its return value should be saved in case it's positive (it contains the task state). - Prepare the infection handler. This is done by calling
compel_prepare(int pid)
. The return value is an opaque pointer tostruct parasite_ctl
. - Run the remote code:
- Execute a system call with
compel_syscall(ctl, int syscall_nr, long *ret, int arg ...)
(all 6 parameters) - Infect the victim with the parasite blob by calling
compel_infect(ctl, nr_thread, size_of_args_area)
- Execute a system call with
- Cure the victim by calling
compel_cure(ctl)
. Note thatctl
pointer is freed by the call so it should not be used thereafter. - Resume the task by calling
compel_resume_task(pid, orig_state, state)
with the saved state value from the first step.
Infecting
Infecting the victim with a parasite blob needs some special treatment.
First, the ctl
should be configured with the blob information. For that,
you should call PREFIX_setup_c_header()
function
with ctl
as an argument. Here PREFIX
is the same as the argument given to -p
option
to compel hgen
, and if not given, it is derived
from the input file name, dropping the path and the suffix
(in the above example it's parasite
).
Second, when infecting a parasite one should specify the amount of threads it will mess with (1, if only the thread leader will be accessed) and the maximum size of the memory area shared between infecting task and parasite used for arguments/result passing.
Arguments passing
To pass arguments between the infecting code and parasite, one may call compel_parasite_args(ctl, type)
and get the pointer where it can put data. Subsequent calls to compel_rpc_call[_sync]()
will result in this data visible in void *arg
address of the parasite_daemon_cmd()
.
See also
- Compel/Usage_scenarios
- Compel plugins
- Examples of working code available on github [2]
- Info about CRIU code blobs in which the same technology is used
- ↑ This is done for historical reasons. It was the most handy way to load parasite code by CRIU. In plans we have loading the .po ELF file itself
- ↑ Clone the repo, build the project by running
make
, then go tocompel/test/$test_name
directory and runmake
there. Runningspy
bynary runs the example. Then -- RTFS or talk to us on criu@openvz.org :)