Bug Summary

File:build-scan/../src/libsystemd-network/dhcp-option.c
Warning:line 268, column 25
Potential leak of memory pointed to by 'error_message'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name dhcp-option.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/libsystemd-network/libsystemd-network.a.p -I src/libsystemd-network -I ../src/libsystemd-network -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I . -I .. -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/libsystemd-network/dhcp-option.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2/***
3 Copyright © 2013 Intel Corporation. All rights reserved.
4***/
5
6#include <errno(*__errno_location ()).h>
7#include <stdint.h>
8#include <stdio.h>
9#include <string.h>
10
11#include "alloc-util.h"
12#include "utf8.h"
13#include "strv.h"
14
15#include "dhcp-internal.h"
16
17static int option_append(uint8_t options[], size_t size, size_t *offset,
18 uint8_t code, size_t optlen, const void *optval) {
19 assert(options)do { if ((__builtin_expect(!!(!(options)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("options"), "../src/libsystemd-network/dhcp-option.c"
, 19, __PRETTY_FUNCTION__); } while (0)
;
20 assert(offset)do { if ((__builtin_expect(!!(!(offset)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("offset"), "../src/libsystemd-network/dhcp-option.c"
, 20, __PRETTY_FUNCTION__); } while (0)
;
21
22 if (code != SD_DHCP_OPTION_END)
23 /* always make sure there is space for an END option */
24 size--;
25
26 switch (code) {
27
28 case SD_DHCP_OPTION_PAD:
29 case SD_DHCP_OPTION_END:
30 if (size < *offset + 1)
31 return -ENOBUFS105;
32
33 options[*offset] = code;
34 *offset += 1;
35 break;
36
37 case SD_DHCP_OPTION_USER_CLASS: {
38 size_t len = 0;
39 char **s;
40
41 STRV_FOREACH(s, (char **) optval)for ((s) = ((char **) optval); (s) && *(s); (s)++)
42 len += strlen(*s) + 1;
43
44 if (size < *offset + len + 2)
45 return -ENOBUFS105;
46
47 options[*offset] = code;
48 options[*offset + 1] = len;
49 *offset += 2;
50
51 STRV_FOREACH(s, (char **) optval)for ((s) = ((char **) optval); (s) && *(s); (s)++) {
52 len = strlen(*s);
53
54 if (len > 255)
55 return -ENAMETOOLONG36;
56
57 options[*offset] = len;
58
59 memcpy_safe(&options[*offset + 1], *s, len);
60 *offset += len + 1;
61 }
62
63 break;
64 }
65 default:
66 if (size < *offset + optlen + 2)
67 return -ENOBUFS105;
68
69 options[*offset] = code;
70 options[*offset + 1] = optlen;
71
72 memcpy_safe(&options[*offset + 2], optval, optlen);
73 *offset += optlen + 2;
74
75 break;
76 }
77
78 return 0;
79}
80
81int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
82 uint8_t overload,
83 uint8_t code, size_t optlen, const void *optval) {
84 size_t file_offset = 0, sname_offset =0;
85 bool_Bool file, sname;
86 int r;
87
88 assert(message)do { if ((__builtin_expect(!!(!(message)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("message"), "../src/libsystemd-network/dhcp-option.c"
, 88, __PRETTY_FUNCTION__); } while (0)
;
89 assert(offset)do { if ((__builtin_expect(!!(!(offset)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("offset"), "../src/libsystemd-network/dhcp-option.c"
, 89, __PRETTY_FUNCTION__); } while (0)
;
90
91 file = overload & DHCP_OVERLOAD_FILE;
92 sname = overload & DHCP_OVERLOAD_SNAME;
93
94 if (*offset < size) {
95 /* still space in the options array */
96 r = option_append(message->options, size, offset, code, optlen, optval);
97 if (r >= 0)
98 return 0;
99 else if (r == -ENOBUFS105 && (file || sname)) {
100 /* did not fit, but we have more buffers to try
101 close the options array and move the offset to its end */
102 r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL((void*)0));
103 if (r < 0)
104 return r;
105
106 *offset = size;
107 } else
108 return r;
109 }
110
111 if (overload & DHCP_OVERLOAD_FILE) {
112 file_offset = *offset - size;
113
114 if (file_offset < sizeof(message->file)) {
115 /* still space in the 'file' array */
116 r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval);
117 if (r >= 0) {
118 *offset = size + file_offset;
119 return 0;
120 } else if (r == -ENOBUFS105 && sname) {
121 /* did not fit, but we have more buffers to try
122 close the file array and move the offset to its end */
123 r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL((void*)0));
124 if (r < 0)
125 return r;
126
127 *offset = size + sizeof(message->file);
128 } else
129 return r;
130 }
131 }
132
133 if (overload & DHCP_OVERLOAD_SNAME) {
134 sname_offset = *offset - size - (file ? sizeof(message->file) : 0);
135
136 if (sname_offset < sizeof(message->sname)) {
137 /* still space in the 'sname' array */
138 r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval);
139 if (r >= 0) {
140 *offset = size + (file ? sizeof(message->file) : 0) + sname_offset;
141 return 0;
142 } else {
143 /* no space, or other error, give up */
144 return r;
145 }
146 }
147 }
148
149 return -ENOBUFS105;
150}
151
152static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
153 uint8_t *message_type, char **error_message, dhcp_option_callback_t cb,
154 void *userdata) {
155 uint8_t code, len;
156 const uint8_t *option;
157 size_t offset = 0;
158
159 while (offset < buflen) {
11
Loop condition is true. Entering loop body
28
Assuming 'offset' is >= 'buflen'
29
Loop condition is false. Execution continues on line 231
160 code = options[offset ++];
161
162 switch (code) {
12
'Default' branch taken. Execution continues on line 170
163 case SD_DHCP_OPTION_PAD:
164 continue;
165
166 case SD_DHCP_OPTION_END:
167 return 0;
168 }
169
170 if (buflen < offset + 1)
13
Taking false branch
171 return -ENOBUFS105;
172
173 len = options[offset ++];
174
175 if (buflen < offset + len)
14
Assuming the condition is false
15
Taking false branch
176 return -EINVAL22;
177
178 option = &options[offset];
179
180 switch (code) {
16
Control jumps to 'case SD_DHCP_OPTION_ERROR_MESSAGE:' at line 190
181 case SD_DHCP_OPTION_MESSAGE_TYPE:
182 if (len != 1)
183 return -EINVAL22;
184
185 if (message_type)
186 *message_type = *option;
187
188 break;
189
190 case SD_DHCP_OPTION_ERROR_MESSAGE:
191 if (len == 0)
17
Assuming 'len' is not equal to 0
18
Taking false branch
192 return -EINVAL22;
193
194 if (error_message
18.1
'error_message' is non-null
) {
19
Taking true branch
195 _cleanup_free___attribute__((cleanup(freep))) char *string = NULL((void*)0);
196
197 /* Accept a trailing NUL byte */
198 if (memchr(option, 0, len - 1))
20
Assuming the condition is false
21
Taking false branch
199 return -EINVAL22;
200
201 string = strndup((const char *) option, len);
22
Memory is allocated
202 if (!string)
23
Assuming 'string' is non-null
24
Taking false branch
203 return -ENOMEM12;
204
205 if (!ascii_is_valid(string))
25
Assuming the condition is false
26
Taking false branch
206 return -EINVAL22;
207
208 free_and_replace(*error_message, string)({ free(*error_message); (*error_message) = (string); (string
) = ((void*)0); 0; })
;
209 }
210
211 break;
27
Execution continues on line 228
212 case SD_DHCP_OPTION_OVERLOAD:
213 if (len != 1)
214 return -EINVAL22;
215
216 if (overload)
217 *overload = *option;
218
219 break;
220
221 default:
222 if (cb)
223 cb(code, len, option, userdata);
224
225 break;
226 }
227
228 offset += len;
229 }
230
231 if (offset
29.1
'offset' is >= 'buflen'
< buflen)
30
Taking false branch
232 return -EINVAL22;
233
234 return 0;
235}
236
237int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) {
238 _cleanup_free___attribute__((cleanup(freep))) char *error_message = NULL((void*)0);
239 uint8_t overload = 0;
240 uint8_t message_type = 0;
241 int r;
242
243 if (!message)
1
Assuming 'message' is non-null
2
Taking false branch
244 return -EINVAL22;
245
246 if (len < sizeof(DHCPMessage))
3
Assuming the condition is false
4
Taking false branch
247 return -EINVAL22;
248
249 len -= sizeof(DHCPMessage);
250
251 r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata);
252 if (r
4.1
'r' is >= 0
< 0)
5
Taking false branch
253 return r;
254
255 if (overload & DHCP_OVERLOAD_FILE) {
6
Assuming the condition is false
7
Taking false branch
256 r = parse_options(message->file, sizeof(message->file), NULL((void*)0), &message_type, &error_message, cb, userdata);
257 if (r < 0)
258 return r;
259 }
260
261 if (overload & DHCP_OVERLOAD_SNAME) {
8
Assuming the condition is true
9
Taking true branch
262 r = parse_options(message->sname, sizeof(message->sname), NULL((void*)0), &message_type, &error_message, cb, userdata);
10
Calling 'parse_options'
31
Returned allocated memory via 5th parameter
263 if (r
31.1
'r' is >= 0
< 0)
32
Taking false branch
264 return r;
265 }
266
267 if (message_type
32.1
'message_type' is equal to 0
== 0)
33
Taking true branch
268 return -ENOMSG42;
34
Potential leak of memory pointed to by 'error_message'
269
270 if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){DHCP_NAK, DHCP_DECLINE})/sizeof(int)]; switch
(message_type) { case DHCP_NAK: case DHCP_DECLINE: _found = 1
; break; default: break; } _found; })
)
271 *_error_message = TAKE_PTR(error_message)({ typeof(error_message) _ptr_ = (error_message); (error_message
) = ((void*)0); _ptr_; })
;
272
273 return message_type;
274}