Estimated read time: 5 minutes
sanitizers (ASAN, UBSAN, etc.) is a collection of tools to detect memory corruption bugs, undefined behavior and more by instrumenting the code generated by the compiler. (That’s the main difference from valgrind.) From LibreOffice’s perspective one more important difference is that there is a Jenkins_Linux_Ubsan tinderbox that makes sure that the master branch is kept clean from errors detected by a given configuration.
So when the tinderbox failed after a commit of mine, I wanted to set up a similar environment locally, reproduce and fix the bug, and push the fix once I saw that the fix indeed solves the problem. You can set many options both at build and runtime, so while we have some documentation on the TDF wiki (and also Stephan was kind enough to share his config) on how to use these sanitizers, it wasn’t clear to me what to do step by step. So here is one possible setup that worked for me — in my case I wanted to reproduce a stack-use-after-return problem. If you haven’t ever built LibreOffice before, then go to the Development wiki page, first do a normal build, and if everything went fine, came back here.
Build options
My autogen.input
looks like this:
CC=clang -fsanitize=address
CXX=clang++ -fsanitize=address
--enable-dbgutil
--disable-firebird-sdbc
Which is a normal clang debug build, except:
-
you need to add
-fsanitize=...
toCXX
(not toCXXFLAGS
), as explained on the wiki -
you need to explicitly disable Firebird integration for now
Building
My first attempt failed at build time, as even the tools used only during the build are instrumented, and some memory leak was detected there, which means the build aborted before reaching the problem I was interested in. To disable leak detection during build, and disable parallelism (I needed this, as I did the build in the background while using the machine for something else):
make build-nocheck ASAN_OPTIONS=detect_leaks=0 PARALLELISM=1
This also means that I explicitly disabled running any tests, as I knew which is the single unit test I want to run for the purposes of reproducing and fixing the problem.
Testing
Once the build completed, it turns out that the stack-use-after-return detection is disabled at runtime by default, which means I could not see any problem locally. Here is the commandline to run one specific CppunitTest with this detection on:
cd sw; make -sr CppunitTest_sw_tiledrendering ASAN_OPTIONS=detect_leaks=0:detect_stack_use_after_return=1
Again, this is just one possible setup, you can use other -fsanitize=...
options, other environment variables during build and during testing — but
hopefully it helps in the future to avoid pushing fixes for such problems
detected by sanitizers just blindly.
Update, 2019-01-25
The above described "do it yourself" way doesn’t work with LibreOffice master (towards 6.3) and openSUSE Leap 15.0 anymore. I tried to debug what is the exact problem, but there are many moving parts here:
-
gcc version (providing libstdc++)
-
clang version (5.0.2 is too old; 7 failed to build the plugins, trunk towards 9 also generated false positives for me)
-
various sanitizer-related environment variables
-
various sanitizer-related compiler options
So it’s much easier to just use the combination used by the
Jenkins_Linux_Ubsan
tinderbox than something custom. Doing that is
reasonably straightforward, but still there are a couple of non-trivial steps.
What worked for me is:
-
Set up LODE according to its wiki page.
-
Instead of plain
./setup --dev
, do:
./setup --jenkins
./setup --jenkins-san
./setup --dev
cd $LODE_HOME/dev/core
This will build a working version of both gcc and clang for you.
-
Instead of manually setting up the environment variables, do:
. $LODE_HOME/bin/lode_ubsan_env
-
Instead of manually setting autogen options, use this autogen.input:
--with-distro=Jenkins/Linux_ubsan_master
-
Finally to build the code and run a specific test based on tinderbox mail:
make build-nocheck
cd sw; make -sr CppunitTest_sw_unowriter CPPUNIT_TEST_NAME="testPasteListener"
Update, 2019-11-14
The 2019-01-25 setup can be fine-tuned further. Sadly LODE mixes two goals: setting up dependencies / environment and cloning repos. I recently added support for bypassing any cloning, so you can use LODE even if you already built the code and want to keep using your own way. The tricks are:
-
Use autogen.env-san in two ways: first comment out the second half to run the above
./setup --jenkins && ./setup --jenkins-san
, then to source the full environment. This means you get all the up to date environment variables from LODE, but you don’t need a chroot to avoid LODE messing up your environment. -
Use autogen.input-san to again inherit all up to date autogen switches from LODE, but avoid the submodule pain that would be the default. Now you can do a
make check gb_SUPPRESS_TESTS=y
to build (but not run) all the code & tests. -
Use up.sh to build and run all Online code & tests — with new enough core.git master and online.git master the online.git
make check
passes for me in this environment.
I believe this setup mostly delegates (envrionment, config switches and toolchain) maintenance to LODE, still allows not running in a chroot and managing your git clones without LODE.
Update, 2023-12-02
These days it works to use the system clang compiler, so no need to to execute ./setup
in lode.git. This means:
-
Use (source) autogen.env-san to set the compiler flags and environment variables from LODE.
-
Use autogen.input-san as
autogen.input
in the core.git build to set the autogen options. -
Use up.sh to build Online the usual way.