Bug Summary

File:build-scan/../src/resolve/resolvconf-compat.c
Warning:line 230, column 13
Potential leak of memory pointed to by 'iface'

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 resolvconf-compat.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 static -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 resolvectl.p -I . -I .. -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 src/libsystemd-network -I ../src/libsystemd-network -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/resolve/resolvconf-compat.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <getopt.h>
4#include <net/if.h>
5
6#include "alloc-util.h"
7#include "def.h"
8#include "dns-domain.h"
9#include "extract-word.h"
10#include "fileio.h"
11#include "parse-util.h"
12#include "resolvconf-compat.h"
13#include "resolvectl.h"
14#include "resolved-def.h"
15#include "string-util.h"
16#include "strv.h"
17
18static void resolvconf_help(void) {
19 printf("%1$s -a INTERFACE < FILE\n"
20 "%1$s -d INTERFACE\n"
21 "\n"
22 "Register DNS server and domain configuration with systemd-resolved.\n\n"
23 " -h --help Show this help\n"
24 " --version Show package version\n"
25 " -a Register per-interface DNS server and domain data\n"
26 " -d Unregister per-interface DNS server and domain data\n"
27 " -f Ignore if specified interface does not exist\n"
28 " -x Send DNS traffic preferably over this interface\n"
29 "\n"
30 "This is a compatibility alias for the resolvectl(1) tool, providing native\n"
31 "command line compatibility with the resolvconf(8) tool of various Linux\n"
32 "distributions and BSD systems. Some options supported by other implementations\n"
33 "are not supported and are ignored: -m, -p. Various options supported by other\n"
34 "implementations are not supported and will cause the invocation to fail: -u,\n"
35 "-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n"
36 "--updates-are-enabled.\n"
37 , program_invocation_short_name);
38}
39
40static int parse_nameserver(const char *string) {
41 int r;
42
43 assert(string)do { if ((__builtin_expect(!!(!(string)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("string"), "../src/resolve/resolvconf-compat.c"
, 43, __PRETTY_FUNCTION__); } while (0)
;
44
45 for (;;) {
46 _cleanup_free___attribute__((cleanup(freep))) char *word = NULL((void*)0);
47
48 r = extract_first_word(&string, &word, NULL((void*)0), 0);
49 if (r < 0)
50 return r;
51 if (r == 0)
52 break;
53
54 if (strv_push(&arg_set_dns, word) < 0)
55 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/resolve/resolvconf-compat.c"
, 55, __func__)
;
56
57 word = NULL((void*)0);
58 }
59
60 return 0;
61}
62
63static int parse_search_domain(const char *string) {
64 int r;
65
66 assert(string)do { if ((__builtin_expect(!!(!(string)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("string"), "../src/resolve/resolvconf-compat.c"
, 66, __PRETTY_FUNCTION__); } while (0)
;
67
68 for (;;) {
69 _cleanup_free___attribute__((cleanup(freep))) char *word = NULL((void*)0);
70
71 r = extract_first_word(&string, &word, NULL((void*)0), EXTRACT_QUOTES);
72 if (r < 0)
73 return r;
74 if (r == 0)
75 break;
76
77 if (strv_push(&arg_set_domain, word) < 0)
78 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/resolve/resolvconf-compat.c"
, 78, __func__)
;
79
80 word = NULL((void*)0);
81 }
82
83 return 0;
84}
85
86int resolvconf_parse_argv(int argc, char *argv[]) {
87
88 enum {
89 ARG_VERSION = 0x100,
90 ARG_ENABLE_UPDATES,
91 ARG_DISABLE_UPDATES,
92 ARG_UPDATES_ARE_ENABLED,
93 };
94
95 static const struct option options[] = {
96 { "help", no_argument0, NULL((void*)0), 'h' },
97 { "version", no_argument0, NULL((void*)0), ARG_VERSION },
98
99 /* The following are specific to Debian's original resolvconf */
100 { "enable-updates", no_argument0, NULL((void*)0), ARG_ENABLE_UPDATES },
101 { "disable-updates", no_argument0, NULL((void*)0), ARG_DISABLE_UPDATES },
102 { "updates-are-enabled", no_argument0, NULL((void*)0), ARG_UPDATES_ARE_ENABLED },
103 {}
104 };
105
106 enum {
107 TYPE_REGULAR,
108 TYPE_PRIVATE, /* -p: Not supported, treated identically to TYPE_REGULAR */
109 TYPE_EXCLUSIVE, /* -x */
110 } type = TYPE_REGULAR;
111
112 const char *dot, *iface;
113 int c, r;
114
115 assert(argc >= 0)do { if ((__builtin_expect(!!(!(argc >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("argc >= 0"), "../src/resolve/resolvconf-compat.c"
, 115, __PRETTY_FUNCTION__); } while (0)
;
1
Assuming 'argc' is >= 0
2
Taking false branch
3
Loop condition is false. Exiting loop
116 assert(argv)do { if ((__builtin_expect(!!(!(argv)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("argv"), "../src/resolve/resolvconf-compat.c"
, 116, __PRETTY_FUNCTION__); } while (0)
;
4
Assuming 'argv' is non-null
5
Taking false branch
6
Loop condition is false. Exiting loop
117
118 /* openresolv checks these environment variables */
119 if (getenv("IF_EXCLUSIVE"))
7
Assuming the condition is false
8
Taking false branch
120 type = TYPE_EXCLUSIVE;
121 if (getenv("IF_PRIVATE"))
9
Assuming the condition is false
10
Taking false branch
122 type = TYPE_PRIVATE; /* not actually supported */
123
124 arg_mode = _MODE_INVALID;
125
126 while ((c = getopt_long(argc, argv, "hadxpfm:uIi:l:Rr:vV", options, NULL((void*)0))) >= 0)
11
Assuming the condition is true
12
Loop condition is true. Entering loop body
15
Assuming the condition is false
16
Loop condition is false. Execution continues on line 195
127 switch(c) {
13
Control jumps to 'case 100:' at line 141
128
129 case 'h':
130 resolvconf_help();
131 return 0; /* done */;
132
133 case ARG_VERSION:
134 return version();
135
136 /* -a and -d is what everybody can agree on */
137 case 'a':
138 arg_mode = MODE_SET_LINK;
139 break;
140
141 case 'd':
142 arg_mode = MODE_REVERT_LINK;
143 break;
14
Execution continues on line 126
144
145 /* The exclusive/private/force stuff is an openresolv invention, we support in some skewed way */
146 case 'x':
147 type = TYPE_EXCLUSIVE;
148 break;
149
150 case 'p':
151 type = TYPE_PRIVATE; /* not actually supported */
152 break;
153
154 case 'f':
155 arg_ifindex_permissive = true1;
156 break;
157
158 /* The metrics stuff is an openresolv invention we ignore (and don't really need) */
159 case 'm':
160 log_debug("Switch -%c ignored.", c)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 160, __func__, "Switch -%c ignored."
, c) : -abs(_e); })
;
161 break;
162
163 /* Everybody else can agree on the existance of -u but we don't support it. */
164 case 'u':
165
166 /* The following options are openresolv inventions we don't support. */
167 case 'I':
168 case 'i':
169 case 'l':
170 case 'R':
171 case 'r':
172 case 'v':
173 case 'V':
174 log_error("Switch -%c not supported.", c)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 174, __func__, "Switch -%c not supported."
, c) : -abs(_e); })
;
175 return -EINVAL22;
176
177 /* The Debian resolvconf commands we don't support. */
178 case ARG_ENABLE_UPDATES:
179 log_error("Switch --enable-updates not supported.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 179, __func__, "Switch --enable-updates not supported."
) : -abs(_e); })
;
180 return -EINVAL22;
181 case ARG_DISABLE_UPDATES:
182 log_error("Switch --disable-updates not supported.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 182, __func__, "Switch --disable-updates not supported."
) : -abs(_e); })
;
183 return -EINVAL22;
184 case ARG_UPDATES_ARE_ENABLED:
185 log_error("Switch --updates-are-enabled not supported.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 185, __func__, "Switch --updates-are-enabled not supported."
) : -abs(_e); })
;
186 return -EINVAL22;
187
188 case '?':
189 return -EINVAL22;
190
191 default:
192 assert_not_reached("Unhandled option")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, (
"Unhandled option"), "../src/resolve/resolvconf-compat.c", 192
, __PRETTY_FUNCTION__); } while (0)
;
193 }
194
195 if (arg_mode
16.1
'arg_mode' is not equal to _MODE_INVALID
== _MODE_INVALID) {
17
Taking false branch
196 log_error("Expected either -a or -d on the command line.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 196, __func__, "Expected either -a or -d on the command line."
) : -abs(_e); })
;
197 return -EINVAL22;
198 }
199
200 if (optind+1 != argc) {
18
Assuming the condition is false
19
Taking false branch
201 log_error("Expected interface name as argument.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 201, __func__, "Expected interface name as argument."
) : -abs(_e); })
;
202 return -EINVAL22;
203 }
204
205 dot = strchr(argv[optind], '.');
206 if (dot) {
20
Assuming 'dot' is non-null
21
Taking true branch
207 iface = strndup(argv[optind], dot - argv[optind]);
22
Memory is allocated
208 log_debug("Ignoring protocol specifier '%s'.", dot + 1)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 208, __func__, "Ignoring protocol specifier '%s'."
, dot + 1) : -abs(_e); })
;
23
Assuming the condition is false
24
'?' condition is false
209 } else
210 iface = argv[optind];
211 optind++;
212
213 if (parse_ifindex(iface, &arg_ifindex) < 0) {
25
Assuming the condition is false
26
Taking false branch
214 int ifi;
215
216 ifi = if_nametoindex(iface);
217 if (ifi <= 0) {
218 if (errno(*__errno_location ()) == ENODEV19 && arg_ifindex_permissive) {
219 log_debug("Interface '%s' not found, but -f specified, ignoring.", iface)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 219, __func__, "Interface '%s' not found, but -f specified, ignoring."
, iface) : -abs(_e); })
;
220 return 0; /* done */
221 }
222
223 return log_error_errno(errno, "Unknown interface '%s': %m", iface)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/resolve/resolvconf-compat.c", 223
, __func__, "Unknown interface '%s': %m", iface) : -abs(_e); }
)
;
224 }
225
226 arg_ifindex = ifi;
227 arg_ifname = iface;
228 }
229
230 if (arg_mode == MODE_SET_LINK) {
27
Potential leak of memory pointed to by 'iface'
231 unsigned n = 0;
232
233 for (;;) {
234 _cleanup_free___attribute__((cleanup(freep))) char *line = NULL((void*)0);
235 const char *a, *l;
236
237 r = read_line(stdinstdin, LONG_LINE_MAX(1U*1024U*1024U), &line);
238 if (r < 0)
239 return log_error_errno(r, "Failed to read from stdin: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 239, __func__, "Failed to read from stdin: %m"
) : -abs(_e); })
;
240 if (r == 0)
241 break;
242
243 n++;
244
245 l = strstrip(line);
246 if (IN_SET(*l, '#', ';', 0)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){'#', ';', 0})/sizeof(int)]; switch(*l) {
case '#': case ';': case 0: _found = 1; break; default: break
; } _found; })
)
247 continue;
248
249 a = first_word(l, "nameserver");
250 if (a) {
251 (void) parse_nameserver(a);
252 continue;
253 }
254
255 a = first_word(l, "domain");
256 if (!a)
257 a = first_word(l, "search");
258 if (a) {
259 (void) parse_search_domain(a);
260 continue;
261 }
262
263 log_syntax(NULL, LOG_DEBUG, "stdin", n, 0, "Ignoring resolv.conf line: %s", l)({ int _level = (7), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD
) >= ((_level) & 0x07)) ? log_syntax_internal(((void*)
0), _level, "stdin", n, _e, "../src/resolve/resolvconf-compat.c"
, 263, __func__, "Ignoring resolv.conf line: %s", l) : -abs(_e
); })
;
264 }
265
266 if (type == TYPE_EXCLUSIVE) {
267
268 /* If -x mode is selected, let's preferably route non-suffixed lookups to this interface. This
269 * somewhat matches the original -x behaviour */
270
271 r = strv_extend(&arg_set_domain, "~.");
272 if (r < 0)
273 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/resolve/resolvconf-compat.c"
, 273, __func__)
;
274
275 } else if (type == TYPE_PRIVATE)
276 log_debug("Private DNS server data not supported, ignoring.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 276, __func__, "Private DNS server data not supported, ignoring."
) : -abs(_e); })
;
277
278 if (!arg_set_dns) {
279 log_error("No DNS servers specified, refusing operation.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/resolve/resolvconf-compat.c", 279, __func__, "No DNS servers specified, refusing operation."
) : -abs(_e); })
;
280 return -EINVAL22;
281 }
282 }
283
284 return 1; /* work to do */
285}