This is an archive of the discontinued LLVM Phabricator instance.

[analyzer][StreamChecker] Adding precall and refactoring.
AbandonedPublic

Authored by balazske on Feb 26 2020, 4:06 AM.

Details

Summary

The validity checks are performed in precall callback.

Semantic of checking for opened stream has changed:
It is not allowed at all to use the stream after 'fclose'.
This is what cppreference.com says at fclose.
So the double-close error changes to use-after-close.

Diff Detail

Event Timeline

balazske created this revision.Feb 26 2020, 4:06 AM
Herald added a project: Restricted Project. · View Herald Transcript

Aha, so we're moving every check as to whether the stream is closed to preCall? Makes sense, if not much else changed. Could you please run clang-format-diff.py after adding the tests?

balazske updated this revision to Diff 246687.Feb 26 2020, 5:53 AM

Reformat code and fixed errors.

balazske updated this revision to Diff 246711.Feb 26 2020, 7:08 AM

Really reformat, bugfixes, and updated tests.

Plan:

  • Add an error state to StreamState.
  • Model every stream function that can fail. Split the state into failed and non-failed and set the return value accordingly (as done at fopen now, but not only for stream return values). This is needed here to have a correlation between the return value and the stream state.

Probably the check for null stream pointer can be removed from here if the StdLibraryFunctionsChecker will do this job.

To summarize, we're moving every stream check (illegal call to a stream management function with a null stream, or a closed stream, etc) to preCall, and leave only the modeling portions in evalCall. Makes sense!

You did an amazing job here! I like the new infrastructure and everything.

clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
84–101

Hmm, all of these braces are kinda hard to navigate, but its not too bad. Could you check whether changing them to nullptr improves readability? I'll leave it to your judgement.

117–122

Could we add/move some comments for these? :)

Checks whether the stream is non-null. If it is null, an fatal bug report is emitted and the return value is nullptr. Otherwise in the returned state StreamVal is non-null.
180

This would assert in Expr::getArg if Desc->StreamArgNo == ArgNone, but that shouldn't ever happen in this function, correct? Could we add an assert here as well as a poor man's documentation?

222–224

According to my research, yes, closed streams are okay. Also, the rest of the comment can be removed as well -- it is for sure not okay to pass NULL to freopen, even if it doesn't crash on some systems.

300–304

Ah, we're moving the closing of the stream from checkDoubleClose to here. Makes much more sense!

359–363

Could we move this to the declaration and start it with ///?

367–373

Previously, we only returned a nullptr after generating a fatal error node. Why do we need to change this?

378–379

How about

"Closing an already closed file descriptor."

395–396

How about

"Using a file descriptor after (re-)opening it failed."

balazske updated this revision to Diff 246955.Feb 27 2020, 8:13 AM
  • Improved comments in the code.
  • Added function to get stream arg.
  • Changed error messages.
balazske marked 10 inline comments as done.Feb 27 2020, 8:21 AM

Error messages are now in similar form as the null stream pointer case.

clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
84–101

nullptr looks better. (Missing value at "fseek" will be updated.)

367–373

This is fixed.

balazske updated this revision to Diff 247192.Feb 28 2020, 2:23 AM
balazske marked 2 inline comments as done.

Added a missing argument, lint warning reformat.

Szelethus accepted this revision.Feb 28 2020, 2:38 AM

LGTM! It makes much more sense to do the checking in preCall and the modeling in evalCall, the comments in the patch are precise and helpful, the tests cover everything. While I feel fairly confident that this patch is great both in concept and in execution, please leave some time for others to respond -- if that doesn't happen in a day or two, I think this is ready to land.

clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
64–74

You could make this a method of FnDescription.

119
/// If it can only be NULL a fatal error is emitted and nullptr returned.
/// Otherwise the return value is a new state where the stream is constrained to be non-null.
130

/// Check that the stream is in an opened state.

148–151

I vaguely recall us having this conversation once, but isn't this redundant with

if (!Call.isGlobalCFunction())
  return nullptr;

? I don't mind not addressing this before commiting.

This revision is now accepted and ready to land.Feb 28 2020, 2:38 AM
balazske marked 6 inline comments as done.Mar 3 2020, 3:33 AM
balazske added inline comments.
clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
64–74

Originally I wanted it too but there is no strong relation to it, somehow I like better to have it as separate function.

148–151

According to the documentation isGlobalCFunction should filter out member functions.

balazske updated this revision to Diff 247847.Mar 3 2020, 4:00 AM
balazske marked an inline comment as done.

Comment changes, updated lookupFn, other small change.

If we were to refactor this check I wonder if it would make sense to port evalCall to postCall, so the analyzer engine will conjure the symbol for us.
I wonder what @NoQ thinks about the pros and cons of the approaches.

As far as I understand the con of evalCall is that there is only one check that can "win" to do the modelling. Also, it is error prone as everything including binding the return values and invalidating some state needs to be done by hand. If all we need to do is to record some state for the returned symbol, postCall should be sufficient. But this can be problematic when we see the body of the function and hence we might not see a returned symbol (but a constant or something else).

The name of the patch implies refactoring but some functional changes were also done. Is it possible to separate the functional changes into a separate patch? If it is not a big effort I would prefer that way.

If we were to refactor this check I wonder if it would make sense to port evalCall to postCall, so the analyzer engine will conjure the symbol for us.
I wonder what @NoQ thinks about the pros and cons of the approaches.

Once I wanted to remove evalCall but it was no success:
https://reviews.llvm.org/D69662

Split the patch into two is really start it from "scratch" again, copy the code, decide what to go into the first or second part (and what order) (really what?), make some mess with git commands and branches and rebase, probably rebuild whole llvm, if review changes are to be done again comes the "git mess" problem (change code in a commit that belongs logically before another). It can be more work to make changes in the "old way" that are done in the second part in a new way. Or somebody could say "this thing makes no sense, I want to see the next diff how about it is used", and commit can happen really only if the next change is accepted too. So I do not prefer to split it, the change is not a very complicated one and the modifications belong somewhat together.

If we were to refactor this check I wonder if it would make sense to port evalCall to postCall, so the analyzer engine will conjure the symbol for us.
I wonder what @NoQ thinks about the pros and cons of the approaches.

Once I wanted to remove evalCall but it was no success:
https://reviews.llvm.org/D69662

Sounds fair.

The name of the patch implies refactoring but some functional changes were also done. Is it possible to separate the functional changes into a separate patch? If it is not a big effort I would prefer that way.

It would indeed be nicer, but given that the patch is relatively small both in LOC and in scope, I think its okay. In the future, I would also prefer to separate functional and non-functional changes.

I had no other work to do so here is the change for the non-functional part of this:
https://reviews.llvm.org/D75612

Szelethus added inline comments.Mar 5 2020, 3:10 AM
clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
64–74

Okay, I'll leave that to you.

This comment was removed by balazske.
Szelethus requested changes to this revision.Mar 6 2020, 6:52 AM

Okay, very well, then lets abandon this patch. Please keep in mind that phabricator can mark whether a revision was mentioned somewhere else if you only copy the revision ID, not the entire link: D75163 vs. https://reviews.llvm.org/D75163.

This revision now requires changes to proceed.Mar 6 2020, 6:52 AM
balazske abandoned this revision.Mar 6 2020, 7:21 AM

The patch was split into D75612 and D75614.