diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-01-07 19:55:37 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-01-07 19:55:37 +0000 |
commit | ca9211ecdede9bdedb812b2243a4abdb8dacd1b9 (patch) | |
tree | 9b19e801150082c33e9152275829a6ce90614b55 /test | |
parent | 8ef50bf3d1c287b5013c3168de77a462dfce3495 (diff) | |
download | src-ca9211ecdede9bdedb812b2243a4abdb8dacd1b9.tar.gz src-ca9211ecdede9bdedb812b2243a4abdb8dacd1b9.zip |
Import compiler-rt trunk r224034.vendor/compiler-rt/compiler-rt-r224034
Notes
Notes:
svn path=/vendor/compiler-rt/dist/; revision=276789
svn path=/vendor/compiler-rt/compiler-rt-r224034/; revision=276790; tag=vendor/compiler-rt/compiler-rt-r224034
Diffstat (limited to 'test')
945 files changed, 28902 insertions, 104 deletions
diff --git a/test/BlocksRuntime/block-static.c b/test/BlocksRuntime/block-static.c new file mode 100644 index 000000000000..d38c816cf015 --- /dev/null +++ b/test/BlocksRuntime/block-static.c @@ -0,0 +1,25 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// testfilerunner CONFIG + +#include <stdio.h> + + +int main(int argc, char **argv) { + static int numberOfSquesals = 5; + + ^{ numberOfSquesals = 6; }(); + + if (numberOfSquesals == 6) { + printf("%s: success\n", argv[0]); + return 0; + } + printf("**** did not update static local, rdar://6177162\n"); + return 1; + +} + diff --git a/test/BlocksRuntime/blockimport.c b/test/BlocksRuntime/blockimport.c new file mode 100644 index 000000000000..178fce4395e5 --- /dev/null +++ b/test/BlocksRuntime/blockimport.c @@ -0,0 +1,51 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * blockimport.c + * testObjects + * + * Created by Blaine Garst on 10/13/08. + * + */ + + +// +// pure C nothing more needed +// CONFIG rdar://6289344 + +#include <stdio.h> +#include <Block.h> +#include <Block_private.h> + + + + +int main(int argc, char *argv[]) { + int i = 1; + int (^intblock)(void) = ^{ return i*10; }; + + void (^vv)(void) = ^{ + if (argc > 0) { + printf("intblock returns %d\n", intblock()); + } + }; + +#if 0 + //printf("Block dump %s\n", _Block_dump(vv)); + { + struct Block_layout *layout = (struct Block_layout *)(void *)vv; + printf("isa %p\n", layout->isa); + printf("flags %x\n", layout->flags); + printf("descriptor %p\n", layout->descriptor); + printf("descriptor->size %d\n", layout->descriptor->size); + } +#endif + void (^vvcopy)(void) = Block_copy(vv); + Block_release(vvcopy); + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/byrefaccess.c b/test/BlocksRuntime/byrefaccess.c new file mode 100644 index 000000000000..4565553338ac --- /dev/null +++ b/test/BlocksRuntime/byrefaccess.c @@ -0,0 +1,34 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// byrefaccess.m +// test that byref access to locals is accurate +// testObjects +// +// Created by Blaine Garst on 5/13/08. +// +// CONFIG + +#include <stdio.h> + + +void callVoidVoid(void (^closure)(void)) { + closure(); +} + +int main(int argc, char *argv[]) { + __block int i = 10; + + callVoidVoid(^{ ++i; }); + + if (i != 11) { + printf("*** %s didn't update i\n", argv[0]); + return 1; + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/byrefcopy.c b/test/BlocksRuntime/byrefcopy.c new file mode 100644 index 000000000000..513b63c2725d --- /dev/null +++ b/test/BlocksRuntime/byrefcopy.c @@ -0,0 +1,41 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// byrefcopy.m +// testObjects +// +// Created by Blaine Garst on 5/13/08. +// + +#include <stdio.h> +#include <Block.h> +#include <Block_private.h> + +// CONFIG + +void callVoidVoid(void (^closure)(void)) { + closure(); +} + +int main(int argc, char *argv[]) { + int __block i = 10; + + void (^block)(void) = ^{ ++i; }; + //printf("original (old style) is %s\n", _Block_dump_old(block)); + //printf("original (new style) is %s\n", _Block_dump(block)); + void (^blockcopy)(void) = Block_copy(block); + //printf("copy is %s\n", _Block_dump(blockcopy)); + // use a copy & see that it updates i + callVoidVoid(block); + + if (i != 11) { + printf("*** %s didn't update i\n", argv[0]); + return 1; + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/byrefcopycopy.c b/test/BlocksRuntime/byrefcopycopy.c new file mode 100644 index 000000000000..d6fafc152e1d --- /dev/null +++ b/test/BlocksRuntime/byrefcopycopy.c @@ -0,0 +1,46 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG rdar://6255170 + +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <Block.h> +#include <Block_private.h> +#include <assert.h> + + +int +main(int argc, char *argv[]) +{ + __block int var = 0; + int shouldbe = 0; + void (^b)(void) = ^{ var++; /*printf("var is at %p with value %d\n", &var, var);*/ }; + __typeof(b) _b; + //printf("before copy...\n"); + b(); ++shouldbe; + size_t i; + + for (i = 0; i < 10; i++) { + _b = Block_copy(b); // make a new copy each time + assert(_b); + ++shouldbe; + _b(); // should still update the stack + Block_release(_b); + } + + //printf("after...\n"); + b(); ++shouldbe; + + if (var != shouldbe) { + printf("Hmm, var is %d but should be %d\n", var, shouldbe); + return 1; + } + printf("%s: Success!!\n", argv[0]); + + return 0; +} diff --git a/test/BlocksRuntime/byrefcopyinner.c b/test/BlocksRuntime/byrefcopyinner.c new file mode 100644 index 000000000000..07770933afe3 --- /dev/null +++ b/test/BlocksRuntime/byrefcopyinner.c @@ -0,0 +1,32 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include <Block.h> +#include <stdio.h> + +// CONFIG rdar://6225809 +// fixed in 5623 + +int main(int argc, char *argv[]) { + __block int a = 42; + int* ap = &a; // just to keep the address on the stack. + + void (^b)(void) = ^{ + //a; // workaround, a should be implicitly imported + Block_copy(^{ + a = 2; + }); + }; + + Block_copy(b); + + if(&a == ap) { + printf("**** __block heap storage should have been created at this point\n"); + return 1; + } + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/byrefcopyint.c b/test/BlocksRuntime/byrefcopyint.c new file mode 100644 index 000000000000..d632f88a0bc5 --- /dev/null +++ b/test/BlocksRuntime/byrefcopyint.c @@ -0,0 +1,69 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * byrefcopyint.c + * testObjects + * + * Created by Blaine Garst on 12/1/08. + * + */ + +// +// byrefcopyid.m +// testObjects +// +// Created by Blaine Garst on 5/13/08. +// + +// Tests copying of blocks with byref ints +// CONFIG rdar://6414583 -C99 + +#include <stdio.h> +#include <string.h> +#include <Block.h> +#include <Block_private.h> + + + + +typedef void (^voidVoid)(void); + +voidVoid dummy; + +void callVoidVoid(voidVoid closure) { + closure(); +} + + +voidVoid testRoutine(const char *whoami) { + __block int dumbo = strlen(whoami); + dummy = ^{ + //printf("incring dumbo from %d\n", dumbo); + ++dumbo; + }; + + + voidVoid copy = Block_copy(dummy); + + + return copy; +} + +int main(int argc, char *argv[]) { + voidVoid array[100]; + for (int i = 0; i < 100; ++i) { + array[i] = testRoutine(argv[0]); + array[i](); + } + for (int i = 0; i < 100; ++i) { + Block_release(array[i]); + } + + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/byrefcopystack.c b/test/BlocksRuntime/byrefcopystack.c new file mode 100644 index 000000000000..d119afa3668f --- /dev/null +++ b/test/BlocksRuntime/byrefcopystack.c @@ -0,0 +1,41 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// byrefcopystack.m +// testObjects +// +// Created by Blaine Garst on 5/13/08. +// + + + +#include <stdio.h> +#include <Block.h> + +// CONFIG rdar://6255170 + +void (^bumpi)(void); +int (^geti)(void); + +void setClosures() { + int __block i = 10; + bumpi = Block_copy(^{ ++i; }); + geti = Block_copy(^{ return i; }); +} + +int main(int argc, char *argv[]) { + setClosures(); + bumpi(); + int i = geti(); + + if (i != 11) { + printf("*** %s didn't update i\n", argv[0]); + return 1; + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/byrefsanity.c b/test/BlocksRuntime/byrefsanity.c new file mode 100644 index 000000000000..dfa16b0ddd6a --- /dev/null +++ b/test/BlocksRuntime/byrefsanity.c @@ -0,0 +1,73 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG + + +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <Block.h> + +int +main(int argc, char *argv[]) +{ + __block int var = 0; + void (^b)(void) = ^{ var++; }; + + //sanity(b); + b(); + printf("%s: success!\n", argv[0]); + return 0; +} + + +#if 1 +/* replicated internal data structures: BEWARE, MAY CHANGE!!! */ + +enum { + BLOCK_REFCOUNT_MASK = (0xffff), + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_NO_COPY = (1 << 26), // interim byref: no copies allowed + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), +}; + +struct byref_id { + struct byref_id *forwarding; + int flags;//refcount; + int size; + void (*byref_keep)(struct byref_id *dst, struct byref_id *src); + void (*byref_destroy)(struct byref_id *); + int var; +}; +struct Block_basic2 { + void *isa; + int Block_flags; // int32_t + int Block_size; // XXX should be packed into Block_flags + void (*Block_invoke)(void *); + void (*Block_copy)(void *dst, void *src); + void (*Block_dispose)(void *); + struct byref_id *ref; +}; + +void sanity(void *arg) { + struct Block_basic2 *bb = (struct Block_basic2 *)arg; + if ( ! (bb->Block_flags & BLOCK_HAS_COPY_DISPOSE)) { + printf("missing copy/dispose helpers for byref data\n"); + exit(1); + } + struct byref_id *ref = bb->ref; + if (ref->forwarding != ref) { + printf("forwarding pointer should be %p but is %p\n", ref, ref->forwarding); + exit(1); + } +} +#endif + + + diff --git a/test/BlocksRuntime/byrefstruct.c b/test/BlocksRuntime/byrefstruct.c new file mode 100644 index 000000000000..a3dc44e2c36e --- /dev/null +++ b/test/BlocksRuntime/byrefstruct.c @@ -0,0 +1,57 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG + +#import <stdio.h> +#import <stdlib.h> +#import <string.h> + +typedef struct { + unsigned long ps[30]; + int qs[30]; +} BobTheStruct; + +int main (int argc, const char * argv[]) { + __block BobTheStruct fiddly; + BobTheStruct copy; + + void (^incrementFiddly)() = ^{ + int i; + for(i=0; i<30; i++) { + fiddly.ps[i]++; + fiddly.qs[i]++; + } + }; + + memset(&fiddly, 0xA5, sizeof(fiddly)); + memset(©, 0x2A, sizeof(copy)); + + int i; + for(i=0; i<30; i++) { + fiddly.ps[i] = i * i * i; + fiddly.qs[i] = -i * i * i; + } + + copy = fiddly; + incrementFiddly(); + + if ( © == &fiddly ) { + printf("%s: struct wasn't copied.", argv[0]); + exit(1); + } + for(i=0; i<30; i++) { + //printf("[%d]: fiddly.ps: %lu, copy.ps: %lu, fiddly.qs: %d, copy.qs: %d\n", i, fiddly.ps[i], copy.ps[i], fiddly.qs[i], copy.qs[i]); + if ( (fiddly.ps[i] != copy.ps[i] + 1) || (fiddly.qs[i] != copy.qs[i] + 1) ) { + printf("%s: struct contents were not incremented.", argv[0]); + exit(1); + } + } + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/c99.c b/test/BlocksRuntime/c99.c new file mode 100644 index 000000000000..8f31ab3fdfb8 --- /dev/null +++ b/test/BlocksRuntime/c99.c @@ -0,0 +1,20 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// c99.m +// +// CONFIG C99 rdar://problem/6399225 + +#import <stdio.h> +#import <stdlib.h> + +int main(int argc, char *argv[]) { + void (^blockA)(void) = ^ { ; }; + blockA(); + printf("%s: success\n", argv[0]); + exit(0); +} diff --git a/test/BlocksRuntime/cast.c b/test/BlocksRuntime/cast.c new file mode 100644 index 000000000000..5bef2c19def5 --- /dev/null +++ b/test/BlocksRuntime/cast.c @@ -0,0 +1,37 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * cast.c + * testObjects + * + * Created by Blaine Garst on 2/17/09. + * + */ + +// PURPOSE should allow casting of a Block reference to an arbitrary pointer and back +// CONFIG open + +#include <stdio.h> + + + +int main(int argc, char *argv[]) { + + void (^aBlock)(void); + int *ip; + char *cp; + double *dp; + + ip = (int *)aBlock; + cp = (char *)aBlock; + dp = (double *)aBlock; + aBlock = (void (^)(void))ip; + aBlock = (void (^)(void))cp; + aBlock = (void (^)(void))dp; + printf("%s: success", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/constassign.c b/test/BlocksRuntime/constassign.c new file mode 100644 index 000000000000..537cb2df0597 --- /dev/null +++ b/test/BlocksRuntime/constassign.c @@ -0,0 +1,28 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// constassign.c +// bocktest +// +// Created by Blaine Garst on 3/21/08. +// +// shouldn't be able to assign to a const pointer +// CONFIG error: assignment of read-only + +#import <stdio.h> + +void foo(void) { printf("I'm in foo\n"); } +void bar(void) { printf("I'm in bar\n"); } + +int main(int argc, char *argv[]) { + void (*const fptr)(void) = foo; + void (^const blockA)(void) = ^ { printf("hello\n"); }; + blockA = ^ { printf("world\n"); } ; + fptr = bar; + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/copy-block-literal-rdar6439600.c b/test/BlocksRuntime/copy-block-literal-rdar6439600.c new file mode 100644 index 000000000000..6fa488eee4ff --- /dev/null +++ b/test/BlocksRuntime/copy-block-literal-rdar6439600.c @@ -0,0 +1,29 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG open rdar://6439600 + +#import <stdio.h> +#import <stdlib.h> + +#define NUMBER_OF_BLOCKS 100 +int main (int argc, const char * argv[]) { + int (^x[NUMBER_OF_BLOCKS])(); + int i; + + for(i=0; i<NUMBER_OF_BLOCKS; i++) x[i] = ^{ return i; }; + + for(i=0; i<NUMBER_OF_BLOCKS; i++) { + if (x[i]() != i) { + printf("%s: failure, %d != %d\n", argv[0], x[i](), i); + exit(1); + } + } + + printf("%s: success\n", argv[0]); + + return 0; +} diff --git a/test/BlocksRuntime/copyconstructor.C b/test/BlocksRuntime/copyconstructor.C new file mode 100644 index 000000000000..626d33e80e80 --- /dev/null +++ b/test/BlocksRuntime/copyconstructor.C @@ -0,0 +1,85 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include <stdio.h> +#include <Block.h> + +// CONFIG C++ rdar://6243400,rdar://6289367 + + +int constructors = 0; +int destructors = 0; + + +#define CONST const + +class TestObject +{ +public: + TestObject(CONST TestObject& inObj); + TestObject(); + ~TestObject(); + + TestObject& operator=(CONST TestObject& inObj); + + int version() CONST { return _version; } +private: + mutable int _version; +}; + +TestObject::TestObject(CONST TestObject& inObj) + +{ + ++constructors; + _version = inObj._version; + //printf("%p (%d) -- TestObject(const TestObject&) called\n", this, _version); +} + + +TestObject::TestObject() +{ + _version = ++constructors; + //printf("%p (%d) -- TestObject() called\n", this, _version); +} + + +TestObject::~TestObject() +{ + //printf("%p -- ~TestObject() called\n", this); + ++destructors; +} + + +TestObject& TestObject::operator=(CONST TestObject& inObj) +{ + //printf("%p -- operator= called\n", this); + _version = inObj._version; + return *this; +} + + + +void testRoutine() { + TestObject one; + + void (^b)(void) = ^{ printf("my const copy of one is %d\n", one.version()); }; +} + + + +int main(int argc, char *argv[]) { + testRoutine(); + if (constructors == 0) { + printf("No copy constructors!!!\n"); + return 1; + } + if (constructors != destructors) { + printf("%d constructors but only %d destructors\n", constructors, destructors); + return 1; + } + printf("%s:success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/copynull.c b/test/BlocksRuntime/copynull.c new file mode 100644 index 000000000000..c49e499f3ab6 --- /dev/null +++ b/test/BlocksRuntime/copynull.c @@ -0,0 +1,37 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * copynull.c + * testObjects + * + * Created by Blaine Garst on 10/15/08. + * + */ + +#import <stdio.h> +#import <Block.h> +#import <Block_private.h> + +// CONFIG rdar://6295848 + +int main(int argc, char *argv[]) { + + void (^block)(void) = (void (^)(void))0; + void (^blockcopy)(void) = Block_copy(block); + + if (blockcopy != (void (^)(void))0) { + printf("whoops, somehow we copied NULL!\n"); + return 1; + } + // make sure we can also + Block_release(blockcopy); + // and more secretly + //_Block_destroy(blockcopy); + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/dispatch_async.c b/test/BlocksRuntime/dispatch_async.c new file mode 100644 index 000000000000..e3e517c54650 --- /dev/null +++ b/test/BlocksRuntime/dispatch_async.c @@ -0,0 +1,57 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include <CoreFoundation/CoreFoundation.h> + +#include <dispatch/dispatch.h> +#include <unistd.h> +//#import <Foundation/Foundation.h> +#include <Block.h> + +// CONFIG rdar://problem/6371811 + +const char *whoami = "nobody"; + +void EnqueueStuff(dispatch_queue_t q) +{ + __block CFIndex counter; + + // above call has a side effect: it works around: + // <rdar://problem/6225809> __block variables not implicitly imported into intermediate scopes + dispatch_async(q, ^{ + counter = 0; + }); + + + dispatch_async(q, ^{ + //printf("outer block.\n"); + counter++; + dispatch_async(q, ^{ + //printf("inner block.\n"); + counter--; + if(counter == 0) { + printf("%s: success\n", whoami); + exit(0); + } + }); + if(counter == 0) { + printf("already done? inconceivable!\n"); + exit(1); + } + }); +} + +int main (int argc, const char * argv[]) { + dispatch_queue_t q = dispatch_queue_create("queue", NULL); + + whoami = argv[0]; + + EnqueueStuff(q); + + dispatch_main(); + printf("shouldn't get here\n"); + return 1; +} diff --git a/test/BlocksRuntime/dispatch_call_Block_with_release.c b/test/BlocksRuntime/dispatch_call_Block_with_release.c new file mode 100644 index 000000000000..9e06f69ba762 --- /dev/null +++ b/test/BlocksRuntime/dispatch_call_Block_with_release.c @@ -0,0 +1,31 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include <stdio.h> +#include <Block.h> + +// CONFIG + +void callsomething(const char *format, int argument) { +} + +void +dispatch_call_Block_with_release2(void *block) +{ + void (^b)(void) = (void (^)(void))block; + b(); + Block_release(b); +} + +int main(int argc, char *argv[]) { + void (^b1)(void) = ^{ callsomething("argc is %d\n", argc); }; + void (^b2)(void) = ^{ callsomething("hellow world\n", 0); }; // global block now + + dispatch_call_Block_with_release2(Block_copy(b1)); + dispatch_call_Block_with_release2(Block_copy(b2)); + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/fail.c b/test/BlocksRuntime/fail.c new file mode 100644 index 000000000000..28dbc2d1521f --- /dev/null +++ b/test/BlocksRuntime/fail.c @@ -0,0 +1,107 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * fail.c + * testObjects + * + * Created by Blaine Garst on 9/16/08. + * + */ +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> + + +bool readfile(char *buffer, const char *from) { + int fd = open(from, 0); + if (fd < 0) return false; + int count = read(fd, buffer, 512); + if (count < 0) return false; + buffer[count] = 0; // zap newline + return true; +} + +// basic idea, take compiler args, run compiler, and verify that expected failure matches any existing one + +int main(int argc, char *argv[]) { + if (argc == 1) return 0; + char *copy[argc+1]; // make a copy + // find and strip off -e "errorfile" + char *errorfile = NULL; + int counter = 0, i = 0; + for (i = 1; i < argc; ++i) { // skip 0 arg which is "fail" + if (!strncmp(argv[i], "-e", 2)) { + errorfile = argv[++i]; + } + else { + copy[counter++] = argv[i]; + } + } + copy[counter] = NULL; + pid_t child = fork(); + char buffer[512]; + if (child == 0) { + // in child + sprintf(buffer, "/tmp/errorfile_%d", getpid()); + close(1); + int fd = creat(buffer, 0777); + if (fd != 1) { + fprintf(stderr, "didn't open custom error file %s as 1, got %d\n", buffer, fd); + exit(1); + } + close(2); + dup(1); + int result = execv(copy[0], copy); + exit(10); + } + if (child < 0) { + printf("fork failed\n"); + exit(1); + } + int status = 0; + pid_t deadchild = wait(&status); + if (deadchild != child) { + printf("wait got %d instead of %d\n", deadchild, child); + exit(1); + } + if (WEXITSTATUS(status) == 0) { + printf("compiler exited normally, not good under these circumstances\n"); + exit(1); + } + //printf("exit status of child %d was %d\n", child, WEXITSTATUS(status)); + sprintf(buffer, "/tmp/errorfile_%d", child); + if (errorfile) { + //printf("ignoring error file: %s\n", errorfile); + char desired[512]; + char got[512]; + bool gotErrorFile = readfile(desired, errorfile); + bool gotOutput = readfile(got, buffer); + if (!gotErrorFile && gotOutput) { + printf("didn't read errorfile %s, it should have something from\n*****\n%s\n*****\nin it.\n", + errorfile, got); + exit(1); + } + else if (gotErrorFile && gotOutput) { + char *where = strstr(got, desired); + if (!where) { + printf("didn't find contents of %s in %s\n", errorfile, buffer); + exit(1); + } + } + else { + printf("errorfile %s and output %s inconsistent\n", errorfile, buffer); + exit(1); + } + } + unlink(buffer); + printf("success\n"); + exit(0); +} + diff --git a/test/BlocksRuntime/flagsisa.c b/test/BlocksRuntime/flagsisa.c new file mode 100644 index 000000000000..5d4b2dcb4030 --- /dev/null +++ b/test/BlocksRuntime/flagsisa.c @@ -0,0 +1,21 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include <stdio.h> + +/* CONFIG rdar://6310599 + */ + +int main(int argc, char *argv[]) +{ + __block int flags; + __block void *isa; + + ^{ flags=1; isa = (void *)isa; }; + printf("%s: success\n", argv[0]); + return 0; +} + diff --git a/test/BlocksRuntime/globalexpression.c b/test/BlocksRuntime/globalexpression.c new file mode 100644 index 000000000000..eeedd75e7078 --- /dev/null +++ b/test/BlocksRuntime/globalexpression.c @@ -0,0 +1,42 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// testfilerunner CONFIG + +#import <stdio.h> +#import <Block.h> + +int global; + +void (^gblock)(int) = ^(int x){ global = x; }; + +int main(int argc, char *argv[]) { + gblock(1); + if (global != 1) { + printf("%s: *** did not set global to 1\n", argv[0]); + return 1; + } + void (^gblockcopy)(int) = Block_copy(gblock); + if (gblockcopy != gblock) { + printf("global copy %p not a no-op %p\n", (void *)gblockcopy, (void *)gblock); + return 1; + } + Block_release(gblockcopy); + gblock(3); + if (global != 3) { + printf("%s: *** did not set global to 3\n", argv[0]); + return 1; + } + gblockcopy = Block_copy(gblock); + gblockcopy(5); + if (global != 5) { + printf("%s: *** did not set global to 5\n", argv[0]); + return 1; + } + printf("%s: Success!\n", argv[0]); + return 0; +} + diff --git a/test/BlocksRuntime/goto.c b/test/BlocksRuntime/goto.c new file mode 100644 index 000000000000..7e5b08adbe8e --- /dev/null +++ b/test/BlocksRuntime/goto.c @@ -0,0 +1,34 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * goto.c + * testObjects + * + * Created by Blaine Garst on 10/17/08. + * + */ + +// CONFIG rdar://6289031 + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + __block int val = 0; + + ^{ val = 1; }(); + + if (val == 0) { + goto out_bad; // error: local byref variable val is in the scope of this goto + } + + printf("%s: Success!\n", argv[0]); + return 0; +out_bad: + printf("%s: val not updated!\n", argv[0]); + return 1; +} diff --git a/test/BlocksRuntime/hasdescriptor.c b/test/BlocksRuntime/hasdescriptor.c new file mode 100644 index 000000000000..429adb9bdb14 --- /dev/null +++ b/test/BlocksRuntime/hasdescriptor.c @@ -0,0 +1,29 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + + + +// CONFIG C + +#include <stdio.h> +#include <Block_private.h> + + +int main(int argc, char *argv[]) { + void (^inner)(void) = ^ { printf("argc was %d\n", argc); }; + void (^outer)(void) = ^{ + inner(); + inner(); + }; + //printf("size of inner is %ld\n", Block_size(inner)); + //printf("size of outer is %ld\n", Block_size(outer)); + if (Block_size(inner) != Block_size(outer)) { + printf("not the same size, using old compiler??\n"); + return 1; + } + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/josh.C b/test/BlocksRuntime/josh.C new file mode 100644 index 000000000000..dbc7369e8c39 --- /dev/null +++ b/test/BlocksRuntime/josh.C @@ -0,0 +1,32 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG C++ GC RR open rdar://6347910 + + + +struct MyStruct { + int something; +}; + +struct TestObject { + + void test(void){ + { + MyStruct first; // works + } + void (^b)(void) = ^{ + MyStruct inner; // fails to compile! + }; + } +}; + + + +int main(int argc, char *argv[]) { + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/k-and-r.c b/test/BlocksRuntime/k-and-r.c new file mode 100644 index 000000000000..16b9cc643b50 --- /dev/null +++ b/test/BlocksRuntime/k-and-r.c @@ -0,0 +1,33 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG error: incompatible block pointer types assigning + +#import <stdio.h> +#import <stdlib.h> + +int main(int argc, char *argv[]) { +#ifndef __cplusplus + char (^rot13)(); + rot13 = ^(char c) { return (char)(((c - 'a' + 13) % 26) + 'a'); }; + char n = rot13('a'); + char c = rot13('p'); + if ( n != 'n' || c != 'c' ) { + printf("%s: rot13('a') returned %c, rot13('p') returns %c\n", argv[0], n, c); + exit(1); + } +#else +// yield characteristic error message for C++ +#error incompatible block pointer types assigning +#endif +#ifndef __clang__ +// yield characteristic error message for C++ +#error incompatible block pointer types assigning +#endif + printf("%s: success\n", argv[0]); + exit(0); +} diff --git a/test/BlocksRuntime/large-struct.c b/test/BlocksRuntime/large-struct.c new file mode 100644 index 000000000000..1867bd02dfab --- /dev/null +++ b/test/BlocksRuntime/large-struct.c @@ -0,0 +1,51 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG + +#import <stdio.h> +#import <stdlib.h> +#import <string.h> + +typedef struct { + unsigned long ps[30]; + int qs[30]; +} BobTheStruct; + +int main (int argc, const char * argv[]) { + BobTheStruct inny; + BobTheStruct outty; + BobTheStruct (^copyStruct)(BobTheStruct); + int i; + + memset(&inny, 0xA5, sizeof(inny)); + memset(&outty, 0x2A, sizeof(outty)); + + for(i=0; i<30; i++) { + inny.ps[i] = i * i * i; + inny.qs[i] = -i * i * i; + } + + copyStruct = ^(BobTheStruct aBigStruct){ return aBigStruct; }; // pass-by-value intrinsically copies the argument + + outty = copyStruct(inny); + + if ( &inny == &outty ) { + printf("%s: struct wasn't copied.", argv[0]); + exit(1); + } + for(i=0; i<30; i++) { + if ( (inny.ps[i] != outty.ps[i]) || (inny.qs[i] != outty.qs[i]) ) { + printf("%s: struct contents did not match.", argv[0]); + exit(1); + } + } + + printf("%s: success\n", argv[0]); + + return 0; +} diff --git a/test/BlocksRuntime/localisglobal.c b/test/BlocksRuntime/localisglobal.c new file mode 100644 index 000000000000..75a79dff48ee --- /dev/null +++ b/test/BlocksRuntime/localisglobal.c @@ -0,0 +1,42 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * localisglobal.c + * testObjects + * + * Created by Blaine Garst on 9/29/08. + * + * works in all configurations + * CONFIG rdar://6230297 + */ + +#include <stdio.h> + +void (^global)(void) = ^{ printf("hello world\n"); }; + +int aresame(void *first, void *second) { + long *f = (long *)first; + long *s = (long *)second; + return *f == *s; +} +int main(int argc, char *argv[]) { + int i = 10; + void (^local)(void) = ^ { printf("hi %d\n", i); }; + void (^localisglobal)(void) = ^ { printf("hi\n"); }; + + if (aresame(local, localisglobal)) { + printf("local block could be global, but isn't\n"); + return 1; + } + if (!aresame(global, localisglobal)) { + printf("local block is not global, not stack, what is it??\n"); + return 1; + } + printf("%s: success\n", argv[0]); + return 0; + +} diff --git a/test/BlocksRuntime/macro.c b/test/BlocksRuntime/macro.c new file mode 100644 index 000000000000..988c0689b8f8 --- /dev/null +++ b/test/BlocksRuntime/macro.c @@ -0,0 +1,14 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG open rdar://6718399 +#include <Block.h> + +void foo() { + void (^bbb)(void) = Block_copy(^ { + int j, cnt; + }); +} diff --git a/test/BlocksRuntime/makefile b/test/BlocksRuntime/makefile new file mode 100644 index 000000000000..2734bcae35f5 --- /dev/null +++ b/test/BlocksRuntime/makefile @@ -0,0 +1,70 @@ +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +CCDIR=/usr/bin +#CCDIR=/Volumes/Keep/gcc/usr/bin + +all: std + +clean: + rm -fr *.dSYM *.o *-bin testfilerunner + +TFR = ~public/bin/testfilerunner + +testfilerunner: testfilerunner.h testfilerunner.m + gcc -fobjc-gc-only -g -arch x86_64 -arch i386 -std=gnu99 testfilerunner.m -o testfilerunner -framework Foundation + +tests: + grep CONFIG *.[cmCM] | $(TFR) $(CCDIR) -- + +open: + grep CONFIG *.[cmCM] | $(TFR) $(CCDIR) -open -- + +fast: + grep CONFIG *.[cmCM] | $(TFR) -fast $(CCDIR) -- + +std: + grep CONFIG *.[cmCM] | $(TFR) -- + +clang: + grep CONFIG *.[cmCM] | $(TFR) -clang -fast -- + +fastd: + grep CONFIG *.[cmCM] | $(TFR) -fast -- + + +# Hack Alert: arguably most of the following belongs in libclosure's Makefile; sticking it here until I get around to grokking what goes on in that file. +sudid: + @echo Enabling sudo: # Hack Alert: enable sudo first thing so we don't hang at the password prompt later + @sudo echo Thanks + + +RootsDirectory ?= /tmp/ +# Note: the libsystem project (built by the libsystemroot target below) uses the ALTUSRLOCALLIBSYSTEM variable, so we use it here to maintain parity +ALTUSRLOCALLIBSYSTEM ?= $(RootsDirectory)/alt-usr-local-lib-system/ +altusrlocallibsystem: + ditto /usr/local/lib/system $(ALTUSRLOCALLIBSYSTEM) # FIXME: conditionalize this copy + + +# <rdar://problem/6456031> ER: option to not require extra privileges (-nosudo or somesuch) +Buildit ?= ~rc/bin/buildit -rootsDirectory $(RootsDirectory) -arch i386 -arch ppc -arch x86_64 +blocksroot: sudid clean altusrlocallibsystem + sudo $(Buildit) .. + ditto $(RootsDirectory)/libclosure.roots/libclosure~dst/usr/local/lib/system $(ALTUSRLOCALLIBSYSTEM) + + +LibsystemVersion ?= 121 +LibsystemPath ?= ~rc/Software/SnowLeopard/Projects/Libsystem/Libsystem-$(LibsystemVersion) +LibsystemTmpPath ?= $(RootsDirectory)/Libsystem-$(LibsystemVersion) +libsystemroot: blocksroot + ditto $(LibsystemPath) $(LibsystemTmpPath) # FIXME: conditionalize this copy + sudo ALTUSRLOCALLIBSYSTEM=$(ALTUSRLOCALLIBSYSTEM) $(Buildit) $(LibsystemTmpPath) + + +# Defaults to product of the libsystemroot target but does not automatically rebuild that, make both targets if you want a fresh root +LibsystemRootPath ?= $(RootsDirectory)/Libsystem-$(LibsystemVersion).roots/Libsystem-$(LibsystemVersion)~dst/usr/lib/ +roottests: + grep CONFIG *.[cmCM] | $(TFR) -dyld $(LibsystemRootPath) -- # FIXME: figure out if I can "call" the std target instead of duplicating it + diff --git a/test/BlocksRuntime/modglobal.c b/test/BlocksRuntime/modglobal.c new file mode 100644 index 000000000000..562d5a5cc418 --- /dev/null +++ b/test/BlocksRuntime/modglobal.c @@ -0,0 +1,18 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +#include <stdio.h> + +// CONFIG + +int AGlobal; + +int main(int argc, char *argv[]) { + void (^f)(void) = ^ { AGlobal++; }; + + printf("%s: success\n", argv[0]); + return 0; + +} diff --git a/test/BlocksRuntime/nestedimport.c b/test/BlocksRuntime/nestedimport.c new file mode 100644 index 000000000000..e8066922fbba --- /dev/null +++ b/test/BlocksRuntime/nestedimport.c @@ -0,0 +1,44 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// nestedimport.m +// testObjects +// +// Created by Blaine Garst on 6/24/08. +// +// pure C nothing more needed +// CONFIG + + +#include <stdio.h> +#include <stdlib.h> + + +int Global = 0; + +void callVoidVoid(void (^closure)(void)) { + closure(); +} + +int main(int argc, char *argv[]) { + int i = 1; + + void (^vv)(void) = ^{ + if (argc > 0) { + callVoidVoid(^{ Global = i; }); + } + }; + + i = 2; + vv(); + if (Global != 1) { + printf("%s: error, Global not set to captured value\n", argv[0]); + exit(1); + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/nullblockisa.c b/test/BlocksRuntime/nullblockisa.c new file mode 100644 index 000000000000..ba0282e82084 --- /dev/null +++ b/test/BlocksRuntime/nullblockisa.c @@ -0,0 +1,43 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// nullblockisa.m +// testObjects +// +// Created by Blaine Garst on 9/24/08. +// +// CONFIG rdar://6244520 + + + +#include <stdio.h> +#include <stdlib.h> +#include <Block_private.h> + + +void check(void (^b)(void)) { + struct _custom { + struct Block_layout layout; + struct Block_byref *innerp; + } *custom = (struct _custom *)(void *)(b); + //printf("block is at %p, size is %lx, inner is %p\n", (void *)b, Block_size(b), innerp); + if (custom->innerp->isa != (void *)NULL) { + printf("not a NULL __block isa\n"); + exit(1); + } + return; +} + +int main(int argc, char *argv[]) { + + __block int i; + + check(^{ printf("%d\n", ++i); }); + printf("%s: success\n", argv[0]); + return 0; +} + diff --git a/test/BlocksRuntime/objectRRGC.c b/test/BlocksRuntime/objectRRGC.c new file mode 100644 index 000000000000..2cefea2afd3a --- /dev/null +++ b/test/BlocksRuntime/objectRRGC.c @@ -0,0 +1,77 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * objectRRGC.c + * testObjects + * + * Created by Blaine Garst on 10/31/08. + * + * Test that the runtime honors the new callouts properly for retain/release and GC + * CON FIG C rdar://6175959 + */ + + + +#include <stdio.h> +#include <Block_private.h> + + +int AssignCalled = 0; +int DisposeCalled = 0; + +// local copy instead of libSystem.B.dylib copy +void _Block_object_assign(void *destAddr, const void *object, const int isWeak) { + //printf("_Block_object_assign(%p, %p, %d) called\n", destAddr, object, isWeak); + AssignCalled = 1; +} + +void _Block_object_dispose(const void *object, const int isWeak) { + //printf("_Block_object_dispose(%p, %d) called\n", object, isWeak); + DisposeCalled = 1; +} + +struct MyStruct { + long isa; + long field; +}; + +typedef struct MyStruct *__attribute__((NSObject)) MyStruct_t; + +int main(int argc, char *argv[]) { + // create a block + struct MyStruct X; + MyStruct_t xp = (MyStruct_t)&X; + xp->field = 10; + void (^myBlock)(void) = ^{ printf("field is %ld\n", xp->field); }; + // should be a copy helper generated with a calls to above routines + // Lets find out! + struct Block_layout *bl = (struct Block_layout *)(void *)myBlock; + if ((bl->flags & BLOCK_HAS_DESCRIPTOR) != BLOCK_HAS_DESCRIPTOR) { + printf("using old runtime layout!\n"); + return 1; + } + if ((bl->flags & BLOCK_HAS_COPY_DISPOSE) != BLOCK_HAS_COPY_DISPOSE) { + printf("no copy dispose!!!!\n"); + return 1; + } + // call helper routines directly. These will, in turn, we hope, call the stubs above + long destBuffer[256]; + //printf("destbuffer is at %p, block at %p\n", destBuffer, (void *)bl); + //printf("dump is %s\n", _Block_dump(myBlock)); + bl->descriptor->copy(destBuffer, bl); + bl->descriptor->dispose(bl); + if (AssignCalled == 0) { + printf("did not call assign helper!\n"); + return 1; + } + if (DisposeCalled == 0) { + printf("did not call dispose helper\n"); + return 1; + } + printf("%s: Success!\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/objectassign.c b/test/BlocksRuntime/objectassign.c new file mode 100644 index 000000000000..1c4f4841419e --- /dev/null +++ b/test/BlocksRuntime/objectassign.c @@ -0,0 +1,76 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * objectassign.c + * testObjects + * + * Created by Blaine Garst on 10/28/08. + * + * This just tests that the compiler is issuing the proper helper routines + * CONFIG C rdar://6175959 + */ + + +#include <stdio.h> +#include <Block_private.h> + + +int AssignCalled = 0; +int DisposeCalled = 0; + +// local copy instead of libSystem.B.dylib copy +void _Block_object_assign(void *destAddr, const void *object, const int isWeak) { + //printf("_Block_object_assign(%p, %p, %d) called\n", destAddr, object, isWeak); + AssignCalled = 1; +} + +void _Block_object_dispose(const void *object, const int isWeak) { + //printf("_Block_object_dispose(%p, %d) called\n", object, isWeak); + DisposeCalled = 1; +} + +struct MyStruct { + long isa; + long field; +}; + +typedef struct MyStruct *__attribute__((NSObject)) MyStruct_t; + +int main(int argc, char *argv[]) { + if (__APPLE_CC__ < 5627) { + printf("need compiler version %d, have %d\n", 5627, __APPLE_CC__); + return 0; + } + // create a block + struct MyStruct X; + MyStruct_t xp = (MyStruct_t)&X; + xp->field = 10; + void (^myBlock)(void) = ^{ printf("field is %ld\n", xp->field); }; + // should be a copy helper generated with a calls to above routines + // Lets find out! + struct Block_layout *bl = (struct Block_layout *)(void *)myBlock; + if ((bl->flags & BLOCK_HAS_COPY_DISPOSE) != BLOCK_HAS_COPY_DISPOSE) { + printf("no copy dispose!!!!\n"); + return 1; + } + // call helper routines directly. These will, in turn, we hope, call the stubs above + long destBuffer[256]; + //printf("destbuffer is at %p, block at %p\n", destBuffer, (void *)bl); + //printf("dump is %s\n", _Block_dump(myBlock)); + bl->descriptor->copy(destBuffer, bl); + bl->descriptor->dispose(bl); + if (AssignCalled == 0) { + printf("did not call assign helper!\n"); + return 1; + } + if (DisposeCalled == 0) { + printf("did not call dispose helper\n"); + return 1; + } + printf("%s: Success!\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/orbars.c b/test/BlocksRuntime/orbars.c new file mode 100644 index 000000000000..18a9244452f1 --- /dev/null +++ b/test/BlocksRuntime/orbars.c @@ -0,0 +1,23 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * orbars.c + * testObjects + * + * Created by Blaine Garst on 9/17/08. + * + * CONFIG rdar://6276695 error: before ‘|’ token + */ + +#include <stdio.h> + +int main(int argc, char *argv[]) { + int i = 10; + void (^b)(void) = ^(void){ | i | printf("hello world, %d\n", ++i); }; + printf("%s: success :-(\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/rdar6396238.c b/test/BlocksRuntime/rdar6396238.c new file mode 100644 index 000000000000..280415643a1a --- /dev/null +++ b/test/BlocksRuntime/rdar6396238.c @@ -0,0 +1,32 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG rdar://6396238 + +#include <stdio.h> +#include <stdlib.h> + +static int count = 0; + +void (^mkblock(void))(void) +{ + count++; + return ^{ + count++; + }; +} + +int main (int argc, const char * argv[]) { + mkblock()(); + if (count != 2) { + printf("%s: failure, 2 != %d\n", argv[0], count); + exit(1); + } else { + printf("%s: success\n", argv[0]); + exit(0); + } + return 0; +} diff --git a/test/BlocksRuntime/rdar6405500.c b/test/BlocksRuntime/rdar6405500.c new file mode 100644 index 000000000000..1ab4624bcfce --- /dev/null +++ b/test/BlocksRuntime/rdar6405500.c @@ -0,0 +1,29 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG rdar://6405500 + +#include <stdio.h> +#include <stdlib.h> +#import <dispatch/dispatch.h> +#import <objc/objc-auto.h> + +int main (int argc, const char * argv[]) { + __block void (^blockFu)(size_t t); + blockFu = ^(size_t t){ + if (t == 20) { + printf("%s: success\n", argv[0]); + exit(0); + } else + dispatch_async(dispatch_get_main_queue(), ^{ blockFu(20); }); + }; + + dispatch_apply(10, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), blockFu); + + dispatch_main(); + printf("shouldn't get here\n"); + return 1; +} diff --git a/test/BlocksRuntime/rdar6414583.c b/test/BlocksRuntime/rdar6414583.c new file mode 100644 index 000000000000..2ada04d3ddcc --- /dev/null +++ b/test/BlocksRuntime/rdar6414583.c @@ -0,0 +1,31 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG rdar://6414583 + +// a smaller case of byrefcopyint + +#include <Block.h> +#include <dispatch/dispatch.h> +#include <stdio.h> + +int main(int argc, char *argv[]) { + __block int c = 1; + + //printf("&c = %p - c = %i\n", &c, c); + + int i; + for(i =0; i < 2; i++) { + dispatch_block_t block = Block_copy(^{ c = i; }); + + block(); +// printf("%i: &c = %p - c = %i\n", i, &c, c); + + Block_release(block); + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/recursive-block.c b/test/BlocksRuntime/recursive-block.c new file mode 100644 index 000000000000..454ad48267df --- /dev/null +++ b/test/BlocksRuntime/recursive-block.c @@ -0,0 +1,55 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include <stdio.h> +#include <Block.h> +#include <Block_private.h> +#include <stdlib.h> + +// CONFIG + + +int cumulation = 0; + +int doSomething(int i) { + cumulation += i; + return cumulation; +} + +void dirtyStack() { + int i = random(); + int j = doSomething(i); + int k = doSomething(j); + doSomething(i + j + k); +} + +typedef void (^voidVoid)(void); + +voidVoid testFunction() { + int i = random(); + __block voidVoid inner = ^{ doSomething(i); }; + //printf("inner, on stack, is %p\n", (void*)inner); + /*__block*/ voidVoid outer = ^{ + //printf("will call inner block %p\n", (void *)inner); + inner(); + }; + //printf("outer looks like: %s\n", _Block_dump(outer)); + voidVoid result = Block_copy(outer); + //Block_release(inner); + return result; +} + + +int main(int argc, char **argv) { + voidVoid block = testFunction(); + dirtyStack(); + block(); + Block_release(block); + + printf("%s: success\n", argv[0]); + + return 0; +} diff --git a/test/BlocksRuntime/recursive-test.c b/test/BlocksRuntime/recursive-test.c new file mode 100644 index 000000000000..f79914863121 --- /dev/null +++ b/test/BlocksRuntime/recursive-test.c @@ -0,0 +1,74 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG open rdar://6416474 +// was rdar://5847976 +// was rdar://6348320 + +#include <stdio.h> +#include <Block.h> + +int verbose = 0; + +int main(int argc, char* argv[]) { + + if (argc > 1) verbose = 1; + + __block void (^recursive_local_block)(int); + + if (verbose) printf("recursive_local_block is a local recursive block\n"); + recursive_local_block = ^(int i) { + if (verbose) printf("%d\n", i); + if (i > 0) { + recursive_local_block(i - 1); + } + }; + + if (verbose) printf("recursive_local_block's address is %p, running it:\n", (void*)recursive_local_block); + recursive_local_block(5); + + if (verbose) printf("Creating other_local_block: a local block that calls recursive_local_block\n"); + + void (^other_local_block)(int) = ^(int i) { + if (verbose) printf("other_local_block running\n"); + recursive_local_block(i); + }; + + if (verbose) printf("other_local_block's address is %p, running it:\n", (void*)other_local_block); + + other_local_block(5); + +#if __APPLE_CC__ >= 5627 + if (verbose) printf("Creating other_copied_block: a Block_copy of a block that will call recursive_local_block\n"); + + void (^other_copied_block)(int) = Block_copy(^(int i) { + if (verbose) printf("other_copied_block running\n"); + recursive_local_block(i); + }); + + if (verbose) printf("other_copied_block's address is %p, running it:\n", (void*)other_copied_block); + + other_copied_block(5); +#endif + + __block void (^recursive_copy_block)(int); + + if (verbose) printf("Creating recursive_copy_block: a Block_copy of a block that will call recursive_copy_block recursively\n"); + + recursive_copy_block = Block_copy(^(int i) { + if (verbose) printf("%d\n", i); + if (i > 0) { + recursive_copy_block(i - 1); + } + }); + + if (verbose) printf("recursive_copy_block's address is %p, running it:\n", (void*)recursive_copy_block); + + recursive_copy_block(5); + + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/recursiveassign.c b/test/BlocksRuntime/recursiveassign.c new file mode 100644 index 000000000000..f0070cbe5c93 --- /dev/null +++ b/test/BlocksRuntime/recursiveassign.c @@ -0,0 +1,44 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * recursiveassign.c + * testObjects + * + * Created by Blaine Garst on 12/3/08. + * + */ + +// CONFIG rdar://6639533 + +// The compiler is prefetching x->forwarding before evaluting code that recomputes forwarding and so the value goes to a place that is never seen again. + +#include <stdio.h> +#include <stdlib.h> +#include <Block.h> + + +int main(int argc, char* argv[]) { + + __block void (^recursive_copy_block)(int) = ^(int arg) { printf("got wrong Block\n"); exit(1); }; + + + recursive_copy_block = Block_copy(^(int i) { + if (i > 0) { + recursive_copy_block(i - 1); + } + else { + printf("done!\n"); + } + }); + + + recursive_copy_block(5); + + printf("%s: Success\n", argv[0]); + return 0; +} + diff --git a/test/BlocksRuntime/reference.C b/test/BlocksRuntime/reference.C new file mode 100644 index 000000000000..f86f11e86ce1 --- /dev/null +++ b/test/BlocksRuntime/reference.C @@ -0,0 +1,95 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#import <Block.h> +#import <stdio.h> +#import <stdlib.h> + +// CONFIG C++ + +int recovered = 0; + + + +int constructors = 0; +int destructors = 0; + +#define CONST const + +class TestObject +{ +public: + TestObject(CONST TestObject& inObj); + TestObject(); + ~TestObject(); + + TestObject& operator=(CONST TestObject& inObj); + + void test(void); + + int version() CONST { return _version; } +private: + mutable int _version; +}; + +TestObject::TestObject(CONST TestObject& inObj) + +{ + ++constructors; + _version = inObj._version; + //printf("%p (%d) -- TestObject(const TestObject&) called", this, _version); +} + + +TestObject::TestObject() +{ + _version = ++constructors; + //printf("%p (%d) -- TestObject() called\n", this, _version); +} + + +TestObject::~TestObject() +{ + //printf("%p -- ~TestObject() called\n", this); + ++destructors; +} + +#if 1 +TestObject& TestObject::operator=(CONST TestObject& inObj) +{ + //printf("%p -- operator= called", this); + _version = inObj._version; + return *this; +} +#endif + +void TestObject::test(void) { + void (^b)(void) = ^{ recovered = _version; }; + void (^b2)(void) = Block_copy(b); + b2(); + Block_release(b2); +} + + + +void testRoutine() { + TestObject one; + + + one.test(); +} + + + +int main(int argc, char *argv[]) { + testRoutine(); + if (recovered == 1) { + printf("%s: success\n", argv[0]); + exit(0); + } + printf("%s: *** didn't recover byref block variable\n", argv[0]); + exit(1); +} diff --git a/test/BlocksRuntime/rettypepromotion.c b/test/BlocksRuntime/rettypepromotion.c new file mode 100644 index 000000000000..597eafe8b858 --- /dev/null +++ b/test/BlocksRuntime/rettypepromotion.c @@ -0,0 +1,36 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * rettypepromotion.c + * testObjects + * + * Created by Blaine Garst on 11/3/08. + * + */ + +// CONFIG error: +// C++ and C give different errors so we don't check for an exact match. +// The error is that enum's are defined to be ints, always, even if defined with explicit long values + + +#include <stdio.h> +#include <stdlib.h> + +enum { LESS = -1, EQUAL, GREATER }; + +void sortWithBlock(long (^comp)(void *arg1, void *arg2)) { +} + +int main(int argc, char *argv[]) { + sortWithBlock(^(void *arg1, void *arg2) { + if (random()) return LESS; + if (random()) return EQUAL; + if (random()) return GREATER; + }); + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/returnfunctionptr.c b/test/BlocksRuntime/returnfunctionptr.c new file mode 100644 index 000000000000..6c7df631f8db --- /dev/null +++ b/test/BlocksRuntime/returnfunctionptr.c @@ -0,0 +1,23 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + + +// CONFIG rdar://6339747 but wasn't + +#include <stdio.h> + +int (*funcptr)(long); + +int (*(^b)(char))(long); + +int main(int argc, char *argv[]) { + // implicit is fine + b = ^(char x) { return funcptr; }; + // explicit never parses + b = ^int (*(char x))(long) { return funcptr; }; + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/shorthandexpression.c b/test/BlocksRuntime/shorthandexpression.c new file mode 100644 index 000000000000..bf4582072b48 --- /dev/null +++ b/test/BlocksRuntime/shorthandexpression.c @@ -0,0 +1,24 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * shorthandexpression.c + * testObjects + * + * Created by Blaine Garst on 9/16/08. + * + * CONFIG error: + */ + + +void foo() { + void (^b)(void) = ^(void)printf("hello world\n"); +} + +int main(int argc, char *argv[]) { + printf("%s: this shouldn't compile\n", argv[0]); + return 1; +} diff --git a/test/BlocksRuntime/sizeof.c b/test/BlocksRuntime/sizeof.c new file mode 100644 index 000000000000..1f84fc16f38f --- /dev/null +++ b/test/BlocksRuntime/sizeof.c @@ -0,0 +1,26 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * sizeof.c + * testObjects + * + * Created by Blaine Garst on 2/17/09. + * + */ + +#include <stdio.h> + +// CONFIG error: + +int main(int argc, char *argv[]) { + + void (^aBlock)(void) = ^{ printf("hellow world\n"); }; + + printf("the size of a block is %ld\n", sizeof(*aBlock)); + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/small-struct.c b/test/BlocksRuntime/small-struct.c new file mode 100644 index 000000000000..434f3c179f7e --- /dev/null +++ b/test/BlocksRuntime/small-struct.c @@ -0,0 +1,45 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG + +#import <stdio.h> +#import <stdlib.h> +#import <string.h> + +typedef struct { + int a; + int b; +} MiniStruct; + +int main (int argc, const char * argv[]) { + MiniStruct inny; + MiniStruct outty; + MiniStruct (^copyStruct)(MiniStruct); + + memset(&inny, 0xA5, sizeof(inny)); + memset(&outty, 0x2A, sizeof(outty)); + + inny.a = 12; + inny.b = 42; + + copyStruct = ^(MiniStruct aTinyStruct){ return aTinyStruct; }; // pass-by-value intrinsically copies the argument + + outty = copyStruct(inny); + + if ( &inny == &outty ) { + printf("%s: struct wasn't copied.", argv[0]); + exit(1); + } + if ( (inny.a != outty.a) || (inny.b != outty.b) ) { + printf("%s: struct contents did not match.", argv[0]); + exit(1); + } + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/structmember.c b/test/BlocksRuntime/structmember.c new file mode 100644 index 000000000000..c451d3f348c9 --- /dev/null +++ b/test/BlocksRuntime/structmember.c @@ -0,0 +1,45 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * structmember.c + * testObjects + * + * Created by Blaine Garst on 9/30/08. + * CONFIG + */ +#include <Block.h> +#include <Block_private.h> +#include <stdio.h> + +// CONFIG + +int main(int argc, char *argv[]) { + struct stuff { + long int a; + long int b; + long int c; + } localStuff = { 10, 20, 30 }; + int d; + + void (^a)(void) = ^ { printf("d is %d", d); }; + void (^b)(void) = ^ { printf("d is %d, localStuff.a is %lu", d, localStuff.a); }; + + unsigned nominalsize = Block_size(b) - Block_size(a); +#if __cplusplus__ + // need copy+dispose helper for C++ structures + nominalsize += 2*sizeof(void*); +#endif + if ((Block_size(b) - Block_size(a)) != nominalsize) { + printf("sizeof a is %ld, sizeof b is %ld, expected %d\n", Block_size(a), Block_size(b), nominalsize); + printf("dump of b is %s\n", _Block_dump(b)); + return 1; + } + printf("%s: Success\n", argv[0]); + return 0; +} + + diff --git a/test/BlocksRuntime/testfilerunner.h b/test/BlocksRuntime/testfilerunner.h new file mode 100644 index 000000000000..d4e54f029047 --- /dev/null +++ b/test/BlocksRuntime/testfilerunner.h @@ -0,0 +1,110 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// testfilerunner.h +// testObjects +// +// Created by Blaine Garst on 9/24/08. +// + +#import <Cocoa/Cocoa.h> + +/* + variations: + four source types: C, ObjC, C++, ObjC++, + and for ObjC or ObjC++ we have + RR and GC capabilities + we assume C++ friendly includes for C/ObjC even if C++ isn't used + + + four compilers: C, ObjC, C++, ObjC++ + and for ObjC or ObjC++ we can compile + RR, RR+GC, GC+RR, GC + although to test RR+GC we need to build a shell "main" in both modes + and/or run with GC disabled if possible. + + To maximize coverage we mark files with capabilities and then ask them to be + compiled with each variation of compiler and option. + If the file doesn't have the capability it politely refuses. +*/ + +enum options { + Do64 = (1 << 0), + DoCPP = (1 << 1), + DoOBJC = (1 << 3), + DoGC = (1 << 4), + DoRR = (1 << 5), + DoRRGC = (1 << 6), // -fobjc-gc but main w/o so it runs in RR mode + DoGCRR = (1 << 7), // -fobjc-gc & run GC mode + + //DoDashG = (1 << 8), + DoDashO = (1 << 9), + DoDashOs = (1 << 10), + DoDashO2 = (1 << 11), + + DoC99 = (1 << 12), // -std=c99 +}; + + +@class TestFileExeGenerator; + +// this class will actually compile and/or run a target binary +// XXX we don't track which dynamic libraries requested/used nor set them up +@interface TestFileExe : NSObject { + NSPointerArray *compileLine; + int options; + bool shouldFail; + TestFileExeGenerator *generator; + __strong char *binaryName; + __strong char *sourceName; + __strong char *libraryPath; + __strong char *frameworkPath; +} +@property int options; +@property(assign) NSPointerArray *compileLine; +@property(assign) TestFileExeGenerator *generator; +@property bool shouldFail; +@property __strong char *binaryName; +@property __strong char *sourceName; +@property __strong char *libraryPath; +@property __strong char *frameworkPath; +- (bool) compileUnlessExists:(bool)skip; +- (bool) run; +@property(readonly) __strong char *radar; +@end + +// this class generates an appropriate set of configurations to compile +// we don't track which gcc we use but we should XXX +@interface TestFileExeGenerator : NSObject { + bool hasObjC; + bool hasRR; + bool hasGC; + bool hasCPlusPlus; + bool wantsC99; + bool wants64; + bool wants32; + bool supposedToNotCompile; + bool open; // this problem is still open - e.g. unresolved + __strong char *radar; // for things already known to go wrong + __strong char *filename; + __strong char *compilerPath; + __strong char *errorString; + __strong char *warningString; + NSPointerArray *extraLibraries; +} +@property bool hasObjC, hasRR, hasGC, hasCPlusPlus, wantsC99, supposedToNotCompile, open, wants32, wants64; +@property(assign) __strong char *radar; +@property __strong char *filename; +@property __strong char *compilerPath; +@property __strong char *errorString; +@property __strong char *warningString; +- (TestFileExe *)lineForOptions:(int)options; // nil if no can do ++ (NSArray *)generatorsFromFILE:(FILE *)fd; ++ (NSArray *)generatorsFromPath:(NSString *)path; +@end + + diff --git a/test/BlocksRuntime/testfilerunner.m b/test/BlocksRuntime/testfilerunner.m new file mode 100644 index 000000000000..459adf889f6e --- /dev/null +++ b/test/BlocksRuntime/testfilerunner.m @@ -0,0 +1,805 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// testfilerunner.m +// testObjects +// +// Created by Blaine Garst on 9/24/08. +// + +#import "testfilerunner.h" +#import <Foundation/Foundation.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> + +bool Everything = false; // do it also with 3 levels of optimization +bool DoClang = false; + +static bool isDirectory(char *path); +static bool isExecutable(char *path); +static bool isYounger(char *source, char *binary); +static bool readErrorFile(char *buffer, const char *from); + +__strong char *gcstrcpy2(__strong const char *arg, char *endp) { + unsigned size = endp - arg + 1; + __strong char *result = NSAllocateCollectable(size, 0); + strncpy(result, arg, size); + result[size-1] = 0; + return result; +} +__strong char *gcstrcpy1(__strong char *arg) { + unsigned size = strlen(arg) + 1; + __strong char *result = NSAllocateCollectable(size, 0); + strncpy(result, arg, size); + result[size-1] = 0; + return result; +} + +@implementation TestFileExe + +@synthesize options, compileLine, shouldFail, binaryName, sourceName; +@synthesize generator; +@synthesize libraryPath, frameworkPath; + +- (NSString *)description { + NSMutableString *result = [NSMutableString new]; + if (shouldFail) [result appendString:@"fail"]; + for (id x in compileLine) { + [result appendString:[NSString stringWithFormat:@" %s", (char *)x]]; + } + return result; +} + +- (__strong char *)radar { + return generator.radar; +} + +- (bool) compileUnlessExists:(bool)skip { + if (shouldFail) { + printf("don't use this to compile anymore!\n"); + return false; + } + if (skip && isExecutable(binaryName) && !isYounger(sourceName, binaryName)) return true; + int argc = [compileLine count]; + char *argv[argc+1]; + for (int i = 0; i < argc; ++i) + argv[i] = (char *)[compileLine pointerAtIndex:i]; + argv[argc] = NULL; + pid_t child = fork(); + if (child == 0) { + execv(argv[0], argv); + exit(10); // shouldn't happen + } + if (child < 0) { + printf("fork failed\n"); + return false; + } + int status = 0; + pid_t deadchild = wait(&status); + if (deadchild != child) { + printf("wait got %d instead of %d\n", deadchild, child); + exit(1); + } + if (WEXITSTATUS(status) == 0) { + return true; + } + printf("run failed\n"); + return false; +} + +bool lookforIn(char *lookfor, const char *format, pid_t child) { + char buffer[512]; + char got[512]; + sprintf(buffer, format, child); + bool gotOutput = readErrorFile(got, buffer); + if (!gotOutput) { + printf("**** didn't get an output file %s to analyze!!??\n", buffer); + return false; + } + char *where = strstr(got, lookfor); + if (!where) { + printf("didn't find '%s' in output file %s\n", lookfor, buffer); + return false; + } + unlink(buffer); + return true; +} + +- (bool) compileWithExpectedFailure { + if (!shouldFail) { + printf("Why am I being called?\n"); + return false; + } + int argc = [compileLine count]; + char *argv[argc+1]; + for (int i = 0; i < argc; ++i) + argv[i] = (char *)[compileLine pointerAtIndex:i]; + argv[argc] = NULL; + pid_t child = fork(); + char buffer[512]; + if (child == 0) { + // in child + sprintf(buffer, "/tmp/errorfile_%d", getpid()); + close(1); + int fd = creat(buffer, 0777); + if (fd != 1) { + fprintf(stderr, "didn't open custom error file %s as 1, got %d\n", buffer, fd); + exit(1); + } + close(2); + dup(1); + int result = execv(argv[0], argv); + exit(10); + } + if (child < 0) { + printf("fork failed\n"); + return false; + } + int status = 0; + pid_t deadchild = wait(&status); + if (deadchild != child) { + printf("wait got %d instead of %d\n", deadchild, child); + exit(11); + } + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) { + return false; + } + } + else { + printf("***** compiler borked/ICEd/died unexpectedly (status %x)\n", status); + return false; + } + char *error = generator.errorString; + + if (!error) return true; +#if 0 + char got[512]; + sprintf(buffer, "/tmp/errorfile_%d", child); + bool gotOutput = readErrorFile(got, buffer); + if (!gotOutput) { + printf("**** didn't get an error file %s to analyze!!??\n", buffer); + return false; + } + char *where = strstr(got, error); + if (!where) { + printf("didn't find '%s' in error file %s\n", error, buffer); + return false; + } + unlink(buffer); +#else + if (!lookforIn(error, "/tmp/errorfile_%d", child)) return false; +#endif + return true; +} + +- (bool) run { + if (shouldFail) return true; + if (sizeof(long) == 4 && options & Do64) { + return true; // skip 64-bit tests + } + int argc = 1; + char *argv[argc+1]; + argv[0] = binaryName; + argv[argc] = NULL; + pid_t child = fork(); + if (child == 0) { + // set up environment + char lpath[1024]; + char fpath[1024]; + char *myenv[3]; + int counter = 0; + if (libraryPath) { + sprintf(lpath, "DYLD_LIBRARY_PATH=%s", libraryPath); + myenv[counter++] = lpath; + } + if (frameworkPath) { + sprintf(fpath, "DYLD_FRAMEWORK_PATH=%s", frameworkPath); + myenv[counter++] = fpath; + } + myenv[counter] = NULL; + if (generator.warningString) { + // set up stdout/stderr + char outfile[1024]; + sprintf(outfile, "/tmp/stdout_%d", getpid()); + close(2); + close(1); + creat(outfile, 0700); + dup(1); + } + execve(argv[0], argv, myenv); + exit(10); // shouldn't happen + } + if (child < 0) { + printf("fork failed\n"); + return false; + } + int status = 0; + pid_t deadchild = wait(&status); + if (deadchild != child) { + printf("wait got %d instead of %d\n", deadchild, child); + exit(1); + } + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + if (generator.warningString) { + if (!lookforIn(generator.warningString, "/tmp/stdout_%d", child)) return false; + } + return true; + } + printf("**** run failed for %s\n", binaryName); + return false; +} + +@end + +@implementation TestFileExeGenerator +@synthesize filename, compilerPath, errorString; +@synthesize hasObjC, hasRR, hasGC, hasCPlusPlus, wantsC99, supposedToNotCompile, open, wants32, wants64; +@synthesize radar; +@synthesize warningString; + +- (void)setFilename:(__strong char *)name { + filename = gcstrcpy1(name); +} +- (void)setCompilerPath:(__strong char *)name { + compilerPath = gcstrcpy1(name); +} + +- (void)forMostThings:(NSMutableArray *)lines options:(int)options { + TestFileExe *item = nil; + item = [self lineForOptions:options]; + if (item) [lines addObject:item]; + item = [self lineForOptions:options|Do64]; + if (item) [lines addObject:item]; + item = [self lineForOptions:options|DoCPP]; + if (item) [lines addObject:item]; + item = [self lineForOptions:options|Do64|DoCPP]; + if (item) [lines addObject:item]; +} + +/* + DoDashG = (1 << 8), + DoDashO = (1 << 9), + DoDashOs = (1 << 10), + DoDashO2 = (1 << 11), +*/ + +- (void)forAllThings:(NSMutableArray *)lines options:(int)options { + [self forMostThings:lines options:options]; + if (!Everything) { + return; + } + // now do it with three explicit optimization flags + [self forMostThings:lines options:options | DoDashO]; + [self forMostThings:lines options:options | DoDashOs]; + [self forMostThings:lines options:options | DoDashO2]; +} + +- (NSArray *)allLines { + NSMutableArray *result = [NSMutableArray new]; + TestFileExe *item = nil; + + int options = 0; + [self forAllThings:result options:0]; + [self forAllThings:result options:DoOBJC | DoRR]; + [self forAllThings:result options:DoOBJC | DoGC]; + [self forAllThings:result options:DoOBJC | DoGCRR]; + //[self forAllThings:result options:DoOBJC | DoRRGC]; + + return result; +} + +- (void)addLibrary:(const char *)dashLSomething { + if (!extraLibraries) { + extraLibraries = [NSPointerArray pointerArrayWithOptions: + NSPointerFunctionsStrongMemory | + NSPointerFunctionsCStringPersonality]; + } + [extraLibraries addPointer:(void *)dashLSomething]; +} + +- (TestFileExe *)lineForOptions:(int)options { // nil if no can do + if (hasObjC && !(options & DoOBJC)) return nil; + if (hasCPlusPlus && !(options & DoCPP)) return nil; + if (hasObjC) { + if (!hasGC && (options & (DoGC|DoGCRR))) return nil; // not smart enough + if (!hasRR && (options & (DoRR|DoRRGC))) return nil; + } + NSPointerArray *pa = [NSPointerArray pointerArrayWithOptions: + NSPointerFunctionsStrongMemory | + NSPointerFunctionsCStringPersonality]; + // construct path + char path[512]; + path[0] = 0; + if (!compilerPath) compilerPath = "/usr/bin"; + if (compilerPath) { + strcat(path, compilerPath); + strcat(path, "/"); + } + if (options & DoCPP) { + strcat(path, DoClang ? "clang++" : "g++-4.2"); + } + else { + strcat(path, DoClang ? "clang" : "gcc-4.2"); + } + [pa addPointer:gcstrcpy1(path)]; + if (options & DoOBJC) { + if (options & DoCPP) { + [pa addPointer:"-ObjC++"]; + } + else { + [pa addPointer:"-ObjC"]; + } + } + [pa addPointer:"-g"]; + if (options & DoDashO) [pa addPointer:"-O"]; + else if (options & DoDashO2) [pa addPointer:"-O2"]; + else if (options & DoDashOs) [pa addPointer:"-Os"]; + if (wantsC99 && (! (options & DoCPP))) { + [pa addPointer:"-std=c99"]; + [pa addPointer:"-fblocks"]; + } + [pa addPointer:"-arch"]; + [pa addPointer: (options & Do64) ? "x86_64" : "i386"]; + + if (options & DoOBJC) { + switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) { + case DoRR: + break; + case DoGC: + [pa addPointer:"-fobjc-gc-only"]; + break; + case DoGCRR: + [pa addPointer:"-fobjc-gc"]; + break; + case DoRRGC: + printf("DoRRGC unsupported right now\n"); + [pa addPointer:"-c"]; + return nil; + } + [pa addPointer:"-framework"]; + [pa addPointer:"Foundation"]; + } + [pa addPointer:gcstrcpy1(filename)]; + [pa addPointer:"-o"]; + + path[0] = 0; + strcat(path, filename); + strcat(path, "."); + strcat(path, (options & Do64) ? "64" : "32"); + if (options & DoOBJC) { + switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) { + case DoRR: strcat(path, "-rr"); break; + case DoGC: strcat(path, "-gconly"); break; + case DoGCRR: strcat(path, "-gcrr"); break; + case DoRRGC: strcat(path, "-rrgc"); break; + } + } + if (options & DoCPP) strcat(path, "++"); + if (options & DoDashO) strcat(path, "-O"); + else if (options & DoDashO2) strcat(path, "-O2"); + else if (options & DoDashOs) strcat(path, "-Os"); + if (wantsC99) strcat(path, "-C99"); + strcat(path, DoClang ? "-clang" : "-gcc"); + strcat(path, "-bin"); + TestFileExe *result = [TestFileExe new]; + result.binaryName = gcstrcpy1(path); // could snarf copy in pa + [pa addPointer:result.binaryName]; + for (id cString in extraLibraries) { + [pa addPointer:cString]; + } + + result.sourceName = gcstrcpy1(filename); // could snarf copy in pa + result.compileLine = pa; + result.options = options; + result.shouldFail = supposedToNotCompile; + result.generator = self; + return result; +} + ++ (NSArray *)generatorsFromPath:(NSString *)path { + FILE *fp = fopen([path fileSystemRepresentation], "r"); + if (fp == NULL) return nil; + NSArray *result = [self generatorsFromFILE:fp]; + fclose(fp); + return result; +} + +#define LOOKFOR "CON" "FIG" + +char *__strong parseRadar(char *line) { + line = strstr(line, "rdar:"); // returns beginning + char *endp = line + strlen("rdar:"); + while (*endp && *endp != ' ' && *endp != '\n') + ++endp; + return gcstrcpy2(line, endp); +} + +- (void)parseLibraries:(const char *)line { + start: + line = strstr(line, "-l"); + char *endp = (char *)line + 2; + while (*endp && *endp != ' ' && *endp != '\n') + ++endp; + [self addLibrary:gcstrcpy2(line, endp)]; + if (strstr(endp, "-l")) { + line = endp; + goto start; + } +} + ++ (TestFileExeGenerator *)generatorFromLine:(char *)line filename:(char *)filename { + TestFileExeGenerator *item = [TestFileExeGenerator new]; + item.filename = gcstrcpy1(filename); + if (strstr(line, "GC")) item.hasGC = true; + if (strstr(line, "RR")) item.hasRR = true; + if (strstr(line, "C++")) item.hasCPlusPlus = true; + if (strstr(line, "-C99")) { + item.wantsC99 = true; + } + if (strstr(line, "64")) item.wants64 = true; + if (strstr(line, "32")) item.wants32 = true; + if (strstr(line, "-l")) [item parseLibraries:line]; + if (strstr(line, "open")) item.open = true; + if (strstr(line, "FAIL")) item.supposedToNotCompile = true; // old + // compile time error + if (strstr(line, "error:")) { + item.supposedToNotCompile = true; + // zap newline + char *error = strstr(line, "error:") + strlen("error:"); + // make sure we have something before the newline + char *newline = strstr(error, "\n"); + if (newline && ((newline-error) > 1)) { + *newline = 0; + item.errorString = gcstrcpy1(strstr(line, "error:") + strlen("error: ")); + } + } + // run time warning + if (strstr(line, "runtime:")) { + // zap newline + char *error = strstr(line, "runtime:") + strlen("runtime:"); + // make sure we have something before the newline + char *newline = strstr(error, "\n"); + if (newline && ((newline-error) > 1)) { + *newline = 0; + item.warningString = gcstrcpy1(strstr(line, "runtime:") + strlen("runtime:")); + } + } + if (strstr(line, "rdar:")) item.radar = parseRadar(line); + if (item.hasGC || item.hasRR) item.hasObjC = true; + if (!item.wants32 && !item.wants64) { // give them both if they ask for neither + item.wants32 = item.wants64 = true; + } + return item; +} + ++ (NSArray *)generatorsFromFILE:(FILE *)fp { + NSMutableArray *result = [NSMutableArray new]; + // pretend this is a grep LOOKFOR *.[cmCM][cmCM] input + // look for + // filename: ... LOOKFOR [GC] [RR] [C++] [FAIL ...] + char buf[512]; + while (fgets(buf, 512, fp)) { + char *config = strstr(buf, LOOKFOR); + if (!config) continue; + char *filename = buf; + char *end = strchr(buf, ':'); + *end = 0; + [result addObject:[self generatorFromLine:config filename:filename]]; + } + return result; +} + ++ (TestFileExeGenerator *)generatorFromFilename:(char *)filename { + FILE *fp = fopen(filename, "r"); + if (!fp) { + printf("didn't open %s!!\n", filename); + return nil; + } + char buf[512]; + while (fgets(buf, 512, fp)) { + char *config = strstr(buf, LOOKFOR); + if (!config) continue; + fclose(fp); + return [self generatorFromLine:config filename:filename]; + } + fclose(fp); + // guess from filename + char *ext = strrchr(filename, '.'); + if (!ext) return nil; + TestFileExeGenerator *result = [TestFileExeGenerator new]; + result.filename = gcstrcpy1(filename); + if (!strncmp(ext, ".m", 2)) { + result.hasObjC = true; + result.hasRR = true; + result.hasGC = true; + } + else if (!strcmp(ext, ".c")) { + ; + } + else if (!strcmp(ext, ".M") || !strcmp(ext, ".mm")) { + result.hasObjC = true; + result.hasRR = true; + result.hasGC = true; + result.hasCPlusPlus = true; + } + else if (!strcmp(ext, ".cc") + || !strcmp(ext, ".cp") + || !strcmp(ext, ".cxx") + || !strcmp(ext, ".cpp") + || !strcmp(ext, ".CPP") + || !strcmp(ext, ".c++") + || !strcmp(ext, ".C")) { + result.hasCPlusPlus = true; + } + else { + printf("unknown extension, file %s ignored\n", filename); + result = nil; + } + return result; + +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%s: %s%s%s%s%s%s", + filename, + LOOKFOR, + hasGC ? " GC" : "", + hasRR ? " RR" : "", + hasCPlusPlus ? " C++" : "", + wantsC99 ? "C99" : "", + supposedToNotCompile ? " FAIL" : ""]; +} + +@end + +void printDetails(NSArray *failures, const char *whatAreThey) { + if ([failures count]) { + NSMutableString *output = [NSMutableString new]; + printf("%s:\n", whatAreThey); + for (TestFileExe *line in failures) { + printf("%s", line.binaryName); + char *radar = line.generator.radar; + if (radar) + printf(" (due to %s?),", radar); + printf(" recompile via:\n%s\n\n", line.description.UTF8String); + } + printf("\n"); + } +} + +void help(const char *whoami) { + printf("Usage: %s [-fast] [-e] [-dyld librarypath] [gcc4.2dir] [-- | source1 ...]\n", whoami); + printf(" -fast don't recompile if binary younger than source\n"); + printf(" -open only run tests that are thought to still be unresolved\n"); + printf(" -clang use the clang and clang++ compilers\n"); + printf(" -e compile all variations also with -Os, -O2, -O3\n"); + printf(" -dyld p override DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH to p when running tests\n"); + printf(" <compilerpath> directory containing gcc-4.2 (or clang) that you wish to use to compile the tests\n"); + printf(" -- assume stdin is a grep CON" "FIG across the test sources\n"); + printf(" otherwise treat each remaining argument as a single test file source\n"); + printf("%s will compile and run individual test files under a variety of compilers, c, obj-c, c++, and objc++\n", whoami); + printf(" .c files are compiled with all four compilers\n"); + printf(" .m files are compiled with objc and objc++ compilers\n"); + printf(" .C files are compiled with c++ and objc++ compilers\n"); + printf(" .M files are compiled only with the objc++ compiler\n"); + printf("(actually all forms of extensions recognized by the compilers are honored, .cc, .c++ etc.)\n"); + printf("\nTest files should run to completion with no output and exit (return) 0 on success.\n"); + printf("Further they should be able to be compiled and run with GC on or off and by the C++ compilers\n"); + printf("A line containing the string CON" "FIG within the source enables restrictions to the above assumptions\n"); + printf("and other options.\n"); + printf("Following CON" "FIG the string\n"); + printf(" C++ restricts the test to only be run by c++ and objc++ compilers\n"); + printf(" GC restricts the test to only be compiled and run with GC on\n"); + printf(" RR (retain/release) restricts the test to only be compiled and run with GC off\n"); + printf("Additionally,\n"); + printf(" -C99 restricts the C versions of the test to -fstd=c99 -fblocks\n"); + printf(" -O adds the -O optimization level\n"); + printf(" -O2 adds the -O2 optimization level\n"); + printf(" -Os adds the -Os optimization level\n"); + printf("Files that are known to exhibit unresolved problems can provide the term \"open\" and this can"); + printf("in turn allow highlighting of fixes that have regressed as well as identify that fixes are now available.\n"); + printf("Files that exhibit known bugs may provide\n"); + printf(" rdar://whatever such that if they fail the rdar will get cited\n"); + printf("Files that are expected to fail to compile should provide, as their last token sequence,\n"); + printf(" error:\n"); + printf(" or error: substring to match.\n"); + printf("Files that are expected to produce a runtime error message should provide, as their last token sequence,\n"); + printf(" warning: string to match\n"); + printf("\n%s will compile and run all configurations of the test files and report a summary at the end. Good luck.\n", whoami); + printf(" Blaine Garst blaine@apple.com\n"); +} + +int main(int argc, char *argv[]) { + printf("running on %s-bit architecture\n", sizeof(long) == 4 ? "32" : "64"); + char *compilerDir = "/usr/bin"; + NSMutableArray *generators = [NSMutableArray new]; + bool doFast = false; + bool doStdin = false; + bool onlyOpen = false; + char *libraryPath = getenv("DYLD_LIBRARY_PATH"); + char *frameworkPath = getenv("DYLD_FRAMEWORK_PATH"); + // process options + while (argc > 1) { + if (!strcmp(argv[1], "-fast")) { + doFast = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-dyld")) { + doFast = true; + --argc; + ++argv; + frameworkPath = argv[1]; + libraryPath = argv[1]; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-open")) { + onlyOpen = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-clang")) { + DoClang = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-e")) { + Everything = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "--")) { + doStdin = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-")) { + help(argv[0]); + return 1; + } + else if (argc > 1 && isDirectory(argv[1])) { + compilerDir = argv[1]; + ++argv; + --argc; + } + else + break; + } + // process remaining arguments, or stdin + if (argc == 1) { + if (doStdin) + generators = (NSMutableArray *)[TestFileExeGenerator generatorsFromFILE:stdin]; + else { + help(argv[0]); + return 1; + } + } + else while (argc > 1) { + TestFileExeGenerator *generator = [TestFileExeGenerator generatorFromFilename:argv[1]]; + if (generator) [generators addObject:generator]; + ++argv; + --argc; + } + // see if we can generate all possibilities + NSMutableArray *failureToCompile = [NSMutableArray new]; + NSMutableArray *failureToFailToCompile = [NSMutableArray new]; + NSMutableArray *failureToRun = [NSMutableArray new]; + NSMutableArray *successes = [NSMutableArray new]; + for (TestFileExeGenerator *generator in generators) { + //NSLog(@"got %@", generator); + if (onlyOpen && !generator.open) { + //printf("skipping resolved test %s\n", generator.filename); + continue; // skip closed if onlyOpen + } + if (!onlyOpen && generator.open) { + //printf("skipping open test %s\n", generator.filename); + continue; // skip open if not asked for onlyOpen + } + generator.compilerPath = compilerDir; + NSArray *tests = [generator allLines]; + for (TestFileExe *line in tests) { + line.frameworkPath = frameworkPath; // tell generators about it instead XXX + line.libraryPath = libraryPath; // tell generators about it instead XXX + if ([line shouldFail]) { + if (doFast) continue; // don't recompile & don't count as success + if ([line compileWithExpectedFailure]) { + [successes addObject:line]; + } + else + [failureToFailToCompile addObject:line]; + } + else if ([line compileUnlessExists:doFast]) { + if ([line run]) { + printf("%s ran successfully\n", line.binaryName); + [successes addObject:line]; + } + else { + [failureToRun addObject:line]; + } + } + else { + [failureToCompile addObject:line]; + } + } + } + printf("\n--- results ---\n\n%lu successes\n%lu unexpected compile failures\n%lu failure to fail to compile errors\n%lu run failures\n", + [successes count], [failureToCompile count], [failureToFailToCompile count], [failureToRun count]); + printDetails(failureToCompile, "unexpected compile failures"); + printDetails(failureToFailToCompile, "should have failed to compile but didn't failures"); + printDetails(failureToRun, "run failures"); + + if (onlyOpen && [successes count]) { + NSMutableSet *radars = [NSMutableSet new]; + printf("The following tests ran successfully suggesting that they are now resolved:\n"); + for (TestFileExe *line in successes) { + printf("%s\n", line.binaryName); + if (line.radar) [radars addObject:line.generator]; + } + if ([radars count]) { + printf("The following radars may be resolved:\n"); + for (TestFileExeGenerator *line in radars) { + printf("%s\n", line.radar); + } + } + } + + return [failureToCompile count] + [failureToRun count]; +} + +#include <sys/stat.h> + +static bool isDirectory(char *path) { + struct stat statb; + int retval = stat(path, &statb); + if (retval != 0) return false; + if (statb.st_mode & S_IFDIR) return true; + return false; +} + +static bool isExecutable(char *path) { + struct stat statb; + int retval = stat(path, &statb); + if (retval != 0) return false; + if (!(statb.st_mode & S_IFREG)) return false; + if (statb.st_mode & S_IXUSR) return true; + return false; +} + +static bool isYounger(char *source, char *binary) { + struct stat statb; + int retval = stat(binary, &statb); + if (retval != 0) return true; // if doesn't exit, lie + + struct stat stata; + retval = stat(source, &stata); + if (retval != 0) return true; // we're hosed + // the greater the timeval the younger it is + if (stata.st_mtimespec.tv_sec > statb.st_mtimespec.tv_sec) return true; + if (stata.st_mtimespec.tv_nsec > statb.st_mtimespec.tv_nsec) return true; + return false; +} + +static bool readErrorFile(char *buffer, const char *from) { + int fd = open(from, 0); + if (fd < 0) { + printf("didn't open %s, (might not have been created?)\n", buffer); + return false; + } + int count = read(fd, buffer, 512); + if (count < 1) { + printf("read error on %s\n", buffer); + return false; + } + buffer[count-1] = 0; // zap newline + return true; +} diff --git a/test/BlocksRuntime/varargs-bad-assign.c b/test/BlocksRuntime/varargs-bad-assign.c new file mode 100644 index 000000000000..b978668b95c0 --- /dev/null +++ b/test/BlocksRuntime/varargs-bad-assign.c @@ -0,0 +1,44 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// HACK ALERT: gcc and g++ give different errors, referencing the line number to ensure that it checks for the right error; MUST KEEP IN SYNC WITH THE TEST +// CONFIG 27: error: + +#import <stdio.h> +#import <stdlib.h> +#import <string.h> +#import <stdarg.h> + + +int main (int argc, const char * argv[]) { + int (^sumn)(int n, ...); + int six = 0; + + sumn = ^(int a, int b, int n, ...){ + int result = 0; + va_list numbers; + int i; + + va_start(numbers, n); + for (i = 0 ; i < n ; i++) { + result += va_arg(numbers, int); + } + va_end(numbers); + + return result; + }; + + six = sumn(3, 1, 2, 3); + + if ( six != 6 ) { + printf("%s: Expected 6 but got %d\n", argv[0], six); + exit(1); + } + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/varargs.c b/test/BlocksRuntime/varargs.c new file mode 100644 index 000000000000..01affc76e68c --- /dev/null +++ b/test/BlocksRuntime/varargs.c @@ -0,0 +1,39 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG + +#import <stdio.h> +#import <stdlib.h> +#import <string.h> +#import <stdarg.h> + + +int main (int argc, const char * argv[]) { + int (^sumn)(int n, ...) = ^(int n, ...){ + int result = 0; + va_list numbers; + int i; + + va_start(numbers, n); + for (i = 0 ; i < n ; i++) { + result += va_arg(numbers, int); + } + va_end(numbers); + + return result; + }; + int six = sumn(3, 1, 2, 3); + + if ( six != 6 ) { + printf("%s: Expected 6 but got %d\n", argv[0], six); + exit(1); + } + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/test/BlocksRuntime/variadic.c b/test/BlocksRuntime/variadic.c new file mode 100644 index 000000000000..1d80657e9886 --- /dev/null +++ b/test/BlocksRuntime/variadic.c @@ -0,0 +1,66 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * variadic.c + * testObjects + * + * Created by Blaine Garst on 2/17/09. + * + */ + +// PURPOSE Test that variadic arguments compile and work for Blocks +// CONFIG + +#include <stdarg.h> +#include <stdio.h> + +int main(int argc, char *argv[]) { + + long (^addthem)(const char *, ...) = ^long (const char *format, ...){ + va_list argp; + const char *p; + int i; + char c; + double d; + long result = 0; + va_start(argp, format); + //printf("starting...\n"); + for (p = format; *p; p++) switch (*p) { + case 'i': + i = va_arg(argp, int); + //printf("i: %d\n", i); + result += i; + break; + case 'd': + d = va_arg(argp, double); + //printf("d: %g\n", d); + result += (int)d; + break; + case 'c': + c = va_arg(argp, int); + //printf("c: '%c'\n", c); + result += c; + break; + } + //printf("...done\n\n"); + return result; + }; + long testresult = addthem("ii", 10, 20); + if (testresult != 30) { + printf("got wrong result: %ld\n", testresult); + return 1; + } + testresult = addthem("idc", 30, 40.0, 'a'); + if (testresult != (70+'a')) { + printf("got different wrong result: %ld\n", testresult); + return 1; + } + printf("%s: Success\n", argv[0]); + return 0; +} + + diff --git a/test/BlocksRuntime/voidarg.c b/test/BlocksRuntime/voidarg.c new file mode 100644 index 000000000000..a8f034b47c10 --- /dev/null +++ b/test/BlocksRuntime/voidarg.c @@ -0,0 +1,27 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * voidarg.c + * testObjects + * + * Created by Blaine Garst on 2/17/09. + * + */ + +// PURPOSE should complain about missing 'void' but both GCC and clang are supporting K&R instead +// CONFIG open error: + +#include <stdio.h> + +int Global; + +void (^globalBlock)() = ^{ ++Global; }; // should be void (^gb)(void) = ... + +int main(int argc, char *argv[]) { + printf("%s: success", argv[0]); + return 0; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 000000000000..2ceb86463ae0 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,65 @@ +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.common.configured.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.common.configured) + +# BlocksRuntime and builtins testsuites are not yet ported to lit. +# add_subdirectory(BlocksRuntime) +# add_subdirectory(builtins) + +set(SANITIZER_COMMON_LIT_TEST_DEPS) +# When ANDROID, we build tests with the host compiler (i.e. CMAKE_C_COMPILER), +# and run tests with tools from the host toolchain. +if(NOT ANDROID) + if(NOT COMPILER_RT_STANDALONE_BUILD) + # Use LLVM utils and Clang from the same build tree. + list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS + clang clang-headers FileCheck count not llvm-nm llvm-symbolizer + compiler-rt-headers) + if (COMPILER_RT_HAS_PROFILE) + list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS profile) + endif() + endif() + if(UNIX) + list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck) + endif() +endif() + +# Run sanitizer tests only if we're sure that clang would produce +# working binaries. +if(COMPILER_RT_CAN_EXECUTE_TESTS) + if(COMPILER_RT_HAS_ASAN) + add_subdirectory(asan) + endif() + if(COMPILER_RT_HAS_DFSAN) + add_subdirectory(dfsan) + endif() + if(COMPILER_RT_HAS_LSAN) + add_subdirectory(lsan) + endif() + if(COMPILER_RT_HAS_MSAN) + add_subdirectory(msan) + endif() + if(COMPILER_RT_HAS_PROFILE) + add_subdirectory(profile) + endif() + if(COMPILER_RT_HAS_SANITIZER_COMMON) + add_subdirectory(sanitizer_common) + endif() + if(COMPILER_RT_HAS_TSAN) + add_subdirectory(tsan) + endif() + if(COMPILER_RT_HAS_UBSAN) + add_subdirectory(ubsan) + endif() +endif() + +if(COMPILER_RT_STANDALONE_BUILD) + # Now that we've traversed all the directories and know all the lit testsuites, + # introduce a rule to run to run all of them. + get_property(LLVM_LIT_TESTSUITES GLOBAL PROPERTY LLVM_LIT_TESTSUITES) + get_property(LLVM_LIT_DEPENDS GLOBAL PROPERTY LLVM_LIT_DEPENDS) + add_lit_target(check-all + "Running all regression tests" + ${LLVM_LIT_TESTSUITES} + DEPENDS ${LLVM_LIT_DEPENDS}) +endif() diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt new file mode 100644 index 000000000000..14f7f506da51 --- /dev/null +++ b/test/asan/CMakeLists.txt @@ -0,0 +1,156 @@ +set(ASAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(ASAN_TESTSUITES) + +macro(get_bits_for_arch arch bits) + if (${arch} STREQUAL "arm" OR + ${arch} STREQUAL "i386" OR + ${arch} STREQUAL "i686" OR + ${arch} STREQUAL "mips") + set(bits 32) + elseif (${arch} STREQUAL "aarch64" OR + ${arch} STREQUAL "x86_64" OR + ${arch} STREQUAL "mips64") + set(bits 64) + else() + message(FATAL_ERROR "Unknown target architecture: ${arch}") + endif() +endmacro() + +# TODO: merge with non-ANDROID case +if(ANDROID) + foreach(arch ${ASAN_SUPPORTED_ARCH}) + set(ASAN_TEST_TARGET_CC ${CMAKE_CXX_COMPILER}) + set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS}) + set(ASAN_TEST_CONFIG_SUFFIX "-${arch}-android") + get_bits_for_arch(${arch} ASAN_TEST_BITS) + set(ASAN_TEST_DYNAMIC True) + set(ASAN_TEST_TARGET_ARCH "${arch}-android") + string(TOUPPER ${arch} ARCH_UPPER_CASE) + set(CONFIG ${ARCH_UPPER_CASE}AndroidConfig) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}) + endforeach() + +else() # Not Android + + if(CAN_TARGET_arm) + # This is only true if we are cross-compiling. + # Build all tests with host compiler and use host tools. + set(ASAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER}) + set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS}) + set(ASAN_TEST_CONFIG_SUFFIX "-arm-linux") + set(ASAN_TEST_BITS "32") + set(ASAN_TEST_DYNAMIC False) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/ARMLinuxConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/ARMLinuxConfig) + endif() + + if(CAN_TARGET_aarch64) + # This is only true if we are cross-compiling. + # Build all tests with host compiler and use host tools. + set(ASAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER}) + set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS}) + set(ASAN_TEST_CONFIG_SUFFIX "-aarch64-linux") + set(ASAN_TEST_BITS "64") + set(ASAN_TEST_DYNAMIC False) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/AArch64LinuxConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/AArch64LinuxConfig) + endif() + + if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64 OR CAN_TARGET_mips64 OR CAN_TARGET_mips64el) + set(ASAN_TEST_CONFIG_SUFFIX "64") + set(ASAN_TEST_BITS "64") + set(ASAN_TEST_TARGET_CFLAGS ${TARGET_64_BIT_CFLAGS}) + set(ASAN_TEST_DYNAMIC False) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig) + if(COMPILER_RT_BUILD_SHARED_ASAN) + set(ASAN_TEST_CONFIG_SUFFIX "64-Dynamic") + set(ASAN_TEST_DYNAMIC True) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic/lit.site.cfg) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic) + endif() + endif() + + if(CAN_TARGET_i386) + set(ASAN_TEST_CONFIG_SUFFIX "32") + set(ASAN_TEST_BITS "32") + set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS}) + set(ASAN_TEST_DYNAMIC False) + set(ASAN_TEST_TARGET_ARCH "i386") + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) + if(COMPILER_RT_BUILD_SHARED_ASAN) + set(ASAN_TEST_CONFIG_SUFFIX "32-Dynamic") + set(ASAN_TEST_DYNAMIC True) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic/lit.site.cfg) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic) + endif() + endif() + + if(CAN_TARGET_mips OR CAN_TARGET_mipsel) + set(ASAN_TEST_CONFIG_SUFFIX "32") + set(ASAN_TEST_BITS "32") + set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS}) + set(ASAN_TEST_DYNAMIC False) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) + if(COMPILER_RT_BUILD_SHARED_ASAN) + set(ASAN_TEST_CONFIG_SUFFIX "32-Dynamic") + set(ASAN_TEST_DYNAMIC True) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic/lit.site.cfg) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic) + endif() + endif() +endif() # Not Android + +if(COMPILER_RT_INCLUDE_TESTS) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) +endif() + +set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(COMPILER_RT_STANDALONE_BUILD) + add_executable(FileCheck IMPORTED GLOBAL) + set_property(TARGET FileCheck PROPERTY IMPORTED_LOCATION ${LLVM_TOOLS_BINARY_DIR}/FileCheck) + list(APPEND ASAN_TEST_DEPS FileCheck) +else() + list(APPEND ASAN_TEST_DEPS asan) +endif() + +# FIXME: support unit test in the android test runner +if(COMPILER_RT_INCLUDE_TESTS AND NOT ANDROID) + list(APPEND ASAN_TEST_DEPS AsanUnitTests) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) +endif() +add_lit_testsuite(check-asan "Running the AddressSanitizer tests" + ${ASAN_TESTSUITES} + DEPENDS ${ASAN_TEST_DEPS}) +set_target_properties(check-asan PROPERTIES FOLDER "ASan tests") diff --git a/test/asan/TestCases/Android/coverage-android.cc b/test/asan/TestCases/Android/coverage-android.cc new file mode 100644 index 000000000000..071a2e3e1faa --- /dev/null +++ b/test/asan/TestCases/Android/coverage-android.cc @@ -0,0 +1,67 @@ +// Test for direct coverage writing with dlopen. + +// Test normal exit. +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %T/libcoverage_android_test_1.so -fPIC +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSO_DIR=\"%device\" %s -o %t + +// RUN: adb shell rm -rf %device/coverage-android +// RUN: rm -rf %T/coverage-android + +// RUN: adb shell mkdir -p %device/coverage-android/direct +// RUN: mkdir -p %T/coverage-android/direct +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android/direct:verbosity=1 %run %t +// RUN: adb pull %device/coverage-android/direct %T/coverage-android/direct +// RUN: ls; pwd +// RUN: cd %T/coverage-android/direct +// RUN: %sancov rawunpack *.sancov.raw +// RUN: %sancov print *.sancov |& FileCheck %s + + +// Test sudden death. +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED -DKILL %s -shared -o %T/libcoverage_android_test_1.so -fPIC +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSO_DIR=\"%device\" %s -o %t + +// RUN: adb shell rm -rf %device/coverage-android-kill +// RUN: rm -rf %T/coverage-android-kill + +// RUN: adb shell mkdir -p %device/coverage-android-kill/direct +// RUN: mkdir -p %T/coverage-android-kill/direct +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android-kill/direct:verbosity=1 not %run %t +// RUN: adb pull %device/coverage-android-kill/direct %T/coverage-android-kill/direct +// RUN: ls; pwd +// RUN: cd %T/coverage-android-kill/direct +// RUN: %sancov rawunpack *.sancov.raw +// RUN: %sancov print *.sancov |& FileCheck %s + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <signal.h> + +#ifdef SHARED +extern "C" { +void bar() { + printf("bar\n"); +#ifdef KILL + kill(getpid(), SIGKILL); +#endif +} +} +#else + +int main(int argc, char **argv) { + fprintf(stderr, "PID: %d\n", getpid()); + void *handle1 = + dlopen(SO_DIR "/libcoverage_android_test_1.so", RTLD_LAZY); + assert(handle1); + void (*bar1)() = (void (*)())dlsym(handle1, "bar"); + assert(bar1); + bar1(); + + return 0; +} +#endif + +// CHECK: 2 PCs total diff --git a/test/asan/TestCases/Android/lit.local.cfg b/test/asan/TestCases/Android/lit.local.cfg new file mode 100644 index 000000000000..42513dd3aa61 --- /dev/null +++ b/test/asan/TestCases/Android/lit.local.cfg @@ -0,0 +1,11 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.android != "TRUE": + config.unsupported = True + +config.substitutions.append( ("%device", "/data/local/tmp/Output") ) diff --git a/test/asan/TestCases/Darwin/asan_gen_prefixes.cc b/test/asan/TestCases/Darwin/asan_gen_prefixes.cc new file mode 100644 index 000000000000..13363ac47255 --- /dev/null +++ b/test/asan/TestCases/Darwin/asan_gen_prefixes.cc @@ -0,0 +1,14 @@ +// Make sure __asan_gen_* strings have the correct prefixes on Darwin +// ("L" in __TEXT,__cstring, "l" in __TEXT,__const + +// RUN: %clang_asan %s -S -o %t.s +// RUN: cat %t.s | FileCheck %s || exit 1 + +int x, y, z; +int main() { return 0; } +// CHECK: .section{{.*}}__TEXT,__const +// CHECK: l___asan_gen_ +// CHECK: .section{{.*}}__TEXT,__cstring,cstring_literals +// CHECK: L___asan_gen_ +// CHECK: L___asan_gen_ +// CHECK: L___asan_gen_ diff --git a/test/asan/TestCases/Darwin/cstring_literals_regtest.mm b/test/asan/TestCases/Darwin/cstring_literals_regtest.mm new file mode 100644 index 000000000000..bcb15d8f39b0 --- /dev/null +++ b/test/asan/TestCases/Darwin/cstring_literals_regtest.mm @@ -0,0 +1,23 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=274. + +// RUN: %clang_asan %s -framework Foundation -o %t +// RUN: %run %t 2>&1 | FileCheck %s +#import <Foundation/Foundation.h> + +#include <stdio.h> + +int main() { + NSString* version_file = @"MAJOR=35\n"; + int major = 0, minor = 0, build = 0, patch = 0; + NSScanner* scanner = [NSScanner scannerWithString:version_file]; + NSString *res = nil; + if ([scanner scanString:@"MAJOR=" intoString:nil] && + [scanner scanInt:&major]) { + res = [NSString stringWithFormat:@"%d.%d.%d.%d", + major, minor, build, patch]; + } + printf("%s\n", [res UTF8String]); + // CHECK: 35.0.0.0 + return 0; +} diff --git a/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc b/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc new file mode 100644 index 000000000000..b1bb4567f900 --- /dev/null +++ b/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc @@ -0,0 +1,33 @@ +// When DYLD-inserting the ASan dylib from a different location than the +// original, make sure we don't try to reexec. + +// RUN: mkdir -p %T/dyld_insert_libraries_reexec +// RUN: cp `%clang_asan %s -fsanitize=address -### 2>&1 \ +// RUN: | grep "libclang_rt.asan_osx_dynamic.dylib" \ +// RUN: | sed -e 's/.*"\(.*libclang_rt.asan_osx_dynamic.dylib\)".*/\1/'` \ +// RUN: %T/dyld_insert_libraries_reexec/libclang_rt.asan_osx_dynamic.dylib +// RUN: %clangxx_asan %s -o %T/dyld_insert_libraries_reexec/a.out +// RUN: DYLD_INSERT_LIBRARIES=@executable_path/libclang_rt.asan_osx_dynamic.dylib \ +// RUN: ASAN_OPTIONS=verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \ +// RUN: | FileCheck %s +// RUN: ASAN_OPTIONS=verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOINSERT %s + +#include <stdio.h> + +int main() { + printf("Passed\n"); + return 0; +} + +// CHECK-NOINSERT: Parsed ASAN_OPTIONS: verbosity=1 +// CHECK-NOINSERT: exec()-ing the program with +// CHECK-NOINSERT: DYLD_INSERT_LIBRARIES +// CHECK-NOINSERT: to enable ASan wrappers. +// CHECK-NOINSERT: Passed + +// CHECK: Parsed ASAN_OPTIONS: verbosity=1 +// CHECK-NOT: exec()-ing the program with +// CHECK-NOT: DYLD_INSERT_LIBRARIES +// CHECK-NOT: to enable ASan wrappers. +// CHECK: Passed diff --git a/test/asan/TestCases/Darwin/interception-in-shared-lib-test.cc b/test/asan/TestCases/Darwin/interception-in-shared-lib-test.cc new file mode 100644 index 000000000000..e472a9dc6972 --- /dev/null +++ b/test/asan/TestCases/Darwin/interception-in-shared-lib-test.cc @@ -0,0 +1,32 @@ +// Check that memset() call from a shared library gets intercepted. +// Please always keep this file in sync with +// ../Linux/interception-in-shared-lib-test.cc. + +// RUN: %clangxx_asan -O0 %s -DSHARED_LIB \ +// RUN: -shared -o %T/libinterception-in-shared-lib-test.so \ +// RUN: -fPIC +// TODO(glider): figure out how to set rpath in a more portable way and unite +// this test with ../Linux/interception-in-shared-lib-test.cc. +// RUN: %clangxx_asan -O0 %s -o %t -Wl,-rpath,@executable-path -L%T -linterception-in-shared-lib-test && \ +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> + +#if defined(SHARED_LIB) +extern "C" +void my_memset(void *p, size_t sz) { + memset(p, 0, sz); +} +#else +extern "C" void my_memset(void *p, size_t sz); + +int main(int argc, char *argv[]) { + char buf[10]; + my_memset(buf, 11); + // CHECK: {{.*ERROR: AddressSanitizer: stack-buffer-overflow}} + // CHECK: {{WRITE of size 11 at 0x.* thread T0}} + // CHECK: {{0x.* in my_memset .*interception-in-shared-lib-test.cc:19}} + return 0; +} +#endif diff --git a/test/asan/TestCases/Darwin/interface_symbols_darwin.c b/test/asan/TestCases/Darwin/interface_symbols_darwin.c new file mode 100644 index 000000000000..8680d678cf91 --- /dev/null +++ b/test/asan/TestCases/Darwin/interface_symbols_darwin.c @@ -0,0 +1,39 @@ +// Check the presence of interface symbols in the ASan runtime dylib. +// If you're changing this file, please also change +// ../Linux/interface_symbols.c + +// RUN: %clang_asan -dead_strip -O2 %s -o %t.exe +// RUN: rm -f %t.symbols %t.interface + +// RUN: nm -g `%clang_asan %s -fsanitize=address -### 2>&1 | grep "libclang_rt.asan_osx_dynamic.dylib" | sed -e 's/.*"\(.*libclang_rt.asan_osx_dynamic.dylib\)".*/\1/'` \ +// RUN: | grep " T " | sed "s/.* T //" \ +// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ +// RUN: | sed -E "s/__asan_init_v[0-9]+/__asan_init/" \ +// RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_on_error" > %t.symbols + +// RUN: cat %p/../../../../lib/asan/asan_interface_internal.h \ +// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ +// RUN: | grep -v "OPTIONAL" \ +// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ +// RUN: > %t.interface +// RUN: echo __asan_report_load1 >> %t.interface +// RUN: echo __asan_report_load2 >> %t.interface +// RUN: echo __asan_report_load4 >> %t.interface +// RUN: echo __asan_report_load8 >> %t.interface +// RUN: echo __asan_report_load16 >> %t.interface +// RUN: echo __asan_report_store1 >> %t.interface +// RUN: echo __asan_report_store2 >> %t.interface +// RUN: echo __asan_report_store4 >> %t.interface +// RUN: echo __asan_report_store8 >> %t.interface +// RUN: echo __asan_report_store16 >> %t.interface +// RUN: echo __asan_report_load_n >> %t.interface +// RUN: echo __asan_report_store_n >> %t.interface +// RUN: echo __asan_get_current_fake_stack >> %t.interface +// RUN: echo __asan_addr_is_in_fake_stack >> %t.interface +// RUN: for i in `jot - 0 10`; do echo __asan_stack_malloc_$i >> %t.interface; done +// RUN: for i in `jot - 0 10`; do echo __asan_stack_free_$i >> %t.interface; done + +// RUN: cat %t.interface | sort -u | diff %t.symbols - + +int main() { return 0; } diff --git a/test/asan/TestCases/Darwin/lit.local.cfg b/test/asan/TestCases/Darwin/lit.local.cfg new file mode 100644 index 000000000000..a85dfcd24c08 --- /dev/null +++ b/test/asan/TestCases/Darwin/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Darwin']: + config.unsupported = True diff --git a/test/asan/TestCases/Darwin/malloc_set_zone_name-mprotect.cc b/test/asan/TestCases/Darwin/malloc_set_zone_name-mprotect.cc new file mode 100644 index 000000000000..2c643bc03c52 --- /dev/null +++ b/test/asan/TestCases/Darwin/malloc_set_zone_name-mprotect.cc @@ -0,0 +1,51 @@ +// Regression test for a bug in malloc_create_zone() +// (https://code.google.com/p/address-sanitizer/issues/detail?id=203) +// The old implementation of malloc_create_zone() didn't always return a +// page-aligned address, so we can only test on a best-effort basis. + +// RUN: %clangxx_asan %s -o %t +// RUN: %run %t 2>&1 + +#include <malloc/malloc.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +const int kNumIter = 4096; +const int kNumZones = 100; +int main() { + char *mem[kNumIter * 2]; + // Allocate memory chunks from different size classes up to 1 page. + // (For the case malloc() returns memory chunks in descending order) + for (int i = 0; i < kNumIter; i++) { + mem[i] = (char*)malloc(8 * i); + } + // Try to allocate a page-aligned malloc zone. Otherwise the mprotect() call + // in malloc_set_zone_name() will silently fail. + malloc_zone_t *zone = NULL; + bool aligned = false; + for (int i = 0; i < kNumZones; i++) { + zone = malloc_create_zone(0, 0); + if (((uintptr_t)zone & (~0xfff)) == (uintptr_t)zone) { + aligned = true; + break; + } + } + if (!aligned) { + printf("Warning: couldn't allocate a page-aligned zone."); + return 0; + } + // malloc_set_zone_name() calls mprotect(zone, 4096, PROT_READ | PROT_WRITE), + // modifies the zone contents and then calls mprotect(zone, 4096, PROT_READ). + malloc_set_zone_name(zone, "foobar"); + // Allocate memory chunks from different size classes again. + for (int i = 0; i < kNumIter; i++) { + mem[i + kNumIter] = (char*)malloc(8 * i); + } + // Access the allocated memory chunks and free them. + for (int i = 0; i < kNumIter * 2; i++) { + memset(mem[i], 'a', 8 * (i % kNumIter)); + free(mem[i]); + } + return 0; +} diff --git a/test/asan/TestCases/Darwin/malloc_zone-protected.cc b/test/asan/TestCases/Darwin/malloc_zone-protected.cc new file mode 100644 index 000000000000..362b60e20b55 --- /dev/null +++ b/test/asan/TestCases/Darwin/malloc_zone-protected.cc @@ -0,0 +1,20 @@ +// Make sure the zones created by malloc_create_zone() are write-protected. +#include <malloc/malloc.h> +#include <stdio.h> + +// RUN: %clangxx_asan %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + + +void *pwn(malloc_zone_t *unused_zone, size_t unused_size) { + printf("PWNED\n"); + return NULL; +} + +int main() { + malloc_zone_t *zone = malloc_create_zone(0, 0); + zone->malloc = pwn; + void *v = malloc_zone_malloc(zone, 1); + // CHECK-NOT: PWNED + return 0; +} diff --git a/test/asan/TestCases/Darwin/objc-odr.mm b/test/asan/TestCases/Darwin/objc-odr.mm new file mode 100644 index 000000000000..72bc39c80dd4 --- /dev/null +++ b/test/asan/TestCases/Darwin/objc-odr.mm @@ -0,0 +1,23 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=360. + +// RUN: %clang_asan %s -o %t -framework Foundation +// RUN: %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +void f() { + int y = 7; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + dispatch_sync(dispatch_get_main_queue(), ^{ + printf("num = %d\n", y); + }); + }); +} + +int main() { + NSLog(@"Hello world"); +} + +// CHECK-NOT: AddressSanitizer: odr-violation +// CHECK: Hello world diff --git a/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc b/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc new file mode 100644 index 000000000000..59ddd634b400 --- /dev/null +++ b/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc @@ -0,0 +1,25 @@ +// Make sure ASan doesn't hang in an exec loop if DYLD_INSERT_LIBRARIES is set. +// This is a regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=159 + +// RUN: %clangxx_asan %s -o %t +// RUN: %clangxx -DSHARED_LIB %s \ +// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib + +// FIXME: the following command line may hang in the case of a regression. +// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \ +// RUN: %run %t 2>&1 | FileCheck %s || exit 1 + +#if !defined(SHARED_LIB) +#include <stdio.h> +#include <stdlib.h> + +int main() { + const char kEnvName[] = "DYLD_INSERT_LIBRARIES"; + printf("%s=%s\n", kEnvName, getenv(kEnvName)); + // CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} +#else // SHARED_LIB +void foo() {} +#endif // SHARED_LIB diff --git a/test/asan/TestCases/Darwin/suppressions-darwin.cc b/test/asan/TestCases/Darwin/suppressions-darwin.cc new file mode 100644 index 000000000000..9a8f56d5dc50 --- /dev/null +++ b/test/asan/TestCases/Darwin/suppressions-darwin.cc @@ -0,0 +1,34 @@ +// Check that without suppressions, we catch the issue. +// RUN: %clangxx_asan -O0 %s -o %t -framework Foundation +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// Check that suppressing the interceptor by name works. +// RUN: echo "interceptor_name:memmove" > %t.supp +// RUN: ASAN_OPTIONS=suppressions=%t.supp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +// Check that suppressing by interceptor name works even without the symbolizer +// RUN: ASAN_OPTIONS=suppressions=%t.supp:symbolize=false %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +// Check that suppressing all reports from a library works. +// RUN: echo "interceptor_via_lib:CoreFoundation" > %t.supp +// RUN: ASAN_OPTIONS=suppressions=%t.supp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +// Check that suppressing library works even without the symbolizer. +// RUN: ASAN_OPTIONS=suppressions=%t.supp:symbolize=false %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +#include <CoreFoundation/CoreFoundation.h> + +int main() { + char *a = (char *)malloc(6); + strcpy(a, "hello"); + CFStringRef str = + CFStringCreateWithBytes(kCFAllocatorDefault, (unsigned char *)a, 10, + kCFStringEncodingUTF8, FALSE); // BOOM + fprintf(stderr, "Ignored.\n"); + free(a); +} + +// CHECK-CRASH: AddressSanitizer: heap-buffer-overflow +// CHECK-CRASH-NOT: Ignored. +// CHECK-IGNORE-NOT: AddressSanitizer: heap-buffer-overflow +// CHECK-IGNORE: Ignored. diff --git a/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc b/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc new file mode 100644 index 000000000000..ed476b223af3 --- /dev/null +++ b/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc @@ -0,0 +1,25 @@ +// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before +// executing other programs. + +// RUN: %clangxx_asan %s -o %t +// RUN: %clangxx %p/../Helpers/echo-env.cc -o %T/echo-env +// RUN: %clangxx -DSHARED_LIB %s \ +// RUN: -dynamiclib -o %t-darwin-dummy-shared-lib-so.dylib + +// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before +// execl(). + +// RUN: %run %t %T/echo-env >/dev/null 2>&1 +// RUN: DYLD_INSERT_LIBRARIES=%t-darwin-dummy-shared-lib-so.dylib \ +// RUN: %run %t %T/echo-env 2>&1 | FileCheck %s || exit 1 + +#if !defined(SHARED_LIB) +#include <unistd.h> +int main(int argc, char *argv[]) { + execl(argv[1], argv[1], "DYLD_INSERT_LIBRARIES", NULL); + // CHECK: {{DYLD_INSERT_LIBRARIES = .*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} +#else // SHARED_LIB +void foo() {} +#endif // SHARED_LIB diff --git a/test/asan/TestCases/Helpers/blacklist-extra.cc b/test/asan/TestCases/Helpers/blacklist-extra.cc new file mode 100644 index 000000000000..627115cdda2b --- /dev/null +++ b/test/asan/TestCases/Helpers/blacklist-extra.cc @@ -0,0 +1,5 @@ +// This function is broken, but this file is blacklisted +int externalBrokenFunction(int argc) { + char x[10] = {0}; + return x[argc * 10]; // BOOM +} diff --git a/test/asan/TestCases/Helpers/echo-env.cc b/test/asan/TestCases/Helpers/echo-env.cc new file mode 100644 index 000000000000..65e91c155c84 --- /dev/null +++ b/test/asan/TestCases/Helpers/echo-env.cc @@ -0,0 +1,19 @@ +// Helper binary for +// lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc +// Prints the environment variable with the given name. +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: %s ENVNAME\n", argv[0]); + exit(1); + } + const char *value = getenv(argv[1]); + if (value) { + printf("%s = %s\n", argv[1], value); + } else { + printf("%s not set.\n", argv[1]); + } + return 0; +} diff --git a/test/asan/TestCases/Helpers/init-order-atexit-extra.cc b/test/asan/TestCases/Helpers/init-order-atexit-extra.cc new file mode 100644 index 000000000000..e4189d19d099 --- /dev/null +++ b/test/asan/TestCases/Helpers/init-order-atexit-extra.cc @@ -0,0 +1,16 @@ +#include <stdio.h> + +class C { + public: + C() { value = 42; } + ~C() { } + int value; +}; + +C c; + +void AccessC() { + printf("C value: %d\n", c.value); +} + +int main() { return 0; } diff --git a/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc b/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc new file mode 100644 index 000000000000..d4606f0afb52 --- /dev/null +++ b/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc @@ -0,0 +1,2 @@ +void *bar(void *input); +void *glob2 = bar((void*)0x2345); diff --git a/test/asan/TestCases/Helpers/initialization-blacklist-extra.cc b/test/asan/TestCases/Helpers/initialization-blacklist-extra.cc new file mode 100644 index 000000000000..09aed2112d5e --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-blacklist-extra.cc @@ -0,0 +1,15 @@ +int zero_init() { return 0; } +int badGlobal = zero_init(); +int readBadGlobal() { return badGlobal; } + +namespace badNamespace { +class BadClass { + public: + BadClass() { value = 0; } + int value; +}; +// Global object with non-trivial constructor. +BadClass bad_object; +} // namespace badNamespace + +int accessBadObject() { return badNamespace::bad_object.value; } diff --git a/test/asan/TestCases/Helpers/initialization-blacklist-extra2.cc b/test/asan/TestCases/Helpers/initialization-blacklist-extra2.cc new file mode 100644 index 000000000000..69455a0a6fc9 --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-blacklist-extra2.cc @@ -0,0 +1,4 @@ +int zero_init(); +int badSrcGlobal = zero_init(); +int readBadSrcGlobal() { return badSrcGlobal; } + diff --git a/test/asan/TestCases/Helpers/initialization-blacklist.txt b/test/asan/TestCases/Helpers/initialization-blacklist.txt new file mode 100644 index 000000000000..83294635622d --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-blacklist.txt @@ -0,0 +1,3 @@ +global:*badGlobal*=init +type:*badNamespace::BadClass*=init +src:*initialization-blacklist-extra2.cc=init diff --git a/test/asan/TestCases/Helpers/initialization-bug-extra.cc b/test/asan/TestCases/Helpers/initialization-bug-extra.cc new file mode 100644 index 000000000000..3c4cb411defa --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-bug-extra.cc @@ -0,0 +1,5 @@ +// This file simply declares a dynamically initialized var by the name of 'y'. +int initY() { + return 5; +} +int y = initY(); diff --git a/test/asan/TestCases/Helpers/initialization-bug-extra2.cc b/test/asan/TestCases/Helpers/initialization-bug-extra2.cc new file mode 100644 index 000000000000..a3d8f190e58b --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-bug-extra2.cc @@ -0,0 +1,6 @@ +// 'z' is dynamically initialized global from different TU. +extern int z; +int __attribute__((noinline)) initY() { + return z + 1; +} +int y = initY(); diff --git a/test/asan/TestCases/Helpers/initialization-constexpr-extra.cc b/test/asan/TestCases/Helpers/initialization-constexpr-extra.cc new file mode 100644 index 000000000000..b32466a981b3 --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-constexpr-extra.cc @@ -0,0 +1,3 @@ +// Constexpr: +int getCoolestInteger(); +static int coolest_integer = getCoolestInteger(); diff --git a/test/asan/TestCases/Helpers/initialization-nobug-extra.cc b/test/asan/TestCases/Helpers/initialization-nobug-extra.cc new file mode 100644 index 000000000000..886165affd76 --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-nobug-extra.cc @@ -0,0 +1,9 @@ +// Linker initialized: +int getAB(); +static int ab = getAB(); +// Function local statics: +int countCalls(); +static int one = countCalls(); +// Trivial constructor, non-trivial destructor: +int getStructWithDtorValue(); +static int val = getStructWithDtorValue(); diff --git a/test/asan/TestCases/Helpers/lit.local.cfg b/test/asan/TestCases/Helpers/lit.local.cfg new file mode 100644 index 000000000000..2fc4d99456b0 --- /dev/null +++ b/test/asan/TestCases/Helpers/lit.local.cfg @@ -0,0 +1,3 @@ +# Sources in this directory are helper files for tests which test functionality +# involving multiple translation units. +config.suffixes = [] diff --git a/test/asan/TestCases/Linux/asan-asm-stacktrace-test.cc b/test/asan/TestCases/Linux/asan-asm-stacktrace-test.cc new file mode 100644 index 000000000000..5332c992a0db --- /dev/null +++ b/test/asan/TestCases/Linux/asan-asm-stacktrace-test.cc @@ -0,0 +1,33 @@ +// Check that a stack unwinding algorithm works corretly even with the assembly +// instrumentation. + +// REQUIRES: x86_64-supported-target +// RUN: %clangxx_asan -g -O1 %s -fno-inline-functions -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -mllvm -asan-instrument-assembly -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -g -O1 %s -fno-inline-functions -fomit-frame-pointer -momit-leaf-frame-pointer -mllvm -asan-instrument-assembly -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -g0 -O1 %s -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-exceptions -fno-inline-functions -fomit-frame-pointer -momit-leaf-frame-pointer -mllvm -asan-instrument-assembly -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nounwind + +#include <cstddef> + +// CHECK: READ of size 4 +// CHECK-NEXT: {{#0 0x[0-9a-fA-F]+ in foo}} +// CHECK-NEXT: {{#1 0x[0-9a-fA-F]+ in main}} + +// CHECK-nounwind: READ of size 4 +// CHECK-nounwind-NEXT: {{#0 0x[0-9a-fA-F]+ in foo}} + +__attribute__((noinline)) int foo(size_t n, int *buffer) { + int r; + __asm__("movl (%[buffer], %[n], 4), %[r] \n\t" + : [r] "=r"(r) + : [buffer] "r"(buffer), [n] "r"(n) + : "memory"); + return r; +} + +int main() { + const size_t n = 16; + int *buffer = new int[n]; + foo(n, buffer); + delete[] buffer; + return 0; +} diff --git a/test/asan/TestCases/Linux/asan_dlopen_test.cc b/test/asan/TestCases/Linux/asan_dlopen_test.cc new file mode 100644 index 000000000000..f1e31b0a0553 --- /dev/null +++ b/test/asan/TestCases/Linux/asan_dlopen_test.cc @@ -0,0 +1,15 @@ +// Test that dlopen of dynamic runtime is prohibited. +// +// RUN: %clangxx %s -DRT=\"%shared_libasan\" -o %t -ldl +// RUN: not %run %t 2>&1 | FileCheck %s +// REQUIRES: asan-dynamic-runtime +// XFAIL: android + +#include <dlfcn.h> + +int main(int argc, char **argv) { + dlopen(RT, RTLD_LAZY); + return 0; +} + +// CHECK: ASan runtime does not come first in initial library list diff --git a/test/asan/TestCases/Linux/asan_prelink_test.cc b/test/asan/TestCases/Linux/asan_prelink_test.cc new file mode 100644 index 000000000000..6145c01f7342 --- /dev/null +++ b/test/asan/TestCases/Linux/asan_prelink_test.cc @@ -0,0 +1,29 @@ +// Test if asan works with prelink. +// It does not actually use prelink, but relies on ld's flag -Ttext-segment +// or gold's flag -Ttext (we try the first flag first, if that fails we +// try the second flag). +// +// RUN: %clangxx_asan -c %s -o %t.o +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\ +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000 +// RUN: %clangxx_asan %t.o %t.so -Wl,-R. -o %t +// RUN: ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s + +// GNU driver doesn't handle .so files properly. +// REQUIRES: x86_64-supported-target, asan-64-bits, Clang +#if BUILD_SO +int G; +int *getG() { + return &G; +} +#else +#include <stdio.h> +extern int *getG(); +int main(int argc, char **argv) { + long p = (long)getG(); + printf("SO mapped at %lx\n", p & ~0xffffffffUL); + *getG() = 0; +} +#endif +// CHECK: 0x003000000000, 0x004fffffffff{{.*}} MidMem +// CHECK: SO mapped at 3600000000 diff --git a/test/asan/TestCases/Linux/asan_preload_test-1.cc b/test/asan/TestCases/Linux/asan_preload_test-1.cc new file mode 100644 index 000000000000..e5eab5545b83 --- /dev/null +++ b/test/asan/TestCases/Linux/asan_preload_test-1.cc @@ -0,0 +1,30 @@ +// Test that non-sanitized executables work with sanitized shared libs +// and preloaded runtime. +// +// RUN: %clangxx -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: %clangxx %s %t.so -o %t +// +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s + +// REQUIRES: asan-dynamic-runtime + +// This way of setting LD_PRELOAD does not work with Android test runner. +// REQUIRES: not-android + +#if BUILD_SO +char dummy; +void do_access(const void *p) { + // CHECK: AddressSanitizer: heap-buffer-overflow + dummy = ((const char *)p)[1]; +} +#else +#include <stdlib.h> +extern void do_access(const void *p); +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + free(p); + return 0; +} +#endif diff --git a/test/asan/TestCases/Linux/asan_preload_test-2.cc b/test/asan/TestCases/Linux/asan_preload_test-2.cc new file mode 100644 index 000000000000..00b32e15d17d --- /dev/null +++ b/test/asan/TestCases/Linux/asan_preload_test-2.cc @@ -0,0 +1,24 @@ +// Test that preloaded runtime works with unsanitized executables. +// +// RUN: %clangxx %s -o %t +// RUN: LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s + +// REQUIRES: asan-dynamic-runtime + +// This way of setting LD_PRELOAD does not work with Android test runner. +// REQUIRES: not-android + +#include <stdlib.h> + +extern "C" void *memset(void *p, int val, size_t n); + +void do_access(void *p) { + // CHECK: AddressSanitizer: heap-buffer-overflow + memset(p, 0, 2); +} + +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + return 0; +} diff --git a/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc b/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc new file mode 100644 index 000000000000..30f1c17700c8 --- /dev/null +++ b/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc @@ -0,0 +1,13 @@ +// Test that preloading dynamic runtime to statically sanitized +// executable is prohibited. +// +// RUN: %clangxx_asan_static %s -o %t +// RUN: LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s + +// REQUIRES: asan-dynamic-runtime +// XFAIL: android + +#include <stdlib.h> +int main(int argc, char **argv) { return 0; } + +// CHECK: Your application is linked against incompatible ASan runtimes diff --git a/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc b/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc new file mode 100644 index 000000000000..4c935e2b0f3b --- /dev/null +++ b/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc @@ -0,0 +1,25 @@ +// Test that mixed static/dynamic sanitization of program objects +// is prohibited. +// +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: %clangxx_asan_static %s %t.so -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +// REQUIRES: asan-dynamic-runtime +// XFAIL: android + +#if BUILD_SO +char dummy; +void do_access(const void *p) { dummy = ((const char *)p)[1]; } +#else +#include <stdlib.h> +extern void do_access(const void *p); +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + free(p); + return 0; +} +#endif + +// CHECK: Your application is linked against incompatible ASan runtimes diff --git a/test/asan/TestCases/Linux/clang_gcc_abi.cc b/test/asan/TestCases/Linux/clang_gcc_abi.cc new file mode 100644 index 000000000000..e833881661d2 --- /dev/null +++ b/test/asan/TestCases/Linux/clang_gcc_abi.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_asan -O0 -x c %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 -x c %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 -x c %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 -x c %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: arm-supported-target +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <stdlib.h> + +int boom() { + volatile int three = 3; + char *s = (char *)malloc(three); +// CHECK: #1 0x{{.*}} in boom {{.*}}clang_gcc_abi.cc:[[@LINE-1]] + return s[three]; //BOOM +} + +__attribute__((naked, noinline)) void gcc_abi() { +// CHECK: #2 0x{{.*}} in gcc_abi {{.*}}clang_gcc_abi.cc:[[@LINE+1]] + asm volatile("str fp, [sp, #-8]!\n\t" + "str lr, [sp, #4]\n\t" + "add fp, sp, #4\n\t" + "bl boom\n\t" + "sub sp, fp, #4\n\t" + "ldr fp, [sp]\n\t" + "add sp, sp, #4\n\t" + "ldr pc, [sp], #4\n\t" + ); +} + +__attribute__((naked, noinline)) void clang_abi() { +// CHECK: #3 0x{{.*}} in clang_abi {{.*}}clang_gcc_abi.cc:[[@LINE+1]] + asm volatile("push {r11, lr}\n\t" + "mov r11, sp\n\t" + "bl gcc_abi\n\t" + "add r0, r0, #1\n\t" + "pop {r11, pc}\n\t" + ); +} + +int main() { + clang_abi(); +// CHECK: #4 0x{{.*}} in main {{.*}}clang_gcc_abi.cc:[[@LINE-1]] +} diff --git a/test/asan/TestCases/Linux/clone_test.cc b/test/asan/TestCases/Linux/clone_test.cc new file mode 100644 index 000000000000..e9c1f166eb45 --- /dev/null +++ b/test/asan/TestCases/Linux/clone_test.cc @@ -0,0 +1,45 @@ +// Regression test for: +// http://code.google.com/p/address-sanitizer/issues/detail?id=37 + +// RUN: %clangxx_asan -O0 %s -o %t && %run %t | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %run %t | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %run %t | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %run %t | FileCheck %s +// XFAIL: arm-linux-gnueabi + +#include <stdio.h> +#include <sched.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +int Child(void *arg) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + _exit(1); // NoReturn, stack will remain unpoisoned unless we do something. +} + +int main(int argc, char **argv) { + const int kStackSize = 1 << 20; + char child_stack[kStackSize + 1]; + char *sp = child_stack + kStackSize; // Stack grows down. + printf("Parent: %p\n", sp); + pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL); + int status; + pid_t wait_result = waitpid(clone_pid, &status, __WCLONE); + if (wait_result < 0) { + perror("waitpid"); + return 0; + } + if (wait_result == clone_pid && WIFEXITED(status)) { + // Make sure the child stack was indeed unpoisoned. + for (int i = 0; i < kStackSize; i++) + child_stack[i] = i; + int ret = child_stack[argc - 1]; + printf("PASSED\n"); + // CHECK: PASSED + return ret; + } + return 0; +} diff --git a/test/asan/TestCases/Linux/coverage-and-lsan.cc b/test/asan/TestCases/Linux/coverage-and-lsan.cc new file mode 100644 index 000000000000..4cb8e2af3084 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-and-lsan.cc @@ -0,0 +1,20 @@ +// Make sure coverage is dumped even if there are reported leaks. +// +// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// +// RUN: rm -rf %T/coverage-and-lsan +// +// RUN: mkdir -p %T/coverage-and-lsan/normal +// RUN: ASAN_OPTIONS=coverage=1:coverage_dir=%T/coverage-and-lsan:verbosity=1 not %run %t 2>&1 | FileCheck %s +// RUN: %sancov print %T/coverage-and-lsan/*.sancov 2>&1 +// +// REQUIRES: leak-detection + +int *g = new int; +int main(int argc, char **argv) { + g = 0; + return 0; +} + +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: CovDump: diff --git a/test/asan/TestCases/Linux/coverage-caller-callee-total-count.cc b/test/asan/TestCases/Linux/coverage-caller-callee-total-count.cc new file mode 100644 index 000000000000..0201425106f9 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-caller-callee-total-count.cc @@ -0,0 +1,41 @@ +// Test __sanitizer_get_total_unique_coverage for caller-callee coverage + +// RUN: %clangxx_asan -fsanitize-coverage=4 %s -o %t +// RUN: ASAN_OPTIONS=coverage=1 %run %t +// RUN: rm -f caller-callee*.sancov +// +// REQUIRES: asan-64-bits + +#include <sanitizer/common_interface_defs.h> +#include <stdio.h> +#include <assert.h> +int P = 0; +struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}}; +struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; + +Foo *foo[3] = {new Foo, new Foo1, new Foo2}; + +uintptr_t CheckNewTotalUniqueCoverageIsLargerAndReturnIt(uintptr_t old_total) { + uintptr_t new_total = __sanitizer_get_total_unique_coverage(); + assert(new_total > old_total); + return new_total; +} + +int main(int argc, char **argv) { + uintptr_t total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(0); + foo[0]->f(); + total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); + foo[1]->f(); + total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); + foo[2]->f(); + total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); + // Ok, called every function once. + // Now call them again from another call site. Should get new coverage. + foo[0]->f(); + total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); + foo[1]->f(); + total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); + foo[2]->f(); + total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); +} diff --git a/test/asan/TestCases/Linux/coverage-caller-callee.cc b/test/asan/TestCases/Linux/coverage-caller-callee.cc new file mode 100644 index 000000000000..cd318962b8e0 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-caller-callee.cc @@ -0,0 +1,74 @@ +// Test caller-callee coverage with large number of threads +// and various numbers of callers and callees. + +// RUN: %clangxx_asan -fsanitize-coverage=4 %s -o %t +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 10 1 2>&1 | FileCheck %s --check-prefix=CHECK-10-1 +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 9 2 2>&1 | FileCheck %s --check-prefix=CHECK-9-2 +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 7 3 2>&1 | FileCheck %s --check-prefix=CHECK-7-3 +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 17 1 2>&1 | FileCheck %s --check-prefix=CHECK-17-1 +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 15 2 2>&1 | FileCheck %s --check-prefix=CHECK-15-2 +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 18 3 2>&1 | FileCheck %s --check-prefix=CHECK-18-3 +// RUN: rm -f caller-callee*.sancov +// +// REQUIRES: asan-64-bits +// +// CHECK-10-1: CovDump: 10 caller-callee pairs written +// CHECK-9-2: CovDump: 18 caller-callee pairs written +// CHECK-7-3: CovDump: 21 caller-callee pairs written +// CHECK-17-1: CovDump: 14 caller-callee pairs written +// CHECK-15-2: CovDump: 28 caller-callee pairs written +// CHECK-18-3: CovDump: 42 caller-callee pairs written + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +int P = 0; +struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}}; +struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo3 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo4 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo5 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo6 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo7 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo8 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo9 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo10 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo11 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo12 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo13 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo14 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo15 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo16 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo17 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo18 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; +struct Foo19 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; + +Foo *foo[20] = { + new Foo, new Foo1, new Foo2, new Foo3, new Foo4, new Foo5, new Foo6, + new Foo7, new Foo8, new Foo9, new Foo10, new Foo11, new Foo12, new Foo13, + new Foo14, new Foo15, new Foo16, new Foo17, new Foo18, new Foo19, +}; + +int n_functions = 10; +int n_callers = 2; + +void *Thread(void *arg) { + if (n_callers >= 1) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f(); + if (n_callers >= 2) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f(); + if (n_callers >= 3) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f(); + return arg; +} + +int main(int argc, char **argv) { + if (argc >= 2) + n_functions = atoi(argv[1]); + if (argc >= 3) + n_callers = atoi(argv[2]); + const int kNumThreads = 16; + pthread_t t[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) + pthread_create(&t[i], 0, Thread, 0); + for (int i = 0; i < kNumThreads; i++) + pthread_join(t[i], 0); +} diff --git a/test/asan/TestCases/Linux/coverage-direct-large.cc b/test/asan/TestCases/Linux/coverage-direct-large.cc new file mode 100644 index 000000000000..78aa68621ad1 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-direct-large.cc @@ -0,0 +1,45 @@ +// Test for direct coverage writing with lots of data. +// Current implementation maps output file in chunks of 64K. This test overflows +// 1 chunk. +// RUN: %clangxx_asan -fsanitize-coverage=1 -O0 %s -o %t + +// RUN: rm -rf %T/coverage-direct-large + +// RUN: mkdir -p %T/coverage-direct-large/normal && cd %T/coverage-direct-large/normal +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=0:verbosity=1 %run %t +// RUN: %sancov print *.sancov >out.txt +// RUN: cd ../.. + +// RUN: mkdir -p %T/coverage-direct-large/direct && cd %T/coverage-direct-large/direct +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:verbosity=1 %run %t +// RUN: %sancov rawunpack *.sancov.raw +// RUN: %sancov print *.sancov >out.txt +// RUN: cd ../.. + +// RUN: diff -u coverage-direct-large/normal/out.txt coverage-direct-large/direct/out.txt +// +// XFAIL: android + +#define F0(Q, x) Q(x) +#define F1(Q, x) \ + F0(Q, x##0) F0(Q, x##1) F0(Q, x##2) F0(Q, x##3) F0(Q, x##4) F0(Q, x##5) \ + F0(Q, x##6) F0(Q, x##7) F0(Q, x##8) F0(Q, x##9) +#define F2(Q, x) \ + F1(Q, x##0) F1(Q, x##1) F1(Q, x##2) F1(Q, x##3) F1(Q, x##4) F1(Q, x##5) \ + F1(Q, x##6) F1(Q, x##7) F1(Q, x##8) F1(Q, x##9) +#define F3(Q, x) \ + F2(Q, x##0) F2(Q, x##1) F2(Q, x##2) F2(Q, x##3) F2(Q, x##4) F2(Q, x##5) \ + F2(Q, x##6) F2(Q, x##7) F2(Q, x##8) F2(Q, x##9) +#define F4(Q, x) \ + F3(Q, x##0) F3(Q, x##1) F3(Q, x##2) F3(Q, x##3) F3(Q, x##4) F3(Q, x##5) \ + F3(Q, x##6) F3(Q, x##7) F3(Q, x##8) F3(Q, x##9) + +#define DECL(x) __attribute__((noinline)) void x() {} +#define CALL(x) x(); + +F4(DECL, f) + +int main(void) { + F4(CALL, f) + return 0; +} diff --git a/test/asan/TestCases/Linux/coverage-direct.cc b/test/asan/TestCases/Linux/coverage-direct.cc new file mode 100644 index 000000000000..2cc1aed0a0fa --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-direct.cc @@ -0,0 +1,44 @@ +// Test for direct coverage writing with dlopen. +// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_direct_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=1 -DSO_DIR=\"%T\" %s -o %t + +// RUN: rm -rf %T/coverage-direct + +// RUN: mkdir -p %T/coverage-direct/normal +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=0:coverage_dir=%T/coverage-direct/normal:verbosity=1 %run %t +// RUN: %sancov print %T/coverage-direct/normal/*.sancov >%T/coverage-direct/normal/out.txt + +// RUN: mkdir -p %T/coverage-direct/direct +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%T/coverage-direct/direct:verbosity=1 %run %t +// RUN: cd %T/coverage-direct/direct +// RUN: %sancov rawunpack *.sancov.raw +// RUN: %sancov print *.sancov >out.txt +// RUN: cd ../.. + +// RUN: diff -u coverage-direct/normal/out.txt coverage-direct/direct/out.txt +// +// XFAIL: android + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <unistd.h> + +#ifdef SHARED +extern "C" { +void bar() { printf("bar\n"); } +} +#else + +int main(int argc, char **argv) { + fprintf(stderr, "PID: %d\n", getpid()); + void *handle1 = + dlopen(SO_DIR "/libcoverage_direct_test_1.so", RTLD_LAZY); + assert(handle1); + void (*bar1)() = (void (*)())dlsym(handle1, "bar"); + assert(bar1); + bar1(); + + return 0; +} +#endif diff --git a/test/asan/TestCases/Linux/coverage-disabled.cc b/test/asan/TestCases/Linux/coverage-disabled.cc new file mode 100644 index 000000000000..a75b26dc02e9 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-disabled.cc @@ -0,0 +1,18 @@ +// Test that no data is collected without a runtime flag. +// +// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// +// RUN: rm -rf %T/coverage-disabled +// +// RUN: mkdir -p %T/coverage-disabled/normal +// RUN: ASAN_OPTIONS=coverage_direct=0:coverage_dir=%T/coverage-disabled/normal:verbosity=1 %run %t +// RUN: not %sancov print %T/coverage-disabled/normal/*.sancov 2>&1 +// +// RUN: mkdir -p %T/coverage-disabled/direct +// RUN: ASAN_OPTIONS=coverage_direct=1:coverage_dir=%T/coverage-disabled/direct:verbosity=1 %run %t +// RUN: cd %T/coverage-disabled/direct +// RUN: not %sancov rawunpack *.sancov + +int main(int argc, char **argv) { + return 0; +} diff --git a/test/asan/TestCases/Linux/coverage-fork-direct.cc b/test/asan/TestCases/Linux/coverage-fork-direct.cc new file mode 100644 index 000000000000..51cbbd821b8e --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-fork-direct.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// RUN: rm -rf %T/coverage-fork-direct +// RUN: mkdir -p %T/coverage-fork-direct && cd %T/coverage-fork-direct +// RUN: (ASAN_OPTIONS=coverage=1:coverage_direct=1:verbosity=1 %run %t; \ +// RUN: %sancov rawunpack *.sancov.raw; %sancov print *.sancov) 2>&1 +// +// XFAIL: android + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +__attribute__((noinline)) +void foo() { printf("foo\n"); } + +__attribute__((noinline)) +void bar() { printf("bar\n"); } + +__attribute__((noinline)) +void baz() { printf("baz\n"); } + +int main(int argc, char **argv) { + pid_t child_pid = fork(); + if (child_pid == 0) { + fprintf(stderr, "Child PID: %d\n", getpid()); + baz(); + } else { + fprintf(stderr, "Parent PID: %d\n", getpid()); + foo(); + bar(); + } + return 0; +} + +// CHECK-DAG: Child PID: [[ChildPID:[0-9]+]] +// CHECK-DAG: Parent PID: [[ParentPID:[0-9]+]] +// CHECK-DAG: read 3 PCs from {{.*}}.[[ParentPID]].sancov +// CHECK-DAG: read 1 PCs from {{.*}}.[[ChildPID]].sancov diff --git a/test/asan/TestCases/Linux/coverage-fork.cc b/test/asan/TestCases/Linux/coverage-fork.cc new file mode 100644 index 000000000000..38c200942609 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-fork.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// RUN: export ASAN_OPTIONS=coverage=1:coverage_direct=0:verbosity=1 +// RUN: rm -rf %T/coverage-fork +// RUN: mkdir -p %T/coverage-fork && cd %T/coverage-fork +// RUN: %run %t 2>&1 | FileCheck %s +// +// XFAIL: android + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +__attribute__((noinline)) +void foo() { printf("foo\n"); } + +__attribute__((noinline)) +void bar() { printf("bar\n"); } + +__attribute__((noinline)) +void baz() { printf("baz\n"); } + +int main(int argc, char **argv) { + pid_t child_pid = fork(); + if (child_pid == 0) { + fprintf(stderr, "Child PID: %d\n", getpid()); + baz(); + } else { + fprintf(stderr, "Parent PID: %d\n", getpid()); + foo(); + bar(); + } + return 0; +} + +// CHECK-DAG: Child PID: [[ChildPID:[0-9]+]] +// CHECK-DAG: [[ChildPID]].sancov: 1 PCs written +// CHECK-DAG: Parent PID: [[ParentPID:[0-9]+]] +// CHECK-DAG: [[ParentPID]].sancov: 3 PCs written diff --git a/test/asan/TestCases/Linux/coverage-levels.cc b/test/asan/TestCases/Linux/coverage-levels.cc new file mode 100644 index 000000000000..748ef1f08db5 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-levels.cc @@ -0,0 +1,20 @@ +// Test various levels of coverage +// +// RUN: %clangxx_asan -O1 -fsanitize-coverage=1 %s -o %t +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: %clangxx_asan -O1 -fsanitize-coverage=2 %s -o %t +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: %clangxx_asan -O1 -fsanitize-coverage=3 %s -o %t +// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 +// +// REQUIRES: asan-64-bits + +volatile int sink; +int main(int argc, char **argv) { + if (argc == 0) + sink = 0; +} + +// CHECK1: 1 PCs written +// CHECK2: 2 PCs written +// CHECK3: 3 PCs written diff --git a/test/asan/TestCases/Linux/coverage-maybe-open-file.cc b/test/asan/TestCases/Linux/coverage-maybe-open-file.cc new file mode 100644 index 000000000000..4664cef7f5af --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-maybe-open-file.cc @@ -0,0 +1,31 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// RUN: rm -rf %T/coverage-maybe-open-file +// RUN: mkdir -p %T/coverage-maybe-open-file && cd %T/coverage-maybe-open-file +// RUN: ASAN_OPTIONS=coverage=1 %run %t | FileCheck %s --check-prefix=CHECK-success +// RUN: ASAN_OPTIONS=coverage=0 %run %t | FileCheck %s --check-prefix=CHECK-fail +// RUN: [ "$(cat test.sancov.packed)" == "test" ] +// RUN: cd .. && rm -rf %T/coverage-maybe-open-file + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <sanitizer/common_interface_defs.h> + +int main(int argc, char **argv) { + int fd = __sanitizer_maybe_open_cov_file("test"); + if (fd > 0) { + printf("SUCCESS\n"); + const char s[] = "test\n"; + write(fd, s, strlen(s)); + close(fd); + } else { + printf("FAIL\n"); + } +} + +// CHECK-success: SUCCESS +// CHECK-fail: FAIL diff --git a/test/asan/TestCases/Linux/coverage-module-unloaded.cc b/test/asan/TestCases/Linux/coverage-module-unloaded.cc new file mode 100644 index 000000000000..449841e78189 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-module-unloaded.cc @@ -0,0 +1,56 @@ +// Check that unloading a module doesn't break coverage dumping for remaining +// modules. +// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_module_unloaded_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_module_unloaded_test_2.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=1 -DSO_DIR=\"%T\" %s -o %t +// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 +// RUN: mkdir -p %T/coverage-module-unloaded && cd %T/coverage-module-unloaded +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %run %t foo 2>&1 | FileCheck %s +// RUN: cd .. && rm coverage-module-unloaded -r +// +// https://code.google.com/p/address-sanitizer/issues/detail?id=263 +// XFAIL: android + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <unistd.h> + +#ifdef SHARED +extern "C" { +void bar() { printf("bar\n"); } +} +#else + +int main(int argc, char **argv) { + fprintf(stderr, "PID: %d\n", getpid()); + void *handle1 = + dlopen(SO_DIR "/libcoverage_module_unloaded_test_1.so", RTLD_LAZY); + assert(handle1); + void (*bar1)() = (void (*)())dlsym(handle1, "bar"); + assert(bar1); + bar1(); + void *handle2 = + dlopen(SO_DIR "/libcoverage_module_unloaded_test_2.so", RTLD_LAZY); + assert(handle2); + void (*bar2)() = (void (*)())dlsym(handle2, "bar"); + assert(bar2); + bar2(); + + // It matters whether the unloaded module has a higher or lower address range + // than the remaining one. Make sure to test both cases. + if (argc < 2) + dlclose(bar1 < bar2 ? handle1 : handle2); + else + dlclose(bar1 < bar2 ? handle2 : handle1); + return 0; +} +#endif + +// CHECK: PID: [[PID:[0-9]+]] +// CHECK: [[PID]].sancov: 1 PCs written +// CHECK: .so.[[PID]] +// If we get coverage for both DSOs, it means the module wasn't unloaded and +// this test is useless. +// CHECK-NOT: .so.[[PID]] diff --git a/test/asan/TestCases/Linux/coverage-sandboxing.cc b/test/asan/TestCases/Linux/coverage-sandboxing.cc new file mode 100644 index 000000000000..56f9c40f4cc0 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-sandboxing.cc @@ -0,0 +1,85 @@ +// RUN: %clangxx_asan -fsanitize-coverage=2 -DSHARED %s -shared -o %T/libcoverage_sandboxing_test.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_sandboxing_test +// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 +// RUN: rm -rf %T/coverage_sandboxing_test +// RUN: mkdir %T/coverage_sandboxing_test && cd %T/coverage_sandboxing_test +// RUN: mkdir vanilla && cd vanilla +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-vanilla +// RUN: mkdir ../sandbox1 && cd ../sandbox1 +// RUN: %run %t a 2>&1 | FileCheck %s --check-prefix=CHECK-sandbox +// RUN: %sancov unpack coverage_sandboxing_test.sancov.packed +// RUN: mkdir ../sandbox2 && cd ../sandbox2 +// RUN: %run %t a b 2>&1 | FileCheck %s --check-prefix=CHECK-sandbox +// RUN: %sancov unpack coverage_sandboxing_test.sancov.packed +// RUN: cd .. +// RUN: %sancov print vanilla/libcoverage_sandboxing_test.so.*.sancov > vanilla.txt +// RUN: %sancov print sandbox1/libcoverage_sandboxing_test.so.*.sancov > sandbox1.txt +// RUN: %sancov print sandbox2/libcoverage_sandboxing_test.so.*.sancov > sandbox2.txt +// RUN: diff vanilla.txt sandbox1.txt +// RUN: diff vanilla.txt sandbox2.txt +// RUN: cd ../ && rm coverage_sandboxing_test -r +// https://code.google.com/p/address-sanitizer/issues/detail?id=263 +// XFAIL: android + +#include <assert.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <sanitizer/common_interface_defs.h> + +#define bb0(n) \ + case n: \ + fprintf(stderr, "foo: %d\n", n); \ + break; + +#define bb1(n) bb0(n) bb0(n + 1) +#define bb2(n) bb1(n) bb1(n + 2) +#define bb3(n) bb2(n) bb2(n + 4) +#define bb4(n) bb3(n) bb3(n + 8) +#define bb5(n) bb4(n) bb4(n + 16) +#define bb6(n) bb5(n) bb5(n + 32) +#define bb7(n) bb6(n) bb6(n + 64) +#define bb8(n) bb7(n) bb7(n + 128) + +#ifdef SHARED +void foo(int i) { + switch(i) { + // 256 basic blocks + bb8(0) + } +} +#else +extern void foo(int i); + +int main(int argc, char **argv) { + assert(argc <= 3); + for (int i = 0; i < 256; i++) foo(i); + fprintf(stderr, "PID: %d\n", getpid()); + if (argc == 1) { + // Vanilla mode, dump to individual files. + return 0; + } + // Dump to packed file. + int fd = creat("coverage_sandboxing_test.sancov.packed", 0660); + __sanitizer_sandbox_arguments args = {0}; + args.coverage_sandboxed = 1; + args.coverage_fd = fd; + if (argc == 2) + // Write to packed file, do not split into blocks. + args.coverage_max_block_size = 0; + else if (argc == 3) + // Write to packed file, split into blocks (as if writing to a socket). + args.coverage_max_block_size = 100; + __sanitizer_sandbox_on_notify(&args); + return 0; +} +#endif + +// CHECK-vanilla: PID: [[PID:[0-9]+]] +// CHECK-vanilla: [[PID]].sancov: 1 PCs written +// CHECK-vanilla: .so.[[PID]].sancov: 258 PCs written + +// CHECK-sandbox: PID: [[PID:[0-9]+]] +// CHECK-sandbox: 258 PCs written to packed file diff --git a/test/asan/TestCases/Linux/coverage-tracing.cc b/test/asan/TestCases/Linux/coverage-tracing.cc new file mode 100644 index 000000000000..89ab0d283add --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-tracing.cc @@ -0,0 +1,22 @@ +// Test -mllvm -sanitizer-coverage-experimental-tracing +// +// RUN: %clangxx_asan -O1 -fsanitize-coverage=1 -mllvm -sanitizer-coverage-experimental-tracing %s -o %t +// RUN: rm -rf %T/coverage-tracing +// RUN: mkdir -p %T/coverage-tracing +// RUN: ASAN_OPTIONS=coverage=1:coverage_dir=%T/coverage-tracing:verbosity=1 %run %t 1 2 3 4 2>&1 | FileCheck %s +// RUN: rm -rf %T/coverage-tracing +// +// REQUIRES: asan-64-bits + +volatile int sink; +int main(int argc, char **argv) { + volatile int i = 0; + do { + sink = 0; + i++; + } while (i < argc); + return 0; +} + +// CHECK: CovDump: Trace: {{[3-9]}} PCs written +// CHECK: CovDump: Trace: {{[6-9]}} Events written diff --git a/test/asan/TestCases/Linux/coverage.cc b/test/asan/TestCases/Linux/coverage.cc new file mode 100644 index 000000000000..f6eb0ae9285b --- /dev/null +++ b/test/asan/TestCases/Linux/coverage.cc @@ -0,0 +1,71 @@ +// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_test.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_test +// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 +// RUN: mkdir -p %T/coverage && cd %T/coverage +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-main +// RUN: %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo +// RUN: %run %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar +// RUN: %run %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar +// RUN: not %run %t foo bar 4 2>&1 | FileCheck %s --check-prefix=CHECK-report +// RUN: not %run %t foo bar 4 5 2>&1 | FileCheck %s --check-prefix=CHECK-segv +// RUN: cd .. && rm coverage -r +// +// https://code.google.com/p/address-sanitizer/issues/detail?id=263 +// XFAIL: android + +#include "sanitizer/common_interface_defs.h" +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#ifdef SHARED +void bar() { printf("bar\n"); } +#else +__attribute__((noinline)) +void foo() { printf("foo\n"); } +extern void bar(); + +int G[4]; + +int main(int argc, char **argv) { + fprintf(stderr, "PID: %d\n", getpid()); + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "foo")) { + uintptr_t old_coverage = __sanitizer_get_total_unique_coverage(); + foo(); + uintptr_t new_coverage = __sanitizer_get_total_unique_coverage(); + assert(new_coverage > old_coverage); + } + if (!strcmp(argv[i], "bar")) + bar(); + } + if (argc == 5) { + static volatile char *zero = 0; + *zero = 0; // SEGV if argc == 5. + } + return G[argc]; // Buffer overflow if argc >= 4. +} +#endif + +// CHECK-main: PID: [[PID:[0-9]+]] +// CHECK-main: [[PID]].sancov: 1 PCs written +// CHECK-main-NOT: .so.[[PID]] +// +// CHECK-foo: PID: [[PID:[0-9]+]] +// CHECK-foo: [[PID]].sancov: 2 PCs written +// CHECK-foo-NOT: .so.[[PID]] +// +// CHECK-bar: PID: [[PID:[0-9]+]] +// CHECK-bar: [[PID]].sancov: 1 PCs written +// CHECK-bar: .so.[[PID]].sancov: 1 PCs written +// +// CHECK-foo-bar: PID: [[PID:[0-9]+]] +// CHECK-foo-bar: [[PID]].sancov: 2 PCs written +// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written +// +// CHECK-report: AddressSanitizer: global-buffer-overflow +// CHECK-report: PCs written +// +// CHECK-segv: AddressSanitizer: SEGV +// CHECK-segv: PCs written diff --git a/test/asan/TestCases/Linux/function-sections-are-bad.cc b/test/asan/TestCases/Linux/function-sections-are-bad.cc new file mode 100644 index 000000000000..15aaccbc957f --- /dev/null +++ b/test/asan/TestCases/Linux/function-sections-are-bad.cc @@ -0,0 +1,41 @@ +// Check that --gc-sections does not throw away (or localize) parts of sanitizer +// interface. +// RUN: %clang_asan %s -Wl,--gc-sections -ldl -o %t +// RUN: %clang_asan %s -DBUILD_SO -fPIC -o %t-so.so -shared +// RUN: %run %t 2>&1 + +// REQUIRES: asan-64-bits + +#ifndef BUILD_SO +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + char path[4096]; + snprintf(path, sizeof(path), "%s-so.so", argv[0]); + + void *handle = dlopen(path, RTLD_LAZY); + if (!handle) fprintf(stderr, "%s\n", dlerror()); + assert(handle != 0); + + typedef void (*F)(); + F f = (F)dlsym(handle, "call_rtl_from_dso"); + printf("%s\n", dlerror()); + assert(dlerror() == 0); + f(); + + dlclose(handle); + return 0; +} + +#else // BUILD_SO + +#include <sanitizer/asan_interface.h> +extern "C" void call_rtl_from_dso() { + volatile int32_t x; + volatile int32_t y = __sanitizer_unaligned_load32((void *)&x); +} + +#endif // BUILD_SO diff --git a/test/asan/TestCases/Linux/globals-gc-sections.cc b/test/asan/TestCases/Linux/globals-gc-sections.cc new file mode 100644 index 000000000000..72a9e9498f85 --- /dev/null +++ b/test/asan/TestCases/Linux/globals-gc-sections.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan %s -o %t -Wl,--gc-sections -ffunction-sections -mllvm -asan-globals=0 +// RUN: %clangxx_asan %s -o %t -Wl,--gc-sections -ffunction-sections -mllvm -asan-globals=1 + +// https://code.google.com/p/address-sanitizer/issues/detail?id=260 +// XFAIL: * + +int undefined(); + +int (*unused)() = undefined; + +int main() { + return 0; +} diff --git a/test/asan/TestCases/Linux/initialization-bug-any-order.cc b/test/asan/TestCases/Linux/initialization-bug-any-order.cc new file mode 100644 index 000000000000..a462f4a163f1 --- /dev/null +++ b/test/asan/TestCases/Linux/initialization-bug-any-order.cc @@ -0,0 +1,36 @@ +// Test to make sure basic initialization order errors are caught. +// Check that on Linux initialization order bugs are caught +// independently on order in which we list source files (if we specify +// strict init-order checking). + +// RUN: %clangxx_asan -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true not %run %t 2>&1 | FileCheck %s + +// Do not test with optimization -- the error may be optimized away. + +#include <cstdio> + +// 'y' is a dynamically initialized global residing in a different TU. This +// dynamic initializer will read the value of 'y' before main starts. The +// result is undefined behavior, which should be caught by initialization order +// checking. +extern int y; +int __attribute__((noinline)) initX() { + return y + 1; + // CHECK: {{AddressSanitizer: initialization-order-fiasco}} + // CHECK: {{READ of size .* at 0x.* thread T0}} + // CHECK: {{#0 0x.* in .*initX.* .*initialization-bug-any-order.cc:}}[[@LINE-3]] + // CHECK: {{0x.* is located 0 bytes inside of global variable .*y.*}} +} + +// This initializer begins our initialization order problems. +static int x = initX(); + +int main() { + // ASan should have caused an exit before main runs. + printf("PASS\n"); + // CHECK-NOT: PASS + return 0; +} diff --git a/test/asan/TestCases/Linux/interception-in-shared-lib-test.cc b/test/asan/TestCases/Linux/interception-in-shared-lib-test.cc new file mode 100644 index 000000000000..b828d5524ee0 --- /dev/null +++ b/test/asan/TestCases/Linux/interception-in-shared-lib-test.cc @@ -0,0 +1,32 @@ +// Check that memset() call from a shared library gets intercepted. +// Please always keep this file in sync with +// ../Darwin/interception-in-shared-lib-test.cc. + +// RUN: %clangxx_asan -O0 %s -DSHARED_LIB \ +// RUN: -shared -o %T/libinterception-in-shared-lib-test.so \ +// RUN: -fPIC +// TODO(glider): figure out how to set rpath in a more portable way and unite +// this test with ../Darwin/interception-in-shared-lib-test.cc. +// RUN: %clangxx_asan -O0 %s -o %t -Wl,-R,\$ORIGIN -L%T -linterception-in-shared-lib-test && \ +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> + +#if defined(SHARED_LIB) +extern "C" +void my_memset(void *p, size_t sz) { + memset(p, 0, sz); +} +#else +extern "C" void my_memset(void *p, size_t sz); + +int main(int argc, char *argv[]) { + char buf[10]; + my_memset(buf, 11); + // CHECK: {{.*ERROR: AddressSanitizer: stack-buffer-overflow}} + // CHECK: {{WRITE of size 11 at 0x.* thread T0}} + // CHECK: {{0x.* in my_memset .*interception-in-shared-lib-test.cc:19}} + return 0; +} +#endif diff --git a/test/asan/TestCases/Linux/interception_malloc_test.cc b/test/asan/TestCases/Linux/interception_malloc_test.cc new file mode 100644 index 000000000000..f6d6d340bd9c --- /dev/null +++ b/test/asan/TestCases/Linux/interception_malloc_test.cc @@ -0,0 +1,23 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" void *__interceptor_malloc(size_t size); +extern "C" void *malloc(size_t size) { + write(2, "malloc call\n", sizeof("malloc call\n") - 1); + return __interceptor_malloc(size); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: malloc call + // CHECK: heap-use-after-free +} diff --git a/test/asan/TestCases/Linux/interception_readdir_r_test.cc b/test/asan/TestCases/Linux/interception_readdir_r_test.cc new file mode 100644 index 000000000000..93b553c3744f --- /dev/null +++ b/test/asan/TestCases/Linux/interception_readdir_r_test.cc @@ -0,0 +1,62 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx_asan -O0 %s -DTEMP_DIR='"'"%T"'"' -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -DTEMP_DIR='"'"%T"'"' -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -DTEMP_DIR='"'"%T"'"' -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTEMP_DIR='"'"%T"'"' -o %t && %run %t 2>&1 | FileCheck %s +// +// RUN: %clangxx_asan -O0 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %run %t 2>&1 | FileCheck %s + +#include <dirent.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + + +int main() { + // Ensure the readdir_r interceptor doesn't erroneously mark the entire dirent + // as written when the end of the directory pointer is reached. + fputs("test1: reading the " TEMP_DIR " directory...\n", stderr); + DIR *d = opendir(TEMP_DIR); + struct dirent *result = (struct dirent *)(0xfeedbeef); + // We assume the temp dir for this test doesn't have crazy long file names. + char entry_buffer[4096]; + memset(entry_buffer, 0xab, sizeof(entry_buffer)); + unsigned count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + ((struct dirent *)entry_buffer)->d_reclen = 9999; + if (readdir_r(d, (struct dirent *)entry_buffer, &result) != 0) + abort(); + ++count; + } while (result != NULL); + fprintf(stderr, "read %d entries\n", count); + closedir(d); + // CHECK: test1: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries + + // Ensure the readdir64_r interceptor doesn't have the bug either. + fputs("test2: reading the " TEMP_DIR " directory...\n", stderr); + d = opendir(TEMP_DIR); + struct dirent64 *result64; + memset(entry_buffer, 0xab, sizeof(entry_buffer)); + count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + ((struct dirent64 *)entry_buffer)->d_reclen = 9999; + if (readdir64_r(d, (struct dirent64 *)entry_buffer, &result64) != 0) + abort(); + ++count; + } while (result64 != NULL); + fprintf(stderr, "read %d entries\n", count); + closedir(d); + // CHECK: test2: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries +} diff --git a/test/asan/TestCases/Linux/interception_test.cc b/test/asan/TestCases/Linux/interception_test.cc new file mode 100644 index 000000000000..fb9d01cfe6d7 --- /dev/null +++ b/test/asan/TestCases/Linux/interception_test.cc @@ -0,0 +1,22 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> + +extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base); +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return __interceptor_strtol(nptr, endptr, base); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK: heap-use-after-free +} diff --git a/test/asan/TestCases/Linux/interface_symbols_linux.c b/test/asan/TestCases/Linux/interface_symbols_linux.c new file mode 100644 index 000000000000..a616732ff9f8 --- /dev/null +++ b/test/asan/TestCases/Linux/interface_symbols_linux.c @@ -0,0 +1,35 @@ +// Check the presence of interface symbols in compiled file. + +// RUN: %clang_asan -O2 %s -o %t.exe +// RUN: nm -D %t.exe | grep " T " | sed "s/.* T //" \ +// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ +// RUN: | sed -E "s/__asan_init_v[0-9]+/__asan_init/" \ +// RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_stack_" \ +// RUN: | grep -v "__asan_on_error" > %t.symbols +// RUN: cat %p/../../../../lib/asan/asan_interface_internal.h \ +// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ +// RUN: | grep -v "OPTIONAL" \ +// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ +// RUN: > %t.interface +// RUN: echo __asan_report_load1 >> %t.interface +// RUN: echo __asan_report_load2 >> %t.interface +// RUN: echo __asan_report_load4 >> %t.interface +// RUN: echo __asan_report_load8 >> %t.interface +// RUN: echo __asan_report_load16 >> %t.interface +// RUN: echo __asan_report_store1 >> %t.interface +// RUN: echo __asan_report_store2 >> %t.interface +// RUN: echo __asan_report_store4 >> %t.interface +// RUN: echo __asan_report_store8 >> %t.interface +// RUN: echo __asan_report_store16 >> %t.interface +// RUN: echo __asan_report_load_n >> %t.interface +// RUN: echo __asan_report_store_n >> %t.interface +// RUN: echo __asan_get_current_fake_stack >> %t.interface +// RUN: echo __asan_addr_is_in_fake_stack >> %t.interface +// RUN: cat %t.interface | sort -u | diff %t.symbols - + +// FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing +// in "initialized data section". +// REQUIRES: x86_64-supported-target,i386-supported-target,asan-static-runtime + +int main() { return 0; } diff --git a/test/asan/TestCases/Linux/kernel-area.cc b/test/asan/TestCases/Linux/kernel-area.cc new file mode 100644 index 000000000000..8dd509f84975 --- /dev/null +++ b/test/asan/TestCases/Linux/kernel-area.cc @@ -0,0 +1,24 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// Test that kernel area is not sanitized on 32-bit machines. +// +// RUN: %clangxx_asan %s -o %t +// RUN: ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%kernel_bits +// RUN: ASAN_OPTIONS=verbosity=1:full_address_space=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%kernel_bits +// RUN: ASAN_OPTIONS=verbosity=1:full_address_space=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-kernel-64-bits +// +// CHECK-kernel-32-bits: || `[0x38000000, 0xbfffffff]` || HighMem || +// CHECK-kernel-32-bits: || `[0x27000000, 0x37ffffff]` || HighShadow || +// CHECK-kernel-32-bits: || `[0x24000000, 0x26ffffff]` || ShadowGap || +// +// CHECK-kernel-64-bits: || `[0x40000000, 0xffffffff]` || HighMem || +// CHECK-kernel-64-bits: || `[0x28000000, 0x3fffffff]` || HighShadow || +// CHECK-kernel-64-bits: || `[0x24000000, 0x27ffffff]` || ShadowGap || +// +// REQUIRES: asan-32-bits + +int main() { + return 0; +} + diff --git a/test/asan/TestCases/Linux/leak.cc b/test/asan/TestCases/Linux/leak.cc new file mode 100644 index 000000000000..36dc6ddb8adf --- /dev/null +++ b/test/asan/TestCases/Linux/leak.cc @@ -0,0 +1,16 @@ +// Minimal test for LeakSanitizer+AddressSanitizer. +// REQUIRES: leak-detection +// +// RUN: %clangxx_asan %s -o %t +// RUN: ASAN_OPTIONS=detect_leaks=1 not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS="" not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=detect_leaks=0 %run %t +#include <stdio.h> +int *t; + +int main(int argc, char **argv) { + t = new int[argc - 1]; + printf("t: %p\n", t); + t = 0; +} +// CHECK: LeakSanitizer: detected memory leaks diff --git a/test/asan/TestCases/Linux/lit.local.cfg b/test/asan/TestCases/Linux/lit.local.cfg new file mode 100644 index 000000000000..57271b8078a4 --- /dev/null +++ b/test/asan/TestCases/Linux/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/asan/TestCases/Linux/malloc-in-qsort.cc b/test/asan/TestCases/Linux/malloc-in-qsort.cc new file mode 100644 index 000000000000..545bc7e42a17 --- /dev/null +++ b/test/asan/TestCases/Linux/malloc-in-qsort.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW + +// Test how well we unwind in presence of qsort in the stack +// (i.e. if we can unwind through a function compiled w/o frame pointers). +// https://code.google.com/p/address-sanitizer/issues/detail?id=137 + +// Fast unwinder is only available on x86_64 and i386. +// REQUIRES: x86_64-supported-target + +// REQUIRES: compiler-rt-optimized + +#include <stdlib.h> +#include <stdio.h> + +int *GlobalPtr; + +extern "C" { +int QsortCallback(const void *a, const void *b) { + char *x = (char*)a; + char *y = (char*)b; + printf("Calling QsortCallback\n"); + GlobalPtr = new int[10]; + return (int)*x - (int)*y; +} + +__attribute__((noinline)) +void MyQsort(char *a, size_t size) { + printf("Calling qsort\n"); + qsort(a, size, sizeof(char), QsortCallback); + printf("Done\n"); // Avoid tail call. +} +} // extern "C" + +int main() { + char a[2] = {1, 2}; + MyQsort(a, 2); + return GlobalPtr[10]; +} + +// Fast unwind: can not unwind through qsort. +// FIXME: this test does not properly work with slow unwind yet. + +// CHECK-FAST: ERROR: AddressSanitizer: heap-buffer-overflow +// CHECK-FAST: is located 0 bytes to the right +// CHECK-FAST: #0{{.*}}operator new +// CHECK-FAST-NEXT: #1{{.*}}QsortCallback +// CHECK-FAST-NOT: MyQsort +// +// CHECK-SLOW: ERROR: AddressSanitizer: heap-buffer-overflow +// CHECK-SLOW: is located 0 bytes to the right +// CHECK-SLOW: #0{{.*}}operator new +// CHECK-SLOW-NEXT: #1{{.*}}QsortCallback +// CHECK-SLOW: #{{.*}}MyQsort +// CHECK-SLOW-NEXT: #{{.*}}main diff --git a/test/asan/TestCases/Linux/malloc_delete_mismatch.cc b/test/asan/TestCases/Linux/malloc_delete_mismatch.cc new file mode 100644 index 000000000000..18d65ce0008f --- /dev/null +++ b/test/asan/TestCases/Linux/malloc_delete_mismatch.cc @@ -0,0 +1,33 @@ +// Check that we detect malloc/delete mismatch only if the approptiate flag +// is set. + +// RUN: %clangxx_asan -g %s -o %t 2>&1 + +// Find error and provide malloc context. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=ALLOC-STACK + +// No error here. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %run %t + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf +#include <stdlib.h> + +static volatile char *x; + +int main() { + x = (char*)malloc(10); + x[0] = 0; + delete x; +} +// CHECK: ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete) on 0x +// CHECK-NEXT: #0{{.*}}operator delete +// CHECK: #{{.*}}main +// CHECK: is located 0 bytes inside of 10-byte region +// CHECK-NEXT: allocated by thread T0 here: +// ALLOC-STACK-NEXT: #0{{.*}}malloc +// ALLOC-STACK: #{{.*}}main +// CHECK: HINT: {{.*}} you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0 diff --git a/test/asan/TestCases/Linux/odr-violation.cc b/test/asan/TestCases/Linux/odr-violation.cc new file mode 100644 index 000000000000..ddc68a2db0f1 --- /dev/null +++ b/test/asan/TestCases/Linux/odr-violation.cc @@ -0,0 +1,42 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// Different size: detect a bug if detect_odr_violation>=1 +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t-ODR-SO.so +// RUN: %clangxx_asan %s %t-ODR-SO.so -Wl,-R. -o %t-ODR-EXE +// RUN: ASAN_OPTIONS=detect_odr_violation=1 not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=detect_odr_violation=2 not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=detect_odr_violation=0 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED +// RUN: not %run %t-ODR-EXE 2>&1 | FileCheck %s +// +// Same size: report a bug only if detect_odr_violation>=2. +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t-ODR-SO.so -DSZ=100 +// RUN: ASAN_OPTIONS=detect_odr_violation=1 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED +// RUN: ASAN_OPTIONS=detect_odr_violation=2 not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: not %run %t-ODR-EXE 2>&1 | FileCheck %s + +// GNU driver doesn't handle .so files properly. +// REQUIRES: Clang + +#ifndef SZ +# define SZ 4 +#endif + +#if BUILD_SO +namespace foo { char G[SZ]; } +#else +#include <stdio.h> +namespace foo { char G[100]; } +// CHECK: ERROR: AddressSanitizer: odr-violation +// CHECK: size=100 'foo::G' {{.*}}odr-violation.cc:[[@LINE-2]]:22 +// CHECK: size={{4|100}} 'foo::G' +int main(int argc, char **argv) { + printf("PASS: %p\n", &foo::G); +} +#endif + +// CHECK: These globals were registered at these points: +// CHECK: ODR-EXE +// CHECK: ODR-SO +// CHECK: SUMMARY: AddressSanitizer: odr-violation: global 'foo::G' at {{.*}}odr-violation.cc +// DISABLED: PASS diff --git a/test/asan/TestCases/Linux/overflow-in-qsort.cc b/test/asan/TestCases/Linux/overflow-in-qsort.cc new file mode 100644 index 000000000000..79b654e117cd --- /dev/null +++ b/test/asan/TestCases/Linux/overflow-in-qsort.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW + +// Test how well we unwind in presence of qsort in the stack +// (i.e. if we can unwind through a function compiled w/o frame pointers). +// https://code.google.com/p/address-sanitizer/issues/detail?id=137 + +// Fast unwinder is only available on x86_64 and i386. +// REQUIRES: x86_64-supported-target + +#include <stdlib.h> +#include <stdio.h> + +int global_array[10]; +volatile int one = 1; + +extern "C" { +int QsortCallback(const void *a, const void *b) { + char *x = (char*)a; + char *y = (char*)b; + printf("Calling QsortCallback\n"); + global_array[one * 10] = 0; // BOOM + return (int)*x - (int)*y; +} + +__attribute__((noinline)) +void MyQsort(char *a, size_t size) { + printf("Calling qsort\n"); + qsort(a, size, sizeof(char), QsortCallback); + printf("Done\n"); // Avoid tail call. +} +} // extern "C" + +int main() { + char a[2] = {1, 2}; + MyQsort(a, 2); +} + +// Fast unwind: can not unwind through qsort. + +// CHECK-FAST: ERROR: AddressSanitizer: global-buffer-overflow +// CHECK-FAST: #0{{.*}} in QsortCallback +// CHECK-FAST-NOT: MyQsort +// CHECK-FAST: is located 0 bytes to the right of global variable 'global_array + +// CHECK-SLOW: ERROR: AddressSanitizer: global-buffer-overflow +// CHECK-SLOW: #0{{.*}} in QsortCallback +// CHECK-SLOW: #{{.*}} in MyQsort +// CHECK-SLOW: #{{.*}} in main +// CHECK-SLOW: is located 0 bytes to the right of global variable 'global_array diff --git a/test/asan/TestCases/Linux/preinit_test.cc b/test/asan/TestCases/Linux/preinit_test.cc new file mode 100644 index 000000000000..10dde67d6a9b --- /dev/null +++ b/test/asan/TestCases/Linux/preinit_test.cc @@ -0,0 +1,33 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx -DFUNC=zzzz %s -shared -o %t.so -fPIC +// RUN: %clangxx_asan -DFUNC=main %s -o %t -Wl,-R. %t.so +// RUN: %run %t + +// GNU driver doesn't handle .so files properly. +// REQUIRES: Clang + +// This test ensures that we call __asan_init early enough. +// We build a shared library w/o asan instrumentation +// and the binary with asan instrumentation. +// Both files include the same header (emulated by -DFUNC here) +// with C++ template magic which runs global initializer at library load time. +// The function get() is instrumented with asan, but called +// before the usual constructors are run. +// So, we must make sure that __asan_init is executed even earlier. +// +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56393 + +struct A { + int foo() const { return 0; } +}; +A get () { return A(); } +template <class> struct O { + static A const e; +}; +template <class T> A const O <T>::e = get(); +int FUNC() { + return O<int>::e.foo(); +} + diff --git a/test/asan/TestCases/Linux/ptrace.cc b/test/asan/TestCases/Linux/ptrace.cc new file mode 100644 index 000000000000..7e5acb64c7a1 --- /dev/null +++ b/test/asan/TestCases/Linux/ptrace.cc @@ -0,0 +1,56 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx_asan -O0 %s -o %t && %run %t +// RUN: %clangxx_asan -DPOSITIVE -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// REQUIRES: x86_64-supported-target,i386-supported-target + +#include <assert.h> +#include <stdio.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(void) { + pid_t pid; + pid = fork(); + if (pid == 0) { // child + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + execl("/bin/true", "true", NULL); + } else { + wait(NULL); + user_regs_struct regs; + int res; + user_regs_struct * volatile pregs = ®s; +#ifdef POSITIVE + ++pregs; +#endif + res = ptrace(PTRACE_GETREGS, pid, NULL, pregs); + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{.*ptrace.cc:}}[[@LINE-2]] + assert(!res); +#if __WORDSIZE == 64 + printf("%zx\n", regs.rip); +#else + printf("%lx\n", regs.eip); +#endif + + user_fpregs_struct fpregs; + res = ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs); + assert(!res); + printf("%lx\n", (unsigned long)fpregs.cwd); + +#if __WORDSIZE == 32 + user_fpxregs_struct fpxregs; + res = ptrace(PTRACE_GETFPXREGS, pid, NULL, &fpxregs); + assert(!res); + printf("%lx\n", (unsigned long)fpxregs.mxcsr); +#endif + + ptrace(PTRACE_CONT, pid, NULL, NULL); + wait(NULL); + } + return 0; +} diff --git a/test/asan/TestCases/Linux/rlimit_mmap_test.cc b/test/asan/TestCases/Linux/rlimit_mmap_test.cc new file mode 100644 index 000000000000..7f37727b2eeb --- /dev/null +++ b/test/asan/TestCases/Linux/rlimit_mmap_test.cc @@ -0,0 +1,16 @@ +// Check that we properly report mmap failure. +// RUN: %clangxx_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <assert.h> +#include <sys/time.h> +#include <sys/resource.h> + +static volatile void *x; + +int main(int argc, char **argv) { + struct rlimit mmap_resource_limit = { 0, 0 }; + assert(0 == setrlimit(RLIMIT_AS, &mmap_resource_limit)); + x = malloc(10000000); +// CHECK: ERROR: Failed to mmap + return 0; +} diff --git a/test/asan/TestCases/Linux/shmctl.cc b/test/asan/TestCases/Linux/shmctl.cc new file mode 100644 index 000000000000..e1752bc894c0 --- /dev/null +++ b/test/asan/TestCases/Linux/shmctl.cc @@ -0,0 +1,27 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1 +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=250 +#include <stdio.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <assert.h> + +int main() { + int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); + assert(id > -1); + struct shmid_ds ds; + int res = shmctl(id, IPC_STAT, &ds); + assert(res > -1); + printf("shm_segsz: %zd\n", ds.shm_segsz); + assert(ds.shm_segsz == 4096); + assert(-1 != shmctl(id, IPC_RMID, 0)); + + struct shm_info shmInfo; + res = shmctl(0, SHM_INFO, (struct shmid_ds *)&shmInfo); + assert(res > -1); + + return 0; +} diff --git a/test/asan/TestCases/Linux/sized_delete_test.cc b/test/asan/TestCases/Linux/sized_delete_test.cc new file mode 100644 index 000000000000..823e3c0bf88e --- /dev/null +++ b/test/asan/TestCases/Linux/sized_delete_test.cc @@ -0,0 +1,93 @@ +// RUN: %clangxx_asan -Xclang -fsized-deallocation -O0 %s -o %t +// RUN: not %run %t scalar 2>&1 | FileCheck %s -check-prefix=SCALAR +// RUN: ASAN_OPTIONS=new_delete_type_mismatch=1 not %run %t scalar 2>&1 | FileCheck %s -check-prefix=SCALAR +// RUN: not %run %t array 2>&1 | FileCheck %s -check-prefix=ARRAY +// RUN: ASAN_OPTIONS=new_delete_type_mismatch=1 not %run %t array 2>&1 | FileCheck %s -check-prefix=ARRAY +// RUN: ASAN_OPTIONS=new_delete_type_mismatch=0 %run %t scalar +// RUN: ASAN_OPTIONS=new_delete_type_mismatch=0 %run %t array + +// Sized-delete is implemented with a weak delete() definition. +// Weak symbols are kind of broken on Android. +// XFAIL: android + +#include <new> +#include <stdio.h> +#include <string> + +inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +struct S12 { + int a, b, c; +}; + +struct S20 { + int a, b, c, d, e; +}; + +struct D1 { + int a, b, c; + ~D1() { fprintf(stderr, "D1::~D1\n"); } +}; + +struct D2 { + int a, b, c, d, e; + ~D2() { fprintf(stderr, "D2::~D2\n"); } +}; + +void Del12(S12 *x) { + break_optimization(x); + delete x; +} +void Del12NoThrow(S12 *x) { + break_optimization(x); + operator delete(x, std::nothrow); +} +void Del12Ar(S12 *x) { + break_optimization(x); + delete [] x; +} +void Del12ArNoThrow(S12 *x) { + break_optimization(x); + operator delete[](x, std::nothrow); +} + +int main(int argc, char **argv) { + if (argc != 2) return 1; + std::string flag = argv[1]; + // These are correct. + Del12(new S12); + Del12NoThrow(new S12); + Del12Ar(new S12[100]); + Del12ArNoThrow(new S12[100]); + + // Here we pass wrong type of pointer to delete, + // but [] and nothrow variants of delete are not sized. + Del12Ar(reinterpret_cast<S12*>(new S20[100])); + Del12NoThrow(reinterpret_cast<S12*>(new S20)); + Del12ArNoThrow(reinterpret_cast<S12*>(new S20[100])); + fprintf(stderr, "OK SO FAR\n"); + // SCALAR: OK SO FAR + // ARRAY: OK SO FAR + if (flag == "scalar") { + // Here asan should bark as we are passing a wrong type of pointer + // to sized delete. + Del12(reinterpret_cast<S12*>(new S20)); + // SCALAR: AddressSanitizer: new-delete-type-mismatch + // SCALAR: object passed to delete has wrong type: + // SCALAR: size of the allocated type: 20 bytes; + // SCALAR: size of the deallocated type: 12 bytes. + // SCALAR: is located 0 bytes inside of 20-byte region + // SCALAR: SUMMARY: AddressSanitizer: new-delete-type-mismatch + } else if (flag == "array") { + D1 *d1 = reinterpret_cast<D1*>(new D2[10]); + break_optimization(d1); + delete [] d1; + // ARRAY-NOT: D2::~D2 + // ARRAY: D1::~D1 + // ARRAY: AddressSanitizer: new-delete-type-mismatch + // ARRAY: size of the allocated type: 20{{4|8}} bytes; + // ARRAY: size of the deallocated type: 12{{4|8}} bytes. + } +} diff --git a/test/asan/TestCases/Linux/stack-trace-dlclose.cc b/test/asan/TestCases/Linux/stack-trace-dlclose.cc new file mode 100644 index 000000000000..e494e5661d1d --- /dev/null +++ b/test/asan/TestCases/Linux/stack-trace-dlclose.cc @@ -0,0 +1,45 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx_asan -DSHARED %s -shared -o %T/stack_trace_dlclose.so -fPIC +// RUN: %clangxx_asan -DSO_DIR=\"%T\" %s -o %t +// RUN: ASAN_OPTIONS=exitcode=0 %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <assert.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <sanitizer/common_interface_defs.h> + +#ifdef SHARED +extern "C" { +void *foo() { + return malloc(1); +} +} +#else +void *handle; + +int main(int argc, char **argv) { + void *handle = dlopen(SO_DIR "/stack_trace_dlclose.so", RTLD_LAZY); + assert(handle); + void *(*foo)() = (void *(*)())dlsym(handle, "foo"); + assert(foo); + void *p = foo(); + assert(p); + dlclose(handle); + + free(p); + free(p); // double-free + + return 0; +} +#endif + +// CHECK: {{ #0 0x.* in malloc}} +// CHECK: {{ #1 0x.* \(<unknown module>\)}} +// CHECK: {{ #2 0x.* in main}} diff --git a/test/asan/TestCases/Linux/stress_dtls.c b/test/asan/TestCases/Linux/stress_dtls.c new file mode 100644 index 000000000000..cb901ee59953 --- /dev/null +++ b/test/asan/TestCases/Linux/stress_dtls.c @@ -0,0 +1,116 @@ +// REQUIRES: asan-64-bits +// Stress test dynamic TLS + dlopen + threads. +// +// Note that glibc 2.15 seems utterly broken on this test, +// it fails with ~17 DSOs dlopen-ed. +// glibc 2.19 seems fine. +// +// +// RUN: %clangxx_asan -x c -DSO_NAME=f0 %s -shared -o %t-f0.so -fPIC +// RUN: %clangxx_asan -x c -DSO_NAME=f1 %s -shared -o %t-f1.so -fPIC +// RUN: %clangxx_asan -x c -DSO_NAME=f2 %s -shared -o %t-f2.so -fPIC +// RUN: %clangxx_asan %s -ldl -pthread -o %t +// RUN: %run %t 0 3 +// RUN: %run %t 2 3 +// RUN: ASAN_OPTIONS=verbosity=2 %run %t 10 2 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=verbosity=2:intercept_tls_get_addr=1 %run %t 10 2 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=verbosity=2:intercept_tls_get_addr=0 %run %t 10 2 2>&1 | FileCheck %s --check-prefix=CHECK0 +// CHECK: __tls_get_addr +// CHECK: Creating thread 0 +// CHECK: __tls_get_addr +// CHECK: Creating thread 1 +// CHECK: __tls_get_addr +// CHECK: Creating thread 2 +// CHECK: __tls_get_addr +// CHECK: Creating thread 3 +// CHECK: __tls_get_addr +// Make sure that TLS slots don't leak +// CHECK-NOT: num_live_dtls 5 +// +// CHECK0-NOT: __tls_get_addr +/* +cc=your-compiler + +$cc stress_dtls.c -pthread -ldl +for((i=0;i<100;i++)); do + $cc -fPIC -shared -DSO_NAME=f$i -o a.out-f$i.so stress_dtls.c; +done +./a.out 2 4 # <<<<<< 2 threads, 4 libs +./a.out 3 50 # <<<<<< 3 threads, 50 libs +*/ +#ifndef SO_NAME +#define _GNU_SOURCE +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <stdint.h> + +typedef void **(*f_t)(); + +__thread int my_tls; + +#define MAX_N_FUNCTIONS 1000 +f_t Functions[MAX_N_FUNCTIONS]; + +void *PrintStuff(void *unused) { + uintptr_t stack; + // fprintf(stderr, "STACK: %p TLS: %p SELF: %p\n", &stack, &my_tls, + // (void *)pthread_self()); + int i; + for (i = 0; i < MAX_N_FUNCTIONS; i++) { + if (!Functions[i]) break; + uintptr_t dtls = (uintptr_t)Functions[i](); + fprintf(stderr, " dtls[%03d]: %lx\n", i, dtls); + *(long*)dtls = 42; // check that this is writable. + } + return NULL; +} + +int main(int argc, char *argv[]) { + int num_threads = 1; + int num_libs = 1; + if (argc >= 2) + num_threads = atoi(argv[1]); + if (argc >= 3) + num_libs = atoi(argv[2]); + assert(num_libs <= MAX_N_FUNCTIONS); + + int lib; + for (lib = 0; lib < num_libs; lib++) { + char buf[4096]; + snprintf(buf, sizeof(buf), "%s-f%d.so", argv[0], lib); + void *handle = dlopen(buf, RTLD_LAZY); + if (!handle) { + fprintf(stderr, "%s\n", dlerror()); + exit(1); + } + snprintf(buf, sizeof(buf), "f%d", lib); + Functions[lib] = (f_t)dlsym(handle, buf); + if (!Functions[lib]) { + fprintf(stderr, "%s\n", dlerror()); + exit(1); + } + fprintf(stderr, "LIB[%03d] %s: %p\n", lib, buf, Functions[lib]); + PrintStuff(0); + + int i; + for (i = 0; i < num_threads; i++) { + pthread_t t; + fprintf(stderr, "Creating thread %d\n", i); + pthread_create(&t, 0, PrintStuff, 0); + pthread_join(t, 0); + } + } + return 0; +} +#else // SO_NAME +#ifndef DTLS_SIZE +# define DTLS_SIZE (1 << 17) +#endif +__thread void *huge_thread_local_array[DTLS_SIZE]; +void **SO_NAME() { + return &huge_thread_local_array[0]; +} +#endif diff --git a/test/asan/TestCases/Linux/swapcontext_test.cc b/test/asan/TestCases/Linux/swapcontext_test.cc new file mode 100644 index 000000000000..86ed5930bcf4 --- /dev/null +++ b/test/asan/TestCases/Linux/swapcontext_test.cc @@ -0,0 +1,90 @@ +// Check that ASan plays well with easy cases of makecontext/swapcontext. + +// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %run %t 2>&1 | FileCheck %s +// +// This test is too sublte to try on non-x86 arch for now. +// REQUIRES: x86_64-supported-target,i386-supported-target + +#include <stdio.h> +#include <ucontext.h> +#include <unistd.h> + +ucontext_t orig_context; +ucontext_t child_context; + +const int kStackSize = 1 << 20; + +__attribute__((noinline)) +void Throw() { + throw 1; +} + +__attribute__((noinline)) +void ThrowAndCatch() { + try { + Throw(); + } catch(int a) { + printf("ThrowAndCatch: %d\n", a); + } +} + +void Child(int mode) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + ThrowAndCatch(); // Simulate __asan_handle_no_return(). + // (a) Do nothing, just return to parent function. + // (b) Jump into the original function. Stack remains poisoned unless we do + // something. + if (mode == 1) { + if (swapcontext(&child_context, &orig_context) < 0) { + perror("swapcontext"); + _exit(0); + } + } +} + +int Run(int arg, int mode, char *child_stack) { + printf("Child stack: %p\n", child_stack); + // Setup child context. + getcontext(&child_context); + child_context.uc_stack.ss_sp = child_stack; + child_context.uc_stack.ss_size = kStackSize / 2; + if (mode == 0) { + child_context.uc_link = &orig_context; + } + makecontext(&child_context, (void (*)())Child, 1, mode); + if (swapcontext(&orig_context, &child_context) < 0) { + perror("swapcontext"); + return 0; + } + // Touch childs's stack to make sure it's unpoisoned. + for (int i = 0; i < kStackSize; i++) { + child_stack[i] = i; + } + return child_stack[arg]; +} + +int main(int argc, char **argv) { + char stack[kStackSize + 1]; + // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext + int ret = 0; + ret += Run(argc - 1, 0, stack); + printf("Test1 passed\n"); + // CHECK: Test1 passed + ret += Run(argc - 1, 1, stack); + printf("Test2 passed\n"); + // CHECK: Test2 passed + char *heap = new char[kStackSize + 1]; + ret += Run(argc - 1, 0, heap); + printf("Test3 passed\n"); + // CHECK: Test3 passed + ret += Run(argc - 1, 1, heap); + printf("Test4 passed\n"); + // CHECK: Test4 passed + + delete [] heap; + return ret; +} diff --git a/test/asan/TestCases/Linux/syscalls.cc b/test/asan/TestCases/Linux/syscalls.cc new file mode 100644 index 000000000000..bcdd5bc82119 --- /dev/null +++ b/test/asan/TestCases/Linux/syscalls.cc @@ -0,0 +1,25 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <errno.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> + +#include <sanitizer/linux_syscall_hooks.h> + +/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general + sanity of their behaviour. */ + +int main(int argc, char *argv[]) { + char buf[1000]; + __sanitizer_syscall_pre_recvmsg(0, buf - 1, 0); + // CHECK: AddressSanitizer: stack-buffer-{{.*}}erflow + // CHECK: READ of size {{.*}} at {{.*}} thread T0 + // CHECK: #0 {{.*}} in __sanitizer_syscall{{.*}}recvmsg + return 0; +} diff --git a/test/asan/TestCases/Linux/uar_signals.cc b/test/asan/TestCases/Linux/uar_signals.cc new file mode 100644 index 000000000000..f42c3f666554 --- /dev/null +++ b/test/asan/TestCases/Linux/uar_signals.cc @@ -0,0 +1,70 @@ +// This test checks that the implementation of use-after-return +// is async-signal-safe. +// RUN: %clangxx_asan -O1 %s -o %t -pthread && %run %t +// REQUIRES: stable-runtime +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/time.h> +#include <pthread.h> + +int *g; +int n_signals; + +typedef void (*Sigaction)(int, siginfo_t *, void *); + +void SignalHandler(int, siginfo_t*, void*) { + int local; + g = &local; + n_signals++; + // printf("s: %p\n", &local); +} + +static void EnableSigprof(Sigaction SignalHandler) { + struct sigaction sa; + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGPROF, &sa, NULL) != 0) { + perror("sigaction"); + abort(); + } + struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 1; + timer.it_value = timer.it_interval; + if (setitimer(ITIMER_PROF, &timer, 0) != 0) { + perror("setitimer"); + abort(); + } +} + +void RecursiveFunction(int depth) { + if (depth == 0) return; + int local; + g = &local; + // printf("r: %p\n", &local); + // printf("[%2d] n_signals: %d\n", depth, n_signals); + RecursiveFunction(depth - 1); + RecursiveFunction(depth - 1); +} + +void *Thread(void *) { + RecursiveFunction(18); + return NULL; +} + +int main(int argc, char **argv) { + EnableSigprof(SignalHandler); + + for (int i = 0; i < 4; i++) { + fprintf(stderr, "."); + const int kNumThread = sizeof(void*) == 8 ? 16 : 8; + pthread_t t[kNumThread]; + for (int i = 0; i < kNumThread; i++) + pthread_create(&t[i], 0, Thread, 0); + for (int i = 0; i < kNumThread; i++) + pthread_join(t[i], 0); + } + fprintf(stderr, "\n"); +} diff --git a/test/asan/TestCases/Linux/unpoison_tls.cc b/test/asan/TestCases/Linux/unpoison_tls.cc new file mode 100644 index 000000000000..9c1d74b28e5f --- /dev/null +++ b/test/asan/TestCases/Linux/unpoison_tls.cc @@ -0,0 +1,35 @@ +// Test that TLS is unpoisoned on thread death. +// REQUIRES: x86_64-supported-target,i386-supported-target + +// RUN: %clangxx_asan -O1 %s -pthread -o %t && %run %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> + +#include <sanitizer/asan_interface.h> + +__thread int64_t tls_var[2]; + +volatile int64_t *p_tls_var; + +void *first(void *arg) { + ASAN_POISON_MEMORY_REGION(&tls_var, sizeof(tls_var)); + p_tls_var = tls_var; + return 0; +} + +void *second(void *arg) { + assert(tls_var == p_tls_var); + *p_tls_var = 1; + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t p; + assert(0 == pthread_create(&p, 0, first, 0)); + assert(0 == pthread_join(p, 0)); + assert(0 == pthread_create(&p, 0, second, 0)); + assert(0 == pthread_join(p, 0)); + return 0; +} diff --git a/test/asan/TestCases/Posix/allow_user_segv.cc b/test/asan/TestCases/Posix/allow_user_segv.cc new file mode 100644 index 000000000000..b6443fab85df --- /dev/null +++ b/test/asan/TestCases/Posix/allow_user_segv.cc @@ -0,0 +1,59 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=180 + +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %run %t 2>&1 | FileCheck %s + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +struct sigaction original_sigaction_sigbus; +struct sigaction original_sigaction_sigsegv; + +void User_OnSIGSEGV(int signum, siginfo_t *siginfo, void *context) { + fprintf(stderr, "User sigaction called\n"); + struct sigaction original_sigaction; + if (signum == SIGBUS) + original_sigaction = original_sigaction_sigbus; + else if (signum == SIGSEGV) + original_sigaction = original_sigaction_sigsegv; + else { + printf("Invalid signum"); + exit(1); + } + if (original_sigaction.sa_flags | SA_SIGINFO) + original_sigaction.sa_sigaction(signum, siginfo, context); + else + original_sigaction.sa_handler(signum); +} + +int DoSEGV() { + volatile int *x = 0; + return *x; +} + +int InstallHandler(int signum, struct sigaction *original_sigaction) { + struct sigaction user_sigaction; + user_sigaction.sa_sigaction = User_OnSIGSEGV; + user_sigaction.sa_flags = SA_SIGINFO; + if (sigaction(signum, &user_sigaction, original_sigaction)) { + perror("sigaction"); + return 1; + } + return 0; +} + +int main() { + // Let's install handlers for both SIGSEGV and SIGBUS, since pre-Yosemite + // 32-bit Darwin triggers SIGBUS instead. + if (InstallHandler(SIGSEGV, &original_sigaction_sigsegv)) return 1; + if (InstallHandler(SIGBUS, &original_sigaction_sigbus)) return 1; + fprintf(stderr, "User sigaction installed\n"); + return DoSEGV(); +} + +// CHECK: User sigaction installed +// CHECK-NEXT: User sigaction called +// CHECK-NEXT: ASAN:SIGSEGV +// CHECK: AddressSanitizer: SEGV on unknown address diff --git a/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc b/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc new file mode 100644 index 000000000000..6ed02f4d5374 --- /dev/null +++ b/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc @@ -0,0 +1,63 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// Check that asan_symbolize.py script works (for binaries, ASan RTL and +// shared object files. + +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: env ASAN_OPTIONS=symbolize=0 not %run %t 2>&1 | %asan_symbolize | FileCheck %s +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#if !defined(SHARED_LIB) +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> + +using std::string; + +typedef void (fun_t)(int*, int); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc2 = (fun_t*)dlsym(lib, "inc2"); + if (!inc2) return 1; + printf("ok\n"); + int *array = (int*)malloc(40); + inc2(array, 1); + inc2(array, -1); // BOOM + // CHECK: ERROR: AddressSanitizer: heap-buffer-overflow + // CHECK: READ of size 4 at 0x{{.*}} + // CHECK: #0 {{.*}} in inc2 {{.*}}asan-symbolize-sanity-test.cc:[[@LINE+21]] + // CHECK: #1 {{.*}} in main {{.*}}asan-symbolize-sanity-test.cc:[[@LINE-4]] + // CHECK: allocated by thread T{{.*}} here: + // CHECK: #{{.*}} in {{(wrap_|__interceptor_)?}}malloc + // CHECK: #{{.*}} in main {{.*}}asan-symbolize-sanity-test.cc:[[@LINE-9]] + return 0; +} +#else // SHARED_LIBS +#include <stdio.h> +#include <string.h> + +int pad[10]; +int GLOB[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +extern "C" +void inc(int index) { + GLOB[index]++; +} + +extern "C" +void inc2(int *a, int index) { + a[index]++; +} +#endif // SHARED_LIBS diff --git a/test/asan/TestCases/Posix/asprintf.cc b/test/asan/TestCases/Posix/asprintf.cc new file mode 100644 index 000000000000..6946e5013d2c --- /dev/null +++ b/test/asan/TestCases/Posix/asprintf.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %run %t 2>&1 | FileCheck %s + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) { + char *p; + int res = asprintf(&p, "%d", argc); + fprintf(stderr, "x%d %sx\n", res, p); + // CHECK: x1 1x + free(p); + fprintf(stderr, "DONE\n"); + // CHECK: DONE + return 0; +} diff --git a/test/asan/TestCases/Posix/assign_large_valloc_to_global.cc b/test/asan/TestCases/Posix/assign_large_valloc_to_global.cc new file mode 100644 index 000000000000..ad547ce0ce1b --- /dev/null +++ b/test/asan/TestCases/Posix/assign_large_valloc_to_global.cc @@ -0,0 +1,9 @@ +// Make sure we don't report a leak nor hang. +// RUN: %clangxx_asan -O3 %s -o %t && %run %t +#include <stdlib.h> +#include <unistd.h> +#if !defined(__APPLE__) && !defined(__FreeBSD__) +# include <malloc.h> +#endif // !__APPLE__ && !__FreeBSD__ +int *p = (int*)valloc(1 << 20); +int main() { } diff --git a/test/asan/TestCases/Posix/glob.cc b/test/asan/TestCases/Posix/glob.cc new file mode 100644 index 000000000000..e0eeb33cca24 --- /dev/null +++ b/test/asan/TestCases/Posix/glob.cc @@ -0,0 +1,33 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx_asan -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <string> + + +int main(int argc, char *argv[]) { + std::string path = argv[1]; + std::string pattern = path + "/glob_test_root/*a"; + printf("pattern: %s\n", pattern.c_str()); + + glob_t globbuf; + int res = glob(pattern.c_str(), 0, 0, &globbuf); + + printf("%d %s\n", errno, strerror(errno)); + assert(res == 0); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + globfree(&globbuf); + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/test/asan/TestCases/Posix/glob_test_root/aa b/test/asan/TestCases/Posix/glob_test_root/aa new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/asan/TestCases/Posix/glob_test_root/aa diff --git a/test/asan/TestCases/Posix/glob_test_root/ab b/test/asan/TestCases/Posix/glob_test_root/ab new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/asan/TestCases/Posix/glob_test_root/ab diff --git a/test/asan/TestCases/Posix/glob_test_root/ba b/test/asan/TestCases/Posix/glob_test_root/ba new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/asan/TestCases/Posix/glob_test_root/ba diff --git a/test/asan/TestCases/Posix/init-order-dlopen.cc b/test/asan/TestCases/Posix/init-order-dlopen.cc new file mode 100644 index 000000000000..6f204775eb4e --- /dev/null +++ b/test/asan/TestCases/Posix/init-order-dlopen.cc @@ -0,0 +1,72 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=178 + +// Assume we're on Darwin and try to pass -U to the linker. If this flag is +// unsupported, don't use it. +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s \ +// RUN: -fPIC -shared -o %t-so.so -Wl,-U,_inc_global || \ +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s \ +// RUN: -fPIC -shared -o %t-so.so +// If the linker doesn't support --export-dynamic (which is ELF-specific), +// try to link without that option. +// FIXME: find a better solution. +// RUN: %clangxx_asan -O0 %s -pthread -o %t -Wl,--export-dynamic || \ +// RUN: %clangxx_asan -O0 %s -pthread -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true %run %t 2>&1 | FileCheck %s +#if !defined(SHARED_LIB) +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +#include <string> + +using std::string; + +int foo() { + return 42; +} +int global = foo(); + +__attribute__((visibility("default"))) +extern "C" +void inc_global() { + global++; +} + +void *global_poller(void *arg) { + while (true) { + if (global != 42) + break; + usleep(100); + } + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t p; + pthread_create(&p, 0, global_poller, 0); + string path = string(argv[0]) + "-so.so"; + if (0 == dlopen(path.c_str(), RTLD_NOW)) { + fprintf(stderr, "dlerror: %s\n", dlerror()); + return 1; + } + pthread_join(p, 0); + printf("PASSED\n"); + // CHECK: PASSED + return 0; +} +#else // SHARED_LIB +#include <stdio.h> +#include <unistd.h> + +extern "C" void inc_global(); + +int slow_init() { + sleep(1); + inc_global(); + return 42; +} + +int slowly_init_glob = slow_init(); +#endif // SHARED_LIB diff --git a/test/asan/TestCases/Posix/ioctl.cc b/test/asan/TestCases/Posix/ioctl.cc new file mode 100644 index 000000000000..78f152fe93fe --- /dev/null +++ b/test/asan/TestCases/Posix/ioctl.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %run %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -O0 -g %s -o %t && %run %t +// RUN: %clangxx_asan -O3 -g %s -o %t && %run %t + +#include <assert.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + int nonblock; + int res = ioctl(fd, FIONBIO, &nonblock + 1); + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 4 at + // CHECK: {{#.* in main .*ioctl.cc:}}[[@LINE-3]] + assert(res == 0); + close(fd); + return 0; +} diff --git a/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc b/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc new file mode 100644 index 000000000000..0a4998049cb0 --- /dev/null +++ b/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc @@ -0,0 +1,39 @@ +// Test that LargeAllocator unpoisons memory before releasing it to the OS. +// RUN: %clangxx_asan %s -o %t +// The memory is released only when the deallocated chunk leaves the quarantine, +// otherwise the mmap(p, ...) call overwrites the malloc header. +// RUN: ASAN_OPTIONS=quarantine_size=1 %run %t + +#include <assert.h> +#include <string.h> +#include <sys/mman.h> +#include <stdlib.h> +#include <unistd.h> + +#ifdef __ANDROID__ +#include <malloc.h> +void *my_memalign(size_t boundary, size_t size) { + return memalign(boundary, size); +} +#else +void *my_memalign(size_t boundary, size_t size) { + void *p; + posix_memalign(&p, boundary, size); + return p; +} +#endif + +int main() { + const long kPageSize = sysconf(_SC_PAGESIZE); + void *p = my_memalign(kPageSize, 1024 * 1024); + free(p); + + char *q = (char *)mmap(p, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); + assert(q == p); + + memset(q, 42, kPageSize); + + munmap(q, kPageSize); + return 0; +} diff --git a/test/asan/TestCases/Posix/lit.local.cfg b/test/asan/TestCases/Posix/lit.local.cfg new file mode 100644 index 000000000000..60a9460820a6 --- /dev/null +++ b/test/asan/TestCases/Posix/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os in ['Windows']: + config.unsupported = True diff --git a/test/asan/TestCases/Posix/new_array_cookie_test.cc b/test/asan/TestCases/Posix/new_array_cookie_test.cc new file mode 100644 index 000000000000..85d51f361835 --- /dev/null +++ b/test/asan/TestCases/Posix/new_array_cookie_test.cc @@ -0,0 +1,24 @@ +// REQUIRES: asan-64-bits +// RUN: %clangxx_asan -O3 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=poison_array_cookie=1 not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=poison_array_cookie=0 not %run %t 2>&1 | FileCheck %s --check-prefix=NO_COOKIE +#include <stdio.h> +#include <stdlib.h> +struct C { + int x; + ~C() { + fprintf(stderr, "ZZZZZZZZ\n"); + exit(1); + } +}; + +int main(int argc, char **argv) { + C *buffer = new C[argc]; + buffer[-2].x = 10; +// CHECK: AddressSanitizer: heap-buffer-overflow +// CHECK: in main {{.*}}new_array_cookie_test.cc:[[@LINE-2]] +// CHECK: is located 0 bytes inside of 12-byte region +// NO_COOKIE: ZZZZZZZZ + delete [] buffer; +} diff --git a/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc b/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc new file mode 100644 index 000000000000..c35ccebb8c79 --- /dev/null +++ b/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc @@ -0,0 +1,38 @@ +// REQUIRES: asan-64-bits +// RUN: %clangxx_asan -O3 %s -o %t +// RUN: ASAN_OPTIONS=poison_array_cookie=1 not %run %t 2>&1 | FileCheck %s --check-prefix=COOKIE +// RUN: ASAN_OPTIONS=poison_array_cookie=0 not %run %t 2>&1 | FileCheck %s --check-prefix=NO_COOKIE +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +int dtor_counter; +struct C { + int x; + ~C() { + dtor_counter++; + fprintf(stderr, "DTOR %d\n", dtor_counter); + } +}; + +__attribute__((noinline)) void Delete(C *c) { delete[] c; } +__attribute__((no_sanitize_address)) void Write42ToCookie(C *c) { + long *p = reinterpret_cast<long*>(c); + p[-1] = 42; +} + +int main(int argc, char **argv) { + C *buffer = new C[argc]; + delete [] buffer; + Write42ToCookie(buffer); + delete [] buffer; +// COOKIE: DTOR 1 +// COOKIE-NOT: DTOR 2 +// COOKIE: AddressSanitizer: loaded array cookie from free-d memory +// COOKIE: AddressSanitizer: attempting double-free +// NO_COOKIE: DTOR 1 +// NO_COOKIE: DTOR 43 +// NO_COOKIE-NOT: DTOR 44 +// NO_COOKIE-NOT: AddressSanitizer: loaded array cookie from free-d memory +// NO_COOKIE: AddressSanitizer: attempting double-free + +} diff --git a/test/asan/TestCases/Posix/new_array_cookie_with_new_from_class.cc b/test/asan/TestCases/Posix/new_array_cookie_with_new_from_class.cc new file mode 100644 index 000000000000..1cea6f68adb2 --- /dev/null +++ b/test/asan/TestCases/Posix/new_array_cookie_with_new_from_class.cc @@ -0,0 +1,38 @@ +// Test that we do not poison the array cookie if the operator new is defined +// inside the class. +// RUN: %clangxx_asan %s -o %t && %run %t +// +// XFAIL: android +// XFAIL: armv7l-unknown-linux-gnueabihf +#include <new> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <assert.h> +struct Foo { + void *operator new(size_t s) { return Allocate(s); } + void *operator new[] (size_t s) { return Allocate(s); } + ~Foo(); + static void *allocated; + static void *Allocate(size_t s) { + assert(!allocated); + return allocated = ::new char[s]; + } +}; + +Foo::~Foo() {} +void *Foo::allocated; + +Foo *getFoo(size_t n) { + return new Foo[n]; +} + +int main() { + Foo *foo = getFoo(10); + fprintf(stderr, "foo : %p\n", foo); + fprintf(stderr, "alloc: %p\n", Foo::allocated); + assert(reinterpret_cast<uintptr_t>(foo) == + reinterpret_cast<uintptr_t>(Foo::allocated) + sizeof(void*)); + *reinterpret_cast<uintptr_t*>(Foo::allocated) = 42; + return 0; +} diff --git a/test/asan/TestCases/Posix/readv.cc b/test/asan/TestCases/Posix/readv.cc new file mode 100644 index 000000000000..27436a1ad3d9 --- /dev/null +++ b/test/asan/TestCases/Posix/readv.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %run %t +// RUN: %clangxx_asan -O0 %s -DPOSITIVE -o %t && not %run %t 2>&1 | FileCheck %s + +// Test the readv() interceptor. + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <time.h> + +int main() { + char buf[2011]; + struct iovec iov[2]; +#ifdef POSITIVE + char * volatile buf_ = buf; + iov[0].iov_base = buf_ - 1; +#else + iov[0].iov_base = buf + 1; +#endif + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/etc/hosts", O_RDONLY); + assert(fd > 0); + readv(fd, iov, 2); + // CHECK: WRITE of size 5 at + close(fd); + return 0; +} diff --git a/test/asan/TestCases/Posix/shared-lib-test.cc b/test/asan/TestCases/Posix/shared-lib-test.cc new file mode 100644 index 000000000000..a0827b5fefbf --- /dev/null +++ b/test/asan/TestCases/Posix/shared-lib-test.cc @@ -0,0 +1,57 @@ +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi + +#if !defined(SHARED_LIB) +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> + +#include <string> + +using std::string; + +typedef void (fun_t)(int x); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc = (fun_t*)dlsym(lib, "inc"); + if (!inc) return 1; + printf("ok\n"); + inc(1); + inc(-1); // BOOM + // CHECK: {{.*ERROR: AddressSanitizer: global-buffer-overflow}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: {{ #0 0x.*}} + // CHECK: {{ #1 0x.* in main .*shared-lib-test.cc:}}[[@LINE-4]] + return 0; +} +#else // SHARED_LIB +#include <stdio.h> +#include <string.h> + +int pad[10]; +int GLOB[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +extern "C" +void inc(int index) { + GLOB[index]++; +} + +extern "C" +void inc2(int *a, int index) { + a[index]++; +} +#endif // SHARED_LIB diff --git a/test/asan/TestCases/Posix/start-deactivated.cc b/test/asan/TestCases/Posix/start-deactivated.cc new file mode 100644 index 000000000000..d60677a8a5bb --- /dev/null +++ b/test/asan/TestCases/Posix/start-deactivated.cc @@ -0,0 +1,69 @@ +// Test for ASAN_OPTIONS=start_deactivated=1 mode. +// Main executable is uninstrumented, but linked to ASan runtime. The shared +// library is instrumented. Memory errors before dlopen are not detected. + +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx -O0 %s -c -o %t.o +// RUN: %clangxx_asan -O0 %t.o -o %t +// RUN: ASAN_OPTIONS=start_deactivated=1 not %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#if !defined(SHARED_LIB) +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <string> + +#include "sanitizer/asan_interface.h" + +void test_malloc_shadow() { + char *p = (char *)malloc(100); + char *q = (char *)__asan_region_is_poisoned(p + 95, 8); + fprintf(stderr, "=%zd=\n", q ? q - (p + 95) : -1); + free(p); +} + +typedef void (*Fn)(); + +int main(int argc, char *argv[]) { + test_malloc_shadow(); + // CHECK: =-1= + + std::string path = std::string(argv[0]) + "-so.so"; + void *dso = dlopen(path.c_str(), RTLD_NOW); + if (!dso) { + fprintf(stderr, "dlopen failed: %s\n", dlerror()); + return 1; + } + + test_malloc_shadow(); + // CHECK: =5= + + void *fn = dlsym(dso, "do_another_bad_thing"); + if (!fn) { + fprintf(stderr, "dlsym failed: %s\n", dlerror()); + return 1; + } + + ((Fn)fn)(); + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: READ of size 1 + // CHECK: {{#0 .* in do_another_bad_thing}} + // CHECK: is located 5 bytes to the right of 100-byte region + // CHECK: in do_another_bad_thing + + return 0; +} +#else // SHARED_LIB +#include <stdio.h> +#include <stdlib.h> + +extern "C" void do_another_bad_thing() { + char *volatile p = (char *)malloc(100); + printf("%hhx\n", p[105]); +} +#endif // SHARED_LIB diff --git a/test/asan/TestCases/Posix/strerror_r_test.cc b/test/asan/TestCases/Posix/strerror_r_test.cc new file mode 100644 index 000000000000..e6df441770df --- /dev/null +++ b/test/asan/TestCases/Posix/strerror_r_test.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %run %t + +// Regression test for PR17138. + +#include <assert.h> +#include <string.h> +#include <stdio.h> + +int main() { + char buf[1024]; + char *res = (char *)strerror_r(300, buf, sizeof(buf)); + printf("%p\n", res); + return 0; +} diff --git a/test/asan/TestCases/Posix/tsd_dtor_leak.cc b/test/asan/TestCases/Posix/tsd_dtor_leak.cc new file mode 100644 index 000000000000..32253afc8b25 --- /dev/null +++ b/test/asan/TestCases/Posix/tsd_dtor_leak.cc @@ -0,0 +1,39 @@ +// Regression test for a leak in tsd: +// https://code.google.com/p/address-sanitizer/issues/detail?id=233 +// RUN: %clangxx_asan -O1 %s -pthread -o %t +// RUN: ASAN_OPTIONS=quarantine_size=1 %run %t +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <sanitizer/allocator_interface.h> + +static pthread_key_t tsd_key; + +void *Thread(void *) { + pthread_setspecific(tsd_key, malloc(10)); + return 0; +} + +static volatile void *v; + +void Dtor(void *tsd) { + v = malloc(10000); + free(tsd); + free((void*)v); // The bug was that this was leaking. +} + +int main() { + assert(0 == pthread_key_create(&tsd_key, Dtor)); + size_t old_heap_size = 0; + for (int i = 0; i < 10; i++) { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); + size_t new_heap_size = __sanitizer_get_heap_size(); + fprintf(stderr, "heap size: new: %zd old: %zd\n", new_heap_size, old_heap_size); + if (old_heap_size) + assert(old_heap_size == new_heap_size); + old_heap_size = new_heap_size; + } +} diff --git a/test/asan/TestCases/Posix/wait.cc b/test/asan/TestCases/Posix/wait.cc new file mode 100644 index 000000000000..99d0212acfab --- /dev/null +++ b/test/asan/TestCases/Posix/wait.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_asan -DWAIT -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAITPID -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAITPID -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT3 -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3_RUSAGE -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT3_RUSAGE -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + + +#include <assert.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(int argc, char **argv) { + pid_t pid = fork(); + if (pid) { // parent + int x[3]; + int *status = x + argc * 3; + int res; +#if defined(WAIT) + res = wait(status); +#elif defined(WAITPID) + res = waitpid(pid, status, WNOHANG); +#elif defined(WAIT3) + res = wait3(status, WNOHANG, NULL); +#elif defined(WAIT3_RUSAGE) + struct rusage *ru = (struct rusage*)(x + argc * 3); + int good_status; + res = wait3(&good_status, WNOHANG, ru); +#endif + // CHECK: stack-buffer-overflow + // CHECK: {{WRITE of size .* at 0x.* thread T0}} + // CHECK: {{in .*wait}} + // CHECK: {{in main .*wait.cc:}} + // CHECK: is located in stack of thread T0 at offset + // CHECK: {{in main}} + return res == -1 ? 1 : 0; + } + // child + return 0; +} diff --git a/test/asan/TestCases/Posix/wait4.cc b/test/asan/TestCases/Posix/wait4.cc new file mode 100644 index 000000000000..b95246efa0e4 --- /dev/null +++ b/test/asan/TestCases/Posix/wait4.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_asan -DWAIT4 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT4 -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4_RUSAGE -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT4_RUSAGE -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// XFAIL: android + +#include <assert.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(int argc, char **argv) { + // This test passes on some versions of Android NDK and fails on other. + // https://code.google.com/p/memory-sanitizer/issues/detail?id=64 + // Make it fail unconditionally on Android. +#ifdef __ANDROID__ + return 0; +#endif + + pid_t pid = fork(); + if (pid) { // parent + int x[3]; + int *status = x + argc * 3; + int res; +#if defined(WAIT4) + res = wait4(pid, status, WNOHANG, NULL); +#elif defined(WAIT4_RUSAGE) + struct rusage *ru = (struct rusage*)(x + argc * 3); + int good_status; + res = wait4(pid, &good_status, WNOHANG, ru); +#endif + // CHECK: stack-buffer-overflow + // CHECK: {{WRITE of size .* at 0x.* thread T0}} + // CHECK: {{in .*wait}} + // CHECK: {{in main .*wait4.cc:}} + // CHECK: is located in stack of thread T0 at offset + // CHECK: {{in main}} + return res == -1 ? 1 : 0; + } + // child + return 0; +} diff --git a/test/asan/TestCases/Posix/waitid.cc b/test/asan/TestCases/Posix/waitid.cc new file mode 100644 index 000000000000..8b516dca9086 --- /dev/null +++ b/test/asan/TestCases/Posix/waitid.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <sys/wait.h> +#include <unistd.h> +#include <signal.h> + +int main(int argc, char **argv) { + pid_t pid = fork(); + if (pid) { // parent + int x[3]; + int *status = x + argc * 3; + int res; + + siginfo_t *si = (siginfo_t*)(x + argc * 3); + res = waitid(P_ALL, 0, si, WEXITED | WNOHANG); + // CHECK: stack-buffer-overflow + // CHECK: {{WRITE of size .* at 0x.* thread T0}} + // CHECK: {{in .*waitid}} + // CHECK: {{in main .*waitid.cc:}} + // CHECK: is located in stack of thread T0 at offset + // CHECK: {{in main}} + return res != -1; + } + // child + return 0; +} diff --git a/test/asan/TestCases/Windows/aligned_mallocs.cc b/test/asan/TestCases/Windows/aligned_mallocs.cc new file mode 100644 index 000000000000..df740b64e51c --- /dev/null +++ b/test/asan/TestCases/Windows/aligned_mallocs.cc @@ -0,0 +1,29 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t + +#include <windows.h> + +#define CHECK_ALIGNED(ptr,alignment) \ + do { \ + if (((uintptr_t)(ptr) % (alignment)) != 0) \ + return __LINE__; \ + } \ + while(0) + +int main(void) { + int *p = (int*)_aligned_malloc(1024 * sizeof(int), 32); + CHECK_ALIGNED(p, 32); + p[512] = 0; + _aligned_free(p); + + p = (int*)_aligned_malloc(128, 128); + CHECK_ALIGNED(p, 128); + p = (int*)_aligned_realloc(p, 2048 * sizeof(int), 128); + CHECK_ALIGNED(p, 128); + p[1024] = 0; + if (_aligned_msize(p, 128, 0) != 2048 * sizeof(int)) + return __LINE__; + _aligned_free(p); + + return 0; +} diff --git a/test/asan/TestCases/Windows/allocators_sanity.cc b/test/asan/TestCases/Windows/allocators_sanity.cc new file mode 100644 index 000000000000..66a862d7aca5 --- /dev/null +++ b/test/asan/TestCases/Windows/allocators_sanity.cc @@ -0,0 +1,37 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t | FileCheck %s + +#include <malloc.h> +#include <stdio.h> + +int main() { + int *p = (int*)malloc(1024 * sizeof(int)); + p[512] = 0; + free(p); + + p = (int*)malloc(128); + p = (int*)realloc(p, 2048 * sizeof(int)); + p[1024] = 0; + free(p); + + p = (int*)calloc(16, sizeof(int)); + if (p[8] != 0) + return 1; + p[15]++; + if (16 * sizeof(int) != _msize(p)) + return 2; + free(p); + + p = new int; + *p = 42; + delete p; + + p = new int[42]; + p[15]++; + delete [] p; + + printf("All ok\n"); +// CHECK: All ok + + return 0; +} diff --git a/test/asan/TestCases/Windows/beginthreadex.cc b/test/asan/TestCases/Windows/beginthreadex.cc new file mode 100644 index 000000000000..f2b2b4511ad8 --- /dev/null +++ b/test/asan/TestCases/Windows/beginthreadex.cc @@ -0,0 +1,21 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t + +#include <windows.h> +#include <process.h> + +unsigned WINAPI thread_proc(void *) { + volatile char stack_buffer[42]; + for (int i = 0; i < sizeof(stack_buffer); ++i) + stack_buffer[i] = 42; + return 0; +} + +int main() { + HANDLE thr = (HANDLE)_beginthreadex(NULL, 0, thread_proc, NULL, 0, NULL); + if (thr == 0) + return 1; + if (WAIT_OBJECT_0 != WaitForSingleObject(thr, INFINITE)) + return 2; + CloseHandle(thr); +} diff --git a/test/asan/TestCases/Windows/bitfield.cc b/test/asan/TestCases/Windows/bitfield.cc new file mode 100644 index 000000000000..253a759b98df --- /dev/null +++ b/test/asan/TestCases/Windows/bitfield.cc @@ -0,0 +1,21 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t + +#include <windows.h> + +typedef struct _S { + unsigned int bf1:1; + unsigned int bf2:2; + unsigned int bf3:3; + unsigned int bf4:4; +} S; + +int main(void) { + S *s = (S*)malloc(sizeof(S)); + s->bf1 = 1; + s->bf2 = 2; + s->bf3 = 3; + s->bf4 = 4; + free(s); + return 0; +} diff --git a/test/asan/TestCases/Windows/bitfield_uaf.cc b/test/asan/TestCases/Windows/bitfield_uaf.cc new file mode 100644 index 000000000000..f49d671e3eb3 --- /dev/null +++ b/test/asan/TestCases/Windows/bitfield_uaf.cc @@ -0,0 +1,34 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +typedef struct _S { + unsigned int bf1:1; + unsigned int bf2:2; + unsigned int bf3:3; + unsigned int bf4:4; +} S; + +void make_access(S *s) { + s->bf2 = 2; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: READ of size {{[124]}} at [[ADDR]] +// CHECK: {{#0 .* make_access .*bitfield_uaf.cc}}:[[@LINE-3]] +// CHECK: {{#1 .* main}} +} + +int main(void) { + S *s = (S*)malloc(sizeof(S)); + free(s); +// CHECK: [[ADDR]] is located 0 bytes inside of 4-byte region +// CHECK-LABEL: freed by thread T0 here: +// CHECK: {{#0 .* free }} +// CHECK: {{#1 .* main .*bitfield_uaf.cc}}:[[@LINE-4]] +// CHECK-LABEL: previously allocated by thread T0 here: +// CHECK: {{#0 .* malloc }} +// CHECK: {{#1 .* main .*bitfield_uaf.cc}}:[[@LINE-8]] + make_access(s); + return 0; +} + diff --git a/test/asan/TestCases/Windows/calloc_left_oob.cc b/test/asan/TestCases/Windows/calloc_left_oob.cc new file mode 100644 index 000000000000..459025bde92c --- /dev/null +++ b/test/asan/TestCases/Windows/calloc_left_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + int *buffer = (int*)calloc(42, sizeof(int)); + buffer[-1] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*calloc_left_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 4 bytes to the left of 168-byte region +// CHECK: allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* calloc }} +// CHECK-NEXT: {{#1 .* main .*calloc_left_oob.cc}}:[[@LINE-8]] + free(buffer); +} diff --git a/test/asan/TestCases/Windows/calloc_right_oob.cc b/test/asan/TestCases/Windows/calloc_right_oob.cc new file mode 100644 index 000000000000..c976b87d9707 --- /dev/null +++ b/test/asan/TestCases/Windows/calloc_right_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + int *buffer = (int*)calloc(42, sizeof(int)); + buffer[42] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*calloc_right_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes to the right of 168-byte region +// CHECK: allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* calloc }} +// CHECK-NEXT: {{#1 .* main .*calloc_right_oob.cc}}:[[@LINE-8]] + free(buffer); +} diff --git a/test/asan/TestCases/Windows/calloc_uaf.cc b/test/asan/TestCases/Windows/calloc_uaf.cc new file mode 100644 index 000000000000..db5e70741b72 --- /dev/null +++ b/test/asan/TestCases/Windows/calloc_uaf.cc @@ -0,0 +1,20 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + int *buffer = (int*)calloc(42, sizeof(int)); + free(buffer); + buffer[0] = 42; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*calloc_uaf.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes inside of 168-byte region +// CHECK: freed by thread T0 here: +// CHECK-NEXT: {{#0 .* free }} +// CHECK-NEXT: {{#1 .* main .*calloc_uaf.cc}}:[[@LINE-8]] +// CHECK: previously allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* calloc }} +// CHECK-NEXT: {{#1 .* main .*calloc_uaf.cc}}:[[@LINE-12]] +} diff --git a/test/asan/TestCases/Windows/crt_initializers.cc b/test/asan/TestCases/Windows/crt_initializers.cc new file mode 100644 index 000000000000..084f8a45e18a --- /dev/null +++ b/test/asan/TestCases/Windows/crt_initializers.cc @@ -0,0 +1,31 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t | FileCheck %s + +// This is a test for http://code.google.com/p/address-sanitizer/issues/detail?id=305 + +#include <stdio.h> + +typedef void (*FPTR)(); + +// __xi_a and __xi_z are defined in VC/crt/src/crt0dat.c +// and are located in .CRT$XIA and .CRT$XIZ respectively. +extern "C" FPTR __xi_a, __xi_z; + +int main() { + unsigned count = 0; + + // Iterate through CRT initializers. + for (FPTR* it = &__xi_a; it < &__xi_z; ++it) { + if (*it) + count++; + } + + printf("Number of nonzero CRT initializers: %u\n", count); +// CHECK: Number of nonzero CRT initializers +} + +void call_me_maybe() {} + +#pragma data_seg(".CRT$XIB") +// Add an initializer that shouldn't get its own redzone. +FPTR run_on_startup = call_me_maybe; diff --git a/test/asan/TestCases/Windows/demangled_names.cc b/test/asan/TestCases/Windows/demangled_names.cc new file mode 100644 index 000000000000..a528555b1e16 --- /dev/null +++ b/test/asan/TestCases/Windows/demangled_names.cc @@ -0,0 +1,50 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s +// +// This test makes sure ASan symbolizes stack traces the way they are typically +// symbolized on Windows. +#include <malloc.h> + +namespace foo { +// A template function in a namespace. +template<int x> +void bar(char *p) { + *p = x; +} + +// A regular function in a namespace. +void spam(char *p) { + bar<42>(p); +} +} + +// A multi-argument template with a bool template parameter. +template<typename T, bool U> +void baz(T t) { + if (U) + foo::spam(t); +} + +template<typename T> +struct A { + A(T v) { v_ = v; } + ~A(); + char *v_; +}; + +// A destructor of a template class. +template<> +A<char*>::~A() { + baz<char*, true>(v_); +} + +int main() { + char *buffer = (char*)malloc(42); + free(buffer); + A<char*> a(buffer); +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: foo::bar<42> {{.*}}demangled_names.cc +// CHECK: foo::spam {{.*}}demangled_names.cc +// CHECK: baz<char *,1> {{.*}}demangled_names.cc +// CHECK: A<char *>::~A<char *> {{.*}}demangled_names.cc +} diff --git a/test/asan/TestCases/Windows/dll_aligned_mallocs.cc b/test/asan/TestCases/Windows/dll_aligned_mallocs.cc new file mode 100644 index 000000000000..8b2c4d6dd957 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_aligned_mallocs.cc @@ -0,0 +1,34 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: %run %t %t.dll | FileCheck %s + +#include <malloc.h> +#include <stdio.h> + +#define CHECK_ALIGNED(ptr,alignment) \ + do { \ + if (((uintptr_t)(ptr) % (alignment)) != 0) \ + return __LINE__; \ + } \ + while(0) + +extern "C" __declspec(dllexport) +int test_function() { + int *p = (int*)_aligned_malloc(1024 * sizeof(int), 32); + CHECK_ALIGNED(p, 32); + p[512] = 0; + _aligned_free(p); + + p = (int*)_aligned_malloc(128, 128); + CHECK_ALIGNED(p, 128); + p = (int*)_aligned_realloc(p, 2048 * sizeof(int), 128); + CHECK_ALIGNED(p, 128); + p[1024] = 0; + if (_aligned_msize(p, 128, 0) != 2048 * sizeof(int)) + return __LINE__; + _aligned_free(p); + + printf("All ok\n"); +// CHECK: All ok + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_allocators_sanity.cc b/test/asan/TestCases/Windows/dll_allocators_sanity.cc new file mode 100644 index 000000000000..1d31f37ca904 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_allocators_sanity.cc @@ -0,0 +1,39 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: %run %t %t.dll | FileCheck %s + +#include <malloc.h> +#include <stdio.h> + +extern "C" __declspec(dllexport) +int test_function() { + int *p = (int*)malloc(1024 * sizeof(int)); + p[512] = 0; + free(p); + + p = (int*)malloc(128); + p = (int*)realloc(p, 2048 * sizeof(int)); + p[1024] = 0; + free(p); + + p = (int*)calloc(16, sizeof(int)); + if (p[8] != 0) + return 1; + p[15]++; + if (16 * sizeof(int) != _msize(p)) + return 2; + free(p); + + p = new int; + *p = 42; + delete p; + + p = new int[42]; + p[15]++; + delete [] p; + + printf("All ok\n"); +// CHECK: All ok + + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_and_lib.cc b/test/asan/TestCases/Windows/dll_and_lib.cc new file mode 100644 index 000000000000..bddaa32df73b --- /dev/null +++ b/test/asan/TestCases/Windows/dll_and_lib.cc @@ -0,0 +1,19 @@ +// Just make sure we can link an implib into another DLL +// This used to fail between r212699 and r212814. +// RUN: %clang_cl_asan -DCONFIG=1 %s -c -Fo%t.1.obj +// RUN: link /nologo /DLL /OUT:%t.1.dll %t.1.obj %asan_dll_thunk +// RUN: %clang_cl_asan -DCONFIG=2 %s -c -Fo%t.2.obj +// RUN: link /nologo /DLL /OUT:%t.2.dll %t.2.obj %t.1.lib %asan_dll_thunk +// REQUIRES: asan-static-runtime + +#if CONFIG==1 +extern "C" __declspec(dllexport) int f1() { + int x = 0; + return 1; +} +#else +extern "C" __declspec(dllexport) int f2() { + int x = 0; + return 2; +} +#endif diff --git a/test/asan/TestCases/Windows/dll_cerr.cc b/test/asan/TestCases/Windows/dll_cerr.cc new file mode 100644 index 000000000000..8f1a699ba801 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_cerr.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: %run %t %t.dll 2>&1 | FileCheck %s + +// Test that it works correctly even with ICF enabled. +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll -link /OPT:REF /OPT:ICF +// RUN: %run %t %t.dll 2>&1 | FileCheck %s + +#include <iostream> + +extern "C" __declspec(dllexport) +int test_function() { + // Just make sure we can use cout. + std::cout << "All ok\n"; +// CHECK: All ok + + // This line forces a declaration of some global basic_ostream internal object that + // calls memcpy() in its constructor. This doesn't work if __asan_init is not + // called early enough. + std::cout << 42; +// CHECK: 42 + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_host.cc b/test/asan/TestCases/Windows/dll_host.cc new file mode 100644 index 000000000000..d3b4c149d009 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_host.cc @@ -0,0 +1,49 @@ +// This is a host program for DLL tests. +// +// Just make sure we can compile this. +// The actual compile&run sequence is to be done by the DLL tests. +// RUN: %clang_cl_asan -O0 %s -Fe%t +// +// Get the list of ASan wrappers exported by the main module RTL: +// RUN: dumpbin /EXPORTS %t | grep -o "__asan_wrap[^ ]*" | grep -v @ | sort | uniq > %t.exported_wrappers +// +// Get the list of ASan wrappers imported by the DLL RTL: +// RUN: grep INTERCEPT_LIBRARY_FUNCTION %p/../../../../lib/asan/asan_win_dll_thunk.cc | grep -v define | sed "s/.*(\(.*\)).*/__asan_wrap_\1/" | sort | uniq > %t.dll_imports +// +// Now make sure the DLL thunk imports everything: +// RUN: echo +// RUN: echo "=== NOTE === If you see a mismatch below, please update asan_win_dll_thunk.cc" +// RUN: diff %t.dll_imports %t.exported_wrappers +// REQUIRES: asan-static-runtime + +#include <stdio.h> +#include <windows.h> + +int main(int argc, char **argv) { + if (argc != 2) { + printf("Usage: %s [client].dll\n", argv[0]); + return 101; + } + + const char *dll_name = argv[1]; + + HMODULE h = LoadLibrary(dll_name); + if (!h) { + printf("Could not load DLL: %s (code: %lu)!\n", + dll_name, GetLastError()); + return 102; + } + + typedef int (*test_function)(); + test_function gf = (test_function)GetProcAddress(h, "test_function"); + if (!gf) { + printf("Could not locate test_function in the DLL!\n"); + FreeLibrary(h); + return 103; + } + + int ret = gf(); + + FreeLibrary(h); + return ret; +} diff --git a/test/asan/TestCases/Windows/dll_intercept_memchr.cc b/test/asan/TestCases/Windows/dll_intercept_memchr.cc new file mode 100644 index 000000000000..1435bdc50127 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_intercept_memchr.cc @@ -0,0 +1,21 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <string.h> + +extern "C" __declspec(dllexport) +int test_function() { + char buff[6] = "Hello"; + + memchr(buff, 'z', 7); +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: READ of size 7 at [[ADDR]] thread T0 +// CHECK-NEXT: __asan_wrap_memchr +// CHECK-NEXT: memchr +// CHECK-NEXT: test_function {{.*}}dll_intercept_memchr.cc:[[@LINE-5]] +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame +// CHECK-NEXT: test_function {{.*}}dll_intercept_memchr.cc +// CHECK: 'buff' <== Memory access at offset {{.*}} overflows this variable + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_intercept_memcpy.cc b/test/asan/TestCases/Windows/dll_intercept_memcpy.cc new file mode 100644 index 000000000000..736e6969d521 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_intercept_memcpy.cc @@ -0,0 +1,32 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +// Test that it works correctly even with ICF enabled. +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll -link /OPT:REF /OPT:ICF +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> + +extern "C" __declspec(dllexport) +int test_function() { + char buff1[6] = "Hello", buff2[5]; + + memcpy(buff2, buff1, 5); + if (buff1[2] != buff2[2]) + return 2; + printf("Initial test OK\n"); + fflush(0); +// CHECK: Initial test OK + + memcpy(buff2, buff1, 6); +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 6 at [[ADDR]] thread T0 +// CHECK-NEXT: __asan_{{.*}}memcpy +// CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy.cc:[[@LINE-4]] +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame +// CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy.cc +// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc b/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc new file mode 100644 index 000000000000..c5f44df3faaf --- /dev/null +++ b/test/asan/TestCases/Windows/dll_intercept_memcpy_indirect.cc @@ -0,0 +1,34 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> + +void call_memcpy(void* (*f)(void *, const void *, size_t), + void *a, const void *b, size_t c) { + f(a, b, c); +} + +extern "C" __declspec(dllexport) +int test_function() { + char buff1[6] = "Hello", buff2[5]; + + call_memcpy(&memcpy, buff2, buff1, 5); + if (buff1[2] != buff2[2]) + return 2; + printf("Initial test OK\n"); + fflush(0); +// CHECK: Initial test OK + + call_memcpy(&memcpy, buff2, buff1, 6); +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 6 at [[ADDR]] thread T0 +// CHECK-NEXT: __asan_{{.*}}memcpy +// CHECK-NEXT: call_memcpy +// CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy_indirect.cc:[[@LINE-5]] +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame +// CHECK-NEXT: test_function {{.*}}dll_intercept_memcpy_indirect.cc +// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_intercept_memset.cc b/test/asan/TestCases/Windows/dll_intercept_memset.cc new file mode 100644 index 000000000000..d4be376f2458 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_intercept_memset.cc @@ -0,0 +1,32 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +// Test that it works correctly even with ICF enabled. +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll -link /OPT:REF /OPT:ICF +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> + +extern "C" __declspec(dllexport) +int test_function() { + char buff[5] = "aaaa"; + + memset(buff, 'b', 5); + if (buff[2] != 'b') + return 2; + printf("Initial test OK\n"); + fflush(0); +// CHECK: Initial test OK + + memset(buff, 'c', 6); +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 6 at [[ADDR]] thread T0 +// CHECK-NEXT: __asan_memset +// CHECK-NEXT: test_function {{.*}}dll_intercept_memset.cc:[[@LINE-4]] +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame +// CHECK-NEXT: test_function {{.*}}dll_intercept_memset.cc +// CHECK: 'buff' <== Memory access at offset {{.*}} overflows this variable + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_intercept_strlen.cc b/test/asan/TestCases/Windows/dll_intercept_strlen.cc new file mode 100644 index 000000000000..f41d47858bee --- /dev/null +++ b/test/asan/TestCases/Windows/dll_intercept_strlen.cc @@ -0,0 +1,28 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> + +extern "C" __declspec(dllexport) +int test_function() { + char str[] = "Hello!"; + if (6 != strlen(str)) + return 1; + printf("Initial test OK\n"); + fflush(0); +// CHECK: Initial test OK + + str[6] = '!'; // Removes '\0' at the end! + int len = strlen(str); +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// FIXME: Should be READ of size 1, see issue 155. +// CHECK: READ of size {{[0-9]+}} at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .*}}strlen +// CHECK-NEXT: {{#1 .* test_function .*}}dll_intercept_strlen.cc:[[@LINE-5]] +// +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame +// CHECK-NEXT: test_function {{.*}}dll_intercept_strlen.cc: + return len > 42; +} diff --git a/test/asan/TestCases/Windows/dll_large_function.cc b/test/asan/TestCases/Windows/dll_large_function.cc new file mode 100644 index 000000000000..039d01f84ba5 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_large_function.cc @@ -0,0 +1,12 @@ +// Make sure we can link a DLL with large functions which would mean +// functions such as __asan_loadN and __asan_storeN will be called +// from the DLL. We simulate the large function with +// -mllvm -asan-instrumentation-with-call-threshold=0. +// RUN: %clang_cl_asan %s -c -Fo%t.obj -mllvm -asan-instrumentation-with-call-threshold=0 +// RUN: link /nologo /DLL /OUT:%t.dll %t.obj %asan_dll_thunk +// REQUIRES: asan-static-runtime + +void f(long* foo, long* bar) { + // One load and one store + *foo = *bar; +} diff --git a/test/asan/TestCases/Windows/dll_malloc_left_oob.cc b/test/asan/TestCases/Windows/dll_malloc_left_oob.cc new file mode 100644 index 000000000000..0653ea45f6ef --- /dev/null +++ b/test/asan/TestCases/Windows/dll_malloc_left_oob.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <malloc.h> +extern "C" __declspec(dllexport) +int test_function() { + char *buffer = (char*)malloc(42); + buffer[-1] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: test_function {{.*}}dll_malloc_left_oob.cc:[[@LINE-3]] +// CHECK-NEXT: main {{.*}}dll_host.cc +// +// CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region +// CHECK-LABEL: allocated by thread T0 here: +// CHECK-NEXT: malloc +// CHECK-NEXT: test_function {{.*}}dll_malloc_left_oob.cc:[[@LINE-10]] +// CHECK-NEXT: main {{.*}}dll_host.cc +// CHECK-LABEL: SUMMARY + free(buffer); + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_malloc_uaf.cc b/test/asan/TestCases/Windows/dll_malloc_uaf.cc new file mode 100644 index 000000000000..b286380ac445 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_malloc_uaf.cc @@ -0,0 +1,28 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <malloc.h> + +extern "C" __declspec(dllexport) +int test_function() { + int *buffer = (int*)malloc(42); + free(buffer); + buffer[0] = 42; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 +// CHECK-NEXT: test_function {{.*}}dll_malloc_uaf.cc:[[@LINE-3]] +// CHECK-NEXT: main {{.*}}dll_host +// +// CHECK: [[ADDR]] is located 0 bytes inside of 42-byte region +// CHECK-LABEL: freed by thread T0 here: +// CHECK-NEXT: free +// CHECK-NEXT: test_function {{.*}}dll_malloc_uaf.cc:[[@LINE-10]] +// CHECK-NEXT: main {{.*}}dll_host +// +// CHECK-LABEL: previously allocated by thread T0 here: +// CHECK-NEXT: malloc +// CHECK-NEXT: test_function {{.*}}dll_malloc_uaf.cc:[[@LINE-16]] +// CHECK-NEXT: main {{.*}}dll_host + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_noreturn.cc b/test/asan/TestCases/Windows/dll_noreturn.cc new file mode 100644 index 000000000000..6ec90725145f --- /dev/null +++ b/test/asan/TestCases/Windows/dll_noreturn.cc @@ -0,0 +1,28 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <process.h> + +void noreturn_f() { + int subscript = -1; + char buffer[42]; + buffer[subscript] = 42; + _exit(1); +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: noreturn_f {{.*}}dll_noreturn.cc:[[@LINE-4]] +// CHECK-NEXT: test_function {{.*}}dll_noreturn.cc +// CHECK-NEXT: main {{.*}}dll_host.cc +// +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame +// CHECK-NEXT: noreturn_f {{.*}}dll_noreturn.cc +// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] underflows this variable +// CHECK-LABEL: SUMMARY +} + +extern "C" __declspec(dllexport) +int test_function() { + noreturn_f(); + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_null_deref.cc b/test/asan/TestCases/Windows/dll_null_deref.cc new file mode 100644 index 000000000000..0fb18de29163 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_null_deref.cc @@ -0,0 +1,18 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +__attribute__((noinline)) +static void NullDeref(int *ptr) { + // CHECK: ERROR: AddressSanitizer: access-violation on unknown address + // CHECK: {{0x0*000.. .*pc 0x.*}} + ptr[10]++; // BOOM +} + +extern "C" __declspec(dllexport) +int test_function() { + NullDeref((int*)0); + // CHECK: {{ #1 0x.* in test_function .*\dll_null_deref.cc:}}[[@LINE-1]] + // CHECK: AddressSanitizer can not provide additional info. + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_operator_array_new_left_oob.cc b/test/asan/TestCases/Windows/dll_operator_array_new_left_oob.cc new file mode 100644 index 000000000000..736ce80cc32a --- /dev/null +++ b/test/asan/TestCases/Windows/dll_operator_array_new_left_oob.cc @@ -0,0 +1,25 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +extern "C" __declspec(dllexport) +int test_function() { + char *buffer = new char[42]; + buffer[-1] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: test_function {{.*}}dll_operator_array_new_left_oob.cc:[[@LINE-3]] +// CHECK-NEXT: main {{.*}}dll_host.cc +// +// CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region +// CHECK-LABEL: allocated by thread T0 here: +// FIXME: Should get rid of the malloc/free frames called from the inside of +// operator new/delete in DLLs when using -MT CRT. +// FIXME: The 'operator new' frame should have []. +// CHECK: operator new +// CHECK-NEXT: test_function {{.*}}dll_operator_array_new_left_oob.cc:[[@LINE-13]] +// CHECK-NEXT: main {{.*}}dll_host.cc +// CHECK-LABEL: SUMMARY + delete [] buffer; + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_operator_array_new_with_dtor_left_oob.cc b/test/asan/TestCases/Windows/dll_operator_array_new_with_dtor_left_oob.cc new file mode 100644 index 000000000000..8306a737bfff --- /dev/null +++ b/test/asan/TestCases/Windows/dll_operator_array_new_with_dtor_left_oob.cc @@ -0,0 +1,33 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +struct C { + int x; + ~C() {} +}; + +extern "C" __declspec(dllexport) +int test_function() { + C *buffer = new C[42]; + buffer[-2].x = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 +// CHECK-NEXT: test_function {{.*}}dll_operator_array_new_with_dtor_left_oob.cc:[[@LINE-3]] +// CHECK-NEXT: main {{.*}}dll_host.cc +// +// FIXME: Currently it says "4 bytes ... left of 172-byte region", +// should be "8 bytes ... left of 168-byte region", see +// https://code.google.com/p/address-sanitizer/issues/detail?id=314 +// CHECK: [[ADDR]] is located {{.*}} bytes to the left of 172-byte region +// FIXME: Should get rid of the malloc/free frames called from the inside of +// operator new/delete in DLLs when using -MT CRT. +// FIXME: The operator new frame should have []. +// CHECK-LABEL: allocated by thread T0 here: +// CHECK: operator new +// CHECK-NEXT: test_function {{.*}}dll_operator_array_new_with_dtor_left_oob.cc:[[@LINE-16]] +// CHECK-NEXT: main {{.*}}dll_host.cc +// CHECK-LABEL: SUMMARY + delete [] buffer; + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_poison_unpoison.cc b/test/asan/TestCases/Windows/dll_poison_unpoison.cc new file mode 100644 index 000000000000..d486cb122251 --- /dev/null +++ b/test/asan/TestCases/Windows/dll_poison_unpoison.cc @@ -0,0 +1,35 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <sanitizer/asan_interface.h> + +void should_not_crash(volatile char *c) { + *c = 42; +} + +void should_crash(volatile char *c) { + *c = 42; +} + +extern "C" __declspec(dllexport) +int test_function() { + char buffer[256]; + should_not_crash(&buffer[0]); + __asan_poison_memory_region(buffer, 128); + should_not_crash(&buffer[192]); + __asan_unpoison_memory_region(buffer, 64); + should_not_crash(&buffer[32]); + + should_crash(&buffer[96]); +// CHECK: AddressSanitizer: use-after-poison on address [[ADDR:0x[0-9a-f]+]] +// CHECK-NEXT: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: should_crash {{.*}}\dll_poison_unpoison.cc +// CHECK-NEXT: test_function {{.*}}\dll_poison_unpoison.cc:[[@LINE-4]] +// CHECK-NEXT: main +// +// CHECK: [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame +// CHECK-NEXT: test_function {{.*}}\dll_poison_unpoison.cc +// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] is inside this variable + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_seh.cc b/test/asan/TestCases/Windows/dll_seh.cc new file mode 100644 index 000000000000..6e4c724e504d --- /dev/null +++ b/test/asan/TestCases/Windows/dll_seh.cc @@ -0,0 +1,60 @@ +// Clang doesn't support SEH on Windows yet, so for the time being we +// build this program in two parts: the code with SEH is built with CL, +// the rest is built with Clang. This represents the typical scenario when we +// build a large project using "clang-cl -fallback -fsanitize=address". +// +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// +// Check both -GS and -GS- builds: +// RUN: cl -LD -c %s -Fo%t.obj +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll %t.obj +// RUN: %run %t %t.dll +// +// RUN: cl -LD -GS- -c %s -Fo%t.obj +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll %t.obj +// RUN: %run %t %t.dll + +#include <windows.h> +#include <assert.h> +#include <stdio.h> + +// Should just "#include <sanitizer/asan_interface.h>" when C++ exceptions are +// supported and we don't need to use CL. +extern "C" bool __asan_address_is_poisoned(void *p); + +void ThrowAndCatch(); + +#if !defined(__clang__) +__declspec(noinline) +void Throw() { + int local, zero = 0; + fprintf(stderr, "Throw: %p\n", &local); + local = 5 / zero; +} + +__declspec(noinline) +void ThrowAndCatch() { + int local; + __try { + Throw(); + } __except(EXCEPTION_EXECUTE_HANDLER) { + fprintf(stderr, "__except: %p\n", &local); + } +} +#else + +extern "C" __declspec(dllexport) +int test_function() { + char x[32]; + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + assert(__asan_address_is_poisoned(x + 32)); + ThrowAndCatch(); + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 32)); + return 0; +} +#endif diff --git a/test/asan/TestCases/Windows/dll_stack_use_after_return.cc b/test/asan/TestCases/Windows/dll_stack_use_after_return.cc new file mode 100644 index 000000000000..6cd74c265b8f --- /dev/null +++ b/test/asan/TestCases/Windows/dll_stack_use_after_return.cc @@ -0,0 +1,28 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=1 not %run %t %t.dll 2>&1 | FileCheck %s + +#include <malloc.h> + +char *x; + +void foo() { + char stack_buffer[42]; + x = &stack_buffer[13]; +} + +extern "C" __declspec(dllexport) +int test_function() { + foo(); + *x = 42; +// CHECK: AddressSanitizer: stack-use-after-return +// CHECK: WRITE of size 1 at [[ADDR:.*]] thread T0 +// CHECK-NEXT: test_function {{.*}}dll_stack_use_after_return.cc:[[@LINE-3]] +// CHECK-NEXT: main +// +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame +// CHECK-NEXT: #0 {{.*}} foo {{.*}}dll_stack_use_after_return.cc +// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] is inside this variable + return 0; +} + diff --git a/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc b/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc new file mode 100644 index 000000000000..8f53623419ce --- /dev/null +++ b/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc @@ -0,0 +1,36 @@ +// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t +// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll +// RUN: not %run %t %t.dll 2>&1 | FileCheck %s + +#include <windows.h> +#include <malloc.h> + +DWORD WINAPI thread_proc(void *context) { + int subscript = -1; + char stack_buffer[42]; + stack_buffer[subscript] = 42; +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T1 +// CHECK-NEXT: thread_proc {{.*}}dll_thread_stack_array_left_oob.cc:[[@LINE-3]] +// +// CHECK: Address [[ADDR]] is located in stack of thread T1 at offset [[OFFSET:.*]] in frame +// CHECK-NEXT: thread_proc {{.*}}dll_thread_stack_array_left_oob.cc +// +// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] underflows this variable + + return 0; +} + +extern "C" __declspec(dllexport) +int test_function() { + HANDLE thr = CreateThread(NULL, 0, thread_proc, NULL, 0, NULL); +// CHECK-LABEL: Thread T1 created by T0 here: +// CHECK: test_function {{.*}}dll_thread_stack_array_left_oob.cc:[[@LINE-2]] +// CHECK-NEXT: main {{.*}}dll_host.cc +// CHECK-LABEL: SUMMARY + if (thr == 0) + return 1; + if (WAIT_OBJECT_0 != WaitForSingleObject(thr, INFINITE)) + return 2; + return 0; +} diff --git a/test/asan/TestCases/Windows/double_free.cc b/test/asan/TestCases/Windows/double_free.cc new file mode 100644 index 000000000000..18a9fcb44a75 --- /dev/null +++ b/test/asan/TestCases/Windows/double_free.cc @@ -0,0 +1,21 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + int *x = (int*)malloc(42 * sizeof(int)); + free(x); + free(x); +// CHECK: AddressSanitizer: attempting double-free on [[ADDR:0x[0-9a-f]+]] +// CHECK-NEXT: {{#0 .* free }} +// CHECK-NEXT: {{#1 .* main .*double_free.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes inside of 168-byte region +// CHECK-LABEL: freed by thread T0 here: +// CHECK-NEXT: {{#0 .* free }} +// CHECK-NEXT: {{#1 .* main .*double_free.cc}}:[[@LINE-8]] +// CHECK-LABEL: previously allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* malloc }} +// CHECK-NEXT: {{#1 .* main .*double_free.cc}}:[[@LINE-12]] + return 0; +} diff --git a/test/asan/TestCases/Windows/double_operator_delete.cc b/test/asan/TestCases/Windows/double_operator_delete.cc new file mode 100644 index 000000000000..eae4a64c2b92 --- /dev/null +++ b/test/asan/TestCases/Windows/double_operator_delete.cc @@ -0,0 +1,25 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + int *x = new int[42]; + delete [] x; + delete [] x; +// CHECK: AddressSanitizer: attempting double-free on [[ADDR:0x[0-9a-f]+]] +// FIXME: The 'operator delete' frame should have []. +// CHECK-NEXT: {{#0 .* operator delete}} +// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-4]] +// CHECK: [[ADDR]] is located 0 bytes inside of 168-byte region +// CHECK-LABEL: freed by thread T0 here: +// FIXME: The 'operator delete' frame should have []. +// CHECK-NEXT: {{#0 .* operator delete}} +// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-10]] +// CHECK-LABEL: previously allocated by thread T0 here: +// FIXME: The 'operator new' frame should have []. +// CHECK-NEXT: {{#0 .* operator new}} +// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-15]] + return 0; +} + diff --git a/test/asan/TestCases/Windows/global_const_string.cc b/test/asan/TestCases/Windows/global_const_string.cc new file mode 100644 index 000000000000..8c147c917c88 --- /dev/null +++ b/test/asan/TestCases/Windows/global_const_string.cc @@ -0,0 +1,12 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t | FileCheck %s + +#include <windows.h> +#include <stdio.h> + +int main(void) { + static const char *foo = "foobarspam"; + printf("Global string is `%s`\n", foo); +// CHECK: Global string is `foobarspam` + return 0; +} diff --git a/test/asan/TestCases/Windows/global_const_string_oob.cc b/test/asan/TestCases/Windows/global_const_string_oob.cc new file mode 100644 index 000000000000..b39e3dbb3b4e --- /dev/null +++ b/test/asan/TestCases/Windows/global_const_string_oob.cc @@ -0,0 +1,20 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> +#include <stdio.h> + +extern "C" const char *foo = "foobarspam"; + +int main(void) { + if (foo[16]) + printf("Boo\n"); +// CHECK-NOT: Boo +// CHECK: AddressSanitizer: global-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: READ of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*global_const_string_oob.cc:}}[[@LINE-5]] +// CHECK: [[ADDR]] is located 5 bytes to the right of global variable [[STR:.*]] defined in {{'.*global_const_string_oob.cc:7:.*' .*}} of size 11 +// CHECK: [[STR]] is ascii string 'foobarspam' + return 0; +} + diff --git a/test/asan/TestCases/Windows/hello_world.cc b/test/asan/TestCases/Windows/hello_world.cc new file mode 100644 index 000000000000..400ca1b3eacc --- /dev/null +++ b/test/asan/TestCases/Windows/hello_world.cc @@ -0,0 +1,9 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t | FileCheck %s + +#include <stdio.h> + +int main() { + printf("Hello, world!\n"); +// CHECK: Hello, world! +} diff --git a/test/asan/TestCases/Windows/intercept_memcpy.cc b/test/asan/TestCases/Windows/intercept_memcpy.cc new file mode 100644 index 000000000000..9ee984b1873d --- /dev/null +++ b/test/asan/TestCases/Windows/intercept_memcpy.cc @@ -0,0 +1,31 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> + +void call_memcpy(void* (*f)(void *, const void *, size_t), + void *a, const void *b, size_t c) { + f(a, b, c); +} + +int main() { + char buff1[6] = "Hello", buff2[5]; + + call_memcpy(&memcpy, buff2, buff1, 5); + if (buff1[2] != buff2[2]) + return 2; + printf("Initial test OK\n"); + fflush(0); +// CHECK: Initial test OK + + call_memcpy(&memcpy, buff2, buff1, 6); +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 6 at [[ADDR]] thread T0 +// CHECK-NEXT: __asan_{{.*}}memcpy +// CHECK-NEXT: call_memcpy +// CHECK-NEXT: main {{.*}}intercept_memcpy.cc:[[@LINE-5]] +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame +// CHECK-NEXT: #0 {{.*}} main +// CHECK: 'buff2' <== Memory access at offset {{.*}} overflows this variable +} diff --git a/test/asan/TestCases/Windows/intercept_strdup.cc b/test/asan/TestCases/Windows/intercept_strdup.cc new file mode 100644 index 000000000000..edb1f2f99245 --- /dev/null +++ b/test/asan/TestCases/Windows/intercept_strdup.cc @@ -0,0 +1,27 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> +#include <malloc.h> + +int main() { + char *ptr = _strdup("Hello"); + int subscript = 1; + ptr[subscript] = '3'; + printf("%s\n", ptr); + fflush(0); +// CHECK: H3llo + + subscript = -1; + ptr[subscript] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK: {{#0 .* main .*}}intercept_strdup.cc:[[@LINE-3]] +// CHECK: [[ADDR]] is located 1 bytes to the left of 6-byte region +// CHECK: allocated by thread T0 here: +// CHECK: {{#0 .* malloc }} +// CHECK: {{#1 .*strdup}} +// CHECK: {{#2 .* main .*}}intercept_strdup.cc:[[@LINE-16]] + free(ptr); +} diff --git a/test/asan/TestCases/Windows/intercept_strlen.cc b/test/asan/TestCases/Windows/intercept_strlen.cc new file mode 100644 index 000000000000..928a286bedfa --- /dev/null +++ b/test/asan/TestCases/Windows/intercept_strlen.cc @@ -0,0 +1,27 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> + +int main() { + char str[] = "Hello"; + if (5 != strlen(str)) + return 1; + + printf("Initial test OK\n"); + fflush(0); +// CHECK: Initial test OK + + str[5] = '!'; // Losing '\0' at the end. + int len = strlen(str); +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// FIXME: Should be READ of size 1, see issue 155. +// CHECK: READ of size {{[0-9]+}} at [[ADDR]] thread T0 +// CHECK: strlen +// CHECK-NEXT: main {{.*}}intercept_strlen.cc:[[@LINE-5]] +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame +// CHECK-NEXT: main {{.*}}intercept_strlen.cc +// CHECK: 'str' <== Memory access at offset {{.*}} overflows this variable + return len < 6; +} diff --git a/test/asan/TestCases/Windows/lit.local.cfg b/test/asan/TestCases/Windows/lit.local.cfg new file mode 100644 index 000000000000..13ef6d428251 --- /dev/null +++ b/test/asan/TestCases/Windows/lit.local.cfg @@ -0,0 +1,14 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +# We only run a small set of tests on Windows for now. +# Override the parent directory's "unsupported" decision until we can handle +# all of its tests. +if root.host_os in ['Windows']: + config.unsupported = False +else: + config.unsupported = True diff --git a/test/asan/TestCases/Windows/longjmp.cc b/test/asan/TestCases/Windows/longjmp.cc new file mode 100644 index 000000000000..443933e8ab62 --- /dev/null +++ b/test/asan/TestCases/Windows/longjmp.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_asan -O %s -o %t && %run %t + +// FIXME: merge this with the common longjmp test when we can run common +// tests on Windows. + +#include <assert.h> +#include <setjmp.h> +#include <stdio.h> +#include <sanitizer/asan_interface.h> + +static jmp_buf buf; + +int main() { + char x[32]; + fprintf(stderr, "\nTestLongJmp\n"); + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + assert(__asan_address_is_poisoned(x + 32)); + if (0 == setjmp(buf)) + longjmp(buf, 1); + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 32)); +} diff --git a/test/asan/TestCases/Windows/malloc_left_oob.cc b/test/asan/TestCases/Windows/malloc_left_oob.cc new file mode 100644 index 000000000000..ec133c393da2 --- /dev/null +++ b/test/asan/TestCases/Windows/malloc_left_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + char *buffer = (char*)malloc(42); + buffer[-1] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*malloc_left_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region +// CHECK: allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* malloc }} +// CHECK-NEXT: {{#1 .* main .*malloc_left_oob.cc}}:[[@LINE-8]] + free(buffer); +} diff --git a/test/asan/TestCases/Windows/malloc_right_oob.cc b/test/asan/TestCases/Windows/malloc_right_oob.cc new file mode 100644 index 000000000000..9975316d3e02 --- /dev/null +++ b/test/asan/TestCases/Windows/malloc_right_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + char *buffer = (char*)malloc(42); + buffer[42] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*malloc_right_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes to the right of 42-byte region +// CHECK: allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* malloc }} +// CHECK-NEXT: {{#1 .* main .*malloc_right_oob.cc}}:[[@LINE-8]] + free(buffer); +} diff --git a/test/asan/TestCases/Windows/malloc_uaf.cc b/test/asan/TestCases/Windows/malloc_uaf.cc new file mode 100644 index 000000000000..f58478947bf4 --- /dev/null +++ b/test/asan/TestCases/Windows/malloc_uaf.cc @@ -0,0 +1,20 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + char *buffer = (char*)malloc(42); + free(buffer); + buffer[0] = 42; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*malloc_uaf.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes inside of 42-byte region +// CHECK: freed by thread T0 here: +// CHECK-NEXT: {{#0 .* free }} +// CHECK-NEXT: {{#1 .* main .*malloc_uaf.cc}}:[[@LINE-8]] +// CHECK: previously allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* malloc }} +// CHECK-NEXT: {{#1 .* main .*malloc_uaf.cc}}:[[@LINE-12]] +} diff --git a/test/asan/TestCases/Windows/null_deref.cc b/test/asan/TestCases/Windows/null_deref.cc new file mode 100644 index 000000000000..202000f59db7 --- /dev/null +++ b/test/asan/TestCases/Windows/null_deref.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// FIXME: merge this with the common null_deref test when we can run common +// tests on Windows. + +__attribute__((noinline)) +static void NullDeref(int *ptr) { + // CHECK: ERROR: AddressSanitizer: access-violation on unknown address + // CHECK: {{0x0*000.. .*pc 0x.*}} + ptr[10]++; // BOOM +} +int main() { + NullDeref((int*)0); + // CHECK: {{ #1 0x.* in main.*null_deref.cc:}}[[@LINE-1]] + // CHECK: AddressSanitizer can not provide additional info. +} diff --git a/test/asan/TestCases/Windows/null_deref_multiple_dlls.cc b/test/asan/TestCases/Windows/null_deref_multiple_dlls.cc new file mode 100644 index 000000000000..62fe544ae545 --- /dev/null +++ b/test/asan/TestCases/Windows/null_deref_multiple_dlls.cc @@ -0,0 +1,40 @@ +// Make sure everything works even if the main module doesn't have any stack +// variables, thus doesn't explicitly reference any symbol exported by the +// runtime thunk. +// +// RUN: %clang_cl_asan -LD -O0 -DDLL1 %s -Fe%t1.dll +// RUN: %clang_cl_asan -LD -O0 -DDLL2 %s -Fe%t2.dll +// RUN: %clang_cl_asan -O0 -DEXE %s %t1.lib %t2.lib -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> +#include <string.h> + +extern "C" { +#if defined(EXE) +__declspec(dllimport) void foo1(); +__declspec(dllimport) void foo2(); + +int main() { + foo1(); + foo2(); +} +#elif defined(DLL1) +__declspec(dllexport) void foo1() {} +#elif defined(DLL2) +__attribute__((noinline)) +static void NullDeref(int *ptr) { + // CHECK: ERROR: AddressSanitizer: access-violation on unknown address + // CHECK: {{0x0*000.. .*pc 0x.*}} + ptr[10]++; // BOOM +} + +__declspec(dllexport) void foo2() { + NullDeref((int*)0); + // CHECK: {{ #1 0x.* in foo2.*null_deref_multiple_dlls.cc:}}[[@LINE-1]] + // CHECK: AddressSanitizer can not provide additional info. +} +#else +# error oops! +#endif +} diff --git a/test/asan/TestCases/Windows/operator_array_new_left_oob.cc b/test/asan/TestCases/Windows/operator_array_new_left_oob.cc new file mode 100644 index 000000000000..20a0f1927e5b --- /dev/null +++ b/test/asan/TestCases/Windows/operator_array_new_left_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +int main() { + char *buffer = new char[42]; + buffer[-1] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*operator_array_new_left_oob.cc}}:[[@LINE-3]] +// +// CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region +// CHECK-LABEL: allocated by thread T0 here: +// FIXME: The 'operator new' frame should have []. +// CHECK-NEXT: {{#0 .* operator new}} +// CHECK-NEXT: {{#1 .* main .*operator_array_new_left_oob.cc}}:[[@LINE-10]] + delete [] buffer; +} diff --git a/test/asan/TestCases/Windows/operator_array_new_right_oob.cc b/test/asan/TestCases/Windows/operator_array_new_right_oob.cc new file mode 100644 index 000000000000..23775ef6066e --- /dev/null +++ b/test/asan/TestCases/Windows/operator_array_new_right_oob.cc @@ -0,0 +1,18 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +int main() { + char *buffer = new char[42]; + buffer[42] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK: {{#0 .* main .*operator_array_new_right_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes to the right of 42-byte region +// CHECK: allocated by thread T0 here: +// FIXME: The 'operator new' frame should have []. +// CHECK: {{#0 .* operator new}} +// CHECK: {{#1 .* main .*operator_array_new_right_oob.cc}}:[[@LINE-9]] + delete [] buffer; +} diff --git a/test/asan/TestCases/Windows/operator_array_new_uaf.cc b/test/asan/TestCases/Windows/operator_array_new_uaf.cc new file mode 100644 index 000000000000..b638ef1df415 --- /dev/null +++ b/test/asan/TestCases/Windows/operator_array_new_uaf.cc @@ -0,0 +1,24 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +int main() { + char *buffer = new char[42]; + delete [] buffer; + buffer[0] = 42; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK: {{#0 .* main .*operator_array_new_uaf.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes inside of 42-byte region +// CHECK-LABEL: freed by thread T0 here: +// FIXME: The 'operator delete' frame should have []. +// CHECK: {{#0 .* operator delete}} +// CHECK: {{#1 .* main .*operator_array_new_uaf.cc}}:[[@LINE-9]] +// CHECK-LABEL: previously allocated by thread T0 here: +// FIXME: The 'operator new' frame should have []. +// CHECK: {{#0 .* operator new}} +// CHECK: {{#1 .* main .*operator_array_new_uaf.cc}}:[[@LINE-14]] + return 0; +} + diff --git a/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cc b/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cc new file mode 100644 index 000000000000..63f2929bd89b --- /dev/null +++ b/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cc @@ -0,0 +1,25 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +struct C { + int x; + ~C() {} +}; + +int main() { + C *buffer = new C[42]; + buffer[-2].x = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*operator_array_new_with_dtor_left_oob.cc}}:[[@LINE-3]] +// +// FIXME: Currently it says "4 bytes ... left of 172-byte region", +// should be "8 bytes ... left of 168-byte region", see +// https://code.google.com/p/address-sanitizer/issues/detail?id=314 +// CHECK: [[ADDR]] is located {{.*}} bytes to the left of 172-byte region +// CHECK-LABEL: allocated by thread T0 here: +// FIXME: The 'operator new' frame should have []. +// CHECK-NEXT: {{#0 .* operator new}} +// CHECK-NEXT: {{#1 .* main .*operator_array_new_with_dtor_left_oob.cc}}:[[@LINE-13]] + delete [] buffer; +} diff --git a/test/asan/TestCases/Windows/operator_delete_wrong_argument.cc b/test/asan/TestCases/Windows/operator_delete_wrong_argument.cc new file mode 100644 index 000000000000..c3e7daca55b0 --- /dev/null +++ b/test/asan/TestCases/Windows/operator_delete_wrong_argument.cc @@ -0,0 +1,12 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +int main() { + int *x = new int[42]; + delete (x + 1); +// CHECK: AddressSanitizer: attempting free on address which was not malloc()-ed +// CHECK: {{#0 0x.* operator delete }} +// CHECK: {{#1 .* main .*operator_delete_wrong_argument.cc}}:[[@LINE-3]] +} diff --git a/test/asan/TestCases/Windows/operator_new_left_oob.cc b/test/asan/TestCases/Windows/operator_new_left_oob.cc new file mode 100644 index 000000000000..c077f11d68f9 --- /dev/null +++ b/test/asan/TestCases/Windows/operator_new_left_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +int main() { + char *buffer = new char; + buffer[-1] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK: {{#0 .* main .*operator_new_left_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 1 bytes to the left of 1-byte region +// CHECK: allocated by thread T0 here: +// CHECK: {{#0 .* operator new }} +// CHECK: {{#1 .* main .*operator_new_left_oob.cc}}:[[@LINE-8]] + delete buffer; +} diff --git a/test/asan/TestCases/Windows/operator_new_right_oob.cc b/test/asan/TestCases/Windows/operator_new_right_oob.cc new file mode 100644 index 000000000000..7a66d1714b97 --- /dev/null +++ b/test/asan/TestCases/Windows/operator_new_right_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +int main() { + char *buffer = new char; + buffer[1] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK: {{#0 .* main .*operator_new_right_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes to the right of 1-byte region +// CHECK: allocated by thread T0 here: +// CHECK: {{#0 .* operator new }} +// CHECK: {{#1 .* main .*operator_new_right_oob.cc}}:[[@LINE-8]] + delete buffer; +} diff --git a/test/asan/TestCases/Windows/operator_new_uaf.cc b/test/asan/TestCases/Windows/operator_new_uaf.cc new file mode 100644 index 000000000000..c435458f0c1c --- /dev/null +++ b/test/asan/TestCases/Windows/operator_new_uaf.cc @@ -0,0 +1,22 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +int main() { + char *buffer = new char; + delete buffer; + *buffer = 42; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK: {{#0 .* main .*operator_new_uaf.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes inside of 1-byte region +// CHECK-LABEL: freed by thread T0 here: +// CHECK: {{#0 .* operator delete }} +// CHECK: {{#1 .* main .*operator_new_uaf.cc}}:[[@LINE-8]] +// CHECK-LABEL: previously allocated by thread T0 here: +// CHECK: {{#0 .* operator new }} +// CHECK: {{#1 .* main .*operator_new_uaf.cc}}:[[@LINE-12]] + return 0; +} + diff --git a/test/asan/TestCases/Windows/realloc_left_oob.cc b/test/asan/TestCases/Windows/realloc_left_oob.cc new file mode 100644 index 000000000000..7d30e1d5c4ad --- /dev/null +++ b/test/asan/TestCases/Windows/realloc_left_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + char *buffer = (char*)realloc(0, 42); + buffer[-1] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*realloc_left_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region +// CHECK: allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* realloc }} +// CHECK-NEXT: {{#1 .* main .*realloc_left_oob.cc}}:[[@LINE-8]] + free(buffer); +} diff --git a/test/asan/TestCases/Windows/realloc_right_oob.cc b/test/asan/TestCases/Windows/realloc_right_oob.cc new file mode 100644 index 000000000000..f741390bd4e9 --- /dev/null +++ b/test/asan/TestCases/Windows/realloc_right_oob.cc @@ -0,0 +1,17 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + char *buffer = (char*)realloc(0, 42); + buffer[42] = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*realloc_right_oob.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes to the right of 42-byte region +// CHECK: allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* realloc }} +// CHECK-NEXT: {{#1 .* main .*realloc_right_oob.cc}}:[[@LINE-8]] + free(buffer); +} diff --git a/test/asan/TestCases/Windows/realloc_uaf.cc b/test/asan/TestCases/Windows/realloc_uaf.cc new file mode 100644 index 000000000000..c5b6953cf76a --- /dev/null +++ b/test/asan/TestCases/Windows/realloc_uaf.cc @@ -0,0 +1,20 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + char *buffer = (char*)realloc(0, 42); + free(buffer); + buffer[0] = 42; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*realloc_uaf.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes inside of 42-byte region +// CHECK: freed by thread T0 here: +// CHECK-NEXT: {{#0 .* free }} +// CHECK-NEXT: {{#1 .* main .*realloc_uaf.cc}}:[[@LINE-8]] +// CHECK: previously allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* realloc }} +// CHECK-NEXT: {{#1 .* main .*realloc_uaf.cc}}:[[@LINE-12]] +} diff --git a/test/asan/TestCases/Windows/report_after_syminitialize.cc b/test/asan/TestCases/Windows/report_after_syminitialize.cc new file mode 100644 index 000000000000..faf5e35db5f5 --- /dev/null +++ b/test/asan/TestCases/Windows/report_after_syminitialize.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <windows.h> +#include <dbghelp.h> + +int main() { + // Make sure the RTL recovers from "no options enabled" dbghelp setup. + SymSetOptions(0); + + // Make sure the RTL recovers from "fInvadeProcess=FALSE". + if (!SymInitialize(GetCurrentProcess(), 0, FALSE)) + return 42; + + *(volatile int*)0 = 42; + // CHECK: ERROR: AddressSanitizer: access-violation on unknown address + // CHECK-NEXT: {{WARNING: .*DbgHelp}} + // CHECK: {{#0 0x.* in main.*report_after_syminitialize.cc:}}[[@LINE-3]] + // CHECK: AddressSanitizer can not provide additional info. +} diff --git a/test/asan/TestCases/Windows/seh.cc b/test/asan/TestCases/Windows/seh.cc new file mode 100644 index 000000000000..50cf6ddba8d6 --- /dev/null +++ b/test/asan/TestCases/Windows/seh.cc @@ -0,0 +1,56 @@ +// Clang doesn't support SEH on Windows yet, so for the time being we +// build this program in two parts: the code with SEH is built with CL, +// the rest is built with Clang. This represents the typical scenario when we +// build a large project using "clang-cl -fallback -fsanitize=address". +// +// Check both -GS and -GS- builds: +// RUN: cl -c %s -Fo%t.obj +// RUN: %clangxx_asan -o %t.exe %s %t.obj +// RUN: %run %t.exe +// +// RUN: cl -GS- -c %s -Fo%t.obj +// RUN: %clangxx_asan -o %t.exe %s %t.obj +// RUN: %run %t.exe + +#include <windows.h> +#include <assert.h> +#include <stdio.h> + +// Should just "#include <sanitizer/asan_interface.h>" when C++ exceptions are +// supported and we don't need to use CL. +extern "C" bool __asan_address_is_poisoned(void *p); + +void ThrowAndCatch(); + +#if !defined(__clang__) +__declspec(noinline) +void Throw() { + int local, zero = 0; + fprintf(stderr, "Throw: %p\n", &local); + local = 5 / zero; +} + +__declspec(noinline) +void ThrowAndCatch() { + int local; + __try { + Throw(); + } __except(EXCEPTION_EXECUTE_HANDLER) { + fprintf(stderr, "__except: %p\n", &local); + } +} +#else + +int main() { + char x[32]; + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + assert(__asan_address_is_poisoned(x + 32)); + ThrowAndCatch(); + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 32)); +} +#endif diff --git a/test/asan/TestCases/Windows/stack_array_left_oob.cc b/test/asan/TestCases/Windows/stack_array_left_oob.cc new file mode 100644 index 000000000000..040d855b48e2 --- /dev/null +++ b/test/asan/TestCases/Windows/stack_array_left_oob.cc @@ -0,0 +1,16 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> + +int main() { + int subscript = -1; + char buffer[42]; + buffer[subscript] = 42; +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*stack_array_left_oob.cc}}:[[@LINE-3]] +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame +// CHECK-NEXT: {{#0 .* main .*stack_array_left_oob.cc}} +// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] underflows this variable +} diff --git a/test/asan/TestCases/Windows/stack_array_right_oob.cc b/test/asan/TestCases/Windows/stack_array_right_oob.cc new file mode 100644 index 000000000000..a370246aa072 --- /dev/null +++ b/test/asan/TestCases/Windows/stack_array_right_oob.cc @@ -0,0 +1,16 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> + +int main() { + int subscript = 42; + char buffer[42]; + buffer[subscript] = 42; +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*stack_array_right_oob.cc}}:[[@LINE-3]] +// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame +// CHECK-NEXT: {{#0 .* main .*stack_array_right_oob.cc}} +// CHECK: 'buffer' <== Memory access at offset [[OFFSET]] overflows this variable +} diff --git a/test/asan/TestCases/Windows/stack_array_sanity.cc b/test/asan/TestCases/Windows/stack_array_sanity.cc new file mode 100644 index 000000000000..1aef1a923d24 --- /dev/null +++ b/test/asan/TestCases/Windows/stack_array_sanity.cc @@ -0,0 +1,12 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t | FileCheck %s + +#include <stdio.h> + +int main() { + int subscript = 1; + char buffer[42]; + buffer[subscript] = 42; + printf("OK\n"); +// CHECK: OK +} diff --git a/test/asan/TestCases/Windows/stack_use_after_return.cc b/test/asan/TestCases/Windows/stack_use_after_return.cc new file mode 100644 index 000000000000..7955f2685308 --- /dev/null +++ b/test/asan/TestCases/Windows/stack_use_after_return.cc @@ -0,0 +1,22 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=1 not %run %t 2>&1 | FileCheck %s + +char *x; + +void foo() { + char stack_buffer[42]; + x = &stack_buffer[13]; +} + +int main() { + foo(); + *x = 42; +// CHECK: AddressSanitizer: stack-use-after-return +// CHECK: WRITE of size 1 at {{.*}} thread T0 +// CHECK-NEXT: {{#0 0x.* in main .*stack_use_after_return.cc}}:[[@LINE-3]] +// +// CHECK: is located in stack of thread T0 at offset [[OFFSET:.*]] in frame +// CHECK-NEXT: {{#0 0x.* in foo .*stack_use_after_return.cc}} +// +// CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] is inside this variable +} diff --git a/test/asan/TestCases/Windows/thread_simple.cc b/test/asan/TestCases/Windows/thread_simple.cc new file mode 100644 index 000000000000..14bb82f042aa --- /dev/null +++ b/test/asan/TestCases/Windows/thread_simple.cc @@ -0,0 +1,26 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t + +#include <windows.h> + +DWORD WINAPI thread_proc(void *) { + volatile char stack_buffer[42]; + for (int i = 0; i < sizeof(stack_buffer); ++i) + stack_buffer[i] = 42; + return 0x42; +} + +int main() { + DWORD exitcode; + HANDLE thr = CreateThread(NULL, 0, thread_proc, NULL, 0, NULL); + if (thr == 0) + return 1; + if (WAIT_OBJECT_0 != WaitForSingleObject(thr, INFINITE)) + return 2; + + GetExitCodeThread(thr, &exitcode); + if (exitcode != 0x42) + return 3; + CloseHandle(thr); +} + diff --git a/test/asan/TestCases/Windows/thread_stack_array_left_oob.cc b/test/asan/TestCases/Windows/thread_stack_array_left_oob.cc new file mode 100644 index 000000000000..17b9b1bf8ecb --- /dev/null +++ b/test/asan/TestCases/Windows/thread_stack_array_left_oob.cc @@ -0,0 +1,27 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +DWORD WINAPI thread_proc(void *) { + int subscript = -1; + volatile char stack_buffer[42]; + stack_buffer[subscript] = 42; +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T1 +// CHECK: {{#0 .* thread_proc .*thread_stack_array_left_oob.cc}}:[[@LINE-3]] +// CHECK: Address [[ADDR]] is located in stack of thread T1 at offset {{.*}} in frame +// CHECK: thread_proc + return 0; +} + +int main() { + HANDLE thr = CreateThread(NULL, 0, thread_proc, NULL, 0, NULL); +// CHECK: Thread T1 created by T0 here: +// CHECK: {{#[01] .* main .*thread_stack_array_left_oob.cc}}:[[@LINE-2]] + + // A failure to create a thread should fail the test! + if (thr == 0) return 0; + + WaitForSingleObject(thr, INFINITE); +} diff --git a/test/asan/TestCases/Windows/thread_stack_array_right_oob.cc b/test/asan/TestCases/Windows/thread_stack_array_right_oob.cc new file mode 100644 index 000000000000..601a1b8a8760 --- /dev/null +++ b/test/asan/TestCases/Windows/thread_stack_array_right_oob.cc @@ -0,0 +1,27 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +DWORD WINAPI thread_proc(void *) { + int subscript = 42; + volatile char stack_buffer[42]; + stack_buffer[subscript] = 42; +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T1 +// CHECK: {{#0 .* thread_proc .*thread_stack_array_right_oob.cc}}:[[@LINE-3]] +// CHECK: Address [[ADDR]] is located in stack of thread T1 at offset {{.*}} in frame +// CHECK: thread_proc + return 0; +} + +int main(void) { + HANDLE thr = CreateThread(NULL, 0, thread_proc, NULL, 0, NULL); +// CHECK: Thread T1 created by T0 here: +// CHECK: {{#[01] .* main .*thread_stack_array_right_oob.cc}}:[[@LINE-2]] + + // A failure to create a thread should fail the test! + if (thr == 0) return 0; + + WaitForSingleObject(thr, INFINITE); +} diff --git a/test/asan/TestCases/Windows/thread_stack_reuse.cc b/test/asan/TestCases/Windows/thread_stack_reuse.cc new file mode 100644 index 000000000000..7da3a807dac1 --- /dev/null +++ b/test/asan/TestCases/Windows/thread_stack_reuse.cc @@ -0,0 +1,37 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t + +#include <windows.h> + +DWORD WINAPI thread_proc_1(void *) { + volatile int x, y, z; + x = 1; + y = 2; + z = 3; + return 0; +} + +DWORD WINAPI thread_proc_2(void *) { + volatile char stack_buffer[42]; + for (int i = 0; i < sizeof(stack_buffer); ++i) + stack_buffer[i] = 42; + return 0; +} + +int main(void) { + HANDLE thr = NULL; + + thr = CreateThread(NULL, 0, thread_proc_1, NULL, 0, NULL); + if (thr == 0) + return 1; + if (WAIT_OBJECT_0 != WaitForSingleObject(thr, INFINITE)) + return 2; + + thr = CreateThread(NULL, 0, thread_proc_2, NULL, 0, NULL); + if (thr == 0) + return 3; + if (WAIT_OBJECT_0 != WaitForSingleObject(thr, INFINITE)) + return 4; + CloseHandle(thr); +} + diff --git a/test/asan/TestCases/Windows/thread_stress.cc b/test/asan/TestCases/Windows/thread_stress.cc new file mode 100644 index 000000000000..74be8d88c665 --- /dev/null +++ b/test/asan/TestCases/Windows/thread_stress.cc @@ -0,0 +1,30 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t + +#include <windows.h> + +DWORD WINAPI thread_proc(void *) { + volatile char stack_buffer[42]; + for (int i = 0; i < sizeof(stack_buffer); ++i) + stack_buffer[i] = 42; + return 0; +} + +int main(void) { + for (int iter = 0; iter < 1024; ++iter) { + const int NUM_THREADS = 8; + HANDLE thr[NUM_THREADS]; + for (int i = 0; i < NUM_THREADS; ++i) { + thr[i] = CreateThread(NULL, 0, thread_proc, NULL, 0, NULL); + if (thr[i] == 0) + return 1; + } + for (int i = 0; i < NUM_THREADS; ++i) { + if (WAIT_OBJECT_0 != WaitForSingleObject(thr[i], INFINITE)) + return 2; + CloseHandle(thr[i]); + } + } + return 0; +} + diff --git a/test/asan/TestCases/Windows/throw_catch.cc b/test/asan/TestCases/Windows/throw_catch.cc new file mode 100644 index 000000000000..5313d25b26d6 --- /dev/null +++ b/test/asan/TestCases/Windows/throw_catch.cc @@ -0,0 +1,73 @@ +// Clang doesn't support exceptions on Windows yet, so for the time being we +// build this program in two parts: the code with exceptions is built with CL, +// the rest is built with Clang. This represents the typical scenario when we +// build a large project using "clang-cl -fallback -fsanitize=address". +// +// RUN: cl -c %s -Fo%t.obj +// RUN: %clangxx_asan -o %t.exe %s %t.obj +// RUN: %run %t.exe + +#include <assert.h> +#include <stdio.h> + +// Should just "#include <sanitizer/asan_interface.h>" when C++ exceptions are +// supported and we don't need to use CL. +extern "C" bool __asan_address_is_poisoned(void *p); + +void ThrowAndCatch(); +void TestThrowInline(); + +#if !defined(__clang__) +__declspec(noinline) +void Throw() { + int local; + fprintf(stderr, "Throw: %p\n", &local); + throw 1; +} + +__declspec(noinline) +void ThrowAndCatch() { + int local; + try { + Throw(); + } catch(...) { + fprintf(stderr, "Catch: %p\n", &local); + } +} + +void TestThrowInline() { + char x[32]; + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + try { + Throw(); + } catch(...) { + fprintf(stderr, "Catch\n"); + } + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 32)); +} + +#else + +void TestThrow() { + char x[32]; + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + assert(__asan_address_is_poisoned(x + 32)); + ThrowAndCatch(); + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 32)); +} + +int main(int argc, char **argv) { + TestThrowInline(); + TestThrow(); +} +#endif diff --git a/test/asan/TestCases/Windows/use_after_realloc.cc b/test/asan/TestCases/Windows/use_after_realloc.cc new file mode 100644 index 000000000000..9d2c025258fa --- /dev/null +++ b/test/asan/TestCases/Windows/use_after_realloc.cc @@ -0,0 +1,23 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <malloc.h> + +int main() { + char *buffer = (char*)realloc(0, 32), + *stale = buffer; + buffer = (char*)realloc(buffer, 64); + // The 'stale' may now point to a free'd memory. + stale[0] = 42; +// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 .* main .*use_after_realloc.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes inside of 32-byte region +// CHECK: freed by thread T0 here: +// CHECK-NEXT: {{#0 .* realloc }} +// CHECK-NEXT: {{#1 .* main .*use_after_realloc.cc}}:[[@LINE-9]] +// CHECK: previously allocated by thread T0 here: +// CHECK-NEXT: {{#0 .* realloc }} +// CHECK-NEXT: {{#1 .* main .*use_after_realloc.cc}}:[[@LINE-14]] + free(buffer); +} diff --git a/test/asan/TestCases/Windows/use_after_return_linkage.cc b/test/asan/TestCases/Windows/use_after_return_linkage.cc new file mode 100644 index 000000000000..48c5065a0fa9 --- /dev/null +++ b/test/asan/TestCases/Windows/use_after_return_linkage.cc @@ -0,0 +1,12 @@ +// Make sure LIBCMT doesn't accidentally get added to the list of DEFAULTLIB +// directives. REQUIRES: asan-dynamic-runtime +// RUN: %clang_cl_asan -LD %s | FileCheck %s +// CHECK: Creating library +// CHECK-NOT: LIBCMT + +void foo(int *p) { *p = 42; } + +__declspec(dllexport) void bar() { + int x; + foo(&x); +} diff --git a/test/asan/TestCases/Windows/windows_h.cc b/test/asan/TestCases/Windows/windows_h.cc new file mode 100644 index 000000000000..40cf5a10ad4f --- /dev/null +++ b/test/asan/TestCases/Windows/windows_h.cc @@ -0,0 +1,7 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: %run %t + +// Just make sure we can parse <windows.h> +#include <windows.h> + +int main() {} diff --git a/test/asan/TestCases/Windows/wrong_downcast_on_heap.cc b/test/asan/TestCases/Windows/wrong_downcast_on_heap.cc new file mode 100644 index 000000000000..112dd5308d11 --- /dev/null +++ b/test/asan/TestCases/Windows/wrong_downcast_on_heap.cc @@ -0,0 +1,26 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +class Parent { + public: + int field; +}; + +class Child : public Parent { + public: + int extra_field; +}; + +int main(void) { + Parent *p = new Parent; + Child *c = (Child*)p; // Intentional error here! + c->extra_field = 42; +// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 +// CHECK: {{#0 0x[0-9a-f]* in main .*wrong_downcast_on_heap.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located 0 bytes to the right of 4-byte region +// CHECK: allocated by thread T0 here: +// CHECK: #0 {{.*}} operator new + return 0; +} + diff --git a/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc b/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc new file mode 100644 index 000000000000..2859ecc521d2 --- /dev/null +++ b/test/asan/TestCases/Windows/wrong_downcast_on_stack.cc @@ -0,0 +1,26 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +class Parent { + public: + int field; +}; + +class Child : public Parent { + public: + int extra_field; +}; + +int main(void) { + Parent p; + Child *c = (Child*)&p; // Intentional error here! + c->extra_field = 42; +// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 +// CHECK-NEXT: {{#0 0x[0-9a-f]* in main .*wrong_downcast_on_stack.cc}}:[[@LINE-3]] +// CHECK: [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:[0-9]+]] in frame +// CHECK-NEXT: {{#0 0x[0-9a-f]* in main }} +// CHECK: 'p' <== Memory access at offset [[OFFSET]] overflows this variable + return 0; +} + diff --git a/test/asan/TestCases/alloca_big_alignment.cc b/test/asan/TestCases/alloca_big_alignment.cc new file mode 100644 index 000000000000..2ede3f949b24 --- /dev/null +++ b/test/asan/TestCases/alloca_big_alignment.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +#include <assert.h> + +__attribute__((noinline)) void foo(int index, int len) { + volatile char str[len] __attribute__((aligned(128))); + assert(!(reinterpret_cast<long>(str) & 127L)); + str[index] = '1'; // BOOM +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} + +int main(int argc, char **argv) { + foo(10, 10); + return 0; +} diff --git a/test/asan/TestCases/alloca_detect_custom_size_.cc b/test/asan/TestCases/alloca_detect_custom_size_.cc new file mode 100644 index 000000000000..2b0f573de3d0 --- /dev/null +++ b/test/asan/TestCases/alloca_detect_custom_size_.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +#include <assert.h> + +struct A { + char a[3]; + int b[3]; +}; + +__attribute__((noinline)) void foo(int index, int len) { + volatile struct A str[len] __attribute__((aligned(32))); + assert(!(reinterpret_cast<long>(str) & 31L)); + str[index].a[0] = '1'; // BOOM +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} + +int main(int argc, char **argv) { + foo(10, 10); + return 0; +} diff --git a/test/asan/TestCases/alloca_instruments_all_paddings.cc b/test/asan/TestCases/alloca_instruments_all_paddings.cc new file mode 100644 index 000000000000..d60a3b22dcb9 --- /dev/null +++ b/test/asan/TestCases/alloca_instruments_all_paddings.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: %run %t 2>&1 +// + +#include "sanitizer/asan_interface.h" +#include <assert.h> + +__attribute__((noinline)) void foo(int index, int len) { + volatile char str[len] __attribute__((aligned(32))); + assert(!(reinterpret_cast<long>(str) & 31L)); + char *q = (char *)__asan_region_is_poisoned((char *)str, 64); + assert(q && ((q - str) == index)); +} + +int main(int argc, char **argv) { + for (int i = 1; i < 33; ++i) + foo(i, i); + + for (int i = 1; i < 33; ++i) + foo(i, i); + + return 0; +} diff --git a/test/asan/TestCases/alloca_overflow_partial.cc b/test/asan/TestCases/alloca_overflow_partial.cc new file mode 100644 index 000000000000..590f35465dad --- /dev/null +++ b/test/asan/TestCases/alloca_overflow_partial.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +#include <assert.h> + +__attribute__((noinline)) void foo(int index, int len) { + volatile char str[len] __attribute__((aligned(32))); + assert(!(reinterpret_cast<long>(str) & 31L)); + str[index] = '1'; // BOOM +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} + +int main(int argc, char **argv) { + foo(10, 10); + return 0; +} diff --git a/test/asan/TestCases/alloca_overflow_right.cc b/test/asan/TestCases/alloca_overflow_right.cc new file mode 100644 index 000000000000..caec846838ef --- /dev/null +++ b/test/asan/TestCases/alloca_overflow_right.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +#include <assert.h> + +__attribute__((noinline)) void foo(int index, int len) { + volatile char str[len] __attribute__((aligned(32))); + assert(!(reinterpret_cast<long>(str) & 31L)); + str[index] = '1'; // BOOM +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} + +int main(int argc, char **argv) { + foo(33, 10); + return 0; +} diff --git a/test/asan/TestCases/alloca_safe_access.cc b/test/asan/TestCases/alloca_safe_access.cc new file mode 100644 index 000000000000..240454fd55e4 --- /dev/null +++ b/test/asan/TestCases/alloca_safe_access.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: %run %t 2>&1 +// + +#include <assert.h> + +__attribute__((noinline)) void foo(int index, int len) { + volatile char str[len] __attribute__((aligned(32))); + assert(!(reinterpret_cast<long>(str) & 31L)); + str[index] = '1'; +} + +int main(int argc, char **argv) { + foo(4, 5); + foo(39, 40); + return 0; +} diff --git a/test/asan/TestCases/alloca_underflow_left.cc b/test/asan/TestCases/alloca_underflow_left.cc new file mode 100644 index 000000000000..6e7061f7cfe2 --- /dev/null +++ b/test/asan/TestCases/alloca_underflow_left.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +#include <assert.h> + +__attribute__((noinline)) void foo(int index, int len) { + volatile char str[len] __attribute__((aligned(32))); + assert(!(reinterpret_cast<long>(str) & 31L)); + str[index] = '1'; // BOOM +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 +} + +int main(int argc, char **argv) { + foo(-1, 10); + return 0; +} diff --git a/test/asan/TestCases/allocator_returns_null.cc b/test/asan/TestCases/allocator_returns_null.cc new file mode 100644 index 000000000000..59a053c3dcad --- /dev/null +++ b/test/asan/TestCases/allocator_returns_null.cc @@ -0,0 +1,83 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + free(t); + } + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %lx\n", (long)x); + free(x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: AddressSanitizer's allocator is terminating the process + +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 diff --git a/test/asan/TestCases/asan_and_llvm_coverage_test.cc b/test/asan/TestCases/asan_and_llvm_coverage_test.cc new file mode 100644 index 000000000000..35bdfcb353c2 --- /dev/null +++ b/test/asan/TestCases/asan_and_llvm_coverage_test.cc @@ -0,0 +1,10 @@ +// RUN: %clangxx_asan -coverage -O0 %s -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=1 %run %t 2>&1 | FileCheck %s +// XFAIL: android +#include <stdio.h> +int foo() { return 1; } +int XXX = foo(); +int main() { + printf("PASS\n"); +// CHECK: PASS +} diff --git a/test/asan/TestCases/atexit_stats.cc b/test/asan/TestCases/atexit_stats.cc new file mode 100644 index 000000000000..be6534475245 --- /dev/null +++ b/test/asan/TestCases/atexit_stats.cc @@ -0,0 +1,18 @@ +// Make sure we report atexit stats. +// RUN: %clangxx_asan -O3 %s -o %t +// RUN: env ASAN_OPTIONS=atexit=1:print_stats=1 %run %t 2>&1 | FileCheck %s +// +// No atexit output on Android due to +// https://code.google.com/p/address-sanitizer/issues/detail?id=263 +// XFAIL: android + +#include <stdlib.h> +#if !defined(__APPLE__) && !defined(__FreeBSD__) +#include <malloc.h> +#endif +int *p1 = (int*)malloc(900); +int *p2 = (int*)malloc(90000); +int *p3 = (int*)malloc(9000000); +int main() { } + +// CHECK: AddressSanitizer exit stats: diff --git a/test/asan/TestCases/blacklist.cc b/test/asan/TestCases/blacklist.cc new file mode 100644 index 000000000000..7c31484c2174 --- /dev/null +++ b/test/asan/TestCases/blacklist.cc @@ -0,0 +1,38 @@ +// Test the blacklist functionality of ASan + +// RUN: echo "fun:*brokenFunction*" > %tmp +// RUN: echo "global:*badGlobal*" >> %tmp +// RUN: echo "src:*blacklist-extra.cc" >> %tmp +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O0 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %run %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O1 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %run %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O2 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %run %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O3 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %run %t 2>&1 + +// badGlobal is accessed improperly, but we blacklisted it. Align +// it to make sure memory past the end of badGlobal will be in +// the same page. +__attribute__((aligned(16))) int badGlobal; +int readBadGlobal() { + return (&badGlobal)[1]; +} + +// A function which is broken, but excluded in the blacklist. +int brokenFunction(int argc) { + char x[10] = {0}; + return x[argc * 10]; // BOOM +} + +// This function is defined in Helpers/blacklist-extra.cc, a source file which +// is blacklisted by name +int externalBrokenFunction(int x); + +int main(int argc, char **argv) { + brokenFunction(argc); + int x = readBadGlobal(); + externalBrokenFunction(argc); + return 0; +} diff --git a/test/asan/TestCases/contiguous_container.cc b/test/asan/TestCases/contiguous_container.cc new file mode 100644 index 000000000000..0f3a7db5b060 --- /dev/null +++ b/test/asan/TestCases/contiguous_container.cc @@ -0,0 +1,75 @@ +// RUN: %clangxx_asan -O %s -o %t && %run %t +// +// Test __sanitizer_annotate_contiguous_container. + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <sanitizer/asan_interface.h> + +void TestContainer(size_t capacity) { + char *beg = new char[capacity]; + char *end = beg + capacity; + char *mid = beg + capacity; + char *old_mid = 0; + + for (int i = 0; i < 10000; i++) { + size_t size = rand() % (capacity + 1); + assert(size <= capacity); + old_mid = mid; + mid = beg + size; + __sanitizer_annotate_contiguous_container(beg, end, old_mid, mid); + + for (size_t idx = 0; idx < size; idx++) + assert(!__asan_address_is_poisoned(beg + idx)); + for (size_t idx = size; idx < capacity; idx++) + assert(__asan_address_is_poisoned(beg + idx)); + assert(__sanitizer_verify_contiguous_container(beg, mid, end)); + if (mid != beg) + assert(!__sanitizer_verify_contiguous_container(beg, mid - 1, end)); + if (mid != end) + assert(!__sanitizer_verify_contiguous_container(beg, mid + 1, end)); + } + + // Don't forget to unpoison the whole thing before destroing/reallocating. + __sanitizer_annotate_contiguous_container(beg, end, mid, end); + for (size_t idx = 0; idx < capacity; idx++) + assert(!__asan_address_is_poisoned(beg + idx)); + delete[] beg; +} + +__attribute__((noinline)) +void Throw() { throw 1; } + +__attribute__((noinline)) +void ThrowAndCatch() { + try { + Throw(); + } catch(...) { + } +} + +void TestThrow() { + char x[32]; + __sanitizer_annotate_contiguous_container(x, x + 32, x + 32, x + 14); + assert(!__asan_address_is_poisoned(x + 13)); + assert(__asan_address_is_poisoned(x + 14)); + ThrowAndCatch(); + assert(!__asan_address_is_poisoned(x + 13)); + // FIXME: invert the assertion below once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + // This assertion works only w/o UAR. + if (!__asan_get_current_fake_stack()) + assert(!__asan_address_is_poisoned(x + 14)); + __sanitizer_annotate_contiguous_container(x, x + 32, x + 14, x + 32); + assert(!__asan_address_is_poisoned(x + 13)); + assert(!__asan_address_is_poisoned(x + 14)); +} + +int main(int argc, char **argv) { + int n = argc == 1 ? 128 : atoi(argv[1]); + for (int i = 0; i <= n; i++) + TestContainer(i); + TestThrow(); +} diff --git a/test/asan/TestCases/contiguous_container_crash.cc b/test/asan/TestCases/contiguous_container_crash.cc new file mode 100644 index 000000000000..143ae9d8edee --- /dev/null +++ b/test/asan/TestCases/contiguous_container_crash.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_asan -O %s -o %t +// RUN: not %run %t crash 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s +// RUN: not %run %t bad-bounds 2>&1 | FileCheck --check-prefix=CHECK-BAD %s +// RUN: env ASAN_OPTIONS=detect_container_overflow=0 %run %t crash +// +// Test crash due to __sanitizer_annotate_contiguous_container. + +#include <assert.h> +#include <string.h> + +extern "C" { +void __sanitizer_annotate_contiguous_container(const void *beg, const void *end, + const void *old_mid, + const void *new_mid); +} // extern "C" + +static volatile int one = 1; + +int TestCrash() { + long t[100]; + t[60] = 0; + __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 100, &t[0] + 100, + &t[0] + 50); + return (int)t[60 * one]; // Touches the poisoned memory. +} + +void BadBounds() { + long t[100]; + __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 100, &t[0] + 101, + &t[0] + 50); +} + +int main(int argc, char **argv) { + assert(argc == 2); + if (!strcmp(argv[1], "crash")) + return TestCrash(); + else if (!strcmp(argv[1], "bad-bounds")) + BadBounds(); +} +// CHECK-CRASH: AddressSanitizer: container-overflow +// CHECK-BAD: ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container diff --git a/test/asan/TestCases/current_allocated_bytes.cc b/test/asan/TestCases/current_allocated_bytes.cc new file mode 100644 index 000000000000..c49e433b1e8b --- /dev/null +++ b/test/asan/TestCases/current_allocated_bytes.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_asan -O0 %s -pthread -o %t && %run %t +// RUN: %clangxx_asan -O2 %s -pthread -o %t && %run %t +// REQUIRES: stable-runtime + +#include <assert.h> +#include <pthread.h> +#include <sanitizer/allocator_interface.h> +#include <stdio.h> +#include <stdlib.h> + +const size_t kLargeAlloc = 1UL << 20; + +void* allocate(void *arg) { + volatile void *ptr = malloc(kLargeAlloc); + free((void*)ptr); + return 0; +} + +void* check_stats(void *arg) { + assert(__sanitizer_get_current_allocated_bytes() > 0); + return 0; +} + +int main() { + size_t used_mem = __sanitizer_get_current_allocated_bytes(); + printf("Before: %zu\n", used_mem); + const int kNumIterations = 1000; + for (int iter = 0; iter < kNumIterations; iter++) { + pthread_t thr[4]; + for (int j = 0; j < 4; j++) { + assert(0 == + pthread_create(&thr[j], 0, (j < 2) ? allocate : check_stats, 0)); + } + for (int j = 0; j < 4; j++) + assert(0 == pthread_join(thr[j], 0)); + used_mem = __sanitizer_get_current_allocated_bytes(); + if (used_mem > kLargeAlloc) { + printf("After iteration %d: %zu\n", iter, used_mem); + return 1; + } + } + printf("Success after %d iterations\n", kNumIterations); + return 0; +} diff --git a/test/asan/TestCases/debug_locate.cc b/test/asan/TestCases/debug_locate.cc new file mode 100644 index 000000000000..5971a772786b --- /dev/null +++ b/test/asan/TestCases/debug_locate.cc @@ -0,0 +1,80 @@ +// Checks the ASan memory address type debugging API, makes sure it returns +// the correct memory type for heap, stack, global and shadow addresses and +// that it correctly finds out which region (and name and size) the address +// belongs to. +// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 + +#include <assert.h> +#include <sanitizer/asan_interface.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int global_var; + +int main() { + int local_var; + char *heap_ptr = (char *)malloc(10); + + char name[100]; + void *region_address; + size_t region_size; + const char *type; + + type = __asan_locate_address(&global_var, name, 100, + ®ion_address, ®ion_size); + assert(0 == strcmp(name, "global_var")); + assert(0 == strcmp(type, "global")); + assert(region_address == &global_var); + assert(region_size == sizeof(global_var)); + + type = __asan_locate_address((char *)(&global_var)+1, name, 100, + ®ion_address, ®ion_size); + assert(0 == strcmp(name, "global_var")); + assert(0 == strcmp(type, "global")); + assert(region_address == &global_var); + assert(region_size == sizeof(global_var)); + + type = __asan_locate_address(&local_var, name, 100, + ®ion_address, ®ion_size); + assert(0 == strcmp(name, "local_var")); + assert(0 == strcmp(type, "stack")); + assert(region_address == &local_var); + assert(region_size == sizeof(local_var)); + + type = __asan_locate_address((char *)(&local_var)+1, name, 100, + ®ion_address, ®ion_size); + assert(0 == strcmp(name, "local_var")); + assert(0 == strcmp(type, "stack")); + assert(region_address == &local_var); + assert(region_size == sizeof(local_var)); + + type = __asan_locate_address(heap_ptr, name, 100, + ®ion_address, ®ion_size); + assert(0 == strcmp(type, "heap")); + assert(region_address == heap_ptr); + assert(10 == region_size); + + type = __asan_locate_address(heap_ptr+1, name, 100, + ®ion_address, ®ion_size); + assert(0 == strcmp(type, "heap")); + assert(region_address == heap_ptr); + assert(10 == region_size); + + size_t shadow_scale; + size_t shadow_offset; + __asan_get_shadow_mapping(&shadow_scale, &shadow_offset); + + uintptr_t shadow_ptr = (((uintptr_t)heap_ptr) >> shadow_scale) + + shadow_offset; + type = __asan_locate_address((void *)shadow_ptr, NULL, 0, NULL, NULL); + assert((0 == strcmp(type, "high shadow")) || 0 == strcmp(type, "low shadow")); + + uintptr_t shadow_gap = (shadow_ptr >> shadow_scale) + shadow_offset; + type = __asan_locate_address((void *)shadow_gap, NULL, 0, NULL, NULL); + assert(0 == strcmp(type, "shadow gap")); + + free(heap_ptr); + + return 0; +} diff --git a/test/asan/TestCases/debug_mapping.cc b/test/asan/TestCases/debug_mapping.cc new file mode 100644 index 000000000000..f96abf6d11cf --- /dev/null +++ b/test/asan/TestCases/debug_mapping.cc @@ -0,0 +1,24 @@ +// Checks that the debugging API returns correct shadow scale and offset. +// RUN: %clangxx_asan -O %s -o %t +// RUN: env ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s + +#include <sanitizer/asan_interface.h> +#include <stdio.h> +#include <stdlib.h> + +// printed because of verbosity=1 +// CHECK: SHADOW_SCALE: [[SCALE:[0-9]+]] +// CHECK: SHADOW_OFFSET: [[OFFSET:[0-9]+]] + +int main() { + size_t scale, offset; + __asan_get_shadow_mapping(&scale, &offset); + + fprintf(stderr, "scale: %lx\n", scale); + fprintf(stderr, "offset: %lx\n", offset); + + // CHECK: scale: [[SCALE]] + // CHECK: offset: [[OFFSET]] + + return 0; +} diff --git a/test/asan/TestCases/debug_ppc64_mapping.cc b/test/asan/TestCases/debug_ppc64_mapping.cc new file mode 100644 index 000000000000..3ddd3e1404ce --- /dev/null +++ b/test/asan/TestCases/debug_ppc64_mapping.cc @@ -0,0 +1,37 @@ +// RUN: %clang_asan -O0 %s -o %t +// RUN: env ASAN_OPTIONS=verbosity=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PPC64-V0 +// RUN: env ASAN_OPTIONS=verbosity=2 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PPC64 +// REQUIRES: powerpc64-supported-target + +#include <stdio.h> + +int main() { +// CHECK-PPC64: || `[{{0x0a0|0x040}}000000000, {{0x3ff|0x0ff}}fffffffff]` || HighMem || +// CHECK-PPC64: || `[{{0x034|0x028}}000000000, {{0x09f|0x03f}}fffffffff]` || HighShadow || +// CHECK-PPC64: || `[{{0x024|0x024}}000000000, {{0x033|0x027}}fffffffff]` || ShadowGap || +// CHECK-PPC64: || `[0x020000000000, 0x023fffffffff]` || LowShadow || +// CHECK-PPC64: || `[0x000000000000, 0x01ffffffffff]` || LowMem || +// + printf("ppc64 eyecatcher \n"); +// CHECK-PPC64-V0: ppc64 eyecatcher + + return 0; +} + +/* + * Two different signatures noted at the time of writing. +Newish kernel: (64TB address range support, starting with kernel version 3.7) +|| `[0x0a0000000000, 0x3fffffffffff]` || HighMem || +|| `[0x034000000000, 0x09ffffffffff]` || HighShadow || +|| `[0x024000000000, 0x033fffffffff]` || ShadowGap || +|| `[0x020000000000, 0x023fffffffff]` || LowShadow || +|| `[0x000000000000, 0x01ffffffffff]` || LowMem || + +Oldish kernel: +|| `[0x040000000000, 0x0fffffffffff]` || HighMem || +|| `[0x028000000000, 0x03ffffffffff]` || HighShadow || +|| `[0x024000000000, 0x027fffffffff]` || ShadowGap || +|| `[0x020000000000, 0x023fffffffff]` || LowShadow || +|| `[0x000000000000, 0x01ffffffffff]` || LowMem || +*/ + diff --git a/test/asan/TestCases/debug_report.cc b/test/asan/TestCases/debug_report.cc new file mode 100644 index 000000000000..acf52f918dd8 --- /dev/null +++ b/test/asan/TestCases/debug_report.cc @@ -0,0 +1,48 @@ +// Checks that the ASan debugging API for getting report information +// returns correct values. +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <sanitizer/asan_interface.h> +#include <stdio.h> +#include <stdlib.h> + +int main() { + char *heap_ptr = (char *)malloc(10); + free(heap_ptr); + int present = __asan_report_present(); + fprintf(stderr, "%s\n", (present == 0) ? "no report" : ""); + // CHECK: no report + heap_ptr[0] = 'A'; // BOOM + return 0; +} + +void __asan_on_error() { + int present = __asan_report_present(); + void *pc = __asan_get_report_pc(); + void *bp = __asan_get_report_bp(); + void *sp = __asan_get_report_sp(); + void *addr = __asan_get_report_address(); + int is_write = __asan_get_report_access_type(); + size_t access_size = __asan_get_report_access_size(); + const char *description = __asan_get_report_description(); + + fprintf(stderr, "%s\n", (present == 1) ? "report" : ""); + // CHECK: report + fprintf(stderr, "pc: %p\n", pc); + // CHECK: pc: 0x[[PC:[0-9a-f]+]] + fprintf(stderr, "bp: %p\n", bp); + // CHECK: bp: 0x[[BP:[0-9a-f]+]] + fprintf(stderr, "sp: %p\n", sp); + // CHECK: sp: 0x[[SP:[0-9a-f]+]] + fprintf(stderr, "addr: %p\n", addr); + // CHECK: addr: 0x[[ADDR:[0-9a-f]+]] + fprintf(stderr, "type: %s\n", (is_write ? "write" : "read")); + // CHECK: type: write + fprintf(stderr, "access_size: %ld\n", access_size); + // CHECK: access_size: 1 + fprintf(stderr, "description: %s\n", description); + // CHECK: description: heap-use-after-free +} + +// CHECK: AddressSanitizer: heap-use-after-free on address {{0x0*}}[[ADDR]] at pc {{0x0*}}[[PC]] bp {{0x0*}}[[BP]] sp {{0x0*}}[[SP]] +// CHECK: WRITE of size 1 at {{0x0*}}[[ADDR]] thread T0 diff --git a/test/asan/TestCases/debug_stacks.cc b/test/asan/TestCases/debug_stacks.cc new file mode 100644 index 000000000000..57bb5465035a --- /dev/null +++ b/test/asan/TestCases/debug_stacks.cc @@ -0,0 +1,62 @@ +// Check that the stack trace debugging API works and returns correct +// malloc and free stacks. +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <sanitizer/asan_interface.h> +#include <stdio.h> +#include <stdlib.h> + +char *mem; +void func1() { + mem = (char *)malloc(10); +} + +void func2() { + free(mem); +} + +int main() { + func1(); + func2(); + + void *trace[100]; + size_t num_frames = 100; + int thread_id; + num_frames = __asan_get_alloc_stack(mem, trace, num_frames, &thread_id); + + fprintf(stderr, "alloc stack retval %s\n", (num_frames > 0 && num_frames < 10) + ? "ok" : ""); + // CHECK: alloc stack retval ok + fprintf(stderr, "thread id = %d\n", thread_id); + // CHECK: thread id = 0 + fprintf(stderr, "0x%lx\n", trace[0]); + // CHECK: [[ALLOC_FRAME_0:0x[0-9a-f]+]] + fprintf(stderr, "0x%lx\n", trace[1]); + // CHECK: [[ALLOC_FRAME_1:0x[0-9a-f]+]] + + num_frames = 100; + num_frames = __asan_get_free_stack(mem, trace, num_frames, &thread_id); + + fprintf(stderr, "free stack retval %s\n", (num_frames > 0 && num_frames < 10) + ? "ok" : ""); + // CHECK: free stack retval ok + fprintf(stderr, "thread id = %d\n", thread_id); + // CHECK: thread id = 0 + fprintf(stderr, "0x%lx\n", trace[0]); + // CHECK: [[FREE_FRAME_0:0x[0-9a-f]+]] + fprintf(stderr, "0x%lx\n", trace[1]); + // CHECK: [[FREE_FRAME_1:0x[0-9a-f]+]] + + mem[0] = 'A'; // BOOM + + // CHECK: ERROR: AddressSanitizer: heap-use-after-free + // CHECK: WRITE of size 1 at 0x{{.*}} + // CHECK: freed by thread T0 here: + // CHECK: #0 [[FREE_FRAME_0]] + // CHECK: #1 [[FREE_FRAME_1]] + // CHECK: previously allocated by thread T0 here: + // CHECK: #0 [[ALLOC_FRAME_0]] + // CHECK: #1 [[ALLOC_FRAME_1]] + + return 0; +} diff --git a/test/asan/TestCases/deep_call_stack.cc b/test/asan/TestCases/deep_call_stack.cc new file mode 100644 index 000000000000..789f23454d19 --- /dev/null +++ b/test/asan/TestCases/deep_call_stack.cc @@ -0,0 +1,25 @@ +// Check that UAR mode can handle very deep recusrion. +// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: (ulimit -s 4096; %run %t) 2>&1 | FileCheck %s +// Also check that use_sigaltstack+verbosity doesn't crash. +// RUN: env ASAN_OPTIONS=verbosity=1:use_sigaltstack=1 %run %t | FileCheck %s +#include <stdio.h> + +__attribute__((noinline)) +void RecursiveFunc(int depth, int *ptr) { + if ((depth % 1000) == 0) + printf("[%05d] ptr: %p\n", depth, ptr); + if (depth == 0) + return; + int local; + RecursiveFunc(depth - 1, &local); +} + +int main(int argc, char **argv) { + RecursiveFunc(15000, 0); + return 0; +} +// CHECK: [15000] ptr: +// CHECK: [07000] ptr: +// CHECK: [00000] ptr: diff --git a/test/asan/TestCases/deep_stack_uaf.cc b/test/asan/TestCases/deep_stack_uaf.cc new file mode 100644 index 000000000000..3e88d697fcef --- /dev/null +++ b/test/asan/TestCases/deep_stack_uaf.cc @@ -0,0 +1,36 @@ +// Check that we can store lots of stack frames if asked to. + +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 +// RUN: env ASAN_OPTIONS=malloc_context_size=120:redzone=512 not %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf +#include <stdlib.h> +#include <stdio.h> + +template <int depth> +struct DeepFree { + static void free(char *x) { + DeepFree<depth - 1>::free(x); + } +}; + +template<> +struct DeepFree<0> { + static void free(char *x) { + ::free(x); + } +}; + +int main() { + char *x = (char*)malloc(10); + // deep_free(x); + DeepFree<200>::free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // The libcxxrt demangling procedure on FreeBSD 9.2 incorrectly appends + // extra 'E' characters to the end of template arguments; see: + // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=192115 + // CHECK: {{DeepFree<36>|DeepFree<36E>}} + // CHECK: {{DeepFree<98>|DeepFree<98E>}} + // CHECK: {{DeepFree<115>|DeepFree<115E>}} +} diff --git a/test/asan/TestCases/deep_tail_call.cc b/test/asan/TestCases/deep_tail_call.cc new file mode 100644 index 000000000000..628ef06db144 --- /dev/null +++ b/test/asan/TestCases/deep_tail_call.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// CHECK: AddressSanitizer: global-buffer-overflow +int global[10]; +// CHECK: {{#0.*call4}} +void __attribute__((noinline)) call4(int i) { global[i+10]++; } +// CHECK: {{#1.*call3}} +void __attribute__((noinline)) call3(int i) { call4(i); } +// CHECK: {{#2.*call2}} +void __attribute__((noinline)) call2(int i) { call3(i); } +// CHECK: {{#3.*call1}} +void __attribute__((noinline)) call1(int i) { call2(i); } +// CHECK: {{#4.*main}} +int main(int argc, char **argv) { + call1(argc); + return global[0]; +} diff --git a/test/asan/TestCases/deep_thread_stack.cc b/test/asan/TestCases/deep_thread_stack.cc new file mode 100644 index 000000000000..535da79ff58d --- /dev/null +++ b/test/asan/TestCases/deep_thread_stack.cc @@ -0,0 +1,58 @@ +// RUN: %clangxx_asan -O0 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s +// REQUIRES: stable-runtime + +#include <pthread.h> + +int *x; + +void *AllocThread(void *arg) { + x = new int; + *x = 42; + return NULL; +} + +void *FreeThread(void *arg) { + delete x; + return NULL; +} + +void *AccessThread(void *arg) { + *x = 43; // BOOM + return NULL; +} + +typedef void* (*callback_type)(void* arg); + +void *RunnerThread(void *function) { + pthread_t thread; + pthread_create(&thread, NULL, (callback_type)function, NULL); + pthread_join(thread, NULL); + return NULL; +} + +void RunThread(callback_type function) { + pthread_t runner; + pthread_create(&runner, NULL, RunnerThread, (void*)function); + pthread_join(runner, NULL); +} + +int main(int argc, char *argv[]) { + RunThread(AllocThread); + RunThread(FreeThread); + RunThread(AccessThread); + return (x != 0); +} + +// CHECK: AddressSanitizer: heap-use-after-free +// CHECK: WRITE of size 4 at 0x{{.*}} thread T[[ACCESS_THREAD:[0-9]+]] +// CHECK: freed by thread T[[FREE_THREAD:[0-9]+]] here: +// CHECK: previously allocated by thread T[[ALLOC_THREAD:[0-9]+]] here: +// CHECK: Thread T[[ACCESS_THREAD]] created by T[[ACCESS_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[ACCESS_RUNNER]] created by T0 here: +// CHECK: Thread T[[FREE_THREAD]] created by T[[FREE_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[FREE_RUNNER]] created by T0 here: +// CHECK: Thread T[[ALLOC_THREAD]] created by T[[ALLOC_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[ALLOC_RUNNER]] created by T0 here: diff --git a/test/asan/TestCases/default_blacklist.cc b/test/asan/TestCases/default_blacklist.cc new file mode 100644 index 000000000000..9358cc47cbaa --- /dev/null +++ b/test/asan/TestCases/default_blacklist.cc @@ -0,0 +1,6 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// Test that ASan uses the default blacklist from resource directory. +// RUN: %clangxx_asan -### %s 2>&1 | FileCheck %s +// CHECK: fsanitize-blacklist={{.*}}asan_blacklist.txt diff --git a/test/asan/TestCases/default_options.cc b/test/asan/TestCases/default_options.cc new file mode 100644 index 000000000000..6453f66a9523 --- /dev/null +++ b/test/asan/TestCases/default_options.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// __asan_default_options() are not supported on Windows. +// XFAIL: win32 + +const char *kAsanDefaultOptions="verbosity=1 foo=bar"; + +extern "C" +__attribute__((no_sanitize_address)) +const char *__asan_default_options() { + // CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar + return kAsanDefaultOptions; +} + +int main() { + return 0; +} diff --git a/test/asan/TestCases/describe_address.cc b/test/asan/TestCases/describe_address.cc new file mode 100644 index 000000000000..868c0eb1c446 --- /dev/null +++ b/test/asan/TestCases/describe_address.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <sanitizer/asan_interface.h> + +int global; + +int main(int argc, char *argv[]) { + int stack; + int *heap = new int[100]; + __asan_describe_address(heap); + // CHECK: {{.*}} is located 0 bytes inside of 400-byte region + // CHECK: allocated by thread T{{.*}} here + __asan_describe_address(&stack); + // CHECK: Address {{.*}} is located in stack of thread T{{.*}} at offset {{.*}} + __asan_describe_address(&global); + // CHECK: {{.*}} is located 0 bytes inside of global variable 'global' + delete[] heap; + return 0; +} diff --git a/test/asan/TestCases/dlclose-test.cc b/test/asan/TestCases/dlclose-test.cc new file mode 100644 index 000000000000..094453f3de2a --- /dev/null +++ b/test/asan/TestCases/dlclose-test.cc @@ -0,0 +1,99 @@ +// Regression test for +// http://code.google.com/p/address-sanitizer/issues/detail?id=19 +// Bug description: +// 1. application dlopens foo.so +// 2. asan registers all globals from foo.so +// 3. application dlcloses foo.so +// 4. application mmaps some memory to the location where foo.so was before +// 5. application starts using this mmaped memory, but asan still thinks there +// are globals. +// 6. BOOM + +// This sublte test assumes that after a foo.so is dlclose-d +// we can mmap the region of memory that has been occupied by the library. +// It works on i368/x86_64 Linux, but not necessary anywhere else. +// REQUIRES: x86_64-supported-target,i386-supported-target + +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O2 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O3 %s -o %t && %run %t 2>&1 | FileCheck %s + +#if !defined(SHARED_LIB) +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +#include <string> + +using std::string; + +typedef int *(fun_t)(); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + size_t PageSize = sysconf(_SC_PAGESIZE); + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *get = (fun_t*)dlsym(lib, "get_address_of_static_var"); + if (!get) { + printf("failed dlsym\n"); + return 1; + } + int *addr = get(); + assert(((size_t)addr % 32) == 0); // should be 32-byte aligned. + printf("addr: %p\n", addr); + addr[0] = 1; // make sure we can write there. + + // Now dlclose the shared library. + printf("attempting to dlclose\n"); + if (dlclose(lib)) { + printf("failed to dlclose\n"); + return 1; + } + // Now, the page where 'addr' is unmapped. Map it. + size_t page_beg = ((size_t)addr) & ~(PageSize - 1); + void *res = mmap((void*)(page_beg), PageSize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, -1, 0); + if (res == (char*)-1L) { + printf("failed to mmap\n"); + return 1; + } + addr[1] = 2; // BOOM (if the bug is not fixed). + printf("PASS\n"); + // CHECK: PASS + return 0; +} +#else // SHARED_LIB +#include <stdio.h> + +static int pad1; +static int static_var; +static int pad2; + +extern "C" +int *get_address_of_static_var() { + return &static_var; +} + +__attribute__((constructor)) +void at_dlopen() { + printf("%s: I am being dlopened\n", __FILE__); +} +__attribute__((destructor)) +void at_dlclose() { + printf("%s: I am being dlclosed\n", __FILE__); +} +#endif // SHARED_LIB diff --git a/test/asan/TestCases/double-free.cc b/test/asan/TestCases/double-free.cc new file mode 100644 index 000000000000..f0dd29174849 --- /dev/null +++ b/test/asan/TestCases/double-free.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX + +// Also works if no malloc context is available. +// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc]; + free(x); + free(x + argc - 1); // BOOM + // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0 + // CHECK: #0 0x{{.*}} in {{.*}}free + // CHECK: #1 0x{{.*}} in main {{.*}}double-free.cc:[[@LINE-3]] + // CHECK: freed by thread T0 here: + // MALLOC-CTX: #0 0x{{.*}} in {{.*}}free + // MALLOC-CTX: #1 0x{{.*}} in main {{.*}}double-free.cc:[[@LINE-7]] + // CHECK: allocated by thread T0 here: + // MALLOC-CTX: double-free.cc:[[@LINE-12]] + return res; +} diff --git a/test/asan/TestCases/dump_instruction_bytes.cc b/test/asan/TestCases/dump_instruction_bytes.cc new file mode 100644 index 000000000000..981e3c31327f --- /dev/null +++ b/test/asan/TestCases/dump_instruction_bytes.cc @@ -0,0 +1,20 @@ +// Check that ASan prints the faulting instruction bytes on +// dump_instruction_bytes=1 +// RUN: %clangxx_asan %s -o %t +// RUN: env ASAN_OPTIONS=dump_instruction_bytes=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-DUMP +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NODUMP +// +// REQUIRES: x86_64-supported-target,i386-supported-target + +int main() { +#if defined(__x86_64__) + asm("movq $0, %rax"); + asm("movl $0xcafebabe, 0x0(%rax)"); +#elif defined(i386) + asm("movl $0, %eax"); + asm("movl $0xcafebabe, 0x0(%eax)"); +#endif + // CHECK-DUMP: First 16 instruction bytes at pc: c7 00 be ba fe ca + // CHECK-NODUMP-NOT: First 16 instruction bytes + return 0; +} diff --git a/test/asan/TestCases/force_inline_opt0.cc b/test/asan/TestCases/force_inline_opt0.cc new file mode 100644 index 000000000000..e6e5d26c7998 --- /dev/null +++ b/test/asan/TestCases/force_inline_opt0.cc @@ -0,0 +1,14 @@ +// This test checks that we are no instrumenting a memory access twice +// (before and after inlining) +// RUN: %clangxx_asan -O1 %s -o %t && %run %t +// RUN: %clangxx_asan -O0 %s -o %t && %run %t +__attribute__((always_inline)) +void foo(int *x) { + *x = 0; +} + +int main() { + int x; + foo(&x); + return x; +} diff --git a/test/asan/TestCases/free_hook_realloc.cc b/test/asan/TestCases/free_hook_realloc.cc new file mode 100644 index 000000000000..4b2753252a8d --- /dev/null +++ b/test/asan/TestCases/free_hook_realloc.cc @@ -0,0 +1,37 @@ +// Check that free hook doesn't conflict with Realloc. +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// Malloc/free hooks are not supported on Windows. +// XFAIL: win32 + +#include <stdlib.h> +#include <unistd.h> +#include <sanitizer/allocator_interface.h> + +static void *glob_ptr; + +extern "C" { +void __sanitizer_free_hook(const volatile void *ptr) { + if (ptr == glob_ptr) { + *(int*)ptr = 0; + write(1, "FreeHook\n", sizeof("FreeHook\n")); + } +} +} + +int main() { + int *x = (int*)malloc(100); + x[0] = 42; + glob_ptr = x; + int *y = (int*)realloc(x, 200); + // Verify that free hook was called and didn't spoil the memory. + if (y[0] != 42) { + _exit(1); + } + write(1, "Passed\n", sizeof("Passed\n")); + free(y); + // CHECK: FreeHook + // CHECK: Passed + return 0; +} diff --git a/test/asan/TestCases/frexp_interceptor.cc b/test/asan/TestCases/frexp_interceptor.cc new file mode 100644 index 000000000000..d75ba992b650 --- /dev/null +++ b/test/asan/TestCases/frexp_interceptor.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test the frexp() interceptor. + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +int main() { + double x = 3.14; + int *exp = (int*)malloc(sizeof(int)); + free(exp); + double y = frexp(x, exp); + // CHECK: use-after-free + // CHECK: SUMMARY + return 0; +} diff --git a/test/asan/TestCases/gc-test.cc b/test/asan/TestCases/gc-test.cc new file mode 100644 index 000000000000..ffbea85b2650 --- /dev/null +++ b/test/asan/TestCases/gc-test.cc @@ -0,0 +1,50 @@ +// RUN: %clangxx_asan %s -pthread -o %t +// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0 +// REQUIRES: stable-runtime + +#include <assert.h> +#include <stdio.h> +#include <pthread.h> +#include <sanitizer/asan_interface.h> + +static const int kNumThreads = 2; + +void *Thread(void *unused) { + void *fake_stack = __asan_get_current_fake_stack(); + char var[15]; + if (fake_stack) { + fprintf(stderr, "fake stack found: %p; var: %p\n", fake_stack, var); + // CHECK1: fake stack found + // CHECK1: fake stack found + void *beg, *end; + void *real_stack = + __asan_addr_is_in_fake_stack(fake_stack, &var[0], &beg, &end); + assert(real_stack); + assert((char*)beg <= (char*)&var[0]); + assert((char*)end > (char*)&var[0]); + for (int i = -32; i < 15; i++) { + void *beg1, *end1; + char *ptr = &var[0] + i; + void *real_stack1 = + __asan_addr_is_in_fake_stack(fake_stack, ptr, &beg1, &end1); + assert(real_stack == real_stack1); + assert(beg == beg1); + assert(end == end1); + } + } else { + fprintf(stderr, "no fake stack\n"); + // CHECK0: no fake stack + // CHECK0: no fake stack + } + return NULL; +} + +int main(int argc, char **argv) { + pthread_t t[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) + pthread_create(&t[i], 0, Thread, 0); + for (int i = 0; i < kNumThreads; i++) + pthread_join(t[i], 0); + return 0; +} diff --git a/test/asan/TestCases/global-demangle.cc b/test/asan/TestCases/global-demangle.cc new file mode 100644 index 000000000000..5f7ff91b1601 --- /dev/null +++ b/test/asan/TestCases/global-demangle.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +namespace XXX { +class YYY { + public: + static char ZZZ[]; +}; +char YYY::ZZZ[] = "abc"; +} + +int main(int argc, char **argv) { + return (int)XXX::YYY::ZZZ[argc + 5]; // BOOM + // CHECK: {{READ of size 1 at 0x.*}} + // CHECK: {{0x.* is located 2 bytes to the right of global variable}} + // CHECK: 'XXX::YYY::ZZZ' {{.*}} of size 4 + // CHECK: 'XXX::YYY::ZZZ' is ascii string 'abc' +} diff --git a/test/asan/TestCases/global-location.cc b/test/asan/TestCases/global-location.cc new file mode 100644 index 000000000000..795e50bf614e --- /dev/null +++ b/test/asan/TestCases/global-location.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %run %t g 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=GLOB +// RUN: not %run %t c 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CLASS_STATIC +// RUN: not %run %t f 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=FUNC_STATIC +// RUN: not %run %t l 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=LITERAL + +// CHECK: AddressSanitizer: global-buffer-overflow + +#include <string.h> + +struct C { + static int array[10]; +}; + +int global[10]; +// GLOB: 0x{{.*}} is located 4 bytes to the right of global variable 'global' defined in '{{.*}}global-location.cc:[[@LINE-1]]:5' {{.*}} of size 40 +int C::array[10]; +// CLASS_STATIC: 0x{{.*}} is located 4 bytes to the right of global variable 'C::array' defined in '{{.*}}global-location.cc:[[@LINE-1]]:8' {{.*}} of size 40 + +int main(int argc, char **argv) { + int one = argc - 1; + switch (argv[1][0]) { + case 'g': return global[one * 11]; + case 'c': return C::array[one * 11]; + case 'f': + static int array[10]; + // FUNC_STATIC: 0x{{.*}} is located 4 bytes to the right of global variable 'array' defined in '{{.*}}global-location.cc:[[@LINE-1]]:16' {{.*}} of size 40 + memset(array, 0, 10); + return array[one * 11]; + case 'l': + const char *str = "0123456789"; + // LITERAL: 0x{{.*}} is located 0 bytes to the right of global variable {{.*}} defined in '{{.*}}global-location.cc:[[@LINE-1]]:23' {{.*}} of size 11 + return str[one * 11]; + } + return 0; +} + +// CHECK: SUMMARY: AddressSanitizer: global-buffer-overflow diff --git a/test/asan/TestCases/global-overflow.cc b/test/asan/TestCases/global-overflow.cc new file mode 100644 index 000000000000..a39a95306352 --- /dev/null +++ b/test/asan/TestCases/global-overflow.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <string.h> +int main(int argc, char **argv) { + static char XXX[10]; + static char YYY[10]; + static char ZZZ[10]; + memset(XXX, 0, 10); + memset(YYY, 0, 10); + memset(ZZZ, 0, 10); + int res = YYY[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*global-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of global variable}} + // CHECK: {{.*YYY.* of size 10}} + res += XXX[argc] + ZZZ[argc]; + return res; +} diff --git a/test/asan/TestCases/heap-overflow-large.cc b/test/asan/TestCases/heap-overflow-large.cc new file mode 100644 index 000000000000..eb2fcc3220e7 --- /dev/null +++ b/test/asan/TestCases/heap-overflow-large.cc @@ -0,0 +1,23 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=183 + +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %run %t 12 2>&1 | FileCheck %s +// RUN: not %run %t 100 2>&1 | FileCheck %s +// RUN: not %run %t 10000 2>&1 | FileCheck %s + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +int main(int argc, char *argv[]) { + fprintf(stderr, "main\n"); + int *x = new int[5]; + memset(x, 0, sizeof(x[0]) * 5); + int index = atoi(argv[1]); + int res = x[index]; + // CHECK: main + // CHECK-NOT: CHECK failed + delete[] x; + return res ? res : 1; +} diff --git a/test/asan/TestCases/heap-overflow.cc b/test/asan/TestCases/heap-overflow.cc new file mode 100644 index 000000000000..70a1203562be --- /dev/null +++ b/test/asan/TestCases/heap-overflow.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=print_stats=1 not %run %t 2>&1 | FileCheck %s + +// FIXME: Fix this test under GCC. +// REQUIRES: Clang + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*heap-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of 10-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // CHECK: {{ #0 0x.* in .*malloc}} + free(x); + return res; +} diff --git a/test/asan/TestCases/heavy_uar_test.cc b/test/asan/TestCases/heavy_uar_test.cc new file mode 100644 index 000000000000..1f8caea21690 --- /dev/null +++ b/test/asan/TestCases/heavy_uar_test.cc @@ -0,0 +1,60 @@ +// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi + +// FIXME: Fix this test under GCC. +// REQUIRES: Clang + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +__attribute__((noinline)) +char *pretend_to_do_something(char *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); + return x; +} + +__attribute__((noinline)) +char *LeakStack() { + char x[1024]; + memset(x, 0, sizeof(x)); + return pretend_to_do_something(x); +} + +template<size_t kFrameSize> +__attribute__((noinline)) +void RecursiveFunctionWithStackFrame(int depth) { + if (depth <= 0) return; + char x[kFrameSize]; + x[0] = depth; + pretend_to_do_something(x); + RecursiveFunctionWithStackFrame<kFrameSize>(depth - 1); +} + +int main(int argc, char **argv) { + int n_iter = argc >= 2 ? atoi(argv[1]) : 1000; + int depth = argc >= 3 ? atoi(argv[2]) : 500; + for (int i = 0; i < n_iter; i++) { + RecursiveFunctionWithStackFrame<10>(depth); + RecursiveFunctionWithStackFrame<100>(depth); + RecursiveFunctionWithStackFrame<500>(depth); + RecursiveFunctionWithStackFrame<1024>(depth); + RecursiveFunctionWithStackFrame<2000>(depth); + // The stack size is tight for the main thread in multithread + // environment on FreeBSD. +#if !defined(__FreeBSD__) + RecursiveFunctionWithStackFrame<5000>(depth); + RecursiveFunctionWithStackFrame<10000>(depth); +#endif + } + char *stale_stack = LeakStack(); + RecursiveFunctionWithStackFrame<1024>(10); + stale_stack[100]++; + // CHECK: ERROR: AddressSanitizer: stack-use-after-return on address + // CHECK: is located in stack of thread T0 at offset {{116|132}} in frame + // CHECK: in LeakStack{{.*}}heavy_uar_test.cc: + // CHECK: [{{16|32}}, {{1040|1056}}) 'x' + return 0; +} diff --git a/test/asan/TestCases/huge_negative_hea_oob.cc b/test/asan/TestCases/huge_negative_hea_oob.cc new file mode 100644 index 000000000000..96e7e613d4bb --- /dev/null +++ b/test/asan/TestCases/huge_negative_hea_oob.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O %s -o %t && not %run %t 2>&1 | FileCheck %s +// Check that we can find huge buffer overflows to the left. +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(1 << 20); + memset(x, 0, 10); + int res = x[-argc * 4000]; // BOOOM + // CHECK: is located 4000 bytes to the left of + free(x); + return res; +} diff --git a/test/asan/TestCases/init-order-atexit.cc b/test/asan/TestCases/init-order-atexit.cc new file mode 100644 index 000000000000..e0dac325ce58 --- /dev/null +++ b/test/asan/TestCases/init-order-atexit.cc @@ -0,0 +1,34 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// Test for the following situation: +// (1) global A is constructed. +// (2) exit() is called during construction of global B. +// (3) destructor of A reads uninitialized global C from another module. +// We do *not* want to report init-order bug in this case. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/init-order-atexit-extra.cc -o %t +// RUN: env ASAN_OPTIONS=strict_init_order=true not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +void AccessC(); + +class A { + public: + A() { } + ~A() { AccessC(); printf("PASSED\n"); } + // CHECK-NOT: AddressSanitizer + // CHECK: PASSED +}; + +A a; + +class B { + public: + B() { exit(1); } + ~B() { } +}; + +B b; diff --git a/test/asan/TestCases/init-order-pthread-create.cc b/test/asan/TestCases/init-order-pthread-create.cc new file mode 100644 index 000000000000..eeff308a4cd5 --- /dev/null +++ b/test/asan/TestCases/init-order-pthread-create.cc @@ -0,0 +1,32 @@ +// Check that init-order checking is properly disabled if pthread_create is +// called. + +// RUN: %clangxx_asan %s %p/Helpers/init-order-pthread-create-extra.cc -pthread -o %t +// RUN: env ASAN_OPTIONS=strict_init_order=true %run %t + +#include <stdio.h> +#include <pthread.h> + +void *run(void *arg) { + return arg; +} + +void *foo(void *input) { + pthread_t t; + pthread_create(&t, 0, run, input); + void *res; + pthread_join(t, &res); + return res; +} + +void *bar(void *input) { + return input; +} + +void *glob = foo((void*)0x1234); +extern void *glob2; + +int main() { + printf("%p %p\n", glob, glob2); + return 0; +} diff --git a/test/asan/TestCases/initialization-blacklist.cc b/test/asan/TestCases/initialization-blacklist.cc new file mode 100644 index 000000000000..8ea6b46c1833 --- /dev/null +++ b/test/asan/TestCases/initialization-blacklist.cc @@ -0,0 +1,29 @@ +// Test for blacklist functionality of initialization-order checker. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 + +// Function is defined in another TU. +int readBadGlobal(); +int x = readBadGlobal(); // init-order bug. + +// Function is defined in another TU. +int accessBadObject(); +int y = accessBadObject(); // init-order bug. + +int readBadSrcGlobal(); +int z = readBadSrcGlobal(); // init-order bug. + +int main(int argc, char **argv) { + return argc + x + y + z - 1; +} diff --git a/test/asan/TestCases/initialization-bug.cc b/test/asan/TestCases/initialization-bug.cc new file mode 100644 index 000000000000..badc6d1d1165 --- /dev/null +++ b/test/asan/TestCases/initialization-bug.cc @@ -0,0 +1,45 @@ +// Test to make sure basic initialization order errors are caught. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true not %run %t 2>&1 | FileCheck %s + +// Do not test with optimization -- the error may be optimized away. + +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=186 +// XFAIL: darwin + +#include <cstdio> + +// The structure of the test is: +// "x", "y", "z" are dynamically initialized globals. +// Value of "x" depends on "y", value of "y" depends on "z". +// "x" and "z" are defined in this TU, "y" is defined in another one. +// Thus we shoud stably report initialization order fiasco independently of +// the translation unit order. + +int initZ() { + return 5; +} +int z = initZ(); + +// 'y' is a dynamically initialized global residing in a different TU. This +// dynamic initializer will read the value of 'y' before main starts. The +// result is undefined behavior, which should be caught by initialization order +// checking. +extern int y; +int __attribute__((noinline)) initX() { + return y + 1; + // CHECK: {{AddressSanitizer: initialization-order-fiasco}} + // CHECK: {{READ of size .* at 0x.* thread T0}} + // CHECK: {{0x.* is located 0 bytes inside of global variable .*(y|z).*}} +} + +// This initializer begins our initialization order problems. +static int x = initX(); + +int main() { + // ASan should have caused an exit before main runs. + printf("PASS\n"); + // CHECK-NOT: PASS + return 0; +} diff --git a/test/asan/TestCases/initialization-constexpr.cc b/test/asan/TestCases/initialization-constexpr.cc new file mode 100644 index 000000000000..644246186e02 --- /dev/null +++ b/test/asan/TestCases/initialization-constexpr.cc @@ -0,0 +1,27 @@ +// Constexpr: +// We need to check that a global variable initialized with a constexpr +// constructor can be accessed during dynamic initialization (as a constexpr +// constructor implies that it was initialized during constant initialization, +// not dynamic initialization). + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-constexpr-extra.cc --std=c++11 -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-constexpr-extra.cc --std=c++11 -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-constexpr-extra.cc --std=c++11 -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-constexpr-extra.cc --std=c++11 -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 + +class Integer { + private: + int value; + + public: + constexpr Integer(int x = 0) : value(x) {} + int getValue() {return value;} +}; +Integer coolestInteger(42); +int getCoolestInteger() { return coolestInteger.getValue(); } + +int main() { return 0; } diff --git a/test/asan/TestCases/initialization-nobug.cc b/test/asan/TestCases/initialization-nobug.cc new file mode 100644 index 000000000000..1249deb425aa --- /dev/null +++ b/test/asan/TestCases/initialization-nobug.cc @@ -0,0 +1,48 @@ +// A collection of various initializers which shouldn't trip up initialization +// order checking. If successful, this will just return 0. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-nobug-extra.cc -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-nobug-extra.cc -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-nobug-extra.cc -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-nobug-extra.cc -o %t +// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 + +// Simple access: +// Make sure that accessing a global in the same TU is safe + +bool condition = true; +int initializeSameTU() { + return condition ? 0x2a : 052; +} +int sameTU = initializeSameTU(); + +// Linker initialized: +// Check that access to linker initialized globals originating from a different +// TU's initializer is safe. + +int A = (1 << 1) + (1 << 3) + (1 << 5), B; +int getAB() { + return A * B; +} + +// Function local statics: +// Check that access to function local statics originating from a different +// TU's initializer is safe. + +int countCalls() { + static int calls; + return ++calls; +} + +// Trivial constructor, non-trivial destructor. +struct StructWithDtor { + ~StructWithDtor() { } + int value; +}; +StructWithDtor struct_with_dtor; +int getStructWithDtorValue() { return struct_with_dtor.value; } + +int main() { return 0; } diff --git a/test/asan/TestCases/inline.cc b/test/asan/TestCases/inline.cc new file mode 100644 index 000000000000..daeb7b49eb22 --- /dev/null +++ b/test/asan/TestCases/inline.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O3 %s -o %t && %run %t + +// Test that no_sanitize_address attribute applies even when the function would +// be normally inlined. + +#include <stdlib.h> + +__attribute__((no_sanitize_address)) +int f(int *p) { + return *p; // BOOOM?? Nope! +} + +int main(int argc, char **argv) { + int * volatile x = (int*)malloc(2*sizeof(int) + 2); + int res = f(x + 2); + if (res) + exit(0); + return 0; +} diff --git a/test/asan/TestCases/interception_failure_test.cc b/test/asan/TestCases/interception_failure_test.cc new file mode 100644 index 000000000000..a23fe6938ca9 --- /dev/null +++ b/test/asan/TestCases/interception_failure_test.cc @@ -0,0 +1,22 @@ +// If user provides his own libc functions, ASan doesn't +// intercept these functions. + +// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> + +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return 0; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK-NOT: heap-use-after-free +} diff --git a/test/asan/TestCases/interface_test.cc b/test/asan/TestCases/interface_test.cc new file mode 100644 index 000000000000..dc9d0652c8c3 --- /dev/null +++ b/test/asan/TestCases/interface_test.cc @@ -0,0 +1,10 @@ +// Check that user may include ASan interface header. +// RUN: %clang_asan %s -o %t && %run %t +// RUN: %clang_asan -x c %s -o %t && %run %t +// RUN: %clang %s -o %t && %run %t +// RUN: %clang -x c %s -o %t && %run %t +#include <sanitizer/asan_interface.h> + +int main() { + return 0; +} diff --git a/test/asan/TestCases/intra-object-overflow.cc b/test/asan/TestCases/intra-object-overflow.cc new file mode 100644 index 000000000000..e48a261f55cc --- /dev/null +++ b/test/asan/TestCases/intra-object-overflow.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_asan -O0 -fsanitize-address-field-padding=1 %s -o %t +// RUN: not %run %t 11 2>&1 | FileCheck %s +// RUN: %run %t 10 +// +// FIXME: fix 32-bits. +// REQUIRES: asan-64-bits +#include <stdio.h> +#include <stdlib.h> +class Foo { + public: + Foo() : pre1(1), pre2(2), post1(3), post2(4) { + } + virtual ~Foo() { + } + void set(int i, int val) { a[i] = val; } +// CHECK: ERROR: AddressSanitizer: intra-object-overflow +// CHECK: #0 {{.*}}Foo::set{{.*}}intra-object-overflow.cc:[[@LINE-2]] + private: + int pre1, pre2; + int a[11]; + int post1, post2; +}; + +int main(int argc, char **argv) { + int idx = argc == 2 ? atoi(argv[1]) : 0; + Foo *foo = new Foo; + foo->set(idx, 42); +// CHECK: #1 {{.*}}main{{.*}}intra-object-overflow.cc:[[@LINE-1]] +// CHECK: is located 84 bytes inside of 128-byte region + delete foo; +} diff --git a/test/asan/TestCases/invalid-free.cc b/test/asan/TestCases/invalid-free.cc new file mode 100644 index 000000000000..cb545ccc215e --- /dev/null +++ b/test/asan/TestCases/invalid-free.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX + +// Also works if no malloc context is available. +// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc]; + free(x + 5); // BOOM + // CHECK: AddressSanitizer: attempting free on address{{.*}}in thread T0 + // CHECK: invalid-free.cc:[[@LINE-2]] + // CHECK: is located 5 bytes inside of 10-byte region + // CHECK: allocated by thread T0 here: + // MALLOC-CTX: invalid-free.cc:[[@LINE-8]] + return res; +} diff --git a/test/asan/TestCases/large_func_test.cc b/test/asan/TestCases/large_func_test.cc new file mode 100644 index 000000000000..6b592f8c4397 --- /dev/null +++ b/test/asan/TestCases/large_func_test.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <stdlib.h> +__attribute__((noinline)) +static void LargeFunction(int *x, int zero) { + x[0]++; + x[1]++; + x[2]++; + x[3]++; + x[4]++; + x[5]++; + x[6]++; + x[7]++; + x[8]++; + x[9]++; + + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + x[zero + 103]++; // we should report this exact line + // atos incorrectly extracts the symbol name for the static functions on + // Darwin. + // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]] + // CHECK-Darwin: {{#0 0x.* in .*LargeFunction.*large_func_test.cc}}:[[@LINE-4]] + + x[10]++; + x[11]++; + x[12]++; + x[13]++; + x[14]++; + x[15]++; + x[16]++; + x[17]++; + x[18]++; + x[19]++; +} + +int main(int argc, char **argv) { + int *x = new int[100]; + LargeFunction(x, argc - 1); + // CHECK: {{ #1 0x.* in main .*large_func_test.cc:}}[[@LINE-1]] + // CHECK: {{0x.* is located 12 bytes to the right of 400-byte region}} + // CHECK: {{allocated by thread T0 here:}} + // CHECK-Linux: {{ #0 0x.* in operator new.*}} + // CHECK-Darwin: {{ #0 0x.* in .*_Zna.*}} + // CHECK: {{ #1 0x.* in main .*large_func_test.cc:}}[[@LINE-7]] + delete x; +} diff --git a/test/asan/TestCases/log-path_test.cc b/test/asan/TestCases/log-path_test.cc new file mode 100644 index 000000000000..5a1d0729119a --- /dev/null +++ b/test/asan/TestCases/log-path_test.cc @@ -0,0 +1,44 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// RUN: %clangxx_asan %s -o %t + +// Regular run. +// RUN: not %run %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.out + +// Good log_path. +// RUN: rm -f %t.log.* +// RUN: env ASAN_OPTIONS=log_path=%t.log not %run %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.* + +// Invalid log_path. +// RUN: env ASAN_OPTIONS=log_path=/INVALID not %run %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-INVALID < %t.out + +// Too long log_path. +// RUN: env ASAN_OPTIONS=log_path=`for((i=0;i<10000;i++)); do echo -n $i; done` \ +// RUN: not %run %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-LONG < %t.out + +// Run w/o errors should not produce any log. +// RUN: rm -f %t.log.* +// RUN: env ASAN_OPTIONS=log_path=%t.log %run %t ARG ARG ARG +// RUN: not cat %t.log.* + +// FIXME: log_path is not supported on Windows yet. +// XFAIL: win32 + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + if (argc > 2) return 0; + char *x = (char*)malloc(10); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + free(x); + return res; +} +// CHECK-ERROR: ERROR: AddressSanitizer +// CHECK-INVALID: ERROR: Can't open file: /INVALID +// CHECK-LONG: ERROR: Path is too long: 01234 diff --git a/test/asan/TestCases/log_path_fork_test.cc.disabled b/test/asan/TestCases/log_path_fork_test.cc.disabled new file mode 100644 index 000000000000..cfe90dfb54d3 --- /dev/null +++ b/test/asan/TestCases/log_path_fork_test.cc.disabled @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan %s -o %t +// RUN: rm -f %t.log.* +// Set verbosity to 1 so that the log files are opened prior to fork(). +// RUN: env ASAN_OPTIONS="log_path=%t.log verbosity=1" not %run %t 2> %t.out +// RUN: for f in %t.log.* ; do FileCheck %s < $f; done +// RUN: [ `ls %t.log.* | wc -l` == 2 ] + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int main(int argc, char **argv) { + void *x = malloc(10); + free(x); + if (fork() == -1) return 1; + // There are two processes at this point, thus there should be two distinct + // error logs. + free(x); + return 0; +} + +// CHECK: ERROR: AddressSanitizer diff --git a/test/asan/TestCases/longjmp.cc b/test/asan/TestCases/longjmp.cc new file mode 100644 index 000000000000..8e9f2ae195c7 --- /dev/null +++ b/test/asan/TestCases/longjmp.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_asan -O %s -o %t && %run %t + +#include <assert.h> +#include <setjmp.h> +#include <stdio.h> +#include <sanitizer/asan_interface.h> + +static jmp_buf buf; + +int main() { + char x[32]; + fprintf(stderr, "\nTestLongJmp\n"); + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + assert(__asan_address_is_poisoned(x + 32)); + if (0 == setjmp(buf)) + longjmp(buf, 1); + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + // This assertion works only w/o UAR. + if (!__asan_get_current_fake_stack()) + assert(!__asan_address_is_poisoned(x + 32)); +} diff --git a/test/asan/TestCases/lsan_annotations.cc b/test/asan/TestCases/lsan_annotations.cc new file mode 100644 index 000000000000..f52b0ff66a8d --- /dev/null +++ b/test/asan/TestCases/lsan_annotations.cc @@ -0,0 +1,16 @@ +// Check that LSan annotations work fine. +// RUN: %clangxx_asan -O0 %s -o %t && %run %t +// RUN: %clangxx_asan -O3 %s -o %t && %run %t + +#include <sanitizer/lsan_interface.h> +#include <stdlib.h> + +int main() { + int *x = new int; + __lsan_ignore_object(x); + { + __lsan::ScopedDisabler disabler; + double *y = new double; + } + return 0; +} diff --git a/test/asan/TestCases/malloc_context_size.cc b/test/asan/TestCases/malloc_context_size.cc new file mode 100644 index 000000000000..0d9f31598545 --- /dev/null +++ b/test/asan/TestCases/malloc_context_size.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=malloc_context_size=2 not %run %t 2>&1 | FileCheck %s --check-prefix=TWO + +int main() { + char *x = new char[20]; + delete[] x; + return x[0]; + + // CHECK: freed by thread T{{.*}} here: + // CHECK-NEXT: #0 0x{{.*}} in {{operator delete( )?\[\]|wrap__ZdaPv}} + // CHECK-NOT: #1 0x{{.*}} + + // CHECK: previously allocated by thread T{{.*}} here: + // CHECK-NEXT: #0 0x{{.*}} in {{operator new( )?\[\]|wrap__Znam}} + // CHECK-NOT: #1 0x{{.*}} + + // CHECK: SUMMARY: AddressSanitizer: heap-use-after-free + + // TWO: previously allocated by thread T{{.*}} here: + // TWO-NEXT: #0 0x{{.*}} + // TWO-NEXT: #1 0x{{.*}} in main {{.*}}malloc_context_size.cc + // TWO: SUMMARY: AddressSanitizer: heap-use-after-free +} diff --git a/test/asan/TestCases/malloc_fill.cc b/test/asan/TestCases/malloc_fill.cc new file mode 100644 index 000000000000..5c926803708d --- /dev/null +++ b/test/asan/TestCases/malloc_fill.cc @@ -0,0 +1,22 @@ +// Check that we fill malloc-ed memory correctly. +// RUN: %clangxx_asan %s -o %t +// RUN: %run %t | FileCheck %s +// RUN: env ASAN_OPTIONS=max_malloc_fill_size=10:malloc_fill_byte=8 %run %t | FileCheck %s --check-prefix=CHECK-10-8 +// RUN: env ASAN_OPTIONS=max_malloc_fill_size=20:malloc_fill_byte=171 %run %t | FileCheck %s --check-prefix=CHECK-20-ab + +#include <stdio.h> +int main(int argc, char **argv) { + // With asan allocator this makes sure we get memory from mmap. + static const int kSize = 1 << 25; + unsigned char *x = new unsigned char[kSize]; + printf("-"); + for (int i = 0; i <= 32; i++) { + printf("%02x", x[i]); + } + printf("-\n"); + delete [] x; +} + +// CHECK: -bebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebe- +// CHECK-10-8: -080808080808080808080000000000000000000000000000000000000000000000- +// CHECK-20-ab: -abababababababababababababababababababab00000000000000000000000000- diff --git a/test/asan/TestCases/max_redzone.cc b/test/asan/TestCases/max_redzone.cc new file mode 100644 index 000000000000..01c25a9f3efc --- /dev/null +++ b/test/asan/TestCases/max_redzone.cc @@ -0,0 +1,26 @@ +// Test max_redzone runtime option. + +// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=max_redzone=16 %run %t 0 2>&1 +// RUN: %clangxx_asan -O0 %s -o %t && %run %t 1 2>&1 +// RUN: %clangxx_asan -O3 %s -o %t && env ASAN_OPTIONS=max_redzone=16 %run %t 0 2>&1 +// RUN: %clangxx_asan -O3 %s -o %t && %run %t 1 2>&1 + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <sanitizer/allocator_interface.h> + +int main(int argc, char **argv) { + if (argc < 2) + return 1; + bool large_redzone = atoi(argv[1]); + size_t before = __sanitizer_get_heap_size(); + void *pp[10000]; + for (int i = 0; i < 10000; ++i) + pp[i] = malloc(4096 - 64); + size_t after = __sanitizer_get_heap_size(); + for (int i = 0; i < 10000; ++i) + free(pp[i]); + size_t diff = after - before; + return !(large_redzone ? diff > 46000000 : diff < 46000000); +} diff --git a/test/asan/TestCases/memcmp_strict_test.cc b/test/asan/TestCases/memcmp_strict_test.cc new file mode 100644 index 000000000000..16b7673dd547 --- /dev/null +++ b/test/asan/TestCases/memcmp_strict_test.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=strict_memcmp=0 %run %t +// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=strict_memcmp=1 not %run %t 2>&1 | FileCheck %s +// Default to strict_memcmp=1. +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> +int main() { + char kFoo[] = "foo"; + char kFubar[] = "fubar"; + int res = memcmp(kFoo, kFubar, strlen(kFubar)); + printf("res: %d\n", res); + // CHECK: AddressSanitizer: stack-buffer-overflow + return 0; +} diff --git a/test/asan/TestCases/memcmp_test.cc b/test/asan/TestCases/memcmp_test.cc new file mode 100644 index 000000000000..3b3b8894b73c --- /dev/null +++ b/test/asan/TestCases/memcmp_test.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: compiler-rt-optimized + +#include <string.h> +int main(int argc, char **argv) { + char a1[] = {argc, 2, 3, 4}; + char a2[] = {1, 2*argc, 3, 4}; + int res = memcmp(a1, a2, 4 + argc); // BOOM + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{#0.*memcmp}} + // CHECK: {{#1.*main}} + return res; +} diff --git a/test/asan/TestCases/memset_test.cc b/test/asan/TestCases/memset_test.cc new file mode 100644 index 000000000000..e244d54deb3c --- /dev/null +++ b/test/asan/TestCases/memset_test.cc @@ -0,0 +1,71 @@ +// Test that large memset/memcpy/memmove check the entire range. + +// RUN: %clangxx_asan -O0 -DTEST_MEMSET %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMSET +// RUN: %clangxx_asan -O1 -DTEST_MEMSET %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMSET +// RUN: %clangxx_asan -O2 -DTEST_MEMSET %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMSET +// RUN: %clangxx_asan -O3 -DTEST_MEMSET %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMSET + +// RUN: %clangxx_asan -O0 -DTEST_MEMCPY %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMCPY +// RUN: %clangxx_asan -O1 -DTEST_MEMCPY %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMCPY +// RUN: %clangxx_asan -O2 -DTEST_MEMCPY %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMCPY +// RUN: %clangxx_asan -O3 -DTEST_MEMCPY %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMCPY + +// RUN: %clangxx_asan -O0 -DTEST_MEMMOVE %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMMOVE +// RUN: %clangxx_asan -O1 -DTEST_MEMMOVE %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMMOVE +// RUN: %clangxx_asan -O2 -DTEST_MEMMOVE %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMMOVE +// RUN: %clangxx_asan -O3 -DTEST_MEMMOVE %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMMOVE + +// RUN: %clangxx_asan -O2 -DTEST_MEMCPY_SIZE_OVERFLOW %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-MEMCPY_SIZE_OVERFLOW + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <sanitizer/asan_interface.h> + +typedef void *(*memcpy_t)(void *, const void *, size_t); + +int main(int argc, char **argv) { + char * volatile p = (char *)malloc(3000); + __asan_poison_memory_region(p + 512, 16); +#if defined(TEST_MEMSET) + memset(p, 0, 3000); + assert(p[1] == 0); + // CHECK-MEMSET: AddressSanitizer: use-after-poison on address + // CHECK-MEMSET: in {{.*}}memset +#else + char * volatile q = (char *)malloc(3000); +#if defined(TEST_MEMCPY) + memcpy(q, p, 3000); + // CHECK-MEMCPY: AddressSanitizer: use-after-poison on address + // On Mac, memmove and memcpy are the same. Accept either one. + // CHECK-MEMCPY: in {{.*(memmove|memcpy)}} +#elif defined(TEST_MEMMOVE) + memmove(q, p, 3000); + // CHECK-MEMMOVE: AddressSanitizer: use-after-poison on address + // CHECK-MEMMOVE: in {{.*(memmove|memcpy)}} +#elif defined(TEST_MEMCPY_SIZE_OVERFLOW) + volatile memcpy_t my_memcpy = &memcpy; + my_memcpy(p, q, -argc); + // CHECK-MEMCPY_SIZE_OVERFLOW: AddressSanitizer: negative-size-param: (size=-1) +#endif + assert(q[1] == 0); + free(q); +#endif + free(p); + return 0; +} diff --git a/test/asan/TestCases/mmap_limit_mb.cc b/test/asan/TestCases/mmap_limit_mb.cc new file mode 100644 index 000000000000..d4ffb2eac246 --- /dev/null +++ b/test/asan/TestCases/mmap_limit_mb.cc @@ -0,0 +1,33 @@ +// Test the mmap_limit_mb flag. +// +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %run %t 20 16 +// RUN: %run %t 30 1000000 +// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 %run %t 20 16 +// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 %run %t 20 1000000 +// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 not %run %t 500 16 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 not %run %t 500 1000000 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include <algorithm> +#include <vector> + +int main(int argc, char **argv) { + assert(argc == 3); + long total_mb = atoi(argv[1]); + long allocation_size = atoi(argv[2]); + fprintf(stderr, "total_mb: %zd allocation_size: %zd\n", total_mb, + allocation_size); + std::vector<char *> v; + for (long total = total_mb << 20; total > 0; total -= allocation_size) + v.push_back(new char[allocation_size]); + for (std::vector<char *>::const_iterator it = v.begin(); it != v.end(); ++it) + delete[](*it); + fprintf(stderr, "PASS\n"); + // CHECK: total_mmaped{{.*}}mmap_limit_mb + return 0; +} diff --git a/test/asan/TestCases/no_asan_gen_globals.c b/test/asan/TestCases/no_asan_gen_globals.c new file mode 100644 index 000000000000..0a383da1384d --- /dev/null +++ b/test/asan/TestCases/no_asan_gen_globals.c @@ -0,0 +1,11 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android +// +// Make sure __asan_gen_* strings do not end up in the symbol table. + +// RUN: %clang_asan %s -o %t.exe +// RUN: nm %t.exe | FileCheck %s + +int x, y, z; +int main() { return 0; } +// CHECK-NOT: __asan_gen_ diff --git a/test/asan/TestCases/null_deref.cc b/test/asan/TestCases/null_deref.cc new file mode 100644 index 000000000000..875d65f2852f --- /dev/null +++ b/test/asan/TestCases/null_deref.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +__attribute__((noinline)) +static void NullDeref(int *ptr) { + // CHECK: ERROR: AddressSanitizer: SEGV on unknown address + // CHECK: {{0x0*000.. .*pc 0x.*}} + ptr[10]++; // BOOM + // atos on Mac cannot extract the symbol name correctly. Also, on FreeBSD 9.2 + // the demangling function rejects local names with 'L' in front of them. + // CHECK: {{ #0 0x.* in .*NullDeref.*null_deref.cc:}}[[@LINE-3]] +} +int main() { + NullDeref((int*)0); + // CHECK: {{ #1 0x.* in main.*null_deref.cc:}}[[@LINE-1]] + // CHECK: AddressSanitizer can not provide additional info. +} diff --git a/test/asan/TestCases/on_error_callback.cc b/test/asan/TestCases/on_error_callback.cc new file mode 100644 index 000000000000..c378c8b2de1b --- /dev/null +++ b/test/asan/TestCases/on_error_callback.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// FIXME: __asan_on_error() is not supported on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +#include <stdlib.h> + +extern "C" +void __asan_on_error() { + fprintf(stderr, "__asan_on_error called"); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: __asan_on_error called +} diff --git a/test/asan/TestCases/partial_right.cc b/test/asan/TestCases/partial_right.cc new file mode 100644 index 000000000000..b60c1a597635 --- /dev/null +++ b/test/asan/TestCases/partial_right.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main(int argc, char **argv) { + volatile int *x = (int*)malloc(2*sizeof(int) + 2); + int res = x[2]; // BOOOM + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: [[ADDR:0x[01-9a-fa-f]+]] is located 0 bytes to the right of {{.*}}-byte region [{{.*}},{{.*}}[[ADDR]]) + return res; +} diff --git a/test/asan/TestCases/poison_partial.cc b/test/asan/TestCases/poison_partial.cc new file mode 100644 index 000000000000..ce9c98b7859a --- /dev/null +++ b/test/asan/TestCases/poison_partial.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t heap 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=poison_partial=0 %run %t +// RUN: env ASAN_OPTIONS=poison_partial=0 %run %t heap +#include <string.h> +char g[21]; +char *x; + +int main(int argc, char **argv) { + if (argc >= 2) + x = new char[21]; + else + x = &g[0]; + memset(x, 0, 21); + int *y = (int*)x; + return y[5]; +} +// CHECK: 0 bytes to the right diff --git a/test/asan/TestCases/print_summary.cc b/test/asan/TestCases/print_summary.cc new file mode 100644 index 000000000000..79411c529469 --- /dev/null +++ b/test/asan/TestCases/print_summary.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=YES +// RUN: env ASAN_OPTIONS=print_summary=false not %run %t 2>&1 | FileCheck %s --check-prefix=NO + +int main() { + char *x = new char[20]; + delete[] x; + return x[0]; + // YES: ERROR: AddressSanitizer: heap-use-after-free + // YES: SUMMARY: AddressSanitizer: heap-use-after-free + // NO: ERROR: AddressSanitizer: heap-use-after-free + // NO-NOT: SUMMARY: AddressSanitizer: heap-use-after-free +} + diff --git a/test/asan/TestCases/printf-1.c b/test/asan/TestCases/printf-1.c new file mode 100644 index 000000000000..5657083c5865 --- /dev/null +++ b/test/asan/TestCases/printf-1.c @@ -0,0 +1,25 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: env ASAN_OPTIONS=check_printf=1 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=check_printf=0 %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#if defined(_WIN32) +# define snprintf _snprintf +#endif + +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + // Check that printf works fine under Asan. + printf("%c %d %.3f %s\n", c, x, f, s); + // CHECK: 0 12 1.239 34 + // Check that snprintf works fine under Asan. + char buf[4]; + snprintf(buf, 1000, "qwe"); + printf("%s\n", buf); + // CHECK: qwe + return 0; +} diff --git a/test/asan/TestCases/printf-2.c b/test/asan/TestCases/printf-2.c new file mode 100644 index 000000000000..e9cb47e24c15 --- /dev/null +++ b/test/asan/TestCases/printf-2.c @@ -0,0 +1,27 @@ +// RUN: %clang_asan -O2 %s -o %t +// We need replace_str=0 and replace_intrin=0 to avoid reporting errors in +// strlen() and memcpy() called by printf(). +// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s + +// FIXME: printf is not intercepted on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + char *p = strdup((const char *)s); + free(p); + printf("%c %d %.3f %s\n", c, x, f, p); + return 0; + // Check that %s is sanitized. + // CHECK-ON: heap-use-after-free + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 12 1.239 +} diff --git a/test/asan/TestCases/printf-3.c b/test/asan/TestCases/printf-3.c new file mode 100644 index 000000000000..d16833d83c6e --- /dev/null +++ b/test/asan/TestCases/printf-3.c @@ -0,0 +1,22 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: env ASAN_OPTIONS=check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s + +// FIXME: printf is not intercepted on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile int n[1]; + printf("%c %d %.3f %s%n\n", c, x, f, s, &n[1]); + return 0; + // Check that %n is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 12 1.239 34 +} diff --git a/test/asan/TestCases/printf-4.c b/test/asan/TestCases/printf-4.c new file mode 100644 index 000000000000..e269211d4871 --- /dev/null +++ b/test/asan/TestCases/printf-4.c @@ -0,0 +1,23 @@ +// RUN: %clang_asan -O2 %s -o %t +// We need replace_str=0 and replace_intrin=0 to avoid reporting errors in +// strlen() and memcpy() called by puts(). +// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s + +// FIXME: printf is not intercepted on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile char buf[2]; + sprintf((char *)buf, "%c %d %.3f %s\n", c, x, f, s); + puts((const char *)buf); + return 0; + // Check that size of output buffer is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 +} diff --git a/test/asan/TestCases/printf-5.c b/test/asan/TestCases/printf-5.c new file mode 100644 index 000000000000..ac2c1c4b2997 --- /dev/null +++ b/test/asan/TestCases/printf-5.c @@ -0,0 +1,25 @@ +// RUN: %clang_asan -O2 %s -o %t +// We need replace_intrin=0 to avoid reporting errors in memcpy. +// RUN: env ASAN_OPTIONS=replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=replace_intrin=0:check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: env ASAN_OPTIONS=replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s + +// FIXME: printf is not intercepted on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +#include <string.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile char fmt[2]; + memcpy((char *)fmt, "%c %d %f %s\n", sizeof(fmt)); + printf((char *)fmt, c, x, f, s); + return 0; + // Check that format string is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 +} diff --git a/test/asan/TestCases/sanity_check_pure_c.c b/test/asan/TestCases/sanity_check_pure_c.c new file mode 100644 index 000000000000..c3a43c8cacb2 --- /dev/null +++ b/test/asan/TestCases/sanity_check_pure_c.c @@ -0,0 +1,21 @@ +// Sanity checking a test in pure C. +// RUN: %clang_asan -O2 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +// Sanity checking a test in pure C with -pie. +// RUN: %clang_asan -O2 %s -pie -fPIE -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: heap-use-after-free + // CHECK: free + // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-4]] + // CHECK: malloc + // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-7]] +} diff --git a/test/asan/TestCases/sleep_before_dying.c b/test/asan/TestCases/sleep_before_dying.c new file mode 100644 index 000000000000..28ae0bf66d47 --- /dev/null +++ b/test/asan/TestCases/sleep_before_dying.c @@ -0,0 +1,10 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: env ASAN_OPTIONS="sleep_before_dying=1" not %run %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: Sleeping for 1 second +} diff --git a/test/asan/TestCases/stack-buffer-overflow-with-position.cc b/test/asan/TestCases/stack-buffer-overflow-with-position.cc new file mode 100644 index 000000000000..88f5825baf42 --- /dev/null +++ b/test/asan/TestCases/stack-buffer-overflow-with-position.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %run %t -2 2>&1 | FileCheck --check-prefix=CHECK-m2 %s +// RUN: not %run %t -1 2>&1 | FileCheck --check-prefix=CHECK-m1 %s +// RUN: %run %t 0 +// RUN: %run %t 8 +// RUN: not %run %t 9 2>&1 | FileCheck --check-prefix=CHECK-9 %s +// RUN: not %run %t 10 2>&1 | FileCheck --check-prefix=CHECK-10 %s +// RUN: not %run %t 30 2>&1 | FileCheck --check-prefix=CHECK-30 %s +// RUN: not %run %t 31 2>&1 | FileCheck --check-prefix=CHECK-31 %s +// RUN: not %run %t 41 2>&1 | FileCheck --check-prefix=CHECK-41 %s +// RUN: not %run %t 42 2>&1 | FileCheck --check-prefix=CHECK-42 %s +// RUN: not %run %t 62 2>&1 | FileCheck --check-prefix=CHECK-62 %s +// RUN: not %run %t 63 2>&1 | FileCheck --check-prefix=CHECK-63 %s +// RUN: not %run %t 73 2>&1 | FileCheck --check-prefix=CHECK-73 %s +// RUN: not %run %t 74 2>&1 | FileCheck --check-prefix=CHECK-74 %s +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +int main(int argc, char **argv) { + assert(argc >= 2); + int idx = atoi(argv[1]); + char AAA[10], BBB[10], CCC[10]; + memset(AAA, 0, sizeof(AAA)); + memset(BBB, 0, sizeof(BBB)); + memset(CCC, 0, sizeof(CCC)); + int res = 0; + char *p = AAA + idx; + printf("AAA: %p\ny: %p\nz: %p\np: %p\n", AAA, BBB, CCC, p); + // make sure BBB and CCC are not removed; + return *(short*)(p) + BBB[argc % 2] + CCC[argc % 2]; +} +// CHECK-m2: 'AAA' <== {{.*}}underflows this variable +// CHECK-m1: 'AAA' <== {{.*}}partially underflows this variable +// CHECK-9: 'AAA' <== {{.*}}partially overflows this variable +// CHECK-10: 'AAA' <== {{.*}}overflows this variable +// CHECK-30: 'BBB' <== {{.*}}underflows this variable +// CHECK-31: 'BBB' <== {{.*}}partially underflows this variable +// CHECK-41: 'BBB' <== {{.*}}partially overflows this variable +// CHECK-42: 'BBB' <== {{.*}}overflows this variable +// CHECK-62: 'CCC' <== {{.*}}underflows this variable +// CHECK-63: 'CCC' <== {{.*}}partially underflows this variable +// CHECK-73: 'CCC' <== {{.*}}partially overflows this variable +// CHECK-74: 'CCC' <== {{.*}}overflows this variable diff --git a/test/asan/TestCases/stack-buffer-overflow.cc b/test/asan/TestCases/stack-buffer-overflow.cc new file mode 100644 index 000000000000..bd0c4772cc84 --- /dev/null +++ b/test/asan/TestCases/stack-buffer-overflow.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <string.h> +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*stack-buffer-overflow.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}} + // CHECK-NEXT: in{{.*}}main{{.*}}stack-buffer-overflow.cc + return res; +} diff --git a/test/asan/TestCases/stack-frame-demangle.cc b/test/asan/TestCases/stack-frame-demangle.cc new file mode 100644 index 000000000000..11a8b38e6724 --- /dev/null +++ b/test/asan/TestCases/stack-frame-demangle.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <string.h> + +namespace XXX { +struct YYY { + static int ZZZ(int x) { + char array[10]; + memset(array, 0, 10); + return array[x]; // BOOOM + // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 1 at + // CHECK: is located in stack of thread T0 at offset + // CHECK: XXX::YYY::ZZZ + } +}; +} // namespace XXX + +int main(int argc, char **argv) { + int res = XXX::YYY::ZZZ(argc + 10); + return res; +} diff --git a/test/asan/TestCases/stack-oob-frames.cc b/test/asan/TestCases/stack-oob-frames.cc new file mode 100644 index 000000000000..3b5d511b2681 --- /dev/null +++ b/test/asan/TestCases/stack-oob-frames.cc @@ -0,0 +1,59 @@ +// RUN: %clangxx_asan -O1 %s -o %t +// RUN: not %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK0 +// RUN: not %run %t 1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: not %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: not %run %t 3 2>&1 | FileCheck %s --check-prefix=CHECK3 + +#define NOINLINE __attribute__((noinline)) +inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { + char s[4] = {0}; + char *d = s; + break_optimization(&d); + switch (frame) { + case 3: a[5]++; break; + case 2: b[5]++; break; + case 1: c[5]++; break; + case 0: d[5]++; break; + } +} +NOINLINE static void Frame1(int frame, char *a, char *b) { + char c[4] = {0}; Frame0(frame, a, b, c); + break_optimization(0); +} +NOINLINE static void Frame2(int frame, char *a) { + char b[4] = {0}; Frame1(frame, a, b); + break_optimization(0); +} +NOINLINE static void Frame3(int frame) { + char a[4] = {0}; Frame2(frame, a); + break_optimization(0); +} + +int main(int argc, char **argv) { + if (argc != 2) return 1; + Frame3(argv[1][0] - '0'); +} + +// CHECK0: AddressSanitizer: stack-buffer-overflow +// CHECK0: #0{{.*}}Frame0 +// CHECK0: #1{{.*}}Frame1 +// CHECK0: #2{{.*}}Frame2 +// CHECK0: #3{{.*}}Frame3 +// CHECK0: is located in stack of thread T0 at offset +// CHECK0-NEXT: #0{{.*}}Frame0 +// +// CHECK1: AddressSanitizer: stack-buffer-overflow +// CHECK1: is located in stack of thread T0 at offset +// CHECK1-NEXT: #0{{.*}}Frame1 +// +// CHECK2: AddressSanitizer: stack-buffer-overflow +// CHECK2: is located in stack of thread T0 at offset +// CHECK2-NEXT: #0{{.*}}Frame2 +// +// CHECK3: AddressSanitizer: stack-buffer-overflow +// CHECK3: is located in stack of thread T0 at offset +// CHECK3-NEXT: #0{{.*}}Frame3 diff --git a/test/asan/TestCases/stack-overflow.cc b/test/asan/TestCases/stack-overflow.cc new file mode 100644 index 000000000000..7542d56b6db8 --- /dev/null +++ b/test/asan/TestCases/stack-overflow.cc @@ -0,0 +1,114 @@ +// Test ASan detection of stack-overflow condition. + +// RUN: %clangxx_asan -O0 %s -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -O0 %s -DTHREAD -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTHREAD -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -DTHREAD -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTHREAD -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -DTHREAD -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTHREAD -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s +// REQUIRES: stable-runtime + +#include <assert.h> +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sanitizer/asan_interface.h> + +const int BS = 1024; +volatile char x; +volatile int y = 1; +volatile int z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13; + +void recursive_func(char *p) { +#if defined(SMALL_FRAME) + char *buf = 0; +#elif defined(SAVE_ALL_THE_REGISTERS) + char *buf = 0; + int t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13; + t0 = z0; + t1 = z1; + t2 = z2; + t3 = z3; + t4 = z4; + t5 = z5; + t6 = z6; + t7 = z7; + t8 = z8; + t9 = z9; + t10 = z10; + t11 = z11; + t12 = z12; + t13 = z13; + + z0 = t0; + z1 = t1; + z2 = t2; + z3 = t3; + z4 = t4; + z5 = t5; + z6 = t6; + z7 = t7; + z8 = t8; + z9 = t9; + z10 = t10; + z11 = t11; + z12 = t12; + z13 = t13; +#else + char buf[BS]; + // Check that the stack grows in the righ direction, unless we use fake stack. + if (p && !__asan_get_current_fake_stack()) + assert(p - buf >= BS); + buf[rand() % BS] = 1; + buf[rand() % BS] = 2; + x = buf[rand() % BS]; +#endif + if (y) + recursive_func(buf); + x = 1; // prevent tail call optimization + // CHECK: {{stack-overflow on address 0x.* \(pc 0x.* bp 0x.* sp 0x.* T.*\)}} + // If stack overflow happens during function prologue, stack trace may be + // corrupted. Unwind tables are not always 100% exact there. + // For this reason, we don't do any further checks. +} + +void *ThreadFn(void* unused) { + recursive_func(0); + return 0; +} + +void LimitStackAndReexec(int argc, char **argv) { + struct rlimit rlim; + int res = getrlimit(RLIMIT_STACK, &rlim); + assert(res == 0); + if (rlim.rlim_cur == RLIM_INFINITY) { + rlim.rlim_cur = 128 * 1024; + res = setrlimit(RLIMIT_STACK, &rlim); + assert(res == 0); + + execv(argv[0], argv); + assert(0 && "unreachable"); + } +} + +int main(int argc, char **argv) { + LimitStackAndReexec(argc, argv); +#ifdef THREAD + pthread_t t; + pthread_create(&t, 0, ThreadFn, 0); + pthread_join(t, 0); +#else + recursive_func(0); +#endif + return 0; +} diff --git a/test/asan/TestCases/stack-use-after-return.cc b/test/asan/TestCases/stack-use-after-return.cc new file mode 100644 index 000000000000..437c457748c4 --- /dev/null +++ b/test/asan/TestCases/stack-use-after-return.cc @@ -0,0 +1,80 @@ +// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t +// Regression test for a CHECK failure with small stack size and large frame. +// RUN: %clangxx_asan -O3 %s -pthread -o %t -DkSize=10000 -DUseThread -DkStackSize=65536 && not %run %t 2>&1 | FileCheck --check-prefix=THREAD %s +// +// Test that we can find UAR in a thread other than main: +// RUN: %clangxx_asan -DUseThread -O2 %s -pthread -o %t && not %run %t 2>&1 | FileCheck --check-prefix=THREAD %s +// +// Test the max_uar_stack_size_log/min_uar_stack_size_log flag. +// +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:max_uar_stack_size_log=20:verbosity=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-20 %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:min_uar_stack_size_log=24:max_uar_stack_size_log=24:verbosity=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-24 %s + +#include <stdio.h> +#include <pthread.h> + +#ifndef kSize +# define kSize 1 +#endif + +#ifndef UseThread +# define UseThread 0 +#endif + +#ifndef kStackSize +# define kStackSize 0 +#endif + +__attribute__((noinline)) +char *Ident(char *x) { + fprintf(stderr, "1: %p\n", x); + return x; +} + +__attribute__((noinline)) +char *Func1() { + char local[kSize]; + return Ident(local); +} + +__attribute__((noinline)) +void Func2(char *x) { + fprintf(stderr, "2: %p\n", x); + *x = 1; + // CHECK: WRITE of size 1 {{.*}} thread T0 + // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]] + // CHECK: is located in stack of thread T0 at offset + // CHECK: 'local' <== Memory access at offset {{16|32}} is inside this variable + // THREAD: WRITE of size 1 {{.*}} thread T{{[1-9]}} + // THREAD: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-6]] + // THREAD: is located in stack of thread T{{[1-9]}} at offset + // THREAD: 'local' <== Memory access at offset {{16|32}} is inside this variable + // CHECK-20: T0: FakeStack created:{{.*}} stack_size_log: 20 + // CHECK-24: T0: FakeStack created:{{.*}} stack_size_log: 24 +} + +void *Thread(void *unused) { + Func2(Func1()); + return NULL; +} + +int main(int argc, char **argv) { +#if UseThread + pthread_attr_t attr; + pthread_attr_init(&attr); + if (kStackSize > 0) + pthread_attr_setstacksize(&attr, kStackSize); + pthread_t t; + pthread_create(&t, &attr, Thread, 0); + pthread_attr_destroy(&attr); + pthread_join(t, 0); +#else + Func2(Func1()); +#endif + return 0; +} diff --git a/test/asan/TestCases/strdup_oob_test.cc b/test/asan/TestCases/strdup_oob_test.cc new file mode 100644 index 000000000000..a039568b2245 --- /dev/null +++ b/test/asan/TestCases/strdup_oob_test.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <string.h> + +char kString[] = "foo"; + +int main(int argc, char **argv) { + char *copy = strdup(kString); + int x = copy[4 + argc]; // BOOM + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: #0 {{.*}}main {{.*}}strdup_oob_test.cc:[[@LINE-2]] + // CHECK-LABEL: allocated by thread T{{.*}} here: + // CHECK: #{{[01]}} {{.*}}strdup + // CHECK-LABEL: SUMMARY + // CHECK: strdup_oob_test.cc:[[@LINE-6]] + return x; +} diff --git a/test/asan/TestCases/strip_path_prefix.c b/test/asan/TestCases/strip_path_prefix.c new file mode 100644 index 000000000000..4556e9031e2d --- /dev/null +++ b/test/asan/TestCases/strip_path_prefix.c @@ -0,0 +1,12 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: env ASAN_OPTIONS="strip_path_prefix='/'" not %run %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // Check that paths in error report don't start with slash. + // CHECK: heap-use-after-free + // CHECK-NOT: #0 0x{{.*}} ({{[/].*}}) +} diff --git a/test/asan/TestCases/strncpy-overflow.cc b/test/asan/TestCases/strncpy-overflow.cc new file mode 100644 index 000000000000..651ae22795f1 --- /dev/null +++ b/test/asan/TestCases/strncpy-overflow.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +// REQUIRES: compiler-rt-optimized +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <string.h> +#include <stdlib.h> +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + char *short_buffer = (char*)malloc(9); + strncpy(short_buffer, hello, 10); // BOOM + // CHECK: {{WRITE of size 10 at 0x.* thread T0}} + // CHECK-Linux: {{ #0 0x.* in .*strncpy}} + // CHECK-Darwin: {{ #0 0x.* in wrap_strncpy}} + // CHECK: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-4]] + // CHECK: {{0x.* is located 0 bytes to the right of 9-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-10]] + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-13]] + return short_buffer[8]; +} diff --git a/test/asan/TestCases/suppressions-function.cc b/test/asan/TestCases/suppressions-function.cc new file mode 100644 index 000000000000..c52b3c303518 --- /dev/null +++ b/test/asan/TestCases/suppressions-function.cc @@ -0,0 +1,29 @@ +// Check that without suppressions, we catch the issue. +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// RUN: echo "interceptor_via_fun:crash_function" > %t.supp +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=suppressions=%t.supp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: %clangxx_asan -O3 %s -o %t && ASAN_OPTIONS=suppressions=%t.supp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +// XFAIL: android + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void crash_function() { + char *a = (char *)malloc(6); + free(a); + size_t len = strlen(a); // BOOM + fprintf(stderr, "strlen ignored, len = %zu\n", len); +} + +int main() { + crash_function(); +} + +// CHECK-CRASH: AddressSanitizer: heap-use-after-free +// CHECK-CRASH-NOT: strlen ignored +// CHECK-IGNORE-NOT: AddressSanitizer: heap-use-after-free +// CHECK-IGNORE: strlen ignored diff --git a/test/asan/TestCases/suppressions-interceptor.cc b/test/asan/TestCases/suppressions-interceptor.cc new file mode 100644 index 000000000000..10d24fdc30a3 --- /dev/null +++ b/test/asan/TestCases/suppressions-interceptor.cc @@ -0,0 +1,24 @@ +// Check that without suppressions, we catch the issue. +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// RUN: echo "interceptor_name:strlen" > %t.supp +// RUN: ASAN_OPTIONS=suppressions=%t.supp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +// XFAIL: android + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main() { + char *a = (char *)malloc(6); + free(a); + size_t len = strlen(a); // BOOM + fprintf(stderr, "strlen ignored, len = %zu\n", len); +} + +// CHECK-CRASH: AddressSanitizer: heap-use-after-free +// CHECK-CRASH-NOT: strlen ignored +// CHECK-IGNORE-NOT: AddressSanitizer: heap-use-after-free +// CHECK-IGNORE: strlen ignored diff --git a/test/asan/TestCases/suppressions-library.cc b/test/asan/TestCases/suppressions-library.cc new file mode 100644 index 000000000000..dfb0d4a5e030 --- /dev/null +++ b/test/asan/TestCases/suppressions-library.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s %t-so.so -o %t + +// Check that without suppressions, we catch the issue. +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// RUN: echo "interceptor_via_lib:%t-so.so" > %t.supp +// RUN: ASAN_OPTIONS=suppressions=%t.supp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +// XFAIL: android + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if !defined(SHARED_LIB) + +void crash_function(); + +int main(int argc, char *argv[]) { + crash_function(); + return 0; +} + +#else // SHARED_LIB + +void crash_function() { + char *a = (char *)malloc(6); + free(a); + size_t len = strlen(a); // BOOM + fprintf(stderr, "strlen ignored, %zu\n", len); +} + +#endif // SHARED_LIB + +// CHECK-CRASH: AddressSanitizer: heap-use-after-free +// CHECK-CRASH-NOT: strlen ignored +// CHECK-IGNORE-NOT: AddressSanitizer: heap-use-after-free +// CHECK-IGNORE: strlen ignored diff --git a/test/asan/TestCases/throw_call_test.cc b/test/asan/TestCases/throw_call_test.cc new file mode 100644 index 000000000000..20e9a5ee565e --- /dev/null +++ b/test/asan/TestCases/throw_call_test.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_asan %s -o %t && %run %t +// http://code.google.com/p/address-sanitizer/issues/detail?id=147 (not fixed). +// BROKEN: %clangxx_asan %s -o %t -static-libstdc++ && %run %t +// +// Android builds with static libstdc++ by default. +// XFAIL: android + +// Clang doesn't support exceptions on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +static volatile int zero = 0; +inline void pretend_to_do_something(void *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline, no_sanitize_address)) +void ReallyThrow() { + fprintf(stderr, "ReallyThrow\n"); + if (zero == 0) + throw 42; +} + +__attribute__((noinline)) +void Throw() { + int a, b, c, d, e; + pretend_to_do_something(&a); + pretend_to_do_something(&b); + pretend_to_do_something(&c); + pretend_to_do_something(&d); + pretend_to_do_something(&e); + fprintf(stderr, "Throw stack = %p\n", &a); + ReallyThrow(); +} + +__attribute__((noinline)) +void CheckStack() { + int ar[100]; + pretend_to_do_something(ar); + for (int i = 0; i < 100; i++) + ar[i] = i; + fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); +} + +int main(int argc, char** argv) { + try { + Throw(); + } catch(int a) { + fprintf(stderr, "a = %d\n", a); + } + CheckStack(); +} diff --git a/test/asan/TestCases/throw_catch.cc b/test/asan/TestCases/throw_catch.cc new file mode 100644 index 000000000000..bce48199dbf7 --- /dev/null +++ b/test/asan/TestCases/throw_catch.cc @@ -0,0 +1,64 @@ +// RUN: %clangxx_asan -O %s -o %t && %run %t + +// Clang doesn't support exceptions on Windows yet. +// XFAIL: win32 + +#include <assert.h> +#include <stdio.h> +#include <sanitizer/asan_interface.h> + +__attribute__((noinline)) +void Throw() { + int local; + fprintf(stderr, "Throw: %p\n", &local); + throw 1; +} + +__attribute__((noinline)) +void ThrowAndCatch() { + int local; + try { + Throw(); + } catch(...) { + fprintf(stderr, "Catch: %p\n", &local); + } +} + +void TestThrow() { + char x[32]; + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + assert(__asan_address_is_poisoned(x + 32)); + ThrowAndCatch(); + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + // This assertion works only w/o UAR. + if (!__asan_get_current_fake_stack()) + assert(!__asan_address_is_poisoned(x + 32)); +} + +void TestThrowInline() { + char x[32]; + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + assert(__asan_address_is_poisoned(x + 32)); + try { + Throw(); + } catch(...) { + fprintf(stderr, "Catch\n"); + } + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + // This assertion works only w/o UAR. + if (!__asan_get_current_fake_stack()) + assert(!__asan_address_is_poisoned(x + 32)); +} + +int main(int argc, char **argv) { + TestThrowInline(); + TestThrow(); +} diff --git a/test/asan/TestCases/throw_invoke_test.cc b/test/asan/TestCases/throw_invoke_test.cc new file mode 100644 index 000000000000..ec48fc7b6a49 --- /dev/null +++ b/test/asan/TestCases/throw_invoke_test.cc @@ -0,0 +1,54 @@ +// RUN: %clangxx_asan %s -o %t && %run %t +// RUN: %clangxx_asan %s -o %t -static-libstdc++ && %run %t + +// Clang doesn't support exceptions on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +static volatile int zero = 0; +inline void pretend_to_do_something(void *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline)) +void ReallyThrow() { + fprintf(stderr, "ReallyThrow\n"); + try { + if (zero == 0) + throw 42; + else if (zero == 1) + throw 1.; + } catch(double x) { + } +} + +__attribute__((noinline)) +void Throw() { + int a, b, c, d, e; + pretend_to_do_something(&a); + pretend_to_do_something(&b); + pretend_to_do_something(&c); + pretend_to_do_something(&d); + pretend_to_do_something(&e); + fprintf(stderr, "Throw stack = %p\n", &a); + ReallyThrow(); +} + +__attribute__((noinline)) +void CheckStack() { + int ar[100]; + pretend_to_do_something(ar); + for (int i = 0; i < 100; i++) + ar[i] = i; + fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); +} + +int main(int argc, char** argv) { + try { + Throw(); + } catch(int a) { + fprintf(stderr, "a = %d\n", a); + } + CheckStack(); +} + diff --git a/test/asan/TestCases/time_interceptor.cc b/test/asan/TestCases/time_interceptor.cc new file mode 100644 index 000000000000..89b2183bcde2 --- /dev/null +++ b/test/asan/TestCases/time_interceptor.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test the time() interceptor. + +// There's no interceptor for time() on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +int main() { + time_t *tm = (time_t*)malloc(sizeof(time_t)); + free(tm); + time_t t = time(tm); + printf("Time: %s\n", ctime(&t)); // NOLINT + // CHECK: use-after-free + // Regression check for + // https://code.google.com/p/address-sanitizer/issues/detail?id=321 + // CHECK: SUMMARY + return 0; +} diff --git a/test/asan/TestCases/uar_and_exceptions.cc b/test/asan/TestCases/uar_and_exceptions.cc new file mode 100644 index 000000000000..0bfe29729555 --- /dev/null +++ b/test/asan/TestCases/uar_and_exceptions.cc @@ -0,0 +1,43 @@ +// Test that use-after-return works with exceptions. +// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && %run %t + +// Clang doesn't support exceptions on Windows yet. +// XFAIL: win32 + +#include <stdio.h> + +volatile char *g; + +#ifndef FRAME_SIZE +# define FRAME_SIZE 100 +#endif + +#ifndef NUM_ITER +# define NUM_ITER 4000 +#endif + +#ifndef DO_THROW +# define DO_THROW 1 +#endif + +void Func(int depth) { + char frame[FRAME_SIZE]; + g = &frame[0]; + if (depth) + Func(depth - 1); + else if (DO_THROW) + throw 1; +} + +int main(int argc, char **argv) { + for (int i = 0; i < NUM_ITER; i++) { + try { + Func(argc * 100); + } catch(...) { + } + if ((i % (NUM_ITER / 10)) == 0) + fprintf(stderr, "done [%d]\n", i); + } + return 0; +} diff --git a/test/asan/TestCases/unaligned_loads_and_stores.cc b/test/asan/TestCases/unaligned_loads_and_stores.cc new file mode 100644 index 000000000000..f1b1d0d457e1 --- /dev/null +++ b/test/asan/TestCases/unaligned_loads_and_stores.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t A 2>&1 | FileCheck --check-prefix=CHECK-A %s +// RUN: not %run %t B 2>&1 | FileCheck --check-prefix=CHECK-B %s +// RUN: not %run %t C 2>&1 | FileCheck --check-prefix=CHECK-C %s +// RUN: not %run %t D 2>&1 | FileCheck --check-prefix=CHECK-D %s +// RUN: not %run %t E 2>&1 | FileCheck --check-prefix=CHECK-E %s + +// RUN: not %run %t K 2>&1 | FileCheck --check-prefix=CHECK-K %s +// RUN: not %run %t L 2>&1 | FileCheck --check-prefix=CHECK-L %s +// RUN: not %run %t M 2>&1 | FileCheck --check-prefix=CHECK-M %s +// RUN: not %run %t N 2>&1 | FileCheck --check-prefix=CHECK-N %s +// RUN: not %run %t O 2>&1 | FileCheck --check-prefix=CHECK-O %s + +#include <sanitizer/asan_interface.h> + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + if (argc != 2) return 1; + char *x = new char[16]; + memset(x, 0xab, 16); + int res = 1; + switch (argv[1][0]) { + case 'A': res = __sanitizer_unaligned_load16(x + 15); break; +// CHECK-A ERROR: AddressSanitizer: heap-buffer-overflow on address +// CHECK-A: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]] +// CHECK-A: is located 0 bytes to the right of 16-byte region + case 'B': res = __sanitizer_unaligned_load32(x + 14); break; +// CHECK-B: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'C': res = __sanitizer_unaligned_load32(x + 13); break; +// CHECK-C: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'D': res = __sanitizer_unaligned_load64(x + 15); break; +// CHECK-D: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'E': res = __sanitizer_unaligned_load64(x + 9); break; +// CHECK-E: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + + case 'K': __sanitizer_unaligned_store16(x + 15, 0); break; +// CHECK-K ERROR: AddressSanitizer: heap-buffer-overflow on address +// CHECK-K: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]] +// CHECK-K: is located 0 bytes to the right of 16-byte region + case 'L': __sanitizer_unaligned_store32(x + 15, 0); break; +// CHECK-L: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'M': __sanitizer_unaligned_store32(x + 13, 0); break; +// CHECK-M: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'N': __sanitizer_unaligned_store64(x + 10, 0); break; +// CHECK-N: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'O': __sanitizer_unaligned_store64(x + 14, 0); break; +// CHECK-O: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + } + delete x; + return res; +} diff --git a/test/asan/TestCases/use-after-delete.cc b/test/asan/TestCases/use-after-delete.cc new file mode 100644 index 000000000000..8fdec8d83c80 --- /dev/null +++ b/test/asan/TestCases/use-after-delete.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <stdlib.h> +int main() { + char * volatile x = new char[10]; + delete[] x; + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-delete.cc:}}[[@LINE-4]] + // CHECK: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in operator delete\[\]}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-delete.cc:}}[[@LINE-10]] + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in operator new\[\]}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-delete.cc:}}[[@LINE-16]] + + // CHECK: Shadow byte legend (one shadow byte represents 8 application bytes): + // CHECK: Global redzone: + // CHECK: ASan internal: +} diff --git a/test/asan/TestCases/use-after-free-right.cc b/test/asan/TestCases/use-after-free-right.cc new file mode 100644 index 000000000000..f714b44f2f1f --- /dev/null +++ b/test/asan/TestCases/use-after-free-right.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +// Test use-after-free report in the case when access is at the right border of +// the allocation. + +#include <stdlib.h> +int main() { + volatile char *x = (char*)malloc(sizeof(char)); + free((void*)x); + *x = 42; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{WRITE of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-free-right.cc:}}[[@LINE-4]] + // CHECK: {{0x.* is located 0 bytes inside of 1-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:}}[[@LINE-10]] + + // CHECK-Darwin: {{ #0 0x.* in wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free-right.cc:}}[[@LINE-13]] + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:}}[[@LINE-19]] + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free-right.cc:}}[[@LINE-22]] +} diff --git a/test/asan/TestCases/use-after-free.cc b/test/asan/TestCases/use-after-free.cc new file mode 100644 index 000000000000..7bc225b1ef86 --- /dev/null +++ b/test/asan/TestCases/use-after-free.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// XFAIL: arm-linux-gnueabi +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-free.cc:}}[[@LINE-4]] + // CHECK: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:}}[[@LINE-10]] + + // CHECK-Darwin: {{ #0 0x.* in wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free.cc:}}[[@LINE-13]] + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:}}[[@LINE-19]] + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free.cc:}}[[@LINE-22]] + // CHECK: Shadow byte legend (one shadow byte represents 8 application bytes): + // CHECK: Global redzone: + // CHECK: ASan internal: +} diff --git a/test/asan/TestCases/use-after-poison.cc b/test/asan/TestCases/use-after-poison.cc new file mode 100644 index 000000000000..3b247ff531b9 --- /dev/null +++ b/test/asan/TestCases/use-after-poison.cc @@ -0,0 +1,20 @@ +// Check that __asan_poison_memory_region works. +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// +// Check that we can disable it +// RUN: env ASAN_OPTIONS=allow_user_poisoning=0 %run %t + +#include <stdlib.h> + +extern "C" void __asan_poison_memory_region(void *, size_t); + +int main(int argc, char **argv) { + char *x = new char[16]; + x[10] = 0; + __asan_poison_memory_region(x, 16); + int res = x[argc * 10]; // BOOOM + // CHECK: ERROR: AddressSanitizer: use-after-poison + // CHECK: main{{.*}}use-after-poison.cc:[[@LINE-2]] + delete [] x; + return res; +} diff --git a/test/asan/TestCases/use-after-scope-dtor-order.cc b/test/asan/TestCases/use-after-scope-dtor-order.cc new file mode 100644 index 000000000000..7896dd30c400 --- /dev/null +++ b/test/asan/TestCases/use-after-scope-dtor-order.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s +// XFAIL: * +#include <stdio.h> + +struct IntHolder { + explicit IntHolder(int *val = 0) : val_(val) { } + ~IntHolder() { + printf("Value: %d\n", *val_); // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in IntHolder::~IntHolder{{.*}}use-after-scope-dtor-order.cc:[[@LINE-2]] + } + void set(int *val) { val_ = val; } + int *get() { return val_; } + + int *val_; +}; + +int main(int argc, char *argv[]) { + // It is incorrect to use "x" int IntHolder destructor, because "x" is + // "destroyed" earlier as it's declared later. + IntHolder holder; + int x = argc; + holder.set(&x); + return 0; +} diff --git a/test/asan/TestCases/use-after-scope-inlined.cc b/test/asan/TestCases/use-after-scope-inlined.cc new file mode 100644 index 000000000000..a0a0d9461cb9 --- /dev/null +++ b/test/asan/TestCases/use-after-scope-inlined.cc @@ -0,0 +1,28 @@ +// Test with "-O2" only to make sure inlining (leading to use-after-scope) +// happens. "always_inline" is not enough, as Clang doesn't emit +// llvm.lifetime intrinsics at -O0. +// +// RUN: %clangxx_asan -O2 -fsanitize=use-after-scope %s -o %t && not %run %t 2>&1 | FileCheck %s +// XFAIL: * + +int *arr; + +__attribute__((always_inline)) +void inlined(int arg) { + int x[5]; + for (int i = 0; i < arg; i++) x[i] = i; + arr = x; +} + +int main(int argc, char *argv[]) { + inlined(argc); + return arr[argc - 1]; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: READ of size 4 at 0x{{.*}} thread T0 + // CHECK: #0 0x{{.*}} in main + // CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]] + // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset + // CHECK: [[OFFSET:[^ ]*]] in frame + // CHECK: main + // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i' +} diff --git a/test/asan/TestCases/use-after-scope-nobug.cc b/test/asan/TestCases/use-after-scope-nobug.cc new file mode 100644 index 000000000000..21b085c96275 --- /dev/null +++ b/test/asan/TestCases/use-after-scope-nobug.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && %run %t +// XFAIL: * + +#include <stdio.h> + +int main() { + int *p = 0; + // Variable goes in and out of scope. + for (int i = 0; i < 3; i++) { + int x = 0; + p = &x; + } + printf("PASSED\n"); + return 0; +} diff --git a/test/asan/TestCases/use-after-scope-temp.cc b/test/asan/TestCases/use-after-scope-temp.cc new file mode 100644 index 000000000000..f9bd779ac1a2 --- /dev/null +++ b/test/asan/TestCases/use-after-scope-temp.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: %run %t 2>&1 | FileCheck %s +// +// Lifetime for temporaries is not emitted yet. +// XFAIL: * + +#include <stdio.h> + +struct IntHolder { + explicit IntHolder(int val) : val(val) { + printf("IntHolder: %d\n", val); + } + int val; +}; + +const IntHolder *saved; + +void save(const IntHolder &holder) { + saved = &holder; +} + +int main(int argc, char *argv[]) { + save(IntHolder(10)); + int x = saved->val; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope-temp.cc:[[@LINE-2]] + printf("saved value: %d\n", x); + return 0; +} diff --git a/test/asan/TestCases/use-after-scope.cc b/test/asan/TestCases/use-after-scope.cc new file mode 100644 index 000000000000..f98a8e6b62e1 --- /dev/null +++ b/test/asan/TestCases/use-after-scope.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS="detect_stack_use_after_return=1" not %run %t 2>&1 | FileCheck %s +// XFAIL: * + +int main() { + int *p = 0; + { + int x = 0; + p = &x; + } + return *p; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope.cc:[[@LINE-2]] + // CHECK: Address 0x{{.*}} is located in stack of thread T{{.*}} at offset [[OFFSET:[^ ]+]] in frame + // {{\[}}[[OFFSET]], {{[0-9]+}}) 'x' +} diff --git a/test/asan/TestCases/zero_page_pc.cc b/test/asan/TestCases/zero_page_pc.cc new file mode 100644 index 000000000000..5810a9fb9dde --- /dev/null +++ b/test/asan/TestCases/zero_page_pc.cc @@ -0,0 +1,12 @@ +// Check that ASan correctly detects SEGV on the zero page. +// RUN: %clangxx_asan %s -o %t && not %run %t 2>&1 | FileCheck %s + +typedef void void_f(); +int main() { + void_f *func = (void_f *)0x4; + func(); + // x86 reports the SEGV with both address=4 and pc=4. + // PowerPC64 reports it with address=4 but pc still in main(). + // CHECK: {{AddressSanitizer: SEGV.*(address|pc) 0x0*4}} + return 0; +} diff --git a/test/asan/Unit/lit.site.cfg.in b/test/asan/Unit/lit.site.cfg.in new file mode 100644 index 000000000000..1791b6b05b1c --- /dev/null +++ b/test/asan/Unit/lit.site.cfg.in @@ -0,0 +1,24 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +import os + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +def push_ld_library_path(config, new_path): + new_ld_library_path = os.path.pathsep.join( + (new_path, config.environment['LD_LIBRARY_PATH'])) + config.environment['LD_LIBRARY_PATH'] = new_ld_library_path + +# Setup config name. +config.name = 'AddressSanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with ASan unit tests. +# FIXME: De-hardcode this path. +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/asan/tests" +config.test_source_root = config.test_exec_root + +# Set LD_LIBRARY_PATH to pick dynamic runtime up properly. +push_ld_library_path(config, config.compiler_rt_libdir) diff --git a/test/asan/android_commands/android_common.py b/test/asan/android_commands/android_common.py new file mode 100644 index 000000000000..43ac7b48d770 --- /dev/null +++ b/test/asan/android_commands/android_common.py @@ -0,0 +1,29 @@ +import os, subprocess, tempfile +import time + +ANDROID_TMPDIR = '/data/local/tmp/Output' +ADB = os.environ.get('ADB', 'adb') + +verbose = False +if os.environ.get('ANDROID_RUN_VERBOSE') == '1': + verbose = True + +def adb(args): + if verbose: + print args + devnull = open(os.devnull, 'w') + return subprocess.call([ADB] + args, stdout=devnull, stderr=subprocess.STDOUT) + +def pull_from_device(path): + tmp = tempfile.mktemp() + adb(['pull', path, tmp]) + text = open(tmp, 'r').read() + os.unlink(tmp) + return text + +def push_to_device(path): + # Workaround for https://code.google.com/p/android/issues/detail?id=65857 + dst_path = os.path.join(ANDROID_TMPDIR, os.path.basename(path)) + tmp_path = dst_path + '.push' + adb(['push', path, tmp_path]) + adb(['shell', 'cp "%s" "%s" 2>&1' % (tmp_path, dst_path)]) diff --git a/test/asan/android_commands/android_compile.py b/test/asan/android_commands/android_compile.py new file mode 100755 index 000000000000..4b880886b0c1 --- /dev/null +++ b/test/asan/android_commands/android_compile.py @@ -0,0 +1,36 @@ +#!/usr/bin/python + +import os, sys, subprocess +from android_common import * + + +here = os.path.abspath(os.path.dirname(sys.argv[0])) +android_run = os.path.join(here, 'android_run.py') + +output = None +output_type = 'executable' + +args = sys.argv[1:] +while args: + arg = args.pop(0) + if arg == '-shared': + output_type = 'shared' + elif arg == '-c': + output_type = 'object' + elif arg == '-o': + output = args.pop(0) + +if output == None: + print "No output file name!" + sys.exit(1) + +ret = subprocess.call(sys.argv[1:]) +if ret != 0: + sys.exit(ret) + +if output_type in ['executable', 'shared']: + push_to_device(output) + +if output_type == 'executable': + os.rename(output, output + '.real') + os.symlink(android_run, output) diff --git a/test/asan/android_commands/android_run.py b/test/asan/android_commands/android_run.py new file mode 100755 index 000000000000..7f8c612a0cf0 --- /dev/null +++ b/test/asan/android_commands/android_run.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +import os, sys, subprocess, tempfile +from android_common import * + +ANDROID_TMPDIR = '/data/local/tmp/Output' + +here = os.path.abspath(os.path.dirname(sys.argv[0])) +device_binary = os.path.join(ANDROID_TMPDIR, os.path.basename(sys.argv[0])) + +def build_env(): + args = [] + # Android linker ignores RPATH. Set LD_LIBRARY_PATH to Output dir. + args.append('LD_LIBRARY_PATH=%s:%s' % + (ANDROID_TMPDIR, os.environ.get('LD_LIBRARY_PATH', ''))) + for (key, value) in os.environ.items(): + if key in ['ASAN_OPTIONS']: + args.append('%s="%s"' % (key, value)) + return ' '.join(args) + +device_env = build_env() +device_args = ' '.join(sys.argv[1:]) # FIXME: escape? +device_stdout = device_binary + '.stdout' +device_stderr = device_binary + '.stderr' +device_exitcode = device_binary + '.exitcode' +ret = adb(['shell', 'cd %s && %s asanwrapper %s %s >%s 2>%s ; echo $? >%s' % + (ANDROID_TMPDIR, device_env, device_binary, device_args, + device_stdout, device_stderr, device_exitcode)]) +if ret != 0: + sys.exit(ret) + +sys.stdout.write(pull_from_device(device_stdout)) +sys.stderr.write(pull_from_device(device_stderr)) +sys.exit(int(pull_from_device(device_exitcode))) diff --git a/test/asan/lit.cfg b/test/asan/lit.cfg new file mode 100644 index 000000000000..82a36de9cdda --- /dev/null +++ b/test/asan/lit.cfg @@ -0,0 +1,153 @@ +# -*- Python -*- + +import os +import platform + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +def push_dynamic_library_lookup_path(config, new_path): + if platform.system() == 'Windows': + dynamic_library_lookup_var = 'PATH' + else: + dynamic_library_lookup_var = 'LD_LIBRARY_PATH' + + new_ld_library_path = os.path.pathsep.join( + (new_path, config.environment[dynamic_library_lookup_var])) + config.environment[dynamic_library_lookup_var] = new_ld_library_path + +# Setup config name. +config.name = 'AddressSanitizer' + config.name_suffix + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# GCC-ASan doesn't link in all the necessary libraries automatically, so +# we have to do it ourselves. +if config.compiler_id == 'GNU': + extra_linkflags = ["-pthread", "-lstdc++"] +else: + extra_linkflags = [] + +# There is no libdl on FreeBSD. +if config.compiler_id == 'GNU' and config.host_os != 'FreeBSD': + extra_linkflags += ["-ldl"] + +# Setup default compiler flags used with -fsanitize=address option. +# FIXME: Review the set of required flags and check if it can be reduced. +target_cflags = [get_required_attr(config, "target_cflags")] + extra_linkflags +target_cxxflags = config.cxx_mode_flags + target_cflags +clang_asan_static_cflags = (["-fsanitize=address", + "-mno-omit-leaf-frame-pointer", + "-fno-omit-frame-pointer", + "-fno-optimize-sibling-calls"] + + config.debug_info_flags + target_cflags) +clang_asan_static_cxxflags = config.cxx_mode_flags + clang_asan_static_cflags + +if config.asan_dynamic: + clang_asan_cflags = clang_asan_static_cflags + ['-shared-libasan'] + clang_asan_cxxflags = clang_asan_static_cxxflags + ['-shared-libasan'] + config.available_features.add("asan-dynamic-runtime") +else: + clang_asan_cflags = clang_asan_static_cflags + clang_asan_cxxflags = clang_asan_static_cxxflags + config.available_features.add("asan-static-runtime") + +asan_lit_source_dir = get_required_attr(config, "asan_lit_source_dir") +if config.android == "1": + config.available_features.add('android') + clang_wrapper = os.path.join(asan_lit_source_dir, + "android_commands", "android_compile.py") + " " +else: + config.available_features.add('not-android') + clang_wrapper = "" + +def build_invocation(compile_flags): + return " " + " ".join([clang_wrapper, config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang ", build_invocation(target_cflags)) ) +config.substitutions.append( ("%clangxx ", build_invocation(target_cxxflags)) ) +config.substitutions.append( ("%clang_asan ", build_invocation(clang_asan_cflags)) ) +config.substitutions.append( ("%clangxx_asan ", build_invocation(clang_asan_cxxflags)) ) +config.substitutions.append( ("%shared_libasan", "libclang_rt.asan-%s.so" % config.target_arch)) +if config.asan_dynamic: + config.substitutions.append( ("%clang_asan_static ", build_invocation(clang_asan_static_cflags)) ) + config.substitutions.append( ("%clangxx_asan_static ", build_invocation(clang_asan_static_cxxflags)) ) + +# Windows-specific tests might also use the clang-cl.exe driver. +if platform.system() == 'Windows': + clang_cl_asan_cxxflags = ["-fsanitize=address", + "-Wno-deprecated-declarations", + "-WX", + "-D_HAS_EXCEPTIONS=0", + "-Zi"] + target_cflags + if config.asan_dynamic: + clang_cl_asan_cxxflags.append("-MD") + clang_invocation = build_invocation(clang_cl_asan_cxxflags) + clang_cl_invocation = clang_invocation.replace("clang.exe","clang-cl.exe") + config.substitutions.append( ("%clang_cl_asan ", clang_cl_invocation) ) + config.substitutions.append( ("%asan_dll_thunk", + os.path.join(config.compiler_rt_libdir, "clang_rt.asan_dll_thunk-i386.lib"))) + +# FIXME: De-hardcode this path. +asan_source_dir = os.path.join( + get_required_attr(config, "compiler_rt_src_root"), "lib", "asan") +# Setup path to asan_symbolize.py script. +asan_symbolize = os.path.join(asan_source_dir, "scripts", "asan_symbolize.py") +if not os.path.exists(asan_symbolize): + lit_config.fatal("Can't find script on path %r" % asan_symbolize) +python_exec = get_required_attr(config, "python_executable") +config.substitutions.append( ("%asan_symbolize", python_exec + " " + asan_symbolize + " ") ) +# Setup path to sancov.py script. +sanitizer_common_source_dir = os.path.join( + get_required_attr(config, "compiler_rt_src_root"), "lib", "sanitizer_common") +sancov = os.path.join(sanitizer_common_source_dir, "scripts", "sancov.py") +if not os.path.exists(sancov): + lit_config.fatal("Can't find script on path %r" % sancov) +python_exec = get_required_attr(config, "python_executable") +config.substitutions.append( ("%sancov", python_exec + " " + sancov + " ") ) + +# Determine kernel bitness +if config.host_arch.find('64') != -1 and config.android != "1": + kernel_bits = '64' +else: + kernel_bits = '32' + +config.substitutions.append( ('CHECK-%kernel_bits', ("CHECK-kernel-" + kernel_bits + "-bits"))) + +config.available_features.add("asan-" + config.bits + "-bits") + +# Allow tests to use REQUIRES=stable-runtime. For use when you cannot use XFAIL +# because the test hangs. +if config.target_arch != 'arm': + config.available_features.add('stable-runtime') + +# Turn on leak detection on 64-bit Linux. +if config.host_os == 'Linux' and config.target_arch == 'x86_64': + config.available_features.add('leak-detection') + +# Set LD_LIBRARY_PATH to pick dynamic runtime up properly. +push_dynamic_library_lookup_path(config, config.compiler_rt_libdir) + +# GCC-ASan uses dynamic runtime by default. +if config.compiler_id == 'GNU': + gcc_dir = os.path.dirname(config.clang) + libasan_dir = os.path.join(gcc_dir, "..", "lib" + config.bits) + push_dynamic_library_lookup_path(config, libasan_dir) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +if config.host_os == 'Darwin': + config.suffixes.append('.mm') + +# AddressSanitizer tests are currently supported on Linux, Darwin and +# FreeBSD only. +if config.host_os not in ['Linux', 'Darwin', 'FreeBSD']: + config.unsupported = True diff --git a/test/asan/lit.site.cfg.in b/test/asan/lit.site.cfg.in new file mode 100644 index 000000000000..332f9ad9f828 --- /dev/null +++ b/test/asan/lit.site.cfg.in @@ -0,0 +1,19 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Tool-specific config options. +config.name_suffix = "@ASAN_TEST_CONFIG_SUFFIX@" +config.asan_lit_source_dir = "@ASAN_LIT_SOURCE_DIR@" +config.target_cflags = "@ASAN_TEST_TARGET_CFLAGS@" +config.clang = "@ASAN_TEST_TARGET_CC@" +config.llvm_tools_dir = "@LLVM_TOOLS_BINARY_DIR@" +config.bits = "@ASAN_TEST_BITS@" +config.android = "@ANDROID@" +config.asan_dynamic = @ASAN_TEST_DYNAMIC@ +config.target_arch = "@ASAN_TEST_TARGET_ARCH@" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@ASAN_LIT_SOURCE_DIR@/lit.cfg") diff --git a/test/Unit/absvdi2_test.c b/test/builtins/Unit/absvdi2_test.c index f0bf560681e7..f0bf560681e7 100644 --- a/test/Unit/absvdi2_test.c +++ b/test/builtins/Unit/absvdi2_test.c diff --git a/test/Unit/absvsi2_test.c b/test/builtins/Unit/absvsi2_test.c index 3b88078dfb4a..3b88078dfb4a 100644 --- a/test/Unit/absvsi2_test.c +++ b/test/builtins/Unit/absvsi2_test.c diff --git a/test/Unit/absvti2_test.c b/test/builtins/Unit/absvti2_test.c index 8233c18ff09a..9b71f200ac7b 100644 --- a/test/Unit/absvti2_test.c +++ b/test/builtins/Unit/absvti2_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> #include <stdlib.h> +#ifdef CRT_HAS_128BIT + // Returns: absolute value // Effects: aborts if abs(x) < 0 @@ -49,7 +49,7 @@ int test__absvti2(ti_int a) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT // if (test__absvti2(make_ti(0x8000000000000000LL, 0))) // should abort // return 1; diff --git a/test/Unit/adddf3vfp_test.c b/test/builtins/Unit/adddf3vfp_test.c index 5ad42f7b3a6e..5ad42f7b3a6e 100644 --- a/test/Unit/adddf3vfp_test.c +++ b/test/builtins/Unit/adddf3vfp_test.c diff --git a/test/Unit/addsf3vfp_test.c b/test/builtins/Unit/addsf3vfp_test.c index 95e057c36305..95e057c36305 100644 --- a/test/Unit/addsf3vfp_test.c +++ b/test/builtins/Unit/addsf3vfp_test.c diff --git a/test/builtins/Unit/addtf3_test.c b/test/builtins/Unit/addtf3_test.c new file mode 100644 index 000000000000..4a3aacd96e6d --- /dev/null +++ b/test/builtins/Unit/addtf3_test.c @@ -0,0 +1,81 @@ +//===--------------- addtf3_test.c - Test __addtf3 ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __addtf3 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +// Returns: a + b +long double __addtf3(long double a, long double b); + +int test__addtf3(long double a, long double b, + uint64_t expectedHi, uint64_t expectedLo) +{ + long double x = __addtf3(a, b); + int ret = compareResultLD(x, expectedHi, expectedLo); + + if (ret){ + printf("error in test__addtf3(%.20Lf, %.20Lf) = %.20Lf, " + "expected %.20Lf\n", a, b, x, + fromRep128(expectedHi, expectedLo)); + } + + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + // qNaN + any = qNaN + if (test__addtf3(makeQNaN128(), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // NaN + any = NaN + if (test__addtf3(makeNaN128(UINT64_C(0x800030000000)), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // inf + inf = inf + if (test__addtf3(makeInf128(), + makeInf128(), + UINT64_C(0x7fff000000000000), + UINT64_C(0x0))) + return 1; + // inf + any = inf + if (test__addtf3(makeInf128(), + 0x1.2335653452436234723489432abcdefp+5L, + UINT64_C(0x7fff000000000000), + UINT64_C(0x0))) + return 1; + // any + any + if (test__addtf3(0x1.23456734245345543849abcdefp+5L, + 0x1.edcba52449872455634654321fp-1L, + UINT64_C(0x40042afc95c8b579), + UINT64_C(0x61e58dd6c51eb77c))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/addvdi3_test.c b/test/builtins/Unit/addvdi3_test.c index 0d7271d5a4ae..0d7271d5a4ae 100644 --- a/test/Unit/addvdi3_test.c +++ b/test/builtins/Unit/addvdi3_test.c diff --git a/test/Unit/addvsi3_test.c b/test/builtins/Unit/addvsi3_test.c index 59fd9d2ae15f..59fd9d2ae15f 100644 --- a/test/Unit/addvsi3_test.c +++ b/test/builtins/Unit/addvsi3_test.c diff --git a/test/Unit/addvti3_test.c b/test/builtins/Unit/addvti3_test.c index 1cf066c48917..fe093e3b26a2 100644 --- a/test/Unit/addvti3_test.c +++ b/test/builtins/Unit/addvti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: a + b // Effects: aborts if a + b overflows @@ -48,7 +48,7 @@ int test__addvti3(ti_int a, ti_int b) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT // should abort // test__addvti3(make_ti(0x8000000000000000LL, 0x0000000000000000LL), // make_ti(0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL)); diff --git a/test/Unit/ashldi3_test.c b/test/builtins/Unit/ashldi3_test.c index fb80c6f77e40..fb80c6f77e40 100644 --- a/test/Unit/ashldi3_test.c +++ b/test/builtins/Unit/ashldi3_test.c diff --git a/test/Unit/ashlti3_test.c b/test/builtins/Unit/ashlti3_test.c index b9f4dcc0a8c7..2361d16b3699 100644 --- a/test/Unit/ashlti3_test.c +++ b/test/builtins/Unit/ashlti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: a << b // Precondition: 0 <= b < bits_in_tword @@ -49,7 +49,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__ashlti3(make_ti(0xFEDCBA9876543215LL, 0xFEDCBA9876543215LL), 0, make_ti(0xFEDCBA9876543215LL, 0xFEDCBA9876543215LL))) return 1; diff --git a/test/Unit/ashrdi3_test.c b/test/builtins/Unit/ashrdi3_test.c index ac517e191880..ac517e191880 100644 --- a/test/Unit/ashrdi3_test.c +++ b/test/builtins/Unit/ashrdi3_test.c diff --git a/test/Unit/ashrti3_test.c b/test/builtins/Unit/ashrti3_test.c index 3abee36b6b7d..62ba1017ecd9 100644 --- a/test/Unit/ashrti3_test.c +++ b/test/builtins/Unit/ashrti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: arithmetic a >> b // Precondition: 0 <= b < bits_in_tword @@ -47,7 +47,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__ashrti3(make_ti(0xFEDCBA9876543215LL, 0xFEDCBA9876543215LL), 0, make_ti(0xFEDCBA9876543215LL, 0xFEDCBA9876543215LL))) return 1; diff --git a/test/Unit/bswapdi2_test.c b/test/builtins/Unit/bswapdi2_test.c index 2d830cf5e41e..2d830cf5e41e 100644 --- a/test/Unit/bswapdi2_test.c +++ b/test/builtins/Unit/bswapdi2_test.c diff --git a/test/Unit/bswapsi2_test.c b/test/builtins/Unit/bswapsi2_test.c index 4488a888e1eb..4488a888e1eb 100644 --- a/test/Unit/bswapsi2_test.c +++ b/test/builtins/Unit/bswapsi2_test.c diff --git a/test/Unit/clear_cache_test.c b/test/builtins/Unit/clear_cache_test.c index 3507fd80b288..3507fd80b288 100644 --- a/test/Unit/clear_cache_test.c +++ b/test/builtins/Unit/clear_cache_test.c diff --git a/test/Unit/clzdi2_test.c b/test/builtins/Unit/clzdi2_test.c index 58403f091f48..58403f091f48 100644 --- a/test/Unit/clzdi2_test.c +++ b/test/builtins/Unit/clzdi2_test.c diff --git a/test/Unit/clzsi2_test.c b/test/builtins/Unit/clzsi2_test.c index cc1da64b03ef..cc1da64b03ef 100644 --- a/test/Unit/clzsi2_test.c +++ b/test/builtins/Unit/clzsi2_test.c diff --git a/test/Unit/clzti2_test.c b/test/builtins/Unit/clzti2_test.c index d2ca1645dd1a..5a0e3e8b1e98 100644 --- a/test/Unit/clzti2_test.c +++ b/test/builtins/Unit/clzti2_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: the number of leading 0-bits // Precondition: a != 0 @@ -41,7 +41,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT const int N = (int)(sizeof(ti_int) * CHAR_BIT); if (test__clzti2(0x00000001, N-1)) diff --git a/test/Unit/cmpdi2_test.c b/test/builtins/Unit/cmpdi2_test.c index 609ab1a63a61..609ab1a63a61 100644 --- a/test/Unit/cmpdi2_test.c +++ b/test/builtins/Unit/cmpdi2_test.c diff --git a/test/Unit/cmpti2_test.c b/test/builtins/Unit/cmpti2_test.c index 4339e0abb5e9..15ee4fc68d53 100644 --- a/test/Unit/cmpti2_test.c +++ b/test/builtins/Unit/cmpti2_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: if (a < b) returns 0 // if (a == b) returns 1 // if (a > b) returns 2 @@ -43,7 +43,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__cmpti2(0, 0, 1)) return 1; if (test__cmpti2(1, 1, 1)) diff --git a/test/Unit/comparedf2_test.c b/test/builtins/Unit/comparedf2_test.c index 662372290290..662372290290 100644 --- a/test/Unit/comparedf2_test.c +++ b/test/builtins/Unit/comparedf2_test.c diff --git a/test/Unit/comparesf2_test.c b/test/builtins/Unit/comparesf2_test.c index 026e90053bcf..026e90053bcf 100644 --- a/test/Unit/comparesf2_test.c +++ b/test/builtins/Unit/comparesf2_test.c diff --git a/test/Unit/ctzdi2_test.c b/test/builtins/Unit/ctzdi2_test.c index 1f2d101a1943..1f2d101a1943 100644 --- a/test/Unit/ctzdi2_test.c +++ b/test/builtins/Unit/ctzdi2_test.c diff --git a/test/Unit/ctzsi2_test.c b/test/builtins/Unit/ctzsi2_test.c index 36f221595b68..36f221595b68 100644 --- a/test/Unit/ctzsi2_test.c +++ b/test/builtins/Unit/ctzsi2_test.c diff --git a/test/Unit/ctzti2_test.c b/test/builtins/Unit/ctzti2_test.c index c4934bd254b7..9a972f9e2f94 100644 --- a/test/Unit/ctzti2_test.c +++ b/test/builtins/Unit/ctzti2_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: the number of trailing 0-bits // Precondition: a != 0 @@ -41,7 +41,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__ctzti2(0x00000001, 0)) return 1; if (test__ctzti2(0x00000002, 1)) diff --git a/test/Unit/divdc3_test.c b/test/builtins/Unit/divdc3_test.c index 9224cddceeb8..9224cddceeb8 100644 --- a/test/Unit/divdc3_test.c +++ b/test/builtins/Unit/divdc3_test.c diff --git a/test/Unit/divdf3vfp_test.c b/test/builtins/Unit/divdf3vfp_test.c index e13822ffcaa0..e13822ffcaa0 100644 --- a/test/Unit/divdf3vfp_test.c +++ b/test/builtins/Unit/divdf3vfp_test.c diff --git a/test/Unit/divdi3_test.c b/test/builtins/Unit/divdi3_test.c index c25f917a419e..c25f917a419e 100644 --- a/test/Unit/divdi3_test.c +++ b/test/builtins/Unit/divdi3_test.c diff --git a/test/Unit/divmodsi4_test.c b/test/builtins/Unit/divmodsi4_test.c index bea31ea9a445..bea31ea9a445 100644 --- a/test/Unit/divmodsi4_test.c +++ b/test/builtins/Unit/divmodsi4_test.c diff --git a/test/Unit/divsc3_test.c b/test/builtins/Unit/divsc3_test.c index 9d060a2d1489..9d060a2d1489 100644 --- a/test/Unit/divsc3_test.c +++ b/test/builtins/Unit/divsc3_test.c diff --git a/test/Unit/divsf3vfp_test.c b/test/builtins/Unit/divsf3vfp_test.c index 8382558412cf..8382558412cf 100644 --- a/test/Unit/divsf3vfp_test.c +++ b/test/builtins/Unit/divsf3vfp_test.c diff --git a/test/Unit/divsi3_test.c b/test/builtins/Unit/divsi3_test.c index 6fda54ff37ec..6fda54ff37ec 100644 --- a/test/Unit/divsi3_test.c +++ b/test/builtins/Unit/divsi3_test.c diff --git a/test/Unit/divtc3_test.c b/test/builtins/Unit/divtc3_test.c index 7bb74d755146..7bb74d755146 100644 --- a/test/Unit/divtc3_test.c +++ b/test/builtins/Unit/divtc3_test.c diff --git a/test/builtins/Unit/divtf3_test.c b/test/builtins/Unit/divtf3_test.c new file mode 100644 index 000000000000..dad631cc4f17 --- /dev/null +++ b/test/builtins/Unit/divtf3_test.c @@ -0,0 +1,94 @@ +//===--------------- divtf3_test.c - Test __divtf3 ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __divtf3 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +// Returns: a / b +long double __divtf3(long double a, long double b); + +int test__divtf3(long double a, long double b, + uint64_t expectedHi, uint64_t expectedLo) +{ + long double x = __divtf3(a, b); + int ret = compareResultLD(x, expectedHi, expectedLo); + + if (ret){ + printf("error in test__divtf3(%.20Lf, %.20Lf) = %.20Lf, " + "expected %.20Lf\n", a, b, x, + fromRep128(expectedHi, expectedLo)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + // qNaN / any = qNaN + if (test__divtf3(makeQNaN128(), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // NaN / any = NaN + if (test__divtf3(makeNaN128(UINT64_C(0x800030000000)), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // inf / any = inf + if (test__divtf3(makeInf128(), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff000000000000), + UINT64_C(0x0))) + return 1; + // any / any + if (test__divtf3(0x1.a23b45362464523375893ab4cdefp+5L, + 0x1.eedcbaba3a94546558237654321fp-1L, + UINT64_C(0x4004b0b72924d407), + UINT64_C(0x0717e84356c6eba2))) + return 1; + if (test__divtf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50L, + 0x1.ed2c3ba15935332532287654321fp-9L, + UINT64_C(0x3fd5b2af3f828c9b), + UINT64_C(0x40e51f64cde8b1f2))) + return 15; + if (test__divtf3(0x1.2345f6aaaa786555f42432abcdefp+456L, + 0x1.edacbba9874f765463544dd3621fp+6400L, + UINT64_C(0x28c62e15dc464466), + UINT64_C(0xb5a07586348557ac))) + return 1; + if (test__divtf3(0x1.2d3456f789ba6322bc665544edefp-234L, + 0x1.eddcdba39f3c8b7a36564354321fp-4455L, + UINT64_C(0x507b38442b539266), + UINT64_C(0x22ce0f1d024e1252))) + return 1; + if (test__divtf3(0x1.2345f6b77b7a8953365433abcdefp+234L, + 0x1.edcba987d6bb3aa467754354321fp-4055L, + UINT64_C(0x50bf2e02f0798d36), + UINT64_C(0x5e6fcb6b60044078))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/divti3_test.c b/test/builtins/Unit/divti3_test.c index 7c8e2970fa6e..bc81c2a5418d 100644 --- a/test/Unit/divti3_test.c +++ b/test/builtins/Unit/divti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: a / b ti_int __divti3(ti_int a, ti_int b); @@ -47,7 +47,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__divti3(0, 1, 0)) return 1; if (test__divti3(0, -1, 0)) diff --git a/test/Unit/divxc3_test.c b/test/builtins/Unit/divxc3_test.c index aa8a7625d139..aa8a7625d139 100644 --- a/test/Unit/divxc3_test.c +++ b/test/builtins/Unit/divxc3_test.c diff --git a/test/Unit/enable_execute_stack_test.c b/test/builtins/Unit/enable_execute_stack_test.c index c0f67b337939..c0f67b337939 100644 --- a/test/Unit/enable_execute_stack_test.c +++ b/test/builtins/Unit/enable_execute_stack_test.c diff --git a/test/Unit/endianness.h b/test/builtins/Unit/endianness.h index 06c53de0bfa9..06c53de0bfa9 100644 --- a/test/Unit/endianness.h +++ b/test/builtins/Unit/endianness.h diff --git a/test/Unit/eqdf2vfp_test.c b/test/builtins/Unit/eqdf2vfp_test.c index 585bd08e9789..585bd08e9789 100644 --- a/test/Unit/eqdf2vfp_test.c +++ b/test/builtins/Unit/eqdf2vfp_test.c diff --git a/test/Unit/eqsf2vfp_test.c b/test/builtins/Unit/eqsf2vfp_test.c index b0eed9402a37..b0eed9402a37 100644 --- a/test/Unit/eqsf2vfp_test.c +++ b/test/builtins/Unit/eqsf2vfp_test.c diff --git a/test/builtins/Unit/eqtf2_test.c b/test/builtins/Unit/eqtf2_test.c new file mode 100644 index 000000000000..038583503ab2 --- /dev/null +++ b/test/builtins/Unit/eqtf2_test.c @@ -0,0 +1,89 @@ +//===------------ eqtf2_test.c - Test __eqtf2------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __eqtf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +int __eqtf2(long double a, long double b); + +int test__eqtf2(long double a, long double b, enum EXPECTED_RESULT expected) +{ + int x = __eqtf2(a, b); + int ret = compareResultCMP(x, expected); + + if (ret){ + printf("error in test__eqtf2(%.20Lf, %.20Lf) = %d, " + "expected %s\n", a, b, x, expectedStr(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + // NaN + if (test__eqtf2(makeQNaN128(), + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // < + // exp + if (test__eqtf2(0x1.234567890abcdef1234567890abcp-3L, + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // mantissa + if (test__eqtf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.334567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // sign + if (test__eqtf2(-0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // == + if (test__eqtf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + EQUAL_0)) + return 1; + // > + // exp + if (test__eqtf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp-3L, + NEQUAL_0)) + return 1; + // mantissa + if (test__eqtf2(0x1.334567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // sign + if (test__eqtf2(0x1.234567890abcdef1234567890abcp+3L, + -0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/extebdsfdf2vfp_test.c b/test/builtins/Unit/extebdsfdf2vfp_test.c index 3a009cf2d7f8..3a009cf2d7f8 100644 --- a/test/Unit/extebdsfdf2vfp_test.c +++ b/test/builtins/Unit/extebdsfdf2vfp_test.c diff --git a/test/builtins/Unit/extenddftf2_test.c b/test/builtins/Unit/extenddftf2_test.c new file mode 100644 index 000000000000..05acc08c0951 --- /dev/null +++ b/test/builtins/Unit/extenddftf2_test.c @@ -0,0 +1,82 @@ +//===--------------- extenddftf2_test.c - Test __extenddftf2 --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __extenddftf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +long double __extenddftf2(double a); + +int test__extenddftf2(double a, uint64_t expectedHi, uint64_t expectedLo) +{ + long double x = __extenddftf2(a); + int ret = compareResultLD(x, expectedHi, expectedLo); + + if (ret){ + printf("error in test__extenddftf2(%f) = %.20Lf, " + "expected %.20Lf\n", a, x, fromRep128(expectedHi, expectedLo)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + // qNaN + if (test__extenddftf2(makeQNaN64(), + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // NaN + if (test__extenddftf2(makeNaN64(UINT64_C(0x7100000000000)), + UINT64_C(0x7fff710000000000), + UINT64_C(0x0))) + return 1; + // inf + if (test__extenddftf2(makeInf64(), + UINT64_C(0x7fff000000000000), + UINT64_C(0x0))) + return 1; + // zero + if (test__extenddftf2(0.0, UINT64_C(0x0), UINT64_C(0x0))) + return 1; + + if (test__extenddftf2(0x1.23456789abcdefp+5, + UINT64_C(0x400423456789abcd), + UINT64_C(0xf000000000000000))) + return 1; + if (test__extenddftf2(0x1.edcba987654321fp-9, + UINT64_C(0x3ff6edcba9876543), + UINT64_C(0x2000000000000000))) + return 1; + if (test__extenddftf2(0x1.23456789abcdefp+45, + UINT64_C(0x402c23456789abcd), + UINT64_C(0xf000000000000000))) + return 1; + if (test__extenddftf2(0x1.edcba987654321fp-45, + UINT64_C(0x3fd2edcba9876543), + UINT64_C(0x2000000000000000))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/builtins/Unit/extendsftf2_test.c b/test/builtins/Unit/extendsftf2_test.c new file mode 100644 index 000000000000..5f41928b862f --- /dev/null +++ b/test/builtins/Unit/extendsftf2_test.c @@ -0,0 +1,83 @@ +//===--------------- extendsftf2_test.c - Test __extendsftf2 --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __extendsftf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +long double __extendsftf2(float a); + +int test__extendsftf2(float a, uint64_t expectedHi, uint64_t expectedLo) +{ + long double x = __extendsftf2(a); + int ret = compareResultLD(x, expectedHi, expectedLo); + + if (ret) + { + printf("error in test__extendsftf2(%f) = %.20Lf, " + "expected %.20Lf\n", a, x, fromRep128(expectedHi, expectedLo)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + // qNaN + if (test__extendsftf2(makeQNaN32(), + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // NaN + if (test__extendsftf2(makeNaN32(UINT32_C(0x410000)), + UINT64_C(0x7fff820000000000), + UINT64_C(0x0))) + return 1; + // inf + if (test__extendsftf2(makeInf32(), + UINT64_C(0x7fff000000000000), + UINT64_C(0x0))) + return 1; + // zero + if (test__extendsftf2(0.0f, UINT64_C(0x0), UINT64_C(0x0))) + return 1; + + if (test__extendsftf2(0x1.23456p+5f, + UINT64_C(0x4004234560000000), + UINT64_C(0x0))) + return 1; + if (test__extendsftf2(0x1.edcbap-9f, + UINT64_C(0x3ff6edcba0000000), + UINT64_C(0x0))) + return 1; + if (test__extendsftf2(0x1.23456p+45f, + UINT64_C(0x402c234560000000), + UINT64_C(0x0))) + return 1; + if (test__extendsftf2(0x1.edcbap-45f, + UINT64_C(0x3fd2edcba0000000), + UINT64_C(0x0))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/ffsdi2_test.c b/test/builtins/Unit/ffsdi2_test.c index 9041127d1a62..9041127d1a62 100644 --- a/test/Unit/ffsdi2_test.c +++ b/test/builtins/Unit/ffsdi2_test.c diff --git a/test/Unit/ffsti2_test.c b/test/builtins/Unit/ffsti2_test.c index fcb5c2725f26..f944ed0a1e45 100644 --- a/test/Unit/ffsti2_test.c +++ b/test/builtins/Unit/ffsti2_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: the index of the least significant 1-bit in a, or // the value zero if a is zero. The least significant bit is index one. @@ -40,7 +40,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__ffsti2(0x00000000, 0)) return 1; if (test__ffsti2(0x00000001, 1)) diff --git a/test/Unit/fixdfdi_test.c b/test/builtins/Unit/fixdfdi_test.c index d08afe3a5336..d08afe3a5336 100644 --- a/test/Unit/fixdfdi_test.c +++ b/test/builtins/Unit/fixdfdi_test.c diff --git a/test/Unit/fixdfsivfp_test.c b/test/builtins/Unit/fixdfsivfp_test.c index c6102e274ee5..c6102e274ee5 100644 --- a/test/Unit/fixdfsivfp_test.c +++ b/test/builtins/Unit/fixdfsivfp_test.c diff --git a/test/Unit/fixdfti_test.c b/test/builtins/Unit/fixdfti_test.c index 98a5628edddf..bfa88fd70e14 100644 --- a/test/Unit/fixdfti_test.c +++ b/test/builtins/Unit/fixdfti_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a signed long long, rounding toward zero. // Assumption: double is a IEEE 64 bit floating point type @@ -49,7 +49,7 @@ char assumption_3[sizeof(double)*CHAR_BIT == 64] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__fixdfti(0.0, 0)) return 1; diff --git a/test/Unit/fixsfdi_test.c b/test/builtins/Unit/fixsfdi_test.c index d3e934a5c02a..d3e934a5c02a 100644 --- a/test/Unit/fixsfdi_test.c +++ b/test/builtins/Unit/fixsfdi_test.c diff --git a/test/Unit/fixsfsivfp_test.c b/test/builtins/Unit/fixsfsivfp_test.c index 9abf5e856476..9abf5e856476 100644 --- a/test/Unit/fixsfsivfp_test.c +++ b/test/builtins/Unit/fixsfsivfp_test.c diff --git a/test/Unit/fixsfti_test.c b/test/builtins/Unit/fixsfti_test.c index 5fdd00578528..2b0b99774819 100644 --- a/test/Unit/fixsfti_test.c +++ b/test/builtins/Unit/fixsfti_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a signed long long, rounding toward zero. // Assumption: float is a IEEE 32 bit floating point type @@ -49,7 +49,7 @@ char assumption_3[sizeof(float)*CHAR_BIT == 32] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__fixsfti(0.0F, 0)) return 1; diff --git a/test/Unit/fixunsdfdi_test.c b/test/builtins/Unit/fixunsdfdi_test.c index 0803fd28f5bb..0803fd28f5bb 100644 --- a/test/Unit/fixunsdfdi_test.c +++ b/test/builtins/Unit/fixunsdfdi_test.c diff --git a/test/Unit/fixunsdfsi_test.c b/test/builtins/Unit/fixunsdfsi_test.c index 54fe35b5c35a..54fe35b5c35a 100644 --- a/test/Unit/fixunsdfsi_test.c +++ b/test/builtins/Unit/fixunsdfsi_test.c diff --git a/test/Unit/fixunsdfsivfp_test.c b/test/builtins/Unit/fixunsdfsivfp_test.c index 3727cf7b02fb..3727cf7b02fb 100644 --- a/test/Unit/fixunsdfsivfp_test.c +++ b/test/builtins/Unit/fixunsdfsivfp_test.c diff --git a/test/Unit/fixunsdfti_test.c b/test/builtins/Unit/fixunsdfti_test.c index 1e44b82660c3..9f89de493680 100644 --- a/test/Unit/fixunsdfti_test.c +++ b/test/builtins/Unit/fixunsdfti_test.c @@ -24,7 +24,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -#if __x86_64 +#ifdef CRT_HAS_128BIT tu_int __fixunsdfti(double a); @@ -51,7 +51,7 @@ char assumption_3[sizeof(double)*CHAR_BIT == 64] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__fixunsdfti(0.0, 0)) return 1; diff --git a/test/Unit/fixunssfdi_test.c b/test/builtins/Unit/fixunssfdi_test.c index ac89be7bbb27..ac89be7bbb27 100644 --- a/test/Unit/fixunssfdi_test.c +++ b/test/builtins/Unit/fixunssfdi_test.c diff --git a/test/Unit/fixunssfsi_test.c b/test/builtins/Unit/fixunssfsi_test.c index ce6a9287515f..ce6a9287515f 100644 --- a/test/Unit/fixunssfsi_test.c +++ b/test/builtins/Unit/fixunssfsi_test.c diff --git a/test/Unit/fixunssfsivfp_test.c b/test/builtins/Unit/fixunssfsivfp_test.c index c8e45f408b63..c8e45f408b63 100644 --- a/test/Unit/fixunssfsivfp_test.c +++ b/test/builtins/Unit/fixunssfsivfp_test.c diff --git a/test/Unit/fixunssfti_test.c b/test/builtins/Unit/fixunssfti_test.c index cc5e44ba59ed..7965b9500d05 100644 --- a/test/Unit/fixunssfti_test.c +++ b/test/builtins/Unit/fixunssfti_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a unsigned long long, rounding toward zero. // Negative values all become zero. @@ -51,7 +51,7 @@ char assumption_3[sizeof(float)*CHAR_BIT == 32] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__fixunssfti(0.0F, 0)) return 1; diff --git a/test/Unit/fixunstfdi_test.c b/test/builtins/Unit/fixunstfdi_test.c index d0a5db7a9c97..d0a5db7a9c97 100644 --- a/test/Unit/fixunstfdi_test.c +++ b/test/builtins/Unit/fixunstfdi_test.c diff --git a/test/Unit/fixunsxfdi_test.c b/test/builtins/Unit/fixunsxfdi_test.c index 4308f6f4ef1f..4308f6f4ef1f 100644 --- a/test/Unit/fixunsxfdi_test.c +++ b/test/builtins/Unit/fixunsxfdi_test.c diff --git a/test/Unit/fixunsxfsi_test.c b/test/builtins/Unit/fixunsxfsi_test.c index cb2a7f487210..cb2a7f487210 100644 --- a/test/Unit/fixunsxfsi_test.c +++ b/test/builtins/Unit/fixunsxfsi_test.c diff --git a/test/Unit/fixunsxfti_test.c b/test/builtins/Unit/fixunsxfti_test.c index 0e3c842bc022..7d18b1267b3a 100644 --- a/test/Unit/fixunsxfti_test.c +++ b/test/builtins/Unit/fixunsxfti_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a unsigned long long, rounding toward zero. // Negative values all become zero. @@ -52,7 +52,7 @@ char assumption_3[sizeof(long double)*CHAR_BIT == 128] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__fixunsxfti(0.0, 0)) return 1; diff --git a/test/Unit/fixxfdi_test.c b/test/builtins/Unit/fixxfdi_test.c index 43ac0f8aaa3c..43ac0f8aaa3c 100644 --- a/test/Unit/fixxfdi_test.c +++ b/test/builtins/Unit/fixxfdi_test.c diff --git a/test/Unit/fixxfti_test.c b/test/builtins/Unit/fixxfti_test.c index 11296b036871..87914c5341bd 100644 --- a/test/Unit/fixxfti_test.c +++ b/test/builtins/Unit/fixxfti_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a signed long long, rounding toward zero. // Assumption: long double is an intel 80 bit floating point type padded with 6 bytes @@ -50,7 +50,7 @@ char assumption_3[sizeof(long double)*CHAR_BIT == 128] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__fixxfti(0.0, 0)) return 1; diff --git a/test/Unit/floatdidf_test.c b/test/builtins/Unit/floatdidf_test.c index af3dacd4f38b..af3dacd4f38b 100644 --- a/test/Unit/floatdidf_test.c +++ b/test/builtins/Unit/floatdidf_test.c diff --git a/test/Unit/floatdisf_test.c b/test/builtins/Unit/floatdisf_test.c index 3e71df7b2228..3e71df7b2228 100644 --- a/test/Unit/floatdisf_test.c +++ b/test/builtins/Unit/floatdisf_test.c diff --git a/test/Unit/floatdixf_test.c b/test/builtins/Unit/floatdixf_test.c index 337666426318..337666426318 100644 --- a/test/Unit/floatdixf_test.c +++ b/test/builtins/Unit/floatdixf_test.c diff --git a/test/Unit/floatsidfvfp_test.c b/test/builtins/Unit/floatsidfvfp_test.c index e21ecda59945..e21ecda59945 100644 --- a/test/Unit/floatsidfvfp_test.c +++ b/test/builtins/Unit/floatsidfvfp_test.c diff --git a/test/Unit/floatsisfvfp_test.c b/test/builtins/Unit/floatsisfvfp_test.c index d20905bd91e3..d20905bd91e3 100644 --- a/test/Unit/floatsisfvfp_test.c +++ b/test/builtins/Unit/floatsisfvfp_test.c diff --git a/test/builtins/Unit/floatsitf_test.c b/test/builtins/Unit/floatsitf_test.c new file mode 100644 index 000000000000..db4d020af2a9 --- /dev/null +++ b/test/builtins/Unit/floatsitf_test.c @@ -0,0 +1,58 @@ +//===--------------- floatsitf_test.c - Test __floatsitf ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __floatsitf for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +long double __floatsitf(int a); + +int test__floatsitf(int a, uint64_t expectedHi, uint64_t expectedLo) +{ + long double x = __floatsitf(a); + int ret = compareResultLD(x, expectedHi, expectedLo); + + if (ret) + { + printf("error in test__floatsitf(%d) = %.20Lf, " + "expected %.20Lf\n", a, x, fromRep128(expectedHi, expectedLo)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + if (test__floatsitf(0x7fffffff, UINT64_C(0x401dfffffffc0000), UINT64_C(0x0))) + return 1; + if (test__floatsitf(0, UINT64_C(0x0), UINT64_C(0x0))) + return 1; + if (test__floatsitf(0xffffffff, UINT64_C(0xbfff000000000000), UINT64_C(0x0))) + return 1; + if (test__floatsitf(0x12345678, UINT64_C(0x401b234567800000), UINT64_C(0x0))) + return 1; + if (test__floatsitf(-0x12345678, UINT64_C(0xc01b234567800000), UINT64_C(0x0))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/floattidf_test.c b/test/builtins/Unit/floattidf_test.c index cfe40849960d..476304f96f10 100644 --- a/test/Unit/floattidf_test.c +++ b/test/builtins/Unit/floattidf_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <float.h> #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a double, rounding toward even. // Assumption: double is a IEEE 64 bit floating point type @@ -47,7 +47,7 @@ char assumption_3[sizeof(double)*CHAR_BIT == 64] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__floattidf(0, 0.0)) return 1; diff --git a/test/Unit/floattisf_test.c b/test/builtins/Unit/floattisf_test.c index a83ec63a25c9..75b8e917f76d 100644 --- a/test/Unit/floattisf_test.c +++ b/test/builtins/Unit/floattisf_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <float.h> #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a float, rounding toward even. // Assumption: float is a IEEE 32 bit floating point type @@ -47,7 +47,7 @@ char assumption_3[sizeof(float)*CHAR_BIT == 32] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__floattisf(0, 0.0F)) return 1; diff --git a/test/Unit/floattixf_test.c b/test/builtins/Unit/floattixf_test.c index 4a9dfb56ac93..ce3566867a47 100644 --- a/test/Unit/floattixf_test.c +++ b/test/builtins/Unit/floattixf_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <float.h> #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a long double, rounding toward even. // Assumption: long double is a IEEE 80 bit floating point type padded to 128 bits @@ -48,7 +48,7 @@ char assumption_3[sizeof(long double)*CHAR_BIT == 128] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__floattixf(0, 0.0)) return 1; diff --git a/test/Unit/floatundidf_test.c b/test/builtins/Unit/floatundidf_test.c index ae91ac374eb0..ae91ac374eb0 100644 --- a/test/Unit/floatundidf_test.c +++ b/test/builtins/Unit/floatundidf_test.c diff --git a/test/Unit/floatundisf_test.c b/test/builtins/Unit/floatundisf_test.c index 394c945a3919..394c945a3919 100644 --- a/test/Unit/floatundisf_test.c +++ b/test/builtins/Unit/floatundisf_test.c diff --git a/test/Unit/floatundixf_test.c b/test/builtins/Unit/floatundixf_test.c index 1974fa01012a..1974fa01012a 100644 --- a/test/Unit/floatundixf_test.c +++ b/test/builtins/Unit/floatundixf_test.c diff --git a/test/builtins/Unit/floatunsitf_test.c b/test/builtins/Unit/floatunsitf_test.c new file mode 100644 index 000000000000..1af72d246ab2 --- /dev/null +++ b/test/builtins/Unit/floatunsitf_test.c @@ -0,0 +1,55 @@ +//===--------------- floatunsitf_test.c - Test __floatunsitf --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __floatunsitf for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +long double __floatunsitf(unsigned int a); + +int test__floatunsitf(unsigned int a, uint64_t expectedHi, uint64_t expectedLo) +{ + long double x = __floatunsitf(a); + int ret = compareResultLD(x, expectedHi, expectedLo); + + if (ret){ + printf("error in test__floatunsitf(%u) = %.20Lf, " + "expected %.20Lf\n", a, x, fromRep128(expectedHi, expectedLo)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + if (test__floatunsitf(0x7fffffff, UINT64_C(0x401dfffffffc0000), UINT64_C(0x0))) + return 1; + if (test__floatunsitf(0, UINT64_C(0x0), UINT64_C(0x0))) + return 1; + if (test__floatunsitf(0xffffffff, UINT64_C(0x401efffffffe0000), UINT64_C(0x0))) + return 1; + if (test__floatunsitf(0x12345678, UINT64_C(0x401b234567800000), UINT64_C(0x0))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/floatunssidfvfp_test.c b/test/builtins/Unit/floatunssidfvfp_test.c index 4883af1cf5fa..4883af1cf5fa 100644 --- a/test/Unit/floatunssidfvfp_test.c +++ b/test/builtins/Unit/floatunssidfvfp_test.c diff --git a/test/Unit/floatunssisfvfp_test.c b/test/builtins/Unit/floatunssisfvfp_test.c index 917061a91b18..917061a91b18 100644 --- a/test/Unit/floatunssisfvfp_test.c +++ b/test/builtins/Unit/floatunssisfvfp_test.c diff --git a/test/Unit/floatuntidf_test.c b/test/builtins/Unit/floatuntidf_test.c index caad5d435e1a..3cab027051c7 100644 --- a/test/Unit/floatuntidf_test.c +++ b/test/builtins/Unit/floatuntidf_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <float.h> #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a double, rounding toward even. // Assumption: double is a IEEE 64 bit floating point type @@ -47,7 +47,7 @@ char assumption_3[sizeof(double)*CHAR_BIT == 64] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__floatuntidf(0, 0.0)) return 1; diff --git a/test/Unit/floatuntisf_test.c b/test/builtins/Unit/floatuntisf_test.c index 85c0727ce13a..aeac3ee41e54 100644 --- a/test/Unit/floatuntisf_test.c +++ b/test/builtins/Unit/floatuntisf_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <float.h> #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a float, rounding toward even. // Assumption: float is a IEEE 32 bit floating point type @@ -47,7 +47,7 @@ char assumption_3[sizeof(float)*CHAR_BIT == 32] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__floatuntisf(0, 0.0F)) return 1; diff --git a/test/Unit/floatuntixf_test.c b/test/builtins/Unit/floatuntixf_test.c index fc7531a9c823..9c3434fdde0c 100644 --- a/test/Unit/floatuntixf_test.c +++ b/test/builtins/Unit/floatuntixf_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <float.h> #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: convert a to a long double, rounding toward even. // Assumption: long double is a IEEE 80 bit floating point type padded to 128 bits @@ -48,7 +48,7 @@ char assumption_3[sizeof(long double)*CHAR_BIT == 128] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__floatuntixf(0, 0.0)) return 1; diff --git a/test/builtins/Unit/fp_test.h b/test/builtins/Unit/fp_test.h new file mode 100644 index 000000000000..da58ca989cda --- /dev/null +++ b/test/builtins/Unit/fp_test.h @@ -0,0 +1,223 @@ +//===--------------------------- fp_test.h - ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines shared functions for the test. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> +#include <limits.h> +#include <string.h> + +enum EXPECTED_RESULT { + LESS_0, LESS_EQUAL_0, EQUAL_0, GREATER_0, GREATER_EQUAL_0, NEQUAL_0 +}; + +static inline float fromRep32(uint32_t x) +{ + float ret; + memcpy(&ret, &x, 4); + return ret; +} + +static inline double fromRep64(uint64_t x) +{ + double ret; + memcpy(&ret, &x, 8); + return ret; +} + +static inline long double fromRep128(uint64_t hi, uint64_t lo) +{ + __uint128_t x = ((__uint128_t)hi << 64) + lo; + long double ret; + memcpy(&ret, &x, 16); + return ret; +} + +static inline uint32_t toRep32(float x) +{ + uint32_t ret; + memcpy(&ret, &x, 4); + return ret; +} + +static inline uint64_t toRep64(double x) +{ + uint64_t ret; + memcpy(&ret, &x, 8); + return ret; +} + +static inline __uint128_t toRep128(long double x) +{ + __uint128_t ret; + memcpy(&ret, &x, 16); + return ret; +} + +static inline int compareResultF(float result, + uint32_t expected) +{ + uint32_t rep = toRep32(result); + + if (rep == expected){ + return 0; + } + // test other posible NaN representation(signal NaN) + else if (expected == 0x7fc00000U){ + if ((rep & 0x7f800000U) == 0x7f800000U && + (rep & 0x7fffffU) > 0){ + return 0; + } + } + return 1; +} + +static inline int compareResultD(double result, + uint64_t expected) +{ + uint64_t rep = toRep64(result); + + if (rep == expected){ + return 0; + } + // test other posible NaN representation(signal NaN) + else if (expected == 0x7ff8000000000000UL){ + if ((rep & 0x7ff0000000000000UL) == 0x7ff0000000000000UL && + (rep & 0xfffffffffffffUL) > 0){ + return 0; + } + } + return 1; +} + +// return 0 if equal +// use two 64-bit integers intead of one 128-bit integer +// because 128-bit integer constant can't be assigned directly +static inline int compareResultLD(long double result, + uint64_t expectedHi, + uint64_t expectedLo) +{ + __uint128_t rep = toRep128(result); + uint64_t hi = rep >> 64; + uint64_t lo = rep; + + if (hi == expectedHi && lo == expectedLo){ + return 0; + } + // test other posible NaN representation(signal NaN) + else if (expectedHi == 0x7fff800000000000UL && expectedLo == 0x0UL){ + if ((hi & 0x7fff000000000000UL) == 0x7fff000000000000UL && + ((hi & 0xffffffffffffUL) > 0 || lo > 0)){ + return 0; + } + } + return 1; +} + +static inline int compareResultCMP(int result, + enum EXPECTED_RESULT expected) +{ + switch(expected){ + case LESS_0: + if (result < 0) + return 0; + break; + case LESS_EQUAL_0: + if (result <= 0) + return 0; + break; + case EQUAL_0: + if (result == 0) + return 0; + break; + case NEQUAL_0: + if (result != 0) + return 0; + break; + case GREATER_EQUAL_0: + if (result >= 0) + return 0; + break; + case GREATER_0: + if (result > 0) + return 0; + break; + default: + return 1; + } + return 1; +} + +static inline char *expectedStr(enum EXPECTED_RESULT expected) +{ + switch(expected){ + case LESS_0: + return "<0"; + case LESS_EQUAL_0: + return "<=0"; + case EQUAL_0: + return "=0"; + case NEQUAL_0: + return "!=0"; + case GREATER_EQUAL_0: + return ">=0"; + case GREATER_0: + return ">0"; + default: + return ""; + } + return ""; +} + +static inline float makeQNaN32() +{ + return fromRep32(0x7fc00000U); +} + +static inline double makeQNaN64() +{ + return fromRep64(0x7ff8000000000000UL); +} + +static inline long double makeQNaN128() +{ + return fromRep128(0x7fff800000000000UL, 0x0UL); +} + +static inline float makeNaN32(uint32_t rand) +{ + return fromRep32(0x7f800000U | (rand & 0x7fffffU)); +} + +static inline double makeNaN64(uint64_t rand) +{ + return fromRep64(0x7ff0000000000000UL | (rand & 0xfffffffffffffUL)); +} + +static inline long double makeNaN128(uint64_t rand) +{ + return fromRep128(0x7fff000000000000UL | (rand & 0xffffffffffffUL), 0x0UL); +} + +static inline float makeInf32() +{ + return fromRep32(0x7f800000U); +} + +static inline double makeInf64() +{ + return fromRep64(0x7ff0000000000000UL); +} + +static inline long double makeInf128() +{ + return fromRep128(0x7fff000000000000UL, 0x0UL); +} diff --git a/test/Unit/gcc_personality_test.c b/test/builtins/Unit/gcc_personality_test.c index f9598c697eb6..f9598c697eb6 100644 --- a/test/Unit/gcc_personality_test.c +++ b/test/builtins/Unit/gcc_personality_test.c diff --git a/test/Unit/gcc_personality_test_helper.cxx b/test/builtins/Unit/gcc_personality_test_helper.cxx index 7d1ddfb5d960..7d1ddfb5d960 100644 --- a/test/Unit/gcc_personality_test_helper.cxx +++ b/test/builtins/Unit/gcc_personality_test_helper.cxx diff --git a/test/Unit/gedf2vfp_test.c b/test/builtins/Unit/gedf2vfp_test.c index e280ce0780a5..e280ce0780a5 100644 --- a/test/Unit/gedf2vfp_test.c +++ b/test/builtins/Unit/gedf2vfp_test.c diff --git a/test/Unit/gesf2vfp_test.c b/test/builtins/Unit/gesf2vfp_test.c index aa53eb739979..aa53eb739979 100644 --- a/test/Unit/gesf2vfp_test.c +++ b/test/builtins/Unit/gesf2vfp_test.c diff --git a/test/builtins/Unit/getf2_test.c b/test/builtins/Unit/getf2_test.c new file mode 100644 index 000000000000..9796b8ab845d --- /dev/null +++ b/test/builtins/Unit/getf2_test.c @@ -0,0 +1,89 @@ +//===------------ getf2_test.c - Test __getf2------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __getf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +int __getf2(long double a, long double b); + +int test__getf2(long double a, long double b, enum EXPECTED_RESULT expected) +{ + int x = __getf2(a, b); + int ret = compareResultCMP(x, expected); + + if (ret){ + printf("error in test__getf2(%.20Lf, %.20Lf) = %d, " + "expected %s\n", a, b, x, expectedStr(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + // NaN + if (test__getf2(makeQNaN128(), + 0x1.234567890abcdef1234567890abcp+3L, + LESS_0)) + return 1; + // < + // exp + if (test__getf2(0x1.234567890abcdef1234567890abcp-3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_0)) + return 1; + // mantissa + if (test__getf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.334567890abcdef1234567890abcp+3L, + LESS_0)) + return 1; + // sign + if (test__getf2(-0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_0)) + return 1; + // == + if (test__getf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + GREATER_EQUAL_0)) + return 1; + // > + // exp + if (test__getf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp-3L, + GREATER_EQUAL_0)) + return 1; + // mantissa + if (test__getf2(0x1.334567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + GREATER_EQUAL_0)) + return 1; + // sign + if (test__getf2(0x1.234567890abcdef1234567890abcp+3L, + -0x1.234567890abcdef1234567890abcp+3L, + GREATER_EQUAL_0)) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/gtdf2vfp_test.c b/test/builtins/Unit/gtdf2vfp_test.c index fd54e0b4e59f..fd54e0b4e59f 100644 --- a/test/Unit/gtdf2vfp_test.c +++ b/test/builtins/Unit/gtdf2vfp_test.c diff --git a/test/Unit/gtsf2vfp_test.c b/test/builtins/Unit/gtsf2vfp_test.c index 2f4ad99a63d6..2f4ad99a63d6 100644 --- a/test/Unit/gtsf2vfp_test.c +++ b/test/builtins/Unit/gtsf2vfp_test.c diff --git a/test/builtins/Unit/gttf2_test.c b/test/builtins/Unit/gttf2_test.c new file mode 100644 index 000000000000..6508d4b978c9 --- /dev/null +++ b/test/builtins/Unit/gttf2_test.c @@ -0,0 +1,89 @@ +//===------------ gttf2_test.c - Test __gttf2------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __gttf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +int __gttf2(long double a, long double b); + +int test__gttf2(long double a, long double b, enum EXPECTED_RESULT expected) +{ + int x = __gttf2(a, b); + int ret = compareResultCMP(x, expected); + + if (ret){ + printf("error in test__gttf2(%.20Lf, %.20Lf) = %d, " + "expected %s\n", a, b, x, expectedStr(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + // NaN + if (test__gttf2(makeQNaN128(), + 0x1.234567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // < + // exp + if (test__gttf2(0x1.234567890abcdef1234567890abcp-3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // mantissa + if (test__gttf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.334567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // sign + if (test__gttf2(-0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // == + if (test__gttf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // > + // exp + if (test__gttf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp-3L, + GREATER_0)) + return 1; + // mantissa + if (test__gttf2(0x1.334567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + GREATER_0)) + return 1; + // sign + if (test__gttf2(0x1.234567890abcdef1234567890abcp+3L, + -0x1.234567890abcdef1234567890abcp+3L, + GREATER_0)) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/ledf2vfp_test.c b/test/builtins/Unit/ledf2vfp_test.c index 5683590778c0..5683590778c0 100644 --- a/test/Unit/ledf2vfp_test.c +++ b/test/builtins/Unit/ledf2vfp_test.c diff --git a/test/Unit/lesf2vfp_test.c b/test/builtins/Unit/lesf2vfp_test.c index b5c20f61e8ad..b5c20f61e8ad 100644 --- a/test/Unit/lesf2vfp_test.c +++ b/test/builtins/Unit/lesf2vfp_test.c diff --git a/test/builtins/Unit/letf2_test.c b/test/builtins/Unit/letf2_test.c new file mode 100644 index 000000000000..1842e3c55620 --- /dev/null +++ b/test/builtins/Unit/letf2_test.c @@ -0,0 +1,89 @@ +//===------------ letf2_test.c - Test __letf2------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __letf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +int __letf2(long double a, long double b); + +int test__letf2(long double a, long double b, enum EXPECTED_RESULT expected) +{ + int x = __letf2(a, b); + int ret = compareResultCMP(x, expected); + + if (ret){ + printf("error in test__letf2(%.20Lf, %.20Lf) = %d, " + "expected %s\n", a, b, x, expectedStr(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + // NaN + if (test__letf2(makeQNaN128(), + 0x1.234567890abcdef1234567890abcp+3L, + GREATER_0)) + return 1; + // < + // exp + if (test__letf2(0x1.234567890abcdef1234567890abcp-3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // mantissa + if (test__letf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.334567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // sign + if (test__letf2(-0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // == + if (test__letf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_EQUAL_0)) + return 1; + // > + // exp + if (test__letf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp-3L, + GREATER_0)) + return 1; + // mantissa + if (test__letf2(0x1.334567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + GREATER_0)) + return 1; + // sign + if (test__letf2(0x1.234567890abcdef1234567890abcp+3L, + -0x1.234567890abcdef1234567890abcp+3L, + GREATER_0)) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/lshrdi3_test.c b/test/builtins/Unit/lshrdi3_test.c index ffc6a69d0e9f..ffc6a69d0e9f 100644 --- a/test/Unit/lshrdi3_test.c +++ b/test/builtins/Unit/lshrdi3_test.c diff --git a/test/Unit/lshrti3_test.c b/test/builtins/Unit/lshrti3_test.c index f266b54f63c0..3f33c089cd62 100644 --- a/test/Unit/lshrti3_test.c +++ b/test/builtins/Unit/lshrti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: logical a >> b // Precondition: 0 <= b < bits_in_dword @@ -47,7 +47,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__lshrti3(make_ti(0xFEDCBA9876543215LL, 0xFEDCBA9876543215LL), 0, make_ti(0xFEDCBA9876543215LL, 0xFEDCBA9876543215LL))) return 1; diff --git a/test/Unit/ltdf2vfp_test.c b/test/builtins/Unit/ltdf2vfp_test.c index 7319397c5e4d..7319397c5e4d 100644 --- a/test/Unit/ltdf2vfp_test.c +++ b/test/builtins/Unit/ltdf2vfp_test.c diff --git a/test/Unit/ltsf2vfp_test.c b/test/builtins/Unit/ltsf2vfp_test.c index 2d920c959326..2d920c959326 100644 --- a/test/Unit/ltsf2vfp_test.c +++ b/test/builtins/Unit/ltsf2vfp_test.c diff --git a/test/builtins/Unit/lttf2_test.c b/test/builtins/Unit/lttf2_test.c new file mode 100644 index 000000000000..e8f9dc17c6f7 --- /dev/null +++ b/test/builtins/Unit/lttf2_test.c @@ -0,0 +1,89 @@ +//===------------ lttf2_test.c - Test __lttf2------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __lttf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +int __lttf2(long double a, long double b); + +int test__lttf2(long double a, long double b, enum EXPECTED_RESULT expected) +{ + int x = __lttf2(a, b); + int ret = compareResultCMP(x, expected); + + if (ret){ + printf("error in test__lttf2(%.20Lf, %.20Lf) = %d, " + "expected %s\n", a, b, x, expectedStr(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + // NaN + if (test__lttf2(makeQNaN128(), + 0x1.234567890abcdef1234567890abcp+3L, + GREATER_EQUAL_0)) + return 1; + // < + // exp + if (test__lttf2(0x1.234567890abcdef1234567890abcp-3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_0)) + return 1; + // mantissa + if (test__lttf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.334567890abcdef1234567890abcp+3L, + LESS_0)) + return 1; + // sign + if (test__lttf2(-0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + LESS_0)) + return 1; + // == + if (test__lttf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + GREATER_EQUAL_0)) + return 1; + // > + // exp + if (test__lttf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp-3L, + GREATER_EQUAL_0)) + return 1; + // mantissa + if (test__lttf2(0x1.334567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + GREATER_EQUAL_0)) + return 1; + // sign + if (test__lttf2(0x1.234567890abcdef1234567890abcp+3L, + -0x1.234567890abcdef1234567890abcp+3L, + GREATER_EQUAL_0)) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/moddi3_test.c b/test/builtins/Unit/moddi3_test.c index 9f6801d6f41b..9f6801d6f41b 100644 --- a/test/Unit/moddi3_test.c +++ b/test/builtins/Unit/moddi3_test.c diff --git a/test/Unit/modsi3_test.c b/test/builtins/Unit/modsi3_test.c index 52ec9a0ae36b..52ec9a0ae36b 100644 --- a/test/Unit/modsi3_test.c +++ b/test/builtins/Unit/modsi3_test.c diff --git a/test/Unit/modti3_test.c b/test/builtins/Unit/modti3_test.c index bd05c7237d2e..ba9f9804dfca 100644 --- a/test/Unit/modti3_test.c +++ b/test/builtins/Unit/modti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: a % b ti_int __modti3(ti_int a, ti_int b); @@ -47,7 +47,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__modti3(0, 1, 0)) return 1; if (test__modti3(0, -1, 0)) diff --git a/test/Unit/muldc3_test.c b/test/builtins/Unit/muldc3_test.c index 112b6120358a..112b6120358a 100644 --- a/test/Unit/muldc3_test.c +++ b/test/builtins/Unit/muldc3_test.c diff --git a/test/Unit/muldf3vfp_test.c b/test/builtins/Unit/muldf3vfp_test.c index 73454bf290aa..73454bf290aa 100644 --- a/test/Unit/muldf3vfp_test.c +++ b/test/builtins/Unit/muldf3vfp_test.c diff --git a/test/Unit/muldi3_test.c b/test/builtins/Unit/muldi3_test.c index 83b525592645..83b525592645 100644 --- a/test/Unit/muldi3_test.c +++ b/test/builtins/Unit/muldi3_test.c diff --git a/test/Unit/mulodi4_test.c b/test/builtins/Unit/mulodi4_test.c index 10a0eaac61f9..10a0eaac61f9 100644 --- a/test/Unit/mulodi4_test.c +++ b/test/builtins/Unit/mulodi4_test.c diff --git a/test/Unit/mulosi4_test.c b/test/builtins/Unit/mulosi4_test.c index fc509db2c178..fc509db2c178 100644 --- a/test/Unit/mulosi4_test.c +++ b/test/builtins/Unit/mulosi4_test.c diff --git a/test/Unit/muloti4_test.c b/test/builtins/Unit/muloti4_test.c index 44abddf26baf..95439a4197a3 100644 --- a/test/Unit/muloti4_test.c +++ b/test/builtins/Unit/muloti4_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: a * b // Effects: sets overflow if a * b overflows @@ -67,7 +67,7 @@ int test__muloti4(ti_int a, ti_int b, ti_int expected, int expected_overflow) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__muloti4(0, 0, 0, 0)) return 1; if (test__muloti4(0, 1, 0, 0)) diff --git a/test/Unit/mulsc3_test.c b/test/builtins/Unit/mulsc3_test.c index 7a1b3ae0092d..7a1b3ae0092d 100644 --- a/test/Unit/mulsc3_test.c +++ b/test/builtins/Unit/mulsc3_test.c diff --git a/test/Unit/mulsf3vfp_test.c b/test/builtins/Unit/mulsf3vfp_test.c index 92cf1f128497..92cf1f128497 100644 --- a/test/Unit/mulsf3vfp_test.c +++ b/test/builtins/Unit/mulsf3vfp_test.c diff --git a/test/Unit/multc3_test.c b/test/builtins/Unit/multc3_test.c index f8c66c0e426f..f8c66c0e426f 100644 --- a/test/Unit/multc3_test.c +++ b/test/builtins/Unit/multc3_test.c diff --git a/test/builtins/Unit/multf3_test.c b/test/builtins/Unit/multf3_test.c new file mode 100644 index 000000000000..89610056b27e --- /dev/null +++ b/test/builtins/Unit/multf3_test.c @@ -0,0 +1,95 @@ +//===--------------- multf3_test.c - Test __multf3 ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __multf3 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +// Returns: a * b +long double __multf3(long double a, long double b); + +int test__multf3(long double a, long double b, + uint64_t expectedHi, uint64_t expectedLo) +{ + long double x = __multf3(a, b); + int ret = compareResultLD(x, expectedHi, expectedLo); + + if (ret){ + printf("error in test__multf3(%.20Lf, %.20Lf) = %.20Lf, " + "expected %.20Lf\n", a, b, x, + fromRep128(expectedHi, expectedLo)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + // qNaN * any = qNaN + if (test__multf3(makeQNaN128(), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // NaN * any = NaN + if (test__multf3(makeNaN128(UINT64_C(0x800030000000)), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // inf * any = inf + if (test__multf3(makeInf128(), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff000000000000), + UINT64_C(0x0))) + return 1; + // any * any + if (test__multf3(0x1.2eab345678439abcdefea56782346p+5L, + 0x1.edcb34a235253948765432134674fp-1L, + UINT64_C(0x400423e7f9e3c9fc), + UINT64_C(0xd906c2c2a85777c4))) + return 1; + if (test__multf3(0x1.353e45674d89abacc3a2ebf3ff4ffp-50L, + 0x1.ed8764648369535adf4be3214567fp-9L, + UINT64_C(0x3fc52a163c6223fc), + UINT64_C(0xc94c4bf0430768b4))) + return 1; + if (test__multf3(0x1.234425696abcad34a35eeffefdcbap+456L, + 0x451.ed98d76e5d46e5f24323dff21ffp+600L, + UINT64_C(0x44293a91de5e0e94), + UINT64_C(0xe8ed17cc2cdf64ac))) + return 1; + if (test__multf3(0x1.4356473c82a9fabf2d22ace345defp-234L, + 0x1.eda98765476743ab21da23d45678fp-455L, + UINT64_C(0x3d4f37c1a3137cae), + UINT64_C(0xfc6807048bc2836a))) + return 1; + // underflow + if (test__multf3(0x1.23456734245345p-10000L, + 0x1.edcba524498724p-6383L, + UINT64_C(0x0), + UINT64_C(0x0))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/multi3_test.c b/test/builtins/Unit/multi3_test.c index f8c6605eb254..2ccbd06c3e7d 100644 --- a/test/Unit/multi3_test.c +++ b/test/builtins/Unit/multi3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + ti_int __multi3(ti_int a, ti_int b); int test__multi3(ti_int a, ti_int b, ti_int expected) @@ -45,7 +45,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__multi3(0, 0, 0)) return 1; if (test__multi3(0, 1, 0)) diff --git a/test/Unit/mulvdi3_test.c b/test/builtins/Unit/mulvdi3_test.c index a023bf611279..a023bf611279 100644 --- a/test/Unit/mulvdi3_test.c +++ b/test/builtins/Unit/mulvdi3_test.c diff --git a/test/Unit/mulvsi3_test.c b/test/builtins/Unit/mulvsi3_test.c index 1eb53a5d5daa..1eb53a5d5daa 100644 --- a/test/Unit/mulvsi3_test.c +++ b/test/builtins/Unit/mulvsi3_test.c diff --git a/test/Unit/mulvti3_test.c b/test/builtins/Unit/mulvti3_test.c index be1aa2d331c6..6336f4550e0d 100644 --- a/test/Unit/mulvti3_test.c +++ b/test/builtins/Unit/mulvti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: a * b // Effects: aborts if a * b overflows @@ -47,7 +47,7 @@ int test__mulvti3(ti_int a, ti_int b, ti_int expected) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__mulvti3(0, 0, 0)) return 1; if (test__mulvti3(0, 1, 0)) diff --git a/test/Unit/mulxc3_test.c b/test/builtins/Unit/mulxc3_test.c index 4297c162bd8e..4297c162bd8e 100644 --- a/test/Unit/mulxc3_test.c +++ b/test/builtins/Unit/mulxc3_test.c diff --git a/test/Unit/nedf2vfp_test.c b/test/builtins/Unit/nedf2vfp_test.c index 2c4404399633..2c4404399633 100644 --- a/test/Unit/nedf2vfp_test.c +++ b/test/builtins/Unit/nedf2vfp_test.c diff --git a/test/Unit/negdf2vfp_test.c b/test/builtins/Unit/negdf2vfp_test.c index dc55428d6780..dc55428d6780 100644 --- a/test/Unit/negdf2vfp_test.c +++ b/test/builtins/Unit/negdf2vfp_test.c diff --git a/test/Unit/negdi2_test.c b/test/builtins/Unit/negdi2_test.c index 510b3b046b56..510b3b046b56 100644 --- a/test/Unit/negdi2_test.c +++ b/test/builtins/Unit/negdi2_test.c diff --git a/test/Unit/negsf2vfp_test.c b/test/builtins/Unit/negsf2vfp_test.c index ef54cee1dbee..ef54cee1dbee 100644 --- a/test/Unit/negsf2vfp_test.c +++ b/test/builtins/Unit/negsf2vfp_test.c diff --git a/test/Unit/negti2_test.c b/test/builtins/Unit/negti2_test.c index 1945accfa952..a40c0c324bb2 100644 --- a/test/Unit/negti2_test.c +++ b/test/builtins/Unit/negti2_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: -a ti_int __negti2(ti_int a); @@ -44,7 +44,7 @@ char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__negti2(0, 0)) return 1; if (test__negti2(1, -1)) diff --git a/test/Unit/negvdi2_test.c b/test/builtins/Unit/negvdi2_test.c index 9617b95ff105..9617b95ff105 100644 --- a/test/Unit/negvdi2_test.c +++ b/test/builtins/Unit/negvdi2_test.c diff --git a/test/Unit/negvsi2_test.c b/test/builtins/Unit/negvsi2_test.c index 5ea0e2e86c2a..5ea0e2e86c2a 100644 --- a/test/Unit/negvsi2_test.c +++ b/test/builtins/Unit/negvsi2_test.c diff --git a/test/Unit/negvti2_test.c b/test/builtins/Unit/negvti2_test.c index 772840989ad6..8126cdb5b668 100644 --- a/test/Unit/negvti2_test.c +++ b/test/builtins/Unit/negvti2_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: -a // Effects: aborts if -a overflows @@ -46,7 +46,7 @@ int test__negvti2(ti_int a) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__negvti2(0)) return 1; if (test__negvti2(1)) diff --git a/test/Unit/nesf2vfp_test.c b/test/builtins/Unit/nesf2vfp_test.c index c085bf8b41fa..c085bf8b41fa 100644 --- a/test/Unit/nesf2vfp_test.c +++ b/test/builtins/Unit/nesf2vfp_test.c diff --git a/test/builtins/Unit/netf2_test.c b/test/builtins/Unit/netf2_test.c new file mode 100644 index 000000000000..bbd953ab59e2 --- /dev/null +++ b/test/builtins/Unit/netf2_test.c @@ -0,0 +1,89 @@ +//===------------ netf2_test.c - Test __netf2------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __netf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +int __netf2(long double a, long double b); + +int test__netf2(long double a, long double b, enum EXPECTED_RESULT expected) +{ + int x = __netf2(a, b); + int ret = compareResultCMP(x, expected); + + if (ret){ + printf("error in test__netf2(%.20Lf, %.20Lf) = %d, " + "expected %s\n", a, b, x, expectedStr(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + // NaN + if (test__netf2(makeQNaN128(), + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // < + // exp + if (test__netf2(0x1.234567890abcdef1234567890abcp-3L, + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // mantissa + if (test__netf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.334567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // sign + if (test__netf2(-0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // == + if (test__netf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + EQUAL_0)) + return 1; + // > + // exp + if (test__netf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp-3L, + NEQUAL_0)) + return 1; + // mantissa + if (test__netf2(0x1.334567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // sign + if (test__netf2(0x1.234567890abcdef1234567890abcp+3L, + -0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/paritydi2_test.c b/test/builtins/Unit/paritydi2_test.c index 5360e374d439..5360e374d439 100644 --- a/test/Unit/paritydi2_test.c +++ b/test/builtins/Unit/paritydi2_test.c diff --git a/test/Unit/paritysi2_test.c b/test/builtins/Unit/paritysi2_test.c index 3ea8473aad5c..3ea8473aad5c 100644 --- a/test/Unit/paritysi2_test.c +++ b/test/builtins/Unit/paritysi2_test.c diff --git a/test/Unit/parityti2_test.c b/test/builtins/Unit/parityti2_test.c index ac67b35669a7..8f065b953d22 100644 --- a/test/Unit/parityti2_test.c +++ b/test/builtins/Unit/parityti2_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> #include <stdlib.h> +#ifdef CRT_HAS_128BIT + // Returns: 1 if number of bits is odd else returns 0 si_int __parityti2(ti_int a); @@ -50,7 +50,7 @@ char assumption_2[sizeof(di_int)*CHAR_BIT == 64] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT int i; for (i = 0; i < 10000; ++i) if (test__parityti2(((ti_int)rand() << 96) + ((ti_int)rand() << 64) + diff --git a/test/Unit/popcountdi2_test.c b/test/builtins/Unit/popcountdi2_test.c index 4c5611796434..4c5611796434 100644 --- a/test/Unit/popcountdi2_test.c +++ b/test/builtins/Unit/popcountdi2_test.c diff --git a/test/Unit/popcountsi2_test.c b/test/builtins/Unit/popcountsi2_test.c index d0a05c45e544..d0a05c45e544 100644 --- a/test/Unit/popcountsi2_test.c +++ b/test/builtins/Unit/popcountsi2_test.c diff --git a/test/Unit/popcountti2_test.c b/test/builtins/Unit/popcountti2_test.c index 1b94a0c6aa51..d17e03afd665 100644 --- a/test/Unit/popcountti2_test.c +++ b/test/builtins/Unit/popcountti2_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> #include <stdlib.h> +#ifdef CRT_HAS_128BIT + // Returns: count of 1 bits si_int __popcountti2(ti_int a); @@ -50,7 +50,7 @@ char assumption_2[sizeof(di_int)*CHAR_BIT == 64] = {0}; int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__popcountti2(0)) return 1; if (test__popcountti2(1)) diff --git a/test/Unit/powidf2_test.c b/test/builtins/Unit/powidf2_test.c index 2abc84de7676..2abc84de7676 100644 --- a/test/Unit/powidf2_test.c +++ b/test/builtins/Unit/powidf2_test.c diff --git a/test/Unit/powisf2_test.c b/test/builtins/Unit/powisf2_test.c index 98409f432e66..98409f432e66 100644 --- a/test/Unit/powisf2_test.c +++ b/test/builtins/Unit/powisf2_test.c diff --git a/test/Unit/powitf2_test.c b/test/builtins/Unit/powitf2_test.c index 817cb1130dee..817cb1130dee 100644 --- a/test/Unit/powitf2_test.c +++ b/test/builtins/Unit/powitf2_test.c diff --git a/test/Unit/powixf2_test.c b/test/builtins/Unit/powixf2_test.c index 201870b37f15..201870b37f15 100644 --- a/test/Unit/powixf2_test.c +++ b/test/builtins/Unit/powixf2_test.c diff --git a/test/Unit/ppc/DD.h b/test/builtins/Unit/ppc/DD.h index fd62dae16b22..fd62dae16b22 100644 --- a/test/Unit/ppc/DD.h +++ b/test/builtins/Unit/ppc/DD.h diff --git a/test/Unit/ppc/fixtfdi_test.c b/test/builtins/Unit/ppc/fixtfdi_test.c index b4865fb8e6e9..b4865fb8e6e9 100644 --- a/test/Unit/ppc/fixtfdi_test.c +++ b/test/builtins/Unit/ppc/fixtfdi_test.c diff --git a/test/Unit/ppc/floatditf_test.c b/test/builtins/Unit/ppc/floatditf_test.c index 578260aeddc4..578260aeddc4 100644 --- a/test/Unit/ppc/floatditf_test.c +++ b/test/builtins/Unit/ppc/floatditf_test.c diff --git a/test/Unit/ppc/floatditf_test.h b/test/builtins/Unit/ppc/floatditf_test.h index 8b47e1731dcc..8b47e1731dcc 100644 --- a/test/Unit/ppc/floatditf_test.h +++ b/test/builtins/Unit/ppc/floatditf_test.h diff --git a/test/Unit/ppc/floatunditf_test.c b/test/builtins/Unit/ppc/floatunditf_test.c index 68390d197229..68390d197229 100644 --- a/test/Unit/ppc/floatunditf_test.c +++ b/test/builtins/Unit/ppc/floatunditf_test.c diff --git a/test/Unit/ppc/floatunditf_test.h b/test/builtins/Unit/ppc/floatunditf_test.h index 4a42e973b69d..4a42e973b69d 100644 --- a/test/Unit/ppc/floatunditf_test.h +++ b/test/builtins/Unit/ppc/floatunditf_test.h diff --git a/test/Unit/ppc/qadd_test.c b/test/builtins/Unit/ppc/qadd_test.c index 6d4ca93c2698..6d4ca93c2698 100644 --- a/test/Unit/ppc/qadd_test.c +++ b/test/builtins/Unit/ppc/qadd_test.c diff --git a/test/Unit/ppc/qdiv_test.c b/test/builtins/Unit/ppc/qdiv_test.c index 8e4e75a50ad6..8e4e75a50ad6 100644 --- a/test/Unit/ppc/qdiv_test.c +++ b/test/builtins/Unit/ppc/qdiv_test.c diff --git a/test/Unit/ppc/qmul_test.c b/test/builtins/Unit/ppc/qmul_test.c index fc5b46d3366a..fc5b46d3366a 100644 --- a/test/Unit/ppc/qmul_test.c +++ b/test/builtins/Unit/ppc/qmul_test.c diff --git a/test/Unit/ppc/qsub_test.c b/test/builtins/Unit/ppc/qsub_test.c index 43bc7f4a854a..43bc7f4a854a 100644 --- a/test/Unit/ppc/qsub_test.c +++ b/test/builtins/Unit/ppc/qsub_test.c diff --git a/test/Unit/ppc/test b/test/builtins/Unit/ppc/test index 96e06320dbe2..96e06320dbe2 100755 --- a/test/Unit/ppc/test +++ b/test/builtins/Unit/ppc/test diff --git a/test/Unit/subdf3vfp_test.c b/test/builtins/Unit/subdf3vfp_test.c index 86d6f2fef22b..86d6f2fef22b 100644 --- a/test/Unit/subdf3vfp_test.c +++ b/test/builtins/Unit/subdf3vfp_test.c diff --git a/test/Unit/subsf3vfp_test.c b/test/builtins/Unit/subsf3vfp_test.c index 223e7f8f3936..223e7f8f3936 100644 --- a/test/Unit/subsf3vfp_test.c +++ b/test/builtins/Unit/subsf3vfp_test.c diff --git a/test/builtins/Unit/subtf3_test.c b/test/builtins/Unit/subtf3_test.c new file mode 100644 index 000000000000..2ab249a994c3 --- /dev/null +++ b/test/builtins/Unit/subtf3_test.c @@ -0,0 +1,74 @@ +//===--------------- subtf3_test.c - Test __subtf3 ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __subtf3 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +// Returns: a - b +long double __subtf3(long double a, long double b); + +int test__subtf3(long double a, long double b, + uint64_t expectedHi, uint64_t expectedLo) +{ + long double x = __subtf3(a, b); + int ret = compareResultLD(x, expectedHi, expectedLo); + + if (ret){ + printf("error in test__subtf3(%.20Lf, %.20Lf) = %.20Lf, " + "expected %.20Lf\n", a, b, x, + fromRep128(expectedHi, expectedLo)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + // qNaN - any = qNaN + if (test__subtf3(makeQNaN128(), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // NaN - any = NaN + if (test__subtf3(makeNaN128(UINT64_C(0x800030000000)), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff800000000000), + UINT64_C(0x0))) + return 1; + // inf - any = inf + if (test__subtf3(makeInf128(), + 0x1.23456789abcdefp+5L, + UINT64_C(0x7fff000000000000), + UINT64_C(0x0))) + return 1; + // any - any + if (test__subtf3(0x1.234567829a3bcdef5678ade36734p+5L, + 0x1.ee9d7c52354a6936ab8d7654321fp-1L, + UINT64_C(0x40041b8af1915166), + UINT64_C(0xa44a7bca780a166c))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/subvdi3_test.c b/test/builtins/Unit/subvdi3_test.c index 0fb8f5156896..0fb8f5156896 100644 --- a/test/Unit/subvdi3_test.c +++ b/test/builtins/Unit/subvdi3_test.c diff --git a/test/Unit/subvsi3_test.c b/test/builtins/Unit/subvsi3_test.c index 14e6ce193904..14e6ce193904 100644 --- a/test/Unit/subvsi3_test.c +++ b/test/builtins/Unit/subvsi3_test.c diff --git a/test/Unit/subvti3_test.c b/test/builtins/Unit/subvti3_test.c index 6176bdfda97a..b2c225c3a7f4 100644 --- a/test/Unit/subvti3_test.c +++ b/test/builtins/Unit/subvti3_test.c @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> #include <stdlib.h> +#ifdef CRT_HAS_128BIT + // Returns: a - b // Effects: aborts if a - b overflows @@ -49,7 +49,7 @@ int test__subvti3(ti_int a, ti_int b) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT // test__subvti3(make_ti(0x8000000000000000LL, 0), 1); // should abort // test__subvti3(0, make_ti(0x8000000000000000LL, 0)); // should abort // test__subvti3(1, make_ti(0x8000000000000000LL, 0)); // should abort diff --git a/test/Unit/test b/test/builtins/Unit/test index e2a39a92ed2f..e2a39a92ed2f 100755 --- a/test/Unit/test +++ b/test/builtins/Unit/test diff --git a/test/Unit/trampoline_setup_test.c b/test/builtins/Unit/trampoline_setup_test.c index dc30fb6df60a..dc30fb6df60a 100644 --- a/test/Unit/trampoline_setup_test.c +++ b/test/builtins/Unit/trampoline_setup_test.c diff --git a/test/Unit/truncdfsf2vfp_test.c b/test/builtins/Unit/truncdfsf2vfp_test.c index afc7868b0de0..afc7868b0de0 100644 --- a/test/Unit/truncdfsf2vfp_test.c +++ b/test/builtins/Unit/truncdfsf2vfp_test.c diff --git a/test/builtins/Unit/trunctfdf2_test.c b/test/builtins/Unit/trunctfdf2_test.c new file mode 100644 index 000000000000..46855e31bb5e --- /dev/null +++ b/test/builtins/Unit/trunctfdf2_test.c @@ -0,0 +1,76 @@ +//===-------------- trunctfdf2_test.c - Test __trunctfdf2 -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __trunctfdf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +double __trunctfdf2(long double a); + +int test__trunctfdf2(long double a, uint64_t expected) +{ + double x = __trunctfdf2(a); + int ret = compareResultD(x, expected); + + if (ret) + { + printf("error in test__trunctfdf2(%.20Lf) = %lf, " + "expected %lf\n", a, x, fromRep64(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + // qNaN + if (test__trunctfdf2(makeQNaN128(), + UINT64_C(0x7ff8000000000000))) + return 1; + // NaN + if (test__trunctfdf2(makeNaN128(UINT64_C(0x810000000000)), + UINT64_C(0x7ff8100000000000))) + return 1; + // inf + if (test__trunctfdf2(makeInf128(), + UINT64_C(0x7ff0000000000000))) + return 1; + // zero + if (test__trunctfdf2(0.0L, UINT64_C(0x0))) + return 1; + + if (test__trunctfdf2(0x1.af23456789bbaaab347645365cdep+5L, + UINT64_C(0x404af23456789bbb))) + return 1; + if (test__trunctfdf2(0x1.dedafcff354b6ae9758763545432p-9L, + UINT64_C(0x3f6dedafcff354b7))) + return 1; + if (test__trunctfdf2(0x1.2f34dd5f437e849b4baab754cdefp+4534L, + UINT64_C(0x7ff0000000000000))) + return 1; + if (test__trunctfdf2(0x1.edcbff8ad76ab5bf46463233214fp-435L, + UINT64_C(0x24cedcbff8ad76ab))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/builtins/Unit/trunctfsf2_test.c b/test/builtins/Unit/trunctfsf2_test.c new file mode 100644 index 000000000000..44e7fd1827fe --- /dev/null +++ b/test/builtins/Unit/trunctfsf2_test.c @@ -0,0 +1,75 @@ +//===--------------- trunctfsf2_test.c - Test __trunctfsf2 ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __trunctfsf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +float __trunctfsf2(long double a); + +int test__trunctfsf2(long double a, uint32_t expected) +{ + float x = __trunctfsf2(a); + int ret = compareResultF(x, expected); + + if (ret){ + printf("error in test__trunctfsf2(%.20Lf) = %f, " + "expected %f\n", a, x, fromRep32(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + // qNaN + if (test__trunctfsf2(makeQNaN128(), + UINT32_C(0x7fc00000))) + return 1; + // NaN + if (test__trunctfsf2(makeNaN128(UINT64_C(0x810000000000)), + UINT32_C(0x7fc08000))) + return 1; + // inf + if (test__trunctfsf2(makeInf128(), + UINT32_C(0x7f800000))) + return 1; + // zero + if (test__trunctfsf2(0.0L, UINT32_C(0x0))) + return 1; + + if (test__trunctfsf2(0x1.23a2abb4a2ddee355f36789abcdep+5L, + UINT32_C(0x4211d156))) + return 1; + if (test__trunctfsf2(0x1.e3d3c45bd3abfd98b76a54cc321fp-9L, + UINT32_C(0x3b71e9e2))) + return 1; + if (test__trunctfsf2(0x1.234eebb5faa678f4488693abcdefp+4534L, + UINT32_C(0x7f800000))) + return 1; + if (test__trunctfsf2(0x1.edcba9bb8c76a5a43dd21f334634p-435L, + UINT32_C(0x0))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/Unit/ucmpdi2_test.c b/test/builtins/Unit/ucmpdi2_test.c index 22059077c795..22059077c795 100644 --- a/test/Unit/ucmpdi2_test.c +++ b/test/builtins/Unit/ucmpdi2_test.c diff --git a/test/Unit/ucmpti2_test.c b/test/builtins/Unit/ucmpti2_test.c index 55f820b89f2c..0713da8971af 100644 --- a/test/Unit/ucmpti2_test.c +++ b/test/builtins/Unit/ucmpti2_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: if (a < b) returns 0 // if (a == b) returns 1 // if (a > b) returns 2 @@ -42,7 +42,7 @@ int test__ucmpti2(tu_int a, tu_int b, si_int expected) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__ucmpti2(0, 0, 1)) return 1; if (test__ucmpti2(1, 1, 1)) diff --git a/test/Unit/udivdi3_test.c b/test/builtins/Unit/udivdi3_test.c index 24843f8bde6a..24843f8bde6a 100644 --- a/test/Unit/udivdi3_test.c +++ b/test/builtins/Unit/udivdi3_test.c diff --git a/test/Unit/udivmoddi4_test.c b/test/builtins/Unit/udivmoddi4_test.c index 43bf1a11f348..43bf1a11f348 100644 --- a/test/Unit/udivmoddi4_test.c +++ b/test/builtins/Unit/udivmoddi4_test.c diff --git a/test/Unit/udivmodsi4_test.c b/test/builtins/Unit/udivmodsi4_test.c index d734cd1fdf71..d734cd1fdf71 100644 --- a/test/Unit/udivmodsi4_test.c +++ b/test/builtins/Unit/udivmodsi4_test.c diff --git a/test/Unit/udivmodti4_test.c b/test/builtins/Unit/udivmodti4_test.c index e0cae35ea7ae..751aa868325f 100644 --- a/test/Unit/udivmodti4_test.c +++ b/test/builtins/Unit/udivmodti4_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Effects: if rem != 0, *rem = a % b // Returns: a / b @@ -65339,7 +65339,7 @@ tu_int tests[][4] = int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT const unsigned N = sizeof(tests) / sizeof(tests[0]); unsigned i; for (i = 0; i < N; ++i) diff --git a/test/Unit/udivsi3_test.c b/test/builtins/Unit/udivsi3_test.c index 325e4e6e8859..325e4e6e8859 100644 --- a/test/Unit/udivsi3_test.c +++ b/test/builtins/Unit/udivsi3_test.c diff --git a/test/Unit/udivti3_test.c b/test/builtins/Unit/udivti3_test.c index 09e0e1cf70d9..af5aad9a391d 100644 --- a/test/Unit/udivti3_test.c +++ b/test/builtins/Unit/udivti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: a / b tu_int __udivti3(tu_int a, tu_int b); @@ -45,7 +45,7 @@ int test__udivti3(tu_int a, tu_int b, tu_int expected_q) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__udivti3(0, 1, 0)) return 1; if (test__udivti3(2, 1, 2)) diff --git a/test/Unit/umoddi3_test.c b/test/builtins/Unit/umoddi3_test.c index a8f39b41550d..a8f39b41550d 100644 --- a/test/Unit/umoddi3_test.c +++ b/test/builtins/Unit/umoddi3_test.c diff --git a/test/Unit/umodsi3_test.c b/test/builtins/Unit/umodsi3_test.c index 66da695ffd96..66da695ffd96 100644 --- a/test/Unit/umodsi3_test.c +++ b/test/builtins/Unit/umodsi3_test.c diff --git a/test/Unit/umodti3_test.c b/test/builtins/Unit/umodti3_test.c index 5eac2d834fbf..93b556ccaa21 100644 --- a/test/Unit/umodti3_test.c +++ b/test/builtins/Unit/umodti3_test.c @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#if __x86_64 - #include "int_lib.h" #include <stdio.h> +#ifdef CRT_HAS_128BIT + // Returns: a % b tu_int __umodti3(tu_int a, tu_int b); @@ -45,7 +45,7 @@ int test__umodti3(tu_int a, tu_int b, tu_int expected_r) int main() { -#if __x86_64 +#ifdef CRT_HAS_128BIT if (test__umodti3(0, 1, 0)) return 1; if (test__umodti3(2, 1, 0)) diff --git a/test/Unit/unorddf2vfp_test.c b/test/builtins/Unit/unorddf2vfp_test.c index d49d567896ae..d49d567896ae 100644 --- a/test/Unit/unorddf2vfp_test.c +++ b/test/builtins/Unit/unorddf2vfp_test.c diff --git a/test/Unit/unordsf2vfp_test.c b/test/builtins/Unit/unordsf2vfp_test.c index 3cadc81c708b..3cadc81c708b 100644 --- a/test/Unit/unordsf2vfp_test.c +++ b/test/builtins/Unit/unordsf2vfp_test.c diff --git a/test/builtins/Unit/unordtf2_test.c b/test/builtins/Unit/unordtf2_test.c new file mode 100644 index 000000000000..114cd8cb38a7 --- /dev/null +++ b/test/builtins/Unit/unordtf2_test.c @@ -0,0 +1,65 @@ +//===------------ unordtf2_test.c - Test __unordtf2------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tests __unordtf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +int __unordtf2(long double a, long double b); + +int test__unordtf2(long double a, long double b, enum EXPECTED_RESULT expected) +{ + int x = __unordtf2(a, b); + int ret = compareResultCMP(x, expected); + + if (ret){ + printf("error in test__unordtf2(%.20Lf, %.20Lf) = %d, " + "expected %s\n", a, b, x, expectedStr(expected)); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LP64__ && __LDBL_MANT_DIG__ == 113 + // NaN + if (test__unordtf2(makeQNaN128(), + 0x1.234567890abcdef1234567890abcp+3L, + NEQUAL_0)) + return 1; + // other + if (test__unordtf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.334567890abcdef1234567890abcp+3L, + EQUAL_0)) + return 1; + if (test__unordtf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp+3L, + EQUAL_0)) + return 1; + if (test__unordtf2(0x1.234567890abcdef1234567890abcp+3L, + 0x1.234567890abcdef1234567890abcp-3L, + EQUAL_0)) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/timing/ashldi3.c b/test/builtins/timing/ashldi3.c index 0d7378d4167a..0d7378d4167a 100644 --- a/test/timing/ashldi3.c +++ b/test/builtins/timing/ashldi3.c diff --git a/test/timing/ashrdi3.c b/test/builtins/timing/ashrdi3.c index 84e11f7fe354..84e11f7fe354 100644 --- a/test/timing/ashrdi3.c +++ b/test/builtins/timing/ashrdi3.c diff --git a/test/timing/divdi3.c b/test/builtins/timing/divdi3.c index 37937aab624b..37937aab624b 100644 --- a/test/timing/divdi3.c +++ b/test/builtins/timing/divdi3.c diff --git a/test/timing/floatdidf.c b/test/builtins/timing/floatdidf.c index 2c970d4df4c9..2c970d4df4c9 100644 --- a/test/timing/floatdidf.c +++ b/test/builtins/timing/floatdidf.c diff --git a/test/timing/floatdisf.c b/test/builtins/timing/floatdisf.c index 801ab0aa2e60..801ab0aa2e60 100644 --- a/test/timing/floatdisf.c +++ b/test/builtins/timing/floatdisf.c diff --git a/test/timing/floatdixf.c b/test/builtins/timing/floatdixf.c index b646e3c1b746..b646e3c1b746 100644 --- a/test/timing/floatdixf.c +++ b/test/builtins/timing/floatdixf.c diff --git a/test/timing/floatundidf.c b/test/builtins/timing/floatundidf.c index 25c02bd2eab0..25c02bd2eab0 100644 --- a/test/timing/floatundidf.c +++ b/test/builtins/timing/floatundidf.c diff --git a/test/timing/floatundisf.c b/test/builtins/timing/floatundisf.c index ec8f3beb9c25..ec8f3beb9c25 100644 --- a/test/timing/floatundisf.c +++ b/test/builtins/timing/floatundisf.c diff --git a/test/timing/floatundixf.c b/test/builtins/timing/floatundixf.c index 46733eb2606b..46733eb2606b 100644 --- a/test/timing/floatundixf.c +++ b/test/builtins/timing/floatundixf.c diff --git a/test/timing/lshrdi3.c b/test/builtins/timing/lshrdi3.c index b474cf3e0c38..b474cf3e0c38 100644 --- a/test/timing/lshrdi3.c +++ b/test/builtins/timing/lshrdi3.c diff --git a/test/timing/moddi3.c b/test/builtins/timing/moddi3.c index 83e2441dc937..83e2441dc937 100644 --- a/test/timing/moddi3.c +++ b/test/builtins/timing/moddi3.c diff --git a/test/timing/modsi3.c b/test/builtins/timing/modsi3.c index 3275b8324474..3275b8324474 100644 --- a/test/timing/modsi3.c +++ b/test/builtins/timing/modsi3.c diff --git a/test/timing/muldi3.c b/test/builtins/timing/muldi3.c index 3ba3af245224..3ba3af245224 100644 --- a/test/timing/muldi3.c +++ b/test/builtins/timing/muldi3.c diff --git a/test/timing/negdi2.c b/test/builtins/timing/negdi2.c index 2668eb1e831f..2668eb1e831f 100644 --- a/test/timing/negdi2.c +++ b/test/builtins/timing/negdi2.c diff --git a/test/timing/time b/test/builtins/timing/time index 35da97dfc887..35da97dfc887 100755 --- a/test/timing/time +++ b/test/builtins/timing/time diff --git a/test/timing/timing.h b/test/builtins/timing/timing.h index bb6c41eb2a62..bb6c41eb2a62 100644 --- a/test/timing/timing.h +++ b/test/builtins/timing/timing.h diff --git a/test/timing/udivdi3.c b/test/builtins/timing/udivdi3.c index 9c6a7e8cca9f..9c6a7e8cca9f 100644 --- a/test/timing/udivdi3.c +++ b/test/builtins/timing/udivdi3.c diff --git a/test/timing/umoddi3.c b/test/builtins/timing/umoddi3.c index a81c46e52223..a81c46e52223 100644 --- a/test/timing/umoddi3.c +++ b/test/builtins/timing/umoddi3.c diff --git a/test/dfsan/CMakeLists.txt b/test/dfsan/CMakeLists.txt new file mode 100644 index 000000000000..3fa1af24be51 --- /dev/null +++ b/test/dfsan/CMakeLists.txt @@ -0,0 +1,15 @@ +set(DFSAN_LIT_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + +set(DFSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND DFSAN_TEST_DEPS dfsan) +endif() + +add_lit_testsuite(check-dfsan "Running the DataFlowSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${DFSAN_TEST_DEPS}) +set_target_properties(check-dfsan PROPERTIES FOLDER "DFSan tests") diff --git a/test/dfsan/Inputs/flags_abilist.txt b/test/dfsan/Inputs/flags_abilist.txt new file mode 100644 index 000000000000..94b1fa298259 --- /dev/null +++ b/test/dfsan/Inputs/flags_abilist.txt @@ -0,0 +1,10 @@ +fun:f=uninstrumented + +fun:main=uninstrumented +fun:main=discard + +fun:dfsan_create_label=uninstrumented +fun:dfsan_create_label=discard + +fun:dfsan_set_label=uninstrumented +fun:dfsan_set_label=discard diff --git a/test/dfsan/basic.c b/test/dfsan/basic.c new file mode 100644 index 000000000000..6582727e5e61 --- /dev/null +++ b/test/dfsan/basic.c @@ -0,0 +1,28 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %run %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t + +// Tests that labels are propagated through loads and stores. + +#include <sanitizer/dfsan_interface.h> +#include <assert.h> + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + dfsan_label new_label = dfsan_get_label(i); + assert(i_label == new_label); + + dfsan_label read_label = dfsan_read_label(&i, sizeof(i)); + assert(i_label == read_label); + + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_add_label(j_label, &i, sizeof(i)); + + read_label = dfsan_read_label(&i, sizeof(i)); + assert(dfsan_has_label(read_label, i_label)); + assert(dfsan_has_label(read_label, j_label)); + + return 0; +} diff --git a/test/dfsan/custom.cc b/test/dfsan/custom.cc new file mode 100644 index 000000000000..d7bb3e3073fb --- /dev/null +++ b/test/dfsan/custom.cc @@ -0,0 +1,958 @@ +// RUN: %clang_dfsan -m64 %s -o %t && DFSAN_OPTIONS="strict_data_dependencies=0" %run %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && DFSAN_OPTIONS="strict_data_dependencies=0" %run %t +// RUN: %clang_dfsan -DSTRICT_DATA_DEPENDENCIES -m64 %s -o %t && %run %t +// RUN: %clang_dfsan -DSTRICT_DATA_DEPENDENCIES -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t + +// Tests custom implementations of various glibc functions. + +#include <sanitizer/dfsan_interface.h> + +#include <arpa/inet.h> +#include <assert.h> +#include <fcntl.h> +#include <link.h> +#include <poll.h> +#include <pthread.h> +#include <pwd.h> +#include <sched.h> +#include <signal.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/select.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +dfsan_label i_label = 0; +dfsan_label j_label = 0; +dfsan_label k_label = 0; +dfsan_label i_j_label = 0; + +#define ASSERT_ZERO_LABEL(data) \ + assert(0 == dfsan_get_label((long) (data))) + +#define ASSERT_READ_ZERO_LABEL(ptr, size) \ + assert(0 == dfsan_read_label(ptr, size)) + +#define ASSERT_LABEL(data, label) \ + assert(label == dfsan_get_label((long) (data))) + +#define ASSERT_READ_LABEL(ptr, size, label) \ + assert(label == dfsan_read_label(ptr, size)) + +void test_stat() { + int i = 1; + dfsan_set_label(i_label, &i, sizeof(i)); + + struct stat s; + s.st_dev = i; + assert(0 == stat("/", &s)); + ASSERT_ZERO_LABEL(s.st_dev); + + s.st_dev = i; + assert(-1 == stat("/nonexistent", &s)); + ASSERT_LABEL(s.st_dev, i_label); +} + +void test_fstat() { + int i = 1; + dfsan_set_label(i_label, &i, sizeof(i)); + + struct stat s; + int fd = open("/dev/zero", O_RDONLY); + s.st_dev = i; + int rv = fstat(fd, &s); + assert(0 == rv); + ASSERT_ZERO_LABEL(s.st_dev); +} + +void test_memcmp() { + char str1[] = "str1", str2[] = "str2"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str2[3], 1); + + int rv = memcmp(str1, str2, sizeof(str1)); + assert(rv < 0); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, i_j_label); +#endif +} + +void test_memcpy() { + char str1[] = "str1"; + char str2[sizeof(str1)]; + dfsan_set_label(i_label, &str1[3], 1); + + ASSERT_ZERO_LABEL(memcpy(str2, str1, sizeof(str1))); + assert(0 == memcmp(str2, str1, sizeof(str1))); + ASSERT_ZERO_LABEL(str2[0]); + ASSERT_LABEL(str2[3], i_label); +} + +void test_memset() { + char buf[8]; + int j = 'a'; + dfsan_set_label(j_label, &j, sizeof(j)); + + ASSERT_ZERO_LABEL(memset(&buf, j, sizeof(buf))); + for (int i = 0; i < 8; ++i) { + ASSERT_LABEL(buf[i], j_label); + assert(buf[i] == 'a'); + } +} + +void test_strcmp() { + char str1[] = "str1", str2[] = "str2"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str2[3], 1); + + int rv = strcmp(str1, str2); + assert(rv < 0); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, i_j_label); +#endif +} + +void test_strlen() { + char str1[] = "str1"; + dfsan_set_label(i_label, &str1[3], 1); + + int rv = strlen(str1); + assert(rv == 4); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, i_label); +#endif +} + +void test_strdup() { + char str1[] = "str1"; + dfsan_set_label(i_label, &str1[3], 1); + + char *strd = strdup(str1); + ASSERT_ZERO_LABEL(strd[0]); + ASSERT_LABEL(strd[3], i_label); + free(strd); +} + +void test_strncpy() { + char str1[] = "str1"; + char str2[sizeof(str1)]; + dfsan_set_label(i_label, &str1[3], 1); + + char *strd = strncpy(str2, str1, 5); + assert(strd == str2); + assert(strcmp(str1, str2) == 0); + ASSERT_ZERO_LABEL(strd); + ASSERT_ZERO_LABEL(strd[0]); + ASSERT_ZERO_LABEL(strd[1]); + ASSERT_ZERO_LABEL(strd[2]); + ASSERT_LABEL(strd[3], i_label); + + strd = strncpy(str2, str1, 3); + assert(strd == str2); + assert(strncmp(str1, str2, 3) == 0); + ASSERT_ZERO_LABEL(strd); + ASSERT_ZERO_LABEL(strd[0]); + ASSERT_ZERO_LABEL(strd[1]); + ASSERT_ZERO_LABEL(strd[2]); +} + +void test_strncmp() { + char str1[] = "str1", str2[] = "str2"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str2[3], 1); + + int rv = strncmp(str1, str2, sizeof(str1)); + assert(rv < 0); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, dfsan_union(i_label, j_label)); +#endif + + rv = strncmp(str1, str2, 3); + assert(rv == 0); + ASSERT_ZERO_LABEL(rv); +} + +void test_strcasecmp() { + char str1[] = "str1", str2[] = "str2", str3[] = "Str1"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str2[3], 1); + dfsan_set_label(j_label, &str3[2], 1); + + int rv = strcasecmp(str1, str2); + assert(rv < 0); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, dfsan_union(i_label, j_label)); +#endif + + rv = strcasecmp(str1, str3); + assert(rv == 0); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, dfsan_union(i_label, j_label)); +#endif +} + +void test_strncasecmp() { + char str1[] = "Str1", str2[] = "str2"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str2[3], 1); + + int rv = strncasecmp(str1, str2, sizeof(str1)); + assert(rv < 0); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, dfsan_union(i_label, j_label)); +#endif + + rv = strncasecmp(str1, str2, 3); + assert(rv == 0); + ASSERT_ZERO_LABEL(rv); +} + +void test_strchr() { + char str1[] = "str1"; + dfsan_set_label(i_label, &str1[3], 1); + + char *crv = strchr(str1, 'r'); + assert(crv == &str1[2]); + ASSERT_ZERO_LABEL(crv); + + crv = strchr(str1, '1'); + assert(crv == &str1[3]); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(crv); +#else + ASSERT_LABEL(crv, i_label); +#endif + + crv = strchr(str1, 'x'); + assert(!crv); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(crv); +#else + ASSERT_LABEL(crv, i_label); +#endif +} + +void test_calloc() { + // With any luck this sequence of calls will cause calloc to return the same + // pointer both times. This is probably the best we can do to test this + // function. + char *crv = (char *) calloc(4096, 1); + ASSERT_ZERO_LABEL(crv[0]); + dfsan_set_label(i_label, crv, 100); + free(crv); + + crv = (char *) calloc(4096, 1); + ASSERT_ZERO_LABEL(crv[0]); + free(crv); +} + +void test_read() { + char buf[16]; + dfsan_set_label(i_label, buf, 1); + dfsan_set_label(j_label, buf + 15, 1); + + ASSERT_LABEL(buf[0], i_label); + ASSERT_LABEL(buf[15], j_label); + + int fd = open("/dev/zero", O_RDONLY); + int rv = read(fd, buf, sizeof(buf)); + assert(rv == sizeof(buf)); + ASSERT_ZERO_LABEL(rv); + ASSERT_ZERO_LABEL(buf[0]); + ASSERT_ZERO_LABEL(buf[15]); + close(fd); +} + +void test_pread() { + char buf[16]; + dfsan_set_label(i_label, buf, 1); + dfsan_set_label(j_label, buf + 15, 1); + + ASSERT_LABEL(buf[0], i_label); + ASSERT_LABEL(buf[15], j_label); + + int fd = open("/bin/sh", O_RDONLY); + int rv = pread(fd, buf, sizeof(buf), 0); + assert(rv == sizeof(buf)); + ASSERT_ZERO_LABEL(rv); + ASSERT_ZERO_LABEL(buf[0]); + ASSERT_ZERO_LABEL(buf[15]); + close(fd); +} + +void test_dlopen() { + void *map = dlopen(NULL, RTLD_NOW); + assert(map); + ASSERT_ZERO_LABEL(map); + dlclose(map); + map = dlopen("/nonexistent", RTLD_NOW); + assert(!map); + ASSERT_ZERO_LABEL(map); +} + +void test_clock_gettime() { + struct timespec tp; + dfsan_set_label(j_label, ((char *)&tp) + 3, 1); + int t = clock_gettime(CLOCK_REALTIME, &tp); + assert(t == 0); + ASSERT_ZERO_LABEL(t); + ASSERT_ZERO_LABEL(((char *)&tp)[3]); +} + +void test_ctime_r() { + char *buf = (char*) malloc(64); + time_t t = 0; + + char *ret = ctime_r(&t, buf); + ASSERT_ZERO_LABEL(ret); + assert(buf == ret); + ASSERT_READ_ZERO_LABEL(buf, strlen(buf) + 1); + + dfsan_set_label(i_label, &t, sizeof(t)); + ret = ctime_r(&t, buf); + ASSERT_ZERO_LABEL(ret); + ASSERT_READ_LABEL(buf, strlen(buf) + 1, i_label); + + t = 0; + dfsan_set_label(j_label, &buf, sizeof(&buf)); + ret = ctime_r(&t, buf); + ASSERT_LABEL(ret, j_label); + ASSERT_READ_ZERO_LABEL(buf, strlen(buf) + 1); +} + +static int write_callback_count = 0; +static int last_fd; +static const unsigned char *last_buf; +static size_t last_count; + +void write_callback(int fd, const void *buf, size_t count) { + write_callback_count++; + + last_fd = fd; + last_buf = (const unsigned char*) buf; + last_count = count; +} + +void test_dfsan_set_write_callback() { + char buf[] = "Sample chars"; + int buf_len = strlen(buf); + + int fd = open("/dev/null", O_WRONLY); + + dfsan_set_write_callback(write_callback); + + write_callback_count = 0; + + // Callback should be invoked on every call to write(). + int res = write(fd, buf, buf_len); + assert(write_callback_count == 1); + ASSERT_READ_ZERO_LABEL(&res, sizeof(res)); + ASSERT_READ_ZERO_LABEL(&last_fd, sizeof(last_fd)); + ASSERT_READ_ZERO_LABEL(last_buf, sizeof(last_buf)); + ASSERT_READ_ZERO_LABEL(&last_count, sizeof(last_count)); + + // Add a label to write() arguments. Check that the labels are readable from + // the values passed to the callback. + dfsan_set_label(i_label, &fd, sizeof(fd)); + dfsan_set_label(j_label, &(buf[3]), 1); + dfsan_set_label(k_label, &buf_len, sizeof(buf_len)); + + res = write(fd, buf, buf_len); + assert(write_callback_count == 2); + ASSERT_READ_ZERO_LABEL(&res, sizeof(res)); + ASSERT_READ_LABEL(&last_fd, sizeof(last_fd), i_label); + ASSERT_READ_LABEL(&last_buf[3], sizeof(last_buf[3]), j_label); + ASSERT_READ_LABEL(last_buf, sizeof(last_buf), j_label); + ASSERT_READ_LABEL(&last_count, sizeof(last_count), k_label); + + dfsan_set_write_callback(NULL); +} + +void test_fgets() { + char *buf = (char*) malloc(128); + FILE *f = fopen("/etc/passwd", "r"); + dfsan_set_label(j_label, buf, 1); + char *ret = fgets(buf, sizeof(buf), f); + assert(ret == buf); + ASSERT_ZERO_LABEL(ret); + ASSERT_READ_ZERO_LABEL(buf, 128); + dfsan_set_label(j_label, &buf, sizeof(&buf)); + ret = fgets(buf, sizeof(buf), f); + ASSERT_LABEL(ret, j_label); + fclose(f); +} + +void test_getcwd() { + char buf[1024]; + char *ptr = buf; + dfsan_set_label(i_label, buf + 2, 2); + char* ret = getcwd(buf, sizeof(buf)); + assert(ret == buf); + assert(ret[0] == '/'); + ASSERT_READ_ZERO_LABEL(buf + 2, 2); + dfsan_set_label(i_label, &ptr, sizeof(ptr)); + ret = getcwd(ptr, sizeof(buf)); + ASSERT_LABEL(ret, i_label); +} + +void test_get_current_dir_name() { + char* ret = get_current_dir_name(); + assert(ret); + assert(ret[0] == '/'); + ASSERT_READ_ZERO_LABEL(ret, strlen(ret) + 1); +} + +void test_gethostname() { + char buf[1024]; + dfsan_set_label(i_label, buf + 2, 2); + assert(gethostname(buf, sizeof(buf)) == 0); + ASSERT_READ_ZERO_LABEL(buf + 2, 2); +} + +void test_getrlimit() { + struct rlimit rlim; + dfsan_set_label(i_label, &rlim, sizeof(rlim)); + assert(getrlimit(RLIMIT_CPU, &rlim) == 0); + ASSERT_READ_ZERO_LABEL(&rlim, sizeof(rlim)); +} + +void test_getrusage() { + struct rusage usage; + dfsan_set_label(i_label, &usage, sizeof(usage)); + assert(getrusage(RUSAGE_SELF, &usage) == 0); + ASSERT_READ_ZERO_LABEL(&usage, sizeof(usage)); +} + +void test_strcpy() { + char src[] = "hello world"; + char dst[sizeof(src) + 2]; + dfsan_set_label(0, src, sizeof(src)); + dfsan_set_label(0, dst, sizeof(dst)); + dfsan_set_label(i_label, src + 2, 1); + dfsan_set_label(j_label, src + 3, 1); + dfsan_set_label(j_label, dst + 4, 1); + dfsan_set_label(i_label, dst + 12, 1); + char *ret = strcpy(dst, src); + assert(ret == dst); + assert(strcmp(src, dst) == 0); + for (int i = 0; i < strlen(src) + 1; ++i) { + assert(dfsan_get_label(dst[i]) == dfsan_get_label(src[i])); + } + // Note: if strlen(src) + 1 were used instead to compute the first untouched + // byte of dest, the label would be I|J. This is because strlen() might + // return a non-zero label, and because by default pointer labels are not + // ignored on loads. + ASSERT_LABEL(dst[12], i_label); +} + +void test_strtol() { + char buf[] = "1234578910"; + char *endptr = NULL; + dfsan_set_label(i_label, buf + 1, 1); + dfsan_set_label(j_label, buf + 10, 1); + long int ret = strtol(buf, &endptr, 10); + assert(ret == 1234578910); + assert(endptr == buf + 10); + ASSERT_LABEL(ret, i_j_label); +} + +void test_strtoll() { + char buf[] = "1234578910 "; + char *endptr = NULL; + dfsan_set_label(i_label, buf + 1, 1); + dfsan_set_label(j_label, buf + 2, 1); + long long int ret = strtoll(buf, &endptr, 10); + assert(ret == 1234578910); + assert(endptr == buf + 10); + ASSERT_LABEL(ret, i_j_label); +} + +void test_strtoul() { + char buf[] = "0xffffffffffffaa"; + char *endptr = NULL; + dfsan_set_label(i_label, buf + 1, 1); + dfsan_set_label(j_label, buf + 2, 1); + long unsigned int ret = strtol(buf, &endptr, 16); + assert(ret == 72057594037927850); + assert(endptr == buf + 16); + ASSERT_LABEL(ret, i_j_label); +} + +void test_strtoull() { + char buf[] = "0xffffffffffffffaa"; + char *endptr = NULL; + dfsan_set_label(i_label, buf + 1, 1); + dfsan_set_label(j_label, buf + 2, 1); + long long unsigned int ret = strtoull(buf, &endptr, 16); + assert(ret == 0xffffffffffffffaa); + assert(endptr == buf + 18); + ASSERT_LABEL(ret, i_j_label); +} + +void test_strtod() { + char buf[] = "12345.76 foo"; + char *endptr = NULL; + dfsan_set_label(i_label, buf + 1, 1); + dfsan_set_label(j_label, buf + 6, 1); + double ret = strtod(buf, &endptr); + assert(ret == 12345.76); + assert(endptr == buf + 8); + ASSERT_LABEL(ret, i_j_label); +} + +void test_time() { + time_t t = 0; + dfsan_set_label(i_label, &t, 1); + time_t ret = time(&t); + assert(ret == t); + assert(ret > 0); + ASSERT_ZERO_LABEL(t); +} + +void test_inet_pton() { + char addr4[] = "127.0.0.1"; + dfsan_set_label(i_label, addr4 + 3, 1); + struct in_addr in4; + int ret4 = inet_pton(AF_INET, addr4, &in4); + assert(ret4 == 1); + ASSERT_READ_LABEL(&in4, sizeof(in4), i_label); + assert(in4.s_addr == 0x0100007f); + + char addr6[] = "::1"; + dfsan_set_label(j_label, addr6 + 3, 1); + struct in6_addr in6; + int ret6 = inet_pton(AF_INET6, addr6, &in6); + assert(ret6 == 1); + ASSERT_READ_LABEL(((char *) &in6) + sizeof(in6) - 1, 1, j_label); +} + +void test_localtime_r() { + time_t t0 = 1384800998; + struct tm t1; + dfsan_set_label(i_label, &t0, sizeof(t0)); + struct tm* ret = localtime_r(&t0, &t1); + assert(ret == &t1); + assert(t1.tm_min == 56); + ASSERT_LABEL(t1.tm_mon, i_label); +} + +void test_getpwuid_r() { + struct passwd pwd; + char buf[1024]; + struct passwd *result; + + dfsan_set_label(i_label, &pwd, 4); + int ret = getpwuid_r(0, &pwd, buf, sizeof(buf), &result); + assert(ret == 0); + assert(strcmp(pwd.pw_name, "root") == 0); + assert(result == &pwd); + ASSERT_READ_ZERO_LABEL(&pwd, 4); +} + +void test_poll() { + struct pollfd fd; + fd.fd = 0; + fd.events = POLLIN; + dfsan_set_label(i_label, &fd.revents, sizeof(fd.revents)); + int ret = poll(&fd, 1, 1); + ASSERT_ZERO_LABEL(fd.revents); + assert(ret >= 0); +} + +void test_select() { + struct timeval t; + fd_set fds; + t.tv_sec = 2; + FD_SET(0, &fds); + dfsan_set_label(i_label, &fds, sizeof(fds)); + dfsan_set_label(j_label, &t, sizeof(t)); + int ret = select(1, &fds, NULL, NULL, &t); + assert(ret >= 0); + ASSERT_ZERO_LABEL(t.tv_sec); + ASSERT_READ_ZERO_LABEL(&fds, sizeof(fds)); +} + +void test_sched_getaffinity() { + cpu_set_t mask; + dfsan_set_label(j_label, &mask, 1); + int ret = sched_getaffinity(0, sizeof(mask), &mask); + assert(ret == 0); + ASSERT_READ_ZERO_LABEL(&mask, sizeof(mask)); +} + +void test_sigemptyset() { + sigset_t set; + dfsan_set_label(j_label, &set, 1); + int ret = sigemptyset(&set); + assert(ret == 0); + ASSERT_READ_ZERO_LABEL(&set, sizeof(set)); +} + +void test_sigaction() { + struct sigaction oldact; + dfsan_set_label(j_label, &oldact, 1); + int ret = sigaction(SIGUSR1, NULL, &oldact); + assert(ret == 0); + ASSERT_READ_ZERO_LABEL(&oldact, sizeof(oldact)); +} + +void test_gettimeofday() { + struct timeval tv; + struct timezone tz; + dfsan_set_label(i_label, &tv, sizeof(tv)); + dfsan_set_label(j_label, &tz, sizeof(tz)); + int ret = gettimeofday(&tv, &tz); + assert(ret == 0); + ASSERT_READ_ZERO_LABEL(&tv, sizeof(tv)); + ASSERT_READ_ZERO_LABEL(&tz, sizeof(tz)); +} + +void *pthread_create_test_cb(void *p) { + assert(p == (void *)1); + ASSERT_ZERO_LABEL(p); + return (void *)2; +} + +void test_pthread_create() { + pthread_t pt; + pthread_create(&pt, 0, pthread_create_test_cb, (void *)1); + void *cbrv; + pthread_join(pt, &cbrv); + assert(cbrv == (void *)2); +} + +int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size, + void *data) { + assert(data == (void *)3); + ASSERT_ZERO_LABEL(info); + ASSERT_ZERO_LABEL(size); + ASSERT_ZERO_LABEL(data); + return 0; +} + +void test_dl_iterate_phdr() { + dl_iterate_phdr(dl_iterate_phdr_test_cb, (void *)3); +} + +void test_strrchr() { + char str1[] = "str1str1"; + dfsan_set_label(i_label, &str1[7], 1); + + char *rv = strrchr(str1, 'r'); + assert(rv == &str1[6]); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, i_label); +#endif +} + +void test_strstr() { + char str1[] = "str1str1"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str1[5], 1); + + char *rv = strstr(str1, "1s"); + assert(rv == &str1[3]); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, i_label); +#endif + + rv = strstr(str1, "2s"); + assert(rv == NULL); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, i_j_label); +#endif +} + +void test_memchr() { + char str1[] = "str1"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str1[4], 1); + + char *crv = (char *) memchr(str1, 'r', sizeof(str1)); + assert(crv == &str1[2]); + ASSERT_ZERO_LABEL(crv); + + crv = (char *) memchr(str1, '1', sizeof(str1)); + assert(crv == &str1[3]); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(crv); +#else + ASSERT_LABEL(crv, i_label); +#endif + + crv = (char *) memchr(str1, 'x', sizeof(str1)); + assert(!crv); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(crv); +#else + ASSERT_LABEL(crv, i_j_label); +#endif +} + +void alarm_handler(int unused) { + ; +} + +void test_nanosleep() { + struct timespec req, rem; + req.tv_sec = 1; + req.tv_nsec = 0; + dfsan_set_label(i_label, &rem, sizeof(rem)); + + // non interrupted + int rv = nanosleep(&req, &rem); + assert(rv == 0); + ASSERT_ZERO_LABEL(rv); + ASSERT_READ_LABEL(&rem, 1, i_label); + + // interrupted by an alarm + signal(SIGALRM, alarm_handler); + req.tv_sec = 3; + alarm(1); + rv = nanosleep(&req, &rem); + assert(rv == -1); + ASSERT_ZERO_LABEL(rv); + ASSERT_READ_ZERO_LABEL(&rem, sizeof(rem)); +} + +void test_socketpair() { + int fd[2]; + + dfsan_set_label(i_label, fd, sizeof(fd)); + int rv = socketpair(PF_LOCAL, SOCK_STREAM, 0, fd); + assert(rv == 0); + ASSERT_ZERO_LABEL(rv); + ASSERT_READ_ZERO_LABEL(fd, sizeof(fd)); +} + +void test_write() { + int fd = open("/dev/null", O_WRONLY); + + char buf[] = "a string"; + int len = strlen(buf); + + // The result of a write always unlabeled. + int res = write(fd, buf, len); + assert(res > 0); + ASSERT_ZERO_LABEL(res); + + // Label all arguments to write(). + dfsan_set_label(i_label, &(buf[3]), 1); + dfsan_set_label(j_label, &fd, sizeof(fd)); + dfsan_set_label(i_label, &len, sizeof(len)); + + // The value returned by write() should have no label. + res = write(fd, buf, len); + ASSERT_ZERO_LABEL(res); + + close(fd); +} + +template <class T> +void test_sprintf_chunk(const char* expected, const char* format, T arg) { + char buf[512]; + memset(buf, 'a', sizeof(buf)); + + char padded_expected[512]; + strcpy(padded_expected, "foo "); + strcat(padded_expected, expected); + strcat(padded_expected, " bar"); + + char padded_format[512]; + strcpy(padded_format, "foo "); + strcat(padded_format, format); + strcat(padded_format, " bar"); + + // Non labelled arg. + assert(sprintf(buf, padded_format, arg) == strlen(padded_expected)); + assert(strcmp(buf, padded_expected) == 0); + ASSERT_READ_LABEL(buf, strlen(padded_expected), 0); + memset(buf, 'a', sizeof(buf)); + + // Labelled arg. + dfsan_set_label(i_label, &arg, sizeof(arg)); + assert(sprintf(buf, padded_format, arg) == strlen(padded_expected)); + assert(strcmp(buf, padded_expected) == 0); + ASSERT_READ_LABEL(buf, 4, 0); + ASSERT_READ_LABEL(buf + 4, strlen(padded_expected) - 8, i_label); + ASSERT_READ_LABEL(buf + (strlen(padded_expected) - 4), 4, 0); +} + +void test_sprintf() { + char buf[2048]; + memset(buf, 'a', sizeof(buf)); + + // Test formatting (no conversion specifier). + assert(sprintf(buf, "Hello world!") == 12); + assert(strcmp(buf, "Hello world!") == 0); + ASSERT_READ_LABEL(buf, sizeof(buf), 0); + + // Test for extra arguments. + assert(sprintf(buf, "Hello world!", 42, "hello") == 12); + assert(strcmp(buf, "Hello world!") == 0); + ASSERT_READ_LABEL(buf, sizeof(buf), 0); + + // Test formatting & label propagation (multiple conversion specifiers): %s, + // %d, %n, %f, and %%. + const char* s = "world"; + int m = 8; + int d = 27; + dfsan_set_label(k_label, (void *) (s + 1), 2); + dfsan_set_label(i_label, &m, sizeof(m)); + dfsan_set_label(j_label, &d, sizeof(d)); + int n; + int r = sprintf(buf, "hello %s, %-d/%d/%d %f %% %n%d", s, 2014, m, d, + 12345.6781234, &n, 1000); + assert(r == 42); + assert(strcmp(buf, "hello world, 2014/8/27 12345.678123 % 1000") == 0); + ASSERT_READ_LABEL(buf, 7, 0); + ASSERT_READ_LABEL(buf + 7, 2, k_label); + ASSERT_READ_LABEL(buf + 9, 9, 0); + ASSERT_READ_LABEL(buf + 18, 1, i_label); + ASSERT_READ_LABEL(buf + 19, 1, 0); + ASSERT_READ_LABEL(buf + 20, 2, j_label); + ASSERT_READ_LABEL(buf + 22, 15, 0); + ASSERT_LABEL(r, 0); + assert(n == 38); + + // Test formatting & label propagation (single conversion specifier, with + // additional length and precision modifiers). + test_sprintf_chunk("-559038737", "%d", 0xdeadbeef); + test_sprintf_chunk("3735928559", "%u", 0xdeadbeef); + test_sprintf_chunk("12345", "%i", 12345); + test_sprintf_chunk("751", "%o", 0751); + test_sprintf_chunk("babe", "%x", 0xbabe); + test_sprintf_chunk("0000BABE", "%.8X", 0xbabe); + test_sprintf_chunk("-17", "%hhd", 0xdeadbeef); + test_sprintf_chunk("-16657", "%hd", 0xdeadbeef); + test_sprintf_chunk("deadbeefdeadbeef", "%lx", 0xdeadbeefdeadbeef); + test_sprintf_chunk("0xdeadbeefdeadbeef", "%p", + (void *) 0xdeadbeefdeadbeef); + test_sprintf_chunk("18446744073709551615", "%ju", (intmax_t) -1); + test_sprintf_chunk("18446744073709551615", "%zu", (size_t) -1); + test_sprintf_chunk("18446744073709551615", "%tu", (size_t) -1); + + test_sprintf_chunk("0x1.f9acffa7eb6bfp-4", "%a", 0.123456); + test_sprintf_chunk("0X1.F9ACFFA7EB6BFP-4", "%A", 0.123456); + test_sprintf_chunk("0.12346", "%.5f", 0.123456); + test_sprintf_chunk("0.123456", "%g", 0.123456); + test_sprintf_chunk("1.234560e-01", "%e", 0.123456); + test_sprintf_chunk("1.234560E-01", "%E", 0.123456); + test_sprintf_chunk("0.1234567891234560", "%.16Lf", + (long double) 0.123456789123456); + + test_sprintf_chunk("z", "%c", 'z'); + + // %n, %s, %d, %f, and %% already tested +} + +void test_snprintf() { + char buf[2048]; + memset(buf, 'a', sizeof(buf)); + dfsan_set_label(0, buf, sizeof(buf)); + const char* s = "world"; + int y = 2014; + int m = 8; + int d = 27; + dfsan_set_label(k_label, (void *) (s + 1), 2); + dfsan_set_label(i_label, &y, sizeof(y)); + dfsan_set_label(j_label, &m, sizeof(m)); + int r = snprintf(buf, 19, "hello %s, %-d/%d/%d %f", s, y, m, d, + 12345.6781234); + // The return value is the number of bytes that would have been written to + // the final string if enough space had been available. + assert(r == 35); + assert(memcmp(buf, "hello world, 2014/", 19) == 0); + ASSERT_READ_LABEL(buf, 7, 0); + ASSERT_READ_LABEL(buf + 7, 2, k_label); + ASSERT_READ_LABEL(buf + 9, 4, 0); + ASSERT_READ_LABEL(buf + 13, 4, i_label); + ASSERT_READ_LABEL(buf + 17, 2, 0); + ASSERT_LABEL(r, 0); +} + +int main(void) { + i_label = dfsan_create_label("i", 0); + j_label = dfsan_create_label("j", 0); + k_label = dfsan_create_label("k", 0); + i_j_label = dfsan_union(i_label, j_label); + + test_calloc(); + test_clock_gettime(); + test_ctime_r(); + test_dfsan_set_write_callback(); + test_dl_iterate_phdr(); + test_dlopen(); + test_fgets(); + test_fstat(); + test_get_current_dir_name(); + test_getcwd(); + test_gethostname(); + test_getpwuid_r(); + test_getrlimit(); + test_getrusage(); + test_gettimeofday(); + test_inet_pton(); + test_localtime_r(); + test_memchr(); + test_memcmp(); + test_memcpy(); + test_memset(); + test_nanosleep(); + test_poll(); + test_pread(); + test_pthread_create(); + test_read(); + test_sched_getaffinity(); + test_select(); + test_sigaction(); + test_sigemptyset(); + test_snprintf(); + test_socketpair(); + test_sprintf(); + test_stat(); + test_strcasecmp(); + test_strchr(); + test_strcmp(); + test_strcpy(); + test_strdup(); + test_strlen(); + test_strncasecmp(); + test_strncmp(); + test_strncpy(); + test_strrchr(); + test_strstr(); + test_strtod(); + test_strtol(); + test_strtoll(); + test_strtoul(); + test_strtoull(); + test_time(); + test_write(); +} diff --git a/test/dfsan/dump_labels.c b/test/dfsan/dump_labels.c new file mode 100644 index 000000000000..67801af1838f --- /dev/null +++ b/test/dfsan/dump_labels.c @@ -0,0 +1,69 @@ +// RUN: %clang_dfsan -m64 %s -o %t +// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout %run %t 2>&1 | FileCheck %s +// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout not %run %t c 2>&1 | FileCheck %s --check-prefix=CHECK-OOL +// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout not %run %t u 2>&1 | FileCheck %s --check-prefix=CHECK-OOL + +// Tests that labels are properly dumped at program termination. + +#include <sanitizer/dfsan_interface.h> +#include <assert.h> +#include <stdio.h> + +int main(int argc, char** argv) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + + int k = 3; + dfsan_label k_label = dfsan_create_label("k", 0); + dfsan_set_label(k_label, &k, sizeof(k)); + + dfsan_label ij_label = dfsan_get_label(i + j); + dfsan_label ijk_label = dfsan_get_label(i + j + k); + + fprintf(stderr, "i %d j %d k %d ij %d ijk %d\n", i_label, j_label, k_label, + ij_label, ijk_label); + + // CHECK: 1 0 0 i + // CHECK: 2 0 0 j + // CHECK: 3 0 0 k + // CHECK: 4 1 2 + // CHECK: 5 3 4 + + if (argc > 1) { + // Exhaust the labels. + unsigned long num_labels = 1 << (sizeof(dfsan_label) * 8); + for (unsigned long i = ijk_label + 1; i < num_labels - 2; ++i) { + dfsan_label l = dfsan_create_label("l", 0); + assert(l == i); + } + + // Consume the last available label. + dfsan_label l = dfsan_union(5, 6); + assert(l == num_labels - 2); + + // Try to allocate another label (either explicitly or by unioning two + // existing labels), but expect a crash. + if (argv[1][0] == 'c') { + l = dfsan_create_label("l", 0); + } else { + l = dfsan_union(6, 7); + } + + // CHECK-OOL: FATAL: DataFlowSanitizer: out of labels + // CHECK-OOL: 1 0 0 i + // CHECK-OOL: 2 0 0 j + // CHECK-OOL: 3 0 0 k + // CHECK-OOL: 4 1 2 + // CHECK-OOL: 5 3 4 + // CHECK-OOL: 6 0 0 + // CHECK-OOL: 65534 5 6 + // CHECK-OOL: 65535 0 0 <init label> + } + + return 0; +} diff --git a/test/dfsan/flags.c b/test/dfsan/flags.c new file mode 100644 index 000000000000..79069b96f83a --- /dev/null +++ b/test/dfsan/flags.c @@ -0,0 +1,24 @@ +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_unimplemented=0 %run %t 2>&1 | count 0 +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_nonzero_labels=1 %run %t 2>&1 | FileCheck --check-prefix=CHECK-NONZERO %s + +// Tests that flags work correctly. + +#include <sanitizer/dfsan_interface.h> + +int f(int i) { + return i; +} + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + // CHECK: WARNING: DataFlowSanitizer: call to uninstrumented function f + // CHECK-NOT: WARNING: DataFlowSanitizer: saw nonzero label + // CHECK-NONZERO: WARNING: DataFlowSanitizer: saw nonzero label + f(i); + + return 0; +} diff --git a/test/dfsan/fncall.c b/test/dfsan/fncall.c new file mode 100644 index 000000000000..e780f3145e87 --- /dev/null +++ b/test/dfsan/fncall.c @@ -0,0 +1,26 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %run %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t + +// Tests that labels are propagated through function calls. + +#include <sanitizer/dfsan_interface.h> +#include <assert.h> + +int f(int x) { + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + return x + j; +} + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + dfsan_label ij_label = dfsan_get_label(f(i)); + assert(dfsan_has_label(ij_label, i_label)); + assert(dfsan_has_label_with_desc(ij_label, "j")); + + return 0; +} diff --git a/test/dfsan/label_count.c b/test/dfsan/label_count.c new file mode 100644 index 000000000000..a4b608701811 --- /dev/null +++ b/test/dfsan/label_count.c @@ -0,0 +1,75 @@ +// RUN: %clang_dfsan -DLIB -m64 -c %s -o %t.lib.o && \ +// RUN: %clang_dfsan -m64 -c %s -o %t.o && \ +// RUN: %clang_dfsan -m64 %t.lib.o %t.o -o %t.bin && \ +// RUN: %run %t.bin + +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 -DLIB -c %s -o %t.lib.o && \ +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 -c %s -o %t.o && \ +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %t.o %t.lib.o -o %t.bin && \ +// RUN: %run %t.bin + +#include <sanitizer/dfsan_interface.h> +#include <assert.h> + +#ifdef LIB +// Compiling this file with and without LIB defined allows this file to be +// built as two separate translation units. This ensures that the code +// can not be optimized in a way that removes behavior we wish to test. For +// example, computing a value should cause labels to be allocated only if +// the computation is actually done. Putting the computation here prevents +// the compiler from optimizing away the computation (and labeling) that +// tests wish to verify. + +int add_in_separate_translation_unit(int a, int b) { + return a + b; +} + +int multiply_in_separate_translation_unit(int a, int b) { + return a * b; +} + +#else + +int add_in_separate_translation_unit(int i, int j); +int multiply_in_separate_translation_unit(int i, int j); + +int main(void) { + size_t label_count; + + // No labels allocated yet. + label_count = dfsan_get_label_count(); + assert(0 == label_count); + + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + // One label allocated for i. + label_count = dfsan_get_label_count(); + assert(1u == label_count); + + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + + // Check that a new label was allocated for j. + label_count = dfsan_get_label_count(); + assert(2u == label_count); + + // Create a value that combines i and j. + int i_plus_j = add_in_separate_translation_unit(i, j); + + // Check that a label was created for the union of i and j. + label_count = dfsan_get_label_count(); + assert(3u == label_count); + + // Combine i and j in a different way. Check that the existing label is + // reused, and a new label is not created. + int j_times_i = multiply_in_separate_translation_unit(j, i); + label_count = dfsan_get_label_count(); + assert(3u == label_count); + assert(dfsan_get_label(i_plus_j) == dfsan_get_label(j_times_i)); + + return 0; +} +#endif // #ifdef LIB diff --git a/test/dfsan/lit.cfg b/test/dfsan/lit.cfg new file mode 100644 index 000000000000..d4adb9a5110a --- /dev/null +++ b/test/dfsan/lit.cfg @@ -0,0 +1,26 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'DataFlowSanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup default compiler flags used with -fsanitize=dataflow option. +clang_dfsan_cflags = ["-fsanitize=dataflow"] +clang_dfsan_cxxflags = config.cxx_mode_flags + clang_dfsan_cflags + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang_dfsan ", build_invocation(clang_dfsan_cflags)) ) +config.substitutions.append( ("%clangxx_dfsan ", build_invocation(clang_dfsan_cxxflags)) ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# DataFlowSanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/dfsan/lit.site.cfg.in b/test/dfsan/lit.site.cfg.in new file mode 100644 index 000000000000..859284eca140 --- /dev/null +++ b/test/dfsan/lit.site.cfg.in @@ -0,0 +1,5 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@DFSAN_LIT_TESTS_DIR@/lit.cfg") diff --git a/test/dfsan/propagate.c b/test/dfsan/propagate.c new file mode 100644 index 000000000000..733538cb109a --- /dev/null +++ b/test/dfsan/propagate.c @@ -0,0 +1,47 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %run %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t + +// Tests that labels are propagated through computation and that union labels +// are properly created. + +#include <sanitizer/dfsan_interface.h> +#include <assert.h> + +int main(void) { + assert(dfsan_union(0, 0) == 0); + + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + + int k = 3; + dfsan_label k_label = dfsan_create_label("k", 0); + dfsan_set_label(k_label, &k, sizeof(k)); + + int k2 = 4; + dfsan_set_label(k_label, &k2, sizeof(k2)); + + dfsan_label ij_label = dfsan_get_label(i + j); + assert(dfsan_has_label(ij_label, i_label)); + assert(dfsan_has_label(ij_label, j_label)); + assert(!dfsan_has_label(ij_label, k_label)); + // Test uniquing. + assert(dfsan_union(i_label, j_label) == ij_label); + assert(dfsan_union(j_label, i_label) == ij_label); + + dfsan_label ijk_label = dfsan_get_label(i + j + k); + assert(dfsan_has_label(ijk_label, i_label)); + assert(dfsan_has_label(ijk_label, j_label)); + assert(dfsan_has_label(ijk_label, k_label)); + + assert(dfsan_get_label(k + k2) == k_label); + + struct { int i, j; } s = { i, j }; + assert(dfsan_read_label(&s, sizeof(s)) == ij_label); + + return 0; +} diff --git a/test/dfsan/vararg.c b/test/dfsan/vararg.c new file mode 100644 index 000000000000..2227ba715639 --- /dev/null +++ b/test/dfsan/vararg.c @@ -0,0 +1,24 @@ +// RUN: %clang_dfsan -m64 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %run %t foo +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %run %t foo + +#include <stdio.h> + +int do_nothing(const char *format, ...) { + return 0; +} + +int main(int argc, char **argv) { + int (*fp)(const char *, ...); + + if (argc > 1) + fp = do_nothing; + else + fp = printf; + + // CHECK: FATAL: DataFlowSanitizer: unsupported indirect call to vararg function printf + fp("hello %s\n", "world"); +} diff --git a/test/dfsan/write_callback.c b/test/dfsan/write_callback.c new file mode 100644 index 000000000000..bb35f3250740 --- /dev/null +++ b/test/dfsan/write_callback.c @@ -0,0 +1,110 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %run %t | FileCheck %s +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t | FileCheck %s + +// Tests that the custom implementation of write() does writes with or without +// a callback set using dfsan_set_write_callback(). + +#include <sanitizer/dfsan_interface.h> + +#include <assert.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +// Check write callback arguments by having the callback store them in +// the following variables: +static int last_callback_arg_fd; +static const void *last_callback_arg_buf; +static size_t last_callback_arg_count; + +// Allow tests to check the number of callbacks made by incrementing +// this count. When callbacks are verified, the count is reset. +static int count_unverified_callbacks = 0; + +// This callbact will be installed using dfsan_set_write_callback() +// in tests below. +static void write_callback(int fd, const void *buf, size_t count) { + // Do not do anything in this function that might call write(). + count_unverified_callbacks++; + + last_callback_arg_fd = fd; + last_callback_arg_buf = buf; + last_callback_arg_count = count; +} + +static void write_string_to_stdout(char *string) { + char *cur = string; + int bytes_left = strlen(string); + while (bytes_left > 0) { + int res = write(fileno(stdout), cur, bytes_left); + assert (res >= 0); + cur += res; + bytes_left -= res; + } +} + +static void test_can_write_without_callback() { + dfsan_set_write_callback(NULL); + count_unverified_callbacks = 0; + + char aString[] = "Test that writes work without callback.\n"; + // CHECK: Test that writes work without callback. + write_string_to_stdout(aString); + + assert(count_unverified_callbacks == 0); +} + +static void test_can_write_with_callback() { + dfsan_set_write_callback(write_callback); + + count_unverified_callbacks = 0; + + char stringWithCallback[] = "Test that writes work with callback.\n"; + // CHECK: Test that writes work with callback. + write_string_to_stdout(stringWithCallback); + + // Data was written, so at least one call to write() was made. + // Because a write may not process all the bytes it is passed, there + // may have been several calls to write(). + assert(count_unverified_callbacks > 0); + count_unverified_callbacks = 0; + + dfsan_set_write_callback(NULL); + + char stringWithoutCallback[] = "Writes work after the callback is removed.\n"; + // CHECK: Writes work after the callback is removed. + write_string_to_stdout(stringWithoutCallback); + assert(count_unverified_callbacks == 0); +} + +static void test_failing_write_runs_callback() { + // Open /dev/null in read-only mode. Calling write() on fd will fail. + int fd = open("/dev/null", O_RDONLY); + assert(fd != -1); + + // Install a callback. + dfsan_set_write_callback(write_callback); + + // Write to the read-only file handle. The write will fail, but the callback + // should still be invoked. + char aString[] = "This text will fail to be written.\n"; + int len = strlen(aString); + int write_result = write(fd, aString, len); + assert(write_result == -1); + + assert(count_unverified_callbacks == 1); + count_unverified_callbacks = 0; + + assert(fd == last_callback_arg_fd); + assert(aString == last_callback_arg_buf); + assert(len == last_callback_arg_count); + + close(fd); +} + +int main(int argc, char* argv[]) { + test_can_write_without_callback(); + test_can_write_with_callback(); + test_failing_write_runs_callback(); +} diff --git a/test/lit.common.cfg b/test/lit.common.cfg new file mode 100644 index 000000000000..0ee2b84481da --- /dev/null +++ b/test/lit.common.cfg @@ -0,0 +1,89 @@ +# -*- Python -*- + +# Configuration file for 'lit' test runner. +# This file contains common rules for various compiler-rt testsuites. +# It is mostly copied from lit.cfg used by Clang. +import os +import platform + +import lit.formats +import lit.util + +# Setup test format +execute_external = (platform.system() != 'Windows' + or lit_config.getBashPath() not in [None, ""]) +config.test_format = lit.formats.ShTest(execute_external) + +# Setup clang binary. +compiler_path = getattr(config, 'clang', None) +if (not compiler_path) or (not os.path.exists(compiler_path)): + lit_config.fatal("Can't find compiler on path %r" % compiler_path) + +compiler_id = getattr(config, 'compiler_id', None) +if compiler_id == "Clang": + if platform.system() != 'Windows': + config.cxx_mode_flags = ["--driver-mode=g++"] + else: + config.cxx_mode_flags = [] + # We assume that sanitizers should provide good enough error + # reports and stack traces even with minimal debug info. + config.debug_info_flags = ["-gline-tables-only"] +elif compiler_id == 'GNU': + config.cxx_mode_flags = ["-x c++"] + config.debug_info_flags = ["-g"] +else: + lit_config.fatal("Unsupported compiler id: %r" % compiler_id) +# Add compiler ID to the list of available features. +config.available_features.add(compiler_id) + +# Clear some environment variables that might affect Clang. +possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS', + 'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH', + 'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH', + 'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH', + 'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING', + 'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX', + 'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS', + 'LIBCLANG_RESOURCE_USAGE', + 'LIBCLANG_CODE_COMPLETION_LOGGING'] +# Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it. +if platform.system() != 'Windows': + possibly_dangerous_env_vars.append('INCLUDE') +for name in possibly_dangerous_env_vars: + if name in config.environment: + del config.environment[name] + +# Tweak PATH to include llvm tools dir. +llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) +if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)): + lit_config.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir) +path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH'])) +config.environment['PATH'] = path + +# Help MSVS link.exe find the standard libraries. +if platform.system() == 'Windows': + config.environment['LIB'] = os.environ['LIB'] + +# Use ugly construction to explicitly prohibit "clang", "clang++" etc. +# in RUN lines. +config.substitutions.append( + (' clang', """\n\n*** Do not use 'clangXXX' in tests, + instead define '%clangXXX' substitution in lit config. ***\n\n""") ) + +# Allow tests to be executed on a simulator or remotely. +config.substitutions.append( ('%run', config.emulator) ) + +# Define CHECK-%os to check for OS-dependent output. +config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) + +# Add supported compiler_rt architectures to a list of available features. +compiler_rt_arch = getattr(config, 'compiler_rt_arch', None) +if compiler_rt_arch: + for arch in compiler_rt_arch.split(";"): + config.available_features.add(arch + "-supported-target") + +compiler_rt_debug = getattr(config, 'compiler_rt_debug', False) +if not compiler_rt_debug: + config.available_features.add('compiler-rt-optimized') + +lit.util.usePlatformSdkOnDarwin(config, lit_config) diff --git a/test/lit.common.configured.in b/test/lit.common.configured.in new file mode 100644 index 000000000000..beecaa25886a --- /dev/null +++ b/test/lit.common.configured.in @@ -0,0 +1,38 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Set attribute value if it is unset. +def set_default(attr, value): + if not getattr(config, attr, None): + setattr(config, attr, value) + +# Generic config options for all compiler-rt lit tests. +set_default("target_triple", "@COMPILER_RT_TEST_TARGET_TRIPLE@") +set_default("target_cflags", "@COMPILER_RT_TEST_COMPILER_CFLAGS@") +set_default("host_arch", "@HOST_ARCH@") +set_default("target_arch", "@COMPILER_RT_TEST_TARGET_ARCH@") +set_default("host_os", "@HOST_OS@") +set_default("llvm_build_mode", "@LLVM_BUILD_MODE@") +set_default("llvm_src_root", "@LLVM_SOURCE_DIR@") +set_default("llvm_obj_root", "@LLVM_BINARY_DIR@") +set_default("compiler_rt_src_root", "@COMPILER_RT_SOURCE_DIR@") +set_default("compiler_rt_obj_root", "@COMPILER_RT_BINARY_DIR@") +set_default("llvm_tools_dir", "@LLVM_TOOLS_DIR@") +set_default("clang", "@COMPILER_RT_TEST_COMPILER@") +set_default("compiler_id", "@COMPILER_RT_TEST_COMPILER_ID@") +set_default("compiler_rt_arch", "@COMPILER_RT_SUPPORTED_ARCH@") +set_default("python_executable", "@PYTHON_EXECUTABLE@") +set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@) +set_default("compiler_rt_libdir", "@COMPILER_RT_LIBRARY_OUTPUT_DIR@") +set_default("emulator", "@COMPILER_RT_EMULATOR@") + +# LLVM tools dir can be passed in lit parameters, so try to +# apply substitution. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params +except KeyError,e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) + +# Setup attributes common for all compiler-rt projects. +lit_config.load_config(config, "@COMPILER_RT_SOURCE_DIR@/test/lit.common.cfg") diff --git a/test/lsan/CMakeLists.txt b/test/lsan/CMakeLists.txt new file mode 100644 index 000000000000..7f49b0d3983d --- /dev/null +++ b/test/lsan/CMakeLists.txt @@ -0,0 +1,23 @@ +set(LSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(LSAN_LIT_TEST_MODE "Standalone") +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig/lit.site.cfg) + +set(LSAN_LIT_TEST_MODE "AddressSanitizer") +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg) + +if(NOT APPLE AND NOT ANDROID) + set(LSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND LSAN_TEST_DEPS lsan asan) + endif() + add_lit_testsuite(check-lsan "Running the LeakSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig + DEPENDS ${LSAN_TEST_DEPS}) + set_target_properties(check-lsan PROPERTIES FOLDER "LSan tests") +endif() diff --git a/test/lsan/TestCases/cleanup_in_tsd_destructor.cc b/test/lsan/TestCases/cleanup_in_tsd_destructor.cc new file mode 100644 index 000000000000..5335454ffbeb --- /dev/null +++ b/test/lsan/TestCases/cleanup_in_tsd_destructor.cc @@ -0,0 +1,45 @@ +// Regression test for thread lifetime tracking. Thread data should be +// considered live during the thread's termination, at least until the +// user-installed TSD destructors have finished running (since they may contain +// additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it +// makes its best effort. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %run %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +pthread_key_t key; +__thread void *p; + +void key_destructor(void *arg) { + // Generally this may happen on a different thread. + __lsan_do_leak_check(); +} + +void *thread_func(void *arg) { + p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + int res = pthread_setspecific(key, (void*)1); + assert(res == 0); + return 0; +} + +int main() { + int res = pthread_key_create(&key, &key_destructor); + assert(res == 0); + pthread_t thread_id; + res = pthread_create(&thread_id, 0, thread_func, 0); + assert(res == 0); + res = pthread_join(thread_id, 0); + assert(res == 0); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: [[ADDR]] (1337 bytes) diff --git a/test/lsan/TestCases/disabler.cc b/test/lsan/TestCases/disabler.cc new file mode 100644 index 000000000000..f83106501fa3 --- /dev/null +++ b/test/lsan/TestCases/disabler.cc @@ -0,0 +1,23 @@ +// Test for ScopedDisabler. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +int main() { + void **p; + { + __lsan::ScopedDisabler d; + p = new void *; + } + *reinterpret_cast<void **>(p) = malloc(666); + void *q = malloc(1337); + // Break optimization. + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/test/lsan/TestCases/disabler_in_tsd_destructor.cc b/test/lsan/TestCases/disabler_in_tsd_destructor.cc new file mode 100644 index 000000000000..a0012c74dd96 --- /dev/null +++ b/test/lsan/TestCases/disabler_in_tsd_destructor.cc @@ -0,0 +1,38 @@ +// Regression test. Disabler should not depend on TSD validity. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +pthread_key_t key; + +void key_destructor(void *arg) { + __lsan::ScopedDisabler d; + void *p = malloc(1337); + // Break optimization. + fprintf(stderr, "Test alloc: %p.\n", p); + pthread_setspecific(key, 0); +} + +void *thread_func(void *arg) { + int res = pthread_setspecific(key, (void*)1); + assert(res == 0); + return 0; +} + +int main() { + int res = pthread_key_create(&key, &key_destructor); + assert(res == 0); + pthread_t thread_id; + res = pthread_create(&thread_id, 0, thread_func, 0); + assert(res == 0); + res = pthread_join(thread_id, 0); + assert(res == 0); + return 0; +} diff --git a/test/lsan/TestCases/do_leak_check_override.cc b/test/lsan/TestCases/do_leak_check_override.cc new file mode 100644 index 000000000000..bedb0cad6908 --- /dev/null +++ b/test/lsan/TestCases/do_leak_check_override.cc @@ -0,0 +1,36 @@ +// Test for __lsan_do_leak_check(). We test it by making the leak check run +// before global destructors, which also tests compatibility with HeapChecker's +// "normal" mode (LSan runs in "strict" mode by default). +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s + +#include <stdio.h> +#include <stdlib.h> +#include <sanitizer/lsan_interface.h> + +struct LeakyGlobal { + LeakyGlobal() { + p = malloc(1337); + } + ~LeakyGlobal() { + p = 0; + } + void *p; +}; + +LeakyGlobal leaky_global; + +int main(int argc, char *argv[]) { + // Register leak check to run before global destructors. + if (argc > 1) + atexit(&__lsan_do_leak_check); + void *p = malloc(666); + printf("Test alloc: %p\n", p); + printf("Test alloc in leaky global: %p\n", leaky_global.p); + return 0; +} + +// CHECK-strict: SUMMARY: {{(Leak|Address)}}Sanitizer: 2003 byte(s) leaked in 2 allocation(s) +// CHECK-normal: SUMMARY: {{(Leak|Address)}}Sanitizer: 666 byte(s) leaked in 1 allocation(s) diff --git a/test/lsan/TestCases/fork.cc b/test/lsan/TestCases/fork.cc new file mode 100644 index 000000000000..9e72fe871cf1 --- /dev/null +++ b/test/lsan/TestCases/fork.cc @@ -0,0 +1,24 @@ +// Test that thread local data is handled correctly after forking without exec(). +// RUN: %clangxx_lsan %s -o %t +// RUN: %run %t 2>&1 + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +__thread void *thread_local_var; + +int main() { + int status = 0; + thread_local_var = malloc(1337); + pid_t pid = fork(); + assert(pid >= 0); + if (pid > 0) { + waitpid(pid, &status, 0); + assert(WIFEXITED(status)); + return WEXITSTATUS(status); + } + return 0; +} diff --git a/test/lsan/TestCases/fork_threaded.cc b/test/lsan/TestCases/fork_threaded.cc new file mode 100644 index 000000000000..62702b4dfe47 --- /dev/null +++ b/test/lsan/TestCases/fork_threaded.cc @@ -0,0 +1,43 @@ +// Test that thread local data is handled correctly after forking without +// exec(). In this test leak checking is initiated from a non-main thread. +// RUN: %clangxx_lsan %s -o %t +// RUN: %run %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +__thread void *thread_local_var; + +void *exit_thread_func(void *arg) { + exit(0); +} + +void ExitFromThread() { + pthread_t tid; + int res; + res = pthread_create(&tid, 0, exit_thread_func, 0); + assert(res == 0); + pthread_join(tid, 0); +} + +int main() { + int status = 0; + thread_local_var = malloc(1337); + pid_t pid = fork(); + assert(pid >= 0); + if (pid > 0) { + waitpid(pid, &status, 0); + assert(WIFEXITED(status)); + return WEXITSTATUS(status); + } else { + // Spawn a thread and call exit() from there, to check that we track main + // thread's pid correctly even if leak checking is initiated from another + // thread. + ExitFromThread(); + } + return 0; +} diff --git a/test/lsan/TestCases/high_allocator_contention.cc b/test/lsan/TestCases/high_allocator_contention.cc new file mode 100644 index 000000000000..2543897bcbb4 --- /dev/null +++ b/test/lsan/TestCases/high_allocator_contention.cc @@ -0,0 +1,48 @@ +// A benchmark that executes malloc/free pairs in parallel. +// Usage: ./a.out number_of_threads total_number_of_allocations +// RUN: %clangxx_lsan %s -o %t +// RUN: %run %t 5 1000000 2>&1 +#include <assert.h> +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +int num_threads; +int total_num_alloc; +const int kMaxNumThreads = 5000; +pthread_t tid[kMaxNumThreads]; + +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +bool go = false; + +void *thread_fun(void *arg) { + pthread_mutex_lock(&mutex); + while (!go) pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < total_num_alloc / num_threads; i++) { + void *p = malloc(10); + __asm__ __volatile__("" : : "r"(p) : "memory"); + free((void *)p); + } + return 0; +} + +int main(int argc, char** argv) { + assert(argc == 3); + num_threads = atoi(argv[1]); + assert(num_threads > 0); + assert(num_threads <= kMaxNumThreads); + total_num_alloc = atoi(argv[2]); + assert(total_num_alloc > 0); + printf("%d threads, %d allocations in each\n", num_threads, + total_num_alloc / num_threads); + for (int i = 0; i < num_threads; i++) + pthread_create(&tid[i], 0, thread_fun, 0); + pthread_mutex_lock(&mutex); + go = true; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < num_threads; i++) pthread_join(tid[i], 0); + return 0; +} diff --git a/test/lsan/TestCases/ignore_object.cc b/test/lsan/TestCases/ignore_object.cc new file mode 100644 index 000000000000..38d76e6798b2 --- /dev/null +++ b/test/lsan/TestCases/ignore_object.cc @@ -0,0 +1,24 @@ +// Test for __lsan_ignore_object(). +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=2" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +int main() { + // Explicitly ignored object. + void **p = new void *; + // Transitively ignored object. + *p = malloc(666); + // Non-ignored object. + volatile void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + __lsan_ignore_object(p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: ignoring heap object at [[ADDR]] +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/test/lsan/TestCases/ignore_object_errors.cc b/test/lsan/TestCases/ignore_object_errors.cc new file mode 100644 index 000000000000..39b9b0288bb3 --- /dev/null +++ b/test/lsan/TestCases/ignore_object_errors.cc @@ -0,0 +1,22 @@ +// Test for incorrect use of __lsan_ignore_object(). +// RUN: LSAN_BASE="verbosity=2" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +int main() { + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + __lsan_ignore_object(p); + __lsan_ignore_object(p); + free(p); + __lsan_ignore_object(p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: heap object at [[ADDR]] is already being ignored +// CHECK: no heap object found at [[ADDR]] diff --git a/test/lsan/TestCases/large_allocation_leak.cc b/test/lsan/TestCases/large_allocation_leak.cc new file mode 100644 index 000000000000..f41143a8a501 --- /dev/null +++ b/test/lsan/TestCases/large_allocation_leak.cc @@ -0,0 +1,18 @@ +// Test that LargeMmapAllocator's chunks aren't reachable via some internal data structure. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +int main() { + // maxsize in primary allocator is always less than this (1 << 25). + void *large_alloc = malloc(33554432); + fprintf(stderr, "Test alloc: %p.\n", large_alloc); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (33554432 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/leak_check_at_exit.cc b/test/lsan/TestCases/leak_check_at_exit.cc new file mode 100644 index 000000000000..fe3f70e40005 --- /dev/null +++ b/test/lsan/TestCases/leak_check_at_exit.cc @@ -0,0 +1,21 @@ +// Test for the leak_check_at_exit flag. +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS=$LSAN_BASE:"leak_check_at_exit=0" not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS=%LSAN_BASE:"leak_check_at_exit=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont + +#include <stdio.h> +#include <stdlib.h> +#include <sanitizer/lsan_interface.h> + +int main(int argc, char *argv[]) { + fprintf(stderr, "Test alloc: %p.\n", malloc(1337)); + if (argc > 1) + __lsan_do_leak_check(); + return 0; +} + +// CHECK-do: SUMMARY: {{(Leak|Address)}}Sanitizer: +// CHECK-dont-NOT: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/leak_check_before_thread_started.cc b/test/lsan/TestCases/leak_check_before_thread_started.cc new file mode 100644 index 000000000000..891cd699a255 --- /dev/null +++ b/test/lsan/TestCases/leak_check_before_thread_started.cc @@ -0,0 +1,32 @@ +// Regression test for http://llvm.org/bugs/show_bug.cgi?id=21621 +// This test relies on timing between threads, so any failures will be flaky. +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: %run %t +#include <assert.h> +#include <pthread.h> +#include <stdlib.h> +#include <unistd.h> + +void *func(void *arg) { + sleep(1); + free(arg); + return 0; +} + +void create_detached_thread() { + pthread_t thread_id; + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + void *arg = malloc(1337); + assert(arg); + int res = pthread_create(&thread_id, &attr, func, arg); + assert(res == 0); +} + +int main() { + create_detached_thread(); +} diff --git a/test/lsan/TestCases/link_turned_off.cc b/test/lsan/TestCases/link_turned_off.cc new file mode 100644 index 000000000000..2482f6197d92 --- /dev/null +++ b/test/lsan/TestCases/link_turned_off.cc @@ -0,0 +1,24 @@ +// Test for disabling LSan at link-time. +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s + +#include <sanitizer/lsan_interface.h> + +int argc_copy; + +extern "C" { +int __lsan_is_turned_off() { + return (argc_copy == 1); +} +} + +int main(int argc, char *argv[]) { + volatile int *x = new int; + *x = 42; + argc_copy = argc; + return 0; +} + +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 4 byte(s) leaked in 1 allocation(s) diff --git a/test/lsan/TestCases/new_array_with_dtor_0.cc b/test/lsan/TestCases/new_array_with_dtor_0.cc new file mode 100644 index 000000000000..59259616e827 --- /dev/null +++ b/test/lsan/TestCases/new_array_with_dtor_0.cc @@ -0,0 +1,19 @@ +// Regression test: +// https://code.google.com/p/address-sanitizer/issues/detail?id=257 +// RUN: %clangxx_lsan %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdio.h> + +struct T { + ~T() { printf("~T\n"); } +}; + +T *t; + +int main(int argc, char **argv) { + t = new T[argc - 1]; + printf("OK\n"); +} + +// CHECK: OK + diff --git a/test/lsan/TestCases/pointer_to_self.cc b/test/lsan/TestCases/pointer_to_self.cc new file mode 100644 index 000000000000..63bde2ccf35d --- /dev/null +++ b/test/lsan/TestCases/pointer_to_self.cc @@ -0,0 +1,18 @@ +// Regression test: pointers to self should not confuse LSan into thinking the +// object is indirectly leaked. Only external pointers count. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +int main() { + void *p = malloc(1337); + *reinterpret_cast<void **>(p) = p; + fprintf(stderr, "Test alloc: %p.\n", p); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/print_suppressions.cc b/test/lsan/TestCases/print_suppressions.cc new file mode 100644 index 000000000000..b292c0a7c3cf --- /dev/null +++ b/test/lsan/TestCases/print_suppressions.cc @@ -0,0 +1,33 @@ +// Print matched suppressions only if print_suppressions=1 AND at least one is +// matched. Default is print_suppressions=true. +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:print_suppressions=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print +// RUN: LSAN_OPTIONS=$LSAN_BASE:print_suppressions=0 %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-print + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +extern "C" +const char *__lsan_default_suppressions() { + return "leak:*LSanTestLeakingFunc*"; +} + +void LSanTestLeakingFunc() { + void *p = malloc(666); + fprintf(stderr, "Test alloc: %p.\n", p); +} + +int main(int argc, char **argv) { + printf("print for nonempty output\n"); + if (argc > 1) + LSanTestLeakingFunc(); + return 0; +} +// CHECK-print: Suppressions used: +// CHECK-print: 1 666 *LSanTestLeakingFunc* +// CHECK-dont-print-NOT: Suppressions used: diff --git a/test/lsan/TestCases/register_root_region.cc b/test/lsan/TestCases/register_root_region.cc new file mode 100644 index 000000000000..6fc84c2fb50f --- /dev/null +++ b/test/lsan/TestCases/register_root_region.cc @@ -0,0 +1,32 @@ +// Test for __lsan_(un)register_root_region(). +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_root_regions=0 not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <unistd.h> + +#include <sanitizer/lsan_interface.h> + +int main(int argc, char *argv[]) { + size_t size = getpagesize() * 2; + void *p = + mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + assert(p); + // Make half of the memory inaccessible. LSan must not crash trying to read it. + assert(0 == mprotect((char *)p + size / 2, size / 2, PROT_NONE)); + + __lsan_register_root_region(p, size); + *((void **)p) = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + if (argc > 1) + __lsan_unregister_root_region(p, size); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/test/lsan/TestCases/sanity_check_pure_c.c b/test/lsan/TestCases/sanity_check_pure_c.c new file mode 100644 index 000000000000..085412b47d55 --- /dev/null +++ b/test/lsan/TestCases/sanity_check_pure_c.c @@ -0,0 +1,10 @@ +// Check that we can build C code. +// RUN: %clang_lsan %s -o %t +#ifdef __cplusplus +#error "This test must be built in C mode" +#endif + +int main() { + // FIXME: ideally this should somehow check that we don't have libstdc++ + return 0; +} diff --git a/test/lsan/TestCases/stale_stack_leak.cc b/test/lsan/TestCases/stale_stack_leak.cc new file mode 100644 index 000000000000..4b8a54edf4cc --- /dev/null +++ b/test/lsan/TestCases/stale_stack_leak.cc @@ -0,0 +1,43 @@ +// Test that out-of-scope local variables are ignored by LSan. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=1" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %run %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s + +#include <stdio.h> +#include <stdlib.h> + +void **pp; + +// Put pointer far enough on the stack that LSan has space to run in without +// overwriting it. +// Hopefully the argument p will be passed on a register, saving us from false +// negatives. +__attribute__((noinline)) +void *PutPointerOnStaleStack(void *p) { + void *locals[2048]; + locals[0] = p; + pp = &locals[0]; + fprintf(stderr, "Test alloc: %p.\n", locals[0]); + return 0; +} + +int main() { + PutPointerOnStaleStack(malloc(1337)); + return 0; +} + +// This must run after LSan, to ensure LSan didn't overwrite the pointer before +// it had a chance to see it. If LSan is invoked with atexit(), this works. +// Otherwise, we need a different method. +__attribute__((destructor)) +__attribute__((no_sanitize_address)) +void ConfirmPointerHasSurvived() { + fprintf(stderr, "Value after LSan: %p.\n", *pp); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK-sanity: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: +// CHECK-sanity: Value after LSan: [[ADDR]]. diff --git a/test/lsan/TestCases/suppressions_default.cc b/test/lsan/TestCases/suppressions_default.cc new file mode 100644 index 000000000000..b4c0de016cd1 --- /dev/null +++ b/test/lsan/TestCases/suppressions_default.cc @@ -0,0 +1,28 @@ +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +extern "C" +const char *__lsan_default_suppressions() { + return "leak:*LSanTestLeakingFunc*"; +} + +void LSanTestLeakingFunc() { + void *p = malloc(666); + fprintf(stderr, "Test alloc: %p.\n", p); +} + +int main() { + LSanTestLeakingFunc(); + void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: Suppressions used: +// CHECK: 1 666 *LSanTestLeakingFunc* +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/test/lsan/TestCases/suppressions_file.cc b/test/lsan/TestCases/suppressions_file.cc new file mode 100644 index 000000000000..16ad9323461e --- /dev/null +++ b/test/lsan/TestCases/suppressions_file.cc @@ -0,0 +1,26 @@ +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" +// RUN: %clangxx_lsan %s -o %t + +// RUN: echo "leak:*LSanTestLeakingFunc*" > %t.supp1 +// RUN: LSAN_OPTIONS=$LSAN_BASE:suppressions=%t.supp1 not %run %t 2>&1 | FileCheck %s + +// RUN: echo "leak:%t" > %t.supp2 +// RUN: LSAN_OPTIONS=$LSAN_BASE:suppressions="%t.supp2":symbolize=false %run %t + +#include <stdio.h> +#include <stdlib.h> + +void LSanTestLeakingFunc() { + void *p = malloc(666); + fprintf(stderr, "Test alloc: %p.\n", p); +} + +int main() { + LSanTestLeakingFunc(); + void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: Suppressions used: +// CHECK: 1 666 *LSanTestLeakingFunc* +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/test/lsan/TestCases/swapcontext.cc b/test/lsan/TestCases/swapcontext.cc new file mode 100644 index 000000000000..f7e95ed2ca5c --- /dev/null +++ b/test/lsan/TestCases/swapcontext.cc @@ -0,0 +1,47 @@ +// We can't unwind stack if we're running coroutines on heap-allocated +// memory. Make sure we don't report these leaks. + +// RUN: %clangxx_lsan %s -o %t +// RUN: %run %t 2>&1 +// RUN: not %run %t foo 2>&1 | FileCheck %s + +#include <stdio.h> +#if defined(__APPLE__) +// Note: ucontext.h is deprecated on OSX, so this test may stop working +// someday. We define _XOPEN_SOURCE to keep using ucontext.h for now. +#define _XOPEN_SOURCE 1 +#endif +#include <ucontext.h> +#include <unistd.h> + +const int kStackSize = 1 << 20; + +void Child() { + int child_stack; + printf("Child: %p\n", &child_stack); + int *leaked = new int[666]; +} + +int main(int argc, char *argv[]) { + char stack_memory[kStackSize + 1]; + char *heap_memory = new char[kStackSize + 1]; + char *child_stack = (argc > 1) ? stack_memory : heap_memory; + + printf("Child stack: %p\n", child_stack); + ucontext_t orig_context; + ucontext_t child_context; + getcontext(&child_context); + child_context.uc_stack.ss_sp = child_stack; + child_context.uc_stack.ss_size = kStackSize / 2; + child_context.uc_link = &orig_context; + makecontext(&child_context, Child, 0); + if (swapcontext(&orig_context, &child_context) < 0) { + perror("swapcontext"); + return 1; + } + + delete[] heap_memory; + return 0; +} + +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 2664 byte(s) leaked in 1 allocation(s) diff --git a/test/lsan/TestCases/use_after_return.cc b/test/lsan/TestCases/use_after_return.cc new file mode 100644 index 000000000000..eb917c01ea80 --- /dev/null +++ b/test/lsan/TestCases/use_after_return.cc @@ -0,0 +1,23 @@ +// Test that fake stack (introduced by ASan's use-after-return mode) is included +// in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -O2 -o %t +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS="" %run %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +int main() { + void *stack_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", stack_var); + // Take pointer to variable, to ensure it's not optimized into a register. + fprintf(stderr, "Stack var at: %p.\n", &stack_var); + // Do not return from main to prevent the pointer from going out of scope. + exit(0); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_globals_initialized.cc b/test/lsan/TestCases/use_globals_initialized.cc new file mode 100644 index 000000000000..172d22a9f056 --- /dev/null +++ b/test/lsan/TestCases/use_globals_initialized.cc @@ -0,0 +1,21 @@ +// Test that initialized globals are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +void *data_var = (void *)1; + +int main() { + data_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", data_var); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_globals_uninitialized.cc b/test/lsan/TestCases/use_globals_uninitialized.cc new file mode 100644 index 000000000000..2daa661611f4 --- /dev/null +++ b/test/lsan/TestCases/use_globals_uninitialized.cc @@ -0,0 +1,21 @@ +// Test that uninitialized globals are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +void *bss_var; + +int main() { + bss_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", bss_var); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_poisoned_asan.cc b/test/lsan/TestCases/use_poisoned_asan.cc new file mode 100644 index 000000000000..a1c544c55f28 --- /dev/null +++ b/test/lsan/TestCases/use_poisoned_asan.cc @@ -0,0 +1,25 @@ +// ASan-poisoned memory should be ignored if use_poisoned is false. +// REQUIRES: asan +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_poisoned=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_poisoned=1" %run %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> +#include <sanitizer/asan_interface.h> +#include <assert.h> + +void **p; + +int main() { + p = new void *; + *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", *p); + __asan_poison_memory_region(p, sizeof(*p)); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: AddressSanitizer: diff --git a/test/lsan/TestCases/use_registers.cc b/test/lsan/TestCases/use_registers.cc new file mode 100644 index 000000000000..ce11c3f77bcb --- /dev/null +++ b/test/lsan/TestCases/use_registers.cc @@ -0,0 +1,52 @@ +// Test that registers of running threads are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0" +// RUN: %clangxx_lsan -pthread %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> + +extern "C" +void *registers_thread_func(void *arg) { + int *sync = reinterpret_cast<int *>(arg); + void *p = malloc(1337); + // To store the pointer, choose a register which is unlikely to be reused by + // a function call. +#if defined(__i386__) + asm ( "mov %0, %%esi" + : + : "r" (p) + ); +#elif defined(__x86_64__) + asm ( "mov %0, %%r15" + : + : "r" (p) + ); +#else +#error "Test is not supported on this architecture." +#endif + fprintf(stderr, "Test alloc: %p.\n", p); + fflush(stderr); + __sync_fetch_and_xor(sync, 1); + while (true) + sched_yield(); +} + +int main() { + int sync = 0; + pthread_t thread_id; + int res = pthread_create(&thread_id, 0, registers_thread_func, &sync); + assert(res == 0); + while (!__sync_fetch_and_xor(&sync, 0)) + sched_yield(); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_stacks.cc b/test/lsan/TestCases/use_stacks.cc new file mode 100644 index 000000000000..7afcde15c733 --- /dev/null +++ b/test/lsan/TestCases/use_stacks.cc @@ -0,0 +1,20 @@ +// Test that stack of main thread is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +int main() { + void *stack_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", stack_var); + // Do not return from main to prevent the pointer from going out of scope. + exit(0); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_stacks_threaded.cc b/test/lsan/TestCases/use_stacks_threaded.cc new file mode 100644 index 000000000000..a1d4383e9569 --- /dev/null +++ b/test/lsan/TestCases/use_stacks_threaded.cc @@ -0,0 +1,37 @@ +// Test that stacks of non-main threads are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan -pthread %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> + +extern "C" +void *stacks_thread_func(void *arg) { + int *sync = reinterpret_cast<int *>(arg); + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + fflush(stderr); + __sync_fetch_and_xor(sync, 1); + while (true) + sched_yield(); +} + +int main() { + int sync = 0; + pthread_t thread_id; + int res = pthread_create(&thread_id, 0, stacks_thread_func, &sync); + assert(res == 0); + while (!__sync_fetch_and_xor(&sync, 0)) + sched_yield(); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_tls_dynamic.cc b/test/lsan/TestCases/use_tls_dynamic.cc new file mode 100644 index 000000000000..860db041ae40 --- /dev/null +++ b/test/lsan/TestCases/use_tls_dynamic.cc @@ -0,0 +1,50 @@ +// Test that dynamically allocated TLS space is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx %s -DBUILD_DSO -fPIC -shared -o %t-so.so +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#ifndef BUILD_DSO +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string> + +int main(int argc, char *argv[]) { + std::string path = std::string(argv[0]) + "-so.so"; + + void *handle = dlopen(path.c_str(), RTLD_LAZY); + assert(handle != 0); + typedef void **(* store_t)(void *p); + store_t StoreToTLS = (store_t)dlsym(handle, "StoreToTLS"); + assert(dlerror() == 0); + + void *p = malloc(1337); + // If we don't know about dynamic TLS, we will return a false leak above. + void **p_in_tls = StoreToTLS(p); + assert(*p_in_tls == p); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: + +#else // BUILD_DSO +// A loadable module with a large thread local section, which would require +// allocation of a new TLS storage chunk when loaded with dlopen(). We use it +// to test the reachability of such chunks in LSan tests. + +// This must be large enough that it doesn't fit into preallocated static TLS +// space (see STATIC_TLS_SURPLUS in glibc). +__thread void *huge_thread_local_array[(1 << 20) / sizeof(void *)]; // NOLINT + +extern "C" void **StoreToTLS(void *p) { + huge_thread_local_array[0] = p; + return &huge_thread_local_array[0]; +} +#endif // BUILD_DSO diff --git a/test/lsan/TestCases/use_tls_pthread_specific_dynamic.cc b/test/lsan/TestCases/use_tls_pthread_specific_dynamic.cc new file mode 100644 index 000000000000..14883712e608 --- /dev/null +++ b/test/lsan/TestCases/use_tls_pthread_specific_dynamic.cc @@ -0,0 +1,37 @@ +// Test that dynamically allocated thread-specific storage is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +// From glibc: this many keys are stored in the thread descriptor directly. +const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; + +int main() { + static const unsigned kDummyKeysCount = PTHREAD_KEY_2NDLEVEL_SIZE; + int res; + pthread_key_t dummy_keys[kDummyKeysCount]; + for (unsigned i = 0; i < kDummyKeysCount; i++) { + res = pthread_key_create(&dummy_keys[i], NULL); + assert(res == 0); + } + pthread_key_t key; + res = pthread_key_create(&key, NULL); + assert(key >= PTHREAD_KEY_2NDLEVEL_SIZE); + assert(res == 0); + void *p = malloc(1337); + res = pthread_setspecific(key, p); + assert(res == 0); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_tls_pthread_specific_static.cc b/test/lsan/TestCases/use_tls_pthread_specific_static.cc new file mode 100644 index 000000000000..1fd5681b6080 --- /dev/null +++ b/test/lsan/TestCases/use_tls_pthread_specific_static.cc @@ -0,0 +1,31 @@ +// Test that statically allocated thread-specific storage is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +// From glibc: this many keys are stored in the thread descriptor directly. +const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; + +int main() { + pthread_key_t key; + int res; + res = pthread_key_create(&key, NULL); + assert(res == 0); + assert(key < PTHREAD_KEY_2NDLEVEL_SIZE); + void *p = malloc(1337); + res = pthread_setspecific(key, p); + assert(res == 0); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_tls_static.cc b/test/lsan/TestCases/use_tls_static.cc new file mode 100644 index 000000000000..50db23abb825 --- /dev/null +++ b/test/lsan/TestCases/use_tls_static.cc @@ -0,0 +1,21 @@ +// Test that statically allocated TLS space is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: LSAN_OPTIONS="" %run %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +__thread void *tls_var; + +int main() { + tls_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", tls_var); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/TestCases/use_unaligned.cc b/test/lsan/TestCases/use_unaligned.cc new file mode 100644 index 000000000000..3e43ed4c092c --- /dev/null +++ b/test/lsan/TestCases/use_unaligned.cc @@ -0,0 +1,23 @@ +// Test that unaligned pointers are detected correctly. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=0" not %run %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=1" %run %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void *arr[2]; + +int main() { + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + char *char_arr = (char *)arr; + memcpy(char_arr + 1, &p, sizeof(p)); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/test/lsan/lit.common.cfg b/test/lsan/lit.common.cfg new file mode 100644 index 000000000000..bd1aa2769c42 --- /dev/null +++ b/test/lsan/lit.common.cfg @@ -0,0 +1,51 @@ +# -*- Python -*- + +# Common configuration for running leak detection tests under LSan/ASan. + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Choose between standalone and LSan+ASan modes. +lsan_lit_test_mode = get_required_attr(config, 'lsan_lit_test_mode') +if lsan_lit_test_mode == "Standalone": + config.name = "LeakSanitizer-Standalone" + lsan_cflags = ["-fsanitize=leak"] +elif lsan_lit_test_mode == "AddressSanitizer": + config.name = "LeakSanitizer-AddressSanitizer" + lsan_cflags = ["-fsanitize=address"] + config.available_features.add('asan') +else: + lit_config.fatal("Unknown LSan test mode: %r" % lsan_lit_test_mode) + +clang_cflags = ["-O0", "-m64"] + config.debug_info_flags +clang_cxxflags = config.cxx_mode_flags + clang_cflags +clang_lsan_cflags = clang_cflags + lsan_cflags +clang_lsan_cxxflags = clang_cxxflags + lsan_cflags + +config.clang_cflags = clang_cflags +config.clang_cxxflags = clang_cxxflags + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) ) +config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) ) +config.substitutions.append( ("%clang_lsan ", build_invocation(clang_lsan_cflags)) ) +config.substitutions.append( ("%clangxx_lsan ", build_invocation(clang_lsan_cxxflags)) ) + +# LeakSanitizer tests are currently supported on x86-64 Linux only. +if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64']: + config.unsupported = True + +config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/test/lsan/lit.site.cfg.in b/test/lsan/lit.site.cfg.in new file mode 100644 index 000000000000..7d2877bdc528 --- /dev/null +++ b/test/lsan/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Tool-specific config options. +config.lsan_lit_test_mode = "@LSAN_LIT_TEST_MODE@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/lit.common.cfg") diff --git a/test/msan/CMakeLists.txt b/test/msan/CMakeLists.txt new file mode 100644 index 000000000000..08786ee777eb --- /dev/null +++ b/test/msan/CMakeLists.txt @@ -0,0 +1,23 @@ +set(MSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + +set(MSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND MSAN_TEST_DEPS msan) +endif() + +if(COMPILER_RT_INCLUDE_TESTS AND COMPILER_RT_HAS_LIBCXX_SOURCES) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) + list(APPEND MSAN_TEST_DEPS MsanUnitTests) +endif() + +add_lit_testsuite(check-msan "Running the MemorySanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${MSAN_TEST_DEPS} + ) +set_target_properties(check-msan PROPERTIES FOLDER "MSan tests") diff --git a/test/msan/Linux/getresid.cc b/test/msan/Linux/getresid.cc new file mode 100644 index 000000000000..385351dfdcde --- /dev/null +++ b/test/msan/Linux/getresid.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 + +#include <assert.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + uid_t uids[6]; + assert(0 == __msan_test_shadow(uids, 6 * sizeof(uid_t))); + assert(0 == getresuid(&uids[0], &uids[2], &uids[4])); + for (int i = 0; i < 3; i++) + assert(sizeof(uid_t) == + __msan_test_shadow(uids + 2 * i, 2 * sizeof(uid_t))); + + gid_t gids[6]; + assert(0 == __msan_test_shadow(gids, 6 * sizeof(gid_t))); + assert(0 == getresgid(&gids[0], &gids[2], &gids[4])); + for (int i = 0; i < 3; i++) + assert(sizeof(gid_t) == + __msan_test_shadow(gids + 2 * i, 2 * sizeof(gid_t))); + return 0; +} diff --git a/test/msan/Linux/glob.cc b/test/msan/Linux/glob.cc new file mode 100644 index 000000000000..8604e4d76265 --- /dev/null +++ b/test/msan/Linux/glob.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 | FileCheck %s + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*a"); + + glob_t globbuf; + int res = glob(buf, 0, 0, &globbuf); + + printf("%d %s\n", errno, strerror(errno)); + assert(res == 0); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/test/msan/Linux/glob_altdirfunc.cc b/test/msan/Linux/glob_altdirfunc.cc new file mode 100644 index 000000000000..2c02e735e05e --- /dev/null +++ b/test/msan/Linux/glob_altdirfunc.cc @@ -0,0 +1,78 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 | FileCheck %s + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + +static void my_gl_closedir(void *dir) { + if (!dir) + exit(1); + closedir((DIR *)dir); +} + +static struct dirent *my_gl_readdir(void *dir) { + if (!dir) + exit(1); + struct dirent *d = readdir((DIR *)dir); + if (d) __msan_poison(d, d->d_reclen); // hehe + return d; +} + +static void *my_gl_opendir(const char *s) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + return opendir(s); +} + +static int my_gl_lstat(const char *s, struct stat *st) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + if (!st) + exit(1); + return lstat(s, st); +} + +static int my_gl_stat(const char *s, struct stat *st) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + if (!st) + exit(1); + return lstat(s, st); +} + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*a"); + + glob_t globbuf; + globbuf.gl_closedir = my_gl_closedir; + globbuf.gl_readdir = my_gl_readdir; + globbuf.gl_opendir = my_gl_opendir; + globbuf.gl_lstat = my_gl_lstat; + globbuf.gl_stat = my_gl_stat; + for (int i = 0; i < 10000; ++i) { + int res = glob(buf, GLOB_ALTDIRFUNC | GLOB_MARK, 0, &globbuf); + assert(res == 0); + printf("%d %s\n", errno, strerror(errno)); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + __msan_poison(globbuf.gl_pathv[0], strlen(globbuf.gl_pathv[0]) + 1); + __msan_poison(globbuf.gl_pathv[1], strlen(globbuf.gl_pathv[1]) + 1); + globfree(&globbuf); + } + + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/test/msan/Linux/glob_nomatch.cc b/test/msan/Linux/glob_nomatch.cc new file mode 100644 index 000000000000..bc35c30d6d07 --- /dev/null +++ b/test/msan/Linux/glob_nomatch.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*c"); + + glob_t globbuf; + int res = glob(buf, 0, 0, &globbuf); + assert(res == GLOB_NOMATCH); + assert(globbuf.gl_pathc == 0); + if (globbuf.gl_pathv == 0) + exit(0); + return 0; +} diff --git a/test/msan/Linux/glob_test_root/aa b/test/msan/Linux/glob_test_root/aa new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/msan/Linux/glob_test_root/aa diff --git a/test/msan/Linux/glob_test_root/ab b/test/msan/Linux/glob_test_root/ab new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/msan/Linux/glob_test_root/ab diff --git a/test/msan/Linux/glob_test_root/ba b/test/msan/Linux/glob_test_root/ba new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/msan/Linux/glob_test_root/ba diff --git a/test/msan/Linux/lit.local.cfg b/test/msan/Linux/lit.local.cfg new file mode 100644 index 000000000000..57271b8078a4 --- /dev/null +++ b/test/msan/Linux/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/msan/Linux/sunrpc.cc b/test/msan/Linux/sunrpc.cc new file mode 100644 index 000000000000..78645a7dcbf2 --- /dev/null +++ b/test/msan/Linux/sunrpc.cc @@ -0,0 +1,40 @@ +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=int -DFN=xdr_int %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=int -DFN=xdr_int -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=double -DFN=xdr_double %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=double -DFN=xdr_double -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=u_quad_t -DFN=xdr_u_longlong_t %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=u_quad_t -DFN=xdr_u_longlong_t -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <rpc/xdr.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + XDR xdrs; + char buf[100]; + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_ENCODE); + TYPE x; +#ifndef UNINIT + x = 42; +#endif + bool_t res = FN(&xdrs, &x); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main.*sunrpc.cc:}}[[@LINE-2]] + assert(res == TRUE); + xdr_destroy(&xdrs); + + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_DECODE); + TYPE y; + res = FN(&xdrs, &y); + assert(res == TRUE); + assert(__msan_test_shadow(&y, sizeof(y)) == -1); + xdr_destroy(&xdrs); + return 0; +} diff --git a/test/msan/Linux/sunrpc_bytes.cc b/test/msan/Linux/sunrpc_bytes.cc new file mode 100644 index 000000000000..f0c35746f34d --- /dev/null +++ b/test/msan/Linux/sunrpc_bytes.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_msan -m64 -g -O0 %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <string.h> +#include <rpc/xdr.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + XDR xdrs; + char buf[100]; + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_ENCODE); + char s[20]; +#ifndef UNINIT + strcpy(s, "hello"); +#endif + char *sp = s; + unsigned sz = 6; + bool_t res = xdr_bytes(&xdrs, &sp, &sz, sizeof(s)); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main.*sunrpc_bytes.cc:}}[[@LINE-2]] + assert(res == TRUE); + xdr_destroy(&xdrs); + + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_DECODE); + char s2[20]; + char *sp2 = s2; + unsigned sz2; + res = xdr_bytes(&xdrs, &sp2, &sz2, sizeof(s2)); + assert(res == TRUE); + assert(sz == sz2); + assert(strcmp(s, s2) == 0); + xdr_destroy(&xdrs); + return 0; +} diff --git a/test/msan/Linux/sunrpc_string.cc b/test/msan/Linux/sunrpc_string.cc new file mode 100644 index 000000000000..3f44a96d114c --- /dev/null +++ b/test/msan/Linux/sunrpc_string.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_msan -m64 -g -O0 %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <string.h> +#include <rpc/xdr.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + XDR xdrs; + char buf[100]; + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_ENCODE); + char s[20]; +#ifndef UNINIT + strcpy(s, "hello"); +#endif + char *sp = s; + bool_t res = xdr_string(&xdrs, &sp, sizeof(s)); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main.*sunrpc_string.cc:}}[[@LINE-2]] + assert(res == TRUE); + xdr_destroy(&xdrs); + + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_DECODE); + char s2[20]; + char *sp2 = s2; + res = xdr_string(&xdrs, &sp2, sizeof(s2)); + assert(res == TRUE); + assert(strcmp(s, s2) == 0); + xdr_destroy(&xdrs); + return 0; +} diff --git a/test/msan/Linux/syscalls.cc b/test/msan/Linux/syscalls.cc new file mode 100644 index 000000000000..4dd97e745148 --- /dev/null +++ b/test/msan/Linux/syscalls.cc @@ -0,0 +1,115 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t 2>&1 + +#include <assert.h> +#include <errno.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> + +#include <linux/aio_abi.h> +#include <sys/ptrace.h> +#include <sys/stat.h> +#include <sys/uio.h> + +#include <sanitizer/linux_syscall_hooks.h> +#include <sanitizer/msan_interface.h> + +/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general + sanity of their behaviour. */ + +int main(int argc, char *argv[]) { + char buf[1000]; + const int kTen = 10; + const int kFortyTwo = 42; + memset(buf, 0, sizeof(buf)); + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_pre_recvmsg(0, buf, 0); + __sanitizer_syscall_pre_rt_sigpending(buf, kTen); + __sanitizer_syscall_pre_getdents(0, buf, kTen); + __sanitizer_syscall_pre_getdents64(0, buf, kTen); + + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_post_recvmsg(0, 0, buf, 0); + __sanitizer_syscall_post_rt_sigpending(-1, buf, kTen); + __sanitizer_syscall_post_getdents(0, 0, buf, kTen); + __sanitizer_syscall_post_getdents64(0, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == -1); + + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_post_recvmsg(kTen, 0, buf, 0); + + // Tell the kernel that the output struct size is 10 bytes, verify that those + // bytes are unpoisoned, and the next byte is not. + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_rt_sigpending(0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_getdents(kTen, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_getdents64(kTen, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_getres(0, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_gettime(0, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); + + // Failed syscall does not write to the buffer. + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_gettime(-1, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == 0); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_read(5, 42, buf, 10); + assert(__msan_test_shadow(buf, sizeof(buf)) == 5); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_newfstatat(0, 5, "/path/to/file", buf, 0); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(struct stat)); + + __msan_poison(buf, sizeof(buf)); + int prio = 0; + __sanitizer_syscall_post_mq_timedreceive(kFortyTwo, 5, buf, sizeof(buf), &prio, 0); + assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo); + assert(__msan_test_shadow(&prio, sizeof(prio)) == -1); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_ptrace(0, PTRACE_PEEKUSER, kFortyTwo, 0xABCD, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(void *)); + + __msan_poison(buf, sizeof(buf)); + struct iocb iocb[3]; + struct iocb *iocbp[3] = { &iocb[0], &iocb[1], &iocb[2] }; + memset(iocb, 0, sizeof(iocb)); + iocb[0].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[0].aio_buf = (__u64)buf; + iocb[0].aio_nbytes = 10; + iocb[1].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[1].aio_buf = (__u64)(&buf[20]); + iocb[1].aio_nbytes = 15; + struct iovec vec[2] = { {&buf[40], 3}, {&buf[50], 20} }; + iocb[2].aio_lio_opcode = IOCB_CMD_PREADV; + iocb[2].aio_buf = (__u64)(&vec); + iocb[2].aio_nbytes = 2; + __sanitizer_syscall_pre_io_submit(0, 3, &iocbp); + assert(__msan_test_shadow(buf, sizeof(buf)) == 10); + assert(__msan_test_shadow(buf + 20, sizeof(buf) - 20) == 15); + assert(__msan_test_shadow(buf + 40, sizeof(buf) - 40) == 3); + assert(__msan_test_shadow(buf + 50, sizeof(buf) - 50) == 20); + + __msan_poison(buf, sizeof(buf)); + char *p = buf; + __msan_poison(&p, sizeof(p)); + __sanitizer_syscall_post_io_setup(0, 1, &p); + assert(__msan_test_shadow(&p, sizeof(p)) == -1); + assert(__msan_test_shadow(buf, sizeof(buf)) >= 32); + + return 0; +} diff --git a/test/msan/Linux/tcgetattr.cc b/test/msan/Linux/tcgetattr.cc new file mode 100644 index 000000000000..e1425b84f550 --- /dev/null +++ b/test/msan/Linux/tcgetattr.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <termios.h> +#include <unistd.h> + +int main(int argc, char *argv[]) { + int fd = getpt(); + assert(fd >= 0); + + struct termios t; + int res = tcgetattr(fd, &t); + assert(!res); + + if (t.c_iflag == 0) + exit(0); + return 0; +} diff --git a/test/msan/Linux/xattr.cc b/test/msan/Linux/xattr.cc new file mode 100644 index 000000000000..1beba117d574 --- /dev/null +++ b/test/msan/Linux/xattr.cc @@ -0,0 +1,145 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 + +#include <argz.h> +#include <assert.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sanitizer/msan_interface.h> + +// Do not depend on libattr headers. +#ifndef ENOATTR +#define ENOATTR ENODATA +#endif + +extern "C" { +ssize_t listxattr(const char *path, char *list, size_t size); +ssize_t llistxattr(const char *path, char *list, size_t size); +ssize_t flistxattr(int fd, char *list, size_t size); +ssize_t getxattr(const char *path, const char *name, void *value, size_t size); +ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); +ssize_t fgetxattr(int fd, const char *name, void *value, size_t size); +} + +char g_path[1024]; +int g_fd; + +// Life before closures... +ssize_t listxattr_wrapper(char *buf, size_t size) { + return listxattr(g_path, buf, size); +} + +ssize_t llistxattr_wrapper(char *buf, size_t size) { + return llistxattr(g_path, buf, size); +} + +ssize_t flistxattr_wrapper(char *buf, size_t size) { + return flistxattr(g_fd, buf, size); +} + +ssize_t getxattr_wrapper(const char *name, char *buf, size_t size) { + return getxattr(g_path, name, buf, size); +} + +ssize_t lgetxattr_wrapper(const char *name, char *buf, size_t size) { + return lgetxattr(g_path, name, buf, size); +} + +ssize_t fgetxattr_wrapper(const char *name, char *buf, size_t size) { + return fgetxattr(g_fd, name, buf, size); +} + +size_t test_list(ssize_t fun(char*, size_t), char **buf) { + int buf_size = 1024; + while (true) { + *buf = (char *)malloc(buf_size); + assert(__msan_test_shadow(*buf, buf_size) != -1); + ssize_t res = fun(*buf, buf_size); + if (res >= 0) { + assert(__msan_test_shadow(*buf, buf_size) == res); + return res; + } + if (errno == ENOTSUP) { + printf("Extended attributes are disabled. *xattr test is a no-op.\n"); + exit(0); + } + assert(errno == ERANGE); + free(*buf); + buf_size *= 2; + } +} + +// True means success. False means result inconclusive because we don't have +// access to this attribute. +bool test_get_single_attr(ssize_t fun(const char *, char *, size_t), + const char *attr_name) { + char *buf; + int buf_size = 1024; + while (true) { + buf = (char *)malloc(buf_size); + assert(__msan_test_shadow(buf, buf_size) != -1); + ssize_t res = fun(attr_name, buf, buf_size); + if (res >= 0) { + assert(__msan_test_shadow(buf, buf_size) == res); + free(buf); + return true; + } + if (errno == ENOTSUP) { + printf("Extended attributes are disabled. *xattr test is a no-op.\n"); + exit(0); + } + if (errno == ENOATTR) + return false; + assert(errno == ERANGE); + free(buf); + buf_size *= 2; + } +} + +void test_get(ssize_t fun(const char *, char *, size_t), const char *attr_list, + size_t attr_list_size) { + // Try every attribute, until we see one we can access. Attribute names are + // null-separated strings in attr_list. + size_t attr_list_len = argz_count(attr_list, attr_list_size); + size_t argv_size = (attr_list_len + 1) * sizeof(char *); + char **attrs = (char **)malloc(argv_size); + argz_extract(attr_list, attr_list_size, attrs); + // TODO(smatveev): we need proper argz_* interceptors + __msan_unpoison(attrs, argv_size); + for (size_t i = 0; (i < attr_list_len) && attrs[i]; i++) { + if (test_get_single_attr(fun, attrs[i])) + return; + } + printf("*xattr test could not access any attributes.\n"); +} + +// TODO: set some attributes before trying to retrieve them with *getxattr. +// Currently the list is empty, so *getxattr is not tested. +int main(int argc, char *argv[]) { + assert(argc == 2); + snprintf(g_path, sizeof(g_path), "%s/%s", argv[1], "xattr_test_root/a"); + + g_fd = open(g_path, O_RDONLY); + assert(g_fd); + + char *attr_list; + size_t attr_list_size; + attr_list_size = test_list(listxattr_wrapper, &attr_list); + free(attr_list); + attr_list_size = test_list(llistxattr_wrapper, &attr_list); + free(attr_list); + attr_list_size = test_list(flistxattr_wrapper, &attr_list); + + test_get(getxattr_wrapper, attr_list, attr_list_size); + test_get(lgetxattr_wrapper, attr_list, attr_list_size); + test_get(fgetxattr_wrapper, attr_list, attr_list_size); + + free(attr_list); + return 0; +} diff --git a/test/msan/Linux/xattr_test_root/a b/test/msan/Linux/xattr_test_root/a new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/msan/Linux/xattr_test_root/a diff --git a/test/msan/Unit/lit.site.cfg.in b/test/msan/Unit/lit.site.cfg.in new file mode 100644 index 000000000000..dc0e9613d59e --- /dev/null +++ b/test/msan/Unit/lit.site.cfg.in @@ -0,0 +1,14 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +# Setup config name. +config.name = 'MemorySanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with MSan unit tests. +# FIXME: Don't use hardcoded path to MSan unit tests. +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/msan/tests" +config.test_source_root = config.test_exec_root diff --git a/test/msan/allocator_returns_null.cc b/test/msan/allocator_returns_null.cc new file mode 100644 index 000000000000..f4ea51d58872 --- /dev/null +++ b/test/msan/allocator_returns_null.cc @@ -0,0 +1,81 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_msan -O0 %s -o %t +// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %lx\n", (long)x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: MemorySanitizer's allocator is terminating the process + +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 diff --git a/test/msan/backtrace.cc b/test/msan/backtrace.cc new file mode 100644 index 000000000000..473e0ae8f88b --- /dev/null +++ b/test/msan/backtrace.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +#include <assert.h> +#include <execinfo.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +__attribute__((noinline)) +void f() { + void *buf[10]; + int sz = backtrace(buf, sizeof(buf) / sizeof(*buf)); + assert(sz > 0); + for (int i = 0; i < sz; ++i) + if (!buf[i]) + exit(1); + char **s = backtrace_symbols(buf, sz); + assert(s > 0); + for (int i = 0; i < sz; ++i) + printf("%d\n", strlen(s[i])); +} + +int main(void) { + f(); + return 0; +} diff --git a/test/msan/c-strdup.c b/test/msan/c-strdup.c new file mode 100644 index 000000000000..059300e4205a --- /dev/null +++ b/test/msan/c-strdup.c @@ -0,0 +1,17 @@ +// RUN: %clang_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O3 %s -o %t && %run %t >%t.out 2>&1 + +// Test that strdup in C programs is intercepted. +// GLibC headers translate strdup to __strdup at -O1 and higher. + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char buf[] = "abc"; + char *p = strdup(buf); + if (*p) + exit(0); + return 0; +} diff --git a/test/msan/chained_origin.cc b/test/msan/chained_origin.cc new file mode 100644 index 000000000000..336bbd852cb3 --- /dev/null +++ b/test/msan/chained_origin.cc @@ -0,0 +1,66 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-STACK < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DHEAP=1 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-HEAP < %t.out + + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-STACK < %t.out + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DHEAP=1 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-HEAP < %t.out + + +#include <stdio.h> + +volatile int x, y; + +__attribute__((noinline)) +void fn_g(int a) { + x = a; +} + +__attribute__((noinline)) +void fn_f(int a) { + fn_g(a); +} + +__attribute__((noinline)) +void fn_h() { + y = x; +} + +int main(int argc, char *argv[]) { +#ifdef HEAP + int * volatile zz = new int; + int z = *zz; +#else + int volatile z; +#endif + fn_f(z); + fn_h(); + return y; +} + +// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK: {{#0 .* in main.*chained_origin.cc:47}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK: {{#0 .* in fn_h.*chained_origin.cc:35}} +// CHECK: {{#1 .* in main.*chained_origin.cc:46}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK: {{#0 .* in fn_g.*chained_origin.cc:25}} +// CHECK: {{#1 .* in fn_f.*chained_origin.cc:30}} +// CHECK: {{#2 .* in main.*chained_origin.cc:45}} + +// CHECK-STACK: Uninitialized value was created by an allocation of 'z' in the stack frame of function 'main' +// CHECK-STACK: {{#0 .* in main.*chained_origin.cc:38}} + +// CHECK-HEAP: Uninitialized value was created by a heap allocation +// CHECK-HEAP: {{#1 .* in main.*chained_origin.cc:40}} diff --git a/test/msan/chained_origin_empty_stack.cc b/test/msan/chained_origin_empty_stack.cc new file mode 100644 index 000000000000..36727e3d7aa7 --- /dev/null +++ b/test/msan/chained_origin_empty_stack.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: MSAN_OPTIONS=store_context_size=1 not %run %t 2>&1 | FileCheck %s + +// Test that stack trace for the intermediate store is not empty. + +// CHECK: MemorySanitizer: use-of-uninitialized-value +// CHECK: #0 {{.*}} in main + +// CHECK: Uninitialized value was stored to memory at +// CHECK: #0 {{.*}} in fn_g +// CHECK-NOT: #1 + +// CHECK: Uninitialized value was created by an allocation of 'z' in the stack frame of function 'main' +// CHECK: #0 {{.*}} in main + +#include <stdio.h> + +volatile int x; + +__attribute__((noinline)) +void fn_g(int a) { + x = a; +} + +__attribute__((noinline)) +void fn_f(int a) { + fn_g(a); +} + +int main(int argc, char *argv[]) { + int volatile z; + fn_f(z); + return x; +} diff --git a/test/msan/chained_origin_limits.cc b/test/msan/chained_origin_limits.cc new file mode 100644 index 000000000000..0cc57f32a6ac --- /dev/null +++ b/test/msan/chained_origin_limits.cc @@ -0,0 +1,178 @@ +// This test program creates a very large number of unique histories. + +// Heap origin. +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t + +// RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=2 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK2 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// Stack origin. +// RUN: %clangxx_msan -DSTACK -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t + +// RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=2 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK2 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + + +// Heap origin, with calls. +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t + +// RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=2 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK2 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + + +// Stack origin, with calls. +// RUN: %clangxx_msan -DSTACK -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t + +// RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=2 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK2 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static char *buf, *cur, *end; +void init() { + buf = new char[1000]; +#ifdef STACK + char stackbuf[1000]; + char *volatile p = stackbuf; + memcpy(buf, p, 1000); +#endif + cur = buf; + end = buf + 1000; +} + +void line_flush() { + char *p; + for (p = cur - 1; p >= buf; --p) + if (*p == '\n') + break; + if (p >= buf) { + size_t write_sz = p - buf + 1; + // write(2, buf, write_sz); + memmove(buf, p + 1, end - p - 1); + cur -= write_sz; + } +} + +void buffered_write(const char *p, size_t sz) { + while (sz > 0) { + size_t copy_sz = end - cur; + if (sz < copy_sz) copy_sz = sz; + memcpy(cur, p, copy_sz); + cur += copy_sz; + sz -= copy_sz; + line_flush(); + } +} + +void fn1() { + buffered_write("a\n", 2); +} + +void fn2() { + buffered_write("a\n", 2); +} + +void fn3() { + buffered_write("a\n", 2); +} + +int main(void) { + init(); + for (int i = 0; i < 2000; ++i) { + fn1(); + fn2(); + fn3(); + } + return buf[50]; +} + +// CHECK7: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was created + +// CHECK2: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK2-NOT: Uninitialized value was stored to memory at +// CHECK2: Uninitialized value was stored to memory at +// CHECK2-NOT: Uninitialized value was stored to memory at +// CHECK2: Uninitialized value was created + +// CHECK-PER-STACK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK-PER-STACK: Uninitialized value was stored to memory at +// CHECK-PER-STACK: in fn3 +// CHECK-PER-STACK: Uninitialized value was stored to memory at +// CHECK-PER-STACK: in fn2 +// CHECK-PER-STACK: Uninitialized value was stored to memory at +// CHECK-PER-STACK: in fn1 +// CHECK-PER-STACK: Uninitialized value was created + +// CHECK-UNLIMITED: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was created diff --git a/test/msan/chained_origin_memcpy.cc b/test/msan/chained_origin_memcpy.cc new file mode 100644 index 000000000000..f4c2f7fcac87 --- /dev/null +++ b/test/msan/chained_origin_memcpy.cc @@ -0,0 +1,61 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -DOFFSET=0 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z1 < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DOFFSET=10 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z2 < %t.out + + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -DOFFSET=0 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z1 < %t.out + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DOFFSET=10 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z2 < %t.out + + +#include <stdio.h> +#include <string.h> + +int xx[10000]; +int yy[10000]; +volatile int idx = 30; + +__attribute__((noinline)) +void fn_g(int a, int b) { + xx[idx] = a; xx[idx + 10] = b; +} + +__attribute__((noinline)) +void fn_f(int a, int b) { + fn_g(a, b); +} + +__attribute__((noinline)) +void fn_h() { + memcpy(&yy, &xx, sizeof(xx)); +} + +int main(int argc, char *argv[]) { + int volatile z1; + int volatile z2; + fn_f(z1, z2); + fn_h(); + return yy[idx + OFFSET]; +} + +// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK: {{#0 .* in main .*chained_origin_memcpy.cc:46}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK: {{#1 .* in fn_h.*chained_origin_memcpy.cc:38}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK: {{#0 .* in fn_g.*chained_origin_memcpy.cc:28}} +// CHECK: {{#1 .* in fn_f.*chained_origin_memcpy.cc:33}} + +// CHECK-Z1: Uninitialized value was created by an allocation of 'z1' in the stack frame of function 'main' +// CHECK-Z2: Uninitialized value was created by an allocation of 'z2' in the stack frame of function 'main' +// CHECK: {{#0 .* in main.*chained_origin_memcpy.cc:41}} diff --git a/test/msan/chained_origin_with_signals.cc b/test/msan/chained_origin_with_signals.cc new file mode 100644 index 000000000000..2841e34a1f1d --- /dev/null +++ b/test/msan/chained_origin_with_signals.cc @@ -0,0 +1,36 @@ +// Check that stores in signal handlers are not recorded in origin history. +// This is, in fact, undesired behavior caused by our chained origins +// implementation being not async-signal-safe. + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <signal.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +volatile int x, y; + +void SignalHandler(int signo) { + y = x; +} + +int main(int argc, char *argv[]) { + int volatile z; + x = z; + + signal(SIGHUP, SignalHandler); + kill(getpid(), SIGHUP); + signal(SIGHUP, SIG_DFL); + + return y; +} + +// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK-NOT: in SignalHandler diff --git a/test/msan/check_mem_is_initialized.cc b/test/msan/check_mem_is_initialized.cc new file mode 100644 index 000000000000..7d2328810d90 --- /dev/null +++ b/test/msan/check_mem_is_initialized.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <sanitizer/msan_interface.h> +#include <stdlib.h> + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*check_mem_is_initialized.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by a heap allocation + // CHECK-ORIGINS: {{#0 0x.* in .*malloc}} + // CHECK-ORIGINS: {{#1 0x.* in main .*check_mem_is_initialized.cc:}}[[@LINE-8]] + return 0; +} diff --git a/test/msan/coverage-levels.cc b/test/msan/coverage-levels.cc new file mode 100644 index 000000000000..7c2e143d3ab8 --- /dev/null +++ b/test/msan/coverage-levels.cc @@ -0,0 +1,28 @@ +// Test various levels of coverage +// +// RUN: %clangxx_msan -DINIT_VAR=1 -O1 -fsanitize-coverage=1 %s -o %t +// RUN: mkdir -p %T/coverage-levels +// RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_NOWARN +// RUN: %clangxx_msan -O1 -fsanitize-coverage=1 %s -o %t +// RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_WARN +// RUN: %clangxx_msan -O1 -fsanitize-coverage=2 %s -o %t +// RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 --check-prefix=CHECK_WARN +// RUN: %clangxx_msan -O1 -fsanitize-coverage=3 %s -o %t +// RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 --check-prefix=CHECK_WARN +// +volatile int sink; +int main(int argc, char **argv) { + int var; +#if INIT_VAR + var = 0; +#endif + if (argc == 0) + sink = 0; + return *(volatile int*)&var; +} + +// CHECK_WARN: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK_NOWARN-NOT: ERROR +// CHECK1: 1 PCs written +// CHECK2: 2 PCs written +// CHECK3: 3 PCs written diff --git a/test/msan/cxa_atexit.cc b/test/msan/cxa_atexit.cc new file mode 100644 index 000000000000..0aa36ecee011 --- /dev/null +++ b/test/msan/cxa_atexit.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p + +// PR17377: C++ module destructors get stale argument shadow. + +#include <stdio.h> +#include <stdlib.h> +class A { +public: + // This destructor get stale argument shadow left from the call to f(). + ~A() { + if (this) + exit(0); + } +}; + +A a; + +__attribute__((noinline)) +void f(long x) { +} + +int main(void) { + long x; + long * volatile p = &x; + // This call poisons TLS shadow for the first function argument. + f(*p); + return 0; +} diff --git a/test/msan/death-callback.cc b/test/msan/death-callback.cc new file mode 100644 index 000000000000..6d0488339998 --- /dev/null +++ b/test/msan/death-callback.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_msan -m64 -DERROR %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB +// RUN: %clangxx_msan -m64 -DERROR -DMSANCB_SET %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-CB +// RUN: %clangxx_msan -m64 -DERROR -DMSANCB_SET -DMSANCB_CLEAR %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB +// RUN: %clangxx_msan -m64 -DMSANCB_SET %s -o %t && %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB + +#include <sanitizer/msan_interface.h> +#include <stdio.h> +#include <stdlib.h> + +void cb(void) { + fprintf(stderr, "msan-death-callback\n"); +} + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + *p = 42; + free(p); + +#ifdef MSANCB_SET + __msan_set_death_callback(cb); +#endif + +#ifdef MSANCB_CLEAR + __msan_set_death_callback(0); +#endif + +#ifdef ERROR + if (*p) + exit(0); +#endif + // CHECK-CB: msan-death-callback + // CHECK-NOCB-NOT: msan-death-callback + fprintf(stderr, "done\n"); + return 0; +} diff --git a/test/msan/default_blacklist.cc b/test/msan/default_blacklist.cc new file mode 100644 index 000000000000..32cc02257cb0 --- /dev/null +++ b/test/msan/default_blacklist.cc @@ -0,0 +1,3 @@ +// Test that MSan uses the default blacklist from resource directory. +// RUN: %clangxx_msan -### %s 2>&1 | FileCheck %s +// CHECK: fsanitize-blacklist={{.*}}msan_blacklist.txt diff --git a/test/msan/dlerror.cc b/test/msan/dlerror.cc new file mode 100644 index 000000000000..2c726d36041e --- /dev/null +++ b/test/msan/dlerror.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> + +int main(void) { + void *p = dlopen("/bad/file/name", RTLD_NOW); + assert(!p); + char *s = dlerror(); + printf("%s, %zu\n", s, strlen(s)); + return 0; +} diff --git a/test/msan/dso-origin.cc b/test/msan/dso-origin.cc new file mode 100644 index 000000000000..ba008c00718d --- /dev/null +++ b/test/msan/dso-origin.cc @@ -0,0 +1,48 @@ +// Build a library with origin tracking and an executable w/o origin tracking. +// Test that origin tracking is enabled at runtime. +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_msan -m64 -O0 %s %t-so.so -o %t && not %run %t 2>&1 | FileCheck %s + +#ifdef BUILD_SO + +#include <stdlib.h> + +extern "C" { +void my_access(int *p) { + volatile int tmp; + // Force initialize-ness check. + if (*p) + tmp = 1; +} + +void *my_alloc(unsigned sz) { + return malloc(sz); +} +} // extern "C" + +#else // BUILD_SO + +#include <stdlib.h> + +extern "C" { +void my_access(int *p); +void *my_alloc(unsigned sz); +} + +int main(int argc, char **argv) { + int *x = (int *)my_alloc(sizeof(int)); + my_access(x); + delete x; + + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in my_access .*dso-origin.cc:}} + // CHECK: {{#1 0x.* in main .*dso-origin.cc:}}[[@LINE-5]] + // CHECK: Uninitialized value was created by a heap allocation + // CHECK: {{#0 0x.* in .*malloc}} + // CHECK: {{#1 0x.* in my_alloc .*dso-origin.cc:}} + // CHECK: {{#2 0x.* in main .*dso-origin.cc:}}[[@LINE-10]] + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*dso-origin.cc:.* my_access}} + return 0; +} + +#endif // BUILD_SO diff --git a/test/msan/dtls_test.c b/test/msan/dtls_test.c new file mode 100644 index 000000000000..cb88ede2f0a6 --- /dev/null +++ b/test/msan/dtls_test.c @@ -0,0 +1,60 @@ +/* RUN: %clang_msan -g -m64 %s -o %t + RUN: %clang_msan -g -m64 %s -DBUILD_SO -fPIC -o %t-so.so -shared + RUN: %run %t 2>&1 + + Regression test for a bug in msan/glibc integration, + see https://sourceware.org/bugzilla/show_bug.cgi?id=16291 + and https://code.google.com/p/memory-sanitizer/issues/detail?id=44 +*/ + +#ifndef BUILD_SO +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +typedef long *(* get_t)(); +get_t GetTls; +void *Thread1(void *unused) { + long uninitialized; + long *x = GetTls(); + if (*x) + fprintf(stderr, "bar\n"); + *x = uninitialized; + fprintf(stderr, "stack: %p dtls: %p\n", &x, x); + return 0; +} + +void *Thread2(void *unused) { + long *x = GetTls(); + fprintf(stderr, "stack: %p dtls: %p\n", &x, x); + if (*x) + fprintf(stderr, "foo\n"); // False negative here. + return 0; +} + +int main(int argc, char *argv[]) { + char path[4096]; + snprintf(path, sizeof(path), "%s-so.so", argv[0]); + int i; + + void *handle = dlopen(path, RTLD_LAZY); + if (!handle) fprintf(stderr, "%s\n", dlerror()); + assert(handle != 0); + GetTls = (get_t)dlsym(handle, "GetTls"); + assert(dlerror() == 0); + + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + pthread_join(t, 0); + pthread_create(&t, 0, Thread2, 0); + pthread_join(t, 0); + return 0; +} +#else // BUILD_SO +__thread long huge_thread_local_array[1 << 17]; +long *GetTls() { + return &huge_thread_local_array[0]; +} +#endif diff --git a/test/msan/errno.cc b/test/msan/errno.cc new file mode 100644 index 000000000000..8af8eb5ee6f9 --- /dev/null +++ b/test/msan/errno.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +int main() +{ + int x; + int *volatile p = &x; + errno = *p; + int res = read(-1, 0, 0); + assert(res == -1); + if (errno) printf("errno %d\n", errno); + return 0; +} diff --git a/test/msan/fork.cc b/test/msan/fork.cc new file mode 100644 index 000000000000..10de8a9379d8 --- /dev/null +++ b/test/msan/fork.cc @@ -0,0 +1,121 @@ +// Test that chained origins are fork-safe. +// Run a number of threads that create new chained origins, then fork +// and verify that origin reads do not deadlock in the child process. + +// RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins=2 -g -m64 -O3 %s -o %t +// RUN: MSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t |& FileCheck %s + +// Fun fact: if test output is redirected to a file (as opposed to +// being piped directly to FileCheck), we may lose some "done"s due to +// a kernel bug: +// https://lkml.org/lkml/2014/2/17/324 + + +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <signal.h> +#include <errno.h> + +#include <sanitizer/msan_interface.h> + +int done; + +void copy_uninit_thread2() { + volatile int x; + volatile int v; + while (true) { + v = x; + x = v; + if (__atomic_load_n(&done, __ATOMIC_RELAXED)) + return; + } +} + +void copy_uninit_thread1(int level) { + if (!level) + copy_uninit_thread2(); + else + copy_uninit_thread1(level - 1); +} + +void *copy_uninit_thread(void *id) { + copy_uninit_thread1((long)id); + return 0; +} + +// Run through stackdepot in the child process. +// If any of the hash table cells are locked, this may deadlock. +void child() { + volatile int x; + volatile int v; + for (int i = 0; i < 10000; ++i) { + v = x; + x = v; + } + write(2, "done\n", 5); +} + +void test() { + const int kThreads = 10; + pthread_t t[kThreads]; + for (int i = 0; i < kThreads; ++i) + pthread_create(&t[i], NULL, copy_uninit_thread, (void*)(long)i); + usleep(100000); + pid_t pid = fork(); + if (pid) { + // parent + __atomic_store_n(&done, 1, __ATOMIC_RELAXED); + pid_t p; + while ((p = wait(NULL)) == -1) { } + } else { + // child + child(); + } +} + +int main() { + const int kChildren = 20; + for (int i = 0; i < kChildren; ++i) { + pid_t pid = fork(); + if (pid) { + // parent + } else { + test(); + exit(0); + } + } + + for (int i = 0; i < kChildren; ++i) { + pid_t p; + while ((p = wait(NULL)) == -1) { } + } + + return 0; +} + +// Expect 20 (== kChildren) "done" messages. +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done diff --git a/test/msan/ftime.cc b/test/msan/ftime.cc new file mode 100644 index 000000000000..2d0935d18037 --- /dev/null +++ b/test/msan/ftime.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <sys/timeb.h> + +#include <sanitizer/msan_interface.h> + +int main(void) { + struct timeb tb; + int res = ftime(&tb); + assert(!res); + assert(__msan_test_shadow(&tb, sizeof(tb)) == -1); + return 0; +} diff --git a/test/msan/getaddrinfo-positive.cc b/test/msan/getaddrinfo-positive.cc new file mode 100644 index 000000000000..7658cd504dba --- /dev/null +++ b/test/msan/getaddrinfo-positive.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdlib.h> + +volatile int z; + +int main(void) { + struct addrinfo *ai; + struct addrinfo hint; + int res = getaddrinfo("localhost", NULL, NULL, &ai); + if (ai) z = 1; // OK + res = getaddrinfo("localhost", NULL, &hint, &ai); + // CHECK: Uninitialized bytes in __interceptor_getaddrinfo at offset 0 inside [0x{{.*}}, 48) + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 {{.*}} in main {{.*}}getaddrinfo-positive.cc:[[@LINE-3]] + return 0; +} diff --git a/test/msan/getaddrinfo.cc b/test/msan/getaddrinfo.cc new file mode 100644 index 000000000000..abab8bd78f75 --- /dev/null +++ b/test/msan/getaddrinfo.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdlib.h> + +void poison_stack_ahead() { + char buf[100000]; + // With -O0 this poisons a large chunk of stack. +} + +int main(void) { + poison_stack_ahead(); + + struct addrinfo *ai; + + // This should trigger loading of libnss_dns and friends. + // Those libraries are typically uninstrumented.They will call strlen() on a + // stack-allocated buffer, which is very likely to be poisoned. Test that we + // don't report this as an UMR. + int res = getaddrinfo("not-in-etc-hosts", NULL, NULL, &ai); + return 0; +} diff --git a/test/msan/getc_unlocked.c b/test/msan/getc_unlocked.c new file mode 100644 index 000000000000..7df958ad657a --- /dev/null +++ b/test/msan/getc_unlocked.c @@ -0,0 +1,32 @@ +// RUN: %clangxx_msan -DGETC -m64 -O0 -g -xc++ %s -o %t && %run %t +// RUN: %clangxx_msan -DGETC -m64 -O3 -g -xc++ %s -o %t && %run %t +// RUN: %clang_msan -DGETC -m64 -O0 -g %s -o %t && %run %t +// RUN: %clang_msan -DGETC -m64 -O3 -g %s -o %t && %run %t + +// RUN: %clangxx_msan -DGETCHAR -m64 -O0 -g -xc++ %s -o %t && %run %t +// RUN: %clangxx_msan -DGETCHAR -m64 -O3 -g -xc++ %s -o %t && %run %t +// RUN: %clang_msan -DGETCHAR -m64 -O0 -g %s -o %t && %run %t +// RUN: %clang_msan -DGETCHAR -m64 -O3 -g %s -o %t && %run %t + +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +int main() { + FILE *stream = fopen("/dev/zero", "r"); + flockfile (stream); + int c; +#if defined(GETCHAR) + int res = dup2(fileno(stream), 0); + assert(res == 0); + c = getchar_unlocked(); +#elif defined(GETC) + c = getc_unlocked (stream); +#endif + funlockfile (stream); + if (c == EOF) + return 1; + printf("%c\n", (char)c); + fclose(stream); + return 0; +} diff --git a/test/msan/getline.cc b/test/msan/getline.cc new file mode 100644 index 000000000000..51e105e0be5c --- /dev/null +++ b/test/msan/getline.cc @@ -0,0 +1,36 @@ +// RUN: echo "abcde" > %t-testdata +// RUN: echo "12345" >> %t-testdata +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %t-testdata +// RUN: %clangxx_msan -O2 %s -o %t && %run %t %t-testdata +// RUN: %clang_msan -O0 -xc %s -o %t && %run %t %t-testdata +// RUN: %clang_msan -O2 -xc %s -o %t && %run %t %t-testdata +// RUN: %clang_msan -O0 -xc -D_GNU_SOURCE=1 %s -o %t && %run %t %t-testdata +// RUN: %clang_msan -O2 -xc -D_GNU_SOURCE=1 %s -o %t && %run %t %t-testdata + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + assert(argc == 2); + printf("%s\n", argv[1]); + + FILE *fp = fopen(argv[1], "r"); + assert(fp); + + char *line = 0; + size_t len = 0; + int n = getline(&line, &len, fp); + assert(n == 6); + assert(strcmp(line, "abcde\n") == 0); + + n = getline(&line, &len, fp); + assert(n == 6); + assert(strcmp(line, "12345\n") == 0); + + free(line); + fclose(fp); + + return 0; +} diff --git a/test/msan/heap-origin.cc b/test/msan/heap-origin.cc new file mode 100644 index 000000000000..0920001beed7 --- /dev/null +++ b/test/msan/heap-origin.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <stdlib.h> +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + return *x; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*heap-origin.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by a heap allocation + // CHECK-ORIGINS: {{#0 0x.* in .*malloc}} + // CHECK-ORIGINS: {{#1 0x.* in main .*heap-origin.cc:}}[[@LINE-7]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*heap-origin.cc:.* main}} +} diff --git a/test/msan/iconv.cc b/test/msan/iconv.cc new file mode 100644 index 000000000000..ea6958b79b96 --- /dev/null +++ b/test/msan/iconv.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t && not %run %t |& FileCheck %s + +#include <assert.h> +#include <iconv.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +int main(void) { + iconv_t cd = iconv_open("ASCII", "ASCII"); + assert(cd != (iconv_t)-1); + + char inbuf_[100]; + strcpy(inbuf_, "sample text"); + char outbuf_[100]; + char *inbuf = inbuf_; + char *outbuf = outbuf_; + size_t inbytesleft = strlen(inbuf_); + size_t outbytesleft = sizeof(outbuf_); + +#ifdef POSITIVE + { + char u; + char *volatile p = &u; + inbuf_[5] = *p; + } +#endif + + size_t res; + res = iconv(cd, 0, 0, 0, 0); + assert(res != (size_t)-1); + + res = iconv(cd, 0, 0, &outbuf, &outbytesleft); + assert(res != (size_t)-1); + + res = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 {{.*}} in main {{.*}}iconv.cc:[[@LINE-2]] + assert(res != (size_t)-1); + assert(inbytesleft == 0); + + assert(memcmp(inbuf_, outbuf_, strlen(inbuf_)) == 0); + + iconv_close(cd); + return 0; +} diff --git a/test/msan/if_indextoname.cc b/test/msan/if_indextoname.cc new file mode 100644 index 000000000000..b74aec16c98a --- /dev/null +++ b/test/msan/if_indextoname.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t 2>&1 + +#include <assert.h> +#include <errno.h> +#include <net/if.h> +#include <stdio.h> +#include <string.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + char ifname[IF_NAMESIZE + 1]; + assert(0 == __msan_test_shadow(ifname, sizeof(ifname))); + if (!if_indextoname(1, ifname)) { + assert(errno == ENXIO); + printf("No network interfaces found.\n"); + return 0; + } + assert(strlen(ifname) + 1 == __msan_test_shadow(ifname, sizeof(ifname))); + return 0; +} diff --git a/test/msan/ifaddrs.cc b/test/msan/ifaddrs.cc new file mode 100644 index 000000000000..6a0db3a5b71f --- /dev/null +++ b/test/msan/ifaddrs.cc @@ -0,0 +1,50 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 + +#include <assert.h> +#include <errno.h> +#include <ifaddrs.h> +#include <stdio.h> +#include <string.h> + +#include <vector> + +#include <sanitizer/msan_interface.h> + +#define CHECK_AND_PUSH(addr, size) \ + if (addr) { \ + assert(-1 == __msan_test_shadow(addr, sizeof(size))); \ + ranges.push_back(std::make_pair((void *)addr, (size_t)size)); \ + } + +int main(int argc, char *argv[]) { + struct ifaddrs *ifas; + + assert(0 == __msan_test_shadow(&ifas, sizeof(ifaddrs *))); + int res = getifaddrs(&ifas); + if (res == -1) { + assert(errno == ENOSYS); + printf("getifaddrs() is not implemented\n"); + return 0; + } + assert(res == 0); + assert(-1 == __msan_test_shadow(&ifas, sizeof(ifaddrs *))); + + std::vector<std::pair<void *, size_t> > ranges; + ifaddrs *p = ifas; + while (p) { + CHECK_AND_PUSH(p, sizeof(ifaddrs)); + CHECK_AND_PUSH(p->ifa_name, strlen(p->ifa_name) + 1); + CHECK_AND_PUSH(p->ifa_addr, sizeof(*p->ifa_addr)); + CHECK_AND_PUSH(p->ifa_netmask, sizeof(*p->ifa_netmask)); + CHECK_AND_PUSH(p->ifa_broadaddr, sizeof(*p->ifa_broadaddr)); + CHECK_AND_PUSH(p->ifa_dstaddr, sizeof(*p->ifa_dstaddr)); + p = p->ifa_next; + } + + freeifaddrs(ifas); + for (int i = 0; i < ranges.size(); i++) + assert(0 == __msan_test_shadow(ranges[i].first, ranges[i].second)); + return 0; +} diff --git a/test/msan/initgroups.cc b/test/msan/initgroups.cc new file mode 100644 index 000000000000..94f6cd8252f3 --- /dev/null +++ b/test/msan/initgroups.cc @@ -0,0 +1,11 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +#include <sys/types.h> +#include <grp.h> + +int main(void) { + initgroups("root", 0); + // The above fails unless you are root. Does not matter, MSan false positive + // (which we are testing for) happens anyway. + return 0; +} diff --git a/test/msan/inline.cc b/test/msan/inline.cc new file mode 100644 index 000000000000..b2fa96150856 --- /dev/null +++ b/test/msan/inline.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -O3 %s -o %t && %run %t + +// Test that no_sanitize_memory attribute applies even when the function would +// be normally inlined. + +#include <stdlib.h> + +__attribute__((no_sanitize_memory)) +int f(int *p) { + if (*p) // BOOOM?? Nope! + exit(0); + return 0; +} + +int main(int argc, char **argv) { + int x; + int * volatile p = &x; + int res = f(p); + return 0; +} diff --git a/test/msan/insertvalue_origin.cc b/test/msan/insertvalue_origin.cc new file mode 100644 index 000000000000..545debffaad5 --- /dev/null +++ b/test/msan/insertvalue_origin.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +// Test origin propagation through insertvalue IR instruction. + +#include <stdio.h> +#include <stdint.h> + +struct mypair { + int64_t x; + int y; +}; + +mypair my_make_pair(int64_t x, int y) { + mypair p; + p.x = x; + p.y = y; + return p; +} + +int main() { + int64_t * volatile p = new int64_t; + mypair z = my_make_pair(*p, 0); + if (z.x) + printf("zzz\n"); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main .*insertvalue_origin.cc:}}[[@LINE-3]] + + // CHECK: Uninitialized value was created by a heap allocation + // CHECK: {{in main .*insertvalue_origin.cc:}}[[@LINE-8]] + delete p; + return 0; +} diff --git a/test/msan/ioctl.cc b/test/msan/ioctl.cc new file mode 100644 index 000000000000..caa5c274f5eb --- /dev/null +++ b/test/msan/ioctl.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %t + +#include <assert.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + unsigned int z; + int res = ioctl(fd, FIOGETOWN, &z); + assert(res == 0); + close(fd); + if (z) + exit(0); + return 0; +} diff --git a/test/msan/ioctl_custom.cc b/test/msan/ioctl_custom.cc new file mode 100644 index 000000000000..7c7fde4bd5d0 --- /dev/null +++ b/test/msan/ioctl_custom.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %t + +// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 -g %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -DPOSITIVE -m64 -O3 -g %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdlib.h> +#include <net/if.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + + struct ifreq ifreqs[20]; + struct ifconf ifc; + ifc.ifc_ifcu.ifcu_req = ifreqs; +#ifndef POSITIVE + ifc.ifc_len = sizeof(ifreqs); +#endif + int res = ioctl(fd, SIOCGIFCONF, (void *)&ifc); + // CHECK: Uninitialized bytes in ioctl{{.*}} at offset 0 inside [0x{{.*}}, 4) + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #{{.*}} in main {{.*}}ioctl_custom.cc:[[@LINE-3]] + assert(res == 0); + for (int i = 0; i < ifc.ifc_len / sizeof(*ifc.ifc_ifcu.ifcu_req); ++i) + printf("%d %zu %s\n", i, strlen(ifreqs[i].ifr_name), ifreqs[i].ifr_name); + return 0; +} diff --git a/test/msan/ioctl_sound.cc b/test/msan/ioctl_sound.cc new file mode 100644 index 000000000000..ed896f761181 --- /dev/null +++ b/test/msan/ioctl_sound.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %t + +#include <assert.h> +#include <fcntl.h> +#include <sound/asound.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char **argv) { + int fd = open("/dev/snd/controlC0", O_RDONLY); + if (fd < 0) { + printf("Unable to open sound device."); + return 0; + } + const unsigned sz = sizeof(snd_ctl_card_info); + void *info = malloc(sz + 1); + assert(__msan_test_shadow(info, sz) == 0); + assert(ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, info) >= 0); + assert(__msan_test_shadow(info, sz + 1) == sz); + close(fd); + free(info); + return 0; +} diff --git a/test/msan/keep-going-dso.cc b/test/msan/keep-going-dso.cc new file mode 100644 index 000000000000..7975306c557f --- /dev/null +++ b/test/msan/keep-going-dso.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// Test how -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going affect reports +// from interceptors. +// -mllvm -msan-keep-going provides the default value of keep_going flag, but is +// always overwritten by MSAN_OPTIONS + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + x[4] = 0; + if (strlen(x) < 3) + exit(0); + fprintf(stderr, "Done\n"); + // CHECK-NOT: Done + // CHECK-KEEP-GOING: Done + return 0; +} diff --git a/test/msan/keep-going.cc b/test/msan/keep-going.cc new file mode 100644 index 000000000000..6426574a9e50 --- /dev/null +++ b/test/msan/keep-going.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out + +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=1 not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=0 not %run %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// Test behaviour of -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going. +// -mllvm -msan-keep-going provides the default value of keep_going flag; value +// of 1 can be overwritten by MSAN_OPTIONS, value of 0 can not. + +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + if (x[0]) + exit(0); + fprintf(stderr, "Done\n"); + // CHECK-NOT: Done + // CHECK-KEEP-GOING: Done + return 0; +} diff --git a/test/msan/lit.cfg b/test/msan/lit.cfg new file mode 100644 index 000000000000..f425e25957ac --- /dev/null +++ b/test/msan/lit.cfg @@ -0,0 +1,30 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'MemorySanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup default compiler flags used with -fsanitize=memory option. +clang_msan_cflags = ["-fsanitize=memory", + "-mno-omit-leaf-frame-pointer", + "-fno-omit-frame-pointer", + "-fno-optimize-sibling-calls", + "-m64"] + config.debug_info_flags +clang_msan_cxxflags = config.cxx_mode_flags + clang_msan_cflags + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang_msan ", build_invocation(clang_msan_cflags)) ) +config.substitutions.append( ("%clangxx_msan ", build_invocation(clang_msan_cxxflags)) ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# MemorySanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/msan/lit.site.cfg.in b/test/msan/lit.site.cfg.in new file mode 100644 index 000000000000..fb22a57a9e66 --- /dev/null +++ b/test/msan/lit.site.cfg.in @@ -0,0 +1,5 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@MSAN_LIT_SOURCE_DIR@/lit.cfg") diff --git a/test/msan/mallinfo.cc b/test/msan/mallinfo.cc new file mode 100644 index 000000000000..3f308683077e --- /dev/null +++ b/test/msan/mallinfo.cc @@ -0,0 +1,12 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <malloc.h> + +#include <sanitizer/msan_interface.h> + +int main(void) { + struct mallinfo mi = mallinfo(); + assert(__msan_test_shadow(&mi, sizeof(mi)) == -1); + return 0; +} diff --git a/test/msan/mktime.cc b/test/msan/mktime.cc new file mode 100644 index 000000000000..c419057c3907 --- /dev/null +++ b/test/msan/mktime.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -g -DUNINIT %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <time.h> + +#include <sanitizer/msan_interface.h> + +int main(void) { + struct tm tm; + tm.tm_year = 2014; + tm.tm_mon = 3; + tm.tm_mday = 28; +#ifndef UNINIT + tm.tm_hour = 13; +#endif + tm.tm_min = 4; + tm.tm_sec = 42; + tm.tm_isdst = -1; + time_t t = mktime(&tm); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: in main{{.*}}mktime.cc:[[@LINE-2]] + assert(t != -1); + assert(__msan_test_shadow(&tm, sizeof(tm)) == -1); + return 0; +} diff --git a/test/msan/mmap_below_shadow.cc b/test/msan/mmap_below_shadow.cc new file mode 100644 index 000000000000..4b5890ba0fb8 --- /dev/null +++ b/test/msan/mmap_below_shadow.cc @@ -0,0 +1,28 @@ +// Test mmap behavior when map address is below shadow range. +// With MAP_FIXED, we return EINVAL. +// Without MAP_FIXED, we ignore the address hint and map somewhere in +// application range. + +// RUN: %clangxx_msan -m64 -O0 -DFIXED=0 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=1 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=1 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t + +#include <assert.h> +#include <errno.h> +#include <stdint.h> +#include <sys/mman.h> + +int main(void) { + // Hint address just below shadow. + uintptr_t hint = 0x4f0000000000ULL; + const uintptr_t app_start = 0x600000000000ULL; + uintptr_t p = (uintptr_t)mmap( + (void *)hint, 4096, PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | (FIXED ? MAP_FIXED : 0), -1, 0); + if (FIXED) + assert(p == (uintptr_t)-1 && errno == EINVAL); + else + assert(p >= app_start); + return 0; +} diff --git a/test/msan/msan_check_mem_is_initialized.cc b/test/msan/msan_check_mem_is_initialized.cc new file mode 100644 index 000000000000..33558cd2feb2 --- /dev/null +++ b/test/msan/msan_check_mem_is_initialized.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK +// RUN: MSAN_OPTIONS=verbosity=1 not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VERBOSE + +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <sanitizer/msan_interface.h> + +int main(void) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p, 10); + __msan_check_mem_is_initialized(p + 12, 30); +#ifdef POSITIVE + __msan_check_mem_is_initialized(p + 5, 20); + // CHECK: Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside [0x{{.*}}, 20) + // CHECK-VERBOSE: Shadow map of [0x{{.*}}, 0x{{.*}}), 20 bytes: + // CHECK-VERBOSE: 0x{{.*}}: ..000000 0000ffff 00000000 00000000 + // CHECK-VERBOSE: 0x{{.*}}: 00000000 00...... ........ ........ + + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 0x{{.*}}in main{{.*}}msan_check_mem_is_initialized.cc:[[@LINE-7]] +#endif + return 0; +} + diff --git a/test/msan/msan_dump_shadow.cc b/test/msan/msan_dump_shadow.cc new file mode 100644 index 000000000000..08371e306f32 --- /dev/null +++ b/test/msan/msan_dump_shadow.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <sanitizer/msan_interface.h> + +int main(void) { + char *p = new char[16]; + __msan_dump_shadow(p, 5); + delete[] p; + const char *q = "abc"; + __msan_dump_shadow(q, 3); + return 0; +} + +// CHECK: ff ff ff ff ff +// CHECK: 00 00 00 diff --git a/test/msan/msan_print_shadow.cc b/test/msan/msan_print_shadow.cc new file mode 100644 index 000000000000..0cc1d660be1b --- /dev/null +++ b/test/msan/msan_print_shadow.cc @@ -0,0 +1,122 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NO-ORIGINS < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS --check-prefix=CHECK-ORIGINS-2 < %t.out + +#include <sanitizer/msan_interface.h> + +int main(void) { + char volatile x; + char *p = new char[320]; + p[2] = p[5] = 1; + p[8] = p[9] = p[10] = p[11] = p[12] = 2; + + __msan_allocated_memory(p + 4*3, 4); + __msan_allocated_memory(p + 4*4, 4); + __msan_allocated_memory(p + 4*5, 4); + __msan_allocated_memory(p + 4*6, 4); + __msan_allocated_memory(p + 4*7, 4); + __msan_allocated_memory(p + 4*8, 4); + __msan_allocated_memory(p + 4*9, 4); + __msan_allocated_memory(p + 4*10, 4); + __msan_allocated_memory(p + 4*11, 4); + __msan_allocated_memory(p + 4*12, 4); + __msan_allocated_memory(p + 4*13, 4); + __msan_allocated_memory(p + 4*14, 4); + __msan_allocated_memory(p + 4*15, 4); + __msan_allocated_memory(p + 4*16, 4); + __msan_allocated_memory(p + 4*17, 4); + __msan_allocated_memory(p + 4*18, 4); + __msan_allocated_memory(p + 4*19, 4); + __msan_allocated_memory(p + 4*20, 4); + __msan_allocated_memory(p + 4*21, 4); + __msan_allocated_memory(p + 4*22, 4); + __msan_allocated_memory(p + 4*23, 4); + __msan_allocated_memory(p + 4*24, 4); + __msan_allocated_memory(p + 4*25, 4); + __msan_allocated_memory(p + 4*26, 4); + __msan_allocated_memory(p + 4*27, 4); + __msan_allocated_memory(p + 4*28, 4); + __msan_allocated_memory(p + 4*29, 4); + __msan_allocated_memory(p + 4*30, 4); + __msan_allocated_memory(p + 4*31, 4); + + p[19] = x; + + __msan_print_shadow(p+5, 297); + delete[] p; + return 0; +} + +// CHECK: Shadow map of [{{.*}}), 297 bytes: + +// CHECK-NO-ORIGINS: 0x{{.*}}: ..00ffff 00000000 ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffff.... ........ + +// CHECK-ORIGINS: 0x{{.*}}: ..00ffff 00000000 ffffffff ffffffff |A . B C| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |D E F G| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |H I J K| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |L M N O| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |P Q R S| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |T U V W| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |X Y Z *| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |* * * A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffff.... ........ |A A A .| + +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): +// CHECK-ORIGINS: Uninitialized value was created by a heap allocation +// CHECK-ORIGINS: #1 {{.*}} in main{{.*}}msan_print_shadow.cc:14 + +// CHECK-ORIGINS: Origin B (origin_id {{.*}}): +// CHECK-ORIGINS: Uninitialized value was created by a heap allocation +// CHECK-ORIGINS: #0 {{.*}} in __msan_allocated_memory +// CHECK-ORIGINS: #1 {{.*}} in main{{.*}}msan_print_shadow.cc:18 + +// CHECK-ORIGINS: Origin C (origin_id {{.*}}): +// CHECK-ORIGINS-2: Uninitialized value was stored to memory at +// CHECK-ORIGINS-2: #0 {{.*}} in main{{.*}}msan_print_shadow.cc:48 +// CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' +// CHECK-ORIGINS: #0 {{.*}} in main{{.*}}msan_print_shadow.cc:12 + +// CHECK-ORIGINS: Origin D (origin_id {{.*}}): +// CHECK-ORIGINS: Uninitialized value was created by a heap allocation +// CHECK-ORIGINS: #0 {{.*}} in __msan_allocated_memory +// CHECK-ORIGINS: #1 {{.*}} in main{{.*}}msan_print_shadow.cc:20 + +// ... + +// CHECK-ORIGINS: Origin Z (origin_id {{.*}}): +// CHECK-ORIGINS: Uninitialized value was created by a heap allocation +// CHECK-ORIGINS: #0 {{.*}} in __msan_allocated_memory +// CHECK-ORIGINS: #1 {{.*}} in main{{.*}}msan_print_shadow.cc:42 diff --git a/test/msan/msan_print_shadow2.cc b/test/msan/msan_print_shadow2.cc new file mode 100644 index 000000000000..343ee9e5c3de --- /dev/null +++ b/test/msan/msan_print_shadow2.cc @@ -0,0 +1,49 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NO-ORIGINS < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out + +#include <sanitizer/msan_interface.h> + +int main(void) { + char *p = new char[16]; + __msan_print_shadow(p, 1); + __msan_print_shadow(p+1, 1); + __msan_print_shadow(p+3, 1); + __msan_print_shadow(p+15, 1); + __msan_print_shadow(p, 0); + delete[] p; + int x = 0; + __msan_print_shadow(&x, 3); + return 0; +} + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 1 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: ff...... ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: ff...... ........ ........ ........ |A . . .| +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 1 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: ..ff.... ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: ..ff.... ........ ........ ........ |A . . .| +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 1 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: ......ff ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: ......ff ........ ........ ........ |A . . .| +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 1 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: ......ff ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: ......ff ........ ........ ........ |A . . .| +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 0 bytes: + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 3 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: 000000.. ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: 000000.. ........ ........ ........ |. . . .| diff --git a/test/msan/msan_print_shadow3.cc b/test/msan/msan_print_shadow3.cc new file mode 100644 index 000000000000..c605ef18886d --- /dev/null +++ b/test/msan/msan_print_shadow3.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdint.h> +#include <sanitizer/msan_interface.h> + +int main(void) { + unsigned long long x = 0; // For 8-byte alignment. + uint32_t x_s = 0x12345678U; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} + +// CHECK: Shadow map of [{{.*}}), 4 bytes: +// CHECK: 0x{{.*}}: 87654321 ........ ........ ........ diff --git a/test/msan/mul_by_const.cc b/test/msan/mul_by_const.cc new file mode 100644 index 000000000000..a975bb92167e --- /dev/null +++ b/test/msan/mul_by_const.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t + +#include <sanitizer/msan_interface.h> + +struct S { + S(int a0) : a(a0) {} + int a; + int b; +}; + +// Here S is passed to FooRun as a 64-bit integer. +// This triggers an optimization where 10000 * s.a is transformed into +// ((*(uint64_t *)&s) * (10000 * 2**32)) >> 32 +// Test that MSan understands that this kills the uninitialized high half of S +// (i.e. S::b). +void FooRun(S s) { + int64_t x = 10000 * s.a; + __msan_check_mem_is_initialized(&x, sizeof(x)); +} + +int main(void) { + S z(1); + // Take &z to ensure that it is built on stack. + S *volatile p = &z; + FooRun(z); + return 0; +} diff --git a/test/msan/no_sanitize_memory.cc b/test/msan/no_sanitize_memory.cc new file mode 100644 index 000000000000..c5643816c281 --- /dev/null +++ b/test/msan/no_sanitize_memory.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t >%t.out 2>&1 + +// RUN: %clangxx_msan -m64 -O0 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 + +// Test that (no_sanitize_memory) functions +// * don't check shadow values (-DCHECK_IN_F) +// * treat all values loaded from memory as fully initialized (-UCHECK_IN_F) + +#include <stdlib.h> +#include <stdio.h> + +__attribute__((noinline)) +__attribute__((no_sanitize_memory)) +int f(void) { + int x; + int * volatile p = &x; +#ifdef CHECK_IN_F + if (*p) + exit(0); +#endif + return *p; +} + +int main(void) { + if (f()) + exit(0); + return 0; +} diff --git a/test/msan/no_sanitize_memory_prop.cc b/test/msan/no_sanitize_memory_prop.cc new file mode 100644 index 000000000000..4275ebbf78e1 --- /dev/null +++ b/test/msan/no_sanitize_memory_prop.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t >%t.out 2>&1 + +// Test that (no_sanitize_memory) functions DO NOT propagate shadow. + +#include <stdlib.h> +#include <stdio.h> + +__attribute__((noinline)) +__attribute__((no_sanitize_memory)) +int f(int x) { + return x; +} + +int main(void) { + int x; + int * volatile p = &x; + int y = f(*p); + if (y) + exit(0); + return 0; +} diff --git a/test/msan/obstack.cc b/test/msan/obstack.cc new file mode 100644 index 000000000000..222f43b839dc --- /dev/null +++ b/test/msan/obstack.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t && not %run %t |& FileCheck %s + +#include <obstack.h> +#include <sanitizer/msan_interface.h> +#include <stdlib.h> + +static void *obstack_chunk_alloc(size_t sz) { + return malloc(sz); +} + +static void obstack_chunk_free(void *p) { + free(p); +} + +int main(void) { + obstack obs; + obstack_init(&obs); + for (size_t sz = 16; sz < 0xFFFF; sz *= 2) { + void *p = obstack_alloc(&obs, sz); + int data[10] = {0}; + obstack_grow(&obs, &data, sizeof(data)); + obstack_blank(&obs, sz); + obstack_grow(&obs, &data, sizeof(data)); + obstack_int_grow(&obs, 13); + p = obstack_finish(&obs); +#ifdef POSITIVE + if (sz == 4096) { + __msan_check_mem_is_initialized(p, sizeof(data)); + __msan_check_mem_is_initialized(p, sizeof(data) + 1); + } + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 0x{{.*}} in main{{.*}}obstack.cc:[[@LINE-30]] +#endif + } + obstack_free(&obs, 0); +} diff --git a/test/msan/param_tls_limit.cc b/test/msan/param_tls_limit.cc new file mode 100644 index 000000000000..869afc935773 --- /dev/null +++ b/test/msan/param_tls_limit.cc @@ -0,0 +1,74 @@ +// ParamTLS has limited size. Everything that does not fit is considered fully +// initialized. + +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 %s -o %t && %run %t + +#include <sanitizer/msan_interface.h> +#include <assert.h> + +// This test assumes that ParamTLS size is 800 bytes. + +// This test passes poisoned values through function argument list. +// In case of overflow, argument is unpoisoned. +#define OVERFLOW(x) assert(__msan_test_shadow(&x, sizeof(x)) == -1) +// In case of no overflow, it is still poisoned. +#define NO_OVERFLOW(x) assert(__msan_test_shadow(&x, sizeof(x)) == 0) + +template<int N> +struct S { + char x[N]; +}; + +void f100(S<100> s) { + NO_OVERFLOW(s); +} + +void f800(S<800> s) { + NO_OVERFLOW(s); +} + +void f801(S<801> s) { + OVERFLOW(s); +} + +void f1000(S<1000> s) { + OVERFLOW(s); +} + +void f_many(int a, double b, S<800> s, int c, double d) { + NO_OVERFLOW(a); + NO_OVERFLOW(b); + OVERFLOW(s); + OVERFLOW(c); + OVERFLOW(d); +} + +// -8 bytes for "int a", aligned by 8 +// -2 to make "int c" a partial fit +void f_many2(int a, S<800 - 8 - 2> s, int c, double d) { + NO_OVERFLOW(a); + NO_OVERFLOW(s); + OVERFLOW(c); + OVERFLOW(d); +} + +int main(void) { + S<100> s100; + S<800> s800; + S<801> s801; + S<1000> s1000; + f100(s100); + f800(s800); + f801(s801); + f1000(s1000); + + int i; + double d; + f_many(i, d, s800, i, d); + + S<800 - 8 - 2> s788; + f_many2(i, s788, i, d); + return 0; +} diff --git a/test/msan/poison_in_free.cc b/test/msan/poison_in_free.cc new file mode 100644 index 000000000000..16e2124c3d51 --- /dev/null +++ b/test/msan/poison_in_free.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=poison_in_free=0 %run %t >%t.out 2>&1 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(50 * sizeof(char)); + memset(x, 0, 50); + free(x); + return x[25]; + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 {{.*}} in main{{.*}}poison_in_free.cc:[[@LINE-2]] +} diff --git a/test/msan/print_stats.cc b/test/msan/print_stats.cc new file mode 100644 index 000000000000..74943835b367 --- /dev/null +++ b/test/msan/print_stats.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g %s -o %t +// RUN: %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS %s +// RUN: MSAN_OPTIONS=print_stats=1 %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS %s +// RUN: MSAN_OPTIONS=print_stats=1,atexit=1 %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-STATS %s + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g -DPOSITIVE=1 %s -o %t +// RUN: not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS %s +// RUN: MSAN_OPTIONS=print_stats=1 not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-STATS %s + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g -DPOSITIVE=1 -mllvm -msan-keep-going=1 %s -o %t +// RUN: not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS --check-prefix=CHECK-KEEPGOING %s +// RUN: MSAN_OPTIONS=print_stats=1 not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-STATS --check-prefix=CHECK-KEEPGOING %s + +#include <stdio.h> +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + fprintf(stderr, "TEST\n"); +#ifdef POSITIVE + return *p; +#else + return 0; +#endif +} + +// CHECK: TEST + +// CHECK-STATS: Unique heap origins: +// CHECK-STATS: Stack depot allocated bytes: +// CHECK-STATS: Unique origin histories: +// CHECK-STATS: History depot allocated bytes: + +// CHECK-NOSTATS-NOT: Unique heap origins: +// CHECK-NOSTATS-NOT: Stack depot allocated bytes: +// CHECK-NOSTATS-NOT: Unique origin histories: +// CHECK-NOSTATS-NOT: History depot allocated bytes: + +// CHECK-KEEPGOING: MemorySanitizer: 1 warnings reported. diff --git a/test/msan/pthread_getattr_np_deadlock.cc b/test/msan/pthread_getattr_np_deadlock.cc new file mode 100644 index 000000000000..07f19cb61b6c --- /dev/null +++ b/test/msan/pthread_getattr_np_deadlock.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -m64 -fsanitize-memory-track-origins -O0 %s -o %t && %run %t + +// Regression test for a deadlock in pthread_getattr_np + +#include <assert.h> +#include <pthread.h> + +void *ThreadFn(void *) { + pthread_attr_t attr; + int res = pthread_getattr_np(pthread_self(), &attr); + assert(!res); + return 0; +} + +int main(void) { + pthread_t t; + int res = pthread_create(&t, 0, ThreadFn, 0); + assert(!res); + res = pthread_join(t, 0); + assert(!res); + return 0; +} diff --git a/test/msan/rand_r.cc b/test/msan/rand_r.cc new file mode 100644 index 000000000000..d6bdb1deaa68 --- /dev/null +++ b/test/msan/rand_r.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -g -DUNINIT %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +int main(void) { + unsigned seed; +#ifndef UNINIT + seed = 42; +#endif + int v = rand_r(&seed); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: in main{{.*}}rand_r.cc:[[@LINE-2]] + if (v) printf(".\n"); + return 0; +} diff --git a/test/msan/readdir64.cc b/test/msan/readdir64.cc new file mode 100644 index 000000000000..4f00d1838794 --- /dev/null +++ b/test/msan/readdir64.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t + +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O1 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O2 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t + +// Test that readdir64 is intercepted as well as readdir. + +#include <sys/types.h> +#include <dirent.h> +#include <stdlib.h> + + +int main(void) { + DIR *dir = opendir("."); + struct dirent *d = readdir(dir); + if (d->d_name[0]) { + closedir(dir); + exit(0); + } + closedir(dir); + return 0; +} diff --git a/test/msan/report-demangling.cc b/test/msan/report-demangling.cc new file mode 100644 index 000000000000..e6d5c27ec85d --- /dev/null +++ b/test/msan/report-demangling.cc @@ -0,0 +1,19 @@ +// Test that function name is mangled in the "created by an allocation" line, +// and demangled in the single-frame "stack trace" that follows. + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +__attribute__((noinline)) +int f() { + int x; + int *volatile p = &x; + return *p; +} + +int main(int argc, char **argv) { + return f(); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function '_Z1fv' + // CHECK: #0 {{.*}} in f() {{.*}}report-demangling.cc:[[@LINE-10]] +} diff --git a/test/msan/scandir.cc b/test/msan/scandir.cc new file mode 100644 index 000000000000..571ba4f603d2 --- /dev/null +++ b/test/msan/scandir.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + + +static int my_filter(const struct dirent *a) { + assert(__msan_test_shadow(&a, sizeof(a)) == (size_t)-1); + printf("%s\n", a->d_name); + __msan_print_shadow(a, a->d_reclen); + assert(__msan_test_shadow(a, a->d_reclen) == (size_t)-1); + printf("%s\n", a->d_name); + return strlen(a->d_name) == 3 && a->d_name[2] == 'b'; +} + +static int my_compar(const struct dirent **a, const struct dirent **b) { + assert(__msan_test_shadow(a, sizeof(*a)) == (size_t)-1); + assert(__msan_test_shadow(*a, (*a)->d_reclen) == (size_t)-1); + assert(__msan_test_shadow(b, sizeof(*b)) == (size_t)-1); + assert(__msan_test_shadow(*b, (*b)->d_reclen) == (size_t)-1); + if ((*a)->d_name[1] == (*b)->d_name[1]) + return 0; + return ((*a)->d_name[1] < (*b)->d_name[1]) ? 1 : -1; +} + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/"); + + struct dirent **d; + int res = scandir(buf, &d, my_filter, my_compar); + assert(res == 2); + assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1); + for (int i = 0; i < res; ++i) { + assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1); + assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1); + } + + assert(strcmp(d[0]->d_name, "bbb") == 0); + assert(strcmp(d[1]->d_name, "aab") == 0); + return 0; +} diff --git a/test/msan/scandir_null.cc b/test/msan/scandir_null.cc new file mode 100644 index 000000000000..e7663dc43a74 --- /dev/null +++ b/test/msan/scandir_null.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/"); + + struct dirent **d; + int res = scandir(buf, &d, NULL, NULL); + assert(res >= 3); + assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1); + for (int i = 0; i < res; ++i) { + assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1); + assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1); + } + return 0; +} diff --git a/test/msan/scandir_test_root/aaa b/test/msan/scandir_test_root/aaa new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/msan/scandir_test_root/aaa diff --git a/test/msan/scandir_test_root/aab b/test/msan/scandir_test_root/aab new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/msan/scandir_test_root/aab diff --git a/test/msan/scandir_test_root/bbb b/test/msan/scandir_test_root/bbb new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/test/msan/scandir_test_root/bbb diff --git a/test/msan/select.cc b/test/msan/select.cc new file mode 100644 index 000000000000..89de75ebaaef --- /dev/null +++ b/test/msan/select.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdlib.h> +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + int z = *p ? 1 : 0; + if (z) + exit(0); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*select.cc:}}[[@LINE-3]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*select.cc:.* main}} + return 0; +} diff --git a/test/msan/select_float_origin.cc b/test/msan/select_float_origin.cc new file mode 100644 index 000000000000..ca8f3a83b0ed --- /dev/null +++ b/test/msan/select_float_origin.cc @@ -0,0 +1,24 @@ +// Regression test for origin propagation in "select i1, float, float". +// https://code.google.com/p/memory-sanitizer/issues/detail?id=78 + +// RUN: %clangxx_msan -O2 -fsanitize-memory-track-origins %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -O2 -fsanitize-memory-track-origins=2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> +#include <sanitizer/msan_interface.h> + +int main() { + volatile bool b = true; + float x, y; + __msan_allocated_memory(&x, sizeof(x)); + __msan_allocated_memory(&y, sizeof(y)); + float z = b ? x : y; + if (z > 0) printf(".\n"); + // CHECK: Uninitialized value was created by a heap allocation + // CHECK: {{#0 0x.* in .*__msan_allocated_memory}} + // CHECK: {{#1 0x.* in main .*select_float_origin.cc:}}[[@LINE-6]] + return 0; +} diff --git a/test/msan/select_origin.cc b/test/msan/select_origin.cc new file mode 100644 index 000000000000..e832c02e99ce --- /dev/null +++ b/test/msan/select_origin.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Test condition origin propagation through "select" IR instruction. + +#include <stdio.h> +#include <stdint.h> + +__attribute__((noinline)) +int *max_by_ptr(int *a, int *b) { + return *a < *b ? b : a; +} + +int main(void) { + int x; + int *volatile px = &x; + int y = 43; + int *p = max_by_ptr(px, &y); + // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + return *p; +} diff --git a/test/msan/setlocale.cc b/test/msan/setlocale.cc new file mode 100644 index 000000000000..b7007f78da7c --- /dev/null +++ b/test/msan/setlocale.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +#include <assert.h> +#include <locale.h> +#include <stdlib.h> + +int main(void) { + char *locale = setlocale (LC_ALL, ""); + assert(locale); + if (locale[0]) + exit(0); + return 0; +} diff --git a/test/msan/signal_stress_test.cc b/test/msan/signal_stress_test.cc new file mode 100644 index 000000000000..654b9676f4ab --- /dev/null +++ b/test/msan/signal_stress_test.cc @@ -0,0 +1,71 @@ +// RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %run %t + +// Test that va_arg shadow from a signal handler does not leak outside. + +#include <signal.h> +#include <stdarg.h> +#include <sanitizer/msan_interface.h> +#include <assert.h> +#include <sys/time.h> +#include <stdio.h> + +const int kSigCnt = 200; + +void f(bool poisoned, int n, ...) { + va_list vl; + va_start(vl, n); + for (int i = 0; i < n; ++i) { + void *p = va_arg(vl, void *); + if (!poisoned) + assert(__msan_test_shadow(&p, sizeof(p)) == -1); + } + va_end(vl); +} + +int sigcnt; + +void SignalHandler(int signo) { + assert(signo == SIGPROF); + void *p; + void **volatile q = &p; + f(true, 10, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q); + ++sigcnt; +} + +int main() { + signal(SIGPROF, SignalHandler); + + itimerval itv; + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 100; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 100; + setitimer(ITIMER_PROF, &itv, NULL); + + void *p; + void **volatile q = &p; + + do { + f(false, 20, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + f(true, 20, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q); + } while (sigcnt < kSigCnt); + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 0; + setitimer(ITIMER_PROF, &itv, NULL); + + signal(SIGPROF, SIG_DFL); + return 0; +} diff --git a/test/msan/sigwait.cc b/test/msan/sigwait.cc new file mode 100644 index 000000000000..f2e77cfd6407 --- /dev/null +++ b/test/msan/sigwait.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <sanitizer/msan_interface.h> +#include <signal.h> +#include <sys/time.h> +#include <unistd.h> + +void test_sigwait() { + sigset_t s; + sigemptyset(&s); + sigaddset(&s, SIGUSR1); + sigprocmask(SIG_BLOCK, &s, 0); + + if (pid_t pid = fork()) { + kill(pid, SIGUSR1); + _exit(0); + } else { + int sig; + int res = sigwait(&s, &sig); + assert(!res); + // The following checks that sig is initialized. + assert(sig == SIGUSR1); + } +} + +int main(void) { + test_sigwait(); + return 0; +} diff --git a/test/msan/sigwaitinfo.cc b/test/msan/sigwaitinfo.cc new file mode 100644 index 000000000000..be7a2c009e0e --- /dev/null +++ b/test/msan/sigwaitinfo.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <sanitizer/msan_interface.h> +#include <signal.h> +#include <sys/time.h> +#include <unistd.h> + +void test_sigwaitinfo() { + sigset_t s; + sigemptyset(&s); + sigaddset(&s, SIGUSR1); + sigprocmask(SIG_BLOCK, &s, 0); + + if (pid_t pid = fork()) { + kill(pid, SIGUSR1); + _exit(0); + } else { + siginfo_t info; + int res = sigwaitinfo(&s, &info); + assert(!res); + // The following checks that sig is initialized. + assert(info.si_signo == SIGUSR1); + assert(-1 == __msan_test_shadow(&info, sizeof(info))); + } +} + +int main(void) { + test_sigwaitinfo(); + return 0; +} diff --git a/test/msan/stack-origin.cc b/test/msan/stack-origin.cc new file mode 100644 index 000000000000..c39c3a8cf6d0 --- /dev/null +++ b/test/msan/stack-origin.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <stdlib.h> +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + return *p; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + // CHECK-ORIGINS: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-8]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*stack-origin.cc:.* main}} +} diff --git a/test/msan/stack-origin2.cc b/test/msan/stack-origin2.cc new file mode 100644 index 000000000000..0ba57607dadb --- /dev/null +++ b/test/msan/stack-origin2.cc @@ -0,0 +1,41 @@ +// Test that on the second entry to a function the origins are still right. + +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <stdlib.h> + +extern "C" +int f(int depth) { + if (depth) return f(depth - 1); + + int x; + int *volatile p = &x; + return *p; +} + +int main(int argc, char **argv) { + return f(1); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*stack-origin2.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'f' + // CHECK-ORIGINS: {{#0 0x.* in f .*stack-origin2.cc:}}[[@LINE-14]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*stack-origin2.cc:.* main}} +} diff --git a/test/msan/strerror_r-non-gnu.c b/test/msan/strerror_r-non-gnu.c new file mode 100644 index 000000000000..d55bf42ef11e --- /dev/null +++ b/test/msan/strerror_r-non-gnu.c @@ -0,0 +1,18 @@ +// RUN: %clang_msan -std=c99 -O0 -g %s -o %t && %run %t + +// strerror_r under a weird set of circumstances can be redirected to +// __xpg_strerror_r. Test that MSan handles this correctly. + +#define _POSIX_C_SOURCE 200112 +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +int main() { + char buf[1000]; + int res = strerror_r(EINVAL, buf, sizeof(buf)); + assert(!res); + volatile int z = strlen(buf); + return 0; +} diff --git a/test/msan/strlen_of_shadow.cc b/test/msan/strlen_of_shadow.cc new file mode 100644 index 000000000000..bb9fe17d41cd --- /dev/null +++ b/test/msan/strlen_of_shadow.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +// Check that strlen() and similar intercepted functions can be called on shadow +// memory. + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +const char *mem_to_shadow(const char *p) { + return (char *)((uintptr_t)p & ~0x400000000000ULL); +} + +int main(void) { + const char *s = "abcdef"; + assert(strlen(s) == 6); + assert(strlen(mem_to_shadow(s)) == 0); + + char *t = new char[42]; + t[41] = 0; + assert(strlen(mem_to_shadow(t)) == 41); + return 0; +} diff --git a/test/msan/strxfrm.cc b/test/msan/strxfrm.cc new file mode 100644 index 000000000000..b930a6af69c4 --- /dev/null +++ b/test/msan/strxfrm.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <sanitizer/msan_interface.h> +#include <stdlib.h> +#include <string.h> + +int main(void) { + const char *p = "abcdef"; + char q[10]; + size_t n = strxfrm(q, p, sizeof(q)); + assert(n < sizeof(q)); + __msan_check_mem_is_initialized(q, n + 1); + return 0; +} diff --git a/test/msan/sync_lock_set_and_test.cc b/test/msan/sync_lock_set_and_test.cc new file mode 100644 index 000000000000..b6d344a61340 --- /dev/null +++ b/test/msan/sync_lock_set_and_test.cc @@ -0,0 +1,7 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +int main(void) { + int i; + __sync_lock_test_and_set(&i, 0); + return i; +} diff --git a/test/msan/textdomain.cc b/test/msan/textdomain.cc new file mode 100644 index 000000000000..47e991e8c16b --- /dev/null +++ b/test/msan/textdomain.cc @@ -0,0 +1,12 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <libintl.h> +#include <stdio.h> + +int main() { + const char *td = textdomain("abcd"); + if (td[0] == 0) { + printf("Try read"); + } + return 0; +} diff --git a/test/msan/times.cc b/test/msan/times.cc new file mode 100644 index 000000000000..a042f548dc0c --- /dev/null +++ b/test/msan/times.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/times.h> + + +int main(void) { + struct tms t; + clock_t res = times(&t); + assert(res != (clock_t)-1); + + if (t.tms_utime) printf("1\n"); + if (t.tms_stime) printf("2\n"); + if (t.tms_cutime) printf("3\n"); + if (t.tms_cstime) printf("4\n"); + + return 0; +} diff --git a/test/msan/tls_reuse.cc b/test/msan/tls_reuse.cc new file mode 100644 index 000000000000..e024a5a8d13f --- /dev/null +++ b/test/msan/tls_reuse.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +// Check that when TLS block is reused between threads, its shadow is cleaned. + +#include <pthread.h> +#include <stdio.h> + +int __thread x; + +void *ThreadFn(void *) { + if (!x) + printf("zzz\n"); + int y; + int * volatile p = &y; + x = *p; + return 0; +} + +int main(void) { + pthread_t t; + for (int i = 0; i < 100; ++i) { + pthread_create(&t, 0, ThreadFn, 0); + pthread_join(t, 0); + } + return 0; +} diff --git a/test/msan/tsearch.cc b/test/msan/tsearch.cc new file mode 100644 index 000000000000..653dc60371cd --- /dev/null +++ b/test/msan/tsearch.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <search.h> +#include <stdlib.h> + +int compare(const void *pa, const void *pb) { + int a = *(const int *)pa; + int b = *(const int *)pb; + if (a < b) + return -1; + else if (a > b) + return 1; + else + return 0; +} + +void myfreenode(void *p) { + delete (int *)p; +} + +int main(void) { + void *root = NULL; + for (int i = 0; i < 5; ++i) { + int *p = new int(i); + void *q = tsearch(p, &root, compare); + if (q == NULL) + exit(1); + if (*(int **)q != p) + delete p; + } + + tdestroy(root, myfreenode); + + return 0; +} diff --git a/test/msan/tzset.cc b/test/msan/tzset.cc new file mode 100644 index 000000000000..ed61d7bcc449 --- /dev/null +++ b/test/msan/tzset.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +extern char *tzname[2]; + +int main(void) { + if (!strlen(tzname[0]) || !strlen(tzname[1])) + exit(1); + tzset(); + if (!strlen(tzname[0]) || !strlen(tzname[1])) + exit(1); + return 0; +} diff --git a/test/msan/unaligned_read_origin.cc b/test/msan/unaligned_read_origin.cc new file mode 100644 index 000000000000..e5618efbde24 --- /dev/null +++ b/test/msan/unaligned_read_origin.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +#include <sanitizer/msan_interface.h> + +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + return __sanitizer_unaligned_load32(p); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*unaligned_read_origin.cc:}}[[@LINE-2]] + // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + // CHECK: {{#0 0x.* in main .*unaligned_read_origin.cc:}}[[@LINE-7]] +} diff --git a/test/msan/unpoison_string.cc b/test/msan/unpoison_string.cc new file mode 100644 index 000000000000..ac947b9cf6fd --- /dev/null +++ b/test/msan/unpoison_string.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t +// RUN: %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t +// RUN: %run %t + +#include <assert.h> +#include <string.h> +#include <sanitizer/msan_interface.h> + +int main(int argc, char **argv) { + char s[20] = "string"; + __msan_poison(s, sizeof(s)); + __msan_unpoison_string(s); + assert(__msan_test_shadow(s, sizeof(s)) == strlen("string") + 1); + return 0; +} diff --git a/test/msan/use-after-free.cc b/test/msan/use-after-free.cc new file mode 100644 index 000000000000..5b408c536495 --- /dev/null +++ b/test/msan/use-after-free.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <stdlib.h> +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + *p = 42; + free(p); + + if (*p) + exit(0); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*use-after-free.cc:}}[[@LINE-3]] + + // CHECK-ORIGINS: Uninitialized value was created by a heap allocation + // CHECK-ORIGINS: {{#0 0x.* in .*free}} + // CHECK-ORIGINS: {{#1 0x.* in main .*use-after-free.cc:}}[[@LINE-9]] + return 0; +} diff --git a/test/msan/vector_cvt.cc b/test/msan/vector_cvt.cc new file mode 100644 index 000000000000..bd9b6a8b8000 --- /dev/null +++ b/test/msan/vector_cvt.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <emmintrin.h> + +int to_int(double v) { + __m128d t = _mm_set_sd(v); + int x = _mm_cvtsd_si32(t); + return x; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #{{.*}} in to_int{{.*}}vector_cvt.cc:[[@LINE-3]] +} + +int main() { +#ifdef POSITIVE + double v; +#else + double v = 1.1; +#endif + double* volatile p = &v; + int x = to_int(*p); + return !x; +} diff --git a/test/msan/vector_select.cc b/test/msan/vector_select.cc new file mode 100644 index 000000000000..e8d55423293c --- /dev/null +++ b/test/msan/vector_select.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_msan -m64 -O0 %s -c -o %t +// RUN: %clangxx_msan -m64 -O3 %s -c -o %t + +// Regression test for MemorySanitizer instrumentation of a select instruction +// with vector arguments. + +#include <emmintrin.h> + +__m128d select(bool b, __m128d c, __m128d d) +{ + return b ? c : d; +} + diff --git a/test/profile/CMakeLists.txt b/test/profile/CMakeLists.txt new file mode 100644 index 000000000000..28fb35a9f6f8 --- /dev/null +++ b/test/profile/CMakeLists.txt @@ -0,0 +1,16 @@ +set(PROFILE_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(PROFILE_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +set(PROFILE_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND PROFILE_TEST_DEPS profile llvm-profdata) +endif() + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) +add_lit_testsuite(check-profile "Running the profile tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${PROFILE_TEST_DEPS}) +set_target_properties(check-profile PROPERTIES FOLDER "Profile tests") diff --git a/test/profile/Inputs/instrprof-dlopen-func.c b/test/profile/Inputs/instrprof-dlopen-func.c new file mode 100644 index 000000000000..f2de3883535a --- /dev/null +++ b/test/profile/Inputs/instrprof-dlopen-func.c @@ -0,0 +1 @@ +void func(int K) { if (K) {} } diff --git a/test/profile/Inputs/instrprof-dlopen-func2.c b/test/profile/Inputs/instrprof-dlopen-func2.c new file mode 100644 index 000000000000..d4d93dc0b256 --- /dev/null +++ b/test/profile/Inputs/instrprof-dlopen-func2.c @@ -0,0 +1 @@ +void func2(int K) { if (K) {} } diff --git a/test/profile/Inputs/instrprof-dlopen-main.c b/test/profile/Inputs/instrprof-dlopen-main.c new file mode 100644 index 000000000000..ec357fe05bd5 --- /dev/null +++ b/test/profile/Inputs/instrprof-dlopen-main.c @@ -0,0 +1,47 @@ + +#include <stdio.h> +#include <stdlib.h> + +#ifdef DLOPEN_FUNC_DIR +#include <dlfcn.h> +#else +void func(int K); +void func2(int K); +#endif + +int main(int argc, char *argv[]) { +#ifdef DLOPEN_FUNC_DIR + dlerror(); + void *f1_handle = dlopen(DLOPEN_FUNC_DIR"/func.shared", DLOPEN_FLAGS); + if (f1_handle == NULL) { + fprintf(stderr, "unable to open '" DLOPEN_FUNC_DIR "/func.shared': %s\n", + dlerror()); + return EXIT_FAILURE; + } + + void (*func)(int) = (void (*)(int))dlsym(f1_handle, "func"); + if (func == NULL) { + fprintf(stderr, "unable to lookup symbol 'func': %s\n", dlerror()); + return EXIT_FAILURE; + } + + void *f2_handle = dlopen(DLOPEN_FUNC_DIR"/func2.shared", DLOPEN_FLAGS); + if (f2_handle == NULL) { + fprintf(stderr, "unable to open '" DLOPEN_FUNC_DIR "/func2.shared': %s\n", + dlerror()); + return EXIT_FAILURE; + } + + void (*func2)(int) = (void (*)(int))dlsym(f2_handle, "func2"); + if (func2 == NULL) { + fprintf(stderr, "unable to lookup symbol 'func2': %s\n", dlerror()); + return EXIT_FAILURE; + } +#endif + + func(1); + func2(0); + + return EXIT_SUCCESS; +} + diff --git a/test/profile/Inputs/instrprof-dynamic-a.cpp b/test/profile/Inputs/instrprof-dynamic-a.cpp new file mode 100644 index 000000000000..67de263c4d88 --- /dev/null +++ b/test/profile/Inputs/instrprof-dynamic-a.cpp @@ -0,0 +1,7 @@ +#include "instrprof-dynamic-header.h" +void a() { + if (true) { + bar<void>(); + bar<char>(); + } +} diff --git a/test/profile/Inputs/instrprof-dynamic-b.cpp b/test/profile/Inputs/instrprof-dynamic-b.cpp new file mode 100644 index 000000000000..c8fb75ef52ed --- /dev/null +++ b/test/profile/Inputs/instrprof-dynamic-b.cpp @@ -0,0 +1,7 @@ +#include "instrprof-dynamic-header.h" +void b() { + if (true) { + bar<void>(); + bar<int>(); + } +} diff --git a/test/profile/Inputs/instrprof-dynamic-header.h b/test/profile/Inputs/instrprof-dynamic-header.h new file mode 100644 index 000000000000..1dc2e37ef82c --- /dev/null +++ b/test/profile/Inputs/instrprof-dynamic-header.h @@ -0,0 +1,5 @@ +template <class T> void bar() { + if (true) {} +} +void a(); +void b(); diff --git a/test/profile/Inputs/instrprof-dynamic-main.cpp b/test/profile/Inputs/instrprof-dynamic-main.cpp new file mode 100644 index 000000000000..0dd60213926e --- /dev/null +++ b/test/profile/Inputs/instrprof-dynamic-main.cpp @@ -0,0 +1,9 @@ +#include "instrprof-dynamic-header.h" +void foo(int K) { if (K) {} } +int main(int argc, char *argv[]) { + foo(5); + bar<void>(); + a(); + b(); + return 0; +} diff --git a/test/profile/instrprof-basic.c b/test/profile/instrprof-basic.c new file mode 100644 index 000000000000..fd3516cfad03 --- /dev/null +++ b/test/profile/instrprof-basic.c @@ -0,0 +1,31 @@ +// RUN: %clang_profgen -o %t -O3 %s +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s + +int begin(int i) { + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (i) + return 0; + return 1; +} + +int end(int i) { + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD2:[0-9]+]] + if (i) + return 0; + return 1; +} + +int main(int argc, const char *argv[]) { + begin(0); + end(1); + + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD2:[0-9]+]] + if (argc) + return 0; + return 1; +} + +// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2} +// CHECK: ![[PD2]] = metadata !{metadata !"branch_weights", i32 2, i32 1} diff --git a/test/profile/instrprof-dlopen.test b/test/profile/instrprof-dlopen.test new file mode 100644 index 000000000000..ba386e34773b --- /dev/null +++ b/test/profile/instrprof-dlopen.test @@ -0,0 +1,34 @@ +RUN: mkdir -p %t.d +RUN: %clang_profgen -o %t.d/func.shared -fPIC -shared %S/Inputs/instrprof-dlopen-func.c +RUN: %clang_profgen -o %t.d/func2.shared -fPIC -shared %S/Inputs/instrprof-dlopen-func2.c +RUN: %clang -o %t-local -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS="RTLD_LAZY | RTLD_LOCAL" %S/Inputs/instrprof-dlopen-main.c +RUN: %clang -o %t-global -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS="RTLD_LAZY | RTLD_GLOBAL" %S/Inputs/instrprof-dlopen-main.c + +RUN: %clang -c -o %t.d/main.o %S/Inputs/instrprof-dlopen-main.c +RUN: %clang_profgen -o %t-static %S/Inputs/instrprof-dlopen-func.c %S/Inputs/instrprof-dlopen-func2.c %t.d/main.o + +RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static +RUN: env LLVM_PROFILE_FILE=%t-local.profraw %run %t-local +RUN: env LLVM_PROFILE_FILE=%t-global.profraw %run %t-global + +RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw +RUN: llvm-profdata merge -o %t-local.profdata %t-local.profraw +RUN: llvm-profdata merge -o %t-global.profdata %t-global.profraw + +RUN: %clang_profuse=%t-static.profdata -o %t-func.static.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func.c +RUN: %clang_profuse=%t-local.profdata -o %t-func.local.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func.c +RUN: %clang_profuse=%t-global.profdata -o %t-func.global.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func.c +RUN: diff %t-func.static.ll %t-func.local.ll +RUN: diff %t-func.static.ll %t-func.global.ll + +RUN: %clang_profuse=%t-static.profdata -o %t-func2.static.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func2.c +RUN: %clang_profuse=%t-local.profdata -o %t-func2.local.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func2.c +RUN: %clang_profuse=%t-global.profdata -o %t-func2.global.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func2.c +RUN: diff %t-func2.static.ll %t-func2.local.ll +RUN: diff %t-func2.static.ll %t-func2.global.ll + +RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-main.c +RUN: %clang_profuse=%t-local.profdata -o %t-main.local.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-main.c +RUN: %clang_profuse=%t-local.profdata -o %t-main.global.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-main.c +RUN: diff %t-main.static.ll %t-main.local.ll +RUN: diff %t-main.static.ll %t-main.global.ll diff --git a/test/profile/instrprof-dynamic-one-shared.test b/test/profile/instrprof-dynamic-one-shared.test new file mode 100644 index 000000000000..38be4fe8bc4e --- /dev/null +++ b/test/profile/instrprof-dynamic-one-shared.test @@ -0,0 +1,23 @@ +RUN: mkdir -p %t.d +RUN: %clang_profgen -o %t.d/a.shared -fPIC -shared %S/Inputs/instrprof-dynamic-a.cpp +RUN: %clang_profgen -o %t-shared -fPIC -rpath %t.d %t.d/a.shared %S/Inputs/instrprof-dynamic-b.cpp %S/Inputs/instrprof-dynamic-main.cpp + +RUN: %clang_profgen -o %t-static %S/Inputs/instrprof-dynamic-a.cpp %S/Inputs/instrprof-dynamic-b.cpp %S/Inputs/instrprof-dynamic-main.cpp + +RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static +RUN: env LLVM_PROFILE_FILE=%t-shared.profraw %run %t-shared + +RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw +RUN: llvm-profdata merge -o %t-shared.profdata %t-shared.profraw + +RUN: %clang_profuse=%t-static.profdata -o %t-a.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-a.cpp +RUN: %clang_profuse=%t-shared.profdata -o %t-a.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-a.cpp +RUN: diff %t-a.static.ll %t-a.shared.ll + +RUN: %clang_profuse=%t-static.profdata -o %t-b.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-b.cpp +RUN: %clang_profuse=%t-shared.profdata -o %t-b.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-b.cpp +RUN: diff %t-b.static.ll %t-b.shared.ll + +RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-main.cpp +RUN: %clang_profuse=%t-shared.profdata -o %t-main.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-main.cpp +RUN: diff %t-main.static.ll %t-main.shared.ll diff --git a/test/profile/instrprof-dynamic-two-shared.test b/test/profile/instrprof-dynamic-two-shared.test new file mode 100644 index 000000000000..830359dec44f --- /dev/null +++ b/test/profile/instrprof-dynamic-two-shared.test @@ -0,0 +1,24 @@ +RUN: mkdir -p %t.d +RUN: %clang_profgen -o %t.d/a.shared -fPIC -shared %S/Inputs/instrprof-dynamic-a.cpp +RUN: %clang_profgen -o %t.d/b.shared -fPIC -shared %S/Inputs/instrprof-dynamic-b.cpp +RUN: %clang_profgen -o %t-shared -fPIC -rpath %t.d %t.d/a.shared %t.d/b.shared %S/Inputs/instrprof-dynamic-main.cpp + +RUN: %clang_profgen -o %t-static %S/Inputs/instrprof-dynamic-a.cpp %S/Inputs/instrprof-dynamic-b.cpp %S/Inputs/instrprof-dynamic-main.cpp + +RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static +RUN: env LLVM_PROFILE_FILE=%t-shared.profraw %run %t-shared + +RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw +RUN: llvm-profdata merge -o %t-shared.profdata %t-shared.profraw + +RUN: %clang_profuse=%t-static.profdata -o %t-a.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-a.cpp +RUN: %clang_profuse=%t-shared.profdata -o %t-a.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-a.cpp +RUN: diff %t-a.static.ll %t-a.shared.ll + +RUN: %clang_profuse=%t-static.profdata -o %t-b.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-b.cpp +RUN: %clang_profuse=%t-shared.profdata -o %t-b.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-b.cpp +RUN: diff %t-b.static.ll %t-b.shared.ll + +RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-main.cpp +RUN: %clang_profuse=%t-shared.profdata -o %t-main.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-main.cpp +RUN: diff %t-main.static.ll %t-main.shared.ll diff --git a/test/profile/instrprof-reset-counters.c b/test/profile/instrprof-reset-counters.c new file mode 100644 index 000000000000..c92732baf45d --- /dev/null +++ b/test/profile/instrprof-reset-counters.c @@ -0,0 +1,19 @@ +// RUN: %clang_profgen -o %t -O3 %s +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s + +void __llvm_profile_reset_counters(void); +void foo(int); +int main(void) { + foo(0); + __llvm_profile_reset_counters(); + foo(1); + return 0; +} +void foo(int N) { + // CHECK-LABEL: define void @foo( + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[FOO:[0-9]+]] + if (N) {} +} +// CHECK: ![[FOO]] = metadata !{metadata !"branch_weights", i32 2, i32 1} diff --git a/test/profile/instrprof-set-filename.c b/test/profile/instrprof-set-filename.c new file mode 100644 index 000000000000..045821899e44 --- /dev/null +++ b/test/profile/instrprof-set-filename.c @@ -0,0 +1,14 @@ +// RUN: %clang_profgen -o %t -O3 %s +// RUN: %run %t %t.profraw +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s + +void __llvm_profile_set_filename(const char *); +int main(int argc, const char *argv[]) { + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc < 2) + return 1; + __llvm_profile_set_filename(argv[1]); + return 0; +} +// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2} diff --git a/test/profile/instrprof-without-libc.c b/test/profile/instrprof-without-libc.c new file mode 100644 index 000000000000..60ca9496c611 --- /dev/null +++ b/test/profile/instrprof-without-libc.c @@ -0,0 +1,60 @@ +// RUN: %clang_profgen -DCHECK_SYMBOLS -O3 -o %t.symbols %s +// RUN: llvm-nm %t.symbols | FileCheck %s --check-prefix=CHECK-SYMBOLS +// RUN: %clang_profgen -O3 -o %t %s +// RUN: %run %t %t.profraw +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s + +#include <stdint.h> +#include <stdlib.h> + +#ifndef CHECK_SYMBOLS +#include <stdio.h> +#endif + +int __llvm_profile_runtime = 0; +uint64_t __llvm_profile_get_size_for_buffer(void); +int __llvm_profile_write_buffer(char *); +int write_buffer(uint64_t, const char *); +int main(int argc, const char *argv[]) { + // CHECK-LABEL: define {{.*}} @main( + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc < 2) + return 1; + + const uint64_t MaxSize = 10000; + static char Buffer[MaxSize]; + + uint64_t Size = __llvm_profile_get_size_for_buffer(); + if (Size > MaxSize) + return 1; + int Write = __llvm_profile_write_buffer(Buffer); + if (__llvm_profile_write_buffer(Buffer)) + return Write; + +#ifdef CHECK_SYMBOLS + // Don't write it out. Since we're checking the symbols, we don't have libc + // available. + return 0; +#else + // Actually write it out so we can FileCheck the output. + FILE *File = fopen(argv[1], "w"); + if (!File) + return 1; + if (fwrite(Buffer, 1, Size, File) != Size) + return 1; + return fclose(File); +#endif +} +// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2} + +// CHECK-SYMBOLS-NOT: ___cxx_global_var_init +// CHECK-SYMBOLS-NOT: ___llvm_profile_register_write_file_atexit +// CHECK-SYMBOLS-NOT: ___llvm_profile_set_filename +// CHECK-SYMBOLS-NOT: ___llvm_profile_write_file +// CHECK-SYMBOLS-NOT: _fdopen +// CHECK-SYMBOLS-NOT: _fopen +// CHECK-SYMBOLS-NOT: _fwrite +// CHECK-SYMBOLS-NOT: _getenv +// CHECK-SYMBOLS-NOT: _malloc +// CHECK-SYMBOLS-NOT: _open diff --git a/test/profile/instrprof-write-file-atexit-explicitly.c b/test/profile/instrprof-write-file-atexit-explicitly.c new file mode 100644 index 000000000000..ba229b9144fa --- /dev/null +++ b/test/profile/instrprof-write-file-atexit-explicitly.c @@ -0,0 +1,17 @@ +// RUN: %clang_profgen -o %t -O3 %s +// RUN: %run %t %t.profraw +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s + +int __llvm_profile_runtime = 0; +int __llvm_profile_register_write_file_atexit(void); +void __llvm_profile_set_filename(const char *); +int main(int argc, const char *argv[]) { + __llvm_profile_register_write_file_atexit(); + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc < 2) + return 1; + __llvm_profile_set_filename(argv[1]); + return 0; +} +// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2} diff --git a/test/profile/instrprof-write-file-only.c b/test/profile/instrprof-write-file-only.c new file mode 100644 index 000000000000..0dd61de39f1a --- /dev/null +++ b/test/profile/instrprof-write-file-only.c @@ -0,0 +1,35 @@ +// RUN: %clang_profgen -o %t -O3 %s +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s + +int __llvm_profile_runtime = 0; +void __llvm_profile_initialize_file(void); +int __llvm_profile_write_file(void); +void __llvm_profile_set_filename(const char *); +int foo(int); +int main(int argc, const char *argv[]) { + // CHECK-LABEL: define {{.*}} @main( + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc > 1) + return 1; + + // Since the runtime has been suppressed, initialize the file name, as the + // writing will fail below as the file name has not been specified. + __llvm_profile_initialize_file(); + + // Write out the profile. + __llvm_profile_write_file(); + + // Change the profile. + return foo(0); +} +int foo(int X) { + // There should be no profiling information for @foo, since it was called + // after the profile was written (and the atexit was suppressed by defining + // profile_runtime). + // CHECK-LABEL: define {{.*}} @foo( + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{[^,]+$}} + return X <= 0 ? -X : X; +} +// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2} diff --git a/test/profile/instrprof-write-file.c b/test/profile/instrprof-write-file.c new file mode 100644 index 000000000000..12967cb94a55 --- /dev/null +++ b/test/profile/instrprof-write-file.c @@ -0,0 +1,34 @@ +// RUN: %clang_profgen -o %t -O3 %s +// RUN: env LLVM_PROFILE_FILE=%t1.profraw %run %t %t2.profraw +// RUN: llvm-profdata merge -o %t1.profdata %t1.profraw +// RUN: %clang_profuse=%t1.profdata -o - -S -emit-llvm %s | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK +// RUN: llvm-profdata merge -o %t2.profdata %t2.profraw +// RUN: %clang_profuse=%t2.profdata -o - -S -emit-llvm %s | FileCheck %s --check-prefix=CHECK2 --check-prefix=CHECK + +int __llvm_profile_write_file(void); +void __llvm_profile_set_filename(const char *); +int foo(int); +int main(int argc, const char *argv[]) { + // CHECK-LABEL: define {{.*}} @main( + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc < 2) + return 1; + + // Write out the profile. + __llvm_profile_write_file(); + + // Change the profile. + int Ret = foo(0); + + // It'll write out again at exit; change the filename so we get two files. + __llvm_profile_set_filename(argv[1]); + return Ret; +} +int foo(int X) { + // CHECK-LABEL: define {{.*}} @foo( + // CHECK1: br i1 %{{.*}}, label %{{.*}}, label %{{[^,]+$}} + // CHECK2: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD2:[0-9]+]] + return X <= 0 ? -X : X; +} +// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2} +// CHECK2: ![[PD2]] = metadata !{metadata !"branch_weights", i32 2, i32 1} diff --git a/test/profile/lit.cfg b/test/profile/lit.cfg new file mode 100644 index 000000000000..e4910abbe7a4 --- /dev/null +++ b/test/profile/lit.cfg @@ -0,0 +1,53 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'Profile' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup executable root. +if hasattr(config, 'profile_lit_binary_dir') and \ + config.profile_lit_binary_dir is not None: + config.test_exec_root = config.profile_lit_binary_dir + +# If the above check didn't work, we're probably in the source tree. Use some +# magic to re-execute from the build tree. +if config.test_exec_root is None: + # The magic relies on knowing compilerrt_site_basedir. + compilerrt_basedir = lit_config.params.get('compilerrt_site_basedir', None) + if compilerrt_basedir: + site_cfg = os.path.join(compilerrt_basedir, 'profile', 'lit.site.cfg') + if os.path.exists(site_cfg): + lit_config.load_config(config, site_cfg) + raise SystemExit + +if config.host_os in ['Linux']: + extra_linkflags = ["-ldl"] +else: + extra_linkflags = [] + +# Test suffixes. +config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm', '.ll', '.test'] + +# What to exclude. +config.excludes = ['Inputs'] + +# Clang flags. +clang_cflags = [config.target_cflags] + extra_linkflags + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +# Add clang substitutions. +config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) ) +config.substitutions.append( ("%clang_profgen ", build_invocation(clang_cflags) + " -fprofile-instr-generate ") ) +config.substitutions.append( ("%clang_profuse=", build_invocation(clang_cflags) + " -fprofile-instr-use=") ) + +if config.host_os not in ['Darwin', 'FreeBSD', 'Linux']: + config.unsupported = True + +if config.target_arch in ['armv7l']: + config.unsupported = True diff --git a/test/profile/lit.site.cfg.in b/test/profile/lit.site.cfg.in new file mode 100644 index 000000000000..758568d6753a --- /dev/null +++ b/test/profile/lit.site.cfg.in @@ -0,0 +1,11 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Tool-specific config options. +config.profile_lit_binary_dir = "@PROFILE_LIT_BINARY_DIR@" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@PROFILE_LIT_SOURCE_DIR@/lit.cfg") diff --git a/test/sanitizer_common/CMakeLists.txt b/test/sanitizer_common/CMakeLists.txt new file mode 100644 index 000000000000..13eecbdc1b2b --- /dev/null +++ b/test/sanitizer_common/CMakeLists.txt @@ -0,0 +1,46 @@ +set(SANITIZER_COMMON_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(SANITIZER_COMMON_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +set(SANITIZER_COMMON_TESTSUITES) + +set(SUPPORTED_TOOLS) +if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux|FreeBSD" AND NOT ANDROID) + list(APPEND SUPPORTED_TOOLS asan) +endif() +if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT ANDROID) + list(APPEND SUPPORTED_TOOLS tsan) + list(APPEND SUPPORTED_TOOLS msan) + list(APPEND SUPPORTED_TOOLS lsan) +endif() + +# Create a separate config for each tool we support. +foreach(tool ${SUPPORTED_TOOLS}) + string(TOUPPER ${tool} tool_toupper) + if(${tool_toupper}_SUPPORTED_ARCH) + set(SANITIZER_COMMON_LIT_TEST_MODE ${tool}) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/${tool}/lit.site.cfg) + list(APPEND SANITIZER_COMMON_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${tool}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND SANITIZER_COMMON_TEST_DEPS ${tool}) + endif() + endif() +endforeach() + +# Unit tests. +if(COMPILER_RT_INCLUDE_TESTS) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) + list(APPEND SANITIZER_COMMON_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) + list(APPEND SANITIZER_COMMON_TEST_DEPS SanitizerUnitTests) +endif() + +if(SANITIZER_COMMON_TESTSUITES) + add_lit_testsuite(check-sanitizer "Running sanitizer_common tests" + ${SANITIZER_COMMON_TESTSUITES} + DEPENDS ${SANITIZER_COMMON_TEST_DEPS}) + set_target_properties(check-sanitizer PROPERTIES FOLDER + "sanitizer_common tests") +endif() diff --git a/test/sanitizer_common/TestCases/Linux/aligned_alloc.c b/test/sanitizer_common/TestCases/Linux/aligned_alloc.c new file mode 100644 index 000000000000..12af18dd32a1 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/aligned_alloc.c @@ -0,0 +1,8 @@ +// RUN: %clang -std=c11 -O0 %s -o %t && %run %t +#include <stdlib.h> +extern void *aligned_alloc (size_t alignment, size_t size); +int main() { + volatile void *p = aligned_alloc(128, 1024); + free((void*)p); + return 0; +} diff --git a/test/sanitizer_common/TestCases/Linux/clock_gettime.c b/test/sanitizer_common/TestCases/Linux/clock_gettime.c new file mode 100644 index 000000000000..ec1386ef2414 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/clock_gettime.c @@ -0,0 +1,11 @@ +// RUN: %clang %s -Wl,-as-needed -o %t && %run %t +// Regression test for PR15823 +// (http://llvm.org/bugs/show_bug.cgi?id=15823). +#include <stdio.h> +#include <time.h> + +int main() { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return 0; +} diff --git a/test/sanitizer_common/TestCases/Linux/getpass.cc b/test/sanitizer_common/TestCases/Linux/getpass.cc new file mode 100644 index 000000000000..c9a2276cc248 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/getpass.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx -O0 -g %s -lutil -o %t && %run %t | FileCheck %s +#include <assert.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <pty.h> + +int +main (int argc, char** argv) +{ + int master; + int pid = forkpty(&master, NULL, NULL, NULL); + + if(pid == -1) { + fprintf(stderr, "forkpty failed\n"); + return 1; + } else if (pid > 0) { + char buf[1024]; + int res = read(master, buf, sizeof(buf)); + write(1, buf, res); + write(master, "password\n", 9); + while ((res = read(master, buf, sizeof(buf))) > 0) write(1, buf, res); + } else { + char *s = getpass("prompt"); + assert(strcmp(s, "password") == 0); + write(1, "done\n", 5); + } + return 0; +} + +// CHECK: prompt +// CHECK: done diff --git a/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cc b/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cc new file mode 100644 index 000000000000..a8b51d7a99c0 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cc @@ -0,0 +1,19 @@ +// Regression test for a crash in getpwnam_r and similar interceptors. +// RUN: %clangxx -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +int main(void) { + struct passwd pwd; + struct passwd *pwdres; + char buf[10000]; + int res = getpwnam_r("no-such-user", &pwd, buf, sizeof(buf), &pwdres); + assert(res == 0); + assert(pwdres == 0); + return 0; +} diff --git a/test/sanitizer_common/TestCases/Linux/lit.local.cfg b/test/sanitizer_common/TestCases/Linux/lit.local.cfg new file mode 100644 index 000000000000..57271b8078a4 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/sanitizer_common/TestCases/Linux/mlock_test.cc b/test/sanitizer_common/TestCases/Linux/mlock_test.cc new file mode 100644 index 000000000000..69ea7cb91c4f --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/mlock_test.cc @@ -0,0 +1,13 @@ +// RUN: %clang %s -o %t && %run %t +// XFAIL: lsan + +#include <assert.h> +#include <sys/mman.h> + +int main() { + assert(0 == mlockall(MCL_CURRENT)); + assert(0 == mlock((void *)0x12345, 0x5678)); + assert(0 == munlockall()); + assert(0 == munlock((void *)0x987, 0x654)); +} + diff --git a/test/sanitizer_common/TestCases/Linux/open_memstream.cc b/test/sanitizer_common/TestCases/Linux/open_memstream.cc new file mode 100644 index 000000000000..6abe0bfb1483 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/open_memstream.cc @@ -0,0 +1,57 @@ +// RUN: %clangxx -m64 -O0 -g -xc++ %s -o %t && %run %t +// RUN: %clangxx -m64 -O3 -g -xc++ %s -o %t && %run %t + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_feature(memory_sanitizer) +#include <sanitizer/msan_interface.h> +static void check_mem_is_good(void *p, size_t s) { + __msan_check_mem_is_initialized(p, s); +} +#elif __has_feature(address_sanitizer) +#include <sanitizer/asan_interface.h> +static void check_mem_is_good(void *p, size_t s) { + assert(__asan_region_is_poisoned(p, s) == 0); +} +#else +static void check_mem_is_good(void *p, size_t s) {} +#endif + +static void run(void) { + char *buf; + size_t buf_len; + fprintf(stderr, " &buf %p, &buf_len %p\n", &buf, &buf_len); + FILE *fp = open_memstream(&buf, &buf_len); + fprintf(fp, "hello"); + fflush(fp); + check_mem_is_good(&buf, sizeof(buf)); + check_mem_is_good(&buf_len, sizeof(buf_len)); + check_mem_is_good(buf, buf_len); + + char *p = new char[1024]; + memset(p, 'a', 1023); + p[1023] = 0; + for (int i = 0; i < 100; ++i) + fprintf(fp, "%s", p); + delete[] p; + fflush(fp); + fprintf(stderr, " %p addr %p, len %zu\n", &buf, buf, buf_len); + check_mem_is_good(&buf, sizeof(buf)); + check_mem_is_good(&buf_len, sizeof(buf_len)); + check_mem_is_good(buf, buf_len); + fclose(fp); + free(buf); +} + +int main(void) { + for (int i = 0; i < 100; ++i) + run(); + return 0; +} diff --git a/test/sanitizer_common/TestCases/Linux/ptrace.cc b/test/sanitizer_common/TestCases/Linux/ptrace.cc new file mode 100644 index 000000000000..2bf0fd2f0f35 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/ptrace.cc @@ -0,0 +1,60 @@ +// RUN: %clangxx -O0 %s -o %t && %run %t + +#include <assert.h> +#include <signal.h> +#include <stdio.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(void) { + pid_t pid; + pid = fork(); + if (pid == 0) { // child + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + execl("/bin/true", "true", NULL); + } else { + wait(NULL); + int res; + +#if __x86_64__ + user_regs_struct regs; + res = ptrace(PTRACE_GETREGS, pid, NULL, ®s); + assert(!res); + if (regs.rip) + printf("%zx\n", regs.rip); + + user_fpregs_struct fpregs; + res = ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs); + assert(!res); + if (fpregs.mxcsr) + printf("%x\n", fpregs.mxcsr); +#endif // __x86_64__ + +#if __powerpc64__ + struct pt_regs regs; + res = ptrace((enum __ptrace_request)PTRACE_GETREGS, pid, NULL, ®s); + assert(!res); + if (regs.nip) + printf("%lx\n", regs.nip); + + elf_fpregset_t fpregs; + res = ptrace((enum __ptrace_request)PTRACE_GETFPREGS, pid, NULL, &fpregs); + assert(!res); + if ((elf_greg_t)fpregs[32]) // fpscr + printf("%lx\n", (elf_greg_t)fpregs[32]); +#endif // __powerpc64__ + + siginfo_t siginfo; + res = ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo); + assert(!res); + assert(siginfo.si_pid == pid); + + ptrace(PTRACE_CONT, pid, NULL, NULL); + + wait(NULL); + } + return 0; +} diff --git a/test/sanitizer_common/TestCases/Linux/timerfd.cc b/test/sanitizer_common/TestCases/Linux/timerfd.cc new file mode 100644 index 000000000000..e7613bb1d111 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/timerfd.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx -O0 -g %s -o %t && %run %t | FileCheck %s +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/timerfd.h> +#include <unistd.h> + +int main (int argc, char** argv) +{ + int fd = timerfd_create(CLOCK_REALTIME, 0); + assert(fd >= 0); + + struct itimerspec its; + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = 1000000; + its.it_interval.tv_sec = its.it_value.tv_sec; + its.it_interval.tv_nsec = its.it_value.tv_nsec; + + int res = timerfd_settime(fd, 0, &its, NULL); + assert(res != -1); + + struct itimerspec its2; + res = timerfd_settime(fd, 0, &its, &its2); + assert(res != -1); + assert(its2.it_interval.tv_sec == its.it_interval.tv_sec); + assert(its2.it_interval.tv_nsec == its.it_interval.tv_nsec); + assert(its2.it_value.tv_sec <= its.it_value.tv_sec); + assert(its2.it_value.tv_nsec <= its.it_value.tv_nsec); + + struct itimerspec its3; + res = timerfd_gettime(fd, &its3); + assert(res != -1); + assert(its3.it_interval.tv_sec == its.it_interval.tv_sec); + assert(its3.it_interval.tv_nsec == its.it_interval.tv_nsec); + assert(its3.it_value.tv_sec <= its.it_value.tv_sec); + assert(its3.it_value.tv_nsec <= its.it_value.tv_nsec); + + + unsigned long long buf; + res = read(fd, &buf, sizeof(buf)); + assert(res == 8); + assert(buf >= 1); + + res = close(fd); + assert(res != -1); + + printf("DONE\n"); + // CHECK: DONE + + return 0; +} diff --git a/test/sanitizer_common/TestCases/corelimit.cc b/test/sanitizer_common/TestCases/corelimit.cc new file mode 100644 index 000000000000..8f54940d04cc --- /dev/null +++ b/test/sanitizer_common/TestCases/corelimit.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx -O0 %s -o %t && %run %t +// XFAIL: lsan + +#include <assert.h> +#include <sys/time.h> +#include <sys/resource.h> + +int main() { + struct rlimit lim_core; + getrlimit(RLIMIT_CORE, &lim_core); + void *p; + if (sizeof(p) == 8) { + assert(0 == lim_core.rlim_max); + } + return 0; +} diff --git a/test/sanitizer_common/TestCases/malloc_hook.cc b/test/sanitizer_common/TestCases/malloc_hook.cc new file mode 100644 index 000000000000..9702249c57e2 --- /dev/null +++ b/test/sanitizer_common/TestCases/malloc_hook.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx -O2 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Malloc/free hooks are not supported on Windows. +// XFAIL: win32 + +#include <stdlib.h> +#include <unistd.h> +#include <sanitizer/allocator_interface.h> + +extern "C" { +const volatile void *global_ptr; + +// Note: avoid calling functions that allocate memory in malloc/free +// to avoid infinite recursion. +void __sanitizer_malloc_hook(const volatile void *ptr, size_t sz) { + if (__sanitizer_get_ownership(ptr)) { + write(1, "MallocHook\n", sizeof("MallocHook\n")); + global_ptr = ptr; + } +} +void __sanitizer_free_hook(const volatile void *ptr) { + if (__sanitizer_get_ownership(ptr) && ptr == global_ptr) + write(1, "FreeHook\n", sizeof("FreeHook\n")); +} +} // extern "C" + +int main() { + volatile int *x = new int; + // CHECK: MallocHook + // Check that malloc hook was called with correct argument. + if (global_ptr != (void*)x) { + _exit(1); + } + *x = 0; + delete x; + // CHECK: FreeHook + return 0; +} diff --git a/test/sanitizer_common/TestCases/print-stack-trace.cc b/test/sanitizer_common/TestCases/print-stack-trace.cc new file mode 100644 index 000000000000..1251f67b83cd --- /dev/null +++ b/test/sanitizer_common/TestCases/print-stack-trace.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx -O0 %s -o %t && %tool_options=stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -O3 %s -o %t && %tool_options=stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s +// RUN: %tool_options='stack_trace_format="frame:%n lineno:%l"' %run %t 2>&1 | FileCheck %s --check-prefix=CUSTOM +// RUN: %tool_options=symbolize_inline_frames=false:stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s --check-prefix=NOINLINE + +#include <sanitizer/common_interface_defs.h> + +static inline void FooBarBaz() { + __sanitizer_print_stack_trace(); +} + +int main() { + FooBarBaz(); + return 0; +} +// CHECK: {{ #0 0x.* in __sanitizer_print_stack_trace}} +// CHECK: {{ #1 0x.* in FooBarBaz(\(\))? .*print-stack-trace.cc:9}} +// CHECK: {{ #2 0x.* in main.*print-stack-trace.cc:13}} + +// CUSTOM: frame:1 lineno:9 +// CUSTOM: frame:2 lineno:13 + +// NOINLINE: #0 0x{{.*}} in __sanitizer_print_stack_trace +// NOINLINE: #1 0x{{.*}} in main{{.*}}print-stack-trace.cc:9 diff --git a/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc b/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc new file mode 100644 index 000000000000..26060f395c11 --- /dev/null +++ b/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx -O0 %s -o %t && %run %t + +#include <assert.h> +#include <pthread.h> + +int main(void) { + pthread_mutexattr_t ma; + int res = pthread_mutexattr_init(&ma); + assert(res == 0); + res = pthread_mutexattr_setpshared(&ma, 1); + assert(res == 0); + int pshared; + res = pthread_mutexattr_getpshared(&ma, &pshared); + assert(res == 0); + assert(pshared == 1); + res = pthread_mutexattr_destroy(&ma); + assert(res == 0); + return 0; +} diff --git a/test/sanitizer_common/Unit/lit.site.cfg.in b/test/sanitizer_common/Unit/lit.site.cfg.in new file mode 100644 index 000000000000..2600585702b2 --- /dev/null +++ b/test/sanitizer_common/Unit/lit.site.cfg.in @@ -0,0 +1,15 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +# Setup config name. +config.name = 'SanitizerCommon-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with sanitizer_common tests. +# FIXME: De-hardcode this path. +config.test_exec_root = os.path.join("@COMPILER_RT_BINARY_DIR@", "lib", + "sanitizer_common", "tests") +config.test_source_root = config.test_exec_root diff --git a/test/sanitizer_common/lit.common.cfg b/test/sanitizer_common/lit.common.cfg new file mode 100644 index 000000000000..fb37815ff472 --- /dev/null +++ b/test/sanitizer_common/lit.common.cfg @@ -0,0 +1,38 @@ +# -*- Python -*- + +# Setup source root. +config.test_source_root = os.path.join(os.path.dirname(__file__), "TestCases") + +config.name = "SanitizerCommon-" + config.tool_name + +if config.tool_name == "asan": + tool_cflags = ["-fsanitize=address"] + tool_options = "ASAN_OPTIONS" +elif config.tool_name == "tsan": + tool_cflags = ["-fsanitize=thread"] + tool_options = "TSAN_OPTIONS" +elif config.tool_name == "msan": + tool_cflags = ["-fsanitize=memory"] + tool_options = "MSAN_OPTIONS" +elif config.tool_name == "lsan": + tool_cflags = ["-fsanitize=leak"] + tool_options = "LSAN_OPTIONS" +else: + lit_config.fatal("Unknown tool for sanitizer_common tests: %r" % config.tool_name) + +config.available_features.add(config.tool_name) + +clang_cflags = config.debug_info_flags + tool_cflags + [config.target_cflags] +clang_cxxflags = config.cxx_mode_flags + clang_cflags + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) ) +config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) ) +config.substitutions.append( ("%tool_options", tool_options) ) + +config.suffixes = ['.c', '.cc', '.cpp'] + +if config.host_os not in ['Linux', 'Darwin']: + config.unsupported = True diff --git a/test/sanitizer_common/lit.site.cfg.in b/test/sanitizer_common/lit.site.cfg.in new file mode 100644 index 000000000000..1e94aa567632 --- /dev/null +++ b/test/sanitizer_common/lit.site.cfg.in @@ -0,0 +1,9 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Tool-specific config options. +config.tool_name = "@SANITIZER_COMMON_LIT_TEST_MODE@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@SANITIZER_COMMON_LIT_SOURCE_DIR@/lit.common.cfg") + diff --git a/test/tsan/CMakeLists.txt b/test/tsan/CMakeLists.txt new file mode 100644 index 000000000000..29c0821b3c5a --- /dev/null +++ b/test/tsan/CMakeLists.txt @@ -0,0 +1,27 @@ +set(TSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND TSAN_TEST_DEPS tsan) +endif() +if(COMPILER_RT_HAS_LIBCXX_SOURCES AND + COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang") + list(APPEND TSAN_TEST_DEPS libcxx_tsan) + set(TSAN_HAS_LIBCXX True) +else() + set(TSAN_HAS_LIBCXX False) +endif() + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + +if(COMPILER_RT_INCLUDE_TESTS) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) + list(APPEND TSAN_TEST_DEPS TsanUnitTests) +endif() + +add_lit_testsuite(check-tsan "Running ThreadSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${TSAN_TEST_DEPS}) +set_target_properties(check-tsan PROPERTIES FOLDER "TSan tests") diff --git a/test/tsan/Linux/lit.local.cfg b/test/tsan/Linux/lit.local.cfg new file mode 100644 index 000000000000..57271b8078a4 --- /dev/null +++ b/test/tsan/Linux/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/tsan/Linux/mutex_robust.cc b/test/tsan/Linux/mutex_robust.cc new file mode 100644 index 000000000000..5ca5e70d49a7 --- /dev/null +++ b/test/tsan/Linux/mutex_robust.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +pthread_mutex_t m; + +void *thr(void *p) { + pthread_mutex_lock(&m); + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_lock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a correct code, and tsan must not bark. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/test/tsan/Linux/mutex_robust2.cc b/test/tsan/Linux/mutex_robust2.cc new file mode 100644 index 000000000000..0914c1763604 --- /dev/null +++ b/test/tsan/Linux/mutex_robust2.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +pthread_mutex_t m; +int x; + +void *thr(void *p) { + pthread_mutex_lock(&m); + x = 42; + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_trylock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + x = 43; + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a false positive, tsan must not bark at the data race. +// But currently it does. +// CHECK-NOT: WARNING: ThreadSanitizer WARNING: double lock of mutex +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/test/tsan/Linux/user_fopen.cc b/test/tsan/Linux/user_fopen.cc new file mode 100644 index 000000000000..c0ff267ff88b --- /dev/null +++ b/test/tsan/Linux/user_fopen.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> + +// Defined by tsan. +extern "C" FILE *__interceptor_fopen(const char *file, const char *mode); +extern "C" int __interceptor_fileno(FILE *f); + +extern "C" FILE *fopen(const char *file, const char *mode) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user fopen\n"); + return __interceptor_fopen(file, mode); +} + +extern "C" int fileno(FILE *f) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user fileno\n"); + return 1; +} + +int main() { + FILE *f = fopen("/dev/zero", "r"); + if (f) { + char buf; + fread(&buf, 1, 1, f); + fclose(f); + } +} + +// CHECK: user fopen +// CHECK-NOT: ThreadSanitizer + diff --git a/test/tsan/Linux/user_malloc.cc b/test/tsan/Linux/user_malloc.cc new file mode 100644 index 000000000000..c671bfcdd17a --- /dev/null +++ b/test/tsan/Linux/user_malloc.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdio.h> + +// Defined by tsan. +extern "C" void *__interceptor_malloc(unsigned long size); +extern "C" void __interceptor_free(void *p); + +extern "C" void *malloc(unsigned long size) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user malloc\n"); + return __interceptor_malloc(size); +} + +extern "C" void free(void *p) { + __interceptor_free(p); +} + +int main() { + volatile char *p = (char*)malloc(10); + p[0] = 0; + free((void*)p); +} + +// CHECK: user malloc +// CHECK-NOT: ThreadSanitizer + diff --git a/test/tsan/Unit/lit.site.cfg.in b/test/tsan/Unit/lit.site.cfg.in new file mode 100644 index 000000000000..9498105653a1 --- /dev/null +++ b/test/tsan/Unit/lit.site.cfg.in @@ -0,0 +1,14 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +# Setup config name. +config.name = 'ThreadSanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with ASan unit tests. +# FIXME: De-hardcode this path. +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/tsan/tests" +config.test_source_root = config.test_exec_root diff --git a/test/tsan/aligned_vs_unaligned_race.cc b/test/tsan/aligned_vs_unaligned_race.cc new file mode 100644 index 000000000000..f82542ed2b85 --- /dev/null +++ b/test/tsan/aligned_vs_unaligned_race.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// Race between an aligned access and an unaligned access, which +// touches the same memory region. +// This is a real race which is not detected by tsan. +// https://code.google.com/p/thread-sanitizer/issues/detail?id=17 +#include <pthread.h> +#include <stdio.h> +#include <stdint.h> + +uint64_t Global[2]; + +void *Thread1(void *x) { + Global[1]++; + return NULL; +} + +void *Thread2(void *x) { + char *p1 = reinterpret_cast<char *>(&Global[0]); + uint64_t *p4 = reinterpret_cast<uint64_t *>(p1 + 1); + (*p4)++; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("Pass\n"); + // CHECK-NOT: ThreadSanitizer: data race + // CHECK: Pass + return 0; +} diff --git a/test/tsan/allocator_returns_null.cc b/test/tsan/allocator_returns_null.cc new file mode 100644 index 000000000000..cde706bc8a1d --- /dev/null +++ b/test/tsan/allocator_returns_null.cc @@ -0,0 +1,64 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + fprintf(stderr, "x: %p\n", x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: ThreadSanitizer's allocator is terminating the process + diff --git a/test/tsan/atexit.cc b/test/tsan/atexit.cc new file mode 100644 index 000000000000..69acb4dd783f --- /dev/null +++ b/test/tsan/atexit.cc @@ -0,0 +1,29 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +class Logger { + public: + Logger() { + fprintf(stderr, "Logger ctor\n"); + } + + ~Logger() { + fprintf(stderr, "Logger dtor\n"); + } +}; + +Logger logger; + +void log_from_atexit() { + fprintf(stderr, "In log_from_atexit\n"); +} + +int main() { + atexit(log_from_atexit); +} + +// CHECK: Logger ctor +// CHECK: In log_from_atexit +// CHECK: Logger dtor diff --git a/test/tsan/atexit2.cc b/test/tsan/atexit2.cc new file mode 100644 index 000000000000..6f74c5f9f6e5 --- /dev/null +++ b/test/tsan/atexit2.cc @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +int n; +const int N = 10000; + +static void atexit1() { + n++; +} + +static void atexit0() { + fprintf(stderr, "run count: %d\n", n); +} + +int main() { + atexit(atexit0); + for (int i = 0; i < N; i++) + atexit(atexit1); +} + +// CHECK-NOT: FATAL: ThreadSanitizer +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: run count: 10000 + diff --git a/test/tsan/atomic_free.cc b/test/tsan/atomic_free.cc new file mode 100644 index 000000000000..1dcf887c41d5 --- /dev/null +++ b/test/tsan/atomic_free.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *a) { + __atomic_fetch_add((int*)a, 1, __ATOMIC_SEQ_CST); + return 0; +} + +int main() { + int *a = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, a); + sleep(1); + delete a; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/atomic_free2.cc b/test/tsan/atomic_free2.cc new file mode 100644 index 000000000000..c50be6bba940 --- /dev/null +++ b/test/tsan/atomic_free2.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *a) { + sleep(1); + __atomic_fetch_add((int*)a, 1, __ATOMIC_SEQ_CST); + return 0; +} + +int main() { + int *a = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, a); + delete a; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: heap-use-after-free diff --git a/test/tsan/atomic_norace.cc b/test/tsan/atomic_norace.cc new file mode 100644 index 000000000000..d9ccda58883c --- /dev/null +++ b/test/tsan/atomic_norace.cc @@ -0,0 +1,61 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +const int kTestCount = 4; +typedef long long T; +T atomics[kTestCount * 2]; + +void Test(int test, T *p, bool main_thread) { + volatile T sink; + if (test == 0) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + } else if (test == 1) { + if (main_thread) + __atomic_exchange_n(p, 1, __ATOMIC_ACQ_REL); + else + __atomic_exchange_n(p, 1, __ATOMIC_ACQ_REL); + } else if (test == 2) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + __atomic_store_n(p, 1, __ATOMIC_SEQ_CST); + } else if (test == 3) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + sink = *p; + } +} + +void *Thread(void *p) { + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[i], false); + } + sleep(2); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d reverse\n", i); + Test(i, &atomics[kTestCount + i], false); + } + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d\n", i); + Test(i, &atomics[i], true); + } + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[kTestCount + i], true); + } + pthread_join(t, 0); +} + +// CHECK-NOT: ThreadSanitizer: data race diff --git a/test/tsan/atomic_race.cc b/test/tsan/atomic_race.cc new file mode 100644 index 000000000000..9cee8ed8272f --- /dev/null +++ b/test/tsan/atomic_race.cc @@ -0,0 +1,80 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> + +const int kTestCount = 4; +typedef long long T; +T atomics[kTestCount * 2]; + +void Test(int test, T *p, bool main_thread) { + volatile T sink; + if (test == 0) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + *p = 42; + } else if (test == 1) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + sink = *p; + } else if (test == 2) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + *p = 42; + } else if (test == 3) { + if (main_thread) + __atomic_store_n(p, 1, __ATOMIC_SEQ_CST); + else + sink = *p; + } +} + +void *Thread(void *p) { + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[i], false); + } + sleep(4); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d reverse\n", i); + Test(i, &atomics[kTestCount + i], false); + } + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d\n", i); + Test(i, &atomics[i], true); + } + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[kTestCount + i], true); + } + pthread_join(t, 0); +} + +// CHECK: Test 0 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 1 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 2 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 3 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 0 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 1 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 2 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 3 reverse +// CHECK: ThreadSanitizer: data race diff --git a/test/tsan/atomic_stack.cc b/test/tsan/atomic_stack.cc new file mode 100644 index 000000000000..7e3176f8e784 --- /dev/null +++ b/test/tsan/atomic_stack.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + __atomic_fetch_add(&Global, 1, __ATOMIC_RELAXED); + return NULL; +} + +void *Thread2(void *x) { + Global++; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Atomic write of size 4 +// CHECK: #0 __tsan_atomic32_fetch_add +// CHECK: #1 Thread1 diff --git a/test/tsan/barrier.cc b/test/tsan/barrier.cc new file mode 100644 index 000000000000..d8c2b6ffe514 --- /dev/null +++ b/test/tsan/barrier.cc @@ -0,0 +1,37 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// CHECK-NOT: ThreadSanitizer: data race +// CHECK: DONE + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> + +const int kSize = 4; +volatile int kIter = 10; // prevent unwinding +int data[2][kSize]; +pthread_barrier_t barrier; + +void *thr(void *p) { + int idx = (int)(long)p; + for (int i = 0; i < kIter; i++) { + int *prev = data[i % 2]; + int *curr = data[(i + 1) % 2]; + int left = idx - 1 >= 0 ? prev[idx - 1] : 0; + int right = idx + 1 < kSize ? prev[idx + 1] : 0; + curr[idx] = (left + right) / 2; + pthread_barrier_wait(&barrier); + } + return 0; +} + +int main() { + pthread_barrier_init(&barrier, 0, kSize); + pthread_t th[kSize]; + for (int i = 0; i < kSize; i++) + pthread_create(&th[i], 0, thr, (void*)(long)i); + for (int i = 0; i < kSize; i++) + pthread_join(th[i], 0); + pthread_barrier_destroy(&barrier); + fprintf(stderr, "DONE\n"); +} diff --git a/test/tsan/bench.h b/test/tsan/bench.h new file mode 100644 index 000000000000..5ae0dd813db9 --- /dev/null +++ b/test/tsan/bench.h @@ -0,0 +1,59 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <stdio.h> +#include <time.h> + +int bench_nthread; +int bench_niter; +int grow_clock_var; +pthread_barrier_t glow_clock_barrier; + +void bench(); // defined by user +void start_thread_group(int nth, void(*f)(int tid)); +void grow_clock_worker(int tid); + +int main(int argc, char **argv) { + bench_nthread = 2; + if (argc > 1) + bench_nthread = atoi(argv[1]); + bench_niter = 100; + if (argc > 2) + bench_niter = atoi(argv[2]); + + // Grow thread's clock. + int clock_size = 10; + if (argc > 1) + clock_size = 1000; + pthread_barrier_init(&glow_clock_barrier, 0, clock_size); + start_thread_group(clock_size, grow_clock_worker); + pthread_barrier_destroy(&glow_clock_barrier); + __atomic_load_n(&grow_clock_var, __ATOMIC_ACQUIRE); + + timespec tp0; + clock_gettime(CLOCK_MONOTONIC, &tp0); + bench(); + timespec tp1; + clock_gettime(CLOCK_MONOTONIC, &tp1); + unsigned long long t = + (tp1.tv_sec * 1000000000ULL + tp1.tv_nsec) - + (tp0.tv_sec * 1000000000ULL + tp0.tv_nsec); + fprintf(stderr, "%llu ns/iter\n", t / bench_niter); + fprintf(stderr, "DONE\n"); +} + +void start_thread_group(int nth, void(*f)(int tid)) { + pthread_t *th = (pthread_t*)malloc(nth * sizeof(pthread_t)); + for (int i = 0; i < nth; i++) + pthread_create(&th[i], 0, (void*(*)(void*))f, (void*)(long)i); + for (int i = 0; i < nth; i++) + pthread_join(th[i], 0); +} + +void grow_clock_worker(int tid) { + int res = pthread_barrier_wait(&glow_clock_barrier); + if (res == PTHREAD_BARRIER_SERIAL_THREAD) + __atomic_store_n(&grow_clock_var, 0, __ATOMIC_RELEASE); +} + diff --git a/test/tsan/bench_acquire_only.cc b/test/tsan/bench_acquire_only.cc new file mode 100644 index 000000000000..5cd6bd74ebee --- /dev/null +++ b/test/tsan/bench_acquire_only.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include "bench.h" + +int x; + +void thread(int tid) { + for (int i = 0; i < bench_niter; i++) + __atomic_load_n(&x, __ATOMIC_ACQUIRE); +} + +void bench() { + __atomic_store_n(&x, 0, __ATOMIC_RELEASE); + start_thread_group(bench_nthread, thread); +} + +// CHECK: DONE + diff --git a/test/tsan/bench_acquire_release.cc b/test/tsan/bench_acquire_release.cc new file mode 100644 index 000000000000..9e53a7b26efa --- /dev/null +++ b/test/tsan/bench_acquire_release.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include "bench.h" + +int x; + +void thread(int tid) { + for (int i = 0; i < bench_niter; i++) + __atomic_fetch_add(&x, 1, __ATOMIC_ACQ_REL); +} + +void bench() { + start_thread_group(bench_nthread, thread); +} + +// CHECK: DONE + diff --git a/test/tsan/bench_local_mutex.cc b/test/tsan/bench_local_mutex.cc new file mode 100644 index 000000000000..0fa1db0c883c --- /dev/null +++ b/test/tsan/bench_local_mutex.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include "bench.h" + +pthread_mutex_t *mtx; +const int kStride = 16; + +void thread(int tid) { + for (int i = 0; i < bench_niter; i++) { + pthread_mutex_lock(&mtx[tid * kStride]); + pthread_mutex_unlock(&mtx[tid * kStride]); + } +} + +void bench() { + mtx = (pthread_mutex_t*)malloc(bench_nthread * kStride * sizeof(*mtx)); + for (int i = 0; i < bench_nthread; i++) { + pthread_mutex_init(&mtx[i * kStride], 0); + pthread_mutex_lock(&mtx[i * kStride]); + pthread_mutex_unlock(&mtx[i * kStride]); + } + start_thread_group(bench_nthread, thread); +} + +// CHECK: DONE + diff --git a/test/tsan/bench_mutex.cc b/test/tsan/bench_mutex.cc new file mode 100644 index 000000000000..324d53fd7f28 --- /dev/null +++ b/test/tsan/bench_mutex.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include "bench.h" + +pthread_mutex_t mtx; +pthread_cond_t cv; +int x; + +void thread(int tid) { + for (int i = 0; i < bench_niter; i++) { + pthread_mutex_lock(&mtx); + while (x != i * 2 + tid) + pthread_cond_wait(&cv, &mtx); + x++; + pthread_cond_signal(&cv); + pthread_mutex_unlock(&mtx); + } +} + +void bench() { + pthread_mutex_init(&mtx, 0); + pthread_cond_init(&cv, 0); + start_thread_group(2, thread); +} + +// CHECK: DONE + diff --git a/test/tsan/bench_release_only.cc b/test/tsan/bench_release_only.cc new file mode 100644 index 000000000000..0a86f73f249e --- /dev/null +++ b/test/tsan/bench_release_only.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include "bench.h" + +int *x; +const int kStride = 32; + +void thread(int tid) { + __atomic_load_n(&x[tid * kStride], __ATOMIC_ACQUIRE); + for (int i = 0; i < bench_niter; i++) + __atomic_store_n(&x[tid * kStride], 0, __ATOMIC_RELEASE); +} + +void bench() { + x = (int*)malloc(bench_nthread * kStride * sizeof(x[0])); + for (int i = 0; i < bench_nthread; i++) + __atomic_store_n(&x[i * kStride], 0, __ATOMIC_RELEASE); + start_thread_group(bench_nthread, thread); +} + +// CHECK: DONE + diff --git a/test/tsan/bench_rwmutex.cc b/test/tsan/bench_rwmutex.cc new file mode 100644 index 000000000000..818ee8c82bc1 --- /dev/null +++ b/test/tsan/bench_rwmutex.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include "bench.h" + +pthread_rwlock_t mtx; + +void thread(int tid) { + for (int i = 0; i < bench_niter; i++) { + pthread_rwlock_rdlock(&mtx); + pthread_rwlock_unlock(&mtx); + } +} + +void bench() { + pthread_rwlock_init(&mtx, 0); + pthread_rwlock_wrlock(&mtx); + pthread_rwlock_unlock(&mtx); + pthread_rwlock_rdlock(&mtx); + pthread_rwlock_unlock(&mtx); + start_thread_group(bench_nthread, thread); +} + +// CHECK: DONE + diff --git a/test/tsan/bench_shadow_flush.cc b/test/tsan/bench_shadow_flush.cc new file mode 100644 index 000000000000..0f412bbe82f9 --- /dev/null +++ b/test/tsan/bench_shadow_flush.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <pthread.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <stdio.h> +#include <time.h> +#include <sys/mman.h> + +const long kSmallPage = 4 << 10; +const long kLargePage = 2 << 20; +const long kStride = 1 << 10; + +typedef unsigned long uptr; + +int main(int argc, const char **argv) { + uptr mem_size = 4 << 20; + if (argc > 1) + mem_size = (uptr)atoi(argv[1]) << 20; + uptr stride = kSmallPage; + if (argc > 2) + stride = (uptr)atoi(argv[2]) << 10; + int niter = 1; + if (argc > 3) + niter = atoi(argv[3]); + int stride2 = 1; + if (argc > 4) + stride2 = atoi(argv[4]); + + uptr sz = mem_size + stride2 * kStride + kLargePage; + void *p = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + uptr a = ((uptr)p + kLargePage - 1) & ~(kLargePage - 1); + volatile char *mem = (volatile char *)a; + + for (int i = 0; i < niter; i++) { + for (uptr off = 0; off < mem_size; off += stride) { + for (uptr off2 = 0; off2 < stride2; off2++) + mem[off + off2 * kStride] = 42; + } + } + + fprintf(stderr, "DONE\n"); +} + +// CHECK: DONE + diff --git a/test/tsan/bench_single_writer.cc b/test/tsan/bench_single_writer.cc new file mode 100644 index 000000000000..0d3810a03ad0 --- /dev/null +++ b/test/tsan/bench_single_writer.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include "bench.h" + +int x; + +void thread(int tid) { + if (tid == 0) { + for (int i = 0; i < bench_niter; i++) + __atomic_store_n(&x, 0, __ATOMIC_RELEASE); + } else { + for (int i = 0; i < bench_niter; i++) + __atomic_load_n(&x, __ATOMIC_ACQUIRE); + } +} + +void bench() { + start_thread_group(bench_nthread, thread); +} + +// CHECK: DONE + diff --git a/test/tsan/bench_ten_mutexes.cc b/test/tsan/bench_ten_mutexes.cc new file mode 100644 index 000000000000..876f1365ee43 --- /dev/null +++ b/test/tsan/bench_ten_mutexes.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include "bench.h" + +const int kMutex = 10; +pthread_mutex_t mtx[kMutex]; + +void thread(int tid) { + for (int i = 0; i < bench_niter; i++) { + int idx = (i % kMutex); + if (tid == 0) + idx = kMutex - idx - 1; + pthread_mutex_lock(&mtx[idx]); + pthread_mutex_unlock(&mtx[idx]); + } +} + +void bench() { + for (int i = 0; i < kMutex; i++) + pthread_mutex_init(&mtx[i], 0); + start_thread_group(2, thread); +} + +// CHECK: DONE + diff --git a/test/tsan/benign_race.cc b/test/tsan/benign_race.cc new file mode 100644 index 000000000000..b6cba19aa96e --- /dev/null +++ b/test/tsan/benign_race.cc @@ -0,0 +1,39 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +int WTFGlobal; + +extern "C" { +void AnnotateBenignRaceSized(const char *f, int l, + void *mem, unsigned int size, const char *desc); +void WTFAnnotateBenignRaceSized(const char *f, int l, + void *mem, unsigned int size, + const char *desc); +} + + +void *Thread(void *x) { + Global = 42; + WTFGlobal = 142; + return 0; +} + +int main() { + AnnotateBenignRaceSized(__FILE__, __LINE__, + &Global, sizeof(Global), "Race on Global"); + WTFAnnotateBenignRaceSized(__FILE__, __LINE__, + &WTFGlobal, sizeof(WTFGlobal), + "Race on WTFGlobal"); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + Global = 43; + WTFGlobal = 143; + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/blacklist.cc b/test/tsan/blacklist.cc new file mode 100644 index 000000000000..d6ca383cb758 --- /dev/null +++ b/test/tsan/blacklist.cc @@ -0,0 +1,30 @@ +// Test blacklist functionality for TSan. + +// RUN: echo "fun:*Blacklisted_Thread2*" > %t.blacklist +// RUN: %clangxx_tsan -O1 %s -fsanitize-blacklist=%t.blacklist -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int Global; + +void *Thread1(void *x) { + Global++; + return NULL; +} + +void *Blacklisted_Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Blacklisted_Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: ThreadSanitizer: data race diff --git a/test/tsan/blacklist2.cc b/test/tsan/blacklist2.cc new file mode 100644 index 000000000000..1092561e55bb --- /dev/null +++ b/test/tsan/blacklist2.cc @@ -0,0 +1,49 @@ +// Test that blacklisted functions are still contained in the stack trace. + +// RUN: echo "fun:*Blacklisted_Thread2*" > %t.blacklist +// RUN: echo "fun:*CallTouchGlobal*" >> %t.blacklist + +// RUN: %clangxx_tsan -O1 %s -fsanitize-blacklist=%t.blacklist -o %t +// RUN: %deflake %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + // CHECK: ThreadSanitizer: data race + // CHECK: Write of size 4 + // CHECK: #0 Thread1{{.*}}blacklist2.cc:[[@LINE+1]] + Global++; + return NULL; +} + +void TouchGlobal() { + // CHECK: Previous write of size 4 + // CHECK: #0 TouchGlobal{{.*}}blacklist2.cc:[[@LINE+1]] + Global--; +} + +void CallTouchGlobal() { + // CHECK: #1 CallTouchGlobal{{.*}}blacklist2.cc:[[@LINE+1]] + TouchGlobal(); +} + +void *Blacklisted_Thread2(void *x) { + Global--; + // CHECK: #2 Blacklisted_Thread2{{.*}}blacklist2.cc:[[@LINE+1]] + CallTouchGlobal(); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Blacklisted_Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("PASS\n"); + return 0; +} diff --git a/test/tsan/cond.c b/test/tsan/cond.c new file mode 100644 index 000000000000..05ea672c6c1c --- /dev/null +++ b/test/tsan/cond.c @@ -0,0 +1,53 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: ThreadSanitizer WARNING: double lock +// CHECK-NOT: ThreadSanitizer WARNING: mutex unlock by another thread +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +pthread_mutex_t m; +pthread_cond_t c; +int x; + +void *thr1(void *p) { + int i; + + for (i = 0; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_cond_signal(&c); + pthread_mutex_unlock(&m); + } + return 0; +} + +void *thr2(void *p) { + int i; + + for (i = 1; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_mutex_unlock(&m); + pthread_cond_broadcast(&c); + } + return 0; +} + +int main() { + pthread_t th1, th2; + + pthread_mutex_init(&m, 0); + pthread_cond_init(&c, 0); + pthread_create(&th1, 0, thr1, 0); + pthread_create(&th2, 0, thr2, 0); + pthread_join(th1, 0); + pthread_join(th2, 0); + fprintf(stderr, "OK\n"); +} diff --git a/test/tsan/cond_cancel.c b/test/tsan/cond_cancel.c new file mode 100644 index 000000000000..397cad4b1838 --- /dev/null +++ b/test/tsan/cond_cancel.c @@ -0,0 +1,37 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// CHECK-NOT: WARNING +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> + +pthread_mutex_t m; +pthread_cond_t c; +int x; + +void *thr1(void *p) { + pthread_mutex_lock(&m); + pthread_cleanup_push((void(*)(void *arg))pthread_mutex_unlock, &m); + while (x == 0) + pthread_cond_wait(&c, &m); + pthread_cleanup_pop(1); + return 0; +} + +int main() { + pthread_t th; + + pthread_mutex_init(&m, 0); + pthread_cond_init(&c, 0); + + pthread_create(&th, 0, thr1, 0); + sleep(1); // let it block on cond var + pthread_cancel(th); + + pthread_join(th, 0); + pthread_mutex_lock(&m); + pthread_mutex_unlock(&m); + fprintf(stderr, "OK\n"); +} diff --git a/test/tsan/cond_race.cc b/test/tsan/cond_race.cc new file mode 100644 index 000000000000..fa42fafca4d2 --- /dev/null +++ b/test/tsan/cond_race.cc @@ -0,0 +1,40 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// CHECK-NOT: unlock of unlocked mutex +// CHECK: ThreadSanitizer: data race +// CHECK: pthread_cond_signal + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> + +struct Ctx { + pthread_mutex_t m; + pthread_cond_t c; + bool done; +}; + +void *thr(void *p) { + Ctx *c = (Ctx*)p; + pthread_mutex_lock(&c->m); + c->done = true; + pthread_mutex_unlock(&c->m); + pthread_cond_signal(&c->c); + return 0; +} + +int main() { + Ctx *c = new Ctx(); + pthread_mutex_init(&c->m, 0); + pthread_cond_init(&c->c, 0); + pthread_t th; + pthread_create(&th, 0, thr, c); + pthread_mutex_lock(&c->m); + while (!c->done) + pthread_cond_wait(&c->c, &c->m); + pthread_mutex_unlock(&c->m); + // w/o this sleep, it can be reported as use-after-free + sleep(1); + delete c; + pthread_join(th, 0); +} diff --git a/test/tsan/cond_version.c b/test/tsan/cond_version.c new file mode 100644 index 000000000000..2282c3ad738d --- /dev/null +++ b/test/tsan/cond_version.c @@ -0,0 +1,44 @@ +// RUN: %clang_tsan -O1 %s -o %t -lrt && %run %t 2>&1 | FileCheck %s +// Test that pthread_cond is properly intercepted, +// previously there were issues with versioned symbols. +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <time.h> +#include <errno.h> + +int main() { + typedef unsigned long long u64; + pthread_mutex_t m; + pthread_cond_t c; + pthread_condattr_t at; + struct timespec ts0, ts1, ts2; + int res; + u64 sleep; + + pthread_mutex_init(&m, 0); + pthread_condattr_init(&at); + pthread_condattr_setclock(&at, CLOCK_MONOTONIC); + pthread_cond_init(&c, &at); + + clock_gettime(CLOCK_MONOTONIC, &ts0); + ts1 = ts0; + ts1.tv_sec += 2; + + pthread_mutex_lock(&m); + do { + res = pthread_cond_timedwait(&c, &m, &ts1); + } while (res == 0); + pthread_mutex_unlock(&m); + + clock_gettime(CLOCK_MONOTONIC, &ts2); + sleep = (u64)ts2.tv_sec * 1000000000 + ts2.tv_nsec - + ((u64)ts0.tv_sec * 1000000000 + ts0.tv_nsec); + if (res != ETIMEDOUT) + exit(printf("bad return value %d, want %d\n", res, ETIMEDOUT)); + if (sleep < 1000000000) + exit(printf("bad sleep duration %lluns, want %dns\n", sleep, 1000000000)); + fprintf(stderr, "OK\n"); +} diff --git a/test/tsan/deadlock_detector_stress_test.cc b/test/tsan/deadlock_detector_stress_test.cc new file mode 100644 index 000000000000..53624782e07b --- /dev/null +++ b/test/tsan/deadlock_detector_stress_test.cc @@ -0,0 +1,596 @@ +// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadMutex +// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND +// TSAN_OPTIONS="detect_deadlocks=1 second_deadlock_stack=1" %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND +// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadSpinLock +// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRWLock +// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RD +// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRecursiveMutex +// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-REC +#include <pthread.h> +#undef NDEBUG +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <new> + +#ifndef LockType +#define LockType PthreadMutex +#endif + +// You can optionally pass [test_number [iter_count]] on command line. +static int test_number = -1; +static int iter_count = 100000; + +class PthreadMutex { + public: + explicit PthreadMutex(bool recursive = false) { + if (recursive) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + assert(0 == pthread_mutex_init(&mu_, &attr)); + } else { + assert(0 == pthread_mutex_init(&mu_, 0)); + } + } + ~PthreadMutex() { + assert(0 == pthread_mutex_destroy(&mu_)); + (void)padding_; + } + static bool supports_read_lock() { return false; } + static bool supports_recursive_lock() { return false; } + void lock() { assert(0 == pthread_mutex_lock(&mu_)); } + void unlock() { assert(0 == pthread_mutex_unlock(&mu_)); } + bool try_lock() { return 0 == pthread_mutex_trylock(&mu_); } + void rdlock() { assert(0); } + void rdunlock() { assert(0); } + bool try_rdlock() { assert(0); } + + private: + pthread_mutex_t mu_; + char padding_[64 - sizeof(pthread_mutex_t)]; +}; + +class PthreadRecursiveMutex : public PthreadMutex { + public: + PthreadRecursiveMutex() : PthreadMutex(true) { } + static bool supports_recursive_lock() { return true; } +}; + +class PthreadSpinLock { + public: + PthreadSpinLock() { assert(0 == pthread_spin_init(&mu_, 0)); } + ~PthreadSpinLock() { + assert(0 == pthread_spin_destroy(&mu_)); + (void)padding_; + } + static bool supports_read_lock() { return false; } + static bool supports_recursive_lock() { return false; } + void lock() { assert(0 == pthread_spin_lock(&mu_)); } + void unlock() { assert(0 == pthread_spin_unlock(&mu_)); } + bool try_lock() { return 0 == pthread_spin_trylock(&mu_); } + void rdlock() { assert(0); } + void rdunlock() { assert(0); } + bool try_rdlock() { assert(0); } + + private: + pthread_spinlock_t mu_; + char padding_[64 - sizeof(pthread_spinlock_t)]; +}; + +class PthreadRWLock { + public: + PthreadRWLock() { assert(0 == pthread_rwlock_init(&mu_, 0)); } + ~PthreadRWLock() { + assert(0 == pthread_rwlock_destroy(&mu_)); + (void)padding_; + } + static bool supports_read_lock() { return true; } + static bool supports_recursive_lock() { return false; } + void lock() { assert(0 == pthread_rwlock_wrlock(&mu_)); } + void unlock() { assert(0 == pthread_rwlock_unlock(&mu_)); } + bool try_lock() { return 0 == pthread_rwlock_trywrlock(&mu_); } + void rdlock() { assert(0 == pthread_rwlock_rdlock(&mu_)); } + void rdunlock() { assert(0 == pthread_rwlock_unlock(&mu_)); } + bool try_rdlock() { return 0 == pthread_rwlock_tryrdlock(&mu_); } + + private: + pthread_rwlock_t mu_; + char padding_[64 - sizeof(pthread_rwlock_t)]; +}; + +class LockTest { + public: + LockTest() : n_(), locks_() {} + void Init(size_t n) { + n_ = n; + locks_ = new LockType*[n_]; + for (size_t i = 0; i < n_; i++) + locks_[i] = new LockType; + } + ~LockTest() { + for (size_t i = 0; i < n_; i++) + delete locks_[i]; + delete [] locks_; + } + void L(size_t i) { + assert(i < n_); + locks_[i]->lock(); + } + + void U(size_t i) { + assert(i < n_); + locks_[i]->unlock(); + } + + void RL(size_t i) { + assert(i < n_); + locks_[i]->rdlock(); + } + + void RU(size_t i) { + assert(i < n_); + locks_[i]->rdunlock(); + } + + void *A(size_t i) { + assert(i < n_); + return locks_[i]; + } + + bool T(size_t i) { + assert(i < n_); + return locks_[i]->try_lock(); + } + + // Simple lock order onversion. + void Test1() { + if (test_number > 0 && test_number != 1) return; + fprintf(stderr, "Starting Test1\n"); + // CHECK: Starting Test1 + Init(5); + fprintf(stderr, "Expecting lock inversion: %p %p\n", A(0), A(1)); + // CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]] + Lock_0_1(); + Lock_1_0(); + // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) + // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ([[A1]]) => [[M2:M[0-9]+]] ([[A2]]) => [[M1]] + // CHECK: Mutex [[M2]] acquired here while holding mutex [[M1]] + // CHECK: #0 pthread_ + // CHECK-SECOND: Mutex [[M1]] previously acquired by the same thread here: + // CHECK-SECOND: #0 pthread_ + // CHECK-NOT-SECOND: second_deadlock_stack=1 to get more informative warning message + // CHECK-NOT-SECOND-NOT: #0 pthread_ + // CHECK: Mutex [[M1]] acquired here while holding mutex [[M2]] + // CHECK: #0 pthread_ + // CHECK-SECOND: Mutex [[M2]] previously acquired by the same thread here: + // CHECK-SECOND: #0 pthread_ + // CHECK-NOT-SECOND-NOT: #0 pthread_ + // CHECK-NOT: WARNING: ThreadSanitizer: + } + + // Simple lock order inversion with 3 locks. + void Test2() { + if (test_number > 0 && test_number != 2) return; + fprintf(stderr, "Starting Test2\n"); + // CHECK: Starting Test2 + Init(5); + fprintf(stderr, "Expecting lock inversion: %p %p %p\n", A(0), A(1), A(2)); + // CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]] [[A3:0x[a-f0-9]*]] + Lock2(0, 1); + Lock2(1, 2); + Lock2(2, 0); + // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) + // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ([[A1]]) => [[M2:M[0-9]+]] ([[A2]]) => [[M3:M[0-9]+]] ([[A3]]) => [[M1]] + // CHECK-NOT: WARNING: ThreadSanitizer: + } + + // Lock order inversion with lots of new locks created (but not used) + // between. Since the new locks are not used we should still detect the + // deadlock. + void Test3() { + if (test_number > 0 && test_number != 3) return; + fprintf(stderr, "Starting Test3\n"); + // CHECK: Starting Test3 + Init(5); + Lock_0_1(); + L(2); + CreateAndDestroyManyLocks(); + U(2); + Lock_1_0(); + // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) + // CHECK-NOT: WARNING: ThreadSanitizer: + } + + // lock l0=>l1; then create and use lots of locks; then lock l1=>l0. + // The deadlock epoch should have changed and we should not report anything. + void Test4() { + if (test_number > 0 && test_number != 4) return; + fprintf(stderr, "Starting Test4\n"); + // CHECK: Starting Test4 + Init(5); + Lock_0_1(); + L(2); + CreateLockUnlockAndDestroyManyLocks(); + U(2); + Lock_1_0(); + // CHECK-NOT: WARNING: ThreadSanitizer: + } + + void Test5() { + if (test_number > 0 && test_number != 5) return; + fprintf(stderr, "Starting Test5\n"); + // CHECK: Starting Test5 + Init(5); + RunThreads(&LockTest::Lock_0_1, &LockTest::Lock_1_0); + // CHECK: WARNING: ThreadSanitizer: lock-order-inversion + // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ({{.*}}) => [[M2:M[0-9]+]] ({{.*}}) => [[M1]] + // CHECK: Mutex [[M2]] acquired here while holding mutex [[M1]] in thread [[T1:T[0-9]+]] + // CHECK: Mutex [[M1]] acquired here while holding mutex [[M2]] in thread [[T2:T[0-9]+]] + // CHECK: Thread [[T1]] {{.*}} created by main thread + // CHECK: Thread [[T2]] {{.*}} created by main thread + // CHECK-NOT: WARNING: ThreadSanitizer: + } + + void Test6() { + if (test_number > 0 && test_number != 6) return; + fprintf(stderr, "Starting Test6: 3 threads lock/unlock private mutexes\n"); + // CHECK: Starting Test6 + Init(100); + // CHECK-NOT: WARNING: ThreadSanitizer: + RunThreads(&LockTest::Lock1_Loop_0, &LockTest::Lock1_Loop_1, + &LockTest::Lock1_Loop_2); + } + + void Test7() { + if (test_number > 0 && test_number != 7) return; + fprintf(stderr, "Starting Test7\n"); + // CHECK: Starting Test7 + Init(10); + L(0); T(1); U(1); U(0); + T(1); L(0); U(1); U(0); + // CHECK-NOT: WARNING: ThreadSanitizer: + fprintf(stderr, "No cycle: 0=>1\n"); + // CHECK: No cycle: 0=>1 + + T(2); L(3); U(3); U(2); + L(3); T(2); U(3); U(2); + // CHECK-NOT: WARNING: ThreadSanitizer: + fprintf(stderr, "No cycle: 2=>3\n"); + // CHECK: No cycle: 2=>3 + + T(4); L(5); U(4); U(5); + L(5); L(4); U(4); U(5); + // CHECK: WARNING: ThreadSanitizer: lock-order-inversion + fprintf(stderr, "Have cycle: 4=>5\n"); + // CHECK: Have cycle: 4=>5 + + L(7); L(6); U(6); U(7); + T(6); L(7); U(6); U(7); + // CHECK: WARNING: ThreadSanitizer: lock-order-inversion + fprintf(stderr, "Have cycle: 6=>7\n"); + // CHECK: Have cycle: 6=>7 + } + + void Test8() { + if (test_number > 0 && test_number != 8) return; + if (!LockType::supports_read_lock()) return; + fprintf(stderr, "Starting Test8\n"); + Init(5); + // CHECK-RD: Starting Test8 + RL(0); L(1); RU(0); U(1); + L(1); RL(0); RU(0); U(1); + // CHECK-RD: WARNING: ThreadSanitizer: lock-order-inversion + fprintf(stderr, "Have cycle: 0=>1\n"); + // CHECK-RD: Have cycle: 0=>1 + + RL(2); RL(3); RU(2); RU(3); + RL(3); RL(2); RU(2); RU(3); + // CHECK-RD: WARNING: ThreadSanitizer: lock-order-inversion + fprintf(stderr, "Have cycle: 2=>3\n"); + // CHECK-RD: Have cycle: 2=>3 + } + + void Test9() { + if (test_number > 0 && test_number != 9) return; + if (!LockType::supports_recursive_lock()) return; + fprintf(stderr, "Starting Test9\n"); + // CHECK-REC: Starting Test9 + Init(5); + L(0); L(0); L(0); L(1); U(1); U(0); U(0); U(0); + L(1); L(1); L(1); L(0); U(0); U(1); U(1); U(1); + // CHECK-REC: WARNING: ThreadSanitizer: lock-order-inversion + } + + void Test10() { + if (test_number > 0 && test_number != 10) return; + fprintf(stderr, "Starting Test10: 4 threads lock/unlock 4 private mutexes, one under another\n"); + // CHECK: Starting Test10 + Init(100); + // CHECK-NOT: WARNING: ThreadSanitizer: + RunThreads(&LockTest::Test10_Thread1, &LockTest::Test10_Thread2, + &LockTest::Test10_Thread3, &LockTest::Test10_Thread4); + } + void Test10_Thread1() { Test10_Thread(0); } + void Test10_Thread2() { Test10_Thread(10); } + void Test10_Thread3() { Test10_Thread(20); } + void Test10_Thread4() { Test10_Thread(30); } + void Test10_Thread(size_t m) { + for (int i = 0; i < iter_count; i++) { + L(m + 0); + L(m + 1); + L(m + 2); + L(m + 3); + U(m + 3); + U(m + 2); + U(m + 1); + U(m + 0); + } + } + + void Test11() { + if (test_number > 0 && test_number != 11) return; + fprintf(stderr, "Starting Test11: 4 threads lock/unlock 4 private mutexes, all under another private mutex\n"); + // CHECK: Starting Test11 + Init(500); + // CHECK-NOT: WARNING: ThreadSanitizer: + RunThreads(&LockTest::Test11_Thread1, &LockTest::Test11_Thread2, + &LockTest::Test11_Thread3, &LockTest::Test11_Thread4); + } + void Test11_Thread1() { Test10_Thread(0); } + void Test11_Thread2() { Test10_Thread(10); } + void Test11_Thread3() { Test10_Thread(20); } + void Test11_Thread4() { Test10_Thread(30); } + void Test11_Thread(size_t m) { + for (int i = 0; i < iter_count; i++) { + L(m); + L(m + 100); + U(m + 100); + L(m + 200); + U(m + 200); + L(m + 300); + U(m + 300); + L(m + 400); + U(m + 500); + U(m); + } + } + + void Test12() { + if (test_number > 0 && test_number != 12) return; + if (!LockType::supports_read_lock()) return; + fprintf(stderr, "Starting Test12: 4 threads read lock/unlock 4 shared mutexes, one under another\n"); + // CHECK-RD: Starting Test12 + Init(500); + // CHECK-RD-NOT: WARNING: ThreadSanitizer: + RunThreads(&LockTest::Test12_Thread, &LockTest::Test12_Thread, + &LockTest::Test12_Thread, &LockTest::Test12_Thread); + } + void Test12_Thread() { + for (int i = 0; i < iter_count; i++) { + RL(000); + RL(100); + RL(200); + RL(300); + RU(300); + RU(200); + RU(100); + RU(000); + } + } + + void Test13() { + if (test_number > 0 && test_number != 13) return; + if (!LockType::supports_read_lock()) return; + fprintf(stderr, "Starting Test13: 4 threads read lock/unlock 4 shared mutexes, all under another shared mutex\n"); + // CHECK-RD: Starting Test13 + Init(500); + // CHECK-RD-NOT: WARNING: ThreadSanitizer: + RunThreads(&LockTest::Test13_Thread, &LockTest::Test13_Thread, + &LockTest::Test13_Thread, &LockTest::Test13_Thread); + } + void Test13_Thread() { + for (int i = 0; i < iter_count; i++) { + RL(0); + RL(100); + RU(100); + RL(200); + RU(200); + RL(300); + RU(300); + RL(400); + RU(400); + RU(0); + } + } + + void Test14() { + if (test_number > 0 && test_number != 14) return; + fprintf(stderr, "Starting Test14: create lots of locks in 4 threads\n"); + Init(10); + // CHECK-RD: Starting Test14 + RunThreads(&LockTest::CreateAndDestroyLocksLoop, + &LockTest::CreateAndDestroyLocksLoop, + &LockTest::CreateAndDestroyLocksLoop, + &LockTest::CreateAndDestroyLocksLoop); + } + + void Test15() { + if (test_number > 0 && test_number != 15) return; + if (!LockType::supports_read_lock()) return; + fprintf(stderr, "Starting Test15: recursive rlock\n"); + // DISABLEDCHECK-RD: Starting Test15 + Init(5); + RL(0); RL(0); RU(0); RU(0); // Recusrive reader lock. + RL(0); RL(0); RL(0); RU(0); RU(0); RU(0); // Recusrive reader lock. + } + + // More detailed output test. + void Test16() { + if (test_number > 0 && test_number != 16) return; + fprintf(stderr, "Starting Test16: detailed output test with two locks\n"); + // CHECK: Starting Test16 + // CHECK: WARNING: ThreadSanitizer: lock-order-inversion + // CHECK: acquired here while holding mutex + // CHECK: LockTest::Acquire1 + // CHECK-NEXT: LockTest::Acquire_0_then_1 + // CHECK-SECOND: previously acquired by the same thread here + // CHECK-SECOND: LockTest::Acquire0 + // CHECK-SECOND-NEXT: LockTest::Acquire_0_then_1 + // CHECK: acquired here while holding mutex + // CHECK: LockTest::Acquire0 + // CHECK-NEXT: LockTest::Acquire_1_then_0 + // CHECK-SECOND: previously acquired by the same thread here + // CHECK-SECOND: LockTest::Acquire1 + // CHECK-SECOND-NEXT: LockTest::Acquire_1_then_0 + Init(5); + Acquire_0_then_1(); + U(0); U(1); + Acquire_1_then_0(); + U(0); U(1); + } + + // More detailed output test. + void Test17() { + if (test_number > 0 && test_number != 17) return; + fprintf(stderr, "Starting Test17: detailed output test with three locks\n"); + // CHECK: Starting Test17 + // CHECK: WARNING: ThreadSanitizer: lock-order-inversion + // CHECK: LockTest::Acquire1 + // CHECK-NEXT: LockTest::Acquire_0_then_1 + // CHECK: LockTest::Acquire2 + // CHECK-NEXT: LockTest::Acquire_1_then_2 + // CHECK: LockTest::Acquire0 + // CHECK-NEXT: LockTest::Acquire_2_then_0 + Init(5); + Acquire_0_then_1(); + U(0); U(1); + Acquire_1_then_2(); + U(1); U(2); + Acquire_2_then_0(); + U(0); U(2); + } + + __attribute__((noinline)) void Acquire2() { L(2); } + __attribute__((noinline)) void Acquire1() { L(1); } + __attribute__((noinline)) void Acquire0() { L(0); } + __attribute__((noinline)) void Acquire_1_then_0() { Acquire1(); Acquire0(); } + __attribute__((noinline)) void Acquire_0_then_1() { Acquire0(); Acquire1(); } + __attribute__((noinline)) void Acquire_1_then_2() { Acquire1(); Acquire2(); } + __attribute__((noinline)) void Acquire_2_then_0() { Acquire2(); Acquire0(); } + + // This test creates, locks, unlocks and destroys lots of mutexes. + void Test18() { + if (test_number > 0 && test_number != 18) return; + fprintf(stderr, "Starting Test18: create, lock and destroy 4 locks; all in " + "4 threads in a loop\n"); + RunThreads(&LockTest::Test18_Thread, &LockTest::Test18_Thread, + &LockTest::Test18_Thread, &LockTest::Test18_Thread); + } + + void Test18_Thread() { + LockType *l = new LockType[4]; + for (size_t i = 0; i < iter_count / 100; i++) { + for (int i = 0; i < 4; i++) l[i].lock(); + for (int i = 0; i < 4; i++) l[i].unlock(); + for (int i = 0; i < 4; i++) l[i].~LockType(); + for (int i = 0; i < 4; i++) new ((void*)&l[i]) LockType(); + } + delete [] l; + } + + private: + void Lock2(size_t l1, size_t l2) { L(l1); L(l2); U(l2); U(l1); } + void Lock_0_1() { Lock2(0, 1); } + void Lock_1_0() { sleep(1); Lock2(1, 0); } + void Lock1_Loop(size_t i, size_t n_iter) { + for (size_t it = 0; it < n_iter; it++) { + // if ((it & (it - 1)) == 0) fprintf(stderr, "%zd", i); + L(i); + U(i); + } + // fprintf(stderr, "\n"); + } + void Lock1_Loop_0() { Lock1_Loop(0, iter_count); } + void Lock1_Loop_1() { Lock1_Loop(10, iter_count); } + void Lock1_Loop_2() { Lock1_Loop(20, iter_count); } + + void CreateAndDestroyManyLocks() { + LockType *create_many_locks_but_never_acquire = + new LockType[kDeadlockGraphSize]; + (void)create_many_locks_but_never_acquire; + delete [] create_many_locks_but_never_acquire; + } + + void CreateAndDestroyLocksLoop() { + for (size_t it = 0; it <= iter_count; it++) { + LockType some_locks[10]; + (void)some_locks; + } + } + + void CreateLockUnlockAndDestroyManyLocks() { + LockType many_locks[kDeadlockGraphSize]; + for (size_t i = 0; i < kDeadlockGraphSize; i++) { + many_locks[i].lock(); + many_locks[i].unlock(); + } + } + + // LockTest Member function callback. + struct CB { + void (LockTest::*f)(); + LockTest *lt; + }; + + // Thread function with CB. + static void *Thread(void *param) { + CB *cb = (CB*)param; + (cb->lt->*cb->f)(); + return NULL; + } + + void RunThreads(void (LockTest::*f1)(), void (LockTest::*f2)(), + void (LockTest::*f3)() = 0, void (LockTest::*f4)() = 0) { + const int kNumThreads = 4; + pthread_t t[kNumThreads]; + CB cb[kNumThreads] = {{f1, this}, {f2, this}, {f3, this}, {f4, this}}; + for (int i = 0; i < kNumThreads && cb[i].f; i++) + pthread_create(&t[i], 0, Thread, &cb[i]); + for (int i = 0; i < kNumThreads && cb[i].f; i++) + pthread_join(t[i], 0); + } + + static const size_t kDeadlockGraphSize = 4096; + size_t n_; + LockType **locks_; +}; + +int main(int argc, char **argv) { + if (argc > 1) + test_number = atoi(argv[1]); + if (argc > 2) + iter_count = atoi(argv[2]); + LockTest().Test1(); + LockTest().Test2(); + LockTest().Test3(); + LockTest().Test4(); + LockTest().Test5(); + LockTest().Test6(); + LockTest().Test7(); + LockTest().Test8(); + LockTest().Test9(); + LockTest().Test10(); + LockTest().Test11(); + LockTest().Test12(); + LockTest().Test13(); + LockTest().Test14(); + LockTest().Test15(); + LockTest().Test16(); + LockTest().Test17(); + LockTest().Test18(); + fprintf(stderr, "ALL-DONE\n"); + // CHECK: ALL-DONE +} diff --git a/test/tsan/deep_stack1.cc b/test/tsan/deep_stack1.cc new file mode 100644 index 000000000000..1d00a0e856d5 --- /dev/null +++ b/test/tsan/deep_stack1.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +volatile int X; +volatile int N; +void (*volatile F)(); + +static void foo() { + if (--N == 0) + X = 42; + else + F(); +} + +void *Thread(void *p) { +#ifdef ORDER1 + sleep(1); +#endif + F(); + return 0; +} + +int main() { + N = 50000; + F = foo; + pthread_t t; + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setstacksize(&a, N * 256 + (1 << 20)); + pthread_create(&t, &a, Thread, 0); +#ifdef ORDER2 + sleep(1); +#endif + X = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #100 foo +// We must output suffucuently large stack (at least 100 frames) + diff --git a/test/tsan/default_options.cc b/test/tsan/default_options.cc new file mode 100644 index 000000000000..77bdcd5086ba --- /dev/null +++ b/test/tsan/default_options.cc @@ -0,0 +1,32 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" const char *__tsan_default_options() { + return "report_bugs=0"; +} + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/deflake.bash b/test/tsan/deflake.bash new file mode 100755 index 000000000000..9731fa53e589 --- /dev/null +++ b/test/tsan/deflake.bash @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# This script is used to deflake inherently flaky tsan tests. +# It is invoked from lit tests as: +# %deflake mybinary +# which is then substituted by lit to: +# $(dirname %s)/deflake.bash mybinary +# The script runs the target program up to 10 times, +# until it fails (i.e. produces a race report). + +for i in $(seq 1 10); do + OUT=`$@ 2>&1` + if [[ $? != 0 ]]; then + echo "$OUT" + exit 0 + fi +done +exit 1 diff --git a/test/tsan/dlclose.cc b/test/tsan/dlclose.cc new file mode 100644 index 000000000000..1a93fe6617e1 --- /dev/null +++ b/test/tsan/dlclose.cc @@ -0,0 +1,58 @@ +// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// If we mention TSAN_OPTIONS, the test won't run from test_output.sh script. + +// Test case for +// https://code.google.com/p/thread-sanitizer/issues/detail?id=80 + +#ifdef BUILD_SO + +#include <stdio.h> + +extern "C" +void sofunc() { + fprintf(stderr, "HELLO FROM SO\n"); +} + +#else // BUILD_SO + +#include <dlfcn.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> +#include <string> + +void *lib; +void *lib2; + +struct Closer { + ~Closer() { + dlclose(lib); + fprintf(stderr, "CLOSED SO\n"); + } +}; +static Closer c; + +int main(int argc, char *argv[]) { + lib = dlopen((std::string(argv[0]) + std::string("-so.so")).c_str(), + RTLD_NOW|RTLD_NODELETE); + if (lib == 0) { + printf("error in dlopen: %s\n", dlerror()); + return 1; + } + void *f = dlsym(lib, "sofunc"); + if (f == 0) { + printf("error in dlsym: %s\n", dlerror()); + return 1; + } + ((void(*)())f)(); + return 0; +} + +#endif // BUILD_SO + +// CHECK: HELLO FROM SO +// CHECK-NOT: Inconsistency detected by ld.so +// CHECK: CLOSED SO + diff --git a/test/tsan/fd_close_norace.cc b/test/tsan/fd_close_norace.cc new file mode 100644 index 000000000000..7238d64b432b --- /dev/null +++ b/test/tsan/fd_close_norace.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +void *Thread1(void *x) { + int f = open("/dev/random", O_RDONLY); + close(f); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + int f = open("/dev/random", O_RDONLY); + close(f); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + + diff --git a/test/tsan/fd_close_norace2.cc b/test/tsan/fd_close_norace2.cc new file mode 100644 index 000000000000..bf94fd5512b3 --- /dev/null +++ b/test/tsan/fd_close_norace2.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int pipes[2]; + +void *Thread(void *x) { + // wait for shutown signal + while (read(pipes[0], &x, 1) != 1) { + } + close(pipes[0]); + close(pipes[1]); + return 0; +} + +int main() { + if (pipe(pipes)) + return 1; + pthread_t t; + pthread_create(&t, 0, Thread, 0); + // send shutdown signal + while (write(pipes[1], &t, 1) != 1) { + } + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/test/tsan/fd_dup_norace.cc b/test/tsan/fd_dup_norace.cc new file mode 100644 index 000000000000..5045325b22b5 --- /dev/null +++ b/test/tsan/fd_dup_norace.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int fds[2]; + +void *Thread1(void *x) { + char buf; + read(fds[0], &buf, 1); + close(fds[0]); + return 0; +} + +void *Thread2(void *x) { + close(fds[1]); + return 0; +} + +int main() { + fds[0] = open("/dev/random", O_RDONLY); + fds[1] = dup2(fds[0], 100); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_location.cc b/test/tsan/fd_location.cc new file mode 100644 index 000000000000..535329e06409 --- /dev/null +++ b/test/tsan/fd_location.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int fds[2]; + +void *Thread1(void *x) { + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + close(fds[0]); + close(fds[1]); + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is file descriptor {{[0-9]+}} created by main thread at: +// CHECK: #0 pipe +// CHECK: #1 main + diff --git a/test/tsan/fd_pipe_norace.cc b/test/tsan/fd_pipe_norace.cc new file mode 100644 index 000000000000..b434703d782a --- /dev/null +++ b/test/tsan/fd_pipe_norace.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int fds[2]; +int X; + +void *Thread1(void *x) { + X = 42; + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + char buf; + while (read(fds[0], &buf, 1) != 1) { + } + X = 43; + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_pipe_race.cc b/test/tsan/fd_pipe_race.cc new file mode 100644 index 000000000000..88c4ed4aa398 --- /dev/null +++ b/test/tsan/fd_pipe_race.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int fds[2]; + +void *Thread1(void *x) { + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + close(fds[0]); + close(fds[1]); + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 8 +// CHECK: #0 close +// CHECK: #1 Thread2 +// CHECK: Previous read of size 8 +// CHECK: #0 write +// CHECK: #1 Thread1 + + diff --git a/test/tsan/fd_socket_connect_norace.cc b/test/tsan/fd_socket_connect_norace.cc new file mode 100644 index 000000000000..ab2a950f17d6 --- /dev/null +++ b/test/tsan/fd_socket_connect_norace.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +struct sockaddr_in addr; +int X; + +void *ClientThread(void *x) { + int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + X = 42; + if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) { + perror("connect"); + exit(1); + } + close(c); + return NULL; +} + +int main() { + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + addr.sin_family = AF_INET; + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + addr.sin_port = INADDR_ANY; + socklen_t len = sizeof(addr); + bind(s, (sockaddr*)&addr, len); + getsockname(s, (sockaddr*)&addr, &len); + listen(s, 10); + pthread_t t; + pthread_create(&t, 0, ClientThread, 0); + int c = accept(s, 0, 0); + X = 42; + pthread_join(t, 0); + close(c); + close(s); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/fd_socket_norace.cc b/test/tsan/fd_socket_norace.cc new file mode 100644 index 000000000000..0f41c4357354 --- /dev/null +++ b/test/tsan/fd_socket_norace.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +struct sockaddr_in addr; +int X; + +void *ClientThread(void *x) { + X = 42; + int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) { + perror("connect"); + exit(1); + } + if (send(c, "a", 1, 0) != 1) { + perror("send"); + exit(1); + } + close(c); + return NULL; +} + +int main() { + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + addr.sin_family = AF_INET; + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + addr.sin_port = INADDR_ANY; + socklen_t len = sizeof(addr); + bind(s, (sockaddr*)&addr, len); + getsockname(s, (sockaddr*)&addr, &len); + listen(s, 10); + pthread_t t; + pthread_create(&t, 0, ClientThread, 0); + int c = accept(s, 0, 0); + char buf; + while (read(c, &buf, 1) != 1) { + } + X = 43; + close(c); + close(s); + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/fd_socketpair_norace.cc b/test/tsan/fd_socketpair_norace.cc new file mode 100644 index 000000000000..a455d44a3965 --- /dev/null +++ b/test/tsan/fd_socketpair_norace.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> + +int fds[2]; +int X; + +void *Thread1(void *x) { + X = 42; + write(fds[1], "a", 1); + close(fds[1]); + return NULL; +} + +void *Thread2(void *x) { + char buf; + while (read(fds[0], &buf, 1) != 1) { + } + X = 43; + close(fds[0]); + return NULL; +} + +int main() { + socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_stdout_race.cc b/test/tsan/fd_stdout_race.cc new file mode 100644 index 000000000000..d6a2c7c796f1 --- /dev/null +++ b/test/tsan/fd_stdout_race.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int X; + +void *Thread1(void *x) { + sleep(1); + int f = open("/dev/random", O_RDONLY); + char buf; + read(f, &buf, 1); + close(f); + X = 42; + return NULL; +} + +void *Thread2(void *x) { + X = 43; + write(STDOUT_FILENO, "a", 1); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 +// CHECK: #0 Thread1 +// CHECK: Previous write of size 4 +// CHECK: #0 Thread2 + + diff --git a/test/tsan/fork_atexit.cc b/test/tsan/fork_atexit.cc new file mode 100644 index 000000000000..6801d3ffff7e --- /dev/null +++ b/test/tsan/fork_atexit.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="atexit_sleep_ms=50" %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +void foo() { + printf("CHILD ATEXIT\n"); +} + +void *worker(void *unused) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, worker, NULL); + int pid = fork(); + if (pid == 0) { + // child + atexit(foo); + fprintf(stderr, "CHILD DONE\n"); + } else { + pthread_join(t, 0); + if (waitpid(pid, 0, 0) == -1) { + perror("waitpid"); + exit(1); + } + fprintf(stderr, "PARENT DONE\n"); + } +} + +// CHECK: CHILD DONE +// CHECK: CHILD ATEXIT +// CHECK: PARENT DONE diff --git a/test/tsan/fork_deadlock.cc b/test/tsan/fork_deadlock.cc new file mode 100644 index 000000000000..cc5b12214cf9 --- /dev/null +++ b/test/tsan/fork_deadlock.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="atexit_sleep_ms=50" %run %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +int counter; + +static void *incrementer(void *p) { + for (;;) + __sync_fetch_and_add(&counter, 1); + return 0; +} + +static void *watchdog(void *p) { + sleep(100); + fprintf(stderr, "timed out after 100 seconds\n"); + exit(1); + return 0; +} + +int main() { + pthread_t th1, th2; + pthread_create(&th1, 0, incrementer, 0); + pthread_create(&th2, 0, watchdog, 0); + for (int i = 0; i < 10; i++) { + switch (fork()) { + default: // parent + while (wait(0) < 0) {} + fprintf(stderr, "."); + break; + case 0: // child + __sync_fetch_and_add(&counter, 1); + exit(0); + break; + case -1: // error + fprintf(stderr, "failed to fork (%d)\n", errno); + exit(1); + } + } + fprintf(stderr, "OK\n"); +} + +// CHECK: OK + diff --git a/test/tsan/fork_multithreaded.cc b/test/tsan/fork_multithreaded.cc new file mode 100644 index 000000000000..5176a14d6a65 --- /dev/null +++ b/test/tsan/fork_multithreaded.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-DIE +// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="die_after_fork=0" %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-NODIE +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +static void *sleeper(void *p) { + sleep(10); + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, sleeper, 0); + switch (fork()) { + default: // parent + while (wait(0) < 0) {} + break; + case 0: // child + { + pthread_t th2; + pthread_create(&th2, 0, sleeper, 0); + exit(0); + break; + } + case -1: // error + fprintf(stderr, "failed to fork (%d)\n", errno); + exit(1); + } + fprintf(stderr, "OK\n"); +} + +// CHECK-DIE: ThreadSanitizer: starting new threads after multi-threaded fork is not supported +// CHECK-DIE: OK + +// CHECK-NODIE-NOT: ThreadSanitizer: starting new threads after multi-threaded fork is not supported +// CHECK-NODIE: OK + diff --git a/test/tsan/fork_multithreaded3.cc b/test/tsan/fork_multithreaded3.cc new file mode 100644 index 000000000000..a651b3c18b4e --- /dev/null +++ b/test/tsan/fork_multithreaded3.cc @@ -0,0 +1,40 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +static void *racer(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + switch (fork()) { + default: // parent + while (wait(0) < 0) {} + break; + case 0: // child + { + int x = 0; + pthread_t th1, th2; + pthread_create(&th1, 0, racer, &x); + pthread_create(&th2, 0, racer, &x); + pthread_join(th1, 0); + pthread_join(th2, 0); + exit(0); + break; + } + case -1: // error + fprintf(stderr, "failed to fork (%d)\n", errno); + exit(1); + } + fprintf(stderr, "OK\n"); +} + +// CHECK: ThreadSanitizer: data race +// CHECK: OK + diff --git a/test/tsan/free_race.c b/test/tsan/free_race.c new file mode 100644 index 000000000000..663d7bcf2eb9 --- /dev/null +++ b/test/tsan/free_race.c @@ -0,0 +1,49 @@ +// RUN: %clang_tsan -O1 %s -o %t +// RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOZUPP +// RUN: TSAN_OPTIONS="suppressions=%s.supp print_suppressions=1" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUPP + +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +int *mem; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx); + free(mem); + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_mutex_lock(&mtx); + mem[0] = 42; + pthread_mutex_unlock(&mtx); + return NULL; +} + +int main() { + mem = (int*)malloc(100); + pthread_mutex_init(&mtx, 0); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + pthread_mutex_destroy(&mtx); + return 0; +} + +// CHECK-NOZUPP: WARNING: ThreadSanitizer: heap-use-after-free +// CHECK-NOZUPP: Write of size 4 at {{.*}} by main thread{{.*}}: +// CHECK-NOZUPP: #0 Thread2 +// CHECK-NOZUPP: #1 main +// CHECK-NOZUPP: Previous write of size 8 at {{.*}} by thread T1{{.*}}: +// CHECK-NOZUPP: #0 free +// CHECK-NOZUPP: #{{(1|2)}} Thread1 +// CHECK-NOZUPP: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 +// CHECK-SUPP: ThreadSanitizer: Matched 1 suppressions +// CHECK-SUPP: 1 race:^Thread2$ diff --git a/test/tsan/free_race.c.supp b/test/tsan/free_race.c.supp new file mode 100644 index 000000000000..f5d6a4969a41 --- /dev/null +++ b/test/tsan/free_race.c.supp @@ -0,0 +1,2 @@ +# Suppression for a use-after-free in free_race.c +race:^Thread2$ diff --git a/test/tsan/free_race2.c b/test/tsan/free_race2.c new file mode 100644 index 000000000000..de6b2ae1fcbb --- /dev/null +++ b/test/tsan/free_race2.c @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <stdlib.h> + +void __attribute__((noinline)) foo(int *mem) { + free(mem); +} + +void __attribute__((noinline)) bar(int *mem) { + mem[0] = 42; +} + +int main() { + int *mem = (int*)malloc(100); + foo(mem); + bar(mem); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: heap-use-after-free +// CHECK: Write of size 4 at {{.*}} by main thread: +// CHECK: #0 bar +// CHECK: #1 main +// CHECK: Previous write of size 8 at {{.*}} by main thread: +// CHECK: #0 free +// CHECK: #{{1|2}} foo +// CHECK: #{{2|3}} main diff --git a/test/tsan/getline_nohang.cc b/test/tsan/getline_nohang.cc new file mode 100644 index 000000000000..89afbe1a66a8 --- /dev/null +++ b/test/tsan/getline_nohang.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t + +// Make sure TSan doesn't deadlock on a file stream lock at program shutdown. +// See https://code.google.com/p/thread-sanitizer/issues/detail?id=47 +#ifdef __FreeBSD__ +#define _WITH_GETLINE // to declare getline() +#endif + +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +void *thread(void *unused) { + char *line = NULL; + size_t size; + int fd[2]; + pipe(fd); + // Forge a non-standard stream to make sure it's not closed. + FILE *stream = fdopen(fd[0], "r"); + while (1) { + volatile int res = getline(&line, &size, stream); + (void)res; + } + return NULL; +} + +int main() { + pthread_t t; + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); + pthread_create(&t, &a, thread, NULL); + pthread_attr_destroy(&a); + fprintf(stderr, "DONE\n"); + return 0; + // ThreadSanitizer used to hang here because of a deadlock on a file stream. +} + +// CHECK: DONE diff --git a/test/tsan/global_race.cc b/test/tsan/global_race.cc new file mode 100644 index 000000000000..e12bb1d220f8 --- /dev/null +++ b/test/tsan/global_race.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan -O1 %s -o %T/global_race.cc.exe && %deflake %run %T/global_race.cc.exe | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +int GlobalData[10]; + +void *Thread(void *a) { + sleep(1); + GlobalData[2] = 42; + return 0; +} + +int main() { + // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not + // match to the format used in the diagnotic message. + fprintf(stderr, "addr=0x%012lx\n", (unsigned long) GlobalData); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + GlobalData[2] = 43; + pthread_join(t, 0); +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'GlobalData' of size 40 at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}}) + diff --git a/test/tsan/global_race2.cc b/test/tsan/global_race2.cc new file mode 100644 index 000000000000..ac994cc0f4c4 --- /dev/null +++ b/test/tsan/global_race2.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +int x; + +void *Thread(void *a) { + sleep(1); + x = 1; + return 0; +} + +int main() { + // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not + // match to the format used in the diagnotic message. + fprintf(stderr, "addr2=0x%012lx\n", (unsigned long) &x); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + x = 0; + pthread_join(t, 0); +} + +// CHECK: addr2=[[ADDR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'x' of size 4 at [[ADDR2]] ({{.*}}+0x{{[0-9,a-f]+}}) + diff --git a/test/tsan/global_race3.cc b/test/tsan/global_race3.cc new file mode 100644 index 000000000000..a3222bb3d8cb --- /dev/null +++ b/test/tsan/global_race3.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +namespace XXX { + struct YYY { + static int ZZZ[10]; + }; + int YYY::ZZZ[10]; +} + +void *Thread(void *a) { + sleep(1); + XXX::YYY::ZZZ[0] = 1; + return 0; +} + +int main() { + // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not + // match to the format used in the diagnotic message. + fprintf(stderr, "addr3=0x%012lx\n", (unsigned long) XXX::YYY::ZZZ); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + XXX::YYY::ZZZ[0] = 0; + pthread_join(t, 0); +} + +// CHECK: addr3=[[ADDR3:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'XXX::YYY::ZZZ' of size 40 at [[ADDR3]] ({{.*}}+0x{{[0-9,a-f]+}}) diff --git a/test/tsan/halt_on_error.cc b/test/tsan/halt_on_error.cc new file mode 100644 index 000000000000..3c55c60a457e --- /dev/null +++ b/test/tsan/halt_on_error.cc @@ -0,0 +1,27 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS halt_on_error=1" %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int X; + +void *Thread(void *x) { + sleep(1); + X = 42; + return 0; +} + +int main() { + fprintf(stderr, "BEFORE\n"); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + fprintf(stderr, "AFTER\n"); + return 0; +} + +// CHECK: BEFORE +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: AFTER + diff --git a/test/tsan/heap_race.cc b/test/tsan/heap_race.cc new file mode 100644 index 000000000000..c3da68f42658 --- /dev/null +++ b/test/tsan/heap_race.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> + +void *Thread(void *a) { + ((int*)a)[0]++; + return NULL; +} + +int main() { + int *p = new int(42); + pthread_t t; + pthread_create(&t, NULL, Thread, p); + p[0]++; + pthread_join(t, NULL); + delete p; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/ignore_free.cc b/test/tsan/ignore_free.cc new file mode 100644 index 000000000000..1df6dce2f5e0 --- /dev/null +++ b/test/tsan/ignore_free.cc @@ -0,0 +1,35 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +void *Thread(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + int *p = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, p); + sleep(1); + AnnotateIgnoreReadsBegin(__FILE__, __LINE__); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + free(p); + AnnotateIgnoreReadsEnd(__FILE__, __LINE__); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + pthread_join(t, 0); + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/test/tsan/ignore_lib0.cc b/test/tsan/ignore_lib0.cc new file mode 100644 index 000000000000..fe1a35558015 --- /dev/null +++ b/test/tsan/ignore_lib0.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib0.so +// RUN: %clangxx_tsan -O1 %s -L%T -lignore_lib0 -o %t +// RUN: echo running w/o suppressions: +// RUN: LD_LIBRARY_PATH=%T${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: LD_LIBRARY_PATH=%T${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// Tests that interceptors coming from a library specified in called_from_lib +// suppression are ignored. + +#ifndef LIB + +extern "C" void libfunc(); + +int main() { + libfunc(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/test/tsan/ignore_lib0.cc.supp b/test/tsan/ignore_lib0.cc.supp new file mode 100644 index 000000000000..7728c926b7de --- /dev/null +++ b/test/tsan/ignore_lib0.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib0.so + diff --git a/test/tsan/ignore_lib1.cc b/test/tsan/ignore_lib1.cc new file mode 100644 index 000000000000..30a9994b94f5 --- /dev/null +++ b/test/tsan/ignore_lib1.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: echo running w/o suppressions: +// RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// Tests that interceptors coming from a dynamically loaded library specified +// in called_from_lib suppression are ignored. + +#ifndef LIB + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib1.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + if (h == 0) + exit(printf("failed to load the library (%d)\n", errno)); + void (*f)() = (void(*)())dlsym(h, "libfunc"); + if (f == 0) + exit(printf("failed to find the func (%d)\n", errno)); + f(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/test/tsan/ignore_lib1.cc.supp b/test/tsan/ignore_lib1.cc.supp new file mode 100644 index 000000000000..9f4119ec0bc4 --- /dev/null +++ b/test/tsan/ignore_lib1.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib1.so$ + diff --git a/test/tsan/ignore_lib2.cc b/test/tsan/ignore_lib2.cc new file mode 100644 index 000000000000..23a0872feabe --- /dev/null +++ b/test/tsan/ignore_lib2.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_0.so +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %deflake %run %t | FileCheck %s + +// Tests that called_from_lib suppression matched against 2 libraries +// causes program crash (this is not supported). + +#ifndef LIB + +#include <dlfcn.h> +#include <stdio.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib0 = std::string(dirname(argv[0])) + "/libignore_lib2_0.so"; + std::string lib1 = std::string(dirname(argv[0])) + "/libignore_lib2_1.so"; + dlopen(lib0.c_str(), RTLD_GLOBAL | RTLD_NOW); + dlopen(lib1.c_str(), RTLD_GLOBAL | RTLD_NOW); + fprintf(stderr, "OK\n"); +} + +#else // #ifdef LIB + +extern "C" void libfunc() { +} + +#endif // #ifdef LIB + +// CHECK: ThreadSanitizer: called_from_lib suppression 'ignore_lib2' is matched against 2 libraries +// CHECK-NOT: OK + diff --git a/test/tsan/ignore_lib2.cc.supp b/test/tsan/ignore_lib2.cc.supp new file mode 100644 index 000000000000..1419c71c67ef --- /dev/null +++ b/test/tsan/ignore_lib2.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib2 + diff --git a/test/tsan/ignore_lib3.cc b/test/tsan/ignore_lib3.cc new file mode 100644 index 000000000000..137109ea6cb4 --- /dev/null +++ b/test/tsan/ignore_lib3.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib3.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %deflake %run %t | FileCheck %s + +// Tests that unloading of a library matched against called_from_lib suppression +// causes program crash (this is not supported). + +#ifndef LIB + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib3.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + dlclose(h); + fprintf(stderr, "OK\n"); +} + +#else // #ifdef LIB + +extern "C" void libfunc() { +} + +#endif // #ifdef LIB + +// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded +// CHECK-NOT: OK + diff --git a/test/tsan/ignore_lib3.cc.supp b/test/tsan/ignore_lib3.cc.supp new file mode 100644 index 000000000000..975dbcef99fe --- /dev/null +++ b/test/tsan/ignore_lib3.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib3.so + diff --git a/test/tsan/ignore_lib_lib.h b/test/tsan/ignore_lib_lib.h new file mode 100644 index 000000000000..2bfe84dfc0ec --- /dev/null +++ b/test/tsan/ignore_lib_lib.h @@ -0,0 +1,25 @@ +#include <pthread.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +void *volatile mem; +volatile int len; + +void *Thread(void *p) { + while ((p = __atomic_load_n(&mem, __ATOMIC_ACQUIRE)) == 0) + usleep(100); + memset(p, 0, len); + return 0; +} + +extern "C" void libfunc() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + len = 10; + __atomic_store_n(&mem, malloc(len), __ATOMIC_RELEASE); + pthread_join(t, 0); + free(mem); + fprintf(stderr, "OK\n"); +} diff --git a/test/tsan/ignore_malloc.cc b/test/tsan/ignore_malloc.cc new file mode 100644 index 000000000000..0f1fb5e3dfdc --- /dev/null +++ b/test/tsan/ignore_malloc.cc @@ -0,0 +1,38 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +int *g; + +void *Thread(void *a) { + int *p = 0; + while ((p = __atomic_load_n(&g, __ATOMIC_RELAXED)) == 0) + usleep(100); + *p = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + int *p = new int(0); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + __atomic_store_n(&g, p, __ATOMIC_RELAXED); + pthread_join(t, 0); + delete p; + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/test/tsan/ignore_race.cc b/test/tsan/ignore_race.cc new file mode 100644 index 000000000000..c6e067fabec0 --- /dev/null +++ b/test/tsan/ignore_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l); +extern "C" void AnnotateIgnoreWritesEnd(const char *f, int l); +extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l); +extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l); + +void *Thread(void *x) { + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + AnnotateIgnoreReadsBegin(__FILE__, __LINE__); + Global = 42; + AnnotateIgnoreReadsEnd(__FILE__, __LINE__); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + Global = 43; + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/ignore_sync.cc b/test/tsan/ignore_sync.cc new file mode 100644 index 000000000000..ae24a8c49e75 --- /dev/null +++ b/test/tsan/ignore_sync.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" void AnnotateIgnoreSyncBegin(const char*, int); +extern "C" void AnnotateIgnoreSyncEnd(const char*, int); + +int Global; +pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER; + +void *Thread(void *x) { + AnnotateIgnoreSyncBegin(0, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + AnnotateIgnoreSyncEnd(0, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/inlined_memcpy_race.cc b/test/tsan/inlined_memcpy_race.cc new file mode 100644 index 000000000000..a95576a83c9a --- /dev/null +++ b/test/tsan/inlined_memcpy_race.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int x[4], z[4]; + +void *MemCpyThread(void *a) { + memcpy((int*)a, z, 16); + return NULL; +} + +void *MemSetThread(void *a) { + sleep(1); + memset((int*)a, 0, 16); + return NULL; +} + +int main() { + pthread_t t[2]; + // Race on x between memcpy and memset + pthread_create(&t[0], NULL, MemCpyThread, x); + pthread_create(&t[1], NULL, MemSetThread, x); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("PASS\n"); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 memset +// CHECK: #1 MemSetThread +// CHECK: Previous write +// CHECK: #0 memcpy +// CHECK: #1 MemCpyThread + diff --git a/test/tsan/inlined_memcpy_race2.cc b/test/tsan/inlined_memcpy_race2.cc new file mode 100644 index 000000000000..63b560f02269 --- /dev/null +++ b/test/tsan/inlined_memcpy_race2.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int y[4], z[4]; + +void *MemMoveThread(void *a) { + memmove((int*)a, z, 16); + return NULL; +} + +void *MemSetThread(void *a) { + sleep(1); + memset((int*)a, 0, 16); + return NULL; +} + +int main() { + pthread_t t[2]; + // Race on y between memmove and memset + pthread_create(&t[0], NULL, MemMoveThread, y); + pthread_create(&t[1], NULL, MemSetThread, y); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + + printf("PASS\n"); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 memset +// CHECK: #1 MemSetThread +// CHECK: Previous write +// CHECK: #0 memmove +// CHECK: #1 MemMoveThread diff --git a/test/tsan/interface_atomic_test.c b/test/tsan/interface_atomic_test.c new file mode 100644 index 000000000000..18d860ea02e2 --- /dev/null +++ b/test/tsan/interface_atomic_test.c @@ -0,0 +1,16 @@ +// Test that we can include header with TSan atomic interface. +// RUN: %clang_tsan %s -o %t && %run %t | FileCheck %s +#include <sanitizer/tsan_interface_atomic.h> +#include <stdio.h> + +int main() { + __tsan_atomic32 a; + __tsan_atomic32_store(&a, 100, __tsan_memory_order_release); + int res = __tsan_atomic32_load(&a, __tsan_memory_order_acquire); + if (res == 100) { + // CHECK: PASS + printf("PASS\n"); + return 0; + } + return 1; +} diff --git a/test/tsan/java.h b/test/tsan/java.h new file mode 100644 index 000000000000..d986d08be060 --- /dev/null +++ b/test/tsan/java.h @@ -0,0 +1,21 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +typedef unsigned long jptr; // NOLINT +void __tsan_java_preinit(const char *libjvm_path); +void __tsan_java_init(jptr heap_begin, jptr heap_size); +int __tsan_java_fini(); +void __tsan_java_alloc(jptr ptr, jptr size); +void __tsan_java_free(jptr ptr, jptr size); +void __tsan_java_move(jptr src, jptr dst, jptr size); +void __tsan_java_finalize(); +void __tsan_java_mutex_lock(jptr addr); +void __tsan_java_mutex_unlock(jptr addr); +void __tsan_java_mutex_read_lock(jptr addr); +void __tsan_java_mutex_read_unlock(jptr addr); +void __tsan_java_mutex_lock_rec(jptr addr, int rec); +int __tsan_java_mutex_unlock_rec(jptr addr); +} diff --git a/test/tsan/java_alloc.cc b/test/tsan/java_alloc.cc new file mode 100644 index 000000000000..4a606f7940d3 --- /dev/null +++ b/test/tsan/java_alloc.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "java.h" + +int const kHeapSize = 1024 * 1024; + +void stress(jptr addr) { + for (jptr sz = 8; sz <= 32; sz <<= 1) { + for (jptr i = 0; i < kHeapSize / 4 / sz; i++) { + __tsan_java_alloc(addr + i * sz, sz); + } + __tsan_java_move(addr, addr + kHeapSize / 2, kHeapSize / 4); + __tsan_java_free(addr + kHeapSize / 2, kHeapSize / 4); + } +} + +void *Thread(void *p) { + stress((jptr)p); + return 0; +} + +int main() { + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)(jheap + kHeapSize / 4)); + stress(jheap); + pthread_join(th, 0); + if (__tsan_java_fini() != 0) { + printf("FAILED\n"); + return 1; + } + printf("DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED +// CHECK: DONE diff --git a/test/tsan/java_finalizer.cc b/test/tsan/java_finalizer.cc new file mode 100644 index 000000000000..d5c6a22d1e16 --- /dev/null +++ b/test/tsan/java_finalizer.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "java.h" + +void *Thread(void *p) { + sleep(1); + __tsan_java_finalize(); + *(int*)p = 42; + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)jheap); + *(int*)jheap = 43; + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + fprintf(stderr, "DONE\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_lock.cc b/test/tsan/java_lock.cc new file mode 100644 index 000000000000..36a0f8b7bff3 --- /dev/null +++ b/test/tsan/java_lock.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "java.h" +#include <unistd.h> + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 42; + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + varaddr = jheap; + lockaddr = jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + fprintf(stderr, "DONE\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_lock_move.cc b/test/tsan/java_lock_move.cc new file mode 100644 index 000000000000..19c3e35d6633 --- /dev/null +++ b/test/tsan/java_lock_move.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "java.h" + +jptr varaddr; +jptr lockaddr; +jptr varaddr2; +jptr lockaddr2; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_lock(lockaddr2); + *(int*)varaddr2 = 42; + __tsan_java_mutex_unlock(lockaddr2); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 64; + int const kMove = 1024; + __tsan_java_alloc(jheap, kBlockSize); + varaddr = jheap; + lockaddr = jheap + 46; + varaddr2 = varaddr + kMove; + lockaddr2 = lockaddr + kMove; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_move(varaddr, varaddr2, kBlockSize); + pthread_join(th, 0); + __tsan_java_free(varaddr2, kBlockSize); + printf("DONE\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_lock_rec.cc b/test/tsan/java_lock_rec.cc new file mode 100644 index 000000000000..2b0ab0eb92d0 --- /dev/null +++ b/test/tsan/java_lock_rec.cc @@ -0,0 +1,55 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "java.h" +#include <unistd.h> + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 42; + int rec = __tsan_java_mutex_unlock_rec(lockaddr); + if (rec != 2) { + printf("FAILED 0 rec=%d\n", rec); + exit(1); + } + sleep(2); + __tsan_java_mutex_lock_rec(lockaddr, rec); + if (*(int*)varaddr != 43) { + printf("FAILED 3 var=%d\n", *(int*)varaddr); + exit(1); + } + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + varaddr = jheap; + *(int*)varaddr = 0; + lockaddr = jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + sleep(1); + __tsan_java_mutex_lock(lockaddr); + if (*(int*)varaddr != 42) { + printf("FAILED 1 var=%d\n", *(int*)varaddr); + exit(1); + } + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + printf("DONE\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED +// CHECK: DONE diff --git a/test/tsan/java_lock_rec_race.cc b/test/tsan/java_lock_rec_race.cc new file mode 100644 index 000000000000..841aa396360d --- /dev/null +++ b/test/tsan/java_lock_rec_race.cc @@ -0,0 +1,49 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "java.h" +#include <unistd.h> + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + int rec = __tsan_java_mutex_unlock_rec(lockaddr); + if (rec != 3) { + printf("FAILED 0 rec=%d\n", rec); + exit(1); + } + *(int*)varaddr = 42; + sleep(2); + __tsan_java_mutex_lock_rec(lockaddr, rec); + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + varaddr = jheap; + *(int*)varaddr = 0; + lockaddr = jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + sleep(1); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + printf("DONE\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED +// CHECK: DONE diff --git a/test/tsan/java_move_overlap.cc b/test/tsan/java_move_overlap.cc new file mode 100644 index 000000000000..12955b4ba0de --- /dev/null +++ b/test/tsan/java_move_overlap.cc @@ -0,0 +1,72 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %run %t arg 2>&1 | FileCheck %s +#include "java.h" + +jptr varaddr1_old; +jptr varaddr2_old; +jptr lockaddr1_old; +jptr lockaddr2_old; +jptr varaddr1_new; +jptr varaddr2_new; +jptr lockaddr1_new; +jptr lockaddr2_new; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_lock(lockaddr1_new); + *(char*)varaddr1_new = 43; + __tsan_java_mutex_unlock(lockaddr1_new); + __tsan_java_mutex_lock(lockaddr2_new); + *(char*)varaddr2_new = 43; + __tsan_java_mutex_unlock(lockaddr2_new); + return 0; +} + +int main(int argc, char **argv) { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + jheap = (char*)jheap + 8; + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 64; + int const kMove = 32; + varaddr1_old = (jptr)jheap; + lockaddr1_old = (jptr)jheap + 1; + varaddr2_old = (jptr)jheap + kBlockSize - 1; + lockaddr2_old = (jptr)jheap + kBlockSize - 16; + varaddr1_new = varaddr1_old + kMove; + lockaddr1_new = lockaddr1_old + kMove; + varaddr2_new = varaddr2_old + kMove; + lockaddr2_new = lockaddr2_old + kMove; + if (argc > 1) { + // Move memory backwards. + varaddr1_old += kMove; + lockaddr1_old += kMove; + varaddr2_old += kMove; + lockaddr2_old += kMove; + varaddr1_new -= kMove; + lockaddr1_new -= kMove; + varaddr2_new -= kMove; + lockaddr2_new -= kMove; + } + __tsan_java_alloc(varaddr1_old, kBlockSize); + + pthread_t th; + pthread_create(&th, 0, Thread, 0); + + __tsan_java_mutex_lock(lockaddr1_old); + *(char*)varaddr1_old = 43; + __tsan_java_mutex_unlock(lockaddr1_old); + __tsan_java_mutex_lock(lockaddr2_old); + *(char*)varaddr2_old = 43; + __tsan_java_mutex_unlock(lockaddr2_old); + + __tsan_java_move(varaddr1_old, varaddr1_new, kBlockSize); + pthread_join(th, 0); + __tsan_java_free(varaddr1_new, kBlockSize); + printf("DONE\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_move_overlap_race.cc b/test/tsan/java_move_overlap_race.cc new file mode 100644 index 000000000000..2b3769be6117 --- /dev/null +++ b/test/tsan/java_move_overlap_race.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: %deflake %run %t | FileCheck %s +// RUN: %deflake %run %t arg | FileCheck %s +#include "java.h" + +jptr varaddr1_old; +jptr varaddr2_old; +jptr varaddr1_new; +jptr varaddr2_new; + +void *Thread(void *p) { + sleep(1); + *(int*)varaddr1_new = 43; + *(int*)varaddr2_new = 43; + return 0; +} + +int main(int argc, char **argv) { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + jheap = (char*)jheap + 8; + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 64; + int const kMove = 32; + varaddr1_old = (jptr)jheap; + varaddr2_old = (jptr)jheap + kBlockSize - 1; + varaddr1_new = varaddr1_old + kMove; + varaddr2_new = varaddr2_old + kMove; + if (argc > 1) { + // Move memory backwards. + varaddr1_old += kMove; + varaddr2_old += kMove; + varaddr1_new -= kMove; + varaddr2_new -= kMove; + } + __tsan_java_alloc(varaddr1_old, kBlockSize); + + pthread_t th; + pthread_create(&th, 0, Thread, 0); + + *(int*)varaddr1_old = 43; + *(int*)varaddr2_old = 43; + + __tsan_java_move(varaddr1_old, varaddr1_new, kBlockSize); + pthread_join(th, 0); + __tsan_java_free(varaddr1_new, kBlockSize); + printf("DONE\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_race.cc b/test/tsan/java_race.cc new file mode 100644 index 000000000000..ede058e85d8a --- /dev/null +++ b/test/tsan/java_race.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "java.h" + +void *Thread(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)jheap); + *(int*)jheap = 43; + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + fprintf(stderr, "DONE\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_race_move.cc b/test/tsan/java_race_move.cc new file mode 100644 index 000000000000..8a51be92aa10 --- /dev/null +++ b/test/tsan/java_race_move.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "java.h" + +jptr varaddr; +jptr varaddr2; + +void *Thread(void *p) { + sleep(1); + *(int*)varaddr2 = 42; + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 64; + int const kMove = 1024; + __tsan_java_alloc(jheap, kBlockSize); + varaddr = jheap + 16; + varaddr2 = varaddr + kMove; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + *(int*)varaddr = 43; + __tsan_java_move(varaddr, varaddr2, kBlockSize); + pthread_join(th, 0); + __tsan_java_free(varaddr2, kBlockSize); + fprintf(stderr, "DONE\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_rwlock.cc b/test/tsan/java_rwlock.cc new file mode 100644 index 000000000000..b03afa6e092d --- /dev/null +++ b/test/tsan/java_rwlock.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "java.h" +#include <unistd.h> + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_read_lock(lockaddr); + *(int*)varaddr = 42; + __tsan_java_mutex_read_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + varaddr = jheap; + lockaddr = jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + printf("DONE\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/libcxx/lit.local.cfg b/test/tsan/libcxx/lit.local.cfg new file mode 100644 index 000000000000..202b44ee116d --- /dev/null +++ b/test/tsan/libcxx/lit.local.cfg @@ -0,0 +1,10 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if not root.has_libcxx: + config.unsupported = True + diff --git a/test/tsan/libcxx/std_shared_ptr.cc b/test/tsan/libcxx/std_shared_ptr.cc new file mode 100644 index 000000000000..191a17cc798d --- /dev/null +++ b/test/tsan/libcxx/std_shared_ptr.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <memory> +#include <thread> + +int main() { + int v1 = 0; + int v2 = 0; + std::thread t1; + std::thread t2; + + { + auto thingy = std::make_shared<int>(42); + t1 = std::thread([thingy, &v1] { v1 = *thingy; }); + t2 = std::thread([thingy, &v2] { v2 = *thingy; }); + } + + t1.join(); + t2.join(); + printf("%d %d\n", v1, v2); + // CHECK-NOT: ThreadSanitizer: data race + // CHECK: 42 42 + return 0; +} diff --git a/test/tsan/lit.cfg b/test/tsan/lit.cfg new file mode 100644 index 000000000000..d27500f8e3ea --- /dev/null +++ b/test/tsan/lit.cfg @@ -0,0 +1,65 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'ThreadSanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup environment variables for running ThreadSanitizer. +tsan_options = "atexit_sleep_ms=0" + +config.environment['TSAN_OPTIONS'] = tsan_options + +# GCC driver doesn't add necessary compile/link flags with -fsanitize=thread. +if config.compiler_id == 'GNU': + extra_cflags = ["-fPIE", "-pthread", "-ldl", "-lstdc++", "-lrt", "-pie"] +else: + extra_cflags = [] + +# Setup default compiler flags used with -fsanitize=thread option. +clang_tsan_cflags = ["-fsanitize=thread", + "-Wall", + "-m64"] + config.debug_info_flags + extra_cflags +clang_tsan_cxxflags = config.cxx_mode_flags + clang_tsan_cflags +# Add additional flags if we're using instrumented libc++. +if config.has_libcxx: + # FIXME: Dehardcode this path somehow. + libcxx_path = os.path.join(config.compiler_rt_obj_root, "lib", + "tsan", "libcxx_tsan") + libcxx_incdir = os.path.join(libcxx_path, "include", "c++", "v1") + libcxx_libdir = os.path.join(libcxx_path, "lib") + libcxx_so = os.path.join(libcxx_libdir, "libc++.so") + clang_tsan_cxxflags += ["-std=c++11", + "-I%s" % libcxx_incdir, + libcxx_so, + "-Wl,-rpath=%s" % libcxx_libdir] + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang_tsan ", build_invocation(clang_tsan_cflags)) ) +config.substitutions.append( ("%clangxx_tsan ", build_invocation(clang_tsan_cxxflags)) ) + +# Define CHECK-%os to check for OS-dependent output. +config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) + +config.substitutions.append( ("%deflake ", os.path.join(os.path.dirname(__file__), "deflake.bash")) ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# ThreadSanitizer tests are currently supported on FreeBSD and Linux only. +if config.host_os not in ['FreeBSD', 'Linux']: + config.unsupported = True diff --git a/test/tsan/lit.site.cfg.in b/test/tsan/lit.site.cfg.in new file mode 100644 index 000000000000..5190b211177d --- /dev/null +++ b/test/tsan/lit.site.cfg.in @@ -0,0 +1,10 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +config.has_libcxx = @TSAN_HAS_LIBCXX@ + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/test/tsan/load_shared_lib.cc b/test/tsan/load_shared_lib.cc new file mode 100644 index 000000000000..a27dc1cc6ffb --- /dev/null +++ b/test/tsan/load_shared_lib.cc @@ -0,0 +1,66 @@ +// Check that if the list of shared libraries changes between the two race +// reports, the second report occurring in a new shared library is still +// symbolized correctly. + +// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s + +#ifdef BUILD_SO + +#include <stddef.h> +#include <unistd.h> + +int GLOB_SHARED = 0; + +extern "C" +void *write_from_so(void *unused) { + if (unused) + sleep(1); + GLOB_SHARED++; + return NULL; +} + +#else // BUILD_SO + +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +#include <string> + +int GLOB = 0; + +void *write_glob(void *unused) { + if (unused) + sleep(1); + GLOB++; + return NULL; +} + +void race_two_threads(void *(*access_callback)(void *unused)) { + pthread_t t1, t2; + pthread_create(&t1, NULL, access_callback, (void*)1); + pthread_create(&t2, NULL, access_callback, NULL); + pthread_join(t1, NULL); + pthread_join(t2, NULL); +} + +int main(int argc, char *argv[]) { + std::string path = std::string(argv[0]) + std::string("-so.so"); + race_two_threads(write_glob); + // CHECK: write_glob + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + void *(*write_from_so)(void *unused); + *(void **)&write_from_so = dlsym(lib, "write_from_so"); + race_two_threads(write_from_so); + // CHECK: write_from_so + return 0; +} + +#endif // BUILD_SO diff --git a/test/tsan/longjmp.cc b/test/tsan/longjmp.cc new file mode 100644 index 000000000000..d7371c5e4069 --- /dev/null +++ b/test/tsan/longjmp.cc @@ -0,0 +1,22 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +int foo(jmp_buf env) { + longjmp(env, 42); +} + +int main() { + jmp_buf env; + if (setjmp(env) == 42) { + printf("JUMPED\n"); + return 0; + } + foo(env); + printf("FAILED\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK: JUMPED diff --git a/test/tsan/longjmp2.cc b/test/tsan/longjmp2.cc new file mode 100644 index 000000000000..546019b2d11a --- /dev/null +++ b/test/tsan/longjmp2.cc @@ -0,0 +1,24 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +int foo(sigjmp_buf env) { + printf("env=%p\n", env); + siglongjmp(env, 42); +} + +int main() { + sigjmp_buf env; + printf("env=%p\n", env); + if (sigsetjmp(env, 1) == 42) { + printf("JUMPED\n"); + return 0; + } + foo(env); + printf("FAILED\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK: JUMPED diff --git a/test/tsan/longjmp3.cc b/test/tsan/longjmp3.cc new file mode 100644 index 000000000000..71d964dbbed9 --- /dev/null +++ b/test/tsan/longjmp3.cc @@ -0,0 +1,48 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +void bar(jmp_buf env) { + volatile int x = 42; + longjmp(env, 42); + x++; +} + +void foo(jmp_buf env) { + volatile int x = 42; + bar(env); + x++; +} + +void badguy() { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + pthread_mutex_destroy(&mtx); +} + +void mymain() { + jmp_buf env; + if (setjmp(env) == 42) { + badguy(); + return; + } + foo(env); + printf("FAILED\n"); +} + +int main() { + volatile int x = 42; + mymain(); + return x; +} + +// CHECK-NOT: FAILED +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 badguy +// CHECK: #2 mymain +// CHECK: #3 main + diff --git a/test/tsan/longjmp4.cc b/test/tsan/longjmp4.cc new file mode 100644 index 000000000000..15330f5d83c8 --- /dev/null +++ b/test/tsan/longjmp4.cc @@ -0,0 +1,51 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#include <string.h> + +void bar(jmp_buf env) { + volatile int x = 42; + jmp_buf env2; + memcpy(env2, env, sizeof(jmp_buf)); + longjmp(env2, 42); + x++; +} + +void foo(jmp_buf env) { + volatile int x = 42; + bar(env); + x++; +} + +void badguy() { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + pthread_mutex_destroy(&mtx); +} + +void mymain() { + jmp_buf env; + if (setjmp(env) == 42) { + badguy(); + return; + } + foo(env); + printf("FAILED\n"); +} + +int main() { + volatile int x = 42; + mymain(); + return x; +} + +// CHECK-NOT: FAILED +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 badguy +// CHECK: #2 mymain +// CHECK: #3 main + diff --git a/test/tsan/malloc_overflow.cc b/test/tsan/malloc_overflow.cc new file mode 100644 index 000000000000..dadc94484f01 --- /dev/null +++ b/test/tsan/malloc_overflow.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS=allocator_may_return_null=1 %run %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> + +int main() { + void *p = malloc((size_t)-1); + if (p != 0) + printf("FAIL malloc(-1) = %p\n", p); + p = malloc((size_t)-1 / 2); + if (p != 0) + printf("FAIL malloc(-1/2) = %p\n", p); + p = calloc((size_t)-1, (size_t)-1); + if (p != 0) + printf("FAIL calloc(-1, -1) = %p\n", p); + p = calloc((size_t)-1 / 2, (size_t)-1 / 2); + if (p != 0) + printf("FAIL calloc(-1/2, -1/2) = %p\n", p); + printf("OK\n"); +} + +// CHECK-NOT: FAIL +// CHECK-NOT: failed to allocate diff --git a/test/tsan/malloc_stack.cc b/test/tsan/malloc_stack.cc new file mode 100644 index 000000000000..6027360754aa --- /dev/null +++ b/test/tsan/malloc_stack.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +_Atomic(int*) p; + +void *thr(void *a) { + sleep(1); + int *pp = __c11_atomic_load(&p, __ATOMIC_RELAXED); + *pp = 42; + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, thr, p); + __c11_atomic_store(&p, new int, __ATOMIC_RELAXED); + pthread_join(th, 0); +} + +// CHECK: data race +// CHECK: Previous write +// CHECK: #0 operator new +// CHECK: Location is heap block +// CHECK: #0 operator new diff --git a/test/tsan/map32bit.cc b/test/tsan/map32bit.cc new file mode 100644 index 000000000000..3a76fa2f6d62 --- /dev/null +++ b/test/tsan/map32bit.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <stdint.h> +#include <unistd.h> +#include <errno.h> +#include <sys/mman.h> + +// Test for issue: +// https://code.google.com/p/thread-sanitizer/issues/detail?id=5 + +void *Thread(void *ptr) { + *(int*)ptr = 42; + return 0; +} + +int main() { + void *ptr = mmap(0, 128 << 10, PROT_READ|PROT_WRITE, + MAP_32BIT|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + fprintf(stderr, "ptr=%p\n", ptr); + if (ptr == MAP_FAILED) { + fprintf(stderr, "mmap failed: %d\n", errno); + return 1; + } + if ((uintptr_t)ptr >= (1ull << 32)) { + fprintf(stderr, "ptr is too high\n"); + return 1; + } + pthread_t t; + pthread_create(&t, 0, Thread, ptr); + sleep(1); + *(int*)ptr = 42; + pthread_join(t, 0); + munmap(ptr, 128 << 10); + fprintf(stderr, "DONE\n"); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: DONE + diff --git a/test/tsan/memcpy_race.cc b/test/tsan/memcpy_race.cc new file mode 100644 index 000000000000..8ec8e0a3e6a5 --- /dev/null +++ b/test/tsan/memcpy_race.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +char *data = new char[10]; +char *data1 = new char[10]; +char *data2 = new char[10]; + +void *Thread1(void *x) { + static volatile int size = 1; + memcpy(data+5, data1, size); + return NULL; +} + +void *Thread2(void *x) { + static volatile int size = 4; + sleep(1); + memcpy(data+3, data2, size); + return NULL; +} + +int main() { + fprintf(stderr, "addr=%p\n", &data[5]); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 at [[ADDR]] by thread T2: +// CHECK: #0 memcpy +// CHECK: #1 Thread2 +// CHECK: Previous write of size 1 at [[ADDR]] by thread T1: +// CHECK: #0 memcpy +// CHECK: #1 Thread1 diff --git a/test/tsan/mmap_large.cc b/test/tsan/mmap_large.cc new file mode 100644 index 000000000000..e715ea666231 --- /dev/null +++ b/test/tsan/mmap_large.cc @@ -0,0 +1,20 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdint.h> +#include <stdio.h> +#include <errno.h> +#include <sys/mman.h> + +int main() { + const size_t kLog2Size = 39; + const uintptr_t kLocation = 0x40ULL << kLog2Size; + void *p = mmap( + reinterpret_cast<void*>(kLocation), + 1ULL << kLog2Size, + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, + -1, 0); + fprintf(stderr, "DONE %p %d\n", p, errno); + return p == MAP_FAILED; +} + +// CHECK: DONE diff --git a/test/tsan/mop_with_offset.cc b/test/tsan/mop_with_offset.cc new file mode 100644 index 000000000000..e44c78b7d1b4 --- /dev/null +++ b/test/tsan/mop_with_offset.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> + +void *Thread1(void *x) { + int *p = (int*)x; + p[0] = 1; + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + char *p = (char*)x; + p[2] = 1; + return NULL; +} + +int main() { + int *data = new int(42); + fprintf(stderr, "ptr1=%p\n", data); + fprintf(stderr, "ptr2=%p\n", (char*)data + 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, data); + pthread_create(&t[1], NULL, Thread2, data); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + delete data; +} + +// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] +// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 at [[PTR2]] by thread T2: +// CHECK: Previous write of size 4 at [[PTR1]] by thread T1: diff --git a/test/tsan/mop_with_offset2.cc b/test/tsan/mop_with_offset2.cc new file mode 100644 index 000000000000..a465d5f09471 --- /dev/null +++ b/test/tsan/mop_with_offset2.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> + +void *Thread1(void *x) { + sleep(1); + int *p = (int*)x; + p[0] = 1; + return NULL; +} + +void *Thread2(void *x) { + char *p = (char*)x; + p[2] = 1; + return NULL; +} + +int main() { + int *data = new int(42); + fprintf(stderr, "ptr1=%p\n", data); + fprintf(stderr, "ptr2=%p\n", (char*)data + 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, data); + pthread_create(&t[1], NULL, Thread2, data); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + delete data; +} + +// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] +// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at [[PTR1]] by thread T1: +// CHECK: Previous write of size 1 at [[PTR2]] by thread T2: diff --git a/test/tsan/must_deadlock.cc b/test/tsan/must_deadlock.cc new file mode 100644 index 000000000000..1409800e9b39 --- /dev/null +++ b/test/tsan/must_deadlock.cc @@ -0,0 +1,50 @@ +// Test that the deadlock detector can find a deadlock that actually happened. +// Currently we will fail to report such a deadlock because we check for +// cycles in lock-order graph after pthread_mutex_lock. + +// RUN: %clangxx_tsan %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// XFAIL: * +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +pthread_mutex_t mu1, mu2; +pthread_barrier_t barrier; + +void *Thread(void *p) { + // mu2 => mu1 + pthread_mutex_lock(&mu2); + pthread_barrier_wait(&barrier); + pthread_mutex_lock(&mu1); + // CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock) + pthread_mutex_unlock(&mu1); + pthread_mutex_unlock(&mu2); + return p; +} + +int main() { + pthread_mutex_init(&mu1, NULL); + pthread_mutex_init(&mu2, NULL); + pthread_barrier_init(&barrier, 0, 2); + + fprintf(stderr, "This test is going to deadlock and die in 3 seconds\n"); + alarm(3); + + pthread_t t; + pthread_create(&t, 0, Thread, 0); + + // mu1 => mu2 + pthread_mutex_lock(&mu1); + pthread_barrier_wait(&barrier); + pthread_mutex_lock(&mu2); + pthread_mutex_unlock(&mu2); + pthread_mutex_unlock(&mu1); + + pthread_join(t, 0); + + pthread_mutex_destroy(&mu1); + pthread_mutex_destroy(&mu2); + pthread_barrier_destroy(&barrier); + fprintf(stderr, "FAILED\n"); +} diff --git a/test/tsan/mutex_bad_read_lock.cc b/test/tsan/mutex_bad_read_lock.cc new file mode 100644 index 000000000000..84a2976d53e4 --- /dev/null +++ b/test/tsan/mutex_bad_read_lock.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +extern "C" void AnnotateRWLockAcquired(const char *f, int l, void *m, long rw); + +int main() { + int m = 0; + AnnotateRWLockAcquired(__FILE__, __LINE__, &m, 1); + AnnotateRWLockAcquired(__FILE__, __LINE__, &m, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: read lock of a write locked mutex +// CHECK: #0 AnnotateRWLockAcquired +// CHECK: #1 main +// CHECK: Location is stack of main thread. +// CHECK: Mutex {{.*}}) created at: +// CHECK: #0 AnnotateRWLockAcquired +// CHECK: #1 main +// CHECK: SUMMARY: ThreadSanitizer: read lock of a write locked mutex + diff --git a/test/tsan/mutex_bad_read_unlock.cc b/test/tsan/mutex_bad_read_unlock.cc new file mode 100644 index 000000000000..dcee51599d55 --- /dev/null +++ b/test/tsan/mutex_bad_read_unlock.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +extern "C" void AnnotateRWLockAcquired(const char *f, int l, void *m, long rw); +extern "C" void AnnotateRWLockReleased(const char *f, int l, void *m, long rw); + +int main() { + int m = 0; + AnnotateRWLockAcquired(__FILE__, __LINE__, &m, 1); + AnnotateRWLockReleased(__FILE__, __LINE__, &m, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: read unlock of a write locked mutex +// CHECK: #0 AnnotateRWLockReleased +// CHECK: #1 main +// CHECK: Location is stack of main thread. +// CHECK: Mutex {{.*}}) created at: +// CHECK: #0 AnnotateRWLockAcquired +// CHECK: #1 main +// CHECK: SUMMARY: ThreadSanitizer: read unlock of a write locked mutex + diff --git a/test/tsan/mutex_bad_unlock.cc b/test/tsan/mutex_bad_unlock.cc new file mode 100644 index 000000000000..6b483cf17eda --- /dev/null +++ b/test/tsan/mutex_bad_unlock.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +extern "C" void AnnotateRWLockReleased(const char *f, int l, void *m, long rw); + +int main() { + int m = 0; + AnnotateRWLockReleased(__FILE__, __LINE__, &m, 1); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: unlock of an unlocked mutex (or by a wrong thread) +// CHECK: #0 AnnotateRWLockReleased +// CHECK: #1 main +// CHECK: Location is stack of main thread. +// CHECK: Mutex {{.*}} created at: +// CHECK: #0 AnnotateRWLockReleased +// CHECK: #1 main +// CHECK: SUMMARY: ThreadSanitizer: unlock of an unlocked mutex (or by a wrong thread) + diff --git a/test/tsan/mutex_cycle2.c b/test/tsan/mutex_cycle2.c new file mode 100644 index 000000000000..031830d5ec63 --- /dev/null +++ b/test/tsan/mutex_cycle2.c @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: TSAN_OPTIONS=detect_deadlocks=1 not %run %t 2>&1 | FileCheck %s +// RUN: TSAN_OPTIONS=detect_deadlocks=0 %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED +// RUN: echo "deadlock:main" > %t.sup +// RUN: TSAN_OPTIONS="suppressions=%t.sup" %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED +// RUN: echo "deadlock:zzzz" > %t.sup +// RUN: TSAN_OPTIONS="suppressions=%t.sup" not %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int main() { + pthread_mutex_t mu1, mu2; + pthread_mutex_init(&mu1, NULL); + pthread_mutex_init(&mu2, NULL); + + // mu1 => mu2 + pthread_mutex_lock(&mu1); + pthread_mutex_lock(&mu2); + pthread_mutex_unlock(&mu2); + pthread_mutex_unlock(&mu1); + + // mu2 => mu1 + pthread_mutex_lock(&mu2); + pthread_mutex_lock(&mu1); + // CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock) + // DISABLED-NOT: ThreadSanitizer + // DISABLED: PASS + pthread_mutex_unlock(&mu1); + pthread_mutex_unlock(&mu2); + + pthread_mutex_destroy(&mu1); + pthread_mutex_destroy(&mu2); + fprintf(stderr, "PASS\n"); +} diff --git a/test/tsan/mutex_destroy_locked.cc b/test/tsan/mutex_destroy_locked.cc new file mode 100644 index 000000000000..b81905ec68fd --- /dev/null +++ b/test/tsan/mutex_destroy_locked.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int main() { + pthread_mutex_t m; + pthread_mutex_init(&m, 0); + pthread_mutex_lock(&m); + pthread_mutex_destroy(&m); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 main +// CHECK: and: +// CHECK: #0 pthread_mutex_lock +// CHECK: #1 main +// CHECK: Mutex {{.*}} created at: +// CHECK: #0 pthread_mutex_init +// CHECK: #1 main +// CHECK: SUMMARY: ThreadSanitizer: destroy of a locked mutex{{.*}}main diff --git a/test/tsan/mutex_double_lock.cc b/test/tsan/mutex_double_lock.cc new file mode 100644 index 000000000000..c1bebf73706e --- /dev/null +++ b/test/tsan/mutex_double_lock.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +extern "C" void AnnotateRWLockAcquired(const char *f, int l, void *m, long rw); + +void *ThreadFunc(void *m) { + AnnotateRWLockAcquired(__FILE__, __LINE__, m, 1); + return 0; +} + +int main() { + int m = 0; + AnnotateRWLockAcquired(__FILE__, __LINE__, &m, 1); + pthread_t th; + pthread_create(&th, 0, ThreadFunc, &m); + pthread_join(th, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: double lock of a mutex +// CHECK: #0 AnnotateRWLockAcquired +// CHECK: #1 ThreadFunc +// CHECK: Location is stack of main thread. +// CHECK: Mutex {{.*}} created at: +// CHECK: #0 AnnotateRWLockAcquired +// CHECK: #1 main +// CHECK: SUMMARY: ThreadSanitizer: double lock of a mutex {{.*}}mutex_double_lock.cc{{.*}}ThreadFunc + diff --git a/test/tsan/mutexset1.cc b/test/tsan/mutexset1.cc new file mode 100644 index 000000000000..72964edfb1e7 --- /dev/null +++ b/test/tsan/mutexset1.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx); + Global++; + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset1.cc:[[@LINE+1]] + pthread_mutex_init(&mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx); +} diff --git a/test/tsan/mutexset2.cc b/test/tsan/mutexset2.cc new file mode 100644 index 000000000000..01a5f5df6e94 --- /dev/null +++ b/test/tsan/mutexset2.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx); + Global++; + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T2: + // CHECK: Previous write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Mutex [[M1]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset2.cc:[[@LINE+1]] + pthread_mutex_init(&mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx); +} diff --git a/test/tsan/mutexset3.cc b/test/tsan/mutexset3.cc new file mode 100644 index 000000000000..e14bb1111e32 --- /dev/null +++ b/test/tsan/mutexset3.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + pthread_mutex_lock(&mtx2); + Global++; + pthread_mutex_unlock(&mtx2); + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+2]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/test/tsan/mutexset4.cc b/test/tsan/mutexset4.cc new file mode 100644 index 000000000000..db860e005d67 --- /dev/null +++ b/test/tsan/mutexset4.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx1); + pthread_mutex_lock(&mtx2); + Global++; + pthread_mutex_unlock(&mtx2); + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T2: + // CHECK: Previous write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]): + // CHECK: Mutex [[M1]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+2]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/test/tsan/mutexset5.cc b/test/tsan/mutexset5.cc new file mode 100644 index 000000000000..e1cc2fcacf62 --- /dev/null +++ b/test/tsan/mutexset5.cc @@ -0,0 +1,46 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + Global++; + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_lock(&mtx2); + Global--; + pthread_mutex_unlock(&mtx2); + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2 + // CHECK: (mutexes: write [[M2:M[0-9]+]]): + // CHECK: Mutex [[M1]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+5]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/test/tsan/mutexset6.cc b/test/tsan/mutexset6.cc new file mode 100644 index 000000000000..07dcc0a7394d --- /dev/null +++ b/test/tsan/mutexset6.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx1; +pthread_spinlock_t mtx2; +pthread_rwlock_t mtx3; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + Global++; + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_lock(&mtx1); + pthread_mutex_unlock(&mtx1); + pthread_spin_lock(&mtx2); + pthread_rwlock_rdlock(&mtx3); + Global--; + pthread_spin_unlock(&mtx2); + pthread_rwlock_unlock(&mtx3); + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2 + // CHECK: (mutexes: write [[M2:M[0-9]+]], read [[M3:M[0-9]+]]): + // CHECK: Mutex [[M1]] (0x{{.*}}) created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+5]] + // CHECK: Mutex [[M2]] (0x{{.*}}) created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+4]] + // CHECK: Mutex [[M3]] (0x{{.*}}) created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+3]] + pthread_mutex_init(&mtx1, 0); + pthread_spin_init(&mtx2, 0); + pthread_rwlock_init(&mtx3, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_spin_destroy(&mtx2); + pthread_rwlock_destroy(&mtx3); +} diff --git a/test/tsan/mutexset7.cc b/test/tsan/mutexset7.cc new file mode 100644 index 000000000000..12174844c799 --- /dev/null +++ b/test/tsan/mutexset7.cc @@ -0,0 +1,40 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +__thread int huge[1024*1024]; + +void *Thread1(void *x) { + sleep(1); + Global++; + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_t *mtx = new pthread_mutex_t; + pthread_mutex_init(mtx, 0); + pthread_mutex_lock(mtx); + Global--; + pthread_mutex_unlock(mtx); + pthread_mutex_destroy(mtx); + delete mtx; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T1: +// CHECK: Previous write of size 4 at {{.*}} by thread T2 +// CHECK: (mutexes: write [[M1:M[0-9]+]]): +// CHECK: Mutex [[M1]] is already destroyed +// CHECK-NOT: Mutex {{.*}} created at + diff --git a/test/tsan/mutexset8.cc b/test/tsan/mutexset8.cc new file mode 100644 index 000000000000..3e1ab8c5a744 --- /dev/null +++ b/test/tsan/mutexset8.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t *mtx; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(mtx); + Global++; + pthread_mutex_unlock(mtx); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] (0x{{.*}}) created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset8.cc + mtx = new pthread_mutex_t; + pthread_mutex_init(mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(mtx); + delete mtx; +} diff --git a/test/tsan/printf-1.c b/test/tsan/printf-1.c new file mode 100644 index 000000000000..9116c956e30e --- /dev/null +++ b/test/tsan/printf-1.c @@ -0,0 +1,16 @@ +// RUN: %clang_tsan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=check_printf=0 %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + printf("%c %d %.3f %s\n", c, x, f, s); + return 0; + // Check that printf works fine under Tsan. + // CHECK: 0 12 1.239 34 +} diff --git a/test/tsan/process_sleep.h b/test/tsan/process_sleep.h new file mode 100644 index 000000000000..5938a42bf8b2 --- /dev/null +++ b/test/tsan/process_sleep.h @@ -0,0 +1,7 @@ +#include <time.h> + +static void process_sleep(int sec) { + clock_t beg = clock(); + while((clock() - beg) / CLOCKS_PER_SEC < sec) + usleep(100); +} diff --git a/test/tsan/pthread_atfork_deadlock.c b/test/tsan/pthread_atfork_deadlock.c new file mode 100644 index 000000000000..0f33b9022e5f --- /dev/null +++ b/test/tsan/pthread_atfork_deadlock.c @@ -0,0 +1,33 @@ +// RUN: %clang_tsan -O1 %s -lpthread -o %t && %deflake %run %t | FileCheck %s +// Regression test for +// https://code.google.com/p/thread-sanitizer/issues/detail?id=61 +// When the data race was reported, pthread_atfork() handler used to be +// executed which caused another race report in the same thread, which resulted +// in a deadlock. +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int glob = 0; + +void *worker(void *unused) { + sleep(1); + glob++; + return NULL; +} + +void atfork() { + fprintf(stderr, "ATFORK\n"); + glob++; +} + +int main() { + pthread_atfork(atfork, NULL, NULL); + pthread_t t; + pthread_create(&t, NULL, worker, NULL); + glob++; + pthread_join(t, NULL); + // CHECK: ThreadSanitizer: data race + // CHECK-NOT: ATFORK + return 0; +} diff --git a/test/tsan/race_on_barrier.c b/test/tsan/race_on_barrier.c new file mode 100644 index 000000000000..99b18fe4d8e6 --- /dev/null +++ b/test/tsan/race_on_barrier.c @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_barrier_t B; +int Global; + +void *Thread1(void *x) { + pthread_barrier_init(&B, 0, 2); + pthread_barrier_wait(&B); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_barrier_wait(&B); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + pthread_barrier_destroy(&B); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/race_on_barrier2.c b/test/tsan/race_on_barrier2.c new file mode 100644 index 000000000000..98c028e19fdd --- /dev/null +++ b/test/tsan/race_on_barrier2.c @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_barrier_t B; +int Global; + +void *Thread1(void *x) { + if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD) + pthread_barrier_destroy(&B); + return NULL; +} + +void *Thread2(void *x) { + if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD) + pthread_barrier_destroy(&B); + return NULL; +} + +int main() { + pthread_barrier_init(&B, 0, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/race_on_heap.cc b/test/tsan/race_on_heap.cc new file mode 100644 index 000000000000..a66e0c4f93f7 --- /dev/null +++ b/test/tsan/race_on_heap.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +void *Thread1(void *p) { + *(int*)p = 42; + return 0; +} + +void *Thread2(void *p) { + *(int*)p = 44; + return 0; +} + +void *alloc() { + return malloc(99); +} + +void *AllocThread(void* arg) { + return alloc(); +} + +int main() { + void *p = 0; + pthread_t t[2]; + pthread_create(&t[0], 0, AllocThread, 0); + pthread_join(t[0], &p); + fprintf(stderr, "addr=%p\n", p); + pthread_create(&t[0], 0, Thread1, (char*)p + 16); + pthread_create(&t[1], 0, Thread2, (char*)p + 16); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + return 0; +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// ... +// CHECK: Location is heap block of size 99 at [[ADDR]] allocated by thread T1: +// CHCEK: #0 malloc +// CHECK: #{{1|2}} alloc +// CHECK: #{{2|3}} AllocThread +// ... +// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at: +// CHECK: #0 pthread_create +// CHECK: #1 main diff --git a/test/tsan/race_on_mutex.c b/test/tsan/race_on_mutex.c new file mode 100644 index 000000000000..b4adeeb4df72 --- /dev/null +++ b/test/tsan/race_on_mutex.c @@ -0,0 +1,42 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_mutex_t Mtx; +int Global; + +void *Thread1(void *x) { + pthread_mutex_init(&Mtx, 0); + pthread_mutex_lock(&Mtx); + Global = 42; + pthread_mutex_unlock(&Mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_mutex_lock(&Mtx); + Global = 43; + pthread_mutex_unlock(&Mtx); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&Mtx); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Atomic read of size 1 at {{.*}} by thread T2: +// CHECK-NEXT: #0 pthread_mutex_lock +// CHECK-NEXT: #1 Thread2{{.*}} {{.*}}race_on_mutex.c:20{{(:3)?}} ({{.*}}) +// CHECK: Previous write of size 1 at {{.*}} by thread T1: +// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}}) +// CHECK-NEXT: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}}) diff --git a/test/tsan/race_on_mutex2.c b/test/tsan/race_on_mutex2.c new file mode 100644 index 000000000000..1796d0c6480b --- /dev/null +++ b/test/tsan/race_on_mutex2.c @@ -0,0 +1,24 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread(void *x) { + pthread_mutex_lock((pthread_mutex_t*)x); + pthread_mutex_unlock((pthread_mutex_t*)x); + return 0; +} + +int main() { + pthread_mutex_t Mtx; + pthread_mutex_init(&Mtx, 0); + pthread_t t; + pthread_create(&t, 0, Thread, &Mtx); + sleep(1); + pthread_mutex_destroy(&Mtx); + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/race_on_puts.cc b/test/tsan/race_on_puts.cc new file mode 100644 index 000000000000..1f2b4db836ed --- /dev/null +++ b/test/tsan/race_on_puts.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +char s[] = "abracadabra"; + +void *Thread0(void *p) { + puts(s); + return 0; +} + +void *Thread1(void *p) { + s[3] = 'z'; + return 0; +} + +int main() { + pthread_t th[2]; + pthread_create(&th[0], 0, Thread0, 0); + pthread_create(&th[1], 0, Thread1, 0); + pthread_join(th[0], 0); + pthread_join(th[1], 0); + fprintf(stderr, "DONE"); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: DONE + diff --git a/test/tsan/race_on_read.cc b/test/tsan/race_on_read.cc new file mode 100644 index 000000000000..1ec0522b9035 --- /dev/null +++ b/test/tsan/race_on_read.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +int fd; +char buf; + +void *Thread(void *x) { + sleep(1); + read(fd, &buf, 1); + return NULL; +} + +int main() { + fd = open("/dev/random", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "failed to open /dev/random (%d)\n", errno); + return 1; + } + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread, NULL); + pthread_create(&t[1], NULL, Thread, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + close(fd); + fprintf(stderr, "DONE\n"); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 +// CHECK: #0 read +// CHECK: Previous write of size 1 +// CHECK: #0 read +// CHECK: DONE + diff --git a/test/tsan/race_on_speculative_load.cc b/test/tsan/race_on_speculative_load.cc new file mode 100644 index 000000000000..f816db9e8853 --- /dev/null +++ b/test/tsan/race_on_speculative_load.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t | FileCheck %s +// Regtest for https://code.google.com/p/thread-sanitizer/issues/detail?id=40 +// This is a correct program and tsan should not report a race. +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> +int g; +__attribute__((noinline)) +int foo(int cond) { + if (cond) + return g; + return 0; +} +void *Thread1(void *p) { + long res = foo((long)p); + sleep(1); + return (void*) res; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + g = 1; + pthread_join(t, 0); + printf("PASS\n"); + // CHECK: PASS +} diff --git a/test/tsan/race_on_write.cc b/test/tsan/race_on_write.cc new file mode 100644 index 000000000000..484bbb7ae022 --- /dev/null +++ b/test/tsan/race_on_write.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int fd; +char buf; + +void *Thread1(void *x) { + buf = 1; + sleep(1); + return NULL; +} + +void *Thread2(void *x) { + write(fd, &buf, 1); + return NULL; +} + +int main() { + fd = open("/dev/null", O_WRONLY); + if (fd < 0) return 1; + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + sleep(1); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + close(fd); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Read of size 1 +// CHECK: #0 write +// CHECK: Previous write of size 1 +// CHECK: #0 Thread1 diff --git a/test/tsan/race_with_finished_thread.cc b/test/tsan/race_with_finished_thread.cc new file mode 100644 index 000000000000..d28760093c42 --- /dev/null +++ b/test/tsan/race_with_finished_thread.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +// Ensure that we can restore a stack of a finished thread. + +int g_data; + +void __attribute__((noinline)) foobar(int *p) { + *p = 42; +} + +void *Thread1(void *x) { + foobar(&g_data); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + g_data = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T2: +// CHECK: Previous write of size 4 at {{.*}} by thread T1: +// CHECK: #0 foobar +// CHECK: #1 Thread1 +// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at: +// CHECK: #0 pthread_create +// CHECK: #1 main diff --git a/test/tsan/signal_errno.cc b/test/tsan/signal_errno.cc new file mode 100644 index 000000000000..1fa20f36810b --- /dev/null +++ b/test/tsan/signal_errno.cc @@ -0,0 +1,49 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> + +pthread_t mainth; +volatile int done; + +static void MyHandler(int, siginfo_t *s, void *c) { + errno = 1; + done = 1; +} + +static void* sendsignal(void *p) { + sleep(1); + pthread_kill(mainth, SIGPROF); + return 0; +} + +static __attribute__((noinline)) void loop() { + while (done == 0) { + volatile char *p = (char*)malloc(1); + p[0] = 0; + free((void*)p); + pthread_yield(); + } +} + +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + loop(); + pthread_join(th, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: signal handler spoils errno +// CHECK: #0 MyHandler(int, {{(__)?}}siginfo{{(_t)?}}*, void*) {{.*}}signal_errno.cc +// CHECK: main +// CHECK: SUMMARY: ThreadSanitizer: signal handler spoils errno{{.*}}MyHandler + diff --git a/test/tsan/signal_longjmp.cc b/test/tsan/signal_longjmp.cc new file mode 100644 index 000000000000..84b0682dcbaf --- /dev/null +++ b/test/tsan/signal_longjmp.cc @@ -0,0 +1,69 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Test case for longjumping out of signal handler: +// https://code.google.com/p/thread-sanitizer/issues/detail?id=75 + +#include <setjmp.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/mman.h> + +sigjmp_buf fault_jmp; +volatile int fault_expected; + +void sigfault_handler(int sig) { + if (!fault_expected) + abort(); + + /* just return from sighandler to proper place */ + fault_expected = 0; + siglongjmp(fault_jmp, 1); +} + +#define MUST_FAULT(code) do { \ + fault_expected = 1; \ + if (!sigsetjmp(fault_jmp, 1)) { \ + code; /* should pagefault -> sihandler does longjmp */ \ + fprintf(stderr, "%s not faulted\n", #code); \ + abort(); \ + } else { \ + fprintf(stderr, "%s faulted ok\n", #code); \ + } \ +} while (0) + +int main() { + struct sigaction act; + act.sa_handler = sigfault_handler; + act.sa_flags = 0; + if (sigemptyset(&act.sa_mask)) { + perror("sigemptyset"); + exit(1); + } + + if (sigaction(SIGSEGV, &act, NULL)) { + perror("sigaction"); + exit(1); + } + + void *mem = mmap(0, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANON, + -1, 0); + + MUST_FAULT(((volatile int *volatile)mem)[0] = 0); + MUST_FAULT(((volatile int *volatile)mem)[1] = 1); + MUST_FAULT(((volatile int *volatile)mem)[3] = 1); + + // Ensure that tsan does not think that we are + // in a signal handler. + void *volatile p = malloc(10); + ((volatile int*)p)[1] = 1; + free((void*)p); + + munmap(p, 4096); + + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: DONE diff --git a/test/tsan/signal_malloc.cc b/test/tsan/signal_malloc.cc new file mode 100644 index 000000000000..06932fba42db --- /dev/null +++ b/test/tsan/signal_malloc.cc @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +static void handler(int, siginfo_t*, void*) { + // CHECK: WARNING: ThreadSanitizer: signal-unsafe call inside of a signal + // CHECK: #0 malloc + // CHECK: #{{(1|2)}} handler(int, {{(__)?}}siginfo{{(_t)?}}*, void*) {{.*}}signal_malloc.cc:[[@LINE+2]] + // CHECK: SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal{{.*}}handler + volatile char *p = (char*)malloc(1); + p[0] = 0; + free((void*)p); +} + +int main() { + struct sigaction act = {}; + act.sa_sigaction = &handler; + sigaction(SIGPROF, &act, 0); + kill(getpid(), SIGPROF); + sleep(1); + return 0; +} + diff --git a/test/tsan/signal_recursive.cc b/test/tsan/signal_recursive.cc new file mode 100644 index 000000000000..bbb6807586a5 --- /dev/null +++ b/test/tsan/signal_recursive.cc @@ -0,0 +1,132 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Test case for recursive signal handlers, adopted from: +// https://code.google.com/p/thread-sanitizer/issues/detail?id=71 + +#include <pthread.h> +#include <semaphore.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> + +#include "process_sleep.h" + +static const int kSigSuspend = SIGUSR1; +static const int kSigRestart = SIGUSR2; +static sigset_t g_suspend_handler_mask; + +static sem_t g_thread_suspend_ack_sem; + +static bool g_busy_thread_received_restart; + +static volatile bool g_busy_thread_garbage_collected; + +static void SaveRegistersInStack() { + // Mono walks thread stacks to detect unreferenced objects. + // If last object reference is kept in register the object will be collected + // This is why threads can't be suspended with something like pthread_suspend +}; + +static void fail(const char *what) { + fprintf(stderr, "FAILED: %s (errno=%d)\n", what, errno); + exit(1); +} + +static void SuspendHandler(int sig) { + int old_errno = errno; + SaveRegistersInStack(); + // Acknowledge that thread is saved and suspended + if (sem_post(&g_thread_suspend_ack_sem) != 0) + fail("sem_post failed"); + + do { + g_busy_thread_received_restart = false; + if (sigsuspend(&g_suspend_handler_mask) != -1 || errno != EINTR) + fail("sigsuspend failed"); + } while (!g_busy_thread_received_restart); + + // Acknowledge that thread restarted + if (sem_post(&g_thread_suspend_ack_sem) != 0) + fail("sem_post failed"); + + g_busy_thread_garbage_collected = true; + + errno = old_errno; +} + +static void RestartHandler(int sig) { + g_busy_thread_received_restart = true; +} + +static void StopWorld(pthread_t thread) { + if (pthread_kill(thread, kSigSuspend) != 0) + fail("pthread_kill failed"); + + while (sem_wait(&g_thread_suspend_ack_sem) != 0) { + if (errno != EINTR) + fail("sem_wait failed"); + } +} + +static void StartWorld(pthread_t thread) { + if (pthread_kill(thread, kSigRestart) != 0) + fail("pthread_kill failed"); + + while (sem_wait(&g_thread_suspend_ack_sem) != 0) { + if (errno != EINTR) + fail("sem_wait failed"); + } +} + +static void CollectGarbage(pthread_t thread) { + StopWorld(thread); + // Walk stacks + process_sleep(1); + StartWorld(thread); +} + +static void Init() { + if (sigfillset(&g_suspend_handler_mask) != 0) + fail("sigfillset failed"); + if (sigdelset(&g_suspend_handler_mask, kSigRestart) != 0) + fail("sigdelset failed"); + if (sem_init(&g_thread_suspend_ack_sem, 0, 0) != 0) + fail("sem_init failed"); + + struct sigaction act = {}; + act.sa_flags = SA_RESTART; + sigfillset(&act.sa_mask); + act.sa_handler = &SuspendHandler; + if (sigaction(kSigSuspend, &act, NULL) != 0) + fail("sigaction failed"); + act.sa_handler = &RestartHandler; + if (sigaction(kSigRestart, &act, NULL) != 0) + fail("sigaction failed"); +} + +void* BusyThread(void *arg) { + (void)arg; + while (!g_busy_thread_garbage_collected) { + usleep(100); // Tsan deadlocks without these sleeps + } + return NULL; +} + +int main(int argc, const char *argv[]) { + Init(); + pthread_t busy_thread; + if (pthread_create(&busy_thread, NULL, &BusyThread, NULL) != 0) + fail("pthread_create failed"); + CollectGarbage(busy_thread); + if (pthread_join(busy_thread, 0) != 0) + fail("pthread_join failed"); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK-NOT: ThreadSanitizer CHECK failed +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: DONE diff --git a/test/tsan/signal_sync.cc b/test/tsan/signal_sync.cc new file mode 100644 index 000000000000..15387b754dfb --- /dev/null +++ b/test/tsan/signal_sync.cc @@ -0,0 +1,58 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#include <errno.h> + +volatile int X; + +static void handler(int sig) { + (void)sig; + if (X != 42) + printf("bad"); +} + +static void* thr(void *p) { + for (int i = 0; i != 1000; i++) + usleep(1000); + return 0; +} + +int main() { + const int kThreads = 10; + pthread_t th[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&th[i], 0, thr, 0); + + X = 42; + + struct sigaction act = {}; + act.sa_handler = &handler; + if (sigaction(SIGPROF, &act, 0)) { + perror("sigaction"); + exit(1); + } + + itimerval t; + t.it_value.tv_sec = 0; + t.it_value.tv_usec = 10; + t.it_interval = t.it_value; + if (setitimer(ITIMER_PROF, &t, 0)) { + perror("setitimer"); + exit(1); + } + + for (int i = 0; i < kThreads; i++) + pthread_join(th[i], 0); + + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer: diff --git a/test/tsan/signal_write.cc b/test/tsan/signal_write.cc new file mode 100644 index 000000000000..626d87a7acc7 --- /dev/null +++ b/test/tsan/signal_write.cc @@ -0,0 +1,27 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +static void handler(int, siginfo_t*, void*) { + const char *str = "HELLO FROM SIGNAL\n"; + write(2, str, strlen(str)); +} + +int main() { + struct sigaction act = {}; + act.sa_sigaction = &handler; + sigaction(SIGPROF, &act, 0); + kill(getpid(), SIGPROF); + sleep(1); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: HELLO FROM SIGNAL +// CHECK: DONE + diff --git a/test/tsan/sigsuspend.cc b/test/tsan/sigsuspend.cc new file mode 100644 index 000000000000..a5930d4e87aa --- /dev/null +++ b/test/tsan/sigsuspend.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Always enable asserts. +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include <assert.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <stdio.h> + +static bool signal_handler_ran = false; + +void do_nothing_signal_handler(int signum) { + write(1, "HANDLER\n", 8); + signal_handler_ran = true; +} + +int main() { + const int kSignalToTest = SIGSYS; + assert(SIG_ERR != signal(kSignalToTest, do_nothing_signal_handler)); + sigset_t empty_set; + assert(0 == sigemptyset(&empty_set)); + sigset_t one_signal = empty_set; + assert(0 == sigaddset(&one_signal, kSignalToTest)); + sigset_t old_set; + assert(0 == sigprocmask(SIG_BLOCK, &one_signal, &old_set)); + raise(kSignalToTest); + assert(!signal_handler_ran); + sigset_t all_but_one; + assert(0 == sigfillset(&all_but_one)); + assert(0 == sigdelset(&all_but_one, kSignalToTest)); + sigsuspend(&all_but_one); + assert(signal_handler_ran); + + // Restore the original set. + assert(0 == sigprocmask(SIG_SETMASK, &old_set, NULL)); + printf("DONE\n"); +} + +// CHECK: HANDLER +// CHECK: DONE diff --git a/test/tsan/simple_race.c b/test/tsan/simple_race.c new file mode 100644 index 000000000000..7b60c5ec249e --- /dev/null +++ b/test/tsan/simple_race.c @@ -0,0 +1,29 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/simple_race.cc b/test/tsan/simple_race.cc new file mode 100644 index 000000000000..f711bb5d114d --- /dev/null +++ b/test/tsan/simple_race.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global++; + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: SUMMARY: ThreadSanitizer: data race{{.*}}Thread diff --git a/test/tsan/simple_stack.c b/test/tsan/simple_stack.c new file mode 100644 index 000000000000..87367033b4c9 --- /dev/null +++ b/test/tsan/simple_stack.c @@ -0,0 +1,66 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void __attribute__((noinline)) foo1() { + Global = 42; +} + +void __attribute__((noinline)) bar1() { + volatile int tmp = 42; (void)tmp; + foo1(); +} + +void __attribute__((noinline)) foo2() { + volatile int v = Global; (void)v; +} + +void __attribute__((noinline)) bar2() { + volatile int tmp = 42; (void)tmp; + foo2(); +} + +void *Thread1(void *x) { + sleep(1); + bar1(); + return NULL; +} + +void *Thread2(void *x) { + bar2(); + return NULL; +} + +void StartThread(pthread_t *t, void *(*f)(void*)) { + pthread_create(t, NULL, f, NULL); +} + +int main() { + pthread_t t[2]; + StartThread(&t[0], Thread1); + StartThread(&t[1], Thread2); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1: +// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:9{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:28{{(:3)?}} ({{.*}}) +// CHECK: Previous read of size 4 at {{.*}} by thread T2: +// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:33{{(:3)?}} ({{.*}}) +// CHECK: Thread T1 (tid={{.*}}, running) created by main thread at: +// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}}) +// CHECK: Thread T2 ({{.*}}) created by main thread at: +// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}}) diff --git a/test/tsan/simple_stack2.cc b/test/tsan/simple_stack2.cc new file mode 100644 index 000000000000..b07d863e4008 --- /dev/null +++ b/test/tsan/simple_stack2.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %T/simple_stack2.cc.exe && %deflake %run %T/simple_stack2.cc.exe | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void __attribute__((noinline)) foo1() { + Global = 42; +} + +void __attribute__((noinline)) bar1() { + volatile int tmp = 42; + int tmp2 = tmp; + (void)tmp2; + foo1(); +} + +void __attribute__((noinline)) foo2() { + volatile int tmp = Global; + int tmp2 = tmp; + (void)tmp2; +} + +void __attribute__((noinline)) bar2() { + volatile int tmp = 42; + int tmp2 = tmp; + (void)tmp2; + foo2(); +} + +void *Thread1(void *x) { + sleep(1); + bar1(); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + bar2(); + pthread_join(t, NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1: +// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} (simple_stack2.cc.exe+{{.*}}) +// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} (simple_stack2.cc.exe+{{.*}}) +// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} (simple_stack2.cc.exe+{{.*}}) +// CHECK: Previous read of size 4 at {{.*}} by main thread: +// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:20{{(:3)?}} (simple_stack2.cc.exe+{{.*}}) +// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:29{{(:3)?}} (simple_stack2.cc.exe+{{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:41{{(:3)?}} (simple_stack2.cc.exe+{{.*}}) diff --git a/test/tsan/sleep_sync.cc b/test/tsan/sleep_sync.cc new file mode 100644 index 000000000000..c7614e16bf3e --- /dev/null +++ b/test/tsan/sleep_sync.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int X = 0; + +void MySleep() { + sleep(1); +} + +void *Thread(void *p) { + MySleep(); // Assume the main thread has done the write. + X = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// ... +// CHECK: As if synchronized via sleep: +// CHECK-NEXT: #0 sleep +// CHECK-NEXT: #1 MySleep +// CHECK-NEXT: #2 Thread diff --git a/test/tsan/sleep_sync2.cc b/test/tsan/sleep_sync2.cc new file mode 100644 index 000000000000..4e616992ecc9 --- /dev/null +++ b/test/tsan/sleep_sync2.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int X = 0; + +void *Thread(void *p) { + X = 42; + return 0; +} + +int main() { + pthread_t t; + sleep(1); + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: As if synchronized via sleep diff --git a/test/tsan/stack_race.cc b/test/tsan/stack_race.cc new file mode 100644 index 000000000000..2e02f46a281f --- /dev/null +++ b/test/tsan/stack_race.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread(void *a) { + sleep(1); + *(int*)a = 43; + return 0; +} + +int main() { + int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread, &Var); + Var = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is stack of main thread. + diff --git a/test/tsan/stack_race2.cc b/test/tsan/stack_race2.cc new file mode 100644 index 000000000000..818db367bab6 --- /dev/null +++ b/test/tsan/stack_race2.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread2(void *a) { + sleep(1); + *(int*)a = 43; + return 0; +} + +void *Thread(void *a) { + int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread2, &Var); + Var = 42; + pthread_join(t, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is stack of thread T1. + diff --git a/test/tsan/static_init1.cc b/test/tsan/static_init1.cc new file mode 100644 index 000000000000..3e5fb14ba44b --- /dev/null +++ b/test/tsan/static_init1.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +struct P { + int x; + int y; +}; + +void *Thread(void *x) { + static P p = {rand(), rand()}; + if (p.x > RAND_MAX || p.y > RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread, 0); + pthread_create(&t[1], 0, Thread, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init2.cc b/test/tsan/static_init2.cc new file mode 100644 index 000000000000..667aed1343dc --- /dev/null +++ b/test/tsan/static_init2.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void foo(Cache *my) { + static Cache *c = my ? my : new Cache(rand()); + if (c->x >= RAND_MAX) + exit(1); +} + +void *Thread(void *x) { + foo(new Cache(rand())); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread, 0); + pthread_create(&t[1], 0, Thread, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init3.cc b/test/tsan/static_init3.cc new file mode 100644 index 000000000000..3b9fe62ae2bc --- /dev/null +++ b/test/tsan/static_init3.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; +}; + +Cache g_cache; + +Cache *CreateCache() { + g_cache.x = rand(); + return &g_cache; +} + +_Atomic(Cache*) queue; + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + __c11_atomic_store(&queue, c, 0); + return 0; +} + +void *Thread2(void *x) { + Cache *c = 0; + for (;;) { + c = __c11_atomic_load(&queue, 0); + if (c) + break; + sched_yield(); + } + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init4.cc b/test/tsan/static_init4.cc new file mode 100644 index 000000000000..85835a2520f7 --- /dev/null +++ b/test/tsan/static_init4.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +int g_other; + +Cache *CreateCache() { + g_other = rand(); + return new Cache(rand()); +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x == g_other) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init5.cc b/test/tsan/static_init5.cc new file mode 100644 index 000000000000..961e3a3b6329 --- /dev/null +++ b/test/tsan/static_init5.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void *AsyncInit(void *p) { + return new Cache((int)(long)p); +} + +Cache *CreateCache() { + pthread_t t; + pthread_create(&t, 0, AsyncInit, (void*)(long)rand()); + void *res; + pthread_join(t, &res); + return (Cache*)res; +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init6.cc b/test/tsan/static_init6.cc new file mode 100644 index 000000000000..77253eac173f --- /dev/null +++ b/test/tsan/static_init6.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -static-libstdc++ -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void *AsyncInit(void *p) { + return new Cache((int)(long)p); +} + +Cache *CreateCache() { + pthread_t t; + pthread_create(&t, 0, AsyncInit, (void*)(long)rand()); + void *res; + pthread_join(t, &res); + return (Cache*)res; +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/sunrpc.cc b/test/tsan/sunrpc.cc new file mode 100644 index 000000000000..579816d64098 --- /dev/null +++ b/test/tsan/sunrpc.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <pthread.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <stdio.h> + +void *thr(void *p) { + XDR xdrs; + char buf[100]; + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_ENCODE); + xdr_destroy(&xdrs); + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t th[2]; + pthread_create(&th[0], 0, thr, 0); + pthread_create(&th[1], 0, thr, 0); + pthread_join(th[0], 0); + pthread_join(th[1], 0); + printf("DONE\n"); + // CHECK: DONE + return 0; +} diff --git a/test/tsan/suppress_same_address.cc b/test/tsan/suppress_same_address.cc new file mode 100644 index 000000000000..df19da1cc7ae --- /dev/null +++ b/test/tsan/suppress_same_address.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +volatile int X; + +void *Thread1(void *x) { + sleep(1); + X = 42; + X = 66; + X = 78; + return 0; +} + +void *Thread2(void *x) { + X = 11; + X = 99; + X = 73; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Thread2(0); + pthread_join(t, 0); +} + +// CHECK: ThreadSanitizer: reported 1 warnings diff --git a/test/tsan/suppress_same_stacks.cc b/test/tsan/suppress_same_stacks.cc new file mode 100644 index 000000000000..9305650eaa17 --- /dev/null +++ b/test/tsan/suppress_same_stacks.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> + +volatile int N; // Prevent loop unrolling. +int **data; + +void *Thread1(void *x) { + for (int i = 0; i < N; i++) + data[i][0] = 42; + return 0; +} + +int main() { + N = 4; + data = new int*[N]; + for (int i = 0; i < N; i++) + data[i] = new int; + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Thread1(0); + pthread_join(t, 0); + for (int i = 0; i < N; i++) + delete data[i]; + delete[] data; +} + +// CHECK: ThreadSanitizer: reported 1 warnings diff --git a/test/tsan/suppressions_global.cc b/test/tsan/suppressions_global.cc new file mode 100644 index 000000000000..c808a63d9e84 --- /dev/null +++ b/test/tsan/suppressions_global.cc @@ -0,0 +1,29 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int RacyGlobal; + +void *Thread1(void *x) { + RacyGlobal = 42; + return NULL; +} + +void *Thread2(void *x) { + RacyGlobal = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/suppressions_global.cc.supp b/test/tsan/suppressions_global.cc.supp new file mode 100644 index 000000000000..5fa8a2e43a93 --- /dev/null +++ b/test/tsan/suppressions_global.cc.supp @@ -0,0 +1,2 @@ +race:RacyGlobal + diff --git a/test/tsan/suppressions_race.cc b/test/tsan/suppressions_race.cc new file mode 100644 index 000000000000..1d72874d9586 --- /dev/null +++ b/test/tsan/suppressions_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/suppressions_race.cc.supp b/test/tsan/suppressions_race.cc.supp new file mode 100644 index 000000000000..cbdba76ea93a --- /dev/null +++ b/test/tsan/suppressions_race.cc.supp @@ -0,0 +1,2 @@ +race:Thread1 + diff --git a/test/tsan/suppressions_race2.cc b/test/tsan/suppressions_race2.cc new file mode 100644 index 000000000000..4ababddf6311 --- /dev/null +++ b/test/tsan/suppressions_race2.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/suppressions_race2.cc.supp b/test/tsan/suppressions_race2.cc.supp new file mode 100644 index 000000000000..b3c4dbc59363 --- /dev/null +++ b/test/tsan/suppressions_race2.cc.supp @@ -0,0 +1,2 @@ +race:Thread2 + diff --git a/test/tsan/test_output.sh b/test/tsan/test_output.sh new file mode 100755 index 000000000000..bce0fe8b5511 --- /dev/null +++ b/test/tsan/test_output.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +ulimit -s 8192 +set -e # fail on any error + +HERE=$(dirname $0) +TSAN_DIR=$(dirname $0)/../../lib/tsan + +# Assume clang and clang++ are in path. +: ${CC:=clang} +: ${CXX:=clang++} +: ${FILECHECK:=FileCheck} + +# TODO: add testing for all of -O0...-O3 +CFLAGS="-fsanitize=thread -O2 -g -Wall" +LDFLAGS="-pthread -ldl -lrt -lm -Wl,--whole-archive $TSAN_DIR/rtl/libtsan.a -Wl,--no-whole-archive" + +test_file() { + SRC=$1 + COMPILER=$2 + echo ----- TESTING $(basename $1) + OBJ=$SRC.o + EXE=$SRC.exe + $COMPILER $SRC $CFLAGS -c -o $OBJ + $COMPILER $OBJ $LDFLAGS -o $EXE + RES=$($EXE 2>&1 || true) + printf "%s\n" "$RES" | $FILECHECK $SRC + if [ "$3" == "" ]; then + rm -f $EXE $OBJ + fi +} + +if [ "$1" == "" ]; then + for c in $HERE/*.{c,cc}; do + if [[ $c == */failing_* ]]; then + echo SKIPPING FAILING TEST $c + continue + fi + if [[ $c == */load_shared_lib.cc ]]; then + echo TEST $c is not supported + continue + fi + if [[ $c == */*blacklist*.cc ]]; then + echo TEST $c is not supported + continue + fi + if [ "`grep "TSAN_OPTIONS" $c`" ]; then + echo SKIPPING $c -- requires TSAN_OPTIONS + continue + fi + if [ "`grep "XFAIL" $c`" ]; then + echo SKIPPING $c -- has XFAIL + continue + fi + COMPILER=$CXX + case $c in + *.c) COMPILER=$CC + esac + test_file $c $COMPILER & + done + for job in `jobs -p`; do + wait $job || exit 1 + done +else + test_file $HERE/$1 $CXX "DUMP" +fi diff --git a/test/tsan/thread_detach.c b/test/tsan/thread_detach.c new file mode 100644 index 000000000000..32cf641b141a --- /dev/null +++ b/test/tsan/thread_detach.c @@ -0,0 +1,20 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + pthread_detach(t); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak +// CHECK: PASS diff --git a/test/tsan/thread_end_with_ignore.cc b/test/tsan/thread_end_with_ignore.cc new file mode 100644 index 000000000000..79bb08d64bcb --- /dev/null +++ b/test/tsan/thread_end_with_ignore.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l); + +void *Thread(void *x) { + AnnotateIgnoreReadsBegin("", 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: ThreadSanitizer: thread T1 finished with ignores enabled, created at: +// CHECK: #0 pthread_create +// CHECK: #1 main +// CHECK: Ignore was enabled at: +// CHECK: #0 AnnotateIgnoreReadsBegin +// CHECK: #1 Thread + diff --git a/test/tsan/thread_end_with_ignore2.cc b/test/tsan/thread_end_with_ignore2.cc new file mode 100644 index 000000000000..9387ea488d5a --- /dev/null +++ b/test/tsan/thread_end_with_ignore2.cc @@ -0,0 +1,12 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l); + +int main() { + AnnotateIgnoreWritesBegin("", 0); +} + +// CHECK: ThreadSanitizer: main thread finished with ignores enabled +// CHECK: Ignore was enabled at: +// CHECK: #0 AnnotateIgnoreWritesBegin +// CHECK: #1 main + diff --git a/test/tsan/thread_end_with_ignore3.cc b/test/tsan/thread_end_with_ignore3.cc new file mode 100644 index 000000000000..55688b2a543f --- /dev/null +++ b/test/tsan/thread_end_with_ignore3.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l); +extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l); + +int main() { + AnnotateIgnoreReadsBegin("", 0); + AnnotateIgnoreReadsBegin("", 0); + AnnotateIgnoreReadsEnd("", 0); + AnnotateIgnoreReadsEnd("", 0); + AnnotateIgnoreReadsBegin("", 0); + AnnotateIgnoreReadsBegin("", 0); + AnnotateIgnoreReadsEnd("", 0); +} + +// CHECK: ThreadSanitizer: main thread finished with ignores enabled +// CHECK: Ignore was enabled at: +// CHECK: #0 AnnotateIgnoreReadsBegin +// CHECK: #1 main {{.*}}thread_end_with_ignore3.cc:10 +// CHECK: Ignore was enabled at: +// CHECK: #0 AnnotateIgnoreReadsBegin +// CHECK: #1 main {{.*}}thread_end_with_ignore3.cc:11 + diff --git a/test/tsan/thread_leak.c b/test/tsan/thread_leak.c new file mode 100644 index 000000000000..9b850dd4b567 --- /dev/null +++ b/test/tsan/thread_leak.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/test/tsan/thread_leak2.c b/test/tsan/thread_leak2.c new file mode 100644 index 000000000000..fc2942b2a05d --- /dev/null +++ b/test/tsan/thread_leak2.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_detach(t); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/test/tsan/thread_leak3.c b/test/tsan/thread_leak3.c new file mode 100644 index 000000000000..f4db484219a0 --- /dev/null +++ b/test/tsan/thread_leak3.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: thread leak +// CHECK: SUMMARY: ThreadSanitizer: thread leak{{.*}}main diff --git a/test/tsan/thread_leak4.c b/test/tsan/thread_leak4.c new file mode 100644 index 000000000000..0d3b8307000a --- /dev/null +++ b/test/tsan/thread_leak4.c @@ -0,0 +1,18 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> + +void *Thread(void *x) { + sleep(10); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/test/tsan/thread_leak5.c b/test/tsan/thread_leak5.c new file mode 100644 index 000000000000..ca244a9f24e1 --- /dev/null +++ b/test/tsan/thread_leak5.c @@ -0,0 +1,20 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + volatile int N = 5; // prevent loop unrolling + for (int i = 0; i < N; i++) { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + } + sleep(1); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: thread leak +// CHECK: And 4 more similar thread leaks diff --git a/test/tsan/thread_name.cc b/test/tsan/thread_name.cc new file mode 100644 index 000000000000..a790c668c084 --- /dev/null +++ b/test/tsan/thread_name.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +#if defined(__linux__) +#define USE_PTHREAD_SETNAME_NP __GLIBC_PREREQ(2, 12) +#elif defined(__FreeBSD__) +#include <pthread_np.h> +#define USE_PTHREAD_SETNAME_NP 1 +#define pthread_setname_np pthread_set_name_np +#else +#define USE_PTHREAD_SETNAME_NP 0 +#endif + +extern "C" void AnnotateThreadName(const char *f, int l, const char *name); + +int Global; + +void *Thread1(void *x) { + sleep(1); + AnnotateThreadName(__FILE__, __LINE__, "Thread1"); + Global++; + return NULL; +} + +void *Thread2(void *x) { +#if USE_PTHREAD_SETNAME_NP + pthread_setname_np(pthread_self(), "Thread2"); +#else + AnnotateThreadName(__FILE__, __LINE__, "Thread2"); +#endif + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T1 'Thread1' +// CHECK: Thread T2 'Thread2' diff --git a/test/tsan/thread_name2.cc b/test/tsan/thread_name2.cc new file mode 100644 index 000000000000..6a3dafe9c763 --- /dev/null +++ b/test/tsan/thread_name2.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +#if defined(__FreeBSD__) +#include <pthread_np.h> +#define pthread_setname_np pthread_set_name_np +#endif + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global++; + return 0; +} + +void *Thread2(void *x) { + pthread_setname_np(pthread_self(), "foobar2"); + Global--; + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_setname_np(t[0], "foobar1"); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T1 'foobar1' +// CHECK: Thread T2 'foobar2' diff --git a/test/tsan/tiny_race.c b/test/tsan/tiny_race.c new file mode 100644 index 000000000000..c10eab15c5a9 --- /dev/null +++ b/test/tsan/tiny_race.c @@ -0,0 +1,21 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return x; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Global = 43; + pthread_join(t, 0); + return Global; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/tls_race.cc b/test/tsan/tls_race.cc new file mode 100644 index 000000000000..18589347e806 --- /dev/null +++ b/test/tsan/tls_race.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread(void *a) { + sleep(1); + *(int*)a = 43; + return 0; +} + +int main() { + static __thread int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread, &Var); + Var = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is TLS of main thread. diff --git a/test/tsan/tls_race2.cc b/test/tsan/tls_race2.cc new file mode 100644 index 000000000000..0ca629ada5cc --- /dev/null +++ b/test/tsan/tls_race2.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread2(void *a) { + sleep(1); + *(int*)a = 43; + return 0; +} + +void *Thread(void *a) { + static __thread int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread2, &Var); + Var = 42; + pthread_join(t, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is TLS of thread T1. + diff --git a/test/tsan/tsan-vs-gvn.cc b/test/tsan/tsan-vs-gvn.cc new file mode 100644 index 000000000000..950f5d30d4da --- /dev/null +++ b/test/tsan/tsan-vs-gvn.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O2 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O3 %s -o %t && %run %t 2>&1 | FileCheck %s +// +// Check that load widening is not tsan-hostile. +#include <pthread.h> +#include <stdio.h> +#include <string.h> + +struct { + int i; + char c1, c2, c3, c4; +} S; + +int G; + +void *Thread1(void *x) { + G = S.c1 + S.c3; + return NULL; +} + +void *Thread2(void *x) { + S.c2 = 1; + return NULL; +} + +int main() { + pthread_t t[2]; + memset(&S, 123, sizeof(S)); + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: PASS diff --git a/test/tsan/unaligned_norace.cc b/test/tsan/unaligned_norace.cc new file mode 100644 index 000000000000..20cb545f7426 --- /dev/null +++ b/test/tsan/unaligned_norace.cc @@ -0,0 +1,84 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +uint64_t objs[8*3*3*2][3]; + +extern "C" { +uint16_t __tsan_unaligned_read2(void *addr); +uint32_t __tsan_unaligned_read4(void *addr); +uint64_t __tsan_unaligned_read8(void *addr); +void __tsan_unaligned_write2(void *addr, uint16_t v); +void __tsan_unaligned_write4(void *addr, uint32_t v); +void __tsan_unaligned_write8(void *addr, uint64_t v); +} + +static void access(char *p, int sz, int rw) { + if (rw) { + switch (sz) { + case 0: __tsan_unaligned_write2(p, 0); break; + case 1: __tsan_unaligned_write4(p, 0); break; + case 2: __tsan_unaligned_write8(p, 0); break; + default: exit(1); + } + } else { + switch (sz) { + case 0: __tsan_unaligned_read2(p); break; + case 1: __tsan_unaligned_read4(p); break; + case 2: __tsan_unaligned_read8(p); break; + default: exit(1); + } + } +} + +static int accesssize(int sz) { + switch (sz) { + case 0: return 2; + case 1: return 4; + case 2: return 8; + } + exit(1); +} + +void Test(bool main) { + uint64_t *obj = objs[0]; + for (int off = 0; off < 8; off++) { + for (int sz1 = 0; sz1 < 3; sz1++) { + for (int sz2 = 0; sz2 < 3; sz2++) { + for (int rw = 0; rw < 2; rw++) { + char *p = (char*)obj + off; + if (main) { + // printf("thr=%d off=%d sz1=%d sz2=%d rw=%d p=%p\n", + // main, off, sz1, sz2, rw, p); + access(p, sz1, true); + } else { + p += accesssize(sz1); + // printf("thr=%d off=%d sz1=%d sz2=%d rw=%d p=%p\n", + // main, off, sz1, sz2, rw, p); + access(p, sz2, rw); + } + obj += 3; + } + } + } + } +} + +void *Thread(void *p) { + (void)p; + Test(false); + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, Thread, 0); + Test(true); + pthread_join(th, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: OK diff --git a/test/tsan/unaligned_race.cc b/test/tsan/unaligned_race.cc new file mode 100644 index 000000000000..6e9b5a33f0da --- /dev/null +++ b/test/tsan/unaligned_race.cc @@ -0,0 +1,139 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> + +#define NOINLINE __attribute__((noinline)) + +volatile uint64_t objs[8*2*(2 + 4 + 8)][2]; + +extern "C" { +uint16_t __sanitizer_unaligned_load16(volatile void *addr); +uint32_t __sanitizer_unaligned_load32(volatile void *addr); +uint64_t __sanitizer_unaligned_load64(volatile void *addr); +void __sanitizer_unaligned_store16(volatile void *addr, uint16_t v); +void __sanitizer_unaligned_store32(volatile void *addr, uint32_t v); +void __sanitizer_unaligned_store64(volatile void *addr, uint64_t v); +} + +// All this mess is to generate unique stack for each race, +// otherwise tsan will suppress similar stacks. + +static NOINLINE void access(volatile char *p, int sz, int rw) { + if (rw) { + switch (sz) { + case 0: __sanitizer_unaligned_store16(p, 0); break; + case 1: __sanitizer_unaligned_store32(p, 0); break; + case 2: __sanitizer_unaligned_store64(p, 0); break; + default: exit(1); + } + } else { + switch (sz) { + case 0: __sanitizer_unaligned_load16(p); break; + case 1: __sanitizer_unaligned_load32(p); break; + case 2: __sanitizer_unaligned_load64(p); break; + default: exit(1); + } + } +} + +static int accesssize(int sz) { + switch (sz) { + case 0: return 2; + case 1: return 4; + case 2: return 8; + } + exit(1); +} + +template<int off, int off2> +static NOINLINE void access3(bool main, int sz1, bool rw, volatile char *p) { + p += off; + if (main) { + access(p, sz1, true); + } else { + p += off2; + if (rw) { + *p = 42; + } else { + if (*p == 42) + printf("bingo!\n"); + } + } +} + +template<int off> +static NOINLINE void +access2(bool main, int sz1, int off2, bool rw, volatile char *obj) { + if (off2 == 0) + access3<off, 0>(main, sz1, rw, obj); + else if (off2 == 1) + access3<off, 1>(main, sz1, rw, obj); + else if (off2 == 2) + access3<off, 2>(main, sz1, rw, obj); + else if (off2 == 3) + access3<off, 3>(main, sz1, rw, obj); + else if (off2 == 4) + access3<off, 4>(main, sz1, rw, obj); + else if (off2 == 5) + access3<off, 5>(main, sz1, rw, obj); + else if (off2 == 6) + access3<off, 6>(main, sz1, rw, obj); + else if (off2 == 7) + access3<off, 7>(main, sz1, rw, obj); +} + +static NOINLINE void +access1(bool main, int off, int sz1, int off2, bool rw, char *obj) { + if (off == 0) + access2<0>(main, sz1, off2, rw, obj); + else if (off == 1) + access2<1>(main, sz1, off2, rw, obj); + else if (off == 2) + access2<2>(main, sz1, off2, rw, obj); + else if (off == 3) + access2<3>(main, sz1, off2, rw, obj); + else if (off == 4) + access2<4>(main, sz1, off2, rw, obj); + else if (off == 5) + access2<5>(main, sz1, off2, rw, obj); + else if (off == 6) + access2<6>(main, sz1, off2, rw, obj); + else if (off == 7) + access2<7>(main, sz1, off2, rw, obj); +} + +NOINLINE void Test(bool main) { + volatile uint64_t *obj = objs[0]; + for (int off = 0; off < 8; off++) { + for (int sz1 = 0; sz1 < 3; sz1++) { + for (int off2 = 0; off2 < accesssize(sz1); off2++) { + for (int rw = 0; rw < 2; rw++) { + // printf("thr=%d off=%d sz1=%d off2=%d rw=%d p=%p\n", + // main, off, sz1, off2, rw, obj); + access1(main, off, sz1, off2, rw, (char*)obj); + obj += 2; + } + } + } + } +} + +void *Thread(void *p) { + (void)p; + sleep(1); + Test(false); + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, Thread, 0); + Test(true); + pthread_join(th, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: ThreadSanitizer: reported 224 warnings diff --git a/test/tsan/vfork.cc b/test/tsan/vfork.cc new file mode 100644 index 000000000000..5ae1dd1ababd --- /dev/null +++ b/test/tsan/vfork.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> + +int fds[2]; +int X; + +void *Thread1(void *x) { + X = 42; + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + char buf; + while (read(fds[0], &buf, 1) != 1) { + } + X = 43; + return NULL; +} + +int main() { + pipe(fds); + int pid = vfork(); + if (pid < 0) { + printf("FAIL to vfork\n"); + exit(1); + } + if (pid == 0) { // child + // Closing of fds must not affect parent process. + // Strictly saying this is undefined behavior, because vfork child is not + // allowed to call any functions other than exec/exit. But this is what + // openjdk does. + close(fds[0]); + close(fds[1]); + _exit(0); + } + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("DONE\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAIL to vfork +// CHECK: DONE diff --git a/test/tsan/virtual_inheritance_compile_bug.cc b/test/tsan/virtual_inheritance_compile_bug.cc new file mode 100644 index 000000000000..2a50c2e88d01 --- /dev/null +++ b/test/tsan/virtual_inheritance_compile_bug.cc @@ -0,0 +1,15 @@ +// Regression test for http://code.google.com/p/thread-sanitizer/issues/detail?id=3. +// The C++ variant is much more compact that the LLVM IR equivalent. + +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <stdio.h> +struct AAA { virtual long aaa () { return 0; } }; // NOLINT +struct BBB: virtual AAA { unsigned long bbb; }; // NOLINT +struct CCC: virtual AAA { }; +struct DDD: CCC, BBB { DDD(); }; // NOLINT +DDD::DDD() { } +int main() { + DDD d; + printf("OK\n"); +} +// CHECK: OK diff --git a/test/tsan/vptr_benign_race.cc b/test/tsan/vptr_benign_race.cc new file mode 100644 index 000000000000..92a2b326e717 --- /dev/null +++ b/test/tsan/vptr_benign_race.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { + sem_wait(&sem_); + sem_destroy(&sem_); + } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "PASS\n"); +} +// CHECK: PASS +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/vptr_harmful_race.cc b/test/tsan/vptr_harmful_race.cc new file mode 100644 index 000000000000..68e12e8e7e89 --- /dev/null +++ b/test/tsan/vptr_harmful_race.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> +#include <unistd.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + sem_wait(&sem_); + sem_destroy(&sem_); + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race on vptr diff --git a/test/tsan/vptr_harmful_race2.cc b/test/tsan/vptr_harmful_race2.cc new file mode 100644 index 000000000000..aa53bbb90fcf --- /dev/null +++ b/test/tsan/vptr_harmful_race2.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> +#include <unistd.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + sem_wait(&sem_); + sem_destroy(&sem_); + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + sleep(1); + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race on vptr diff --git a/test/tsan/vptr_harmful_race3.cc b/test/tsan/vptr_harmful_race3.cc new file mode 100644 index 000000000000..ac6ea94e51eb --- /dev/null +++ b/test/tsan/vptr_harmful_race3.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> +#include <unistd.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + sem_wait(&sem_); + sem_destroy(&sem_); + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { } +}; + +static A *obj = new B; +static void (A::*fn)() = &A::F; + +void *Thread1(void *x) { + sleep(1); + (obj->*fn)(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race on vptr + diff --git a/test/tsan/vptr_harmful_race4.cc b/test/tsan/vptr_harmful_race4.cc new file mode 100644 index 000000000000..969c9d58a016 --- /dev/null +++ b/test/tsan/vptr_harmful_race4.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +struct A { + virtual void F() { + } + + virtual ~A() { + } +}; + +struct B : A { + virtual void F() { + } +}; + +void *Thread(void *x) { + sleep(1); + ((A*)x)->F(); + return 0; +} + +int main() { + A *obj = new B; + pthread_t t; + pthread_create(&t, 0, Thread, obj); + delete obj; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: heap-use-after-free (virtual call vs free) + diff --git a/test/tsan/write_in_reader_lock.cc b/test/tsan/write_in_reader_lock.cc new file mode 100644 index 000000000000..55882139b153 --- /dev/null +++ b/test/tsan/write_in_reader_lock.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +pthread_rwlock_t rwlock; +int GLOB; + +void *Thread1(void *p) { + (void)p; + pthread_rwlock_rdlock(&rwlock); + // Write under reader lock. + sleep(1); + GLOB++; + pthread_rwlock_unlock(&rwlock); + return 0; +} + +int main(int argc, char *argv[]) { + pthread_rwlock_init(&rwlock, NULL); + pthread_rwlock_rdlock(&rwlock); + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + volatile int x = GLOB; + (void)x; + pthread_rwlock_unlock(&rwlock); + pthread_join(t, 0); + pthread_rwlock_destroy(&rwlock); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T1{{.*}}: +// CHECK: #0 Thread1(void*) {{.*}}write_in_reader_lock.cc:13 +// CHECK: Previous read of size 4 at {{.*}} by main thread{{.*}}: +// CHECK: #0 main {{.*}}write_in_reader_lock.cc:23 diff --git a/test/ubsan/CMakeLists.txt b/test/ubsan/CMakeLists.txt new file mode 100644 index 000000000000..1c0c92903298 --- /dev/null +++ b/test/ubsan/CMakeLists.txt @@ -0,0 +1,25 @@ +set(UBSAN_LIT_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(UBSAN_LIT_TEST_MODE "Standalone") +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig/lit.site.cfg) +set(UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig) + +if(COMPILER_RT_HAS_ASAN) + set(UBSAN_LIT_TEST_MODE "AddressSanitizer") + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg) + list(APPEND UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig) +endif() + +set(UBSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND UBSAN_TEST_DEPS ubsan asan) +endif() + +add_lit_testsuite(check-ubsan "Running UndefinedBehaviorSanitizer tests" + ${UBSAN_TESTSUITES} + DEPENDS ${UBSAN_TEST_DEPS}) +set_target_properties(check-ubsan PROPERTIES FOLDER "UBSan unittests") diff --git a/test/ubsan/TestCases/Float/cast-overflow.cpp b/test/ubsan/TestCases/Float/cast-overflow.cpp new file mode 100644 index 000000000000..22991e0a6c55 --- /dev/null +++ b/test/ubsan/TestCases/Float/cast-overflow.cpp @@ -0,0 +1,134 @@ +// FIXME: run this (and other) UBSan tests in both 32- and 64-bit modes (?). +// RUN: %clangxx -fsanitize=float-cast-overflow %s -o %t +// RUN: %run %t _ +// RUN: %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 +// RUN: %run %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1 +// RUN: %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2 +// RUN: %run %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3 +// RUN: %run %t 4 2>&1 | FileCheck %s --check-prefix=CHECK-4 +// RUN: %run %t 5 2>&1 | FileCheck %s --check-prefix=CHECK-5 +// RUN: %run %t 6 2>&1 | FileCheck %s --check-prefix=CHECK-6 +// FIXME: %run %t 7 2>&1 | FileCheck %s --check-prefix=CHECK-7 +// FIXME: not %run %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8 +// RUN: not %run %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9 + +// This test assumes float and double are IEEE-754 single- and double-precision. +// XFAIL: armv7l-unknown-linux-gnueabihf + +#if defined(__APPLE__) +# include <machine/endian.h> +# define BYTE_ORDER __DARWIN_BYTE_ORDER +# define BIG_ENDIAN __DARWIN_BIG_ENDIAN +# define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#elif defined(__FreeBSD__) +# include <sys/endian.h> +# define BYTE_ORDER _BYTE_ORDER +# define BIG_ENDIAN _BIG_ENDIAN +# define LITTLE_ENDIAN _LITTLE_ENDIAN +#else +# include <endian.h> +# define BYTE_ORDER __BYTE_ORDER +# define BIG_ENDIAN __BIG_ENDIAN +# define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif // __APPLE__ +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +float Inf; +float NaN; + +int main(int argc, char **argv) { + float MaxFloatRepresentableAsInt = 0x7fffff80; + (int)MaxFloatRepresentableAsInt; // ok + (int)-MaxFloatRepresentableAsInt; // ok + + float MinFloatRepresentableAsInt = -0x7fffffff - 1; + (int)MinFloatRepresentableAsInt; // ok + + float MaxFloatRepresentableAsUInt = 0xffffff00u; + (unsigned int)MaxFloatRepresentableAsUInt; // ok + +#ifdef __SIZEOF_INT128__ + unsigned __int128 FloatMaxAsUInt128 = -((unsigned __int128)1 << 104); + (void)(float)FloatMaxAsUInt128; // ok +#endif + + float NearlyMinusOne = -0.99999; + unsigned Zero = NearlyMinusOne; // ok + + // Build a '+Inf'. +#if BYTE_ORDER == LITTLE_ENDIAN + char InfVal[] = { 0x00, 0x00, 0x80, 0x7f }; +#else + char InfVal[] = { 0x7f, 0x80, 0x00, 0x00 }; +#endif + float Inf; + memcpy(&Inf, InfVal, 4); + + // Build a 'NaN'. +#if BYTE_ORDER == LITTLE_ENDIAN + char NaNVal[] = { 0x01, 0x00, 0x80, 0x7f }; +#else + char NaNVal[] = { 0x7f, 0x80, 0x00, 0x01 }; +#endif + float NaN; + memcpy(&NaN, NaNVal, 4); + + double DblInf = (double)Inf; // ok + + switch (argv[1][0]) { + // FIXME: Produce a source location for these checks and test for it here. + + // Floating point -> integer overflow. + case '0': + // Note that values between 0x7ffffe00 and 0x80000000 may or may not + // successfully round-trip, depending on the rounding mode. + // CHECK-0: runtime error: value 2.14748{{.*}} is outside the range of representable values of type 'int' + return MaxFloatRepresentableAsInt + 0x80; + case '1': + // CHECK-1: runtime error: value -2.14748{{.*}} is outside the range of representable values of type 'int' + return MinFloatRepresentableAsInt - 0x100; + case '2': { + // CHECK-2: runtime error: value -1 is outside the range of representable values of type 'unsigned int' + volatile float f = -1.0; + volatile unsigned u = (unsigned)f; + return 0; + } + case '3': + // CHECK-3: runtime error: value 4.2949{{.*}} is outside the range of representable values of type 'unsigned int' + return (unsigned)(MaxFloatRepresentableAsUInt + 0x100); + + case '4': + // CHECK-4: runtime error: value {{.*}} is outside the range of representable values of type 'int' + return Inf; + case '5': + // CHECK-5: runtime error: value {{.*}} is outside the range of representable values of type 'int' + return NaN; + + // Integer -> floating point overflow. + case '6': + // CHECK-6: {{runtime error: value 0xffffff00000000000000000000000001 is outside the range of representable values of type 'float'|__int128 not supported}} +#ifdef __SIZEOF_INT128__ + return (float)(FloatMaxAsUInt128 + 1); +#else + puts("__int128 not supported"); + return 0; +#endif + // FIXME: The backend cannot lower __fp16 operations on x86 yet. + //case '7': + // (__fp16)65504; // ok + // // CHECK-7: runtime error: value 65505 is outside the range of representable values of type '__fp16' + // return (__fp16)65505; + + // Floating point -> floating point overflow. + case '8': + // CHECK-8: runtime error: value 1e+39 is outside the range of representable values of type 'float' + return (float)1e39; + case '9': + volatile long double ld = 300.0; + // CHECK-9: runtime error: value 300 is outside the range of representable values of type 'char' + char c = ld; + return c; + } +} diff --git a/test/ubsan/TestCases/Integer/add-overflow.cpp b/test/ubsan/TestCases/Integer/add-overflow.cpp new file mode 100644 index 000000000000..d3425828ec88 --- /dev/null +++ b/test/ubsan/TestCases/Integer/add-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 + +#include <stdint.h> +#include <stdio.h> + +int main() { + // These promote to 'int'. + (void)(int8_t(0x7f) + int8_t(0x7f)); + (void)(int16_t(0x3fff) + int16_t(0x4000)); + +#ifdef ADD_I32 + int32_t k = 0x12345678; + k += 0x789abcde; + // CHECK-ADD_I32: add-overflow.cpp:[[@LINE-1]]:5: runtime error: signed integer overflow: 305419896 + 2023406814 cannot be represented in type 'int' +#endif + +#ifdef ADD_I64 + (void)(int64_t(8000000000000000000ll) + int64_t(2000000000000000000ll)); + // CHECK-ADD_I64: 8000000000000000000 + 2000000000000000000 cannot be represented in type '{{long( long)?}}' +#endif + +#ifdef ADD_I128 +# ifdef __SIZEOF_INT128__ + (void)((__int128_t(1) << 126) + (__int128_t(1) << 126)); +# else + puts("__int128 not supported"); +# endif + // CHECK-ADD_I128: {{0x40000000000000000000000000000000 \+ 0x40000000000000000000000000000000 cannot be represented in type '__int128'|__int128 not supported}} +#endif +} diff --git a/test/ubsan/TestCases/Integer/div-overflow.cpp b/test/ubsan/TestCases/Integer/div-overflow.cpp new file mode 100644 index 000000000000..76dd60de45ce --- /dev/null +++ b/test/ubsan/TestCases/Integer/div-overflow.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + unsigned(0x80000000) / -1; + + // CHECK: div-overflow.cpp:9:23: runtime error: division of -2147483648 by -1 cannot be represented in type 'int' + int32_t(0x80000000) / -1; +} diff --git a/test/ubsan/TestCases/Integer/div-zero.cpp b/test/ubsan/TestCases/Integer/div-zero.cpp new file mode 100644 index 000000000000..9a223312e8e7 --- /dev/null +++ b/test/ubsan/TestCases/Integer/div-zero.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=1U %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %run %t 2>&1 | FileCheck %s + +#ifdef __SIZEOF_INT128__ +typedef __int128 intmax; +#else +typedef long long intmax; +#endif + +int main() { + // CHECK: div-zero.cpp:[[@LINE+1]]:12: runtime error: division by zero + DIVIDEND / 0; +} diff --git a/test/ubsan/TestCases/Integer/incdec-overflow.cpp b/test/ubsan/TestCases/Integer/incdec-overflow.cpp new file mode 100644 index 000000000000..fc7141c803d8 --- /dev/null +++ b/test/ubsan/TestCases/Integer/incdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=++n -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=--m -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + int n = 0x7ffffffd; + n++; + n++; + int m = -n - 1; + // CHECK: incdec-overflow.cpp:15:3: runtime error: signed integer overflow: [[MINUS:-?]]214748364 + // CHECK: + [[MINUS]]1 cannot be represented in type 'int' + OP; +} diff --git a/test/ubsan/TestCases/Integer/mul-overflow.cpp b/test/ubsan/TestCases/Integer/mul-overflow.cpp new file mode 100644 index 000000000000..20fece5f9fe2 --- /dev/null +++ b/test/ubsan/TestCases/Integer/mul-overflow.cpp @@ -0,0 +1,14 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) * int8_t(0x7f)); + (void)(int16_t(0x7fff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * uint16_t(0x8000)); + + // CHECK: mul-overflow.cpp:13:27: runtime error: signed integer overflow: 65535 * 32769 cannot be represented in type 'int' + (void)(uint16_t(0xffff) * uint16_t(0x8001)); +} diff --git a/test/ubsan/TestCases/Integer/negate-overflow.cpp b/test/ubsan/TestCases/Integer/negate-overflow.cpp new file mode 100644 index 000000000000..bde0bdabb292 --- /dev/null +++ b/test/ubsan/TestCases/Integer/negate-overflow.cpp @@ -0,0 +1,12 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECKS +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECKU + +int main() { + // CHECKS-NOT: runtime error + // CHECKU: negate-overflow.cpp:[[@LINE+2]]:3: runtime error: negation of 2147483648 cannot be represented in type 'unsigned int' + // CHECKU-NOT: cast to an unsigned + -unsigned(-0x7fffffff - 1); // ok + // CHECKS: negate-overflow.cpp:[[@LINE+2]]:10: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself + // CHECKU-NOT: runtime error + return -(-0x7fffffff - 1); +} diff --git a/test/ubsan/TestCases/Integer/no-recover.cpp b/test/ubsan/TestCases/Integer/no-recover.cpp new file mode 100644 index 000000000000..575bd0a553fb --- /dev/null +++ b/test/ubsan/TestCases/Integer/no-recover.cpp @@ -0,0 +1,22 @@ +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fsanitize-recover %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=ABORT + +#include <stdint.h> + +int main() { + // These promote to 'int'. + (void)(uint8_t(0xff) + uint8_t(0xff)); + (void)(uint16_t(0xf0fff) + uint16_t(0x0fff)); + // RECOVER-NOT: runtime error + // ABORT-NOT: runtime error + + uint32_t k = 0x87654321; + k += 0xedcba987; + // RECOVER: no-recover.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' + // ABORT: no-recover.cpp:[[@LINE-2]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' + + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // RECOVER: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' + // ABORT-NOT: runtime error +} diff --git a/test/ubsan/TestCases/Integer/shift.cpp b/test/ubsan/TestCases/Integer/shift.cpp new file mode 100644 index 000000000000..e86fac8d574a --- /dev/null +++ b/test/ubsan/TestCases/Integer/shift.cpp @@ -0,0 +1,37 @@ +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH + +#include <stdint.h> + +int main() { + int a = 1; + unsigned b = 1; + + a <<= 31; // ok in C++11, not ok in C99/C11 + b <<= 31; // ok + b <<= 1; // still ok, unsigned + +#ifdef LSH_OVERFLOW + // CHECK-LSH_OVERFLOW: shift.cpp:24:5: runtime error: left shift of negative value -2147483648 + a OP 1; +#endif + +#ifdef TOO_LOW + // CHECK-TOO_LOW: shift.cpp:29:5: runtime error: shift exponent -3 is negative + a OP (-3); +#endif + +#ifdef TOO_HIGH + a = 0; + // CHECK-TOO_HIGH: shift.cpp:35:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' + a OP 32; +#endif +} diff --git a/test/ubsan/TestCases/Integer/sub-overflow.cpp b/test/ubsan/TestCases/Integer/sub-overflow.cpp new file mode 100644 index 000000000000..15e64d951603 --- /dev/null +++ b/test/ubsan/TestCases/Integer/sub-overflow.cpp @@ -0,0 +1,31 @@ +// RUN: %clangxx -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 + +#include <stdint.h> +#include <stdio.h> + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) - int8_t(0x7f)); + (void)(int16_t(-2) - int16_t(0x7fff)); + +#ifdef SUB_I32 + (void)(int32_t(-2) - int32_t(0x7fffffff)); + // CHECK-SUB_I32: sub-overflow.cpp:[[@LINE-1]]:22: runtime error: signed integer overflow: -2 - 2147483647 cannot be represented in type 'int' +#endif + +#ifdef SUB_I64 + (void)(int64_t(-8000000000000000000ll) - int64_t(2000000000000000000ll)); + // CHECK-SUB_I64: -8000000000000000000 - 2000000000000000000 cannot be represented in type '{{long( long)?}}' +#endif + +#ifdef SUB_I128 +# ifdef __SIZEOF_INT128__ + (void)(-(__int128_t(1) << 126) - (__int128_t(1) << 126) - 1); +# else + puts("__int128 not supported"); +# endif + // CHECK-SUB_I128: {{0x80000000000000000000000000000000 - 1 cannot be represented in type '__int128'|__int128 not supported}} +#endif +} diff --git a/test/ubsan/TestCases/Integer/summary.cpp b/test/ubsan/TestCases/Integer/summary.cpp new file mode 100644 index 000000000000..6e9aec63ca74 --- /dev/null +++ b/test/ubsan/TestCases/Integer/summary.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=integer %s -o %t && %t 2>&1 | FileCheck %s +// REQUIRES: ubsan-asan + +#include <stdint.h> + +int main() { + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // CHECK: SUMMARY: AddressSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]] + return 0; +} diff --git a/test/ubsan/TestCases/Integer/uadd-overflow.cpp b/test/ubsan/TestCases/Integer/uadd-overflow.cpp new file mode 100644 index 000000000000..7a96880fe636 --- /dev/null +++ b/test/ubsan/TestCases/Integer/uadd-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 + +#include <stdint.h> +#include <stdio.h> + +int main() { + // These promote to 'int'. + (void)(uint8_t(0xff) + uint8_t(0xff)); + (void)(uint16_t(0xf0fff) + uint16_t(0x0fff)); + +#ifdef ADD_I32 + uint32_t k = 0x87654321; + k += 0xedcba987; + // CHECK-ADD_I32: uadd-overflow.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' +#endif + +#ifdef ADD_I64 + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // CHECK-ADD_I64: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' +#endif + +#ifdef ADD_I128 +# ifdef __SIZEOF_INT128__ + (void)((__uint128_t(1) << 127) + (__uint128_t(1) << 127)); +# else + puts("__int128 not supported"); +# endif + // CHECK-ADD_I128: {{0x80000000000000000000000000000000 \+ 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}} +#endif +} diff --git a/test/ubsan/TestCases/Integer/uincdec-overflow.cpp b/test/ubsan/TestCases/Integer/uincdec-overflow.cpp new file mode 100644 index 000000000000..a236d21fcf1f --- /dev/null +++ b/test/ubsan/TestCases/Integer/uincdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s +// RUN: %clangxx -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s + +#include <stdint.h> + +int main() { + unsigned n = 0xfffffffd; + n++; + n++; + unsigned m = 0; + // CHECK-INC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 4294967295 + 1 cannot be represented in type 'unsigned int' + // CHECK-DEC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int' + OP; +} diff --git a/test/ubsan/TestCases/Integer/umul-overflow.cpp b/test/ubsan/TestCases/Integer/umul-overflow.cpp new file mode 100644 index 000000000000..ad5d1bd0d13c --- /dev/null +++ b/test/ubsan/TestCases/Integer/umul-overflow.cpp @@ -0,0 +1,19 @@ +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) * int8_t(0x7f)); + (void)(int16_t(0x7fff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * uint16_t(0x8000)); + + // Not an unsigned overflow + (void)(uint16_t(0xffff) * uint16_t(0x8001)); + + (void)(uint32_t(0xffffffff) * uint32_t(0x2)); + // CHECK: umul-overflow.cpp:15:31: runtime error: unsigned integer overflow: 4294967295 * 2 cannot be represented in type 'unsigned int' + + return 0; +} diff --git a/test/ubsan/TestCases/Integer/usub-overflow.cpp b/test/ubsan/TestCases/Integer/usub-overflow.cpp new file mode 100644 index 000000000000..e5de7de54eaa --- /dev/null +++ b/test/ubsan/TestCases/Integer/usub-overflow.cpp @@ -0,0 +1,31 @@ +// RUN: %clangxx -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 + +#include <stdint.h> +#include <stdio.h> + +int main() { + // These promote to 'int'. + (void)(uint8_t(0) - uint8_t(0x7f)); + (void)(uint16_t(0) - uint16_t(0x7fff)); + +#ifdef SUB_I32 + (void)(uint32_t(1) - uint32_t(2)); + // CHECK-SUB_I32: usub-overflow.cpp:[[@LINE-1]]:22: runtime error: unsigned integer overflow: 1 - 2 cannot be represented in type 'unsigned int' +#endif + +#ifdef SUB_I64 + (void)(uint64_t(8000000000000000000ll) - uint64_t(9000000000000000000ll)); + // CHECK-SUB_I64: 8000000000000000000 - 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' +#endif + +#ifdef SUB_I128 +# ifdef __SIZEOF_INT128__ + (void)((__uint128_t(1) << 126) - (__uint128_t(1) << 127)); +# else + puts("__int128 not supported\n"); +# endif + // CHECK-SUB_I128: {{0x40000000000000000000000000000000 - 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}} +#endif +} diff --git a/test/ubsan/TestCases/Misc/bool.cpp b/test/ubsan/TestCases/Misc/bool.cpp new file mode 100644 index 000000000000..37ecea27c941 --- /dev/null +++ b/test/ubsan/TestCases/Misc/bool.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=bool %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s + +unsigned char NotABool = 123; + +int main(int argc, char **argv) { + bool *p = (bool*)&NotABool; + + // CHECK: bool.cpp:9:10: runtime error: load of value 123, which is not a valid value for type 'bool' + return *p; +} diff --git a/test/ubsan/TestCases/Misc/bounds.cpp b/test/ubsan/TestCases/Misc/bounds.cpp new file mode 100644 index 000000000000..ffcac528be90 --- /dev/null +++ b/test/ubsan/TestCases/Misc/bounds.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=bounds %s -O3 -o %t +// RUN: %run %t 0 0 0 +// RUN: %run %t 1 2 3 +// RUN: not --crash %run %t 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2 +// RUN: %run %t 0 3 0 2>&1 | FileCheck %s --check-prefix=CHECK-B-3 +// RUN: %run %t 0 0 4 2>&1 | FileCheck %s --check-prefix=CHECK-C-4 + +int main(int argc, char **argv) { + int arr[2][3][4] = {}; + + return arr[argv[1][0] - '0'][argv[2][0] - '0'][argv[3][0] - '0']; + // CHECK-A-2: bounds.cpp:[[@LINE-1]]:10: runtime error: index 2 out of bounds for type 'int [2][3][4]' + // CHECK-B-3: bounds.cpp:[[@LINE-2]]:10: runtime error: index 3 out of bounds for type 'int [3][4]' + // CHECK-C-4: bounds.cpp:[[@LINE-3]]:10: runtime error: index 4 out of bounds for type 'int [4]' +} diff --git a/test/ubsan/TestCases/Misc/deduplication.cpp b/test/ubsan/TestCases/Misc/deduplication.cpp new file mode 100644 index 000000000000..7d7b0bd58c6e --- /dev/null +++ b/test/ubsan/TestCases/Misc/deduplication.cpp @@ -0,0 +1,25 @@ +// RUN: %clangxx -fsanitize=undefined %s -o %t && %run %t 2>&1 | FileCheck %s +// Verify deduplication works by ensuring only one diag is emitted. +#include <limits.h> +#include <stdio.h> + +void overflow() { + int i = INT_MIN; + --i; +} + +int main() { + // CHECK: Start + fprintf(stderr, "Start\n"); + + // CHECK: runtime error + // CHECK-NOT: runtime error + // CHECK-NOT: runtime error + overflow(); + overflow(); + overflow(); + + // CHECK: End + fprintf(stderr, "End\n"); + return 0; +} diff --git a/test/ubsan/TestCases/Misc/enum.cpp b/test/ubsan/TestCases/Misc/enum.cpp new file mode 100644 index 000000000000..49ac7c6bb187 --- /dev/null +++ b/test/ubsan/TestCases/Misc/enum.cpp @@ -0,0 +1,17 @@ +// RUN: %clangxx -fsanitize=enum %s -O3 -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %run %t +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL + +enum E { a = 1 } e; +#undef E + +int main(int argc, char **argv) { + // memset(&e, 0xff, sizeof(e)); + for (unsigned char *p = (unsigned char*)&e; p != (unsigned char*)(&e + 1); ++p) + *p = 0xff; + + // CHECK-PLAIN: error: load of value 4294967295, which is not a valid value for type 'enum E' + // FIXME: Support marshalling and display of enum class values. + // CHECK-BOOL: error: load of value <unknown>, which is not a valid value for type 'enum E' + return (int)e != -1; +} diff --git a/test/ubsan/TestCases/Misc/missing_return.cpp b/test/ubsan/TestCases/Misc/missing_return.cpp new file mode 100644 index 000000000000..5d3d54de17dd --- /dev/null +++ b/test/ubsan/TestCases/Misc/missing_return.cpp @@ -0,0 +1,17 @@ +// RUN: %clangxx -fsanitize=return -g %s -O3 -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: UBSAN_OPTIONS=print_stacktrace=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os-STACKTRACE + +// CHECK: missing_return.cpp:[[@LINE+1]]:5: runtime error: execution reached the end of a value-returning function without returning a value +int f() { +// Slow stack unwinding is disabled on Darwin for now, see +// https://code.google.com/p/address-sanitizer/issues/detail?id=137 +// CHECK-Linux-STACKTRACE: #0 {{.*}} in f(){{.*}}missing_return.cpp:[[@LINE-3]] +// CHECK-FreeBSD-STACKTRACE: #0 {{.*}} in f(void){{.*}}missing_return.cpp:[[@LINE-4]] +// Check for already checked line to avoid lit error reports. +// CHECK-Darwin-STACKTRACE: missing_return.cpp +} + +int main(int, char **argv) { + return f(); +} diff --git a/test/ubsan/TestCases/Misc/nonnull-arg.cpp b/test/ubsan/TestCases/Misc/nonnull-arg.cpp new file mode 100644 index 000000000000..b1061b757899 --- /dev/null +++ b/test/ubsan/TestCases/Misc/nonnull-arg.cpp @@ -0,0 +1,58 @@ +// RUN: %clangxx -fsanitize=nonnull-attribute -fno-sanitize-recover %s -O3 -o %t +// RUN: %run %t nc +// RUN: %run %t nm +// RUN: %run %t nf +// RUN: %run %t nv +// RUN: not %run %t 0c 2>&1 | FileCheck %s --check-prefix=CTOR +// RUN: not %run %t 0m 2>&1 | FileCheck %s --check-prefix=METHOD +// RUN: not %run %t 0f 2>&1 | FileCheck %s --check-prefix=FUNC +// RUN: not %run %t 0v 2>&1 | FileCheck %s --check-prefix=VARIADIC + +class C { + int *null_; + int *nonnull_; + +public: + C(int *null, __attribute__((nonnull)) int *nonnull) + : null_(null), nonnull_(nonnull) {} + int value() { return *nonnull_; } + int method(int *nonnull, int *null) __attribute__((nonnull(2))) { + return *nonnull_ + *nonnull; + } +}; + +__attribute__((nonnull)) int func(int *nonnull) { return *nonnull; } + +#include <stdarg.h> +__attribute__((nonnull)) int variadic(int x, ...) { + va_list args; + va_start(args, x); + int *nonnull = va_arg(args, int*); + int res = *nonnull; + va_end(args); + return res; +} + +int main(int argc, char *argv[]) { + int local = 0; + int *arg = (argv[1][0] == '0') ? 0x0 : &local; + switch (argv[1][1]) { + case 'c': + return C(0x0, arg).value(); + // CTOR: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:21: runtime error: null pointer passed as argument 2, which is declared to never be null + // CTOR-NEXT: {{.*}}nonnull-arg.cpp:16:31: note: nonnull attribute specified here + case 'm': + return C(0x0, &local).method(arg, 0x0); + // METHOD: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:36: runtime error: null pointer passed as argument 1, which is declared to never be null + // METHOD-NEXT: {{.*}}nonnull-arg.cpp:19:54: note: nonnull attribute specified here + case 'f': + return func(arg); + // FUNC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:19: runtime error: null pointer passed as argument 1, which is declared to never be null + // FUNC-NEXT: {{.*}}nonnull-arg.cpp:24:16: note: nonnull attribute specified here + case 'v': + return variadic(42, arg); + // VARIADIC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:27: runtime error: null pointer passed as argument 2, which is declared to never be null + // VARIADIC-NEXT: {{.*}}nonnull-arg.cpp:27:16: note: nonnull attribute specified here + } + return 0; +} diff --git a/test/ubsan/TestCases/Misc/nonnull.cpp b/test/ubsan/TestCases/Misc/nonnull.cpp new file mode 100644 index 000000000000..c3ab49c11df7 --- /dev/null +++ b/test/ubsan/TestCases/Misc/nonnull.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=returns-nonnull-attribute %s -O3 -o %t +// RUN: %run %t foo +// RUN: %run %t 2>&1 | FileCheck %s + +__attribute__((returns_nonnull)) char *foo(char *a); + +char *foo(char *a) { + return a; + // CHECK: nonnull.cpp:[[@LINE+2]]:1: runtime error: null pointer returned from function declared to never return null + // CHECK-NEXT: nonnull.cpp:[[@LINE-5]]:16: note: returns_nonnull attribute specified here +} + +int main(int argc, char **argv) { + return foo(argv[1]) == 0; +} diff --git a/test/ubsan/TestCases/Misc/unreachable.cpp b/test/ubsan/TestCases/Misc/unreachable.cpp new file mode 100644 index 000000000000..e1206edb30d5 --- /dev/null +++ b/test/ubsan/TestCases/Misc/unreachable.cpp @@ -0,0 +1,6 @@ +// RUN: %clangxx -fsanitize=unreachable %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s + +int main(int, char **argv) { + // CHECK: unreachable.cpp:5:3: runtime error: execution reached a __builtin_unreachable() call + __builtin_unreachable(); +} diff --git a/test/ubsan/TestCases/Misc/vla.c b/test/ubsan/TestCases/Misc/vla.c new file mode 100644 index 000000000000..10721537bbbc --- /dev/null +++ b/test/ubsan/TestCases/Misc/vla.c @@ -0,0 +1,11 @@ +// RUN: %clang -fsanitize=vla-bound %s -O3 -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-MINUS-ONE +// RUN: %run %t a 2>&1 | FileCheck %s --check-prefix=CHECK-ZERO +// RUN: %run %t a b + +int main(int argc, char **argv) { + // CHECK-MINUS-ONE: vla.c:9:11: runtime error: variable length array bound evaluates to non-positive value -1 + // CHECK-ZERO: vla.c:9:11: runtime error: variable length array bound evaluates to non-positive value 0 + int arr[argc - 2]; + return 0; +} diff --git a/test/ubsan/TestCases/TypeCheck/Function/function.cpp b/test/ubsan/TestCases/TypeCheck/Function/function.cpp new file mode 100644 index 000000000000..deca77d459c0 --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/Function/function.cpp @@ -0,0 +1,24 @@ +// RUN: %clangxx -fsanitize=function %s -O3 -g -o %t +// RUN: %run %t 2>&1 | FileCheck %s +// Verify that we can disable symbolization if needed: +// RUN: UBSAN_OPTIONS=symbolize=0 ASAN_OPTIONS=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM + +// -fsanitize=function is unsupported on Darwin yet. +// XFAIL: darwin + +#include <stdint.h> + +void f() {} + +void g(int x) {} + +int main(void) { + // CHECK: runtime error: call to function f() through pointer to incorrect function type 'void (*)(int)' + // CHECK-NEXT: function.cpp:11: note: f() defined here + // NOSYM: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)' + // NOSYM-NEXT: ({{.*}}+0x{{.*}}): note: (unknown) defined here + reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(f))(42); + + // CHECK-NOT: runtime error: call to function g + reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(g))(42); +} diff --git a/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg b/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg new file mode 100644 index 000000000000..27c61a34387c --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg @@ -0,0 +1,3 @@ +# The function type checker is only supported on x86 and x86_64 for now. +if config.root.host_arch not in ['x86', 'x86_64']: + config.unsupported = True diff --git a/test/ubsan/TestCases/TypeCheck/misaligned.cpp b/test/ubsan/TestCases/TypeCheck/misaligned.cpp new file mode 100644 index 000000000000..79f5136db963 --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/misaligned.cpp @@ -0,0 +1,105 @@ +// RUN: %clangxx -fsanitize=alignment -g %s -O3 -o %t +// RUN: %run %t l0 && %run %t s0 && %run %t r0 && %run %t m0 && %run %t f0 && %run %t n0 && %run %t u0 +// RUN: %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace +// RUN: %run %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %run %t r1 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: %run %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// RUN: %run %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN +// RUN: %run %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW +// RUN: %run %t u1 2>&1 | FileCheck %s --check-prefix=CHECK-UPCAST +// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-%os-STACK-LOAD + +// RUN: %clangxx -fsanitize=alignment -fno-sanitize-recover %s -O3 -o %t +// RUN: not %run %t w1 2>&1 | FileCheck %s --check-prefix=CHECK-WILD +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <new> + +struct S { + S() {} + int f() { return 0; } + int k; +}; + +struct T : S { + int t; +}; + +int main(int, char **argv) { + char c[] __attribute__((aligned(8))) = { 0, 0, 0, 0, 1, 2, 3, 4, 5 }; + + // Pointer value may be unspecified here, but behavior is not undefined. + int *p = (int*)&c[4 + argv[1][1] - '0']; + S *s = (S*)p; + T *t = (T*)p; + + void *wild = reinterpret_cast<void *>(0x123L); + + (void)*p; // ok! + + switch (argv[1][0]) { + case 'l': + // CHECK-LOAD: misaligned.cpp:[[@LINE+4]]:12: runtime error: load of misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-LOAD-NEXT: [[PTR]]: note: pointer points here + // CHECK-LOAD-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-LOAD-NEXT: {{^ \^}} + return *p && 0; + // Slow stack unwinding is disabled on Darwin for now, see + // https://code.google.com/p/address-sanitizer/issues/detail?id=137 + // CHECK-Linux-STACK-LOAD: #0 {{.*}} in main{{.*}}misaligned.cpp + // Check for the already checked line to avoid lit error reports. + // CHECK-Darwin-STACK-LOAD: {{ }} + + case 's': + // CHECK-STORE: misaligned.cpp:[[@LINE+4]]:5: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-STORE-NEXT: [[PTR]]: note: pointer points here + // CHECK-STORE-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-STORE-NEXT: {{^ \^}} + *p = 1; + break; + + case 'r': + // CHECK-REFERENCE: misaligned.cpp:[[@LINE+4]]:15: runtime error: reference binding to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-REFERENCE-NEXT: [[PTR]]: note: pointer points here + // CHECK-REFERENCE-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-REFERENCE-NEXT: {{^ \^}} + {int &r = *p;} + break; + + case 'm': + // CHECK-MEMBER: misaligned.cpp:[[@LINE+4]]:15: runtime error: member access within misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMBER-NEXT: [[PTR]]: note: pointer points here + // CHECK-MEMBER-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-MEMBER-NEXT: {{^ \^}} + return s->k && 0; + + case 'f': + // CHECK-MEMFUN: misaligned.cpp:[[@LINE+4]]:12: runtime error: member call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMFUN-NEXT: [[PTR]]: note: pointer points here + // CHECK-MEMFUN-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-MEMFUN-NEXT: {{^ \^}} + return s->f() && 0; + + case 'n': + // CHECK-NEW: misaligned.cpp:[[@LINE+4]]:5: runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here + // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-NEW-NEXT: {{^ \^}} + return (new (s) S)->k && 0; + + case 'u': { + // CHECK-UPCAST: misaligned.cpp:[[@LINE+4]]:17: runtime error: upcast of misaligned address [[PTR:0x[0-9a-f]*]] for type 'T', which requires 4 byte alignment + // CHECK-UPCAST-NEXT: [[PTR]]: note: pointer points here + // CHECK-UPCAST-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-UPCAST-NEXT: {{^ \^}} + S *s2 = (S*)t; + return s2->f(); + } + + case 'w': + // CHECK-WILD: misaligned.cpp:[[@LINE+3]]:35: runtime error: member access within misaligned address 0x000000000123 for type 'S', which requires 4 byte alignment + // CHECK-WILD-NEXT: 0x000000000123: note: pointer points here + // CHECK-WILD-NEXT: <memory cannot be printed> + return static_cast<S*>(wild)->k; + } +} diff --git a/test/ubsan/TestCases/TypeCheck/null.cpp b/test/ubsan/TestCases/TypeCheck/null.cpp new file mode 100644 index 000000000000..2a90f7fb956b --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/null.cpp @@ -0,0 +1,38 @@ +// RUN: %clangxx -fsanitize=null %s -O3 -o %t +// RUN: %run %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD +// RUN: not --crash %run %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %run %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: %run %t m 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// RUN: %run %t f 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN + +struct S { + int f() { return 0; } + int k; +}; + +int main(int, char **argv) { + int *p = 0; + S *s = 0; + + (void)*p; // ok! + + switch (argv[1][0]) { + case 'l': + // CHECK-LOAD: null.cpp:22:12: runtime error: load of null pointer of type 'int' + return *p; + case 's': + // CHECK-STORE: null.cpp:25:5: runtime error: store to null pointer of type 'int' + *p = 1; + break; + case 'r': + // CHECK-REFERENCE: null.cpp:29:15: runtime error: reference binding to null pointer of type 'int' + {int &r = *p;} + break; + case 'm': + // CHECK-MEMBER: null.cpp:33:15: runtime error: member access within null pointer of type 'S' + return s->k; + case 'f': + // CHECK-MEMFUN: null.cpp:36:12: runtime error: member call on null pointer of type 'S' + return s->f(); + } +} diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp new file mode 100644 index 000000000000..5261e71c2d3d --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp @@ -0,0 +1,19 @@ +// RUN: %clangxx -fsanitize=vptr -fno-sanitize-recover -g %s -O3 -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +// FIXME: This test produces linker errors on Darwin. +// XFAIL: darwin + +struct S { virtual int f() { return 0; } }; +struct T : virtual S {}; + +struct Foo { virtual int f() { return 0; } }; + +int main(int argc, char **argv) { + Foo foo; + T *t = (T*)&foo; + S *s = t; + // CHECK: vptr-virtual-base.cpp:[[@LINE-1]]:10: runtime error: cast to virtual base of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-NEXT: [[PTR]]: note: object is of type 'Foo' + return s->f(); +} diff --git a/test/ubsan/TestCases/TypeCheck/vptr.cpp b/test/ubsan/TestCases/TypeCheck/vptr.cpp new file mode 100644 index 000000000000..3a6b1553f9b3 --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/vptr.cpp @@ -0,0 +1,166 @@ +// RUN: %clangxx -fsanitize=vptr -g %s -O3 -o %t +// RUN: %run %t rT && %run %t mT && %run %t fT && %run %t cT +// RUN: %run %t rU && %run %t mU && %run %t fU && %run %t cU +// RUN: %run %t rS && %run %t rV && %run %t oV +// RUN: %run %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %run %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %run %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace +// RUN: %run %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %run %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %run %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace +// RUN: %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace +// RUN: %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace + +// RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t mS 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t fS 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t cS 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t mV 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t fV 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t cV 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t oU 2>&1 + +// RUN: echo "vptr_check:S" > %t.loc-supp +// RUN: ASAN_OPTIONS=suppressions=%t.loc-supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.loc-supp:halt_on_error=1 not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS + +// FIXME: This test produces linker errors on Darwin. +// XFAIL: darwin +// REQUIRES: stable-runtime + +extern "C" { +const char *__ubsan_default_options() { + return "print_stacktrace=1"; +} +} + +struct S { + S() : a(0) {} + ~S() {} + int a; + int f() { return 0; } + virtual int v() { return 0; } +}; + +struct T : S { + T() : b(0) {} + int b; + int g() { return 0; } + virtual int v() { return 1; } +}; + +struct X {}; +struct U : S, T, virtual X { virtual int v() { return 2; } }; + +struct V : S {}; + +// Make p global so that lsan does not complain. +T *p = 0; + +int access_p(T *p, char type); + +int main(int, char **argv) { + T t; + (void)t.a; + (void)t.b; + (void)t.f(); + (void)t.g(); + (void)t.v(); + (void)t.S::v(); + + U u; + (void)u.T::a; + (void)u.b; + (void)u.T::f(); + (void)u.g(); + (void)u.v(); + (void)u.T::v(); + (void)((T&)u).S::v(); + + char Buffer[sizeof(U)] = {}; + switch (argv[1][1]) { + case '0': + p = reinterpret_cast<T*>(Buffer); + break; + case 'S': + p = reinterpret_cast<T*>(new S); + break; + case 'T': + p = new T; + break; + case 'U': + p = new U; + break; + case 'V': + p = reinterpret_cast<T*>(new U); + break; + } + + access_p(p, argv[1][0]); + return 0; +} + +int access_p(T *p, char type) { + switch (type) { + case 'r': + // Binding a reference to storage of appropriate size and alignment is OK. + {T &r = *p;} + break; + + case 'x': + for (int i = 0; i < 2; i++) { + // Check that the first iteration ("S") succeeds, while the second ("V") fails. + p = reinterpret_cast<T*>((i == 0) ? new S : new V); + // CHECK-LOC-SUPPRESS: vptr.cpp:[[@LINE+5]]:7: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-LOC-SUPPRESS-NEXT: [[PTR]]: note: object is of type 'V' + // CHECK-LOC-SUPPRESS-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-LOC-SUPPRESS-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-LOC-SUPPRESS-NEXT: {{^ vptr for 'V'}} + p->g(); + } + return 0; + + case 'm': + // CHECK-MEMBER: vptr.cpp:[[@LINE+6]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-MEMBER-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]] + // CHECK-MEMBER-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] + return p->b; + + // CHECK-NULL-MEMBER: vptr.cpp:[[@LINE-2]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-NULL-MEMBER-NEXT: [[PTR]]: note: object has invalid vptr + // CHECK-NULL-MEMBER-NEXT: {{^ ?.. .. .. .. ?00 00 00 00 ?00 00 00 00 ?}} + // CHECK-NULL-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-NULL-MEMBER-NEXT: {{^ invalid vptr}} + // CHECK-NULL-MEMBER-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE-7]] + + case 'f': + // CHECK-MEMFUN: vptr.cpp:[[@LINE+6]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-MEMFUN-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-MEMFUN-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]] + // TODO: Add check for stacktrace here. + return p->g(); + + case 'o': + // CHECK-OFFSET: vptr.cpp:[[@LINE+6]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'U' + // CHECK-OFFSET-NEXT: 0x{{[0-9a-f]*}}: note: object is base class subobject at offset {{8|16}} within object of type [[DYN_TYPE:'U']] + // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)?~~~~~~~~~~~ *$}} + // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]] + // CHECK-OFFSET-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] + return reinterpret_cast<U*>(p)->v() - 2; + + case 'c': + // CHECK-DOWNCAST: vptr.cpp:[[@LINE+6]]:5: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-DOWNCAST-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-DOWNCAST-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-DOWNCAST-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-DOWNCAST-NEXT: {{^ vptr for}} [[DYN_TYPE]] + // CHECK-DOWNCAST-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] + static_cast<T*>(reinterpret_cast<S*>(p)); + return 0; + } +} diff --git a/test/ubsan/lit.common.cfg b/test/ubsan/lit.common.cfg new file mode 100644 index 000000000000..d28733a61cf8 --- /dev/null +++ b/test/ubsan/lit.common.cfg @@ -0,0 +1,56 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Choose between standalone and UBSan+ASan modes. +ubsan_lit_test_mode = get_required_attr(config, 'ubsan_lit_test_mode') +if ubsan_lit_test_mode == "Standalone": + config.name = 'UndefinedBehaviorSanitizer-Standalone' + config.available_features.add("ubsan-standalone") + clang_ubsan_cflags = [] +elif ubsan_lit_test_mode == "AddressSanitizer": + if config.host_os == 'Darwin': + # ubsan-asan doesn't yet work on Darwin, + # see http://llvm.org/bugs/show_bug.cgi?id=21112. + config.unsupported = True + config.name = 'UndefinedBehaviorSanitizer-AddressSanitizer' + config.available_features.add("ubsan-asan") + clang_ubsan_cflags = ["-fsanitize=address"] + config.environment['ASAN_OPTIONS'] = 'detect_leaks=0' +else: + lit_config.fatal("Unknown UBSan test mode: %r" % ubsan_lit_test_mode) + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +target_cflags = [get_required_attr(config, "target_cflags")] +clang_ubsan_cflags += target_cflags +clang_ubsan_cxxflags = config.cxx_mode_flags + clang_ubsan_cflags + +# Define %clang and %clangxx substitutions to use in test RUN lines. +config.substitutions.append( ("%clang ", build_invocation(clang_ubsan_cflags)) ) +config.substitutions.append( ("%clangxx ", build_invocation(clang_ubsan_cxxflags)) ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# Check that the host supports UndefinedBehaviorSanitizer tests +if config.host_os not in ['Linux', 'Darwin', 'FreeBSD']: + config.unsupported = True + +# Allow tests to use REQUIRES=stable-runtime. For use when you cannot use XFAIL +# because the test hangs or fails on one configuration and not the other. +if config.target_arch.startswith('arm') == False: + config.available_features.add('stable-runtime') diff --git a/test/ubsan/lit.site.cfg.in b/test/ubsan/lit.site.cfg.in new file mode 100644 index 000000000000..ef72d2bbb42f --- /dev/null +++ b/test/ubsan/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Tool-specific config options. +config.ubsan_lit_test_mode = "@UBSAN_LIT_TEST_MODE@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/lit.common.cfg") |