This is an archive of the discontinued LLVM Phabricator instance.

adds more checks to -Wfree-nonheap-object
ClosedPublic

Authored by cjdb on Jan 13 2021, 3:48 PM.

Details

Summary

This commit adds checks for the following:

  • labels
  • block expressions
  • random integers cast to void*
  • function pointers cast to void*

Diff Detail

Event Timeline

cjdb requested review of this revision.Jan 13 2021, 3:48 PM
cjdb created this revision.
aaron.ballman added inline comments.Jan 14 2021, 5:16 AM
clang/lib/Sema/SemaChecking.cpp
10274–10279

I think this simplifies things a bit (even though it's doing an isa<> followed by a cast<>).

10282–10288
10340

Please spell out the type (also, we don't typically use top-level const qualification on local variables or parameters).

10342
10345

The extra compound scope doesn't add too much, so I'd remove it.

10368
10372

Any reason not to handle LambdaExpr at the same time?

clang/test/Analysis/free.c
84

The formatting for this diagnostic is somewhat unfortunate in that it has the leading space before the :. I think that changing the diagnostic to use a %select would be an improvement.

cjdb updated this revision to Diff 322228.Feb 8 2021, 3:21 PM
cjdb marked 4 inline comments as done.

addresses comments

cjdb added inline comments.Feb 8 2021, 3:22 PM
clang/lib/Sema/SemaChecking.cpp
10274–10279

Wow, that's much nicer, thanks! :D

10282–10288

This one required a bit more work than the previous one because there are two overloads for CheckFreeArgumentsOnLvalue.

10340

Heh, this was only named for debugging purposes. I've consolidated it into the switch statement ;-)

10342

Done, but why? I quite like making it clear that fallthrough is intentional.

10345

I find it improves readability and groups what the comment above it (now inline with {) is talking about.

10372

None, I hadn't considered it. I'm not sure I see the relationship bet

clang/test/Analysis/free.c
84

I'm having a *lot* of difficulty getting %select to work. Here's what I've tried, but the space in %select{ %2 is being ignored :(

: Warning<"attempt to call %0 on non-heap object%select{ %2|: block expression}1">,
aaron.ballman added inline comments.Feb 10 2021, 8:52 AM
clang/lib/Sema/SemaChecking.cpp
10342

I don't think an explicit marker is necessary because the two cases have no whitespace separating them (so it should be obvious from context that fallthrough is intentional rather than accidental). However, we have the LLVM_FALLTHROUGH macro for the cases where fallthrough is intentional -- we typically only use that macro when compilers would otherwise diagnose the fallthrough though.

clang/test/Analysis/free.c
84

We could cheat a little bit. :-D

Warning<"attempt to call %0 on non-heap %select{object %2|object: block expression}1">

(The diagnostic should probably be updated to distinguish between block expressions and lambda expressions, which may add another layer of %select not shown here.)

cjdb added inline comments.Feb 11 2021, 4:56 PM
clang/test/Analysis/free.c
84

That doesn't fix the issue, which is that everything before %2 is deleted.

aaron.ballman added inline comments.Feb 12 2021, 6:22 AM
clang/test/Analysis/free.c
84

Huh? That seems very surprising given that we use this pattern in plenty of other places:

https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Basic/DiagnosticSemaKinds.td#L483
https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Basic/DiagnosticSemaKinds.td#L685
https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Basic/DiagnosticSemaKinds.td#L1439

Can you post the output you're getting when you try my workaround? (I don't know if your original attempt will work because of the lack of whitespace before the %select in object%select.)

cjdb updated this revision to Diff 323414.Feb 12 2021, 11:19 AM

fixes block expressions

@aaron.ballman I'm not sure I follow what you're after re lambdas. Could you please provide a test case that I can work with?

cjdb marked 3 inline comments as done.Feb 12 2021, 11:20 AM
cjdb added inline comments.
clang/test/Analysis/free.c
84

Yeah, not sure what was going on here (maybe I didn't hit save before testing?), since it ICEd up when I re-applied it. Anyway, got it working :-)

cjdb marked an inline comment as done.Feb 22 2021, 10:38 AM

Ping. @aaron.ballman any further requests? I think this is good to land otherwise?

fixes block expressions

@aaron.ballman I'm not sure I follow what you're after re lambdas. Could you please provide a test case that I can work with?

Sorry about missing your question -- this fell off my radar. I was thinking it'd be roughly the same as blocks, but I now think this'll create compile errors anyway: free([]{ return nullptr; }); (where the lambda is accidentally not being called). So not nearly as important as I was thinking (sorry for that, but thank you for adding the support and test cases just the same!).

One worry I have is the number of effectively duplicated diagnostics we're now getting in conjunction with the static analyzer. It's not quite perfect overlap and so we can't get rid of the static analyzer check, but is there a long-term plan for how to resolve this?

clang/lib/Sema/SemaChecking.cpp
10271–10272

After seeing my suggested workaround in practice, I'm hopeful we can figure out how to get the diagnostics engine to not require us to jump through these hoops. I wouldn't block the patch over this approach, but it's not very obvious what's happening from reading the code either.

10379

FWIW, this diagnostic will be awkward for lambdas because it mentions blocks explicitly. It's not critical thing given how hard it would be to hit the lambda case, but if you think of an easy way to solve it, I won't complain.

clang/test/Analysis/free.c
66–68

This is another variant of the test that's slightly different (and only valid in C):

int *ptr(void);
void func(void) {
  free(ptr); // Oops, forgot to call ptr().
}
cjdb marked an inline comment as done.Feb 23 2021, 1:33 PM

fixes block expressions

@aaron.ballman I'm not sure I follow what you're after re lambdas. Could you please provide a test case that I can work with?

Sorry about missing your question -- this fell off my radar. I was thinking it'd be roughly the same as blocks, but I now think this'll create compile errors anyway: free([]{ return nullptr; }); (where the lambda is accidentally not being called). So not nearly as important as I was thinking (sorry for that, but thank you for adding the support and test cases just the same!).

One worry I have is the number of effectively duplicated diagnostics we're now getting in conjunction with the static analyzer. It's not quite perfect overlap and so we can't get rid of the static analyzer check, but is there a long-term plan for how to resolve this?

I filed issue 48767, but I don't think the analyser folks are motivated to address it any time soon.

clang/lib/Sema/SemaChecking.cpp
10271–10272

Sorry, I'm not entirely sure what you're getting at here? :/

10379

Nothing "easy" comes to mind.

cjdb updated this revision to Diff 325889.Feb 23 2021, 1:33 PM

applies @aaron.ballman's feedback

aaron.ballman added inline comments.Feb 24 2021, 4:40 AM
clang/lib/Sema/SemaChecking.cpp
10271–10272

I used to find the Placeholder stuff to be confusing in practice and would have preferred to see a diagnostic solution that doesn't need the extra argument. Now I think I'm changing my mind and have a different suggestion -- remove the Placeholder and NoPlaceholder variables and pass in a literal with a comment:

S.Diag(UnaryExpr->getBeginLoc(), diag::warn_free_nonheap_object)
        << CalleeName << 0 /*object*/<< cast<NamedDecl>(D);

  S.Diag(Lambda->getBeginLoc(), diag::warn_free_nonheap_object)
      << CalleeName << 2 /*lambda*/;

That removes the akwardness of trying to name the variables, which is what was was I found non-obvious before.

cjdb updated this revision to Diff 326261.Feb 24 2021, 6:24 PM

applies @aaron.ballman's suggestion

cjdb marked an inline comment as done.Feb 24 2021, 6:25 PM
aaron.ballman accepted this revision.Feb 25 2021, 5:00 AM

LGTM, thank you for this!

This revision is now accepted and ready to land.Feb 25 2021, 5:00 AM
This revision was automatically updated to reflect the committed changes.
thakis added a subscriber: thakis.Feb 26 2021, 8:56 AM

This seems to flag https://source.chromium.org/chromium/chromium/src/+/master:third_party/libsync/src/sync.c;l=142?q=sync.c&ss=chromium :

info->sync_fence_info = (uint64_t) calloc(num_fences,
                                sizeof(struct sync_fence_info));
if ((void *)info->sync_fence_info == NULL)
    goto free;

err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
if (err < 0) {
    free((void *)info->sync_fence_info);
    goto free;
}

What's the motivation for flagging an integer that's >= sizeof(void*) and that's explicitly cast to void*? That seems like code that's pretty explicit about its intentions.

Did you do true positive / false positive evaluation of this change?

cjdb added a comment.EditedFeb 26 2021, 9:27 AM

This seems to flag https://source.chromium.org/chromium/chromium/src/+/master:third_party/libsync/src/sync.c;l=142?q=sync.c&ss=chromium :

info->sync_fence_info = (uint64_t) calloc(num_fences,
                                sizeof(struct sync_fence_info));
if ((void *)info->sync_fence_info == NULL)
    goto free;

err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
if (err < 0) {
    free((void *)info->sync_fence_info);
    goto free;
}

What's the motivation for flagging an integer that's >= sizeof(void*) and that's explicitly cast to void*? That seems like code that's pretty explicit about its intentions.

Did you do true positive / false positive evaluation of this change?

This change was a bit too aggressive and is being fixed in D97512.

Ka-Ka added a subscriber: Ka-Ka.Mar 4 2021, 4:01 AM

This patch seems to introduce warnings for the case

#include <stdlib.h>
#include <stdint.h>
void foo(void* ptr)
{

free((void*)(intptr_t) ptr);

}

something that gcc don't warn for (see https://godbolt.org/z/1WT9c6 ).

As intptr_t is suppose to be used to pass pointers in I think its a bit strange that clang warns in this case.

<...>

Is the patch still not reverted?
That should have happened almost a week ago.

<...>

Is the patch still not reverted?
That should have happened almost a week ago.

FWIW, the fix for the issue was just approved.

Ka-Ka added a comment.Mar 4 2021, 4:23 AM

Thanks for the information. I found the review now in https://reviews.llvm.org/D97512

rsmith added a comment.Apr 7 2021, 5:07 PM

Sorry for the late review.

clang/lib/Sema/SemaChecking.cpp
10294–10303

This special case is rather too special, and I don't think it's worth the lines of code we're spending on it.

There's a fair amount of work being done here to figure out what's being passed to free, and some of it, like this, seems pretty hard to justify in isolation. I wonder if we can reuse some of our existing mechanisms -- for example, can we run the constant evaluator on the operand to free instead, and see if it evaluates to a known non-heap object?

10326–10328

Please don't pretty-print source expressions and include them in the diagnostic text. Instead, include a source range to highlight them in the caret diagnostic. See https://clang.llvm.org/docs/InternalsManual.html#the-format-string for our diagnostic best practices.