Skip to content

Commit 9643fc4

Browse files
committed
[BoundsSafety] Fix missing bounds check when indexing into a buffer of aggregates
Previously bounds checks where missing for a case like: ``` struct F {int x;}; struct F access(struct F* __bidi_indexable f, size_t idx) { // the index operation should be bounds checked. return f[idx]; ``` Note this only happened specifically for ArraySubscriptExpr and **only** when the whole aggregate was being loaded with no further operations. E.g. `f[idx].x` was already correctly bounds checked. The missing bounds check is guarded using `-fbounds-safety-bringup-missing-checks=array_subscript_agg` to avoid breaking existing users. The bug occured because when `AggExprEmitter::VisitArraySubscriptExpr` calls `EmitAggLoadOfLValue`, `EmitAggLoadOfLValue` didn't call `EmitCheckedLValue` and instead called `EmitLValue`. In this patch the value of `Checked` even when the `array_subscript_agg` is enabled it still does ``` Checked |= E->getType()->isPointerTypeWithBounds(); ``` which was the previous condition for calling `EmitCheckedLValue`. This is because there's an interaction with UBSan there that we might accidently change if setting `Checked` in this way was removed. We should investigate this in the future and this is tracked by rdar://145257962. rdar://145020583 (cherry picked from commit 3d6f063)
1 parent c5c47b4 commit 9643fc4

File tree

8 files changed

+1765
-12
lines changed

8 files changed

+1765
-12
lines changed

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ LANGOPT(BoundsAttributesCXXExperimental, 1, 0, "Experimental bounds attributes f
540540
LANGOPT(BoundsAttributesObjCExperimental, 1, 0, "Experimental bounds attributes for Objective-C")
541541
LANGOPT(BoundsSafetyRelaxedSystemHeaders, 1, 1,
542542
"Relax bounds safety assignment rules in system headers")
543-
ENUM_LANGOPT(BoundsSafetyBringUpMissingChecks, BoundsSafetyNewChecksMask, 6, BS_CHK_Default,
543+
ENUM_LANGOPT(BoundsSafetyBringUpMissingChecks, BoundsSafetyNewChecksMask, 7, BS_CHK_Default,
544544
"Bring up new -fbounds-safety run-time checks")
545545
/* TO_UPSTREAM(BoundsSafety) OFF*/
546546

clang/include/clang/Basic/LangOptions.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,13 +418,16 @@ class LangOptionsBase {
418418
BS_CHK_EndedByLowerBound = 1 << 3, // rdar://91596663
419419
BS_CHK_CompoundLiteralInit = 1 << 4, // rdar://110871666
420420
BS_CHK_LibCAttributes = 1 << 5, // rdar://84733153
421+
BS_CHK_ArraySubscriptAgg = 1 << 6, // rdar://145020583
421422

422423
BS_CHK_All = BS_CHK_AccessSize | BS_CHK_IndirectCountUpdate |
423424
BS_CHK_ReturnSize | BS_CHK_EndedByLowerBound |
424-
BS_CHK_CompoundLiteralInit | BS_CHK_LibCAttributes,
425+
BS_CHK_CompoundLiteralInit | BS_CHK_LibCAttributes |
426+
BS_CHK_ArraySubscriptAgg,
425427

426428
// This sets the default value assumed by clang if no
427-
// `-fbounds-safety-bringup-missing-checks` flags are passed to clang.
429+
// `-fbounds-safety-bringup-missing-checks` flags are
430+
// passed to clang.
428431
BS_CHK_Default = BS_CHK_None,
429432
};
430433

clang/include/clang/Driver/Options.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,12 +2004,12 @@ defm bounds_safety_adoption_mode : BoolFOption<"bounds-safety-adoption-mode",
20042004
def fbounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fbounds-safety-bringup-missing-checks=">, Group<f_Group>,
20052005
MetaVarName<"<check>">,
20062006
Visibility<[ClangOption, CC1Option]>,
2007-
HelpText<"Enable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, all)">;
2007+
HelpText<"Enable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, array_subscript_agg, all)">;
20082008

20092009
def fno_bounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fno-bounds-safety-bringup-missing-checks=">, Group<f_Group>,
20102010
MetaVarName<"<check>">,
20112011
Visibility<[ClangOption, CC1Option]>,
2012-
HelpText<"Disable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, all)">;
2012+
HelpText<"Disable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, array_subscript_agg, all)">;
20132013

20142014
def fbounds_safety_bringup_missing_checks : Flag<["-"], "fbounds-safety-bringup-missing-checks">, Group<f_Group>,
20152015
Visibility<[ClangOption]>,

clang/lib/CodeGen/CGExprAgg.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
7777
/// EmitAggLoadOfLValue - Given an expression with aggregate type that
7878
/// represents a value lvalue, this method emits the address of the lvalue,
7979
/// then loads the result into DestPtr.
80-
void EmitAggLoadOfLValue(const Expr *E);
80+
/* TO_UPSTREAM(BoundsSafety) ON */
81+
void EmitAggLoadOfLValue(const Expr *E, bool Checked = false);
82+
/* TO_UPSTREAM(BoundsSafety) OFF */
8183

8284
/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
8385
/// SrcIsRValue is true if source comes from an RValue.
@@ -167,7 +169,9 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
167169
void VisitStringLiteral(StringLiteral *E) { EmitAggLoadOfLValue(E); }
168170
void VisitCompoundLiteralExpr(CompoundLiteralExpr *E);
169171
void VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
170-
EmitAggLoadOfLValue(E);
172+
/* TO_UPSTREAM(BoundsSafety) ON */
173+
EmitAggLoadOfLValue(E, E->getBase()->getType()->isPointerTypeWithBounds());
174+
/* TO_UPSTREAM(BoundsSafety) OFF */
171175
}
172176
void VisitPredefinedExpr(const PredefinedExpr *E) {
173177
EmitAggLoadOfLValue(E);
@@ -368,10 +372,30 @@ AggExprEmitter::WidePointerElemCallback AggExprEmitter::DefaultElemCallback =
368372
/// EmitAggLoadOfLValue - Given an expression with aggregate type that
369373
/// represents a value lvalue, this method emits the address of the lvalue,
370374
/// then loads the result into DestPtr.
371-
void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) {
375+
void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E, bool Checked) {
372376
/*TO_UPSTREAM(BoundsSafety) ON*/
373-
LValue LV = E->getType()->isPointerTypeWithBounds() ?
374-
CGF.EmitCheckedLValue(E, CodeGenFunction::TCK_Load) : CGF.EmitLValue(E);
377+
if (CGF.getLangOpts().hasNewBoundsSafetyCheck(
378+
clang::LangOptionsBase::BS_CHK_ArraySubscriptAgg)) {
379+
// TODO(dliew): Modifying `Checked` should probably be removed.
380+
// Calling `EmitCheckedLValue` does two things:
381+
//
382+
// 1. If `E` is an ArraySubscriptExpr then emits bound checks if UBSan is
383+
// on or if the base pointer has bounds information
384+
// 2. Calls `CodeGenFunction::EmitTypeCheck()` in some cases.
385+
//
386+
// (1.) is already handled by `AggExprEmitter::VisitArraySubscriptExpr()` so
387+
// modifying `Checked` isn't need for that. However, (2.) adds UBSan type
388+
// checks.
389+
//
390+
// We should audit this interaction with UBSan to see if its safe to remove
391+
// setting `Checked`. (rdar://145257962).
392+
Checked |= E->getType()->isPointerTypeWithBounds();
393+
} else {
394+
// Preserve old buggy behavior
395+
Checked = E->getType()->isPointerTypeWithBounds();
396+
}
397+
LValue LV = Checked ? CGF.EmitCheckedLValue(E, CodeGenFunction::TCK_Load)
398+
: CGF.EmitLValue(E);
375399
/*TO_UPSTREAM(BoundsSafety) OFF*/
376400

377401
// If the type of the l-value is atomic, then do an atomic load.

clang/lib/Driver/BoundsSafetyArgs.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args,
4949
.Case("compound_literal_init",
5050
LangOptions::BS_CHK_CompoundLiteralInit)
5151
.Case("libc_attributes", LangOptions::BS_CHK_LibCAttributes)
52+
.Case("array_subscript_agg",
53+
LangOptions::BS_CHK_ArraySubscriptAgg)
5254
.Case("all", LangOptions::BS_CHK_All)
5355
.Case("none", LangOptions::BS_CHK_None)
5456
.Default(std::nullopt);

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3940,6 +3940,8 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts,
39403940
Checks += "compound_literal_init,";
39413941
if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_LibCAttributes))
39423942
Checks += "libc_attributes,";
3943+
if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ArraySubscriptAgg))
3944+
Checks += "array_subscript_agg";
39433945
if (StringRef(Checks).ends_with(","))
39443946
Checks.pop_back();
39453947
}

0 commit comments

Comments
 (0)