glider created this revision. glider added reviewers: kcc, rjmccall, rsmith. Herald added a subscriber: cfe-commits.
This patch adds a new feature, -fsanitize=init-locals, which generates zero initializers for uninitialized local variables. There's been discussions in the security community about the impact of zero-initializing all locals to prevent information leaks. The new feature shall help evaluating the pros and cons of such an approach. Credits for the code go to Daniel Micay (original patch is at https://github.com/AndroidHardeningArchive/platform_external_clang/commit/776a0955ef6686d23a82d2e6a3cbd4a6a882c31c) Repository: rC Clang https://reviews.llvm.org/D54473 Files: include/clang/Basic/Sanitizers.def lib/CodeGen/CGDecl.cpp lib/Driver/ToolChain.cpp test/CodeGen/sanitize-init-locals.c
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; +} + 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: 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: 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)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits