-
Notifications
You must be signed in to change notification settings - Fork 222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
libasan? (for "built-in" address sanitization) #148
Comments
Address Sanitizer has not yet been ported to Mingw-w64. While I'd love to
include it, I can't until it's ported. Per the README, you can and should
use Undefined Behavior Sanitizer in trap mode during development:
-fsanitize=undefined -fsanitize-trap
It overlaps a little with ASan, including some bounds checking. Like ASan,
libubsan has not yet been ported to Mingw-w64, but it's only used to print
a diagnostic. Its checks do not require a runtime. When testing via GDB,
which you should always do anyway, it will pause on the offending line of
code. (Once you've got the hang of it, this is a better experience than
libubsan anyway!)
|
Thanks a lot for the tips, and the prompt answer! (Alas, information in READMEs is typically a read-once-or-other-low-number-of-times-early-on resource, and as soon as fragments get forgotten, they are usually gone without noticing forever -- or until reinstated in those flaky brain tissues by patient fellows like you. So, thanks also for that!) |
Why do you say this? I like the diagnostic and think it's helpful, it is not always obvious what sort of UB you have introduced. |
@Peter0x44, I object to sanitizers littering traces with implementation
details. It's an abstraction leak, and creates needless friction in the
typical case by breaking the debugger at the wrong location. I consider it
serious a design flaw — and, sadly, increasingly common across language
implementations generally — that exacerbates pre-existing flaws in C and
C++ toolchains. Sanitizers came out of a culture that did not internalize
debuggers as a standard part of a development workflow, and so sanitizers
have never interoperated well with debuggers. I've written about this more
generally here:
Assertions should be more debugger-oriented
https://nullprogram.com/blog/2022/06/26/
For an illustration, consider this program:
#include <string.h>
int main(void)
{
char *buf = 0;
int len = 0;
memset(buf, 0, len);
}
Ideally GDB would break directly on the memset call, and I could inspect
variables around the call site and start figuring out what happened. UBSan
would be completely invisible, and it would be as though the memset call
site itself trapped. In reality, on a glibc toolchain I get 9 junk stack
frames, with GDB breaking deep inside the runtime with no source code (and
note this is the manually *tidied up* version of the backtrace, so that it
doesn't overwhelm my message here):
(gdb) bt
#0 __pthread_kill_implementation (...) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (...) at ./nptl/pthread_kill.c:78
#2 __GI_raise (...) at ../sysdeps/posix/raise.c:26
#3 __GI_abort () at ./stdlib/abort.c:79
#4 __sanitizer::Abort () at ...
#5 __sanitizer::Die () at ...
#6 __ubsan::ScopedReport::~ScopedReport (...) at ...
#7 handleNonNullArg (...) at ...
#8 __ubsan::__ubsan_handle_nonnull_arg (...) at ...
#9 main () at example.c:6
What a poor experience! It should be so much better than this. If I was an
outsider, new to this ecosystem, and saw this result, my impression would
be, "Wow, this toolchain is half-baked." And I wouldn't be wrong. To be
fair to UBSan, it's "only" responsible for 5 of the junk stack frames, and
glibc is responsible for the other 4 (hence "pre-existing flaws").
Actually, it's even worse than this. The default behavior is to NOT TRAP
AT ALL! UBSan just calls exit(3) and casually discards all the valuable
debug state. One must explicitly request for an abort via the environment
(UBSAN_OPTIONS). Honestly, this is embarrassing, and I'd be ashamed having
to explain it to a thoughtful outsider. (The "saving grace" for GNU-style
C and C++ toolchains is that other major language toolchains have, quite
ingeniously, found ways to be worse!)
UBSan trap mode lacks a diagnostic but otherwise exactly matches ideal,
perfect behavior of a sanitizer. Invisible, no junk stack frames, trap by
default. With experience ("got the hang of it"), the lack of diagnostic is
rarely a problem, especially because you already have GDB providing that
context. The main flaw is lack of feedback for beginners who have not yet
learned the various undefined behavior cases (of which there are too many,
thanks to WG14 incompetence). Though, per above, the alternative is still
confusing for beginners (plus everyone else) for different reasons.
Suppose we could have our cake and eat it too: Invisible, direct trapping,
and a diagnostic. Consider the behavior of that last part: UBSan prints on
standard error. Still not great. It's mixed with program output. Ever seen
sanitizers fire in an ncurses application? Barely usable. The diagnostic
also interferes with GDB TUI even for applications that don't print output
(e.g. GUIs), which can be difficult to read and requires a manual redraw
(partly GDB's fault for relying too much on the terminal for UI).
On Windows we actually have a couple of interesting options unavailable
elsewhere. First, GDB has "set new-console" which is badly missing on unix
(where the "inferior-tty" option only half works). UBSan diagnostics would
be cleanly separated from GDB TUI, solving that particular problem. But
even better, there's OutputDebugString, which has no equivalent on unix
but is supported by GDB. The diagnostic could go straight to the attached
debugger to display in whatever manner is appropriate! It's obviously the
correct answer — aside from UBSan integration into the debugger, though
that's practically ruled out by current system architecture — and that's
how a proper UBSan port would do it (not holding my breath).
I wish I could get the invisible, direct trapping behavior from ASan as
well, even if it required giving up the (overly verbose) diagnostic, but
alas, it's up to the extra runtime gunk to actually trap, and so, without
fundamental toolchain changes, cannot break on the offending line. Visual
Studio is better than GDB in this case. Like other junk runtime frames, it
realizes ASan frames are uninteresting, hides them, and then breaks on the
correct line.
|
I don't know the specifics of how this works but llvm-mingw claims to support it. From its README:
However, it is only documented to only work on UCRT
So that would probably be an extra hurdle, even if it did work I'm not sure it's "Address Sanitizer has not yet been ported to mingw-w64" but more some gcc tooling or so hasn't been hooked up. I don't know the specifics... |
I've never actually used it, just tried to add
-fsanitize=address
after seeing it do its magic somewhere, butresulted in
So, then I realized it's not actually built-in after all.
But could it be bundled in a future release?
The text was updated successfully, but these errors were encountered: