forked from OSchip/llvm-project
[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:
parent
68107657d4
commit
376c28e296
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
Loading…
Reference in New Issue