Skip to content
Lukáš Zaoral edited this page Apr 11, 2022 · 16 revisions

csexec – a dynamic linker wrapper

The ultimate goal is to run dynamic analyzers and formal verifiers on RPM packages fully automatically. We already know how to tweak the build process, how to hook static analyzers on compiler invocation, and how to capture their results. This is, however, not so easy with dynamic analyzers. The %check section of RPM packages can use arbitrary testing frameworks and scripting languages to verify the binaries produced in the %build section. Our goal is to dynamically analyze (or formally verify) the produced binaries. At the same time, we want to interfere with all the testing frameworks as little as possible.

csexec-loader is a binary that can be set as ELF interpreter while linking binaries in %build section of RPM packages. When the resulting binary is executed, the loader executes csexec passing it real path of the executable as argv[0], followed by the original command-line arguments. Then csexec runs the dynamic linker explicitly to execute the original binary. Optionally, csexec inserts an execution wrapper (e.g. valgrind) if the CSEXEC_WRAP_CMD environment variable is set at run time. The string may optionally contain BEL-separated list of arguments that will be passed to the wrapper in front of the path to the dynamic linker executable.

There are some constrains on the implementation of ELF interpreters. Namely, it does not work well if a custom ELF interpreter internally uses the system ELF interpreter or C run-time libraries, which are tightly coupled with the system ELF interpreter. Therefore, csexec-loader is implemented such that it does not use any C run-time libraries.

Quick Demo

Install csexec, cswrap, and fedpkg packages:

$ sudo yum install csexec cswrap fedpkg

Tweak environment such that any build will use /usr/bin/csexec-loader as ELF interpreter:

$ export PATH=$(cswrap --print-path-to-wrap):$PATH
$ export CSWRAP_ADD_CFLAGS=-Wl,--dynamic-linker,/usr/bin/csexec-loader

Build a Fedora package as usually:

$ fedpkg clone -a logrotate
$ cd logrotate
$ git checkout f33
$ fedpkg compile

Run the upstream test-suite natively:

$ cd logrotate-3.17.0/build
$ make check -j16

Run the upstream test-suite through valgrind (without touching the already built binaries):

$ sudo yum install valgrind
$ CSEXEC_WRAP_CMD=$'valgrind\a--quiet\a--log-file=/dev/tty\a--leak-check=full' make check

Run the upstream test-suite through strace (without touching the already built binaries):

$ sudo yum install strace
$ CSEXEC_WRAP_CMD=$'strace\a-e\aopenat\a-o\a/dev/tty' make check

We can also use csexec to dynamically analyze unmodified source RPM packages:

$ sudo yum install cswrap koji rpm-build valgrind
$ koji download-build -a src logrotate-3.17.0-3.fc33
$ sudo yum builddep ./logrotate-3.17.0-3.fc33.src.rpm
$ export PATH=$(cswrap --print-path-to-wrap):$PATH
$ export CSWRAP_ADD_CFLAGS=-Wl,--dynamic-linker,/usr/bin/csexec-loader
$ export CSEXEC_WRAP_CMD=$'valgrind\a--quiet\a--xml=yes\a--xml-file=/tmp/logrotate-valgrind-%p-%n.xml'
$ rpmbuild --rebuild ./logrotate-3.17.0-3.fc33.src.rpm

The above can be achieved in a more user-friendly way using the csmock-plugin-valgrind plug-in of csmock:

$ sudo yum install csmock-plugin-valgrind koji
$ koji download-build -a src logrotate-3.17.0-3.fc33
$ csmock -t valgrind -r fedora-rawhide-x86_64 ./logrotate-3.17.0-3.fc33.src.rpm

Likewise, there is csmock-plugin-strace that runs the %check section of a source RPM package through strace.

Clone this wiki locally