1libtalloc_bestpractices(3)          talloc          libtalloc_bestpractices(3)
2
3
4

NAME

6       libtalloc_bestpractices - Chapter 7: Best practises The following
7       sections contain several best practices and good manners that were
8       found by the Samba and SSSD developers over the years. These will help
9       you to write code which is better, easier to debug and with as few
10       (hopefully none) memory leaks as possible.
11

Keep the context hierarchy steady

13       The talloc is a hierarchy memory allocator. The hierarchy nature is
14       what makes the programming more error proof. It makes the memory easier
15       to manage and to free. Therefore, the first thing we should have on our
16       mind is: always project your data structures into the talloc context
17       hierarchy.
18
19       That means if we have a structure, we should always use it as a parent
20       context for its elements. This way we will not encounter any troubles
21       when freeing the structure or when changing its parent. The same rule
22       applies for arrays.
23
24       For example, the structure user from section Hierarchy of talloc
25       context should be created with the context hierarchy illustrated on the
26       next image.
27

Every function should use its own context

29       It is a good practice to create a temporary talloc context at the
30       function beginning and free the context just before the return
31       statement. All the data must be allocated on this context or on its
32       children. This ensures that no memory leaks are created as long as we
33       do not forget to free the temporary context.
34
35       This pattern applies to both situations - when a function does not
36       return any dynamically allocated value and when it does. However, it
37       needs a little extension for the latter case.
38
39   Functions that do not return any dynamically allocated
40       value
41
42       If the function does not return any value created on the heap, we will
43       just obey the aforementioned pattern.
44
45       int bar()
46       {
47         int ret;
48         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
49         if (tmp_ctx == NULL) {
50           ret = ENOMEM;
51           goto done;
52         }
53         /* allocate data on tmp_ctx or on its descendants */
54         ret = EOK;
55       done:
56         talloc_free(tmp_ctx);
57         return ret;
58       }
59
60   Functions returning dynamically allocated values
61       If our function returns any dynamically allocated data, its first
62       parameter should always be the destination talloc context. This context
63       serves as a parent for the output values. But again, we will create the
64       output values as the descendants of the temporary context. If
65       everything goes well, we will change the parent of the output values
66       from the temporary to the destination talloc context.
67
68       This pattern ensures that if an error occurs (e.g. I/O error or
69       insufficient amount of the memory), all allocated data is freed and no
70       garbage appears on the destination context.
71
72       int struct_foo_init(TALLOC_CTX *mem_ctx, struct foo **_foo)
73       {
74         int ret;
75         struct foo *foo = NULL;
76         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
77         if (tmp_ctx == NULL) {
78           ret = ENOMEM;
79           goto done;
80         }
81         foo = talloc_zero(tmp_ctx, struct foo);
82         /* ... */
83         *_foo = talloc_steal(mem_ctx, foo);
84         ret = EOK;
85       done:
86         talloc_free(tmp_ctx);
87         return ret;
88       }
89

Allocate temporary contexts on NULL

91       As it can be seen on the previous listing, instead of allocating the
92       temporary context directly on mem_ctx, we created a new top level
93       context using NULL as the parameter for talloc_new() function. Take a
94       look at the following example:
95
96       char *create_user_filter(TALLOC_CTX *mem_ctx,
97                                uid_t uid, const char *username)
98       {
99         char *filter = NULL;
100         char *sanitized_username = NULL;
101         /* tmp_ctx is a child of mem_ctx */
102         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
103         if (tmp_ctx == NULL) {
104           return NULL;
105         }
106
107         sanitized_username = sanitize_string(tmp_ctx, username);
108         if (sanitized_username == NULL) {
109           talloc_free(tmp_ctx);
110           return NULL;
111         }
112
113         filter = talloc_aprintf(tmp_ctx,"(|(uid=%llu)(uname=%s))",
114                                 uid, sanitized_username);
115         if (filter == NULL) {
116           return NULL; /* tmp_ctx is not freed */ (*@el{lst:tmp-ctx-3:leak}@*)
117         }
118
119         /* filter becomes a child of mem_ctx */
120         filter = talloc_steal(mem_ctx, filter);
121         talloc_free(tmp_ctx);
122         return filter;
123       }
124
125       We forgot to free tmp_ctx before the return statement in the filter ==
126       NULL condition. However, it is created as a child of mem_ctx context
127       and as such it will be freed as soon as the mem_ctx is freed.
128       Therefore, no detectable memory leak is created.
129
130       On the other hand, we do not have any way to access the allocated data
131       and for all we know mem_ctx may exist for the lifetime of our
132       application. For these reasons this should be considered as a memory
133       leak. How can we detect if it is unreferenced but still attached to its
134       parent context? The only way is to notice the mistake in the source
135       code.
136
137       But if we create the temporary context as a top level context, it will
138       not be freed and memory diagnostic tools (e.g. valgrind) are able to do
139       their job.
140

Temporary contexts and the talloc pool

142       If we want to take the advantage of the talloc pool but also keep to
143       the pattern introduced in the previous section, we are unable to do it
144       directly. The best thing to do is to create a conditional build where
145       we can decide how do we want to create the temporary context. For
146       example, we can create the following macros:
147
148       #ifdef USE_POOL_CONTEXT
149         #define CREATE_POOL_CTX(ctx, size) talloc_pool(ctx, size)
150         #define CREATE_TMP_CTX(ctx)        talloc_new(ctx)
151       #else
152         #define CREATE_POOL_CTX(ctx, size) talloc_new(ctx)
153         #define CREATE_TMP_CTX(ctx)        talloc_new(NULL)
154       #endif
155
156       Now if our application is under development, we will build it with
157       macro USE_POOL_CONTEXT undefined. This way, we can use memory
158       diagnostic utilities to detect memory leaks.
159
160       The release version will be compiled with the macro defined. This will
161       enable pool contexts and therefore reduce the malloc() calls, which
162       will end up in a little bit faster processing.
163
164       int struct_foo_init(TALLOC_CTX *mem_ctx, struct foo **_foo)
165       {
166         int ret;
167         struct foo *foo = NULL;
168         TALLOC_CTX *tmp_ctx = CREATE_TMP_CTX(mem_ctx);
169         /* ... */
170       }
171
172       errno_t handle_request(TALLOC_CTX mem_ctx)
173       {
174         int ret;
175         struct foo *foo = NULL;
176         TALLOC_CTX *pool_ctx = CREATE_POOL_CTX(NULL, 1024);
177         ret = struct_foo_init(mem_ctx, &foo);
178         /* ... */
179       }
180
181Version 2.0                     Wed Jan 29 2020     libtalloc_bestpractices(3)
Impressum