gingell created this revision. gingell added reviewers: cfe-commits, kcc. This patch adds a "value-after-delete" sanitizer, which will invalidate the value of a pointer passed in a delete expression.
For instance, when -fsanitize=value-after-delete is passed: int *foo = new int; delete foo; // foo == 0xDEADBEEFDEADBEEF This is intended to help catch some use-after-free problems by ensuring access through a deleted pointer fails immediately on an address should be obviously suspicious when inspected in the debugger. The expectation is immediately invalidating dangling pointers can help uncover latent bugs that might otherwise cause more subtle problems further down the line. https://reviews.llvm.org/D25199 Files: docs/UndefinedBehaviorSanitizer.rst include/clang/Basic/Sanitizers.def include/clang/Driver/Options.td lib/CodeGen/CGExprScalar.cpp test/CodeGenCXX/sanitize-value-after-delete.cpp test/Driver/fsanitize.c
Index: test/Driver/fsanitize.c =================================================================== --- test/Driver/fsanitize.c +++ test/Driver/fsanitize.c @@ -3,27 +3,27 @@ // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP // RUN: %clang -target x86_64-linux-gnu -fsanitize-undefined-trap-on-error -fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP -// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}} -// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound" -// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound" +// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|value-after-delete|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute|function),?){19}"}} +// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,value-after-delete,vla-bound" +// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,value-after-delete,vla-bound" // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED -// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){19}"}} +// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|value-after-delete|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){20}"}} // RUN: %clang -target x86_64-apple-darwin10 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-DARWIN -// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}} +// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|value-after-delete|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){18}"}} // RUN: %clang -target i386-unknown-openbsd -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-OPENBSD -// CHECK-UNDEFINED-OPENBSD: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}} +// CHECK-UNDEFINED-OPENBSD: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|value-after-delete|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){18}"}} // RUN: %clang -target i386-pc-win32 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN32 // RUN: %clang -target i386-pc-win32 -fsanitize=undefined -x c++ %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN32 --check-prefix=CHECK-UNDEFINED-WIN-CXX // RUN: %clang -target x86_64-pc-win32 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN64 // RUN: %clang -target x86_64-pc-win32 -fsanitize=undefined -x c++ %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN64 --check-prefix=CHECK-UNDEFINED-WIN-CXX // CHECK-UNDEFINED-WIN32: "--dependent-lib={{[^"]*}}ubsan_standalone-i386.lib" // CHECK-UNDEFINED-WIN64: "--dependent-lib={{[^"]*}}ubsan_standalone-x86_64.lib" // CHECK-UNDEFINED-WIN-CXX: "--dependent-lib={{[^"]*}}ubsan_standalone_cxx{{[^"]*}}.lib" -// CHECK-UNDEFINED-WIN-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}} +// CHECK-UNDEFINED-WIN-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|value-after-delete|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){18}"}} // RUN: %clang -target i386-pc-win32 -fsanitize-coverage=bb %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-COVERAGE-WIN32 // CHECK-COVERAGE-WIN32: "--dependent-lib={{[^"]*}}ubsan_standalone-i386.lib" @@ -43,7 +43,7 @@ // CHECK-FNO-SANITIZE-ALL: "-fsanitize=thread" // RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED -// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|object-size|array-bounds|returns-nonnull-attribute|nonnull-attribute),?){15}"}} +// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|value-after-delete|vla-bound|alignment|null|object-size|array-bounds|returns-nonnull-attribute|nonnull-attribute),?){16}"}} // RUN: %clang -target x86_64-linux-gnu -fsanitize=shift -fno-sanitize=shift-base %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSANITIZE-SHIFT-PARTIAL // CHECK-FSANITIZE-SHIFT-PARTIAL: "-fsanitize=shift-exponent" @@ -201,7 +201,7 @@ // RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN // RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=all -fsanitize-recover=thread -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN // RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover=all -fno-sanitize-recover=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN -// CHECK-RECOVER-UBSAN: "-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}} +// CHECK-RECOVER-UBSAN: "-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|value-after-delete|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){18}"}} // CHECK-NO-RECOVER-UBSAN-NOT: sanitize-recover // RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=all -fsanitize-recover=object-size,shift-base -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-RECOVER Index: test/CodeGenCXX/sanitize-value-after-delete.cpp =================================================================== --- test/CodeGenCXX/sanitize-value-after-delete.cpp +++ test/CodeGenCXX/sanitize-value-after-delete.cpp @@ -0,0 +1,24 @@ +// Test -fsanitize-value-after-delete +// RUN: %clang_cc1 -O3 -fsanitize=value-after-delete -disable-llvm-optzns -std=c++11 -triple=x86_64-pc-linux -emit-llvm -o - %s | FileCheck %s + +int *f1() { + return new int; +} + +void test() { + int *p1 = new int; + char *p2 = new char[1024]; + + delete p1; + delete[] p2; + +DO_NOT_MODIFY: + delete f1(); +} + +// CHECK-LABEL: define void @_Z4testv() +// CHECK: store {{.*}} inttoptr (i64 -2401053088876216593 {{.*}} %p1 +// CHECK: store {{.*}} inttoptr (i64 -2401053088876216593 {{.*}} %p2 +// CHECK-NOT: store {{.*}} inttoptr (i64 -2401053088876216593 {{.*}} %p2 +// CHECK-LABEL: DO_NOT_MODIFY +// CHECK-NOT: {{.*}} inttoptr (i64 -2401053088876216593 {{.*}} Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -408,6 +408,23 @@ } Value *VisitCXXDeleteExpr(const CXXDeleteExpr *E) { CGF.EmitCXXDeleteExpr(E); + // If the value after delete sanitize is enabled then set the + // value of the deleted pointer to an invalid debug constant. + if (CGF.SanOpts.has(SanitizerKind::ValueAfterDelete)) { + const Expr *arg = E->getArgument(); + if (arg->IgnoreImplicit()->isLValue() && + !arg->HasSideEffects(CGF.getContext())) { + LValue LHS = EmitLValue(arg); + if (!LHS.isVolatile()) { + const unsigned width = CGF.getTarget().getMaxPointerWidth(); + Value *valueAfterDelete = Builder.getInt( + llvm::APInt(width, 0xDEADBEEFDEADBEEF)); + llvm::Value *RHS = Builder.CreateIntToPtr( + valueAfterDelete, ConvertType(LHS.getType()), "conv"); + CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS); + } + } + } return nullptr; } Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -687,6 +687,9 @@ def fsanitize_address_use_after_scope : Flag<["-"], "fsanitize-address-use-after-scope">, Group<f_clang_Group>, Flags<[CC1Option]>, HelpText<"Enable use-after-scope detection in AddressSanitizer">; +def fsanitize_value_after_delete : Flag<["-"], "fsanitize-value-after-delete">, + Group<f_clang_Group>, Flags<[CC1Option]>, + HelpText<"Set the value of a deleted pointer to 0xDEADBEEF">; def fsanitize_recover : Flag<["-"], "fsanitize-recover">, Group<f_clang_Group>, Flags<[CoreOption]>; def fno_sanitize_recover : Flag<["-"], "fno-sanitize-recover">, Index: include/clang/Basic/Sanitizers.def =================================================================== --- include/clang/Basic/Sanitizers.def +++ include/clang/Basic/Sanitizers.def @@ -72,6 +72,7 @@ SANITIZER_GROUP("shift", Shift, ShiftBase | ShiftExponent) SANITIZER("signed-integer-overflow", SignedIntegerOverflow) SANITIZER("unreachable", Unreachable) +SANITIZER("value-after-delete", ValueAfterDelete) SANITIZER("vla-bound", VLABound) SANITIZER("vptr", Vptr) @@ -100,9 +101,9 @@ SANITIZER_GROUP("undefined", Undefined, Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow | FloatDivideByZero | IntegerDivideByZero | NonnullAttribute | - Null | ObjectSize | Return | ReturnsNonnullAttribute | - Shift | SignedIntegerOverflow | Unreachable | VLABound | - Function | Vptr) + Null | ObjectSize | Return | ReturnsNonnullAttribute | Shift | + SignedIntegerOverflow | ValueAfterDelete | VLABound | + Unreachable | Function | Vptr) // -fsanitize=undefined-trap is an alias for -fsanitize=undefined. SANITIZER_GROUP("undefined-trap", UndefinedTrap, Undefined) Index: docs/UndefinedBehaviorSanitizer.rst =================================================================== --- docs/UndefinedBehaviorSanitizer.rst +++ docs/UndefinedBehaviorSanitizer.rst @@ -118,6 +118,8 @@ ``__builtin_unreachable``. - ``-fsanitize=unsigned-integer-overflow``: Unsigned integer overflows. + - ``-fsanitize=value-after-delete``: Set the value of the pointer + passed in a delete expression to 0xDEADBEEF. - ``-fsanitize=vla-bound``: A variable-length array whose bound does not evaluate to a positive value. - ``-fsanitize=vptr``: Use of an object whose vptr indicates that
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits