Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Implement a stack of talloc contexts
4 : Copyright (C) Volker Lendecke 2007
5 : Copyright (C) Jeremy Allison 2009 - made thread safe.
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 2 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program; if not, write to the Free Software
19 : Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 : */
21 :
22 : /*
23 : * Implement a stack of talloc frames.
24 : *
25 : * When a new talloc stackframe is allocated with talloc_stackframe(), then
26 : * the TALLOC_CTX returned with talloc_tos() is reset to that new
27 : * frame. Whenever that stack frame is TALLOC_FREE()'ed, then the reverse
28 : * happens: The previous talloc_tos() is restored.
29 : *
30 : * This API is designed to be robust in the sense that if someone forgets to
31 : * TALLOC_FREE() a stackframe, then the next outer one correctly cleans up and
32 : * resets the talloc_tos().
33 : *
34 : * This robustness feature means that we can't rely on a linked list with
35 : * talloc destructors because in a hierarchy of talloc destructors the parent
36 : * destructor is called before its children destructors. The child destructor
37 : * called after the parent would set the talloc_tos() to the wrong value.
38 : */
39 :
40 : #include "replace.h"
41 : #include <talloc.h>
42 : #include "lib/util/talloc_stack.h"
43 : #include "lib/util/smb_threads.h"
44 : #include "lib/util/smb_threads_internal.h"
45 : #include "lib/util/fault.h"
46 : #include "lib/util/debug.h"
47 :
48 : struct talloc_stackframe {
49 : int talloc_stacksize;
50 : int talloc_stack_arraysize;
51 : TALLOC_CTX **talloc_stack;
52 : };
53 :
54 : /*
55 : * In the single threaded case this is a pointer
56 : * to the global talloc_stackframe. In the MT-case
57 : * this is the pointer to the thread-specific key
58 : * used to look up the per-thread talloc_stackframe
59 : * pointer.
60 : */
61 :
62 : static void *global_ts;
63 :
64 : /* Variable to ensure TLS value is only initialized once. */
65 : static smb_thread_once_t ts_initialized = SMB_THREAD_ONCE_INIT;
66 :
67 12015 : static void talloc_stackframe_init(void * unused)
68 : {
69 12015 : if (SMB_THREAD_CREATE_TLS("talloc_stackframe", global_ts)) {
70 0 : smb_panic("talloc_stackframe_init create_tls failed");
71 : }
72 12015 : }
73 :
74 12015 : static struct talloc_stackframe *talloc_stackframe_create(void)
75 : {
76 : #if defined(PARANOID_MALLOC_CHECKER)
77 : #ifdef calloc
78 : #undef calloc
79 : #endif
80 : #endif
81 12015 : struct talloc_stackframe *ts = (struct talloc_stackframe *)calloc(
82 : 1, sizeof(struct talloc_stackframe));
83 : #if defined(PARANOID_MALLOC_CHECKER)
84 : #define calloc(n, s) __ERROR_DONT_USE_MALLOC_DIRECTLY
85 : #endif
86 :
87 12015 : if (!ts) {
88 0 : smb_panic("talloc_stackframe_init malloc failed");
89 : }
90 :
91 12015 : SMB_THREAD_ONCE(&ts_initialized, talloc_stackframe_init, NULL);
92 :
93 12015 : if (SMB_THREAD_SET_TLS(global_ts, ts)) {
94 0 : smb_panic("talloc_stackframe_init set_tls failed");
95 : }
96 12015 : return ts;
97 : }
98 :
99 21508772 : static int talloc_pop(TALLOC_CTX *frame)
100 : {
101 21508772 : struct talloc_stackframe *ts =
102 21508772 : (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts);
103 : size_t blocks;
104 : int i;
105 :
106 : /* Catch lazy frame-freeing. */
107 21508772 : if (ts->talloc_stack[ts->talloc_stacksize-1] != frame) {
108 0 : DEBUG(0, ("Freed frame %s, expected %s.\n",
109 : talloc_get_name(frame),
110 : talloc_get_name(ts->talloc_stack
111 : [ts->talloc_stacksize-1])));
112 : #ifdef DEVELOPER
113 0 : smb_panic("Frame not freed in order.");
114 : #endif
115 : }
116 :
117 21508772 : for (i=0; i<10; i++) {
118 :
119 : /*
120 : * We have to free our children first, calling all
121 : * destructors. If a destructor hanging deeply off
122 : * "frame" uses talloc_tos() itself while freeing the
123 : * toplevel frame, we panic because that nested
124 : * talloc_tos() in the destructor does not find a
125 : * stackframe anymore.
126 : *
127 : * Do it in a loop up to 10 times as the destructors
128 : * might use more of talloc_tos().
129 : */
130 :
131 21508772 : talloc_free_children(frame);
132 :
133 21508772 : blocks = talloc_total_blocks(frame);
134 21508772 : if (blocks == 1) {
135 21508772 : break;
136 : }
137 : }
138 :
139 21508772 : if (blocks != 1) {
140 0 : DBG_WARNING("Left %zu blocks after %i "
141 : "talloc_free_children(frame) calls\n",
142 : blocks, i);
143 : }
144 :
145 21508772 : for (i=ts->talloc_stacksize-1; i>0; i--) {
146 15937975 : if (frame == ts->talloc_stack[i]) {
147 15937975 : break;
148 : }
149 0 : TALLOC_FREE(ts->talloc_stack[i]);
150 : }
151 :
152 21508772 : ts->talloc_stack[i] = NULL;
153 21508772 : ts->talloc_stacksize = i;
154 21508772 : return 0;
155 : }
156 :
157 : /*
158 : * Create a new talloc stack frame.
159 : *
160 : * When free'd, it frees all stack frames that were created after this one and
161 : * not explicitly freed.
162 : */
163 :
164 21530289 : static TALLOC_CTX *talloc_stackframe_internal(const char *location,
165 : size_t poolsize)
166 : {
167 : TALLOC_CTX **tmp, *top;
168 21530289 : struct talloc_stackframe *ts =
169 21530289 : (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts);
170 :
171 21530289 : if (ts == NULL) {
172 12015 : ts = talloc_stackframe_create();
173 : }
174 :
175 21530289 : if (ts->talloc_stack_arraysize < ts->talloc_stacksize + 1) {
176 34497 : tmp = talloc_realloc(NULL, ts->talloc_stack, TALLOC_CTX *,
177 : ts->talloc_stacksize + 1);
178 34497 : if (tmp == NULL) {
179 0 : goto fail;
180 : }
181 34497 : ts->talloc_stack = tmp;
182 34497 : ts->talloc_stack_arraysize = ts->talloc_stacksize + 1;
183 : }
184 :
185 21530289 : if (poolsize) {
186 731882 : top = talloc_pool(ts->talloc_stack, poolsize);
187 : } else {
188 : TALLOC_CTX *parent;
189 : /* We chain parentage, so if one is a pool we draw from it. */
190 20798407 : if (ts->talloc_stacksize == 0) {
191 5586627 : parent = ts->talloc_stack;
192 : } else {
193 15211780 : parent = ts->talloc_stack[ts->talloc_stacksize-1];
194 : }
195 20798407 : top = talloc_new(parent);
196 : }
197 :
198 21530289 : if (top == NULL) {
199 0 : goto fail;
200 : }
201 21530289 : talloc_set_name_const(top, location);
202 21530289 : talloc_set_destructor(top, talloc_pop);
203 :
204 21530289 : ts->talloc_stack[ts->talloc_stacksize++] = top;
205 21530289 : return top;
206 :
207 0 : fail:
208 0 : smb_panic("talloc_stackframe failed");
209 : return NULL;
210 : }
211 :
212 20798407 : TALLOC_CTX *_talloc_stackframe(const char *location)
213 : {
214 20798407 : return talloc_stackframe_internal(location, 0);
215 : }
216 :
217 731882 : TALLOC_CTX *_talloc_stackframe_pool(const char *location, size_t poolsize)
218 : {
219 731882 : return talloc_stackframe_internal(location, poolsize);
220 : }
221 :
222 : /*
223 : * Get us the current top of the talloc stack.
224 : */
225 :
226 3886045 : TALLOC_CTX *_talloc_tos(const char *location)
227 : {
228 3886045 : struct talloc_stackframe *ts =
229 3886045 : (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts);
230 :
231 3886045 : if (ts == NULL || ts->talloc_stacksize == 0) {
232 0 : _talloc_stackframe(location);
233 0 : ts = (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts);
234 0 : DEBUG(0, ("no talloc stackframe at %s, leaking memory\n",
235 : location));
236 : #ifdef DEVELOPER
237 0 : smb_panic("No talloc stackframe");
238 : #endif
239 : }
240 :
241 3886045 : return ts->talloc_stack[ts->talloc_stacksize-1];
242 : }
243 :
244 : /*
245 : * return true if a talloc stackframe exists
246 : * this can be used to prevent memory leaks for code that can
247 : * optionally use a talloc stackframe (eg. nt_errstr())
248 : */
249 :
250 206031 : bool talloc_stackframe_exists(void)
251 : {
252 206031 : struct talloc_stackframe *ts =
253 206031 : (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts);
254 :
255 206031 : if (ts == NULL || ts->talloc_stacksize == 0) {
256 131 : return false;
257 : }
258 205900 : return true;
259 : }
|