This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] Add std::string checker
ClosedPublic

Authored by steakhal on Oct 6 2021, 10:24 AM.

Details

Summary

This patch adds a checker checking std::string operations.
At first, it only checks the std::string single const char * constructor for nullness.
If It might be null, it will constrain it to non-null and place a note tag there.

Diff Detail

Event Timeline

steakhal created this revision.Oct 6 2021, 10:24 AM
steakhal requested review of this revision.Oct 6 2021, 10:24 AM
steakhal added inline comments.
clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
96

Hmm, I probably need to check if the pointer is interesting and emit this not only if so.

clang/test/Analysis/diagnostics/explicit-suppression.cpp
22

The mock header changed, thus the line numbers changed.

steakhal updated this revision to Diff 377599.Oct 6 2021, 10:28 AM

Remove an accidentally added file. now the diff should be clean.

NoQ added inline comments.Oct 6 2021, 9:24 PM
clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
38–40

Constructors have identifiers. They totally work in other checkers. You still need to check for the right overload manually but in general they should work.

If you want caching, it probably makes sense to implement it inside CallDescription so that other checkers could take advantage of it as well.

84–89

You can drop the SValBuilder shenanigans and assume on the original value because if (x != nullptr) is entirely equivalent to if (x).

96

I'm not sure this note is necessary. Null dereference checker doesn't make this note. You also made it non-prunable which will cause any nested stack frame that has such note to be brought into the report (never pruned) which may result in hundreds of uninteresting path notes per report.

Such note could be useful in case the pointer value is "interesting", eg. it's tracked because it's used in the condition later down the path. If this is the aim, you should check for this explicitly and only then add a note. In this case a similar note can be added to other null dereference related checkers.

104

The modern-day tradition is to keep these as members and initialize inline:

class StringChecker {
  BugType BT_NullCStringParam { this, "...", "..." };
  ...
};
106

This is basically a null dereference bug. Let's use the same bug category, probably "logic error".

clang/test/Analysis/Inputs/system-header-simulator-cxx.h
567

I think this is supposed to be a template parameter. It probably doesn't matter but the closer we have our tests to the real-world situation the better.

NoQ added a comment.Oct 6 2021, 9:26 PM

Generally I think this is a great start! We might indeed be able to enable it by default from the start. What further plans do you have for this checker?

clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
110

Another great thing to do here is trackExpressionValue(Call.getArgExpr(0)). This would basically take care of all the notes.

steakhal planned changes to this revision.Oct 7 2021, 3:46 AM

Thanks for the review @NoQ!

clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
38–40

Okay, I'm gonna investigate this more thoroughly. I'm not exactly sure why did I choose to do it this way, but at first, I tried using CallDescriptions for sure. Let me come back to this.

About the caching, that is an orthogonal feature, so even if I implement it in the map, it will be done in a follow-up revision.

84–89

Oh yeah!

96

Such note could be useful in case the pointer value is "interesting", eg. it's tracked because it's used in the condition later down the path.

Yeah, I tried to highlight exactly the same in the comment of the next line. I'm gonna check for interestingness in the next update.

104

Thanks.

106

You are right.

110

Oh sure, I'm not even sure why I stuck with the markinteresting() here. It's simply a mistake.

clang/test/Analysis/Inputs/system-header-simulator-cxx.h
567

I did not want to introduce the std::allocator<> stuff, hence chose this private class instead.
Do you insist?

I already had to make tradeoffs. I had to drop constexpr and noexcept and the last deleted overload as well since this header is included in older standard version tests.

clang/test/Analysis/std-string.cpp
14–23

To be fair, even if the checker would accidentally select a wrong overload, it would still not emit diagnostic.
So, the no-warning here is somewhat misleading.
I've tested though, and the checker won't select any of these.

steakhal updated this revision to Diff 378624.Oct 11 2021, 4:54 AM
steakhal marked 6 inline comments as done.
  • Address review comments.
  • Add more tests.
  • Rebase on top D111535.

Generally I think this is a great start! We might indeed be able to enable it by default from the start. What further plans do you have for this checker?

Right now I don't plan to add more to this checker.

clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
38–40

In general, the CallDescription cannot cache the resolved decl, since that would require to match for the given overload. But we can only match for a qualified name and some parameter counts. Matching for the types of operands is generally done at a subsequent step.
So, I'm sticking to caching here for now.

The extension adding support for matching constructors is in D111535.

104

Wait, I thought we do this to lazily initialize the BugType to prevent unnecessary allocations.

steakhal updated this revision to Diff 378627.Oct 11 2021, 4:57 AM

Upload not only the diff to the last revision but actually the complete change :D

steakhal updated this revision to Diff 379673.Oct 14 2021, 5:14 AM

Addressed review comments.
Added a lot of tests.

I like this very much, nice work!

clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
49–64

Argh, I wish CallDescription was able to handle signatures! (StdLibraryFunctionsChecker would receive a big refactor then).

69

Having an early return on if (!isCharToStringCtor(....) could reduce the nesting below.

71–72

How could this happen? Do you have a test case for that?

clang/test/Analysis/std-string.cpp
48–55

Ahhh, this is great!

steakhal updated this revision to Diff 379784.Oct 14 2021, 11:15 AM
steakhal marked 4 inline comments as done.
  • rotated if statement
  • getAs<Loc>() -> castAs<Loc>()
clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
49–64

Yes. Me too.

69

Yea, I'll rotate the if statement.

71–72

Hm, seems reasonable.

I think I addressed all comments I wanted.
I think it's pretty solid, and I want to move forward with this one.
@martong

martong accepted this revision.Oct 20 2021, 8:32 AM

I think I addressed all comments I wanted.
I think it's pretty solid, and I want to move forward with this one.
@martong

Solid indeed! Let's move forward, thanks!

This revision is now accepted and ready to land.Oct 20 2021, 8:32 AM

I think I addressed all comments I wanted.
I think it's pretty solid, and I want to move forward with this one.
@martong

Solid indeed! Let's move forward, thanks!

Thanks!
I'm gonna commit this tomorrow if there is no objection.

This revision was landed with ongoing or failed builds.Oct 25 2021, 2:16 AM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptOct 25 2021, 2:16 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript