aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/CXX/symex/libc++
diff options
context:
space:
mode:
authorFelix Rath <felix.rath@comsys.rwth-aachen.de>2020-05-22 14:09:10 +0200
committerMartinNowack <2443641+MartinNowack@users.noreply.github.com>2020-10-12 11:19:24 +0100
commitc09306ffd894f95be195723327d5b17dca73ebd1 (patch)
tree592773383280012bce8856b28503ab61de0deb98 /test/CXX/symex/libc++
parentd920e049fa955877f772188fdc58cab1b31aabc9 (diff)
downloadklee-c09306ffd894f95be195723327d5b17dca73ebd1.tar.gz
Implemented support for C++ Exceptions
We implement the Itanium ABI unwinding base-API, and leave the C++-specific parts to libcxxabi. Co-authored-by: Lukas Wölfer <lukas.woelfer@rwth-aachen.de>
Diffstat (limited to 'test/CXX/symex/libc++')
-rw-r--r--test/CXX/symex/libc++/atexit.cpp19
-rw-r--r--test/CXX/symex/libc++/can_catch_test.cpp88
-rw-r--r--test/CXX/symex/libc++/catch_recover.cpp26
-rw-r--r--test/CXX/symex/libc++/catch_with_adjusted_exception_pointer.cpp37
-rw-r--r--test/CXX/symex/libc++/cout.cpp4
-rw-r--r--test/CXX/symex/libc++/cout_sym.cpp16
-rw-r--r--test/CXX/symex/libc++/dynamic_cast.cpp56
-rw-r--r--test/CXX/symex/libc++/exception.cpp33
-rw-r--r--test/CXX/symex/libc++/exception_inheritance.cpp78
-rw-r--r--test/CXX/symex/libc++/general_catch.cpp60
-rw-r--r--test/CXX/symex/libc++/landingpad.cpp53
-rw-r--r--test/CXX/symex/libc++/multi_throw.cpp47
-rw-r--r--test/CXX/symex/libc++/multi_unwind.cpp70
-rw-r--r--test/CXX/symex/libc++/nested.cpp26
-rw-r--r--test/CXX/symex/libc++/nested_fail.cpp26
-rw-r--r--test/CXX/symex/libc++/rethrow.cpp35
-rw-r--r--test/CXX/symex/libc++/simple_exception.cpp19
-rw-r--r--test/CXX/symex/libc++/simple_exception_fail.cpp13
-rw-r--r--test/CXX/symex/libc++/symbolic_exception.cpp29
-rw-r--r--test/CXX/symex/libc++/throw_specifiers.cpp35
-rw-r--r--test/CXX/symex/libc++/throwing_exception_destructor.cpp45
-rw-r--r--test/CXX/symex/libc++/uncaught_exception.cpp13
-rw-r--r--test/CXX/symex/libc++/vector.cpp5
23 files changed, 784 insertions, 49 deletions
diff --git a/test/CXX/symex/libc++/atexit.cpp b/test/CXX/symex/libc++/atexit.cpp
index 03b559ae..fa8df475 100644
--- a/test/CXX/symex/libc++/atexit.cpp
+++ b/test/CXX/symex/libc++/atexit.cpp
@@ -1,19 +1,20 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
// REQUIRES: libcxx
// REQUIRES: uclibc
// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
// RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc | FileCheck %s
-//
-// CHECK: Returning from main
-// CHECK: atexit_1
-// CHECK: atexit_2
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
-#include <iostream>
#include <cstdlib>
+#include <iostream>
-int main(int argc, char** args){
- auto atexithandler2 = std::atexit([](){std::cout << "atexit_2" << std::endl;});
- auto atexithandler1 = std::atexit([](){std::cout << "atexit_1" << std::endl;});
+int main(int argc, char **args) {
+ auto atexithandler2 = std::atexit([]() { std::cout << "atexit_2" << std::endl; });
+ auto atexithandler1 = std::atexit([]() { std::cout << "atexit_1" << std::endl; });
std::cout << "Returning from main" << std::endl;
+ // CHECK: Returning from main
return 0;
+ // CHECK: atexit_1
+ // CHECK: atexit_2
}
diff --git a/test/CXX/symex/libc++/can_catch_test.cpp b/test/CXX/symex/libc++/can_catch_test.cpp
new file mode 100644
index 00000000..9d3a2d23
--- /dev/null
+++ b/test/CXX/symex/libc++/can_catch_test.cpp
@@ -0,0 +1,88 @@
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+#include <typeinfo>
+
+extern bool _klee_eh_can_catch(const void *catcher, const void *exception);
+
+class other {};
+class yet_another {};
+class ya_derived : private yet_another {};
+
+class TestClass {
+public:
+ const char *classname = "TestClass";
+
+ TestClass() { printf("%s: Normal constructor called\n", classname); }
+ TestClass(const TestClass &other) {
+ printf("%s: Copy constructor called\n", classname);
+ }
+ TestClass(TestClass &&other) {
+ printf("%s: Move constructor called\n", classname);
+ }
+ TestClass &operator=(const TestClass &&other) {
+ printf("%s: Assignment constructor called\n", classname);
+ return *this;
+ }
+ virtual ~TestClass() { printf("%s: Destructor called\n", classname); }
+};
+
+class DerivedClass : public TestClass {
+public:
+ const char *classname = "DerivedClass";
+
+ DerivedClass() { printf("%s: Normal constructor called\n", classname); }
+ DerivedClass(const DerivedClass &other) {
+ printf("%s: Copy constructor called\n", classname);
+ }
+ DerivedClass(DerivedClass &&other) {
+ printf("%s: Move constructor called\n", classname);
+ }
+ DerivedClass &operator=(const DerivedClass &&other) {
+ printf("%s: Assignment constructor called\n", classname);
+ return *this;
+ }
+ ~DerivedClass() { printf("%s: Destructor called\n", classname); }
+};
+
+class SecondDerivedClass : public DerivedClass, other, yet_another {
+public:
+ const char *classname = "SecondDerivedClass";
+
+ SecondDerivedClass() { printf("%s: Normal constructor called\n", classname); }
+ SecondDerivedClass(const SecondDerivedClass &other) {
+ printf("%s: Copy constructor called\n", classname);
+ }
+ SecondDerivedClass(SecondDerivedClass &&other) {
+ printf("%s: Move constructor called\n", classname);
+ }
+ SecondDerivedClass &operator=(const SecondDerivedClass &&other) {
+ printf("%s: Assignment constructor called\n", classname);
+ return *this;
+ }
+ ~SecondDerivedClass() { printf("%s: Destructor called\n", classname); }
+};
+
+int main(int argc, char **args) {
+ try {
+ try {
+ throw SecondDerivedClass();
+ } catch (SecondDerivedClass &p) {
+ puts("DerivedClass caught\n");
+ // CHECK: DerivedClass caught
+ throw;
+ }
+ } catch (TestClass &tc) {
+ puts("TestClass caught\n");
+ // CHECK: TestClass caught
+ } catch (int) {
+ } catch (int *) {
+ } catch (ya_derived &) {
+ }
+
+ return 0;
+}
diff --git a/test/CXX/symex/libc++/catch_recover.cpp b/test/CXX/symex/libc++/catch_recover.cpp
new file mode 100644
index 00000000..44362b25
--- /dev/null
+++ b/test/CXX/symex/libc++/catch_recover.cpp
@@ -0,0 +1,26 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+#include <stdexcept>
+
+int main(int argc, char **args) {
+ try {
+ throw std::runtime_error("foo");
+ } catch (const std::runtime_error &ex) {
+ printf("In first catch\n");
+ // CHECK-DAG: In first catch
+ }
+ try {
+ throw std::runtime_error("bar");
+ } catch (const std::runtime_error &ex) {
+ printf("In second catch\n");
+ // CHECK-DAG: In second catch
+ }
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 1
diff --git a/test/CXX/symex/libc++/catch_with_adjusted_exception_pointer.cpp b/test/CXX/symex/libc++/catch_with_adjusted_exception_pointer.cpp
new file mode 100644
index 00000000..bfb69be4
--- /dev/null
+++ b/test/CXX/symex/libc++/catch_with_adjusted_exception_pointer.cpp
@@ -0,0 +1,37 @@
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <iostream>
+
+class Base {
+public:
+ int base_int = 42;
+ virtual void print() {
+ std::cout << "Base\n";
+ }
+};
+
+class Derived : public Base {
+public:
+ virtual void print() {
+ std::cout << "Derived\n";
+ }
+};
+
+int main() {
+ Base *bd = new Derived();
+ // CHECK: Derived
+ bd->print();
+
+ try {
+ throw bd;
+ } catch (Base *b) {
+ // CHECK: Derived
+ b->print();
+ // CHECK: 42
+ std::cout << b->base_int << "\n";
+ }
+}
diff --git a/test/CXX/symex/libc++/cout.cpp b/test/CXX/symex/libc++/cout.cpp
index 0f3c338a..62cd0406 100644
--- a/test/CXX/symex/libc++/cout.cpp
+++ b/test/CXX/symex/libc++/cout.cpp
@@ -1,8 +1,10 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
// REQUIRES: libcxx
// REQUIRES: uclibc
// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
// RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
// CHECK-DAG: cout
// CHECK-DAG: cerr
diff --git a/test/CXX/symex/libc++/cout_sym.cpp b/test/CXX/symex/libc++/cout_sym.cpp
index 8bf81122..177c3ed7 100644
--- a/test/CXX/symex/libc++/cout_sym.cpp
+++ b/test/CXX/symex/libc++/cout_sym.cpp
@@ -1,22 +1,22 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
// REQUIRES: libcxx
// REQUIRES: uclibc
// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
// RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc | FileCheck %s
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
-// CHECK-DAG: greater
-// CHECK-DAG: lower
-
-
-#include <iostream>
#include "klee/klee.h"
+#include <iostream>
-int main(int argc, char** args){
+int main(int argc, char **args) {
int x = klee_int("x");
- if (x > 0){
+ if (x > 0) {
std::cout << "greater: " << x << std::endl;
+ // CHECK-DAG: greater
} else {
std::cout << "lower: " << x << std::endl;
+ // CHECK-DAG: lower
}
return 0;
}
diff --git a/test/CXX/symex/libc++/dynamic_cast.cpp b/test/CXX/symex/libc++/dynamic_cast.cpp
index c2d8b528..a2fc8b82 100644
--- a/test/CXX/symex/libc++/dynamic_cast.cpp
+++ b/test/CXX/symex/libc++/dynamic_cast.cpp
@@ -1,54 +1,52 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
// REQUIRES: libcxx
// REQUIRES: uclibc
// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
// RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc
-//
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc
+
// Copied from 'http://en.cppreference.com/w/cpp/language/dynamic_cast'
struct V {
- virtual void f() {}; // must be polymorphic to use runtime-checked dynamic_cast
+ virtual void f(){}; // must be polymorphic to use runtime-checked dynamic_cast
};
struct A : virtual V {};
struct B : virtual V {
- B(V* v, A* a) {
+ B(V *v, A *a) {
// casts during construction (see the call in the constructor of D below)
- dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B, results in B*
- dynamic_cast<B*>(a); // undefined behavior: a has type A*, A not a base of B
+ dynamic_cast<B *>(v); // well-defined: v of type V*, V base of B, results in B*
+ dynamic_cast<B *>(a); // undefined behavior: a has type A*, A not a base of B
}
};
struct D : A, B {
- D() : B((A*)this, this) { }
+ D() : B((A *)this, this) {}
};
struct Base {
- virtual ~Base() {}
+ virtual ~Base() {}
};
-struct Derived: Base {
- virtual void name() {}
+struct Derived : Base {
+ virtual void name() {}
};
-int main()
-{
- D d; // the most derived object
- A& a = d; // upcast, dynamic_cast may be used, but unnecessary
- D& new_d = dynamic_cast<D&>(a); // downcast
- B& new_b = dynamic_cast<B&>(a); // sidecast
-
+int main() {
+ D d; // the most derived object
+ A &a = d; // upcast, dynamic_cast may be used, but unnecessary
+ D &new_d = dynamic_cast<D &>(a); // downcast
+ B &new_b = dynamic_cast<B &>(a); // sidecast
- Base* b1 = new Base;
- if(Derived* d = dynamic_cast<Derived*>(b1))
- {
- d->name(); // safe to call
- }
+ Base *b1 = new Base;
+ if (Derived *d = dynamic_cast<Derived *>(b1)) {
+ d->name(); // safe to call
+ }
- Base* b2 = new Derived;
- if(Derived* d = dynamic_cast<Derived*>(b2))
- {
- d->name(); // safe to call
- }
+ Base *b2 = new Derived;
+ if (Derived *d = dynamic_cast<Derived *>(b2)) {
+ d->name(); // safe to call
+ }
- delete b1;
- delete b2;
+ delete b1;
+ delete b2;
}
diff --git a/test/CXX/symex/libc++/exception.cpp b/test/CXX/symex/libc++/exception.cpp
new file mode 100644
index 00000000..b46d474e
--- /dev/null
+++ b/test/CXX/symex/libc++/exception.cpp
@@ -0,0 +1,33 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cstdio>
+
+int main(int argc, char **args) {
+ int x = klee_int("Test");
+
+ try {
+ if (x % 2 == 0) {
+ printf("Normal return\n");
+ // CHECK-DAG: Normal return
+ return 0;
+ }
+ throw x;
+ } catch (int b) {
+ if (b % 3 == 0) {
+ printf("First exceptional return\n");
+ // CHECK-DAG: First exceptional return
+ return 2;
+ }
+ printf("Second exceptional return\n");
+ // CHECK-DAG: Second exceptional return
+ return 3;
+ }
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 3
diff --git a/test/CXX/symex/libc++/exception_inheritance.cpp b/test/CXX/symex/libc++/exception_inheritance.cpp
new file mode 100644
index 00000000..4e42fbb7
--- /dev/null
+++ b/test/CXX/symex/libc++/exception_inheritance.cpp
@@ -0,0 +1,78 @@
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+class TestClass {
+public:
+ const char *classname = "TestClass";
+
+ TestClass() {
+ printf("%s: Normal constructor called\n", classname);
+ }
+ TestClass(const TestClass &other) {
+ printf("%s: Copy constructor called\n", classname);
+ }
+ TestClass(TestClass &&other) { printf("%s: Move constructor called\n", classname); }
+ TestClass &operator=(const TestClass &&other) {
+ printf("%s: Assignment constructor called\n", classname);
+ return *this;
+ }
+ virtual ~TestClass() { printf("%s: Destructor called\n", classname); }
+};
+
+class DerivedClass : public TestClass {
+public:
+ const char *classname = "DerivedClass";
+
+ DerivedClass() {
+ printf("%s: Normal constructor called\n", classname);
+ }
+ DerivedClass(const DerivedClass &other) {
+ printf("%s: Copy constructor called\n", classname);
+ }
+ DerivedClass(DerivedClass &&other) { printf("%s: Move constructor called\n", classname); }
+ DerivedClass &operator=(const DerivedClass &&other) {
+ printf("%s: Assignment constructor called\n", classname);
+ return *this;
+ }
+ ~DerivedClass() { printf("%s: Destructor called\n", classname); }
+};
+
+class SecondDerivedClass : public DerivedClass {
+public:
+ const char *classname = "SecondDerivedClass";
+
+ SecondDerivedClass() {
+ printf("%s: Normal constructor called\n", classname);
+ }
+ SecondDerivedClass(const SecondDerivedClass &other) {
+ printf("%s: Copy constructor called\n", classname);
+ }
+ SecondDerivedClass(SecondDerivedClass &&other) { printf("%s: Move constructor called\n", classname); }
+ SecondDerivedClass &operator=(const SecondDerivedClass &&other) {
+ printf("%s: Assignment constructor called\n", classname);
+ return *this;
+ }
+ ~SecondDerivedClass() { printf("%s: Destructor called\n", classname); }
+};
+
+int main(int argc, char **args) {
+ try {
+ try {
+ throw SecondDerivedClass();
+ } catch (DerivedClass &p) {
+ puts("DerivedClass caught\n");
+ // CHECK: DerivedClass caught
+ throw;
+ }
+ } catch (TestClass &tc) {
+ puts("TestClass caught\n");
+ // CHECK: TestClass caught
+ }
+
+ return 0;
+}
diff --git a/test/CXX/symex/libc++/general_catch.cpp b/test/CXX/symex/libc++/general_catch.cpp
new file mode 100644
index 00000000..ee7f9983
--- /dev/null
+++ b/test/CXX/symex/libc++/general_catch.cpp
@@ -0,0 +1,60 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cassert>
+#include <cstdio>
+
+struct CustomStruct {
+ int a;
+};
+
+int thrower(int x) {
+ if (x == 0) {
+ CustomStruct p;
+ throw p;
+ } else if (x == 1) {
+ throw 7;
+ } else if (x == 2) {
+ throw new CustomStruct();
+ } else {
+ throw &thrower;
+ }
+}
+
+int catcher(int x) {
+ try {
+ thrower(x);
+ } catch (int ex) {
+ printf("Caught int\n");
+ // CHECK-DAG: Caught int
+ return 1;
+ } catch (CustomStruct ex) {
+ printf("Caught normal CustomStruct\n");
+ // CHECK-DAG: Caught normal CustomStruct
+ return 2;
+ } catch (CustomStruct *ex) {
+ printf("Caught pointer CustomStruct\n");
+ // CHECK-DAG: Caught pointer CustomStruct
+ return 3;
+ } catch (...) {
+ printf("Caught general exception\n");
+ // CHECK-DAG: Caught general exception
+ return 4;
+ }
+ // Unreachable instruction
+ assert(0);
+ return 0;
+}
+
+int main(int argc, char **args) {
+ int x = klee_int("x");
+ int res = catcher(x);
+ return res;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 4
diff --git a/test/CXX/symex/libc++/landingpad.cpp b/test/CXX/symex/libc++/landingpad.cpp
new file mode 100644
index 00000000..21e55536
--- /dev/null
+++ b/test/CXX/symex/libc++/landingpad.cpp
@@ -0,0 +1,53 @@
+// Testcase for proper handling of
+// c++ type, constructors and destructors.
+// Based on: https://gcc.gnu.org/wiki/Dwarf2EHNewbiesHowto
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc | FileCheck %s
+// Expect the following output:
+// CHECK: Throwing 1...
+// CHECK: A() 1
+// CHECK: A(const A&) 2
+// CHECK: Caught.
+// CHECK: ~A() 2
+// CHECK: ~A() 1
+// CHECK: c == 2, d == 2
+
+#include <stdio.h>
+
+int c, d;
+
+struct A {
+ int i;
+ A() {
+ i = ++c;
+ printf("A() %d\n", i);
+ }
+ A(const A &) {
+ i = ++c;
+ printf("A(const A&) %d\n", i);
+ }
+ ~A() {
+ printf("~A() %d\n", i);
+ ++d;
+ }
+};
+
+void f() {
+ printf("Throwing 1...\n");
+ throw A();
+}
+
+int main() {
+ c = 0;
+ d = 0;
+ try {
+ f();
+ } catch (A) {
+ printf("Caught.\n");
+ }
+ printf("c == %d, d == %d\n", c, d);
+ return c != d;
+}
diff --git a/test/CXX/symex/libc++/multi_throw.cpp b/test/CXX/symex/libc++/multi_throw.cpp
new file mode 100644
index 00000000..be07bc17
--- /dev/null
+++ b/test/CXX/symex/libc++/multi_throw.cpp
@@ -0,0 +1,47 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cstdio>
+
+static void x(int a) {
+ if (a == 7) {
+ throw static_cast<float>(a);
+ }
+}
+
+struct ThrowingDestructor {
+ int p = 0;
+ ~ThrowingDestructor() {
+ try {
+ x(p);
+ } catch (float ex) {
+ printf("Caught float in ThrowingDestructor()\n");
+ // CHECK-DAG: Caught float in ThrowingDestructor()
+ }
+ }
+};
+
+static void y(int a) {
+ ThrowingDestructor p;
+ p.p = klee_int("struct_int");
+ try {
+ if (a == 7) {
+ throw a;
+ }
+ } catch (int ex) {
+ printf("Caught int in y()\n");
+ // CHECK-DAG: Caught int in y()
+ }
+}
+
+int main(int argc, char **args) {
+ y(klee_int("fun_int"));
+ return 0;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 4
diff --git a/test/CXX/symex/libc++/multi_unwind.cpp b/test/CXX/symex/libc++/multi_unwind.cpp
new file mode 100644
index 00000000..98bae1dc
--- /dev/null
+++ b/test/CXX/symex/libc++/multi_unwind.cpp
@@ -0,0 +1,70 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cassert>
+#include <cstdio>
+
+struct CustomStruct {
+ int a;
+};
+
+int thrower(int x) {
+ if (x == 0) {
+ CustomStruct p;
+ throw p;
+ } else if (x == 1) {
+ throw 7;
+ } else {
+ throw new CustomStruct();
+ }
+}
+int rethrower(int x) {
+ thrower(x);
+ return 7;
+}
+
+int failed_catch(int x) {
+ try {
+ rethrower(x);
+ } catch (char *ex) {
+ // Don't catch wrong type
+ assert(0);
+ }
+ // rethrower always throws, cannot reach this instruction
+ assert(0);
+ return 0;
+}
+
+int catcher(int x) {
+ try {
+ failed_catch(x);
+ } catch (int ex) {
+ printf("Caught int\n");
+ // CHECK-DAG: Caught int
+ return 1;
+ } catch (CustomStruct ex) {
+ printf("Caught normal CustomStruct\n");
+ // CHECK-DAG: Caught normal CustomStruct
+ return 2;
+ } catch (CustomStruct *ex) {
+ printf("Caught pointer CustomStruct\n");
+ // CHECK-DAG: Caught pointer CustomStruct
+ return 3;
+ }
+ // Unreachable instruction
+ assert(0);
+ return 0;
+}
+
+int main(int argc, char **args) {
+ int x = klee_int("x");
+ int res = catcher(x);
+ return res;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 3
diff --git a/test/CXX/symex/libc++/nested.cpp b/test/CXX/symex/libc++/nested.cpp
new file mode 100644
index 00000000..b85d6759
--- /dev/null
+++ b/test/CXX/symex/libc++/nested.cpp
@@ -0,0 +1,26 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+int main(int argc, char **args) {
+ try {
+ try {
+ char *p = new char[8];
+ throw p;
+ } catch (char *ex) {
+ int i = 7;
+ throw i;
+ }
+ } catch (int ex) {
+ printf("Reached outer catch with i = %d\n", ex);
+ // CHECK-DAG: Reached outer catch with i = 7
+ }
+ return 0;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 1
diff --git a/test/CXX/symex/libc++/nested_fail.cpp b/test/CXX/symex/libc++/nested_fail.cpp
new file mode 100644
index 00000000..919434f1
--- /dev/null
+++ b/test/CXX/symex/libc++/nested_fail.cpp
@@ -0,0 +1,26 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cstdio>
+
+int main(int argc, char **args) {
+ try {
+ try {
+ char *p = new char[8];
+ throw p;
+ } catch (int ex) {
+ printf("Landed in wrong inner catch\n");
+ // CHECK-NOT: Landed in wrong inner catch
+ }
+ } catch (int ex) {
+ printf("Landed in wrong outer catch\n");
+ // CHECK-NOT: Landed in wrong outer catch
+ }
+ return 0;
+}
+// CHECK: terminating with uncaught exception of type char*
diff --git a/test/CXX/symex/libc++/rethrow.cpp b/test/CXX/symex/libc++/rethrow.cpp
new file mode 100644
index 00000000..d6a0954d
--- /dev/null
+++ b/test/CXX/symex/libc++/rethrow.cpp
@@ -0,0 +1,35 @@
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+class TestClass {
+public:
+ virtual ~TestClass() {}
+};
+
+class DerivedClass : public TestClass {
+};
+
+class SecondDerivedClass : public DerivedClass {
+};
+
+int main(int argc, char **args) {
+ try {
+ try {
+ throw SecondDerivedClass();
+ } catch (DerivedClass &p) {
+ puts("DerivedClass caught\n");
+ // CHECK: DerivedClass caught
+ throw;
+ }
+ } catch (TestClass &tc) {
+ puts("TestClass caught\n");
+ // CHECK: TestClass caught
+ }
+
+ return 0;
+}
diff --git a/test/CXX/symex/libc++/simple_exception.cpp b/test/CXX/symex/libc++/simple_exception.cpp
new file mode 100644
index 00000000..af002253
--- /dev/null
+++ b/test/CXX/symex/libc++/simple_exception.cpp
@@ -0,0 +1,19 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+// CHECK: KLEE: done: completed paths = 1
+
+#include "klee/klee.h"
+
+#include <stdexcept>
+
+int main(int argc, char **args) {
+ try {
+ throw std::runtime_error("foo");
+ } catch (const std::runtime_error &ex) {
+ }
+}
diff --git a/test/CXX/symex/libc++/simple_exception_fail.cpp b/test/CXX/symex/libc++/simple_exception_fail.cpp
new file mode 100644
index 00000000..d4a30b25
--- /dev/null
+++ b/test/CXX/symex/libc++/simple_exception_fail.cpp
@@ -0,0 +1,13 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include <stdexcept>
+
+int main(int argc, char **args) {
+ throw std::runtime_error("foo");
+}
+// CHECK: terminating with uncaught exception of type std::runtime_error: foo \ No newline at end of file
diff --git a/test/CXX/symex/libc++/symbolic_exception.cpp b/test/CXX/symex/libc++/symbolic_exception.cpp
new file mode 100644
index 00000000..49d432a8
--- /dev/null
+++ b/test/CXX/symex/libc++/symbolic_exception.cpp
@@ -0,0 +1,29 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cstdio>
+
+int main(int argc, char **args) {
+ try {
+ throw klee_int("x");
+ } catch (int ex) {
+ if (ex > 7) {
+ puts("ex > 7");
+ // CHECK-DAG: ex > 7
+ } else if (ex < 2) {
+ puts("ex < 2");
+ // CHECK-DAG: ex < 2
+ } else {
+ puts("2 <= ex <= 7");
+ // CHECK-DAG: 2 <= ex <= 7
+ }
+ }
+ return 0;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 3
diff --git a/test/CXX/symex/libc++/throw_specifiers.cpp b/test/CXX/symex/libc++/throw_specifiers.cpp
new file mode 100644
index 00000000..5a7955fb
--- /dev/null
+++ b/test/CXX/symex/libc++/throw_specifiers.cpp
@@ -0,0 +1,35 @@
+// Testcase for proper handling of
+// throw specifications on functions
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libcxx --libc=uclibc %t.bc | FileCheck %s
+
+#include <stdio.h>
+
+namespace {
+void throw_expected() throw(int) {
+ throw 5;
+}
+
+void throw_unexpected() throw(int, double) {
+ throw "unexpected string";
+}
+} // namespace
+
+int main() {
+ try {
+ throw_expected();
+ } catch (int i) {
+ puts("caught expected int");
+ // CHECK: caught expected int
+ }
+
+ try {
+ throw_unexpected();
+ } catch (char const *s) {
+ puts("caught unexpected string");
+ // CHECK-NOT: caught unexpected string
+ }
+}
diff --git a/test/CXX/symex/libc++/throwing_exception_destructor.cpp b/test/CXX/symex/libc++/throwing_exception_destructor.cpp
new file mode 100644
index 00000000..d24a15b0
--- /dev/null
+++ b/test/CXX/symex/libc++/throwing_exception_destructor.cpp
@@ -0,0 +1,45 @@
+// Testcase for proper handling of exception destructors that throw.
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm -O0 -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libcxx --libc=uclibc --exit-on-error %t.bc
+
+#include <cassert>
+#include <cstdlib>
+#include <exception>
+
+bool destructor_throw = false;
+bool test_catch = false;
+bool int_catch = false;
+
+struct Test {
+ ~Test() noexcept(false) {
+ destructor_throw = true;
+ throw 5;
+ }
+};
+
+int main() {
+ // For some reason, printing to stdout in this test did not work when running
+ // with FileCheck. I tried puts, printf and std::cout, using fflush(stdout)
+ // and std::endl as well, but the output would only be there when not piping
+ // to FileCheck. Therefore we use a terminate handler here, which will just
+ // do the checks instead of printing strings and using FileCheck.
+ std::set_terminate([]() {
+ assert(destructor_throw && test_catch && !int_catch);
+ std::exit(0);
+ });
+
+ try {
+ try {
+ throw Test();
+ } catch (Test &t) {
+ test_catch = true;
+ }
+ } catch (int i) {
+ int_catch = true;
+ }
+
+ assert(false && "should call terminate due to an uncaught exception");
+}
diff --git a/test/CXX/symex/libc++/uncaught_exception.cpp b/test/CXX/symex/libc++/uncaught_exception.cpp
new file mode 100644
index 00000000..4e8f90f5
--- /dev/null
+++ b/test/CXX/symex/libc++/uncaught_exception.cpp
@@ -0,0 +1,13 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+int main() {
+ throw 7;
+}
+
+// CHECK: KLEE: ERROR:
+// CHECK: KLEE: done: completed paths = 1
diff --git a/test/CXX/symex/libc++/vector.cpp b/test/CXX/symex/libc++/vector.cpp
index 32afecd2..6f69ad65 100644
--- a/test/CXX/symex/libc++/vector.cpp
+++ b/test/CXX/symex/libc++/vector.cpp
@@ -1,10 +1,10 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
// REQUIRES: libcxx
// REQUIRES: uclibc
// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
// RUN: rm -rf %t.klee-out
// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
-//
-// CHECK: KLEE: done: completed paths = 1
#include "klee/klee.h"
#include <vector>
@@ -14,3 +14,4 @@ int main(int argc, char **args) {
a.push_back(klee_int("Test"));
return a.at(0);
}
+// CHECK: KLEE: done: completed paths = 1