TALLOC(3)

πŸ“¦
version 2.5.0 Β· 5 versions
, in alpine 3.23, alpine 3.24, arch rolling, debian trixie, nixos 26.05, nixpkgs unstable, ubuntu questing, ubuntu resolute, ubuntu stonking

πŸ¦‹ talloc(3) πŸ¦‹

System Administration tools manual page, bestie. Buckle up, because this is the whole talloc glow-up ✨

πŸŽ€ NAME πŸŽ€

talloc - hierarchical reference counted memory pool system with destructors. Periodt.


✨ SYNOPSIS ✨

#include <talloc.h>

🌸 DESCRIPTION 🌸

If you are used to talloc from Samba3 then please read this carefully, as talloc has changed a lot.

The new talloc is a hierarchical, reference counted memory pool system with destructors. Quite a mouthful really, but not too bad once you get used to it. It's giving "fancy but friendly," honestly. πŸ’…

Perhaps the biggest change from Samba3 is that there is no distinction between a "talloc context" and a "talloc pointer". Any pointer returned from talloc() is itself a valid talloc context. This means you can do this:

struct foo *X = talloc(mem_ctx, struct foo);
X->name = talloc_strdup(X, "foo");

and the pointer X->name would be a "child" of the talloc context X which is itself a child of mem_ctx. So if you do talloc_free(mem_ctx) then it is all destroyed, whereas if you do talloc_free(X) then just X and X->name are destroyed, and if you do talloc_free(X->name) then just the name element of X is destroyed.

If you think about this, then what this effectively gives you is an n-ary tree, where you can free any part of the tree with talloc_free().

If you find this confusing, then I suggest you run the testsuite program to watch talloc in action. You may also like to add your own tests to testsuite.c to clarify how some particular situation is handled.


🧚 TALLOC API 🧚

The following is a complete guide to the talloc API. Read it all at least twice. No skipping, bestie. πŸ‘‘

(type *)talloc(const void *ctx, type);

The talloc() macro is the core of the talloc library. It takes a memory ctx and a type, and returns a pointer to a new area of memory of the given type.

The returned pointer is itself a talloc context, so you can use it as the ctx argument to more calls to talloc() if you wish. It's lowkey recursive in the best way.

The returned pointer is a "child" of the supplied context. This means that if you talloc_free() the ctx then the new child disappears as well. Alternatively you can free just the child.

The ctx argument to talloc() can be NULL, in which case a new top level context is created.

void *talloc_size(const void *ctx, size_t size);

The function talloc_size() should be used when you don't have a convenient type to pass to talloc(). Unlike talloc(), it is not type safe (as it returns a void *), so you are on your own for type checking. Highkey "trust yourself" energy, but make good choices.

(typeof(ptr)) talloc_ptrtype(const void *ctx, ptr);

The talloc_ptrtype() macro should be used when you have a pointer and want to allocate memory to point at with this pointer. When compiling with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() and talloc_get_name() will return the current location in the source file. and not the type.

int talloc_free(void *ptr);

The talloc_free() function frees a piece of talloc memory, and all its children. You can call talloc_free() on any pointer returned by talloc().

The return value of talloc_free() indicates success or failure, with 0 returned for success and -1 for failure. The only possible failure condition is if ptr had a destructor attached to it and the destructor returned -1. See talloc_set_destructor() for details on destructors.

If this pointer has an additional parent when talloc_free() is called then the memory is not actually released, but instead the most recently established parent is destroyed. See talloc_reference() for details on establishing additional parents.

For more control on which parent is removed, see talloc_unlink().

talloc_free() operates recursively on its children.

From the 2.0 version of talloc, as a special case, talloc_free() is refused on pointers that have more than one parent, as talloc would have no way of knowing which parent should be removed. To free a pointer that has more than one parent please use talloc_unlink().

To help you find problems in your code caused by this behaviour, if you do try and free a pointer with more than one parent then the talloc logging function will be called to give output like this:

ERROR: talloc_free with references at some_dir/source/foo.c:123
 reference at some_dir/source/other.c:325
 reference at some_dir/source/third.c:121

Please see the documentation for talloc_set_log_fn() and talloc_set_log_stderr() for more information on talloc logging functions.

void *talloc_reference(const void *ctx, const void *ptr);

The talloc_reference() function makes ctx an additional parent of ptr.

The return value of talloc_reference() is always the original pointer ptr, unless talloc ran out of memory in creating the reference in which case it will return NULL (each additional reference consumes around 48 bytes of memory on intel x86 platforms).

If ptr is NULL, then the function is a no-op, and simply returns NULL.

After creating a reference you can free it in one of the following ways:

  • you can talloc_free() any parent of the original pointer. That will reduce the number of parents of this pointer by 1, and will cause this pointer to be freed if it runs out of parents.
  • you can talloc_free() the pointer itself if it has at maximum one parent. This behaviour has been changed since the release of version 2.0. Further information in the description of talloc_free.

For more control on which parent to remove, see talloc_unlink().

The talloc_unlink() function removes a specific parent from ptr. The ctx passed must either be a context used in talloc_reference() with this pointer, or must be a direct parent of ptr.

Note that if the parent has already been removed using talloc_free() then this function will fail and will return -1. Likewise, if ptr is NULL, then the function will make no modifications and return -1.

Usually you can just use talloc_free() instead of talloc_unlink(), but sometimes it is useful to have the additional control on which parent is removed.

void talloc_set_destructor(const void *ptr, int (*destructor)(void *));

The function talloc_set_destructor() sets the destructor for the pointer ptr. A destructor is a function that is called when the memory used by a pointer is about to be released. The destructor receives ptr as an argument, and should return 0 for success and -1 for failure.

The destructor can do anything it wants to, including freeing other pieces of memory. A common use for destructors is to clean up operating system resources (such as open file descriptors) contained in the structure the destructor is placed on.

You can only place one destructor on a pointer. If you need more than one destructor then you can create a zero-length child of the pointer and place an additional destructor on that.

To remove a destructor call talloc_set_destructor() with NULL for the destructor.

If your destructor attempts to talloc_free() the pointer that it is the destructor for then talloc_free() will return -1 and the free will be ignored. This would be a pointless operation anyway, as the destructor is only called when the memory is just about to go away.

int talloc_increase_ref_count(const void *ptr);

The talloc_increase_ref_count(ptr) function is exactly equivalent to:

talloc_reference(NULL, ptr);

You can use either syntax, depending on which you think is clearer in your code.

It returns 0 on success and -1 on failure.

size_t talloc_reference_count(const void *ptr);

Return the number of references to the pointer.

void talloc_set_name(const void *ptr, const char *fmt, ...);

Each talloc pointer has a "name". The name is used principally for debugging purposes, although it is also possible to set and get the name on a pointer as a way of "marking" pointers in your code.

The main use for names on pointer is for "talloc reports". See talloc_report_depth_cb(), talloc_report_depth_file(), talloc_report(), talloc_report() and talloc_report_full() for details. Also see talloc_enable_leak_report() and talloc_enable_leak_report_full().

The talloc_set_name() function allocates memory as a child of the pointer. It is logically equivalent to:

talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...));

Note that multiple calls to talloc_set_name() will allocate more memory without releasing the name. All of the memory is released when the ptr is freed using talloc_free().

void talloc_set_name_const(const void *ptr, const char *name);

The function talloc_set_name_const() is just like talloc_set_name(), but it takes a string constant, and is much faster. It is extensively used by the "auto naming" macros, such as talloc_p().

This function does not allocate any memory. It just copies the supplied pointer into the internal representation of the talloc ptr. This means you must not pass a name pointer to memory that will disappear before ptr is freed with talloc_free().

void *talloc_named(const void *ctx, size_t size, const char *fmt, ...);

The talloc_named() function creates a named talloc pointer. It is equivalent to:

ptr = talloc_size(ctx, size);
talloc_set_name(ptr, fmt, ....);

void *talloc_named_const(const void *ctx, size_t size, const char *name);

This is equivalent to:

ptr = talloc_size(ctx, size);
talloc_set_name_const(ptr, name);

const char *talloc_get_name(const void *ptr);

This returns the current name for the given talloc pointer, ptr. See talloc_set_name() for details.

void *talloc_init(const char *fmt, ...);

This function creates a zero length named talloc context as a top level context. It is equivalent to:

talloc_named(NULL, 0, fmt, ...);

void *talloc_new(void *ctx);

This is a utility macro that creates a new memory context hanging off an existing context, automatically naming it "talloc_new: __location__" where __location__ is the source line it is called from. It is particularly useful for creating a new temporary working context.

(type *)talloc_realloc(const void *ctx, void *ptr, type, count);

The talloc_realloc() macro changes the size of a talloc pointer. It has the following equivalences:

talloc_realloc(ctx, NULL, type, 1) ==> talloc(ctx, type);
talloc_realloc(ctx, ptr, type, 0)  ==> talloc_free(ptr);

The ctx argument is only used if ptr is not NULL, otherwise it is ignored.

talloc_realloc() returns the new pointer, or NULL on failure. The call will fail either due to a lack of memory, or because the pointer has more than one parent (see talloc_reference()).

void *talloc_realloc_size(const void *ctx, void *ptr, size_t size);

the talloc_realloc_size() function is useful when the type is not known so the type-safe talloc_realloc() cannot be used.

TYPE *talloc_steal(const void *new_ctx, const TYPE *ptr);

okay so talloc_steal() is the "you're my kid now πŸ’–" function β€” it changes the parent context of a talloc pointer. The classic use case: the context this pointer is currently a child of is about to get freed, and bestie, you are NOT ready to let that memory go, so you move it somewhere safer to live longer.

It returns the same pointer you handed it. no drama, no failure modes, it just works ✨.

⚠️ okay now the serious bit: if you're not careful with talloc_steal(), you can lowkey create loops in the parent/child relationship. And if you do that? No guarantees are provided as to your sanity or the safety of your data. like... none. zero. good luck πŸ’€

Also super important: if you call talloc_steal() on a pointer that has more than one parent, the result is ambiguous. Talloc will choose to remove the parent that is currently indicated by talloc_parent() and replace it with the chosen parent. And it will also throw a message like this at you through the talloc logging functions:

WARNING: talloc_steal with references at some_dir/source/foo.c:123
   reference at some_dir/source/other.c:325
   reference at some_dir/source/third.c:121

If you want to unambiguously change the parent of a pointer, go see talloc_reparent() instead 🫢. And for more info on talloc logging, the talloc_set_log_fn() docs got you.


TYPE *talloc_reparent(const void *old_parent, const void *new_parent, const TYPE *ptr);

talloc_reparent() also changes the parent context of a talloc pointer β€” same energy as the one above, same motivation: the context it's currently a child of is about to be freed and you want to keep the memory around longer.

It returns the pointer you passed it. It does not have any failure modes. slay, consistent queen πŸ‘‘

The difference between talloc_reparent() and talloc_steal()? talloc_reparent() lets you specify which parent you want to change πŸ’…. That's the whole point β€” it's the move when a pointer has multiple parents via references and you need to be precise about which one you're swapping out.


TYPE *talloc_move(const void *new_ctx, TYPE **ptr);

talloc_move() is just a lil wrapper around talloc_steal() that zeros the source pointer after the move. Why does that matter? Because it dodges a whole genre of bugs where a programmer leaves a pointer sitting in two structures at once and then uses it from the old structure after it's already been moved to a new one 😭. Zeroing it means the old slot can't betray you.


size_t talloc_total_size(const void *ptr);

returns the total size in bytes used by this pointer and all of its child pointers. Mostly useful for debugging, ngl πŸ¦‹.

You can pass NULL, but it'll only give you a meaningful result if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called first.


size_t talloc_total_blocks(const void *ptr);

same vibe, different number: this one returns the total memory block count used by this pointer and all its child pointers. Mostly useful for debugging ✨.

And yes, passing NULL is allowed, but again β€” only meaningful if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called.


void talloc_report(const void *ptr, FILE *f);

talloc_report()</purl> prints a summary report of all the memory used by ptr. You get one line of report for each immediate child of ptr, showing the total memory and the number of blocks used by that child. cute and concise πŸŽ€.

You can pass NULL for the pointer, in which case the report is printed for the top level memory context β€” but only if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called.


void talloc_report_full(const void *ptr, FILE *f);

this one is talloc_report() but extra β€” a more detailed report πŸ‘‘. It recursively prints the entire tree of memory referenced by the pointer. References in the tree get shown by giving the name of the pointer that is referenced.

Same deal as before: you can pass NULL for the pointer, in which case a report is printed for the top level memory context, but only if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called.

void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
                            void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *priv),
                            void *priv);

This is the flexible one β€” more flexible than talloc_report(), fr fr. It will recursively call the callback for the entire tree of memory referenced by the pointer. References in the tree come through with is_ref = 1 along with the pointer that is referenced 🌈.

You can pass NULL for the pointer, in which case a report is printed for the top level memory context, but (say it with me) only if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called.

The recursion stops when depth >= max_depth. And max_depth = -1 means it only stops at leaf nodes πŸƒ.

void talloc_report_depth_file(const void *ptr, int depth, int max_depth,
                              FILE *f);

Another flexible report, more flexible than talloc_report() β€” this one lets you specify the depth and max_depth yourself πŸ’….


void talloc_enable_leak_report(void);

this enables calling talloc_report(NULL, stderr) when the program exits. In Samba4 it's switched on with the --leak-report command line option.

For it to actually be useful, you have to call this function before any other talloc function, because it establishes a "null context" that acts as the top of the tree. If you don't call it first, then passing NULL to talloc_report() or talloc_report_full() won't give you the full tree printout 😭. order matters bestie!!

Here's a typical talloc report for you:

talloc report on 'null_context' (total 267 bytes in 15 blocks)
libcli/auth/spnego_parse.c:55  contains   31 bytes in   2 blocks
libcli/auth/spnego_parse.c:55  contains   31 bytes in   2 blocks
iconv(UTF8,CP850)              contains   42 bytes in   2 blocks
libcli/auth/spnego_parse.c:55  contains   31 bytes in   2 blocks
iconv(CP850,UTF8)              contains   42 bytes in   2 blocks
iconv(UTF8,UTF-16LE)           contains   45 bytes in   2 blocks
iconv(UTF-16LE,UTF8)           contains   45 bytes in   2 blocks

void talloc_enable_leak_report_full(void);

same concept, bigger energy: this enables calling talloc_report_full(NULL, stderr) when the program exits 🌸. In Samba4 it's turned on with the --leak-report-full command line option.

And literally same warning as above: for it to be useful, call it before any other talloc function so the "null context" that acts as the top of the tree actually exists. Skip that step and passing NULL to talloc_report() or talloc_report_full() will not hand you the full tree printout. πŸ™ˆ

Here's a typical full report:

full talloc report on 'root' (total 18 bytes in 8 blocks)
p1               contains     18 bytes in   7 blocks (ref 0)
    r1               contains     13 bytes in   2 blocks (ref 0)
        reference to: p2
    p2               contains      1 bytes in   1 blocks (ref 1)
    x3               contains      1 bytes in   1 blocks (ref 0)
    x2               contains      1 bytes in   1 blocks (ref 0)
    x1               contains      1 bytes in   1 blocks (ref 0)

(type *)talloc_zero(const void *ctx, type);

the talloc_zero() macro is literally equivalent to:

ptr = talloc(ctx, type);
if (ptr) memset(ptr, 0, sizeof(type));

zeroed memory the moment you get it, no extra steps ✨.


void *talloc_zero_size(const void *ctx, size_t size)

talloc_zero_size() is your girl when you don't have a known type 🫢. same zeroing energy, you just hand it a size instead.


void *talloc_memdup(const void *ctx, const void *p, size_t size);

talloc_memdup() is equivalent to:

ptr = talloc_size(ctx, size);
if (ptr) memcpy(ptr, p, size);

copy-paste but make it talloc πŸ’–.


char *talloc_strdup(const void *ctx, const char *p);

talloc_strdup() is equivalent to:

ptr = talloc_size(ctx, strlen(p)+1);
if (ptr) memcpy(ptr, p, strlen(p)+1);

BONUS: this function sets the name of the new pointer to the passed string. Which is equivalent to:

talloc_set_name_const(ptr, ptr)

so your debug output is gonna be readable, iconic πŸŽ€.


char *talloc_strndup(const void *t, const char *p, size_t n);

talloc_strndup() is the talloc equivalent of the C library function strndup(3) πŸ‘‘.

This function sets the name of the new pointer to the passed string. Which is equivalent to:

talloc_set_name_const(ptr, ptr)

char *talloc_vasprintf(const void *t, const char *fmt, va_list ap);

talloc_vasprintf() is the talloc equivalent of the C library function vasprintf(3) ✨.

This function sets the name of the new pointer to the new string. Which is equivalent to:

talloc_set_name_const(ptr, ptr)

char *talloc_asprintf(const void *t, const char *fmt, ...);

talloc_asprintf() is the talloc equivalent of the C library function asprintf(3) 🌸.

This function sets the name of the new pointer to the passed string. Which is equivalent to:

talloc_set_name_const(ptr, ptr)

char *talloc_asprintf_append(char *s, const char *fmt, ...);

talloc_asprintf_append() appends the given formatted string to the given string. like, it just keeps going, growth mindset πŸ’….

This function sets the name of the new pointer to the new string. Which is equivalent to:

talloc_set_name_const(ptr, ptr)

(type *)talloc_array(const void *ctx, type, unsigned int count);

the talloc_array() macro is equivalent to:

(type *)talloc_size(ctx, sizeof(type) * count);

…EXCEPT it gives you integer overflow protection on the multiply, returning NULL if the multiply overflows πŸ™Œ. safe math, no cap.


void *talloc_array_size(const void *ctx, size_t size, unsigned int count);

talloc_array_size() is the one for when the type isn't known πŸ¦‹. It operates exactly the same way as talloc_array(), it just takes a size instead of a type.


(typeof(ptr)) talloc_array_ptrtype(const void *ctx, ptr, unsigned int count);

the talloc_ptrtype() macro is the one to reach for when you have a pointer to an array and you want to allocate memory of an array for that pointer to point at. When compiling with gcc >= 3 it's typesafe πŸ‘‘. Heads up though: this is a wrapper of talloc_array_size(), and talloc_get_name() will return the current location in the source file β€” not the type. iykyk.


void *talloc_realloc_fn(const void *ctx, void *ptr, size_t size)

this is the non-macro version of talloc_realloc(), which is handy because libraries sometimes want a realloc function pointer 🫢. A realloc(3) implementation encapsulates the functionality of malloc(3), free(3) and realloc(3) all in one call β€” which is exactly why being able to pass around a single function pointer is so useful.


void *talloc_autofree_context(void);

talloc_autofree_context() is a handy lil utility that returns a talloc context which gets automatically freed on program exit 🌈. Use it to cut down the noise in your memory leak reports. chef's kiss.


void *talloc_check_name(const void *ptr, const char *name);

this one checks if a pointer has the specified name. If it does, the pointer gets returned to you πŸ’–. If it doesn't, you get NULL. that's the whole bit.


(type *)talloc_get_type(const void *ptr, type);

this macro lets you do type checking on talloc pointers, and it's especially useful for void* private pointers πŸ‘‘. It's equivalent to this:

  • talloc_check_name β€” the whole point is that it hands you back a pointer, all typed up and ready:
(type *)talloc_check_name(ptr, #type)

  • talloc_set_type(const void *ptr, type); ✨ β€” this macro straight up forces a pointer's name to be a specific type, no debate. It's the glow-up bestie of talloc_get_type(), and together they do the type-checking thing on void* pointers, because those little guys have no idea what they are. It's lowkey equivalent to this:
talloc_set_name_const(ptr, #type)

  • talloc_set_log_fn(void (*log_fn)(const char *message)); β€” sets the logging function talloc calls when it wants to yell about warnings and errors. And bestie, key detail: by default talloc prints nothing. Zero warnings, zero errors, silence. So if you want receipts, you gotta hand it a function.

  • talloc_set_log_stderr(void); β€” the lazy-girl option πŸŽ€. One call and talloc's log function is pointed straight at stderr, so all those log messages just show up in your terminal. No custom function needed, no thoughts, no effort.

πŸ”₯ PERFORMANCE πŸ”₯

Okay so real talk, because we stay honest in this house: every single extra feature talloc(3) has over malloc(3) does come at a price. Nothing in life is free, not even memory management πŸ’….

There's a performance test in Samba that measures talloc() against malloc(), and ngl the results are what they are β€” talloc() clocks in at about 50% slower than malloc() (that's on an AMD Ryzen 9 3900X, periodt).

But for Samba? It's so worth it. The huge reduction in code complexity they get from using talloc makes the tradeoff make total sense, especially because the total overhead of talloc/malloc in Samba is already pretty small. It's giving "pay a little, save your whole codebase" energy. 🫢


πŸ‘€ SEE ALSO πŸ‘€

go read these next bestie:


πŸ’– AUTHOR πŸ’–

The original Samba software and related utilities were created by Andrew Tridgell. Samba is now developed by the Samba Team as an Open Source project, in a way that's similar to how the Linux kernel is developed. Community-built, iconic behavior, no cap. πŸ‘‘


Copyright (C) Andrew Tridgell 2004

This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/.


okay that's the whole page, and yes I know we went from type checking to license law in like four paragraphs. range. πŸ’€

and that's a wrap on TALLOC(3) bestie!! 🌈✨ this one shipped with Samba 4.0, dated 2015-04-10, and it's filed under TALLOC(3) in your friendly neighborhood man pages. go forth and allocate responsibly, mwah πŸ’…πŸ’–πŸ¦‹