1 /*
2 * Tests for reinterpret_span_cast template in cxx_safe_buffers.h
3 */
4 #include <os/cxx_safe_buffers.h>
5 #include <vector>
6 #include <darwintest.h>
7 #include <darwintest_utils.h>
8 #define CHECK(...) T_ASSERT_TRUE((__VA_ARGS__), # __VA_ARGS__)
9
10 struct A {
11 int a[2];
12 };
13
14 struct B {
15 int b[3];
16 };
17
18 struct C : B {
19 int c[3];
20 };
21
22 struct D {
23 std::uint8_t a[2];
24 };
25
26 struct NoPadding {
27 char a;
28 char b;
29
30 int c;
31 };
32
33 struct WithPaddingMiddle {
34 char a;
35 int b;
36
37 char c;
38 };
39
40 struct PaddingEnd {
41 int a;
42 char c;
43 };
44
45
46 static void
tests()47 tests()
48 {
49 {
50 // convert span<int> to span<byte>
51 std::array<int, 3> a1{{1, 2, 3}};
52 std::span<int> sp{a1}; // static-extent std::span
53 std::span<std::byte> writable_sp = os::reinterpret_span_cast<std::byte>(sp);
54 std::span<const std::byte> nonwritable_sp = os::reinterpret_span_cast<const std::byte>(sp);
55 CHECK(writable_sp.size() == sp.size_bytes() && nonwritable_sp.size() == sp.size_bytes());
56 }
57
58 {
59 // convert span<byte> to span<A>
60 std::vector<std::byte> vec {std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3},
61 std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}};
62 std::span<std::byte> sp{vec}; // dynamic-extent std::span
63 std::span<A> span_a = os::reinterpret_span_cast<A>(sp);
64 CHECK(sp.size() == span_a.size_bytes());
65 }
66
67 {
68 // convert to a span of unrelated type
69 std::array<A, 3> arr;
70 std::span<A> span_a = arr;
71 std::span<B> span_b = os::reinterpret_span_cast<B>(span_a);
72 CHECK(span_b.size() == 2);
73 }
74
75 {
76 // convert to a span of extended type
77 B array[4];
78 std::span<B> span_b = array;
79 std::span<C> span_c = os::reinterpret_span_cast<C>(span_b);
80 CHECK(2 * span_c.size() == span_b.size());
81 }
82
83 {
84 //convert to a span of base type
85 C array[4];
86 std::span<C> span_c = array;
87 std::span<B> span_b = os::reinterpret_span_cast<B>(span_c);
88 CHECK(2 * span_c.size() == span_b.size());
89 }
90 {
91 std::array<std::uint8_t, 12> buf;
92 std::span<std::uint8_t> sp = buf;
93 std::span<D> span_d = os::reinterpret_span_cast<D>(sp);
94 CHECK(span_d.size() == 6);
95 }
96 }
97
98 static void
trapping_test()99 trapping_test()
100 {
101 pid_t pid = fork(); // Fork a new process
102 T_ASSERT_POSIX_SUCCESS(pid, "forked %d", pid);
103
104 if (pid == 0) {
105 // convert to a span of unrelated type
106 A array[2];
107 std::span<A> span_a = {array, 2};
108 // This invocation will cause a run time trap in child process.
109 std::span<B> span_b = os::reinterpret_span_cast<B>(span_a);
110
111 exit(0); // Exit child process
112 }
113
114 int status = 0, signal = 0;
115
116 // wait for the child process to finish
117 T_ASSERT_FALSE(dt_waitpid(pid, &status, &signal, 0), "wait for child (%d) complete with signal %d", pid, signal);
118 // child process must trigger an execution trap
119 T_ASSERT_TRUE(WIFSIGNALED(signal), "Child process successfully triggered an execution trap");
120 }
121
122
123 T_DECL(reinterpret_span_cast, "cxx_safe_buffers.reinterpret_span_cast")
124 {
125 tests();
126 trapping_test();
127 }
128