LCOV - code coverage report
Current view: top level - lib/util - talloc_stack.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 59 80 73.8 %
Date: 2024-04-21 15:09:00 Functions: 7 8 87.5 %

          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             : }

Generated by: LCOV version 1.14