1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef replace_malloc_bridge_h
8 #define replace_malloc_bridge_h
9 
10 // The replace-malloc bridge allows bidirectional method calls between
11 // a program and the replace-malloc library that has been loaded for it.
12 // In Firefox, this is used to allow method calls between code in libxul
13 // and code in the replace-malloc library, without libxul needing to link
14 // against that library or vice-versa.
15 //
16 // Subsystems can add methods for their own need. Replace-malloc libraries
17 // can decide to implement those methods or not.
18 //
19 // Replace-malloc libraries can provide such a bridge by implementing
20 // a ReplaceMallocBridge-derived class, and a replace_get_bridge function
21 // returning an instance of that class. The default methods in
22 // ReplaceMallocBridge are expected to return values that callers would
23 // understand as "the bridge doesn't implement this method", so that a
24 // replace-malloc library doesn't have to implement all methods.
25 //
26 // The ReplaceMallocBridge class contains definitions for methods for
27 // all replace-malloc libraries. Each library picks the methods it wants
28 // to reply to in its ReplaceMallocBridge-derived class instance.
29 // All methods of ReplaceMallocBridge must be virtual. Similarly,
30 // anything passed as an argument to those methods must be plain data, or
31 // an instance of a class with only virtual methods.
32 //
33 // Binary compatibility is expected to be maintained, such that a newer
34 // Firefox can be used with an old replace-malloc library, or an old
35 // Firefox can be used with a newer replace-malloc library. As such, only
36 // new virtual methods should be added to ReplaceMallocBridge, and
37 // each change should have a corresponding bump of the mVersion value.
38 // At the same time, each virtual method should have a corresponding
39 // wrapper calling the virtual method on the instance from
40 // ReplaceMallocBridge::Get(), giving it the version the virtual method
41 // was added.
42 //
43 // Parts that are not relevant to the replace-malloc library end of the
44 // bridge are hidden when REPLACE_MALLOC_IMPL is not defined, which is
45 // the case when including replace_malloc.h.
46 
47 struct ReplaceMallocBridge;
48 
49 #include "mozilla/Types.h"
50 
51 MOZ_BEGIN_EXTERN_C
52 
53 #ifndef REPLACE_MALLOC_IMPL
54 // Returns the replace-malloc bridge if there is one to be returned.
55 MFBT_API ReplaceMallocBridge* get_bridge();
56 #endif
57 
58 // Table of malloc functions.
59 //   e.g. void* (*malloc)(size_t), etc.
60 
61 #define MALLOC_DECL(name, return_type, ...) \
62   typedef return_type(name##_impl_t)(__VA_ARGS__);
63 
64 #include "malloc_decls.h"
65 
66 #define MALLOC_DECL(name, return_type, ...) name##_impl_t* name;
67 
68 typedef struct {
69 #include "malloc_decls.h"
70 } malloc_table_t;
71 
72 MOZ_END_EXTERN_C
73 
74 #ifdef __cplusplus
75 
76 // Table of malloc hook functions.
77 // Those functions are called with the arguments and results of malloc
78 // functions after they are called.
79 //   e.g. void* (*malloc_hook)(void*, size_t), etc.
80 // They can either return the result they're given, or alter it before
81 // returning it.
82 // The hooks corresponding to functions, like free(void*), that return no
83 // value, don't take an extra argument.
84 // The table must at least contain a pointer for malloc_hook and free_hook
85 // functions. They will be used as fallback if no pointer is given for
86 // other allocation functions, like calloc_hook.
87 namespace mozilla {
88 namespace detail {
89 template <typename R, typename... Args>
90 struct AllocHookType {
91   using Type = R (*)(R, Args...);
92 };
93 
94 template <typename... Args>
95 struct AllocHookType<void, Args...> {
96   using Type = void (*)(Args...);
97 };
98 
99 }  // namespace detail
100 }  // namespace mozilla
101 
102 #  define MALLOC_DECL(name, return_type, ...)                                 \
103     typename mozilla::detail::AllocHookType<return_type, ##__VA_ARGS__>::Type \
104         name##_hook;
105 
106 typedef struct {
107 #  include "malloc_decls.h"
108   // Like free_hook, but called before realloc_hook. free_hook is called
109   // instead of not given.
110   void (*realloc_hook_before)(void* aPtr);
111 } malloc_hook_table_t;
112 
113 namespace mozilla {
114 namespace dmd {
115 struct DMDFuncs;
116 }  // namespace dmd
117 
118 namespace phc {
119 class AddrInfo;
120 }  // namespace phc
121 
122 // Callbacks to register debug file handles for Poison IO interpose.
123 // See Mozilla(|Un)RegisterDebugHandle in xpcom/build/PoisonIOInterposer.h
124 struct DebugFdRegistry {
125   virtual void RegisterHandle(intptr_t aFd);
126 
127   virtual void UnRegisterHandle(intptr_t aFd);
128 };
129 
130 }  // namespace mozilla
131 
132 struct ReplaceMallocBridge {
133   ReplaceMallocBridge() : mVersion(4) {}
134 
135   // This method was added in version 1 of the bridge.
136   virtual mozilla::dmd::DMDFuncs* GetDMDFuncs() { return nullptr; }
137 
138   // Send a DebugFdRegistry instance to the replace-malloc library so that
139   // it can register/unregister file descriptors whenever needed. The
140   // instance is valid until the process dies.
141   // This method was added in version 2 of the bridge.
142   virtual void InitDebugFd(mozilla::DebugFdRegistry&) {}
143 
144   // Register a list of malloc functions and hook functions to the
145   // replace-malloc library so that it can choose to dispatch to them
146   // when needed. The details of what is dispatched when is left to the
147   // replace-malloc library.
148   // Passing a nullptr for either table will unregister a previously
149   // registered table under the same name.
150   // Returns nullptr if registration failed.
151   // If registration succeeded, a table of "pure" malloc functions is
152   // returned. Those "pure" malloc functions won't call hooks.
153   // /!\ Do not rely on registration/unregistration to be instantaneous.
154   // Functions from a previously registered table may still be called for
155   // a brief time after RegisterHook returns.
156   // This method was added in version 3 of the bridge.
157   virtual const malloc_table_t* RegisterHook(
158       const char* aName, const malloc_table_t* aTable,
159       const malloc_hook_table_t* aHookTable) {
160     return nullptr;
161   }
162 
163   // If this is a PHC-handled address, return true, and if an AddrInfo is
164   // provided, fill in all of its fields. Otherwise, return false and leave
165   // AddrInfo unchanged.
166   // This method was added in version 4 of the bridge.
167   virtual bool IsPHCAllocation(const void*, mozilla::phc::AddrInfo*) {
168     return false;
169   }
170 
171   // Disable PHC allocations on the current thread. Only useful for tests. Note
172   // that PHC deallocations will still occur as needed.
173   // This method was added in version 4 of the bridge.
174   virtual void DisablePHCOnCurrentThread() {}
175 
176   // Re-enable PHC allocations on the current thread. Only useful for tests.
177   // This method was added in version 4 of the bridge.
178   virtual void ReenablePHCOnCurrentThread() {}
179 
180   // Test whether PHC allocations are enabled on the current thread. Only
181   // useful for tests.
182   // This method was added in version 4 of the bridge.
183   virtual bool IsPHCEnabledOnCurrentThread() { return false; }
184 
185 #  ifndef REPLACE_MALLOC_IMPL
186   // Returns the replace-malloc bridge if its version is at least the
187   // requested one.
188   static ReplaceMallocBridge* Get(int aMinimumVersion) {
189     static ReplaceMallocBridge* sSingleton = get_bridge();
190     return (sSingleton && sSingleton->mVersion >= aMinimumVersion) ? sSingleton
191                                                                    : nullptr;
192   }
193 #  endif
194 
195  protected:
196   const int mVersion;
197 };
198 
199 #  ifndef REPLACE_MALLOC_IMPL
200 // Class containing wrappers for calls to ReplaceMallocBridge methods.
201 // Those wrappers need to be static methods in a class because compilers
202 // complain about unused static global functions, and linkers complain
203 // about multiple definitions of non-static global functions.
204 // Using a separate class from ReplaceMallocBridge allows the function
205 // names to be identical.
206 struct ReplaceMalloc {
207   // Don't call this method from performance critical code. Use
208   // mozilla::dmd::DMDFuncs::Get() instead, it has less overhead.
209   static mozilla::dmd::DMDFuncs* GetDMDFuncs() {
210     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 1);
211     return singleton ? singleton->GetDMDFuncs() : nullptr;
212   }
213 
214   static void InitDebugFd(mozilla::DebugFdRegistry& aRegistry) {
215     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 2);
216     if (singleton) {
217       singleton->InitDebugFd(aRegistry);
218     }
219   }
220 
221   static const malloc_table_t* RegisterHook(
222       const char* aName, const malloc_table_t* aTable,
223       const malloc_hook_table_t* aHookTable) {
224     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 3);
225     return singleton ? singleton->RegisterHook(aName, aTable, aHookTable)
226                      : nullptr;
227   }
228 
229   static bool IsPHCAllocation(const void* aPtr, mozilla::phc::AddrInfo* aOut) {
230     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
231     return singleton ? singleton->IsPHCAllocation(aPtr, aOut) : false;
232   }
233 
234   static void DisablePHCOnCurrentThread() {
235     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
236     if (singleton) {
237       singleton->DisablePHCOnCurrentThread();
238     }
239   }
240 
241   static void ReenablePHCOnCurrentThread() {
242     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
243     if (singleton) {
244       singleton->ReenablePHCOnCurrentThread();
245     }
246   }
247 
248   static bool IsPHCEnabledOnCurrentThread() {
249     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
250     return singleton ? singleton->IsPHCEnabledOnCurrentThread() : false;
251   }
252 };
253 #  endif
254 
255 #endif  // __cplusplus
256 
257 #endif  // replace_malloc_bridge_h
258