1libtalloc_bestpractices(3) talloc libtalloc_bestpractices(3)
2
3
4
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
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
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
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
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 Tue Jul 28 2020 libtalloc_bestpractices(3)