diff options
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", &loc |