1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fbl/alloc_checker.h>
6 
7 #include <zircon/assert.h>
8 #include <zircon/compiler.h>
9 #include <new>
10 
11 namespace fbl {
12 namespace {
13 
14 enum : unsigned {
15     alloc_armed = 1,
16     alloc_ok = 2,
17 };
18 
panic_if_armed(unsigned state)19 void panic_if_armed(unsigned state) {
20 #if LK_DEBUGLEVEL > 1
21     if (state & alloc_armed)
22         ZX_PANIC("AllocChecker::check() needs to be called\n");
23 #endif
24 }
25 
checked(size_t size,AllocChecker * ac,void * mem)26 void* checked(size_t size, AllocChecker* ac, void* mem) {
27     ac->arm(size, mem != nullptr);
28     return mem;
29 }
30 
31 } // namespace
32 
AllocChecker()33 AllocChecker::AllocChecker()
34     : state_(0u) {
35 }
36 
~AllocChecker()37 AllocChecker::~AllocChecker() {
38     panic_if_armed(state_);
39 }
40 
arm(size_t size,bool result)41 void AllocChecker::arm(size_t size, bool result) {
42     panic_if_armed(state_);
43     state_ = alloc_armed |
44              ((size == 0u) ? alloc_ok : (result ? alloc_ok : 0u));
45 }
46 
check()47 bool AllocChecker::check() {
48     state_ &= ~alloc_armed;
49     return (state_ & alloc_ok) == alloc_ok;
50 }
51 
52 // The std::nothrow_t overloads of operator new and operator new[] are
53 // the standard C++ library interfaces that return nullptr instead of
54 // using exceptions, i.e. the same semantics as malloc.  We define our
55 // checked versions in terms of those rather than calling malloc
56 // directly to maintain the invariant that only allocations done via
57 // new are freed via delete, only allocations done via new[] are freed
58 // via delete[], and only allocations done via the C malloc family
59 // functions are freed via the C free function.  The non-throwing
60 // operator new and operator new[] we call might be trivial ones like
61 // zxcpp's that actually just call malloc, or they might be ones that
62 // enforce this invariant (such as the ASan allocator).
63 
64 } // namespace fbl
65 
66 #if !_KERNEL
67 
operator new(size_t size,fbl::AllocChecker * ac)68 void* operator new(size_t size, fbl::AllocChecker* ac) noexcept {
69     return fbl::checked(size, ac, operator new(size, std::nothrow_t()));
70 }
71 
operator new[](size_t size,fbl::AllocChecker * ac)72 void* operator new[](size_t size, fbl::AllocChecker* ac) noexcept {
73     return fbl::checked(size, ac, operator new[](size, std::nothrow_t()));
74 }
75 
76 #else // _KERNEL
77 
operator new(size_t size,fbl::AllocChecker * ac)78 void* operator new(size_t size, fbl::AllocChecker* ac) noexcept {
79     void* operator new(size_t s, void* caller, const std::nothrow_t&) noexcept;
80     return fbl::checked(size, ac, operator new(size, __GET_CALLER(), std::nothrow_t()));
81 }
82 
operator new[](size_t size,fbl::AllocChecker * ac)83 void* operator new[](size_t size, fbl::AllocChecker* ac) noexcept {
84     void* operator new[](size_t s, void* caller, const std::nothrow_t&) noexcept;
85     return fbl::checked(size, ac, operator new[](size, __GET_CALLER(), std::nothrow_t()));
86 }
87 
88 #endif // !_KERNEL
89