-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc++][hardening] Categorize assertions that produce incorrect results #77183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2d62194
a0c0967
b675bfe
797a977
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -307,8 +307,11 @@ public: | |
: __data_(__s), | ||
__size_(__len) { | ||
#if _LIBCPP_STD_VER >= 14 | ||
_LIBCPP_ASSERT_UNCATEGORIZED(__len <= static_cast<size_type>(numeric_limits<difference_type>::max()), | ||
"string_view::string_view(_CharT *, size_t): length does not fit in difference_type"); | ||
// This will result in creating an invalid `string_view` object -- some calculations involving `size` would | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: while a few of these involve integer overflow, I don't think it's a good criterion for classification. To me, it seems very implementation-centric -- the fact that there is an overflow involved doesn't say much about the actual issue that will happen, which can range from something very minor or even benign to compromizing the memory safety of the program. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this one has more serious consequences than the categorization and comment suggest. The size parameter determines the bounds of the string. Every byte from It is not possible for a length over See #61100 for context. |
||
// overflow, making it effectively truncated. | ||
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( | ||
__len <= static_cast<size_type>(numeric_limits<difference_type>::max()), | ||
"string_view::string_view(_CharT *, size_t): length does not fit in difference_type"); | ||
_LIBCPP_ASSERT_NON_NULL( | ||
__len == 0 || __s != nullptr, "string_view::string_view(_CharT *, size_t): received nullptr"); | ||
#endif | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spent a lot of time thinking how to name this category and I can't say I'm completely satisfied with the result. The criteria I'm using is roughly "The given arguments make it impossible for us to produce the correct result; returning a nonsensical value doesn't cause any immediate runtime issues within our library, but is likely to lead to a bug, potentially a serious one, in the user code once they access the value".
Intuitively, it does seem like a well-defined concept to me. I think the difficulty I experienced with the naming is due to the fact that our naming scheme mostly describes the expectation for the condition, whereas my intuitive definition focuses on the outcomes of violating the condition. It's not an issue when there is a direct one-to-one correspondence between the two, which I think is the case for most other category names (e.g.
valid-element-access
arguably describes both the cause and the effect because there is a direct correspondence between them). This, however, is more of a many-to-one relationship. An obvious name would bevalid-argument
, but since we're mostly checking user-provided arguments, this really applies to every category we have -- for example,valid-element-access
is ultimately also a check for a valid argument. I still think that a naming scheme based on the outcomes being prevented would be easier to work with, but it goes against the fact that asserting asserts the successful case, not the failing case.While
argument-within-domain
is not that different frominvalid-argument
, it seems a little more specific to me and also seems to apply to the existing usages pretty well (it is, of course, inspired bystd::domain_error
). I thought about something likevalid-argument-fallback
ornonspecific-valid-argument
but it doesn't really help better express the part I consider the more important one, giving the user back an incorrect result (or object, which can be seen as a result of running a constructor).Another aspect to consider here is how easy it would be for a library developer, especially someone who is new to libc++, to use the new category in the intended way.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a guide about the different categories? Even a link to the definitions would give a clearer context. Would that make sense? -> https://libcxx.llvm.org/Hardening.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, absolutely. I want to finish the actual classification before writing it down -- I think it would be easier to describe it and generalize once the design is finalized. There are only a few dozen unclassified assertions left (not counting in-flight patches like this one), so I hope things settle down pretty soon on that front.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the writeup, this is really useful. Naming this is indeed really tricky. Some suggestions:
_LIBCPP_ASSERT_CANT_MEET_POSTCONDITIONS
_LIBCPP_ASSERT_CONSEQUENCES_UNKNOWN
_LIBCPP_ASSERT_CONSEQUENCES_OUTSIDE_LIBRARY
None of those is really pretty, but that's some food for thought. I think the unifying theme here is that the assertion is about times when you can't meet your postconditions, not about a "fallback category". The documentation should probably be updated not to give the impression that this is a fallback category.