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 file,
5  * 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 /*
11  * The replace-malloc bridge allows bidirectional method calls between
12  * a program and the replace-malloc library that has been loaded for it.
13  * In Firefox, this is used to allow method calls between code in libxul
14  * and code in the replace-malloc library, without libxul needing to link
15  * against that library or vice-versa.
16  *
17  * Subsystems can add methods for their own need. Replace-malloc libraries
18  * can decide to implement those methods or not.
19  *
20  * Replace-malloc libraries can provide such a bridge by implementing
21  * a ReplaceMallocBridge-derived class, and a replace_get_bridge function
22  * returning an instance of that class. The default methods in
23  * ReplaceMallocBridge are expected to return values that callers would
24  * understand as "the bridge doesn't implement this method", so that a
25  * replace-malloc library doesn't have to implement all methods.
26  *
27  * The ReplaceMallocBridge class contains definitions for methods for
28  * all replace-malloc libraries. Each library picks the methods it wants
29  * to reply to in its ReplaceMallocBridge-derived class instance.
30  * All methods of ReplaceMallocBridge must be virtual. Similarly,
31  * anything passed as an argument to those methods must be plain data, or
32  * an instance of a class with only virtual methods.
33  *
34  * Binary compatibility is expected to be maintained, such that a newer
35  * Firefox can be used with an old replace-malloc library, or an old
36  * Firefox can be used with a newer replace-malloc library. As such, only
37  * new virtual methods should be added to ReplaceMallocBridge, and
38  * each change should have a corresponding bump of the mVersion value.
39  * At the same time, each virtual method should have a corresponding
40  * wrapper calling the virtual method on the instance from
41  * ReplaceMallocBridge::Get(), giving it the version the virtual method
42  * was added.
43  *
44  * Parts that are not relevant to the replace-malloc library end of the
45  * bridge are hidden when REPLACE_MALLOC_IMPL is not defined, which is
46  * the case when including replace_malloc.h.
47  */
48 
49 struct ReplaceMallocBridge;
50 
51 #include "mozilla/Types.h"
52 
53 MOZ_BEGIN_EXTERN_C
54 
55 #ifndef REPLACE_MALLOC_IMPL
56 /* Returns the replace-malloc bridge if there is one to be returned. */
57 MFBT_API ReplaceMallocBridge* get_bridge();
58 #endif
59 
60 /* Table of malloc functions.
61  *   e.g. void* (*malloc)(size_t), etc.
62  */
63 #define MALLOC_DECL(name, return_type, ...) \
64   typedef return_type(name ## _impl_t)(__VA_ARGS__);
65 
66 #include "malloc_decls.h"
67 
68 #define MALLOC_DECL(name, return_type, ...) \
69   name ## _impl_t * name;
70 
71 typedef struct {
72 #include "malloc_decls.h"
73 } malloc_table_t;
74 
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  */
88 #define MALLOC_DECL(name, return_type, ...) \
89   return_type (*name ## _hook)(return_type, __VA_ARGS__);
90 #define MALLOC_DECL_VOID(name, ...) \
91   void (*name ## _hook)(__VA_ARGS__);
92 
93 typedef struct {
94 #include "malloc_decls.h"
95   /* Like free_hook, but called before realloc_hook. free_hook is called
96    * instead of not given. */
97   void (*realloc_hook_before)(void* aPtr);
98 } malloc_hook_table_t;
99 
100 MOZ_END_EXTERN_C
101 
102 #ifdef __cplusplus
103 
104 namespace mozilla {
105 namespace dmd {
106 struct DMDFuncs;
107 } // namespace dmd
108 
109 /* Callbacks to register debug file handles for Poison IO interpose.
110  * See Mozilla(|Un)RegisterDebugHandle in xpcom/build/PoisonIOInterposer.h */
111 struct DebugFdRegistry
112 {
113   virtual void RegisterHandle(intptr_t aFd);
114 
115   virtual void UnRegisterHandle(intptr_t aFd);
116 };
117 
118 } // namespace mozilla
119 
120 struct ReplaceMallocBridge
121 {
ReplaceMallocBridgeReplaceMallocBridge122   ReplaceMallocBridge() : mVersion(3) {}
123 
124   /* This method was added in version 1 of the bridge. */
GetDMDFuncsReplaceMallocBridge125   virtual mozilla::dmd::DMDFuncs* GetDMDFuncs() { return nullptr; }
126 
127   /* Send a DebugFdRegistry instance to the replace-malloc library so that
128    * it can register/unregister file descriptors whenever needed. The
129    * instance is valid until the process dies.
130    * This method was added in version 2 of the bridge. */
InitDebugFdReplaceMallocBridge131   virtual void InitDebugFd(mozilla::DebugFdRegistry&) {}
132 
133   /* Register a list of malloc functions and hook functions to the
134    * replace-malloc library so that it can choose to dispatch to them
135    * when needed. The details of what is dispatched when is left to the
136    * replace-malloc library.
137    * Passing a nullptr for either table will unregister a previously
138    * registered table under the same name.
139    * Returns nullptr if registration failed.
140    * If registration succeeded, a table of "pure" malloc functions is
141    * returned. Those "pure" malloc functions won't call hooks.
142    * /!\ Do not rely on registration/unregistration to be instantaneous.
143    * Functions from a previously registered table may still be called for
144    * a brief time after RegisterHook returns.
145    * This method was added in version 3 of the bridge. */
146   virtual const malloc_table_t*
RegisterHookReplaceMallocBridge147   RegisterHook(const char* aName, const malloc_table_t* aTable,
148                const malloc_hook_table_t* aHookTable) { return nullptr; }
149 
150 #ifndef REPLACE_MALLOC_IMPL
151   /* Returns the replace-malloc bridge if its version is at least the
152    * requested one. */
GetReplaceMallocBridge153   static ReplaceMallocBridge* Get(int aMinimumVersion) {
154     static ReplaceMallocBridge* sSingleton = get_bridge();
155     return (sSingleton && sSingleton->mVersion >= aMinimumVersion)
156       ? sSingleton : nullptr;
157   }
158 #endif
159 
160 protected:
161   const int mVersion;
162 };
163 
164 #ifndef REPLACE_MALLOC_IMPL
165 /* Class containing wrappers for calls to ReplaceMallocBridge methods.
166  * Those wrappers need to be static methods in a class because compilers
167  * complain about unused static global functions, and linkers complain
168  * about multiple definitions of non-static global functions.
169  * Using a separate class from ReplaceMallocBridge allows the function
170  * names to be identical. */
171 struct ReplaceMalloc
172 {
173   /* Don't call this method from performance critical code. Use
174    * mozilla::dmd::DMDFuncs::Get() instead, it has less overhead. */
GetDMDFuncsReplaceMalloc175   static mozilla::dmd::DMDFuncs* GetDMDFuncs()
176   {
177     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 1);
178     return singleton ? singleton->GetDMDFuncs() : nullptr;
179   }
180 
InitDebugFdReplaceMalloc181   static void InitDebugFd(mozilla::DebugFdRegistry& aRegistry)
182   {
183     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 2);
184     if (singleton) {
185       singleton->InitDebugFd(aRegistry);
186     }
187   }
188 
189   static const malloc_table_t*
RegisterHookReplaceMalloc190   RegisterHook(const char* aName, const malloc_table_t* aTable,
191                const malloc_hook_table_t* aHookTable)
192   {
193     auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 3);
194     return singleton ? singleton->RegisterHook(aName, aTable, aHookTable)
195                      : nullptr;
196   }
197 };
198 #endif
199 
200 #endif /* __cplusplus */
201 
202 #endif /* replace_malloc_bridge_h */
203