[ubsan] Use pass_object_size info in bounds checks
Teach UBSan's bounds check to opportunistically use pass_object_size
information to check array accesses.
rdar://33272922
llvm-svn: 320128
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 88116f7..64af209 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -814,6 +814,53 @@
return false;
}
+llvm::Value *CodeGenFunction::LoadPassedObjectSize(const Expr *E,
+ QualType EltTy) {
+ ASTContext &C = getContext();
+ uint64_t EltSize = C.getTypeSizeInChars(EltTy).getQuantity();
+ if (!EltSize)
+ return nullptr;
+
+ auto *ArrayDeclRef = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
+ if (!ArrayDeclRef)
+ return nullptr;
+
+ auto *ParamDecl = dyn_cast<ParmVarDecl>(ArrayDeclRef->getDecl());
+ if (!ParamDecl)
+ return nullptr;
+
+ // Arrays don't have pass_object_size attributes, but if they have a constant
+ // size modifier it's the array size (C99 6.5.7.2p1).
+ if (auto *DecayedArrayTy = dyn_cast<DecayedType>(ParamDecl->getType()))
+ if (auto *ArrayTy =
+ dyn_cast<ConstantArrayType>(DecayedArrayTy->getOriginalType()))
+ return llvm::ConstantInt::get(SizeTy,
+ ArrayTy->getSize().getLimitedValue());
+
+ auto *POSAttr = ParamDecl->getAttr<PassObjectSizeAttr>();
+ if (!POSAttr)
+ return nullptr;
+
+ // Don't load the size if it's a lower bound.
+ int POSType = POSAttr->getType();
+ if (POSType != 0 && POSType != 1)
+ return nullptr;
+
+ // Find the implicit size parameter.
+ auto PassedSizeIt = SizeArguments.find(ParamDecl);
+ if (PassedSizeIt == SizeArguments.end())
+ return nullptr;
+
+ const ImplicitParamDecl *PassedSizeDecl = PassedSizeIt->second;
+ assert(LocalDeclMap.count(PassedSizeDecl) && "Passed size not loadable");
+ Address AddrOfSize = LocalDeclMap.find(PassedSizeDecl)->second;
+ llvm::Value *SizeInBytes = EmitLoadOfScalar(AddrOfSize, /*Volatile=*/false,
+ C.getSizeType(), E->getExprLoc());
+ llvm::Value *SizeOfElement =
+ llvm::ConstantInt::get(SizeInBytes->getType(), EltSize);
+ return Builder.CreateUDiv(SizeInBytes, SizeOfElement);
+}
+
/// If Base is known to point to the start of an array, return the length of
/// that array. Return 0 if the length cannot be determined.
static llvm::Value *getArrayIndexingBound(
@@ -835,9 +882,16 @@
return CGF.Builder.getInt(CAT->getSize());
else if (const auto *VAT = dyn_cast<VariableArrayType>(AT))
return CGF.getVLASize(VAT).first;
+ // Ignore pass_object_size here. It's not applicable on decayed pointers.
}
}
+ QualType EltTy{Base->getType()->getPointeeOrArrayElementType(), 0};
+ if (llvm::Value *POS = CGF.LoadPassedObjectSize(Base, EltTy)) {
+ IndexedType = Base->getType();
+ return POS;
+ }
+
return nullptr;
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 1c8c2a5..1a66533 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3927,6 +3927,11 @@
LValueBaseInfo *BaseInfo = nullptr,
TBAAAccessInfo *TBAAInfo = nullptr);
+ /// If \p E references a parameter with pass_object_size info or a constant
+ /// array size modifier, emit the object size divided by the size of \p EltTy.
+ /// Otherwise return null.
+ llvm::Value *LoadPassedObjectSize(const Expr *E, QualType EltTy);
+
void EmitSanitizerStatReport(llvm::SanitizerStatKind SSK);
private: