-
Notifications
You must be signed in to change notification settings - Fork 14.6k
[Clang] add wraps and no_wraps attributes #115094
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
b5515eb
3a8593d
68e5ec1
55f52cb
0cb962f
b9f3a5a
45796e3
acc654b
f9a79ab
3de3561
093bb7f
445f8c7
a6bba49
86663ae
ee4a59b
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 |
---|---|---|
|
@@ -433,6 +433,26 @@ Attribute Changes in Clang | |
- Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or | ||
``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) | ||
|
||
- Introduced ``__attribute__((wraps))`` which can be added to type or variable | ||
declarations. Using an attributed type or variable in an arithmetic | ||
expression will define the overflow behavior for that expression as having | ||
two's complement wrap-around. These expressions will not be instrumented by | ||
overflow sanitizers nor will they cause integer overflow warnings. They also | ||
cannot be optimized away by some eager UB optimizations as the behavior of | ||
the arithmetic is no longer "undefined". | ||
|
||
There is also ``__attribute__((no_wraps))`` which can be added to types or | ||
variable declarations. Types or variables with this attribute may be | ||
instrumented by overflow sanitizers, if enabled. Note that this matches the | ||
default behavior of integer types. So, in most cases, ``no_wraps`` serves | ||
purely as an annotation to readers of code that a type or variable really | ||
shouldn't wrap-around. ``__attribute__((no_wraps))`` has the most function | ||
when paired with `Sanitizer Special Case Lists (SSCL) | ||
<https://clang.llvm.org/docs/SanitizerSpecialCaseList.html>`_. | ||
|
||
These attributes are only valid for C, as there are built-in language | ||
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. If the attribute is used with C++ code will it break the build? 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. No, it won't break the build. You will get an ignored attribute warning and the attributes won't be applied or have any effect. (I'll document this). These attributes are relatively well supported in C++ but that language has many more ways for the attribute to disappear unexpectedly. These attributes are mostly needed in C (as there aren't any alternatives). 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 guess it wouldn't be the worst idea to just let the attribute exist in C++ and document common breakages? We should really urge C++ users to use the type system and operator overloading to their advantage, though, and not use wraps/no_wraps. What do we think? 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. My view is that the semantics of C code should, where possible, not change when compiling as C++. This also holds for your type-based proposal. |
||
alternatives for other languages. | ||
|
||
Improvements to Clang's diagnostics | ||
----------------------------------- | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8710,3 +8710,106 @@ Declares that a function potentially allocates heap memory, and prevents any pot | |
of ``nonallocating`` by the compiler. | ||
}]; | ||
} | ||
|
||
def WrapsDocs : Documentation { | ||
let Category = DocCatField; | ||
let Content = [{ | ||
The ``wraps`` attribute can be used with type or variable declarations to | ||
denote that arithmetic containing attributed types or variables have defined | ||
JustinStitt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
overflow behavior. Specifically, the behavior is defined as being consistent | ||
with two's complement wrap-around. This is similar to ``-fwrapv`` but at the | ||
type and declaration level. For the purposes of sanitizers or warnings that | ||
concern themselves with the definedness of integer arithmetic, they will cease | ||
to instrument or warn about arithmetic that directly involves operands | ||
attributed with the ``wraps`` attribute. | ||
|
||
The ``signed-integer-overflow``, ``unsigned-integer-overflow``, | ||
``implicit-signed-integer-truncation`` and the | ||
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 don't think it should affect instrumentation at all. to suppress report we have no_sanitize attribute already 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. The entire intention of Furthermore, most of the use of |
||
``implicit-unsigned-integer-truncation`` sanitizers will not instrument | ||
arithmetic containing any operands attributed by ``wraps``. Similarly, the | ||
``-Winteger-overflow`` and ``-Wconstant-conversion`` warnings are disabled for | ||
these instances. | ||
|
||
The following example shows how one may disable ``signed-integer-overflow`` | ||
sanitizer instrumentation using ``__attribute__((wraps))`` on a type definition | ||
when building with ``-fsanitize=signed-integer-overflow``: | ||
|
||
.. code-block:: c | ||
|
||
typedef int __attribute__((wraps)) wrapping_int; | ||
|
||
void foo(void) { | ||
wrapping_int A = INT_MAX; | ||
++A; // no sanitizer instrumentation | ||
} | ||
|
||
``wraps`` may also be used with function parameters or declarations of | ||
variables as well as members of structures. | ||
|
||
Using ``wraps`` on non-integer types will result in an error. | ||
|
||
``wraps`` persists through implicit type promotions and will be applied to the | ||
result type of arithmetic expressions containing a wrapping operand. | ||
``-Wimplicitly-discarded-wraps-attribute`` warnings can be caused in situations | ||
where the ``wraps`` attribute cannot persist through implicit type conversions. | ||
Disable this with ``-Wno-implicitly-discarded-wraps-attribute``. | ||
}]; | ||
} | ||
|
||
def NoWrapsDocs : Documentation { | ||
let Category = DocCatField; | ||
let Content = [{ | ||
The ``no_wraps`` attribute can be used to annotate types or variables as | ||
non-wrapping. This may serve as a helpful annotation to readers of code that | ||
particular arithmetic expressions involving these types or variables are not | ||
meant to wrap-around. | ||
|
||
``no_wraps`` only interacts with sanitizers and | ||
`Sanitizer special case lists <https://clang.llvm.org/docs/SanitizerSpecialCaseList.html>`_ but, | ||
unlike ``wraps``, it does not modify the signed overflow behavior of types it | ||
affects. Instead, you should use ``-fwrapv`` or ``-fno-wrapv`` to define signed | ||
overflow behavior at the language level in combination with ``wraps`` for | ||
type-level modifications. | ||
|
||
When overflow or truncation sanitizer instrumentation is modified at the | ||
type-level through `SSCLs <https://clang.llvm.org/docs/SanitizerSpecialCaseList.html>`_, | ||
``no_wraps`` or ``wraps`` may be used to override sanitizer behavior. | ||
|
||
For example, one may specify an ignorelist (with ``-fsanitize-ignorelist=``) to | ||
disable the ``signed-integer-overflow`` sanitizer for all types: | ||
|
||
.. code-block:: text | ||
|
||
[signed-integer-overflow] | ||
type:* | ||
|
||
``no_wraps`` can override the behavior provided by the ignorelist to | ||
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 wrap and sanitize should be independent, because of "recover" mode. 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. Read my other comments about 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. But I see what you're saying about And for 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.
Yes, if, like you said before, wraps is about disabling sanitizers, than So, no_wraps(or sanitize) is here just to inverse ignore list for particular variable? In general idea of sanitizers is to check code with minimal modification. Saying from my experience in google3, I would rather over-suppress than introduce additional burden of maintenance. BTW. you have "typedef" support in ignore list. Wouldn't e nicer just to change e.g type from e.g. int to sanitizer_int typedef or something? So I see value in wraps/no_sanitize, but the reverse looks questionable. 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. Patch itself, if we introduce wrap/no_sanitize, I don't think no_wrap/sanitize is a big problem for clang maintainers. They may think otherwise :) However, this just smells like YAGNI to me. I would see Linux roll-out, as multi step process, something like:
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.
The current plan for the Kernel is ignoring all types and then using
We will be able to keep up with the checks because we will iteratively expand the scope of
Integer overflow (the unexpected kind) cause a lot of bugs and are an entire attack surface of their own. If we can provide more tools to developers such that enabling sanitizers makes more sense and causes less headaches (false-positives) then I see the ROI being potentially high. This is what 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. @vitalybuka Look at AOSP for example: https://android-review.googlesource.com/c/kernel/common/+/3343205
They, and other kernel projects that use sanitizers, really want a wraps or wraps-equivalent thing. 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 see, you are doing this in opposite way. My intuition it's more complicated than needed. But I didn't work on Kernel, maybe there are own specifics. It looks like you need to allow all types used in kernel one by one in the end. We tried allow list like approach in google3. It usually goes slower than ignore list approach - sanitizer everything that pass, the rest go in ignore list, usually by src: or fun:. Then next stage shrinking ignore list by fixing or converting to attribute on functions. @ramosian-glider has experience with other sanitizers, maybe you have opinion on the approach #115094 (comment)? |
||
effectively re-enable instrumentation for specific types or variables. | ||
|
||
.. code-block:: c | ||
|
||
typedef int __attribute__((no_wraps)) non_wrapping_int; | ||
|
||
void foo(non_wrapping_int A, int B) { | ||
++A; // will be instrumented if built with -fsanitize=signed-integer-overflow | ||
++B; // won't be instrumented as it is ignored by the ignorelist | ||
} | ||
|
||
Like ``wraps``, ``no_wraps`` persists through implicit type promotions and will | ||
be automatically applied to the result type of arithmetic expressions | ||
containing a wrapping operand. ``-Wimplicitly-discarded-wraps-attribute`` | ||
warnings can be caused in situations where the ``wraps`` or ``no_wraps`` | ||
attribute cannot persist through implicit type conversions. Disable this with | ||
``-Wno-implicitly-discarded-wraps-attribute``. | ||
|
||
Applying both ``__attribute__((wraps))`` and ``__attribute__((no_wraps))`` | ||
results in an error, as you should only use one or the other. Arithmetic binary | ||
operators containing both `wraps` and `no_wraps`-attributed types will only | ||
persist the `no_wraps` attribute towards the final resulting type. | ||
|
||
Like ``wraps``, ``no_wraps`` may also be used with function parameters or | ||
declarations of variables as well as members of structures but can not be used | ||
with non-integer types. | ||
}]; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2863,6 +2863,15 @@ bool QualType::isWebAssemblyFuncrefType() const { | |
getAddressSpace() == LangAS::wasm_funcref; | ||
} | ||
|
||
bool QualType::hasWrapsAttr() const { | ||
return !isNull() && getTypePtr()->hasAttr(attr::Wraps) && | ||
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. we probably want to do 'more work' to pull out the type you want/canonicalize the type here. |
||
!getTypePtr()->hasAttr(attr::NoWraps); | ||
} | ||
|
||
bool QualType::hasNoWrapsAttr() const { | ||
return !isNull() && getTypePtr()->hasAttr(attr::NoWraps); | ||
} | ||
|
||
QualType::PrimitiveDefaultInitializeKind | ||
QualType::isNonTrivialToPrimitiveDefaultInitialize() const { | ||
if (const auto *RT = | ||
|
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.
So this is essentially no opt?
I don't thing this will help, to me it will trigger readers to think what it's needed, unnecessary noise.
Regular comment will be as good enough?
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.
When used alongside very strict SSCLs there is great utility to be gained from
no_wraps
.In the kernel we want to use an SSCL ignorelist like this:
then annotate specific types like
size_t
in source:... Otherwise, yes, this is just a simple code annotation.