-
Notifications
You must be signed in to change notification settings - Fork 12.4k
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
[Sema] Add check for bitfield assignments to larger integral types #68276
Conversation
@llvm/pr-subscribers-clang ChangesWe noticed that clang does not check for bitfield assignment widths, while gcc does check this. gcc produced a warning like so for it's -Wconversion flag: $ gcc -Wconversion -c test.c This option is disabled by default, and is enabled by usign the -Wbitfield-width option. Full diff: https://github.com/llvm/llvm-project/pull/68276.diff 3 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e2eaeb885e2ea77..9ffb7baacac22d7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6165,6 +6165,9 @@ def warn_signed_bitfield_enum_conversion : Warning<
"signed bit-field %0 needs an extra bit to represent the largest positive "
"enumerators of %1">,
InGroup<BitFieldEnumConversion>, DefaultIgnore;
+def warn_bitfield_too_small_for_integral_type : Warning<
+ "bit-field %0 is not wide enough to store type of %1 ">,
+ InGroup<BitFieldWidth>, DefaultIgnore;
def note_change_bitfield_sign : Note<
"consider making the bitfield type %select{unsigned|signed}0">;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 3b0cc154c2e46ab..eb567bc4935e768 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -14300,6 +14300,16 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
S.Diag(WidthExpr->getExprLoc(), diag::note_widen_bitfield)
<< BitsNeeded << ED << WidthExpr->getSourceRange();
}
+ } else if (OriginalInit->getType()->isIntegralType(S.Context)) {
+ TypeInfo TI = S.Context.getTypeInfo(OriginalInit->getType());
+ if (TI.Width > FieldWidth) {
+ Expr *WidthExpr = Bitfield->getBitWidth();
+ S.Diag(InitLoc, diag::warn_bitfield_too_small_for_integral_type)
+ << Bitfield << OriginalInit->getType();
+ S.Diag(WidthExpr->getExprLoc(), diag::note_widen_bitfield)
+ << TI.Width << OriginalInit->getType()
+ << WidthExpr->getSourceRange();
+ }
}
return false;
diff --git a/clang/test/SemaCXX/bitfield-width.c b/clang/test/SemaCXX/bitfield-width.c
new file mode 100644
index 000000000000000..69aa2e229869906
--- /dev/null
+++ b/clang/test/SemaCXX/bitfield-width.c
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -Wbitfield-width -fsyntax-only -verify %s
+
+typedef struct _xx {
+ int bf:9; // expected-note{{widen this field to 32 bits to store all values of 'int'}}
+ // expected-note@-1{{widen this field to 16 bits to store all values of 'short'}}
+ // expected-note@-2{{widen this field to 64 bits to store all values of 'long'}}
+ // expected-note@-3{{widen this field to 32 bits to store all values of 'int'}}
+ } xx, *pxx;
+
+ xx vxx;
+
+ void foo1(int x) {
+ vxx.bf = x; // expected-warning{{bit-field 'bf' is not wide enough to store type of 'int'}}
+ }
+ void foo2(short x) {
+ vxx.bf = x; // expected-warning{{bit-field 'bf' is not wide enough to store type of 'short'}}
+ }
+ void foo3(char x) {
+ vxx.bf = x;
+ }
+ void foo4(long x) {
+ vxx.bf = x; // expected-warning{{bit-field 'bf' is not wide enough to store type of 'long'}}
+ }
+ void foo5(void * x) {
+ vxx.bf = (int)x; // expected-warning{{cast to smaller integer type 'int' from 'void *'}}
+ // expected-warning@-1{{bit-field 'bf' is not wide enough to store type of 'int'}}
+ }
+ int fee(void) {
+ return 0;
+ }
|
I don't claim this is ready to merge, but I would like to get the review party started. I'm looking for constructive comments to advance this patch to a mergeable state. I don't do a lot of these patches, so have probably missed some basic things. Thanks. |
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.
Why not wrap this into -Wconversion
on clang as well?
a388c20
to
3f5fc97
Compare
@shafik , I updated the check into -Wconversion. Temporarily addressed the CI failure (to get a passing baseline). Awaiting further review comments. Thank you. |
5e95a43
to
83dd0fa
Compare
Hi @AaronBallman and @erichkeane , any improvements you could suggest? I could expand the testing for sure. Before I invest time in that, I'd like to be sure the general approach is ok in Sema. Thanks. |
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.
Also needs a release note.
I'll add a release note also in the next iteration. Thanks |
83dd0fa
to
becc338
Compare
Updates
TBD: Provide information on real findings in an llvm/clang compile using this check |
becc338
to
24660d9
Compare
Hi @AaronBallman , I ran a compile using this change on clang as you asked and have results. The compile ran with no crashes or errors, and produced 1478 bitfield-conversion warnings. I'll show a few examples below. To put that number into context, I enabled -Wconversion to compare the new warning - bitfield-conversion - with the other conversion checks. You can see from the results that while 1478 findings is large, it's less than 10% of the total conversion findings that exist - so looking at those numbers in context is helpful. Are there any other suggested improvements for this patch, or do you think it's ok to merge? |
Thank you for running the experiment! From spot-checking the results, did they help you to find any actual bugs? And did you notice any patterns in the results that could be used to safely silence the diagnostic in some cases? |
24660d9
to
f70a9fb
Compare
We did find issues in our code base, and an "interesting" study in coding style related to the bitfield-enum-conversion check I'll describe. For the bitfield-conversion check, we found no real interesting patterns other than maybe the developer could use a mask on the RHS. But we observed that can mask problems (pun intended!!!) because a value could actually be greater in rank than the bitfield. An improvement to be made is a static analysis check (flow and context sensitive) to check the range of a value to be assigned to a bitfield. This would be an additional check to bitfield-conversion, and could be used to "vote" on the presence of a real defect or not. This may seem a bit too pedantic, but it is what it is :) Back to the interesting topic of bitfield-enum-conversion, you may know some programmers use a style to define C enums where the last element of an enum is the number of enums. An example looks like this...
We see false positives related to this style, but this actually caught a real bug where the bitfield was not large enough to contain the enum. The trouble is suppressing the false positives - and this is certainly possible. It's also possible to change the coding style to accommodate the compiler diagnostic behavior. But tying these two together, it's more difficult to detect unintended use for the bitfield-conversion check compared to the bitfield-enum-conversion check - and I think the contrast is important to highlight (because it helps to comprehend the problem). It's more difficult to infer the programmers intent for the bitfield-conversion check compared to disambiguating user intent for the bitfield-enum diagnostic. Understanding a bitfield-conversion diagnostic well enough to comprehend a true positive still requires the user to recognize the value assigned may take on a rank greater than be defined in the bitfield - which is why we think a static analysis check is appropriate for the code we develop. Hope this was helpful. Please let me know if you require any other changes for this to be accepted. Thank you! |
f70a9fb
to
3bb0c84
Compare
Hi all, I believe all comments have been addressed to date. Please let me know if there's anything else required. Thank you! |
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.
Just some minor things to cleanup, but this otherwise LGTM, thank you!
We noticed that clang does not check for bitfield assignment widths, while gcc does check this. gcc produced a warning like so for it's -Wconversion flag: $ gcc -Wconversion -c test.c test.c: In function 'foo': test.c:10:15: warning: conversion from 'int' to 'signed char:7' may change value [-Wconversion] 10 | vxx.bf = x; // no warning | ^ This change simply adds this for integral types under the -Wconversion compiler option.
3bb0c84
to
47e3626
Compare
Hi! |
I'll revert this change for now then. |
This change introduces the bitfield conversion check after fixes to the test case. The original submission unfortunately did not comprehend differences in architecture for the diagnostic messages, leading to unanticipated failures in the arm build bots. Original PR: #68276 Clang does not check for bitfield assignment widths, while gcc checks this. gcc produced a warning like so for it's -Wconversion flag: ``` $ gcc -Wconversion -c test.c test.c: In function 'foo': test.c:10:15: warning: conversion from 'int' to 'signed char:7' may change value [-Wconversion] 10 | vxx.bf = x; // no warning | ^ ``` This change simply adds this check for integral types under the -Wbitfield-conversion compiler option.
The documentation for this should also explain how to write code such that the warning does not fire (by giving an example with |
Hello, repro Cmd: where fileName.ii is
Could you please revert? |
It was already reverted in 2e955c0. |
This change introduces the bitfield conversion check after fixes to the test case. The original submission unfortunately did not comprehend differences in architecture for the diagnostic messages, leading to unanticipated failures in the arm build bots. Original PR: llvm/llvm-project#68276 Clang does not check for bitfield assignment widths, while gcc checks this. gcc produced a warning like so for it's -Wconversion flag: ``` $ gcc -Wconversion -c test.c test.c: In function 'foo': test.c:10:15: warning: conversion from 'int' to 'signed char:7' may change value [-Wconversion] 10 | vxx.bf = x; // no warning | ^ ``` This change simply adds this check for integral types under the -Wbitfield-conversion compiler option.
We noticed that clang does not check for bitfield assignment widths, while gcc does check this.
gcc produced a warning like so for it's -Wconversion flag:
This change simply adds this check for integral types under the -Wconversion compiler option.