Skip to content

Commit 450f195

Browse files
authored
[clang][UBSan] Add implicit conversion check for bitfields (#75481)
This patch implements the implicit truncation and implicit sign change checks for bitfields using UBSan. E.g., `-fsanitize=implicit-bitfield-truncation` and `-fsanitize=implicit-bitfield-sign-change`.
1 parent a2acf31 commit 450f195

File tree

11 files changed

+493
-73
lines changed

11 files changed

+493
-73
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ Non-comprehensive list of changes in this release
195195

196196
New Compiler Flags
197197
------------------
198+
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and
199+
sign change.
200+
- ``-fsanitize=implicit-integer-conversion`` a group that replaces the previous
201+
group ``-fsanitize=implicit-conversion``.
198202

199203
- ``-Wmissing-designated-field-initializers``, grouped under ``-Wmissing-field-initializers``.
200204
This diagnostic can be disabled to make ``-Wmissing-field-initializers`` behave
@@ -208,6 +212,9 @@ Modified Compiler Flags
208212
- Added a new diagnostic flag ``-Wreturn-mismatch`` which is grouped under
209213
``-Wreturn-type``, and moved some of the diagnostics previously controlled by
210214
``-Wreturn-type`` under this new flag. Fixes #GH72116.
215+
- ``-fsanitize=implicit-conversion`` is now a group for both
216+
``-fsanitize=implicit-integer-conversion`` and
217+
``-fsanitize=implicit-bitfield-conversion``.
211218

212219
- Added ``-Wcast-function-type-mismatch`` under the ``-Wcast-function-type``
213220
warning group. Moved the diagnostic previously controlled by

clang/docs/UndefinedBehaviorSanitizer.rst

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ Available checks are:
148148
Issues caught by this sanitizer are not undefined behavior,
149149
but are often unintentional.
150150
- ``-fsanitize=integer-divide-by-zero``: Integer division by zero.
151+
- ``-fsanitize=implicit-bitfield-conversion``: Implicit conversion from
152+
integer of larger bit width to smaller bitfield, if that results in data
153+
loss. This includes unsigned/signed truncations and sign changes, similarly
154+
to how the ``-fsanitize=implicit-integer-conversion`` group works, but
155+
explicitly for bitfields.
151156
- ``-fsanitize=nonnull-attribute``: Passing null pointer as a function
152157
parameter which is declared to never be null.
153158
- ``-fsanitize=null``: Use of a null pointer or creation of a null
@@ -193,16 +198,16 @@ Available checks are:
193198
signed division overflow (``INT_MIN/-1``). Note that checks are still
194199
added even when ``-fwrapv`` is enabled. This sanitizer does not check for
195200
lossy implicit conversions performed before the computation (see
196-
``-fsanitize=implicit-conversion``). Both of these two issues are handled
197-
by ``-fsanitize=implicit-conversion`` group of checks.
201+
``-fsanitize=implicit-integer-conversion``). Both of these two issues are handled
202+
by ``-fsanitize=implicit-integer-conversion`` group of checks.
198203
- ``-fsanitize=unreachable``: If control flow reaches an unreachable
199204
program point.
200205
- ``-fsanitize=unsigned-integer-overflow``: Unsigned integer overflow, where
201206
the result of an unsigned integer computation cannot be represented in its
202207
type. Unlike signed integer overflow, this is not undefined behavior, but
203208
it is often unintentional. This sanitizer does not check for lossy implicit
204209
conversions performed before such a computation
205-
(see ``-fsanitize=implicit-conversion``).
210+
(see ``-fsanitize=implicit-integer-conversion``).
206211
- ``-fsanitize=vla-bound``: A variable-length array whose bound
207212
does not evaluate to a positive value.
208213
- ``-fsanitize=vptr``: Use of an object whose vptr indicates that it is of
@@ -224,11 +229,15 @@ You can also use the following check groups:
224229
- ``-fsanitize=implicit-integer-arithmetic-value-change``: Catches implicit
225230
conversions that change the arithmetic value of the integer. Enables
226231
``implicit-signed-integer-truncation`` and ``implicit-integer-sign-change``.
227-
- ``-fsanitize=implicit-conversion``: Checks for suspicious
228-
behavior of implicit conversions. Enables
232+
- ``-fsanitize=implicit-integer-conversion``: Checks for suspicious
233+
behavior of implicit integer conversions. Enables
229234
``implicit-unsigned-integer-truncation``,
230235
``implicit-signed-integer-truncation``, and
231236
``implicit-integer-sign-change``.
237+
- ``-fsanitize=implicit-conversion``: Checks for suspicious
238+
behavior of implicit conversions. Enables
239+
``implicit-integer-conversion``, and
240+
``implicit-bitfield-conversion``.
232241
- ``-fsanitize=integer``: Checks for undefined or suspicious integer
233242
behavior (e.g. unsigned integer overflow).
234243
Enables ``signed-integer-overflow``, ``unsigned-integer-overflow``,

clang/include/clang/Basic/Sanitizers.def

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,24 +163,24 @@ SANITIZER_GROUP("implicit-integer-arithmetic-value-change",
163163
ImplicitIntegerArithmeticValueChange,
164164
ImplicitIntegerSignChange | ImplicitSignedIntegerTruncation)
165165

166-
SANITIZER("objc-cast", ObjCCast)
166+
SANITIZER_GROUP("implicit-integer-conversion", ImplicitIntegerConversion,
167+
ImplicitIntegerArithmeticValueChange |
168+
ImplicitUnsignedIntegerTruncation)
167169

168-
// FIXME:
169-
//SANITIZER_GROUP("implicit-integer-conversion", ImplicitIntegerConversion,
170-
// ImplicitIntegerArithmeticValueChange |
171-
// ImplicitUnsignedIntegerTruncation)
172-
//SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
173-
// ImplicitIntegerConversion)
170+
// Implicit bitfield sanitizers
171+
SANITIZER("implicit-bitfield-conversion", ImplicitBitfieldConversion)
174172

175173
SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
176-
ImplicitIntegerArithmeticValueChange |
177-
ImplicitUnsignedIntegerTruncation)
174+
ImplicitIntegerConversion |
175+
ImplicitBitfieldConversion)
178176

179177
SANITIZER_GROUP("integer", Integer,
180-
ImplicitConversion | IntegerDivideByZero | Shift |
178+
ImplicitIntegerConversion | IntegerDivideByZero | Shift |
181179
SignedIntegerOverflow | UnsignedIntegerOverflow |
182180
UnsignedShiftBase)
183181

182+
SANITIZER("objc-cast", ObjCCast)
183+
184184
SANITIZER("local-bounds", LocalBounds)
185185
SANITIZER_GROUP("bounds", Bounds, ArrayBounds | LocalBounds)
186186

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5580,11 +5580,44 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
55805580
break;
55815581
}
55825582

5583-
RValue RV = EmitAnyExpr(E->getRHS());
5583+
// TODO: Can we de-duplicate this code with the corresponding code in
5584+
// CGExprScalar, similar to the way EmitCompoundAssignmentLValue works?
5585+
RValue RV;
5586+
llvm::Value *Previous = nullptr;
5587+
QualType SrcType = E->getRHS()->getType();
5588+
// Check if LHS is a bitfield, if RHS contains an implicit cast expression
5589+
// we want to extract that value and potentially (if the bitfield sanitizer
5590+
// is enabled) use it to check for an implicit conversion.
5591+
if (E->getLHS()->refersToBitField()) {
5592+
llvm::Value *RHS =
5593+
EmitWithOriginalRHSBitfieldAssignment(E, Previous, &SrcType);
5594+
RV = RValue::get(RHS);
5595+
} else
5596+
RV = EmitAnyExpr(E->getRHS());
5597+
55845598
LValue LV = EmitCheckedLValue(E->getLHS(), TCK_Store);
5599+
55855600
if (RV.isScalar())
55865601
EmitNullabilityCheck(LV, RV.getScalarVal(), E->getExprLoc());
5587-
EmitStoreThroughLValue(RV, LV);
5602+
5603+
if (LV.isBitField()) {
5604+
llvm::Value *Result;
5605+
// If bitfield sanitizers are enabled we want to use the result
5606+
// to check whether a truncation or sign change has occurred.
5607+
if (SanOpts.has(SanitizerKind::ImplicitBitfieldConversion))
5608+
EmitStoreThroughBitfieldLValue(RV, LV, &Result);
5609+
else
5610+
EmitStoreThroughBitfieldLValue(RV, LV);
5611+
5612+
// If the expression contained an implicit conversion, make sure
5613+
// to use the value before the scalar conversion.
5614+
llvm::Value *Src = Previous ? Previous : RV.getScalarVal();
5615+
QualType DstType = E->getLHS()->getType();
5616+
EmitBitfieldConversionCheck(Src, SrcType, Result, DstType,
5617+
LV.getBitFieldInfo(), E->getExprLoc());
5618+
} else
5619+
EmitStoreThroughLValue(RV, LV);
5620+
55885621
if (getLangOpts().OpenMP)
55895622
CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(*this,
55905623
E->getLHS());

0 commit comments

Comments
 (0)