Skip to content

Commit 3d6f063

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
1 parent af46dfa commit 3d6f063

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
@@ -548,7 +548,7 @@ LANGOPT(BoundsAttributesCXXExperimental, 1, 0, "Experimental bounds attributes f
548548
LANGOPT(BoundsAttributesObjCExperimental, 1, 0, "Experimental bounds attributes for Objective-C")
549549
LANGOPT(BoundsSafetyRelaxedSystemHeaders, 1, 1,
550550
"Relax bounds safety assignment rules in system headers")
551-
ENUM_LANGOPT(BoundsSafetyBringUpMissingChecks, BoundsSafetyNewChecksMask, 6, BS_CHK_Default,
551+
ENUM_LANGOPT(BoundsSafetyBringUpMissingChecks, BoundsSafetyNewChecksMask, 7, BS_CHK_Default,
552552
"Bring up new -fbounds-safety run-time checks")
553553
/* TO_UPSTREAM(BoundsSafety) OFF*/
554554

clang/include/clang/Basic/LangOptions.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,13 +454,16 @@ class LangOptionsBase {
454454
BS_CHK_EndedByLowerBound = 1 << 3, // rdar://91596663
455455
BS_CHK_CompoundLiteralInit = 1 << 4, // rdar://110871666
456456
BS_CHK_LibCAttributes = 1 << 5, // rdar://84733153
457+
BS_CHK_ArraySubscriptAgg = 1 << 6, // rdar://145020583
457458

458459
BS_CHK_All = BS_CHK_AccessSize | BS_CHK_IndirectCountUpdate |
459460
BS_CHK_ReturnSize | BS_CHK_EndedByLowerBound |
460-
BS_CHK_CompoundLiteralInit | BS_CHK_LibCAttributes,
461+
BS_CHK_CompoundLiteralInit | BS_CHK_LibCAttributes |
462+
BS_CHK_ArraySubscriptAgg,
461463

462464
// This sets the default value assumed by clang if no
463-
// `-fbounds-safety-bringup-missing-checks` flags are passed to clang.
465+
// `-fbounds-safety-bringup-missing-checks` flags are
466+
// passed to clang.
464467
BS_CHK_Default = BS_CHK_None,
465468
};
466469

clang/include/clang/Driver/Options.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1964,12 +1964,12 @@ defm bounds_safety_adoption_mode : BoolFOption<"bounds-safety-adoption-mode",
19641964
def fbounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fbounds-safety-bringup-missing-checks=">, Group<f_Group>,
19651965
MetaVarName<"<check>">,
19661966
Visibility<[ClangOption, CC1Option]>,
1967-
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)">;
1967+
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)">;
19681968

19691969
def fno_bounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fno-bounds-safety-bringup-missing-checks=">, Group<f_Group>,
19701970
MetaVarName<"<check>">,
19711971
Visibility<[ClangOption, CC1Option]>,
1972-
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)">;
1972+
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)">;
19731973

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

clang/lib/CodeGen/CGExprAgg.cpp

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

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

381405
// 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
@@ -4056,6 +4056,8 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts,
40564056
Checks += "compound_literal_init,";
40574057
if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_LibCAttributes))
40584058
Checks += "libc_attributes,";
4059+
if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ArraySubscriptAgg))
4060+
Checks += "array_subscript_agg";
40594061
if (StringRef(Checks).ends_with(","))
40604062
Checks.pop_back();
40614063
}

0 commit comments

Comments
 (0)