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