Skip to content

Commit bc5fe85

Browse files
committed
[clang][UBSan] Add implicit conversion check for bitfields
This patch implements the implicit truncation and implicit sign change checks for bitfields.
1 parent b23f985 commit bc5fe85

File tree

10 files changed

+558
-41
lines changed

10 files changed

+558
-41
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,26 @@ Non-comprehensive list of changes in this release
122122

123123
New Compiler Flags
124124
------------------
125+
- ``-fsanitize=implicit-unsigned-bitfield-truncation`` catches implicit
126+
unsigned conversions involving bitfields.
127+
- ``-fsanitize=implicit-signed-bitfield-truncation`` catches implicit
128+
signed conversions involving bitfields.
129+
- ``-fsanitize=implicit-bitfield-sign-change`` catches implicit
130+
conversions involving bitfields that result in a sign change.
131+
- ``-fsanitize=implicit-bitfield-truncation`` a group to include both
132+
``-fsanitize=implicit-unsigned-bitfield-truncation`` and
133+
``-fsanitize=implicit-signed-bitfield-truncation``.
134+
- ``-fsanitize=implicit-bitfield-arithmetic-value-change`` a group to
135+
include both ``implicit-signed-bitfield-truncation`` and
136+
``implicit-bitfield-sign-change``.
137+
- ``-fsanitize=implicit-bitfield-conversion`` a group to include
138+
``-fsanitize=implicit-unsigned-bitfield-truncation``,
139+
``-fsanitize=implicit-signed-bitfield-truncation`` and
140+
``implicit-bitfield-sign-change``.
141+
- ``-fsanitize=implicit-integer-conversion`` a group to include
142+
``-fsanitize=implicit-unsigned-integer-truncation``,
143+
``-fsanitize=implicit-signed-integer-truncation`` and
144+
``implicit-integer-sign-change``.
125145

126146
.. _target_os_detail:
127147

@@ -139,6 +159,11 @@ Deprecated Compiler Flags
139159

140160
Modified Compiler Flags
141161
-----------------------
162+
- ``-fsanitize=implicit-conversion`` is now a group for both
163+
``-fsanitize=implicit-integer-conversion`` and
164+
``-fsanitize=implicit-bitfield-conversion``. Hence,
165+
``-fsanitize=implicit-integer-conversion`` has replaced what previously
166+
was ``-fsanitize=implicit-conversion``.
142167

143168
Removed Compiler Flags
144169
-------------------------

clang/docs/UndefinedBehaviorSanitizer.rst

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,23 @@ 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-unsigned-bitfield-truncation``,
152+
``-fsanitize=implicit-signed-bitfield-truncation``: Implicit conversion from
153+
integer of larger bit width to smaller bitfield, if that results in data
154+
loss. That is, if the demoted value, after casting back to the original
155+
width, is not equal to the original value before the downcast.
156+
The ``-fsanitize=implicit-unsigned-bitfield-truncation`` handles conversions
157+
between two ``unsigned`` types, while
158+
``-fsanitize=implicit-signed-bitfield-truncation`` handles the rest of the
159+
conversions - when either one, or both of the types are signed.
160+
Issues caught by these sanitizers are not undefined behavior,
161+
but are often unintentional.
162+
- ``-fsanitize=implicit-bitfield-sign-change``: Implicit conversion from
163+
integer of larger bit width to smaller bitfield, if that changes the
164+
sign of the value. That is, if the original value was negative and the
165+
new value is positive (or zero), or the original value was positive,
166+
and the new value is negative. Issues caught by this sanitizer are not
167+
undefined behavior, but are often unintentional.
151168
- ``-fsanitize=nonnull-attribute``: Passing null pointer as a function
152169
parameter which is declared to never be null.
153170
- ``-fsanitize=null``: Use of a null pointer or creation of a null
@@ -192,16 +209,16 @@ Available checks are:
192209
This includes all the checks covered by ``-ftrapv``, as well as checks for
193210
signed division overflow (``INT_MIN/-1``), but not checks for
194211
lossy implicit conversions performed before the computation
195-
(see ``-fsanitize=implicit-conversion``). Both of these two issues are
196-
handled by ``-fsanitize=implicit-conversion`` group of checks.
212+
(see ``-fsanitize=implicit-integer-conversion``). Both of these two issues are
213+
handled by ``-fsanitize=implicit-integer-conversion`` group of checks.
197214
- ``-fsanitize=unreachable``: If control flow reaches an unreachable
198215
program point.
199216
- ``-fsanitize=unsigned-integer-overflow``: Unsigned integer overflow, where
200217
the result of an unsigned integer computation cannot be represented in its
201218
type. Unlike signed integer overflow, this is not undefined behavior, but
202219
it is often unintentional. This sanitizer does not check for lossy implicit
203220
conversions performed before such a computation
204-
(see ``-fsanitize=implicit-conversion``).
221+
(see ``-fsanitize=implicit-integer-conversion``).
205222
- ``-fsanitize=vla-bound``: A variable-length array whose bound
206223
does not evaluate to a positive value.
207224
- ``-fsanitize=vptr``: Use of an object whose vptr indicates that it is of
@@ -213,8 +230,9 @@ Available checks are:
213230
You can also use the following check groups:
214231
- ``-fsanitize=undefined``: All of the checks listed above other than
215232
``float-divide-by-zero``, ``unsigned-integer-overflow``,
216-
``implicit-conversion``, ``local-bounds`` and the ``nullability-*`` group
217-
of checks.
233+
``implicit-integer-conversion``, ``implicit-bitfield-conversion``,
234+
``implicit-conversion``, ``local-bounds`` and the ``nullability-*``
235+
group of checks.
218236
- ``-fsanitize=undefined-trap``: Deprecated alias of
219237
``-fsanitize=undefined``.
220238
- ``-fsanitize=implicit-integer-truncation``: Catches lossy integral
@@ -223,11 +241,26 @@ You can also use the following check groups:
223241
- ``-fsanitize=implicit-integer-arithmetic-value-change``: Catches implicit
224242
conversions that change the arithmetic value of the integer. Enables
225243
``implicit-signed-integer-truncation`` and ``implicit-integer-sign-change``.
226-
- ``-fsanitize=implicit-conversion``: Checks for suspicious
227-
behavior of implicit conversions. Enables
244+
- ``-fsanitize=implicit-integer-conversion``: Checks for suspicious
245+
behavior of implicit integer conversions. Enables
228246
``implicit-unsigned-integer-truncation``,
229247
``implicit-signed-integer-truncation``, and
230248
``implicit-integer-sign-change``.
249+
- ``-fsanitize=implicit-bitfield-truncation``: Catches lossy bitfield
250+
conversions. Enables ``implicit-signed-bitfield-truncation`` and
251+
``implicit-unsigned-bitfield-truncation``.
252+
- ``-fsanitize=implicit-bitfield-arithmetic-value-change``: Catches implicit
253+
conversions that change the arithmetic value of the bitfield. Enables
254+
``implicit-signed-bitfield-truncation`` and ``implicit-bitfield-sign-change``.
255+
- ``-fsanitize=implicit-bitfield-conversion``: Checks for suspicious
256+
behavior of implicit bitfield conversions. Enables
257+
``implicit-unsigned-bitfield-truncation``,
258+
``implicit-signed-bitfield-truncation``, and
259+
``implicit-bitfield-sign-change``.
260+
- ``-fsanitize=implicit-conversion``: Checks for suspicious
261+
behavior of implicit conversions. Enables
262+
``implicit-integer-conversion``, and
263+
``implicit-bitfield-conversion``.
231264
- ``-fsanitize=integer``: Checks for undefined or suspicious integer
232265
behavior (e.g. unsigned integer overflow).
233266
Enables ``signed-integer-overflow``, ``unsigned-integer-overflow``,

clang/include/clang/Basic/Sanitizers.def

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,24 +163,38 @@ 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)
169+
170+
// Implicit bitfield sanitizers
171+
SANITIZER("implicit-unsigned-bitfield-truncation", ImplicitUnsignedBitfieldTruncation)
172+
SANITIZER("implicit-signed-bitfield-truncation", ImplicitSignedBitfieldTruncation)
173+
SANITIZER_GROUP("implicit-bitfield-truncation", ImplicitBitfieldTruncation,
174+
ImplicitUnsignedBitfieldTruncation |
175+
ImplicitSignedBitfieldTruncation)
176+
177+
SANITIZER("implicit-bitfield-sign-change", ImplicitBitfieldSignChange)
167178

168-
// FIXME:
169-
//SANITIZER_GROUP("implicit-integer-conversion", ImplicitIntegerConversion,
170-
// ImplicitIntegerArithmeticValueChange |
171-
// ImplicitUnsignedIntegerTruncation)
172-
//SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
173-
// ImplicitIntegerConversion)
179+
SANITIZER_GROUP("implicit-bitfield-arithmetic-value-change",
180+
ImplicitBitfieldArithmeticValueChange,
181+
ImplicitBitfieldSignChange | ImplicitSignedBitfieldTruncation)
182+
183+
SANITIZER_GROUP("implicit-bitfield-conversion", ImplicitBitfieldConversion,
184+
ImplicitBitfieldArithmeticValueChange |
185+
ImplicitUnsignedBitfieldTruncation)
174186

175187
SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
176-
ImplicitIntegerArithmeticValueChange |
177-
ImplicitUnsignedIntegerTruncation)
188+
ImplicitIntegerConversion |
189+
ImplicitBitfieldConversion)
178190

179191
SANITIZER_GROUP("integer", Integer,
180-
ImplicitConversion | IntegerDivideByZero | Shift |
192+
ImplicitIntegerConversion | IntegerDivideByZero | Shift |
181193
SignedIntegerOverflow | UnsignedIntegerOverflow |
182194
UnsignedShiftBase)
183195

196+
SANITIZER("objc-cast", ObjCCast)
197+
184198
SANITIZER("local-bounds", LocalBounds)
185199
SANITIZER_GROUP("bounds", Bounds, ArrayBounds | LocalBounds)
186200

clang/lib/CodeGen/CGExpr.cpp

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

5573-
RValue RV = EmitAnyExpr(E->getRHS());
5573+
llvm::Value *Previous = nullptr;
5574+
RValue RV;
5575+
QualType SrcType = E->getRHS()->getType();
5576+
// If LHS refers to a bitfield we want to retrieve the value before
5577+
// implicit conversion between the bitfield type and the RHS type
5578+
// and evaluate RHS without integer sanitizer checks (if passed)
5579+
if (auto *ICE = RetrieveImplicitCastExprForBitfieldSanitizer(E)) {
5580+
SrcType = ICE->getSubExpr()->getType();
5581+
Previous = EmitScalarExpr(ICE->getSubExpr());
5582+
// Pass default ScalarConversionOpts so that sanitizer check is
5583+
// not emitted here
5584+
llvm::Value *RHS = EmitScalarConversion(Previous, SrcType, ICE->getType(),
5585+
ICE->getExprLoc());
5586+
RV = RValue::get(RHS);
5587+
}
5588+
5589+
if (!Previous)
5590+
RV = EmitAnyExpr(E->getRHS());
5591+
55745592
LValue LV = EmitCheckedLValue(E->getLHS(), TCK_Store);
5593+
55755594
if (RV.isScalar())
55765595
EmitNullabilityCheck(LV, RV.getScalarVal(), E->getExprLoc());
5577-
EmitStoreThroughLValue(RV, LV);
5596+
5597+
// Passing a pointer EmitStoreThroughBitfieldLValue will emit the result
5598+
// If sanitizer checks are not used, we do not use the result
5599+
// Hence, use EmitStoreThroughLValue instead
5600+
if (SanOpts.hasOneOf(SanitizerKind::ImplicitBitfieldConversion) &&
5601+
LV.isBitField() && RV.isScalar()) {
5602+
llvm::Value *Src = Previous ? Previous : RV.getScalarVal();
5603+
QualType DstType = E->getLHS()->getType();
5604+
llvm::Value *Result;
5605+
EmitStoreThroughBitfieldLValue(RV, LV, &Result);
5606+
EmitBitfieldConversionCheck(Src, SrcType, Result, DstType,
5607+
LV.getBitFieldInfo(), E->getExprLoc());
5608+
} else
5609+
EmitStoreThroughLValue(RV, LV);
5610+
55785611
if (getLangOpts().OpenMP)
55795612
CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(*this,
55805613
E->getLHS());

0 commit comments

Comments
 (0)