[ubsan] PR34266: When sanitizing the 'this' value for a member function that happens to be a lambda call operator, use the lambda's 'this' pointer, not the captured enclosing 'this' pointer (if any).

Do not sanitize the 'this' pointer of a member call operator for a lambda with
no capture-default, since that call operator can legitimately be called with a
null this pointer from the static invoker function. Any actual call with a null
this pointer should still be caught in the caller (if it is being sanitized).

This reinstates r311589 (reverted in r311680) with the above fix.

llvm-svn: 311695
This commit is contained in:
Richard Smith 2017-08-24 20:10:33 +00:00
parent 68107657d4
commit 376c28e296
3 changed files with 54 additions and 3 deletions

View File

@ -2027,7 +2027,10 @@ public:
/// \brief Returns the type of the \c this pointer. /// \brief Returns the type of the \c this pointer.
/// ///
/// Should only be called for instance (i.e., non-static) methods. /// Should only be called for instance (i.e., non-static) methods. Note
/// that for the call operator of a lambda closure type, this returns the
/// desugared 'this' type (a pointer to the closure type), not the captured
/// 'this' type.
QualType getThisType(ASTContext &C) const; QualType getThisType(ASTContext &C) const;
unsigned getTypeQualifiers() const { unsigned getTypeQualifiers() const {

View File

@ -22,6 +22,7 @@
#include "CodeGenPGO.h" #include "CodeGenPGO.h"
#include "TargetInfo.h" #include "TargetInfo.h"
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/Decl.h" #include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtCXX.h" #include "clang/AST/StmtCXX.h"
@ -1014,11 +1015,22 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
} }
// Check the 'this' pointer once per function, if it's available. // Check the 'this' pointer once per function, if it's available.
if (CXXThisValue) { if (CXXABIThisValue) {
SanitizerSet SkippedChecks; SanitizerSet SkippedChecks;
SkippedChecks.set(SanitizerKind::ObjectSize, true); SkippedChecks.set(SanitizerKind::ObjectSize, true);
QualType ThisTy = MD->getThisType(getContext()); QualType ThisTy = MD->getThisType(getContext());
EmitTypeCheck(TCK_Load, Loc, CXXThisValue, ThisTy,
// If this is the call operator of a lambda with no capture-default, it
// may have a static invoker function, which may call this operator with
// a null 'this' pointer.
if (isLambdaCallOperator(MD) &&
cast<CXXRecordDecl>(MD->getParent())->getLambdaCaptureDefault() ==
LCD_None)
SkippedChecks.set(SanitizerKind::Null, true);
EmitTypeCheck(isa<CXXConstructorDecl>(MD) ? TCK_ConstructorCall
: TCK_MemberCall,
Loc, CXXABIThisValue, ThisTy,
getContext().getTypeAlignInChars(ThisTy->getPointeeType()), getContext().getTypeAlignInChars(ThisTy->getPointeeType()),
SkippedChecks); SkippedChecks);
} }

View File

@ -449,6 +449,28 @@ void upcast_to_vbase() {
} }
} }
struct ThisAlign {
void this_align_lambda();
void this_align_lambda_2();
};
void ThisAlign::this_align_lambda() {
// CHECK-LABEL: define {{.*}}@"_ZZN9ThisAlign17this_align_lambdaEvENK3$_0clEv"
// CHECK-SAME: (%{{.*}}* %[[this:[^)]*]])
// CHECK: %[[this_addr:.*]] = alloca
// CHECK: store %{{.*}}* %[[this]], %{{.*}}** %[[this_addr]],
// CHECK: %[[this_inner:.*]] = load %{{.*}}*, %{{.*}}** %[[this_addr]],
// CHECK: %[[this_outer_addr:.*]] = getelementptr inbounds %{{.*}}, %{{.*}}* %[[this_inner]], i32 0, i32 0
// CHECK: %[[this_outer:.*]] = load %{{.*}}*, %{{.*}}** %[[this_outer_addr]],
//
// CHECK: %[[this_inner_isnonnull:.*]] = icmp ne %{{.*}}* %[[this_inner]], null
// CHECK: %[[this_inner_asint:.*]] = ptrtoint %{{.*}}* %[[this_inner]] to i
// CHECK: %[[this_inner_misalignment:.*]] = and i{{32|64}} %[[this_inner_asint]], {{3|7}},
// CHECK: %[[this_inner_isaligned:.*]] = icmp eq i{{32|64}} %[[this_inner_misalignment]], 0
// CHECK: %[[this_inner_valid:.*]] = and i1 %[[this_inner_isnonnull]], %[[this_inner_isaligned]],
// CHECK: br i1 %[[this_inner_valid:.*]]
[&] { return this; } ();
}
namespace CopyValueRepresentation { namespace CopyValueRepresentation {
// CHECK-LABEL: define {{.*}} @_ZN23CopyValueRepresentation2S3aSERKS0_ // CHECK-LABEL: define {{.*}} @_ZN23CopyValueRepresentation2S3aSERKS0_
// CHECK-NOT: call {{.*}} @__ubsan_handle_load_invalid_value // CHECK-NOT: call {{.*}} @__ubsan_handle_load_invalid_value
@ -532,4 +554,18 @@ namespace CopyValueRepresentation {
} }
} }
void ThisAlign::this_align_lambda_2() {
// CHECK-LABEL: define {{.*}}@"_ZZN9ThisAlign19this_align_lambda_2EvENK3$_1clEv"
// CHECK-SAME: (%{{.*}}* %[[this:[^)]*]])
// CHECK: %[[this_addr:.*]] = alloca
// CHECK: store %{{.*}}* %[[this]], %{{.*}}** %[[this_addr]],
// CHECK: %[[this_inner:.*]] = load %{{.*}}*, %{{.*}}** %[[this_addr]],
//
// Do not perform a null check on the 'this' pointer if the function might be
// called from a static invoker.
// CHECK-NOT: icmp ne %{{.*}}* %[[this_inner]], null
auto *p = +[] {};
p();
}
// CHECK: attributes [[NR_NUW]] = { noreturn nounwind } // CHECK: attributes [[NR_NUW]] = { noreturn nounwind }