1 //
2 // Example of providing a malloc() wrapper that returns a `bounded_ptr`.
3 //
4 // This test serves as some kind of integration test, ensuring that we're
5 // able to convert existing code using raw pointers to using `bounded_ptr`s
6 // without too much hassle. This code was lifted from existing code in XNU,
7 // and the variable names were changed to make it more generic.
8 //
9 
10 #include <libkern/c++/bounded_ptr.h>
11 #include <cstddef>
12 #include <cstdint>
13 #include <cstdlib>
14 #include <darwintest.h>
15 #include "test_utils.h"
16 
17 test_bounded_ptr<void>
bounded_malloc(std::size_t size)18 bounded_malloc(std::size_t size)
19 {
20 	void* p = std::malloc(size);
21 	void* end = static_cast<char*>(p) + size;
22 	test_bounded_ptr<void> with_bounds(p, p, end);
23 	return with_bounds;
24 }
25 
26 void
bounded_free(test_bounded_ptr<void> ptr)27 bounded_free(test_bounded_ptr<void> ptr)
28 {
29 	std::free(ptr.discard_bounds());
30 }
31 
32 struct SomeType {
33 	std::uint32_t idx;
34 };
35 
36 // Pretend that those functions are already part of the code base being
37 // transitioned over to `bounded_ptr`s, and we can't change their signature.
38 // The purpose of having those functions is to make sure that we're able to
39 // integrate into existing code bases with decent ease.
40 void
use(SomeType *)41 use(SomeType*)
42 {
43 }
44 void
require(bool condition)45 require(bool condition)
46 {
47 	if (!condition) {
48 		std::exit(EXIT_FAILURE);
49 	}
50 }
51 
52 T_DECL(example_malloc, "bounded_ptr.example.malloc", T_META_TAG_VM_PREFERRED) {
53 	test_bounded_ptr<SomeType> array = nullptr;
54 	std::uint32_t count = 100;
55 	std::uint32_t alloc_size = count * sizeof(SomeType);
56 
57 	// (1) must use a bounded version of malloc
58 	// (2) must use a reinterpret_pointer_cast to go from void* to SomeType*
59 	array = libkern::reinterpret_pointer_cast<SomeType>(bounded_malloc(alloc_size));
60 
61 	require(array != nullptr); // use != nullptr instead of relying on implicit conversion to bool
62 	use(array.discard_bounds()); // must manually discard bounds here
63 
64 	for (std::uint32_t i = 0; i < count; i++) {
65 		std::uint32_t& idx = array[i].idx;
66 		idx = i;
67 		use(&array[idx]);
68 	}
69 
70 	if (array) {
71 		bounded_free(array); // must use a bounded version of free
72 	}
73 
74 	T_PASS("bounded_ptr.example.malloc test done");
75 }
76