Why won't AddressSanitizer log my DEADLYSIGNAL/bug only when LeakSanitizer is off?

I’m having what I would consider to be a very bizarre issue with ASAN. I am working on a linux daemon that is started by systemd. My ASAN flags look like this:

Environment=ASAN_OPTIONS=abort_on_error=1:log_path=%h/.local/share/grd_asan/as_%p.log:symbolize=1:halt_on_error=0:disable_coredump=0:unmap_shadow_on_exit=1:detect_leaks=0
Environment=LSAN_OPTIONS=exitcode=0

The issue I am having is that ASAN does not log the crashes when detect_leaks=0, but it does log them when detect_leaks=1. When I first instrumented, LeakSanitizer was on by default and it did log, but it was annoying so I needed to disable it so I could log my actual segfault, but when I did this, I even got core dumps but no ASAN logs until I turn LeakSanitizer back on, but with LS on, I can’t even get to my bug. And whats even weirder, during my actual crash coredump backtrace, is this:

gef➤  bt
#0  0x00007f5b7b368624 in __pthread_kill_implementation () at /lib64/libc.so.6
#1  0x00007f5b7b30fd1e in raise () at /lib64/libc.so.6
#2  0x00007f5b7b2f7942 in abort () at /lib64/libc.so.6
#3  0x00007f5b7c4e210f in __sanitizer::Abort() () at /lib64/libasan.so.8
#4  0x00007f5b7c4f1b8c in __sanitizer::Die() () at /lib64/libasan.so.8
#5  0x00007f5b7c4d9b20 in __sanitizer::ReportFile::ReopenIfNecessary() () at /lib64/libasan.so.8
#6  0x00007f5b7c4e0f3e in __sanitizer::ReportFile::Write(char const*, unsigned long) () at /lib64/libasan.so.8
#7  0x00007f5b7c4e39b5 in __sanitizer::SharedPrintfCodeNoBuffer(bool, char*, int, char const*, __va_list_tag*) [clone .constprop.0] () at /lib64/libasan.so.8
#8  0x00007f5b7c4e3be9 in __sanitizer::SharedPrintfCode(bool, char const*, __va_list_tag*) () at /lib64/libasan.so.8
#9  0x00007f5b7c4e3c95 in __sanitizer::Printf(char const*, ...) () at /lib64/libasan.so.8
#10 0x00007f5b7c4c8ec0 in __asan::ReportDeadlySignal(__sanitizer::SignalContext const&) () at /lib64/libasan.so.8
#11 0x00007f5b7c4c7ecf in __asan::AsanOnDeadlySignal(int, void*, void*) () at /lib64/libasan.so.8

This would indicate to me that ASAN is actually trying to log the issue but… its not and thats what I don’t understand.

You’re absolutely right to find this behavior bizarre — and it is. From what you’ve described, it seems you’re running into a somewhat rare edge case where the ASan report logging fails when LeakSanitizer is disabled, despite abort_on_error=1 and log_path being set. Let’s break down what might be happening and how to work around or fix it.


Understanding the Behavior

The key observations:

  1. When detect_leaks=1, ASan logs crashes as expected.
  2. When detect_leaks=0, ASan aborts, triggers the report functions (per backtrace), but no log is written.
  3. ASan tries to write the log (backtrace shows ReportFile::ReopenIfNecessary() and ReportFile::Write()), but apparently fails silently.

This is especially odd because detect_leaks should only affect LSAN, not the regular ASAN error reporting for things like buffer overflows or use-after-free.


Probable Cause

The real smoking gun is in the backtrace:

#5  __sanitizer::ReportFile::ReopenIfNecessary()

This suggests ASan is trying to reopen the log file, possibly using environment-specific paths (e.g., %h) at crash time, but something about the daemon context may be interfering.

Now, consider this:

  • %h (user home directory) is expanded by systemd, not ASan.
  • ASan may be failing to write logs because:
    • The %h variable is not reliably expanded before ASan initializes.
    • The log path does not exist or lacks permissions.
    • The log path contains %p, and when detect_leaks=0, the early error handling paths do not fully initialize logging with PID-specific filenames.

This failure path may not be well-handled when LSAN is disabled — and it seems LeakSanitizer’s initialization ends up initializing logging more reliably, which is why enabling it “fixes” the problem.


Recommended Fixes & Workarounds

1. Hardcode the log path to avoid %h and %p

Try setting the path to a fixed location that definitely exists and is writable by the daemon (and avoid % escapes):

Environment=ASAN_OPTIONS=abort_on_error=1:log_path=/var/log/grd_asan.log:symbolize=1:halt_on_error=0:disable_coredump=0:unmap_shadow_on_exit=1:detect_leaks=0

You can include the PID if you want unique logs, but be careful about how it’s parsed — test with something like:

log_path=/var/log/grd_asan.log.%p

But first verify if the directory exists and is writable by the daemon user.


2. Pre-create the log directory

Ensure /var/log/grd_asan/ (or your chosen path) exists with correct ownership:

mkdir -p /var/log/grd_asan
chown your-daemon-user /var/log/grd_asan

3. Redirect stderr to a file

If you’re still stuck, another workaround is to redirect stderr directly in your systemd unit file:

StandardError=append:/var/log/grd_asan_stderr.log

ASAN will fall back to stderr if log_path fails. This can also catch the message in journalctl if you’re not explicitly redirecting it.


4. Debug Logging from ASan

For even deeper insight, you can try enabling internal ASAN debug output:

ASAN_OPTIONS=verbosity=2:...

This may show you why it’s failing to write the report.


5. Patch or Debug the Binary with Verbose ASan

If the log path truly must use %h or %p, and you can’t avoid detect_leaks=0, you may need to:

  • Manually test with ASAN_OPTIONS in a simple shell to reproduce.
  • Rebuild your daemon with extra logging or override __sanitizer::ReportFile behavior (advanced).

Summary

Action Why
Avoid %h in log_path ASan doesn’t expand it; systemd does
Pre-create and chown log dirs Logging may silently fail on permission error
Set StandardError in systemd ASan logs fall back to stderr
Use verbosity=2 Get insights into ASan internals
Don’t rely on detect_leaks=1 to fix logging It’s a workaround masking the real issue