This is an archive of the discontinued LLVM Phabricator instance.

[tsan][llvm] Implement the function attribute to disable TSan checking at run time
ClosedPublic

Authored by zaks.anna on Oct 20 2016, 5:42 PM.

Details

Summary

This implements a function annotation that disables TSan checking for the function at run time. The benefit over attribute((no_sanitize("thread"))) is that the accesses within the callees will also be suppressed.

The motivation for this attribute is a guarantee given by the objective C language that the calls to the reference count decrement and object deallocation will be synchronized. To model this properly, we would need to intercept all ref count decrement calls (which are very common in ObjC due to use of ARC) and also every single message send. Instead, we propose to just ignore all accesses made from within dealloc at run time. The main downside is that this still does not introduce any synchronization, which means we might still report false positives if the code that relies on this synchronization is not executed from within dealloc. However, we have not seen this in practice so far and think these cases will be very rare.

(This problem is similar in nature to https://reviews.llvm.org/D21609; unfortunately, the same solution does not apply here.)

This patch depends on https://reviews.llvm.org/D25857 and https://reviews.llvm.org/D25859

Diff Detail

Repository
rL LLVM

Event Timeline

zaks.anna updated this revision to Diff 75382.Oct 20 2016, 5:42 PM
zaks.anna retitled this revision from to [tsan][llvm] Implement the function attribute to disable TSan checking at run time.
zaks.anna updated this object.
zaks.anna added reviewers: kcc, kubamracek, dvyukov.
zaks.anna added a subscriber: llvm-commits.
zaks.anna updated this revision to Diff 75384.Oct 20 2016, 5:48 PM
zaks.anna updated this object.

Remove references to 'dealloc' from the comments.

dvyukov added inline comments.Oct 30 2016, 7:05 PM
lib/Transforms/Instrumentation/ThreadSanitizer.cpp
390 ↗(On Diff #75384)

Isn't this just "return F.hasFnAttribute("sanitize_thread_no_checking_at_run_time")"?
If you remove the Attribute::SanitizeThread attribute when adding this new attribute, then isNoCheckingAtRunTime will be called at only 1 place. So it will be simpler to just use "F.hasFnAttribute("sanitize_thread_no_checking_at_run_time")" instead of it.

393 ↗(On Diff #75384)

This will go away if you remove the Attribute::SanitizeThread attribute when adding this new attribute.

410 ↗(On Diff #75384)

Do you have any particular use case for noreturn calls? I don't think it will work as expected. We don't return from that function, not from this function. Consider that we longjmp solely within this function, we will still exit from this function and call TsanIgnoreEnd on normal return path.
Moreover, most of the functions that recursively call noreturn function are not noreturn themselves. So if we call a function that recursively calls longjmp, we will fail to call TsanIgnoreEnd.

zaks.anna added inline comments.Oct 31 2016, 12:12 PM
lib/Transforms/Instrumentation/ThreadSanitizer.cpp
410 ↗(On Diff #75384)

One of my reduced test cases was calling 'exit()' inside dealloc, which lead to the runtime warning about mismatched ignores. This code fixes that case. Given that, I do not think it's likely that anyone would call a no-return function from dealloc.

I see that this fix will not work in all cases as you pointed out. Would you prefer removing it altogether or adding a comment explaining that it won't work in general?

dvyukov added inline comments.Oct 31 2016, 1:30 PM
lib/Transforms/Instrumentation/ThreadSanitizer.cpp
410 ↗(On Diff #75384)

For exit() we probably should fix runtime to not complain in such case (when thread is terminated due to exit), because it is not related this functionality, user can write something along the following lines:

...
ANNOTATE_IGNORE_READS_BEGIN();
...
if (some_error) {

printf(...);
exit(1);

}
...
ANNOTATE_IGNORE_READS_END();
...

Producing a warning in such case does not look particularly useful.
However, when main returns with ignores enabled we should still produce a warning, because that means that the program potentially runs with ignores enabled all the time due to a mismatched BEGIN/END somewhere.

Then I would prefer to remove special handling of noreturn functions, because it's not working.

We could handle ignores the same way we handle shadow stack for noreturn functions. I.e. longjmp unwinds shadow stack and restores some other internal state, it could also restore ignore counter.

zaks.anna added inline comments.Oct 31 2016, 1:52 PM
lib/Transforms/Instrumentation/ThreadSanitizer.cpp
410 ↗(On Diff #75384)

Makes sense! I'll remove the handling of noreturn from this patch.

zaks.anna updated this revision to Diff 76646.Nov 1 2016, 3:56 PM

Addressed review comments.

dvyukov added inline comments.Nov 6 2016, 8:06 PM
lib/Transforms/Instrumentation/ThreadSanitizer.cpp
458 ↗(On Diff #76646)

What are the chances that these functions don't contain any function calls?
If the chances are considerable, then I would prefix this on "Res || HasCalls" because there is nothing to ignore otherwise.

zaks.anna updated this revision to Diff 77436.Nov 9 2016, 7:02 PM

Only add the run time ignores if the function contains calls. Since the function is not marked as sanitize_thread, we should not have any reports directly from the body of the function.

dvyukov accepted this revision.Nov 10 2016, 9:10 PM
dvyukov edited edge metadata.

I guess this will conflict with Kuba's patch for exceptions. One of you needs to rebase before submitting. I.e. here we want to end ignores on unwind.

This revision is now accepted and ready to land.Nov 10 2016, 9:10 PM
zaks.anna marked an inline comment as done.Nov 11 2016, 9:21 AM

I'll rebase after the exceptions patch lands.

This revision was automatically updated to reflect the committed changes.