Skip to content

Commit d8447c7

Browse files
authored
[Clang] Correct handling of negative and out-of-bounds indices (#71877)
GCC returns 0 for a negative index on an array in a structure. It also returns 0 for an array index that goes beyond the extent of the array. In addition. a pointer to a struct field returns that field's size, not the size of it plus the rest of the struct, unless it's the first field in the struct. struct s { int count; char dummy; int array[] __attribute((counted_by(count))); }; struct s *p = malloc(...); p->count = 10; A __bdos on the elements of p return: __bdos(p, 0) == 30 __bdos(p->array, 0) == 10 __bdos(&p->array[0], 0) == 10 __bdos(&p->array[-1], 0) == 0 __bdos(&p->array[42], 0) == 0 Also perform some refactoring, putting the "counted_by" calculations in their own function.
1 parent 4376f8c commit d8447c7

File tree

2 files changed

+172
-155
lines changed

2 files changed

+172
-155
lines changed

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 73 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -851,78 +851,94 @@ CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
851851
// max(sizeof(struct s),
852852
// offsetof(struct s, array) + p->count * sizeof(*p->array))
853853
//
854+
ASTContext &Ctx = getContext();
854855
const Expr *Base = E->IgnoreParenImpCasts();
855856
const Expr *Idx = nullptr;
856857

857858
if (const auto *UO = dyn_cast<UnaryOperator>(Base);
858859
UO && UO->getOpcode() == UO_AddrOf) {
859-
if (const auto *ASE =
860-
dyn_cast<ArraySubscriptExpr>(UO->getSubExpr()->IgnoreParens())) {
861-
Base = ASE->getBase();
860+
Expr *SubExpr = UO->getSubExpr()->IgnoreParenImpCasts();
861+
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(SubExpr)) {
862+
Base = ASE->getBase()->IgnoreParenImpCasts();
862863
Idx = ASE->getIdx()->IgnoreParenImpCasts();
863864

864-
if (const auto *IL = dyn_cast<IntegerLiteral>(Idx);
865-
IL && !IL->getValue().getSExtValue())
866-
Idx = nullptr;
865+
if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) {
866+
int64_t Val = IL->getValue().getSExtValue();
867+
if (Val < 0)
868+
// __bdos returns 0 for negative indexes into an array in a struct.
869+
return getDefaultBuiltinObjectSizeResult(Type, ResType);
870+
871+
if (Val == 0)
872+
// The index is 0, so we don't need to take it into account.
873+
Idx = nullptr;
874+
}
875+
} else {
876+
// Potential pointer to another element in the struct.
877+
Base = SubExpr;
867878
}
868879
}
869880

881+
// Get the flexible array member Decl.
882+
const ValueDecl *FAMDecl = nullptr;
883+
if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
884+
// Check if \p Base is referencing the FAM itself.
885+
if (const ValueDecl *MD = ME->getMemberDecl()) {
886+
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
887+
getLangOpts().getStrictFlexArraysLevel();
888+
if (!Decl::isFlexibleArrayMemberLike(
889+
Ctx, MD, MD->getType(), StrictFlexArraysLevel,
890+
/*IgnoreTemplateOrMacroSubstitution=*/true))
891+
return nullptr;
892+
893+
FAMDecl = MD;
894+
}
895+
} else if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
896+
// Check if we're pointing to the whole struct.
897+
QualType Ty = DRE->getDecl()->getType();
898+
if (Ty->isPointerType())
899+
Ty = Ty->getPointeeType();
900+
901+
if (const auto *RD = Ty->getAsRecordDecl())
902+
// Don't use the outer lexical record because the FAM might be in a
903+
// different RecordDecl.
904+
FAMDecl = FindFlexibleArrayMemberField(Ctx, RD);
905+
}
906+
907+
if (!FAMDecl || !FAMDecl->hasAttr<CountedByAttr>())
908+
// No flexible array member found or it doesn't have the "counted_by"
909+
// attribute.
910+
return nullptr;
911+
870912
const ValueDecl *CountedByFD = FindCountedByField(Base);
871913
if (!CountedByFD)
872914
// Can't find the field referenced by the "counted_by" attribute.
873915
return nullptr;
874916

875917
// Build a load of the counted_by field.
876918
bool IsSigned = CountedByFD->getType()->isSignedIntegerType();
877-
const RecordDecl *OuterRD =
878-
CountedByFD->getDeclContext()->getOuterLexicalRecordContext();
879-
ASTContext &Ctx = getContext();
880-
881-
// Load the counted_by field.
882919
const Expr *CountedByExpr = BuildCountedByFieldExpr(Base, CountedByFD);
883920
Value *CountedByInst = EmitAnyExprToTemp(CountedByExpr).getScalarVal();
884921
llvm::Type *CountedByTy = CountedByInst->getType();
885922

923+
// Build a load of the index and subtract it from the count.
924+
Value *IdxInst = nullptr;
886925
if (Idx) {
887-
// There's an index into the array. Remove it from the count.
888926
bool IdxSigned = Idx->getType()->isSignedIntegerType();
889-
Value *IdxInst = EmitAnyExprToTemp(Idx).getScalarVal();
927+
IdxInst = EmitAnyExprToTemp(Idx).getScalarVal();
890928
IdxInst = IdxSigned ? Builder.CreateSExtOrTrunc(IdxInst, CountedByTy)
891929
: Builder.CreateZExtOrTrunc(IdxInst, CountedByTy);
892930

893-
// If the index is negative, don't subtract it from the counted_by
894-
// value. The pointer is pointing to something before the FAM.
895-
IdxInst = Builder.CreateNeg(IdxInst, "", !IdxSigned, IdxSigned);
931+
// We go ahead with the calculation here. If the index turns out to be
932+
// negative, we'll catch it at the end.
896933
CountedByInst =
897-
Builder.CreateAdd(CountedByInst, IdxInst, "", !IsSigned, IsSigned);
934+
Builder.CreateSub(CountedByInst, IdxInst, "", !IsSigned, IsSigned);
898935
}
899936

900-
// Get the size of the flexible array member's base type.
901-
const ValueDecl *FAMDecl = nullptr;
902-
if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
903-
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
904-
getLangOpts().getStrictFlexArraysLevel();
905-
if (const ValueDecl *MD = ME->getMemberDecl()) {
906-
if (!Decl::isFlexibleArrayMemberLike(
907-
Ctx, MD, MD->getType(), StrictFlexArraysLevel,
908-
/*IgnoreTemplateOrMacroSubstitution=*/true))
909-
return nullptr;
910-
// Base is referencing the FAM itself.
911-
FAMDecl = MD;
912-
}
913-
}
914-
915-
if (!FAMDecl)
916-
FAMDecl = FindFlexibleArrayMemberField(Ctx, OuterRD);
917-
918-
assert(FAMDecl && "Can't find the flexible array member field");
919-
937+
// Calculate how large the flexible array member is in bytes.
920938
const ArrayType *ArrayTy = Ctx.getAsArrayType(FAMDecl->getType());
921939
CharUnits Size = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
922940
llvm::Constant *ElemSize =
923941
llvm::ConstantInt::get(CountedByTy, Size.getQuantity(), IsSigned);
924-
925-
// Calculate how large the flexible array member is in bytes.
926942
Value *FAMSize =
927943
Builder.CreateMul(CountedByInst, ElemSize, "", !IsSigned, IsSigned);
928944
FAMSize = IsSigned ? Builder.CreateSExtOrTrunc(FAMSize, ResType)
@@ -931,47 +947,38 @@ CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
931947

932948
if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
933949
// The whole struct is specificed in the __bdos.
950+
const RecordDecl *OuterRD =
951+
CountedByFD->getDeclContext()->getOuterLexicalRecordContext();
934952
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(OuterRD);
935953

936954
// Get the offset of the FAM.
937955
CharUnits Offset = Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(FAMDecl));
938956
llvm::Constant *FAMOffset =
939957
ConstantInt::get(ResType, Offset.getQuantity(), IsSigned);
940-
941-
// max(sizeof(struct s),
942-
// offsetof(struct s, array) + p->count * sizeof(*p->array))
943958
Value *OffsetAndFAMSize =
944959
Builder.CreateAdd(FAMOffset, Res, "", !IsSigned, IsSigned);
945960

946961
// Get the full size of the struct.
947962
llvm::Constant *SizeofStruct =
948963
ConstantInt::get(ResType, Layout.getSize().getQuantity(), IsSigned);
949964

965+
// max(sizeof(struct s),
966+
// offsetof(struct s, array) + p->count * sizeof(*p->array))
950967
Res = IsSigned
951968
? Builder.CreateBinaryIntrinsic(llvm::Intrinsic::smax,
952969
OffsetAndFAMSize, SizeofStruct)
953970
: Builder.CreateBinaryIntrinsic(llvm::Intrinsic::umax,
954971
OffsetAndFAMSize, SizeofStruct);
955-
} else if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
956-
// Pointing to a place before the FAM. Add the difference to the FAM's
957-
// size.
958-
if (const ValueDecl *MD = ME->getMemberDecl(); MD != FAMDecl) {
959-
CharUnits Offset = Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(MD));
960-
CharUnits FAMOffset =
961-
Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(FAMDecl));
962-
963-
Res = Builder.CreateAdd(
964-
Res, ConstantInt::get(ResType, FAMOffset.getQuantity() -
965-
Offset.getQuantity()));
966-
}
967972
}
968973

969-
// A negative 'FAMSize' means that the index was greater than the count,
970-
// or an improperly set count field. Return -1 (for types 0 and 1) or 0
971-
// (for types 2 and 3).
972-
return Builder.CreateSelect(Builder.CreateIsNeg(FAMSize),
973-
getDefaultBuiltinObjectSizeResult(Type, ResType),
974-
Res);
974+
// A negative \p IdxInst or \p CountedByInst means that the index lands
975+
// outside of the flexible array member. If that's the case, we want to
976+
// return 0.
977+
Value *Cmp = Builder.CreateIsNotNeg(CountedByInst);
978+
if (IdxInst)
979+
Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(IdxInst), Cmp);
980+
981+
return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
975982
}
976983

977984
/// Returns a Value corresponding to the size of the given expression.
@@ -1006,19 +1013,19 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
10061013
}
10071014
}
10081015

1009-
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
1010-
// evaluate E for side-effects. In either case, we shouldn't lower to
1011-
// @llvm.objectsize.
1012-
if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext())))
1013-
return getDefaultBuiltinObjectSizeResult(Type, ResType);
1014-
10151016
if (IsDynamic) {
10161017
// Emit special code for a flexible array member with the "counted_by"
10171018
// attribute.
10181019
if (Value *V = emitFlexibleArrayMemberSize(E, Type, ResType))
10191020
return V;
10201021
}
10211022

1023+
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
1024+
// evaluate E for side-effects. In either case, we shouldn't lower to
1025+
// @llvm.objectsize.
1026+
if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext())))
1027+
return getDefaultBuiltinObjectSizeResult(Type, ResType);
1028+
10221029
Value *Ptr = EmittedE ? EmittedE : EmitScalarExpr(E);
10231030
assert(Ptr->getType()->isPointerTy() &&
10241031
"Non-pointer passed to __builtin_object_size?");

0 commit comments

Comments
 (0)