Bug Summary

File:lib/libswan/ttoaddress.c
Warning:line 421, column 10
Access to field 'ai_addrlen' results in a dereference of a null pointer (loaded from variable 'winner')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ttoaddress.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 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/build/quick-libreswan-2/lib/libswan -resource-dir /usr/lib64/clang/13.0.0 -D TimeZoneOffset=timezone -D PIE -D NSS_IPSEC_PROFILE -D XFRM_LIFETIME_DEFAULT=30 -D USE_IKEv1 -D XFRM_SUPPORT -D USE_XFRM_INTERFACE -D USE_DNSSEC -D DEFAULT_DNSSEC_ROOTKEY_FILE="/var/lib/unbound/root.key" -D HAVE_LABELED_IPSEC -D HAVE_SECCOMP -D LIBCURL -D USE_LINUX_AUDIT -D HAVE_NM -D USE_PAM_AUTH -D USE_3DES -D USE_AES -D USE_CAMELLIA -D USE_CHACHA -D USE_DH31 -D USE_MD5 -D USE_SHA1 -D USE_SHA2 -D USE_PRF_AES_XCBC -D USE_NSS_KDF -D DEFAULT_RUNDIR="/run/pluto" -D IPSEC_CONF="/etc/ipsec.conf" -D IPSEC_CONFDDIR="/etc/ipsec.d" -D IPSEC_NSSDIR="/var/lib/ipsec/nss" -D IPSEC_CONFDIR="/etc" -D IPSEC_EXECDIR="/usr/local/libexec/ipsec" -D IPSEC_SBINDIR="/usr/local/sbin" -D IPSEC_VARDIR="/var" -D POLICYGROUPSDIR="/etc/ipsec.d/policies" -D IPSEC_SECRETS_FILE="/etc/ipsec.secrets" -D FORCE_PR_ASSERT -D USE_FORK=1 -D USE_VFORK=0 -D USE_DAEMON=0 -D USE_PTHREAD_SETSCHEDPRIO=1 -D GCC_LINT -D HAVE_LIBCAP_NG -I . -I ../../OBJ.linux.x86_64/lib/libswan -I ../../include -I /usr/include/nss3 -I /usr/include/nspr4 -D HERE_FILENAME="lib/libswan/ttoaddress.c" -internal-isystem /usr/lib64/clang/13.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/11/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu99 -fdebug-compilation-dir=/home/build/quick-libreswan-2/lib/libswan -ferror-limit 19 -stack-protector 3 -fgnuc-version=4.2.1 -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-01-01-205714-1273399-1 -x c /home/build/quick-libreswan-2/lib/libswan/ttoaddress.c
1/*
2 * conversion from text forms of addresses to internal ones
3 *
4 * Copyright (C) 2000 Henry Spencer.
5 * Copyright (C) 2019-2021 Andrew Cagney <cagney@gnu.org>
6 *
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Library General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <https://www.gnu.org/licenses/lgpl-2.1.txt>.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15 * License for more details.
16 *
17 */
18
19/*
20 * Unit testing is available through
21 * OBJ.$WHATEVER/testing/programs/ipcheck/ipcheck
22 * This does not require KVM and is built by "make base".
23 */
24
25#include <string.h>
26#include <netdb.h> /* for gethostbyname2() */
27#include <sys/socket.h> /* for AF_INET/AF_INET6/AF_UNSPEC */
28
29#include "ip_address.h"
30#include "ip_sockaddr.h"
31#include "ip_info.h"
32#include "lswalloc.h" /* for alloc_things(), pfree() */
33#include "lswlog.h" /* for pexpect() */
34#include "hunk.h" /* for char_is_xdigit() */
35
36static bool_Bool tryhex(shunk_t hex, ip_address *dst);
37static err_t trydotted(shunk_t src, ip_address *);
38static err_t colon(shunk_t src, ip_address *);
39static err_t getpiece(const char **, const char *, unsigned *);
40
41/*
42 * ttoaddress_num - convert "numeric" IP address to binary address.
43 *
44 * NULL for success, else static string literal diagnostic.
45 */
46
47err_t ttoaddress_num(shunk_t src, const struct ip_info *afi, ip_address *dst)
48{
49 *dst = unset_address;
50
51 if (src.len == 0) {
52 return "empty string";
53 }
54
55 /*
56 * Hack to recognize a HEX IPv4 address; presumably DNS names
57 * can't start with a number so this can't be misinterpreted.
58 *
59 * If this fails, stumble on like nothing happened, letting
60 * the more typical code report an error.
61 */
62
63 if (afi == &ipv4_info) {
64 if (tryhex(src, dst)) {
65 return NULL((void*)0);
66 }
67 return trydotted(src, dst);
68 }
69
70 if (afi == &ipv6_info) {
71 return colon(src, dst);
72 }
73
74 if (afi == NULL((void*)0)) {
75 if (tryhex(src, dst)) {
76 return NULL((void*)0);
77 }
78 if (memchr(src.ptr, ':', src.len) != NULL((void*)0)) {
79 return colon(src, dst);
80 }
81 return trydotted(src, dst);
82 }
83
84 return "address family unknown";
85}
86
87/*
88 * tryhex - try conversion as an eight-digit hex number (AF_INET only)
89 */
90
91static bool_Bool tryhex(shunk_t hex,
92 ip_address *dst)
93{
94 if (hex.len != strlen("0x12345678")) {
95 return false0;
96 }
97
98 if (!shunk_strcaseeat(&hex, "0x")) {
99 return false0;
100 }
101
102 uintmax_t ul;
103 err_t err = shunk_to_uintmax(hex, NULL((void*)0), 16, &ul, UINT32_MAX(4294967295U));
104 if (err != NULL((void*)0)) {
105 return false0;
106 }
107
108 struct in_addr addr = { htonl(ul), };
109 *dst = address_from_in_addr(&addr);
110 return true1;
111}
112
113/*
114 * trydotted - try conversion as dotted numeric (AF_INET only).
115 *
116 * But could a DNS name also be a valid IPv4 address, vis:
117 * - can start with a decimal-digit
118 * - can end with a decimal-digit
119 * - must contain at least one letter
120 * https://tools.ietf.org/html/rfc1912
121 *
122 * Yes, for instance: 0x01.0x02.0x03.0x04.
123 *
124 * This code tries to avoid this pitfall by only allowing non-decimal
125 * fields when they match a very limited format.
126 */
127
128static err_t trydotted(shunk_t src, ip_address *dst)
129{
130 struct ip_bytes bytes = unset_bytes;
131
132 shunk_t cursor = src;
133 for (unsigned b = 0; b < 4 && cursor.ptr != NULL((void*)0) /* more-input */; b++) {
134 /* Danger: nested breaks and returns. */
135
136 /*
137 * Carve off the next number.
138 *
139 * This way, given "09.1.2.", the full sub-string "09"
140 * is extracted and converted. Being invalid, it will
141 * report an error about a bogus digit (and not the
142 * less meaningful trailing garbage error).
143 *
144 * After the last token has been extracted cursor is
145 * left pointing at NULL i.e., cursor.ptr==NULL.
146 */
147 shunk_t token = shunk_token(&cursor, NULL((void*)0), ".");
148 if (token.len == 0) {
149 /* ex: 0xa. */
150 return "empty dotted-numeric address field";
151 }
152#if 0
153 fprintf(stderrstderr, "cursor="PRI_SHUNK"%.*s"" token="PRI_SHUNK"%.*s""\n",
154 pri_shunk(cursor)((int) (cursor).len), (const char *) ((cursor).ptr), pri_shunk(token)((int) (token).len), (const char *) ((token).ptr));
155#endif
156
157 /*
158 * Try converting to a number.
159 *
160 * See examples in wikepedia.
161 *
162 * This lets uintmax decide how to convert the number
163 * (play with wget).
164 *
165 * The alternative is to do a very strict check.
166 */
167#if 0
168 unsigned base;
169 if (token.len == strlen("0xXX") && shunk_strcaseeat(&token, "0x")) {
170 base = 16;
171 } else if (token.len == strlen("0ooo") && shunk_strcaseeat(&token, "0")) {
172 base = 8;
173 } else {
174 base = 10;
175 }
176 if (hunk_strcasestarteq(token, "0")({ const typeof(token) hunk_ = token; const char *string_ = "0"
; size_t slen_ = string_ != ((void*)0) ? strlen(string_) : 0;
hunk_.len < slen_ ? 0 : case_eq(hunk_.ptr, slen_, string_
, slen_); })
&& token.len == strlen("0xXX")) {
177 base = 16;
178 }
179 uintmax_t byte;
180 err_t err = shunk_to_uintmax(token, NULL((void*)0), base, &byte, 0/*no-celing*/);
181#else
182 uintmax_t byte;
183 err_t err = shunk_to_uintmax(token, NULL((void*)0), 0, &byte, 0/*no-celing*/);
184#endif
185 if (err != NULL((void*)0)) {
186 return err;
187 }
188
189 /*
190 * The last byte overflows into earlier unfilled
191 * bytes. For instance:
192 *
193 * 127.1 -> 127.0.0.1
194 * 127.65534 -> 127.0.255.254
195 *
196 * A bizare hangover from address classes:
197 * https://en.wikipedia.org/wiki/IPv4#Address_representations
198 *
199 * Now while it is arguable that:
200 *
201 * 65534 -> 0.0.255.254
202 * 127 -> 0.0.0.127 !?!?!
203 *
204 * is also valid, that is too weird and rejected (but
205 * 0x01020304 is accepted by an earlier call to
206 * tryhex()).
207 */
208 if (cursor.len == 0 && b > 0) {
209 for (unsigned e = 3; e >= b && byte != 0; e--) {
210 bytes.byte[e] = byte; /* truncate */
211 byte = byte >> 8;
212 }
213 if (byte != 0) {
214 return "overflow in dotted-numeric address";
215 }
216 break;
217 }
218
219 /* A non-last bytes need to fit into the byte */
220 if (byte > UINT8_MAX(255)) {
221 return "byte overflow in dotted-numeric address";
222 }
223
224 bytes.byte[b] = byte;
225 }
226 if (cursor.ptr != NULL((void*)0)) {
227 return "garbage at end of dotted-address";
228 }
229
230 *dst = address_from_raw(HERE({ static const struct where here = { .func = __func__, .file
= "lib/libswan/ttoaddress.c", .line = 230, }; &here; })
, ipv4_info.ip_version, bytes);
231 return NULL((void*)0);
232}
233
234/*
235 * colon - convert IPv6 "numeric" address
236 */
237
238static err_t colon(shunk_t cursor, ip_address *dst)
239{
240 const char *src = cursor.ptr;
241 unsigned srclen = cursor.len;
242 const char *stop = src + srclen; /* just past end */
243 unsigned piece;
244 int gapat; /* where was empty piece seen */
245 err_t oops;
246# define NPIECES8 8
247 struct ip_bytes u = unset_bytes;
248 int i;
249 int j;
250# define IT"IPv6 numeric address" "IPv6 numeric address"
251 int naftergap;
252
253 /* leading or trailing :: becomes single empty field */
254 if (*src == ':') { /* legal only if leading :: */
255 if (srclen == 1 || *(src + 1) != ':')
256 return "illegal leading `:' in " IT"IPv6 numeric address";
257
258 if (srclen == 2) {
259 *dst = ipv6_info.address.any;
260 return NULL((void*)0);
261 }
262 src++; /* past first but not second */
263 srclen--;
264 }
265 if (*(stop - 1) == ':') { /* legal only if trailing :: */
266 if (srclen == 1 || *(stop - 2) != ':')
267 return "illegal trailing `:' in " IT"IPv6 numeric address";
268
269 srclen--; /* leave one */
270 }
271
272 gapat = -1;
273 piece = 0;
274 for (i = 0; i < NPIECES8 && src < stop; i++) {
275 oops = getpiece(&src, stop, &piece);
276 if (oops != NULL((void*)0) && *oops == ':') { /* empty field */
277 if (gapat >= 0)
278 return "more than one :: in " IT"IPv6 numeric address";
279
280 gapat = i;
281 } else if (oops != NULL((void*)0)) {
282 return oops;
283 }
284 u.byte[2 * i] = piece >> 8;
285 u.byte[2 * i + 1] = piece & 0xff;
286 if (i < NPIECES8 - 1) { /* there should be more input */
287 if (src == stop && gapat < 0)
288 return IT"IPv6 numeric address" " ends prematurely";
289
290 if (src != stop && *src++ != ':')
291 return "syntax error in " IT"IPv6 numeric address";
292 }
293 }
294 if (src != stop)
295 return "extra garbage on end of " IT"IPv6 numeric address";
296
297 if (gapat < 0 && i < NPIECES8) /* should have been caught earlier */
298 return "incomplete " IT"IPv6 numeric address" " (internal error)";
299
300 if (gapat >= 0 && i == NPIECES8)
301 return "non-abbreviating empty field in " IT"IPv6 numeric address";
302
303 if (gapat >= 0) {
304 naftergap = i - (gapat + 1);
305 for (i--, j = NPIECES8 - 1; naftergap > 0;
306 i--, j--, naftergap--) {
307 u.byte[2 * j] = u.byte[2 * i];
308 u.byte[2 * j + 1] = u.byte[2 * i + 1];
309 }
310 for (; j >= gapat; j--)
311 u.byte[2 * j] = u.byte[2 * j + 1] = 0;
312 }
313
314 *dst = address_from_raw(HERE({ static const struct where here = { .func = __func__, .file
= "lib/libswan/ttoaddress.c", .line = 314, }; &here; })
, ipv6_info.ip_version, u);
315 return NULL((void*)0);
316}
317
318/*
319 * getpiece - try to scan one 16-bit piece of an IPv6 address
320 *
321 * ":" means "empty field seen"
322 */
323err_t getpiece(const char **srcp, /* *srcp is updated */
324 const char *stop, /* first untouchable char */
325 unsigned *retp) /* return-value pointer */
326{
327 const char *p;
328# define NDIG4 4
329 int d;
330 unsigned long ret;
331 err_t oops;
332
333 if (*srcp >= stop || **srcp == ':') { /* empty field */
334 *retp = 0;
335 return ":";
336 }
337
338 p = *srcp;
339 d = 0;
340 while (p < stop && d < NDIG4 && char_isxdigit(*p)) {
341 p++;
342 d++;
343 }
344 if (d == 0)
345 return "non-hex field in IPv6 numeric address";
346
347 if (p < stop && d == NDIG4 && char_isxdigit(*p))
348 return "field in IPv6 numeric address longer than 4 hex digits";
349
350 oops = ttoul(*srcp, d, 16, &ret);
351 if (oops != NULL((void*)0)) /* shouldn't happen, really... */
352 return oops;
353
354 *srcp = p;
355 *retp = ret;
356 return NULL((void*)0);
357}
358
359/*
360 * ttoaddress_dns
361 *
362 * ??? numeric addresses are handled by getaddrinfo; perhaps the hex form is lost.
363 * ??? change: we no longer filter out bad characters. Surely getaddrinfo(3) does.
364 */
365err_t ttoaddress_dns(shunk_t src, const struct ip_info *afi, ip_address *dst)
366{
367 *dst = unset_address;
368
369 char *name = clone_hunk_as_string(src, "ttoaddress_dns")({ typeof(src) hunk_ = src; clone_bytes_as_string(hunk_.ptr, hunk_
.len, "ttoaddress_dns"); })
; /* must free */
370 struct addrinfo *res = NULL((void*)0); /* must-free when EAI==0 */
371 int family = afi == NULL((void*)0) ? AF_UNSPEC0 : afi->af;
1
Assuming 'afi' is equal to NULL
2
'?' condition is true
372 const struct addrinfo hints = (struct addrinfo) {
373 .ai_family = family,
374 };
375 int eai = getaddrinfo(name, NULL((void*)0), &hints, &res);
3
Value assigned to 'res'
376
377 if (eai != 0) {
4
Assuming 'eai' is equal to 0
5
Taking false branch
378 /*
379 * Return what the pluto testsuite expects for now.
380 *
381 * Error return is intricate because we cannot compose
382 * a static string.
383 *
384 * XXX: How portable are errors returned by
385 * gai_strerror(eai)?
386 *
387 * XXX: what is with "(no validation performed)"?
388 * Perhaps it is refering to DNSSEC.
389 */
390 pfree(name);
391 /* RES is not defined */
392 switch (family) {
393 case AF_INET610:
394 return "not a numeric IPv6 address and name lookup failed (no validation performed)";
395 case AF_INET2:
396 return "not a numeric IPv4 address and name lookup failed (no validation performed)";
397 default:
398 return "not a numeric IPv4 or IPv6 address and name lookup failed (no validation performed)";
399 }
400 }
401
402 /*
403 * When AFI is specified, use the first entry; and prefer IPv4
404 * when it wasn't.
405 *
406 * Linux orders things IPv4->IPv6, but NetBSD at least is the
407 * reverse; hence the search.
408 */
409 struct addrinfo *winner = res;
6
'winner' initialized to the value of 'res'
410 if (afi
6.1
'afi' is equal to NULL
== NULL((void*)0)) {
7
Taking true branch
411 for (struct addrinfo *r = res; r!= NULL((void*)0); r = r->ai_next) {
8
Assuming 'r' is equal to NULL
9
Loop condition is false. Execution continues on line 421
412 if (r->ai_family == AF_INET2) {
413 winner = r;
414 break;
415 }
416 }
417 }
418
419 /* ai_addrlen is probably shorter than (sa.sa) */
420 ip_sockaddr sa = {
421 .len = winner->ai_addrlen,
10
Access to field 'ai_addrlen' results in a dereference of a null pointer (loaded from variable 'winner')
422 };
423 passert(winner->ai_addrlen <= sizeof(sa.sa))({ _Bool assertion__ = winner->ai_addrlen <= sizeof(sa.
sa); if (!assertion__) { where_t here = ({ static const struct
where here = { .func = __func__, .file = "lib/libswan/ttoaddress.c"
, .line = 423, }; &here; }); const struct logger *logger_
= &failsafe_logger; llog_passert(logger_, here, "%s", "winner->ai_addrlen <= sizeof(sa.sa)"
); } (void) 1; })
;
424 memcpy(&sa.sa, winner->ai_addr, winner->ai_addrlen);
425 passert(sa.sa.sa.sa_family == winner->ai_family)({ _Bool assertion__ = sa.sa.sa.sa_family == winner->ai_family
; if (!assertion__) { where_t here = ({ static const struct where
here = { .func = __func__, .file = "lib/libswan/ttoaddress.c"
, .line = 425, }; &here; }); const struct logger *logger_
= &failsafe_logger; llog_passert(logger_, here, "%s", "sa.sa.sa.sa_family == winner->ai_family"
); } (void) 1; })
;
426
427 /* boneheaded getaddrinfo(3) leaves port field undefined */
428 err_t err = sockaddr_to_address_port(sa, dst, NULL((void*)0)/*ignore port*/);
429
430 freeaddrinfo(res);
431 pfree(name);
432 return err;
433}