Index: include/clang/Basic/Sanitizers.def =================================================================== --- include/clang/Basic/Sanitizers.def +++ include/clang/Basic/Sanitizers.def @@ -159,6 +159,9 @@ SANITIZER_GROUP("efficiency-all", Efficiency, EfficiencyCacheFrag | EfficiencyWorkingSet) +// Initialize local variables. +SANITIZER("init-locals", InitLocals) + // Scudo hardened allocator SANITIZER("scudo", Scudo) Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -1434,7 +1434,8 @@ return; } - if (isTrivialInitializer(Init)) + bool trivial = isTrivialInitializer(Init); + if (trivial && !getLangOpts().Sanitize.has(SanitizerKind::InitLocals)) return; // Check whether this is a byref variable that's potentially @@ -1445,8 +1446,15 @@ Address Loc = capturedByInit ? emission.Addr : emission.getObjectAddress(*this); + bool constantAggregate = emission.IsConstantAggregate; + llvm::Constant *constant = nullptr; - if (emission.IsConstantAggregate || D.isConstexpr()) { + if (trivial) { + QualType Ty = D.getType(); + constant = CGM.EmitNullConstant(Ty); + if (Ty->isArrayType() || Ty->isRecordType()) + constantAggregate = true; + } else if (emission.IsConstantAggregate || D.isConstexpr()) { assert(!capturedByInit && "constant init contains a capturing block?"); constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D); } @@ -1457,7 +1465,7 @@ return EmitExprAsInit(Init, &D, lv, capturedByInit); } - if (!emission.IsConstantAggregate) { + if (!constantAggregate) { // For simple scalar/complex initialization, store the value directly. LValue lv = MakeAddrLValue(Loc, type); lv.setNonGC(true); Index: lib/Driver/ToolChain.cpp =================================================================== --- lib/Driver/ToolChain.cpp +++ lib/Driver/ToolChain.cpp @@ -819,7 +819,7 @@ SanitizerMask Res = (Undefined & ~Vptr & ~Function) | (CFI & ~CFIICall) | CFICastStrict | UnsignedIntegerOverflow | - ImplicitConversion | Nullability | LocalBounds; + ImplicitConversion | Nullability | LocalBounds | InitLocals; if (getTriple().getArch() == llvm::Triple::x86 || getTriple().getArch() == llvm::Triple::x86_64 || getTriple().getArch() == llvm::Triple::arm || Index: test/CodeGen/sanitize-init-locals.c =================================================================== --- test/CodeGen/sanitize-init-locals.c +++ test/CodeGen/sanitize-init-locals.c @@ -0,0 +1,42 @@ +// Test for -fsanitize=init-locals. + +// RUN: %clang_cc1 %s -O0 -triple x86_64-unknown-linux-gnu -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O0 -triple x86_64-unknown-linux-gnu -fsanitize=init-locals -emit-llvm -o - | FileCheck -check-prefixes=CHECK,CHECK-INIT-LOCALS %s +// RUN: %clang_cc1 %s -O0 -triple x86_64-unknown-linux-gnu -Wuninitialized -emit-llvm -o - 2>&1 | FileCheck -check-prefix=CHECK-WUNINITIALIZED %s +// RUN: %clang_cc1 %s -O0 -triple x86_64-unknown-linux-gnu -Wuninitialized -fsanitize=init-locals -emit-llvm -o - 2>&1 | FileCheck -check-prefix=CHECK-WUNINITIALIZED %s + + +// CHECK: @testSanitizeInitLocals.local_array_init_part = private unnamed_addr constant [5 x i32] [i32 0, i32 1, i32 2, i32 0, i32 0], align 16 +// CHECK: @testSanitizeInitLocals.local_array_init = private unnamed_addr constant [5 x i32] [i32 0, i32 1, i32 2, i32 3, i32 4], align 16 + +// CHECK: %local_int_uninit = alloca i32, align 4 +// CHECK: %local_int_init_0 = alloca i32, align 4 +// CHECK: %local_int_init = alloca i32, align 4 +// CHECK: %local_array_uninit = alloca [5 x i32], align 16 +// CHECK: %local_array_init_part = alloca [5 x i32], align 16 +// CHECK: %local_array_init = alloca [5 x i32], align 16 + +// CHECK-INIT-LOCALS: store i32 0, i32* %local_int_uninit, align 4 +// CHECK: store i32 0, i32* %local_int_init_0, align 4 +// CHECK: store i32 1, i32* %local_int_init, align 4 +// +// CHECK-INIT-LOCALS: [[CAST0:%.*]] = {{.*}}%local_array_uninit +// CHECK-INIT-LOCALS: call void @llvm.memset{{.*}}({{.*}}[[CAST0]], i8 0, i64 20 +// CHECK: [[CAST1:%.*]] = {{.*}}%local_array_init_part +// CHECK: call void @llvm.memcpy{{.*}}({{.*}}[[CAST1]], {{.*}}@testSanitizeInitLocals.local_array_init_part{{.*}}, {{.*}} 20 +// CHECK: [[CAST2:%.*]] = {{.*}}%local_array_init +// CHECK: call void @llvm.memcpy{{.*}}({{.*}}[[CAST2]], {{.*}}@testSanitizeInitLocals.local_array_init{{.*}}, {{.*}} 20 + +// CHECK-WUNINITIALIZED: warning: variable 'local_int_uninit' is uninitialized when used here + +int testSanitizeInitLocals(void) +{ + const int local_int_uninit; + const int local_int_init_0 = 0; + const int local_int_init = 1; + const int local_array_uninit[5]; + const int local_array_init_part[5] = {0, 1, 2}; + const int local_array_init[5] = {0, 1, 2, 3, 4}; + return local_int_uninit; +} +