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 36156 : static void talloc_stackframe_init(void * unused) 68 : { 69 36156 : if (SMB_THREAD_CREATE_TLS("talloc_stackframe", global_ts)) { 70 0 : smb_panic("talloc_stackframe_init create_tls failed"); 71 : } 72 36156 : } 73 : 74 36156 : 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 36156 : 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 36156 : if (!ts) { 88 0 : smb_panic("talloc_stackframe_init malloc failed"); 89 : } 90 : 91 36156 : SMB_THREAD_ONCE(&ts_initialized, talloc_stackframe_init, NULL); 92 : 93 36156 : if (SMB_THREAD_SET_TLS(global_ts, ts)) { 94 0 : smb_panic("talloc_stackframe_init set_tls failed"); 95 : } 96 36156 : return ts; 97 : } 98 : 99 153853597 : static int talloc_pop(TALLOC_CTX *frame) 100 : { 101 153853597 : struct talloc_stackframe *ts = 102 153853597 : (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts); 103 744177 : size_t blocks; 104 744177 : int i; 105 : 106 : /* Catch lazy frame-freeing. */ 107 153853597 : 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 153853597 : 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 153853597 : talloc_free_children(frame); 132 : 133 153853597 : blocks = talloc_total_blocks(frame); 134 153853597 : if (blocks == 1) { 135 153109420 : break; 136 : } 137 : } 138 : 139 153853597 : 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 153853597 : for (i=ts->talloc_stacksize-1; i>0; i--) { 146 144938732 : if (frame == ts->talloc_stack[i]) { 147 144296320 : break; 148 : } 149 0 : TALLOC_FREE(ts->talloc_stack[i]); 150 : } 151 : 152 153853597 : ts->talloc_stack[i] = NULL; 153 153853597 : ts->talloc_stacksize = i; 154 153853597 : 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 153910811 : static TALLOC_CTX *talloc_stackframe_internal(const char *location, 165 : size_t poolsize) 166 : { 167 745201 : TALLOC_CTX **tmp, *top; 168 153910811 : struct talloc_stackframe *ts = 169 153910811 : (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts); 170 : 171 153910811 : if (ts == NULL) { 172 36156 : ts = talloc_stackframe_create(); 173 : } 174 : 175 153910811 : if (ts->talloc_stack_arraysize < ts->talloc_stacksize + 1) { 176 123161 : tmp = talloc_realloc(NULL, ts->talloc_stack, TALLOC_CTX *, 177 : ts->talloc_stacksize + 1); 178 123161 : if (tmp == NULL) { 179 0 : goto fail; 180 : } 181 123161 : ts->talloc_stack = tmp; 182 123161 : ts->talloc_stack_arraysize = ts->talloc_stacksize + 1; 183 : } 184 : 185 153910811 : if (poolsize) { 186 4872419 : top = talloc_pool(ts->talloc_stack, poolsize); 187 : } else { 188 689000 : TALLOC_CTX *parent; 189 : /* We chain parentage, so if one is a pool we draw from it. */ 190 149038392 : if (ts->talloc_stacksize == 0) { 191 8937944 : parent = ts->talloc_stack; 192 : } else { 193 140100448 : parent = ts->talloc_stack[ts->talloc_stacksize-1]; 194 : } 195 149038392 : top = talloc_new(parent); 196 : } 197 : 198 153910811 : if (top == NULL) { 199 0 : goto fail; 200 : } 201 153910811 : talloc_set_name_const(top, location); 202 153910811 : talloc_set_destructor(top, talloc_pop); 203 : 204 153910811 : ts->talloc_stack[ts->talloc_stacksize++] = top; 205 153910811 : return top; 206 : 207 0 : fail: 208 0 : smb_panic("talloc_stackframe failed"); 209 : return NULL; 210 : } 211 : 212 149038392 : TALLOC_CTX *_talloc_stackframe(const char *location) 213 : { 214 149038392 : return talloc_stackframe_internal(location, 0); 215 : } 216 : 217 4872419 : TALLOC_CTX *_talloc_stackframe_pool(const char *location, size_t poolsize) 218 : { 219 4872419 : return talloc_stackframe_internal(location, poolsize); 220 : } 221 : 222 : /* 223 : * Get us the current top of the talloc stack. 224 : */ 225 : 226 248020177 : TALLOC_CTX *_talloc_tos(const char *location) 227 : { 228 248020177 : struct talloc_stackframe *ts = 229 248020177 : (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts); 230 : 231 248020177 : 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 248020177 : 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 0 : bool talloc_stackframe_exists(void) 251 : { 252 0 : struct talloc_stackframe *ts = 253 0 : (struct talloc_stackframe *)SMB_THREAD_GET_TLS(global_ts); 254 : 255 0 : if (ts == NULL || ts->talloc_stacksize == 0) { 256 0 : return false; 257 : } 258 0 : return true; 259 : }