r320128 - [ubsan] Use pass_object_size info in bounds checks

Vedant Kumar via cfe-commits cfe-commits at lists.llvm.org
Thu Dec 7 17:51:48 PST 2017


Author: vedantk
Date: Thu Dec  7 17:51:47 2017
New Revision: 320128

URL: http://llvm.org/viewvc/llvm-project?rev=320128&view=rev
Log:
[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

Added:
    cfe/trunk/test/CodeGen/ubsan-pass-object-size.c
Modified:
    cfe/trunk/lib/CodeGen/CGExpr.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h

Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=320128&r1=320127&r2=320128&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Thu Dec  7 17:51:47 2017
@@ -814,6 +814,53 @@ static bool isFlexibleArrayMemberExpr(co
   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 @@ static llvm::Value *getArrayIndexingBoun
         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;
 }
 

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=320128&r1=320127&r2=320128&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Thu Dec  7 17:51:47 2017
@@ -3927,6 +3927,11 @@ public:
                                    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:

Added: cfe/trunk/test/CodeGen/ubsan-pass-object-size.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/ubsan-pass-object-size.c?rev=320128&view=auto
==============================================================================
--- cfe/trunk/test/CodeGen/ubsan-pass-object-size.c (added)
+++ cfe/trunk/test/CodeGen/ubsan-pass-object-size.c Thu Dec  7 17:51:47 2017
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 %s -emit-llvm -w -triple x86_64-apple-darwin10 -fsanitize=array-bounds -o - | FileCheck %s
+
+// CHECK-LABEL: define i32 @foo(
+int foo(int *const p __attribute__((pass_object_size(0))), int n) {
+  int x = (p)[n];
+
+  // CHECK: [[SIZE_ALLOCA:%.*]] = alloca i64, align 8
+  // CHECK: store i64 %{{.*}}, i64* [[SIZE_ALLOCA]], align 8
+  // CHECK: [[LOAD_SIZE:%.*]] = load i64, i64* [[SIZE_ALLOCA]], align 8, !nosanitize
+  // CHECK-NEXT: [[SCALED_SIZE:%.*]] = udiv i64 [[LOAD_SIZE]], 4, !nosanitize
+  // CHECK-NEXT: [[SEXT_N:%.*]] = sext i32 %{{.*}} to i64, !nosanitize
+  // CHECK-NEXT: [[ICMP:%.*]] = icmp ult i64 [[SEXT_N]], [[SCALED_SIZE]], !nosanitize
+  // CHECK-NEXT: br i1 [[ICMP]], {{.*}} !nosanitize
+  // CHECK: __ubsan_handle_out_of_bounds
+
+  {
+    int **p = &p; // Shadow the parameter. The pass_object_size info is lost.
+    // CHECK-NOT: __ubsan_handle_out_of_bounds
+    x = *p[n];
+  }
+
+  // CHECK: ret i32
+  return x;
+}
+
+typedef struct {} ZeroSizedType;
+
+// CHECK-LABEL: define void @bar(
+ZeroSizedType bar(ZeroSizedType *const p __attribute__((pass_object_size(0))), int n) {
+  // CHECK-NOT: __ubsan_handle_out_of_bounds
+  // CHECK: ret void
+  return p[n];
+}
+
+// CHECK-LABEL: define i32 @baz(
+int baz(int *const p __attribute__((pass_object_size(1))), int n) {
+  // CHECK: __ubsan_handle_out_of_bounds
+  // CHECK: ret i32
+  return p[n];
+}
+
+// CHECK-LABEL: define i32 @mat(
+int mat(int *const p __attribute__((pass_object_size(2))), int n) {
+  // CHECK-NOT: __ubsan_handle_out_of_bounds
+  // CHECK: ret i32
+  return p[n];
+}
+
+// CHECK-LABEL: define i32 @pat(
+int pat(int *const p __attribute__((pass_object_size(3))), int n) {
+  // CHECK-NOT: __ubsan_handle_out_of_bounds
+  // CHECK: ret i32
+  return p[n];
+}
+
+// CHECK-LABEL: define i32 @cat(
+int cat(int p[static 10], int n) {
+  // CHECK: icmp ult i64 {{.*}}, 10, !nosanitize
+  // CHECK: __ubsan_handle_out_of_bounds
+  // CHECK: ret i32
+  return p[n];
+}
+
+// CHECK-LABEL: define i32 @bat(
+int bat(int n, int p[n]) {
+  // CHECK-NOT: __ubsan_handle_out_of_bounds
+  // CHECK: ret i32
+  return p[n];
+}




More information about the cfe-commits mailing list