This is an archive of the discontinued LLVM Phabricator instance.

Thread safety analysis: Always warn when dropping locks on back edges
ClosedPublic

Authored by aaronpuchert on Jun 14 2021, 12:58 PM.

Details

Summary

We allow branches to join where one holds a managed lock but the other
doesn't, but we can't do so for back edges: because there we can't drop
them from the lockset, as we have already analyzed the loop with the
larger lockset. So we can't allow dropping managed locks on back edges.

We move the managed() check from handleRemovalFromIntersection up to
intersectAndWarn, where we additionally check if we're on a back edge if
we're removing from the first lock set (the entry set of the next block)
but not if we're removing from the second lock set (the exit set of the
previous block). Now that the order of arguments matters, I had to swap
them in one invocation, which also causes some minor differences in the
tests.

Diff Detail

Event Timeline

aaronpuchert requested review of this revision.Jun 14 2021, 12:58 PM
aaronpuchert created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptJun 14 2021, 12:58 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
aaronpuchert added inline comments.Jun 14 2021, 1:15 PM
clang/lib/Analysis/ThreadSafety.cpp
868

One might ask: what about asserted capabilities? I plan to introduce a warning when they are released, because that can't be consistent, and then they can't disappear on back edges without warning.

For negative capabilities we'd presumably see a warning for the "positive" capability instead.

Not sure how universal capabilities are typically used. Presumably one could release such a capability in a loop? Then on the other hand, code using such capabilities is probably not very interested in false negatives.

2227–2228

Presumably we should call these EntrySet and ExitSet instead? The second parameter is always the exit set of an existing block, the first parameter the entry set of a (sometimes new) block.

clang/test/SemaCXX/warn-thread-safety-analysis.cpp
643

These are just swapped because I'm merging the branches in a different order now. I think that's Ok.

2788

This warning is new.

2813

This is also new.

aaronpuchert added inline comments.
clang/lib/Analysis/ThreadSafety.cpp
2227–2228

Did this in a separate change D104649, because it would obfuscate the changes here. Nevertheless, it's probably a good idea to look at both changes together.

I think the CI failure (libarcher.races::lock-unrelated.c) is not related to this patch but is a tsan thing, but you may want to double-check that just in case.

I think this looks reasonable, but I'd like to hear from @delesley before landing.

clang/test/SemaCXX/warn-thread-safety-analysis.cpp
643

I think the new order is actually an improvement because it diagnoses the second acquisition (diagnosing the first is a bit weirdly predictive for my tastes).

2806–2816

How about a test like:

void foo() {
  RelockableMutexLock scope(&mu, ExclusiveTraits{});
  for (unsigned i = 1; i < 10; ++i) {
    if (i > 0) // Always true
      scope.Lock(); // So we always lock
    x = 1; // So I'd assume we don't warn
  }
}

I think the CI failure (libarcher.races::lock-unrelated.c) is not related to this patch but is a tsan thing, but you may want to double-check that just in case.

Seems that an expected race didn't materialize, perhaps it's a bit flaky. I'd be surprised if it was related.

I'd like to hear from @delesley before landing.

Me too. Generally about treating back edges differently, as we can't modify the lockset anymore. (I have a similar change that's going to take back some of relaxations in D102026, namely for an exclusive lock coming back as a shared lock on the back edge.)

clang/test/SemaCXX/warn-thread-safety-analysis.cpp
2806–2816

The CFG isn't that clever, it would only consider i > 0 always true if i was a compile-time constant. It doesn't even consider type limits, so for i >= 0 the else-branch would still be considered reachable:

[B2]
  1: x
  2: [B2.1] (ImplicitCastExpr, LValueToRValue, unsigned int)
  3: 0
  4: [B2.3] (ImplicitCastExpr, IntegralCast, unsigned int)
  5: [B2.2] >= [B2.4]
  T: if [B2.5]
  Preds (1): B3
  Succs (2): B1 B0

versus the same with true:

[B2]
  1: true
  T: if [B2.1]
  Preds (1): B3
  Succs (2): B1 B0(Unreachable)

But let's say we use a compile-time constant, then it's equivalent to not having the Lock call conditional at all, and there is no warning. Actually I might just change loopAcquire into this, because as that test is written right now it doesn't affect the back edge at all. (The lock will be removed from the lockset when the if joins with the else, before we loop back.)

aaronpuchert added inline comments.Jun 25 2021, 8:56 AM
clang/lib/Analysis/ThreadSafety.cpp
868

For negative capabilities we'd presumably see a warning for the "positive" capability instead.

No, because a back edge is missing a positive capability when I'm unlocking in a loop, whereas it would be missing a negative capability when I'm locking in a loop.

But we probably want to warn about this only when -Wthread-safety-negative is active.

delesley accepted this revision.Jun 25 2021, 11:24 AM

This looks good to me. Thanks for the patch! Since you're adding a new warning, this may break some code somewhere, but since it's restricted to relockable managed locks, I'm not too worried...

This revision is now accepted and ready to land.Jun 25 2021, 11:24 AM

since it's restricted to relockable managed locks, I'm not too worried...

Not quite, it affects scoped locks with explicit unlock, which was supported before D49885.

@rupprecht, can you still test patches on Google's code? Would be good to know if this breaks anything.

since it's restricted to relockable managed locks, I'm not too worried...

Not quite, it affects scoped locks with explicit unlock, which was supported before D49885.

@rupprecht, can you still test patches on Google's code? Would be good to know if this breaks anything.

Thanks for the heads up. I ran this on the same targets that broke in D84604 (in case that's what you're looking for), and those continue to pass. I can try further testing of other targets, but that may take a little longer, so I have no problem with you landing this as-is and I can follow up if there are problems elsewhere.

This revision was landed with ongoing or failed builds.Jun 29 2021, 2:57 PM
This revision was automatically updated to reflect the committed changes.
aaronpuchert marked an inline comment as done.