This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] Implement a new checker ThreadPrimitivesChecker
AbandonedPublic

Authored by ASDenysPetrov on Aug 6 2020, 6:28 AM.

Details

Summary

This checker finds STL thread primitives misuse.

  • std::mutex::unlock without std::mutex::lock
  • std::recursive_mutex ::unlock without std::recursive_mutex ::lock
  • std::mutex::lock twice

In future:

  • std::mutex::lock without std::mutex::unlock (discuss)
  • try_lock function

P.S. This is an alpha version of my first checker. Do not hesitate to express your complaints!

Diff Detail

Event Timeline

ASDenysPetrov created this revision.Aug 6 2020, 6:28 AM
ASDenysPetrov requested review of this revision.Aug 6 2020, 6:28 AM

It is an interesting checker, but it seems that this kind of checker is extremely hard in single TU/top-down analysis. It feels like it's going to produce hell a lot of false positives in the wild.
Also mutex is usually a global variable - the nemesis of any static analysis tool.

In general, it would be great to have checkers for multi-threaded programs. However, it seems like we would need some sort of conventions that we enforce. For example, every function containing a lock for a mutex should also have the unlock. And the checker would verify that the user follows the convention rather than has correct code. The convention, of course, should be designed so it implies correctness. This is the model we have with RetainCountChecker.

clang/lib/StaticAnalyzer/Checkers/ThreadPrimitivesChecker.cpp
37

These days we simply do BugType without unique_ptrs

47

You should also cleanup and remove dead symbols from the set.

47

Maybe SymbolRef is more suitable here?

50

Does it conform with clang-format?

53

I think this is better expressed with enum because this contract implies only 3 possible values. I prefer to bake such contracts into the types of the actual solution so that the code is more robust.

65

Looks overindented

75

If we are sure then why are we using dyn_cast instead of cast?

98

Looks under indented

clang/test/Analysis/Checkers/ThreadPrimitivesChecker.cpp
28

I think we should also add a TODO for having a warning for no m1.unlock()

83

๐Ÿ˜ฑ๐Ÿ˜ฑ๐Ÿ˜ฑ

@vsavchenko
Made changes due to your remarks. Thanks you.

ASDenysPetrov added inline comments.Aug 10 2020, 1:09 AM
clang/lib/StaticAnalyzer/Checkers/ThreadPrimitivesChecker.cpp
47

Maybe SymbolRef is more suitable here?

I'm afraid it's not. Sval keeps some data but this data
See, getCXXThisVal returns SVal which is MemRegionVal, thus I can't cast it to SymbolVal and get SymbolRef.
But I would appreciate if you could tell me what other actionsI should do to get a correct SymbolRef.

Aded enum FuncIdKind to define function Ids.

ASDenysPetrov edited the summary of this revision. (Show Details)

Added recursive_mutex support.

Please, look at a test file on TODOs. I want to cover those cases in the future. Could you advise me something on it?

NoQ added a comment.Aug 10 2020, 8:23 PM

Umm, why don't you extend PthreadLockChecker instead?

ASDenysPetrov added a comment.EditedAug 11 2020, 1:39 AM

@NoQ

Umm, why don't you extend PthreadLockChecker instead?

First of all I wanted to try to go through all the steps, since it's my first checker. The second is that I considered PthreadLockChecker designed for C-functions. Another reason is that it better helps me to test the code when it is separated from all other.
When I feel more confident and it makes sence I'll try to merge this patch into PthreadLockChecker. For now it's more just an idea to discuss, to collect expertise.

Please, express you thoughts.
Specifically I've got a question: I have a set which holds SVal. @vsavchenko adviced me to clean the set on checkDeadSymbols. But SymbolReaper can only tell what is dead in terms of SymbolRef. What if I'm interested in any SVal (e.g. MemRegion, ConcreteInt)?

UDP: Ok, I found isLiveRegion for MemRegions. What about a common approach? Or it depends case by case?

NoQ added a comment.Aug 11 2020, 11:04 AM

You're on the right track but your checker repeats PthreadLockChecker word-by-word. Like, you can find answers to all your questions (eg., "how to use isLiveRegion?") by reading that checker. C++ functions aren't any different from C functions; that's an extremely minor difference that doesn't justify developing a new checker from scratch.

clang/lib/StaticAnalyzer/Checkers/ThreadPrimitivesChecker.cpp
34

CallDescriptionMap is the modern API for this stuff.

@NoQ

You're on the right track but your checker repeats PthreadLockChecker word-by-word. Like, you can find answers to all your questions (eg., "how to use isLiveRegion?") by reading that checker. C++ functions aren't any different from C functions; that's an extremely minor difference that doesn't justify developing a new checker from scratch.

Thanks. I've already looked through PthreadLockChecker. Yes, it's pretty similar. I'll keep working separately for a while though as I feel more comfortable making mistakes.

clang/lib/StaticAnalyzer/Checkers/ThreadPrimitivesChecker.cpp
34

Thanks. I'll look.

ASDenysPetrov abandoned this revision.Aug 26 2020, 9:29 AM

Guys!
I've moved my work to PthreadLockChecker https://reviews.llvm.org/D85984. You are welcome to look at it.