This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] PlacementNewChecker, properly handle array overhead (cookie)
Needs ReviewPublic

Authored by martong on Jul 7 2022, 5:34 AM.

Details

Summary

Currently, we warn if the given place is not enough to hold an extra
data for the array allocation overhead (cookie). I.e. if the size of the
place is equal to the size of the target. Also, we warn even if the size
of the place is greater than the size of the target. The current logic
does not know the size of the overhead(cookie), thus we just simply
warn. We warn even if the place is big enough to hold the overhead
and this is clearly wrong as it leads to false reports. This patch
eliminates the false reports and handles C++20 with special care.

Fixes #56264

C++17 and earlier standards state that:

new(2, f) T[5] results in a call of operator new[](sizeof(T) * 5 +
y, 2, f). Here, ... and y are non-negative unspecified values
representing array allocation overhead; the result of the
new-expression will be offset by this amount from the value returned
by operator new[]. This overhead may be applied in all array
new-expressions, including those referencing the library function
operator new[](std::size_t, void*) and other placement allocation
functions. The amount of overhead may vary from one invocation of
new to another.

Since the array overhead is an *unspecified* value, it makes sense to
warn only if the size of the Place is equal to the size of the Target.
Otherwise, e.g if we assumed that the overhead is sizeof(size_t),
then we might report a false positive.

Checking for array cookie does not makes sense if the standard is
C++20 or later. C++20 states that array overhead(cookie) is not
created if the new expression calls the non-allocating (placement)
form of the allocation function.
C++20, section 7.6.2.7 [expr.new], paragraph 15:

That argument shall be no less than the size of the object being
created; it may be greater than the size of the object being created
only if the object is an array and the allocation function is not a
non-allocating form (17.6.2.3).

C++20, section 7.6.2.7 [expr.new], paragraph 19:

This overhead may be applied in all array new-expressions, including
those referencing a placement allocation function, except when
referencing the library function operator new[](std::size_t, void*).

Related Defect Report:

  1. Array allocation overhead for non-allocating placement new

https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2382

Diff Detail

Event Timeline

martong created this revision.Jul 7 2022, 5:34 AM
Herald added a project: Restricted Project. · View Herald Transcript
martong requested review of this revision.Jul 7 2022, 5:34 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 7 2022, 5:34 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
martong edited the summary of this revision. (Show Details)Jul 7 2022, 5:45 AM
NoQ added inline comments.Jul 7 2022, 10:28 PM
clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
157–159

"might" is not very convincing, it may cause a reaction like "I've no idea what it's talking about and the compiler itself isn't sure, must be false positive". Can we do anything to point the user in the right direction? Say, if this is implementation-defined, are we looking at a portability issue?

martong updated this revision to Diff 443174.Jul 8 2022, 2:17 AM
  • Replace "might" with "unspecified value"
martong marked an inline comment as done.Jul 8 2022, 2:17 AM
martong added inline comments.
clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
157–159

Okay, I see your point. Let's dissect the corresponding sections of the standard:

new(2, f) T[5] results in a call of operator new[](sizeof(T) * 5 + y, 2, f).

Here, ... and y are non-negative unspecified values representing array allocation overhead;

The array overhead is an unspecified value. What does it mean exactly? My understanding is that this means it is implementation defined and the implementation is not required to document or guarantee anything. I came to this conclusion based on this stackoverflow question.

My interpretation could be wrong, but that does not matter. I think, we should just simply display the user what the standard says, and let them digest themselves the meaning of "unspecified". I am updating the patch like so.

martong marked an inline comment as done.Jul 8 2022, 2:18 AM
isuckatcs added inline comments.Jul 15 2022, 5:12 PM
clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
157–159

I also looked into this overhead question. In this stackoverflow answer someone links the same defect report that you did, and claims that since it's a defect report, it has been applied retroactively.

Apparently cppreference says the same. If you check the defect reports section on the bottom of the page you can see:

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

...
DR | Applied to | Behavior as published | Correct behavior
CWG 2382 | C++98 | non-allocating placement array new could require allocation overhead | such allocation overhead disallowed

So in my understanding you don't need to worry about this overhead at all. I hope this helps.

NoQ added inline comments.Aug 8 2022, 8:50 PM
clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
157–159

Ok it sounds like we shouldn't emit any warnings at all here, unless we're somehow able to get a signal that the user is compiling their code with a pre-2019 compiler that hasn't been updated to reflect this defect report. One way we could get that signal is by putting the checker into the portability package which is off-by-default. Then we can explain the situation in the warning message:

"Storage provided to placement new is only {0} bytes, which is sufficient to hold the array data but placement new may require additional unspecified array allocation overhead on compilers that don't implement CWG 2382"

Then if the users enable portability but aren't interested in this warning, at least they know how to enable and disable checkers, so they can disable this specific check.

steakhal added inline comments.Aug 26 2022, 1:15 PM
clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
157–159

Personally, I think we should definitely not branch on the standard version.
This was never a thing anyway, AFAIK.