This is an archive of the discontinued LLVM Phabricator instance.

Make the sanitizer Notify callbacks asynchronous
ClosedPublic

Authored by jingham on Sep 29 2022, 5:35 PM.

Details

Summary

lldb handles breakpoints in two phases, the "sync" and "async" phase. The synchronous phase happens early in the event handling, and is for when you need to do some work before anyone else gets a chance to look at the breakpoint. It's used by the dynamic loader plugins, for instance.

As such, we're in a place where we haven't quite figured out about the current event, and are not in a state to handle another one. That makes running the expression evaluator in synchronous callbacks flakey. The Sanitizer callbacks were all registered as synchronous, even though their primary job is to call report generation functions in the target. TTTT, calling functions works better than I would have expected, but if you rapidly continue enough times over a sequence of sanitizer reports, you eventually confuse the event state machine.

I bet with enough thinking we could make expression evaluation in sync callbacks work, but it would further complicate an already complex system. And there's no reason for the sanitizer callbacks to be synchronous, they are very like user report functions in a breakpoint, and should run in the same way. So the simpler path of converting these to async seems the better one.

This patch switches the report functions from sync to async, adds some testing for continuing past sequential UBSan reports, and adds a comment telling people not to use expressions in sync callbacks. The test is not great, in that I can get this same test code to misbehave quite regularly in Xcode, but I couldn't reproduce enough of the Xcode environment in a test to produce a reliably failing test, so this test for the most part passes even without the fix.

Diff Detail

Event Timeline

jingham created this revision.Sep 29 2022, 5:35 PM
Herald added a project: Restricted Project. · View Herald TranscriptSep 29 2022, 5:35 PM
jingham requested review of this revision.Sep 29 2022, 5:35 PM
Herald added a project: Restricted Project. · View Herald TranscriptSep 29 2022, 5:35 PM
kubamracek accepted this revision.Sep 30 2022, 8:24 AM
This revision is now accepted and ready to land.Sep 30 2022, 8:25 AM
JDevlieghere accepted this revision.Sep 30 2022, 8:56 AM

LGTM

lldb/source/Plugins/InstrumentationRuntime/ASan/InstrumentationRuntimeASan.cpp
301–310

Might be nice spell this out explicitly, like we do for the internal and hardware arguments to CreateBreakpoint

I had to fix one other thing. I had arbitrarily made the decision that if an internal breakpoint was hit while running an expression we should stop before running its callbacks. At the time we only had internal breakpoints with sync callbacks (again mostly dynamic loader plugins) so there wasn't really an example to reason on. But with these sanitizer ones now async, we can hit this issue. The problem with hitting a breakpoint while running an expression is that those breakpoint commands could run the target in arbitrary ways, which could hit this breakpoint, and then call other commands, etc, and right now the Command Interpreter can only be invoked recursively with some care, so I outlaw that. But I think it's better for internal breakpoints to let their callbacks run, and leave it up to the callback how it wants to do its business. In the case of the sanitizers, they just don't run their reports if there's a sanitizer issue while calling a function, in other cases we're just reading memory, etc.

So I added that change to StopInfo, and updated the patch. I'll let it sit for a bit in case anyone wants to have a look. I also made variables for the "false" I was passing to SetCallback as Jonas suggested.

jingham updated this revision to Diff 464442.Sep 30 2022, 5:02 PM

Handled hitting the sanitizer breakpoint while calling a function in StopInfoBreakpoint.