lib/util: Fix cppcheck null pointer dereference warning
[metze/samba/wip.git] / lib / util / talloc_report.c
1 /*
2  * talloc_report into a string
3  *
4  * Copyright Volker Lendecke <vl@samba.org> 2015
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "replace.h"
21 #include "talloc_report.h"
22
23 /*
24  * talloc_vasprintf into a buffer that doubles its size. The real string
25  * length is maintained in "pstr_len".
26  */
27
28 static char *talloc_vasprintf_append_largebuf(char *buf, ssize_t *pstr_len,
29                                               const char *fmt, va_list ap)
30                                               PRINTF_ATTRIBUTE(3,0);
31
32 static char *talloc_vasprintf_append_largebuf(char *buf, ssize_t *pstr_len,
33                                               const char *fmt, va_list ap)
34 {
35         ssize_t str_len = *pstr_len;
36         size_t buflen, needed, space = 0;
37         char *start = NULL, *tmpbuf = NULL;
38         va_list ap2;
39         int printlen;
40
41         if (str_len == -1) {
42                 return NULL;
43         }
44         if (buf == NULL) {
45                 return NULL;
46         }
47         if (fmt == NULL) {
48                 return NULL;
49         }
50         buflen = talloc_get_size(buf);
51
52         if (buflen > str_len) {
53                 start = buf + str_len;
54                 space = buflen - str_len;
55         } else {
56                 return NULL;
57         }
58
59         va_copy(ap2, ap);
60         printlen = vsnprintf(start, space, fmt, ap2);
61         va_end(ap2);
62
63         if (printlen < 0) {
64                 goto fail;
65         }
66
67         needed = str_len + printlen + 1;
68
69         if (needed > buflen) {
70                 buflen = MAX(128, buflen);
71
72                 while (buflen < needed) {
73                         buflen *= 2;
74                 }
75
76                 tmpbuf = talloc_realloc(NULL, buf, char, buflen);
77                 if (tmpbuf == NULL) {
78                         goto fail;
79                 }
80                 buf = tmpbuf;
81
82                 va_copy(ap2, ap);
83                 vsnprintf(buf + str_len, buflen - str_len, fmt, ap2);
84                 va_end(ap2);
85         }
86         *pstr_len = (needed - 1);
87         return buf;
88 fail:
89         *pstr_len = -1;
90         return buf;
91 }
92
93 static char *talloc_asprintf_append_largebuf(char *buf, ssize_t *pstr_len,
94                                              const char *fmt, ...)
95                                              PRINTF_ATTRIBUTE(3,4);
96
97 static char *talloc_asprintf_append_largebuf(char *buf, ssize_t *pstr_len,
98                                              const char *fmt, ...)
99 {
100         va_list ap;
101
102         va_start(ap, fmt);
103         buf = talloc_vasprintf_append_largebuf(buf, pstr_len, fmt, ap);
104         va_end(ap);
105         return buf;
106 }
107
108 struct talloc_report_str_state {
109         ssize_t str_len;
110         char *s;
111 };
112
113 static void talloc_report_str_helper(const void *ptr, int depth, int max_depth,
114                                      int is_ref, void *private_data)
115 {
116         struct talloc_report_str_state *state = private_data;
117         const char *name = talloc_get_name(ptr);
118
119         if (ptr == state->s) {
120                 return;
121         }
122
123         if (is_ref) {
124                 state->s = talloc_asprintf_append_largebuf(
125                         state->s, &state->str_len,
126                         "%*sreference to: %s\n", depth*4, "", name);
127                 return;
128         }
129
130         if (depth == 0) {
131                 state->s = talloc_asprintf_append_largebuf(
132                         state->s, &state->str_len,
133                         "%stalloc report on '%s' "
134                         "(total %6lu bytes in %3lu blocks)\n",
135                         (max_depth < 0 ? "full " :""), name,
136                         (unsigned long)talloc_total_size(ptr),
137                         (unsigned long)talloc_total_blocks(ptr));
138                 return;
139         }
140
141         if (strcmp(name, "char") == 0) {
142                 /*
143                  * Print out the first 50 bytes of the string
144                  */
145                 state->s = talloc_asprintf_append_largebuf(
146                         state->s, &state->str_len,
147                         "%*s%-30s contains %6lu bytes in %3lu blocks "
148                         "(ref %zu): %*s\n", depth*4, "", name,
149                         (unsigned long)talloc_total_size(ptr),
150                         (unsigned long)talloc_total_blocks(ptr),
151                         talloc_reference_count(ptr),
152                         (int)MIN(50, talloc_get_size(ptr)),
153                         (const char *)ptr);
154                 return;
155         }
156
157         state->s = talloc_asprintf_append_largebuf(
158                 state->s, &state->str_len,
159                 "%*s%-30s contains %6lu bytes in %3lu blocks (ref %zu) %p\n",
160                 depth*4, "", name,
161                 (unsigned long)talloc_total_size(ptr),
162                 (unsigned long)talloc_total_blocks(ptr),
163                 talloc_reference_count(ptr), ptr);
164 }
165
166 char *talloc_report_str(TALLOC_CTX *mem_ctx, TALLOC_CTX *root)
167 {
168         struct talloc_report_str_state state;
169
170         state.s = talloc_strdup(mem_ctx, "");
171         if (state.s == NULL) {
172                 return NULL;
173         }
174         state.str_len = 0;
175
176         talloc_report_depth_cb(root, 0, -1, talloc_report_str_helper, &state);
177
178         if (state.str_len == -1) {
179                 talloc_free(state.s);
180                 return NULL;
181         }
182
183         return talloc_realloc(mem_ctx, state.s, char, state.str_len+1);
184 }