Bug Summary

File:src/lib/libc/nls/catopen.c
Warning:line 159, column 20
Potential leak of memory pointed to by 'catd'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name catopen.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 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/lib/libc/obj -resource-dir /usr/local/lib/clang/13.0.0 -include namespace.h -I /usr/src/lib/libc/include -I /usr/src/lib/libc/hidden -D __LIBC__ -D APIWARN -D YP -I /usr/src/lib/libc/yp -I /usr/src/lib/libc -I /usr/src/lib/libc/gdtoa -I /usr/src/lib/libc/arch/amd64/gdtoa -D INFNAN_CHECK -D MULTIPLE_THREADS -D NO_FENV_H -D USE_LOCALE -I /usr/src/lib/libc -I /usr/src/lib/libc/citrus -D RESOLVSORT -D FLOATING_POINT -D PRINTF_WIDE_CHAR -D SCANF_WIDE_CHAR -D FUTEX -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/lib/libc/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/lib/libc/nls/catopen.c
1/* $OpenBSD: catopen.c,v 1.21 2017/04/27 23:54:08 millert Exp $ */
2/*-
3 * Copyright (c) 1996 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by J.T. Conklin.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#define _NLS_PRIVATE
32
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <sys/mman.h>
36#include <errno(*__errno()).h>
37#include <fcntl.h>
38#include <limits.h>
39#include <nl_types.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#define MAXIMUM(a, b)(((a) > (b)) ? (a) : (b)) (((a) > (b)) ? (a) : (b))
45
46#define NLS_DEFAULT_LANG"C" "C"
47
48static nl_catd load_msgcat(const char *);
49static int verify_msgcat(nl_catd);
50
51nl_catd
52catopen(const char *name, int oflag)
53{
54 char tmppath[PATH_MAX1024];
55 char *nlspath;
56 char *lang;
57 char *s, *t, *sep, *dot;
58 const char *u;
59 nl_catd catd;
60
61 if (name == NULL((void *)0) || *name == '\0')
1
Assuming 'name' is not equal to NULL
2
Assuming the condition is false
3
Taking false branch
62 return (nl_catd) -1;
63
64 /* absolute or relative path? */
65 if (strchr(name, '/'))
4
Assuming the condition is false
5
Taking false branch
66 return load_msgcat(name);
67
68 if (issetugid() != 0 || (nlspath = getenv("NLSPATH")) == NULL((void *)0))
6
Assuming the condition is false
7
Assuming the condition is false
8
Taking false branch
69 return (nl_catd) -1;
70
71 lang = NULL((void *)0);
72 if (oflag & NL_CAT_LOCALE1) {
9
Assuming the condition is false
10
Taking false branch
73 lang = getenv("LC_ALL");
74 if (lang == NULL((void *)0))
75 lang = getenv("LC_MESSAGES");
76 }
77 if (lang
10.1
'lang' is equal to NULL
== NULL((void *)0))
11
Taking true branch
78 lang = getenv("LANG");
79 if (lang == NULL((void *)0))
12
Assuming 'lang' is not equal to NULL
13
Taking false branch
80 lang = NLS_DEFAULT_LANG"C";
81 if (strcmp(lang, "POSIX") == 0)
14
Assuming the condition is false
15
Taking false branch
82 lang = NLS_DEFAULT_LANG"C";
83
84 s = nlspath;
85 t = tmppath;
86
87 /*
88 * Locale names are of the form language[_territory][.codeset].
89 * See POSIX-1-2008 "8.2 Internationalization Variables"
90 */
91 sep = strchr(lang, '_');
92 dot = strrchr(lang, '.');
93 if (dot && sep && dot < sep)
16
Assuming 'dot' is non-null
17
Assuming 'sep' is null
94 dot = NULL((void *)0); /* ignore dots preceeding _ */
95 if (dot
17.1
'dot' is not equal to NULL
== NULL((void *)0))
18
Taking false branch
96 lang = NLS_DEFAULT_LANG"C"; /* no codeset specified */
97 do {
41
Loop condition is false. Exiting loop
98 while (*s && *s != ':') {
19
Assuming the condition is false
99 if (*s == '%') {
100 switch (*(++s)) {
101 case 'L': /* LANG or LC_MESSAGES */
102 u = lang;
103 while (*u && t < tmppath + PATH_MAX1024-1)
104 *t++ = *u++;
105 break;
106 case 'N': /* value of name parameter */
107 u = name;
108 while (*u && t < tmppath + PATH_MAX1024-1)
109 *t++ = *u++;
110 break;
111 case 'l': /* language part */
112 u = lang;
113 while (*u && t < tmppath + PATH_MAX1024-1) {
114 *t++ = *u++;
115 if (sep && u >= sep)
116 break;
117 if (dot && u >= dot)
118 break;
119 }
120 break;
121 case 't': /* territory part */
122 if (sep == NULL((void *)0))
123 break;
124 u = sep + 1;
125 while (*u && t < tmppath + PATH_MAX1024-1) {
126 *t++ = *u++;
127 if (dot && u >= dot)
128 break;
129 }
130 break;
131 case 'c': /* codeset part */
132 if (dot == NULL((void *)0))
133 break;
134 u = dot + 1;
135 while (*u && t < tmppath + PATH_MAX1024-1)
136 *t++ = *u++;
137 break;
138 default:
139 if (t < tmppath + PATH_MAX1024-1)
140 *t++ = *s;
141 }
142 } else {
143 if (t < tmppath + PATH_MAX1024-1)
144 *t++ = *s;
145 }
146 s++;
147 }
148
149 *t = '\0';
150 catd = load_msgcat(tmppath);
20
Calling 'load_msgcat'
37
Returned allocated memory
151 if (catd != (nl_catd) -1)
38
Assuming the condition is false
39
Taking false branch
152 return catd;
153
154 if (*s)
40
Taking false branch
155 s++;
156 t = tmppath;
157 } while (*s);
158
159 return (nl_catd) -1;
42
Potential leak of memory pointed to by 'catd'
160}
161DEF_WEAK(catopen)__asm__(".weak " "catopen" " ; " "catopen" " = " "_libc_catopen"
)
;
162
163static nl_catd
164load_msgcat(const char *path)
165{
166 struct stat st;
167 nl_catd catd;
168 void *data;
169 int fd;
170
171 catd = NULL((void *)0);
172
173 if ((fd = open(path, O_RDONLY0x0000|O_CLOEXEC0x10000)) == -1)
21
Assuming the condition is false
22
Taking false branch
174 return (nl_catd) -1;
175
176 if (fstat(fd, &st) != 0) {
23
Assuming the condition is false
24
Taking false branch
177 close (fd);
178 return (nl_catd) -1;
179 }
180
181 if (st.st_size > INT_MAX2147483647 || st.st_size < sizeof (struct _nls_cat_hdr)) {
25
Assuming field 'st_size' is <= INT_MAX
26
Assuming the condition is false
27
Taking false branch
182 errno(*__errno()) = EINVAL22;
183 close (fd);
184 return (nl_catd) -1;
185 }
186
187 data = mmap(0, st.st_size, PROT_READ0x01, MAP_SHARED0x0001, fd, 0);
188 close (fd);
189
190 if (data == MAP_FAILED((void *)-1))
28
Assuming the condition is false
29
Taking false branch
191 return (nl_catd) -1;
192
193 if (ntohl(((struct _nls_cat_hdr *) data)->__magic)(__uint32_t)(__builtin_constant_p(((struct _nls_cat_hdr *) data
)->__magic) ? (__uint32_t)(((__uint32_t)(((struct _nls_cat_hdr
*) data)->__magic) & 0xff) << 24 | ((__uint32_t
)(((struct _nls_cat_hdr *) data)->__magic) & 0xff00) <<
8 | ((__uint32_t)(((struct _nls_cat_hdr *) data)->__magic
) & 0xff0000) >> 8 | ((__uint32_t)(((struct _nls_cat_hdr
*) data)->__magic) & 0xff000000) >> 24) : __swap32md
(((struct _nls_cat_hdr *) data)->__magic))
!= _NLS_MAGIC0xff88ff89
)
30
'?' condition is false
31
Assuming the condition is false
32
Taking false branch
194 goto invalid;
195
196 if ((catd = malloc(sizeof (*catd))) == 0)
33
Memory is allocated
34
Assuming the condition is false
35
Taking false branch
197 goto invalid;
198
199 catd->__data = data;
200 catd->__size = st.st_size;
201
202 if (verify_msgcat(catd))
36
Taking false branch
203 goto invalid;
204
205 return catd;
206
207invalid:
208 free(catd);
209 munmap(data, st.st_size);
210 errno(*__errno()) = EINVAL22;
211 return (nl_catd) -1;
212}
213
214static int
215verify_msgcat(nl_catd catd)
216{
217 struct _nls_cat_hdr *cat;
218 struct _nls_set_hdr *set;
219 struct _nls_msg_hdr *msg;
220 size_t remain;
221 int hdr_offset, i, index, j, msgs, nmsgs, nsets, off, txt_offset;
222
223 remain = catd->__size;
224 cat = (struct _nls_cat_hdr *) catd->__data;
225
226 hdr_offset = ntohl(cat->__msg_hdr_offset)(__uint32_t)(__builtin_constant_p(cat->__msg_hdr_offset) ?
(__uint32_t)(((__uint32_t)(cat->__msg_hdr_offset) & 0xff
) << 24 | ((__uint32_t)(cat->__msg_hdr_offset) &
0xff00) << 8 | ((__uint32_t)(cat->__msg_hdr_offset)
& 0xff0000) >> 8 | ((__uint32_t)(cat->__msg_hdr_offset
) & 0xff000000) >> 24) : __swap32md(cat->__msg_hdr_offset
))
;
227 nsets = ntohl(cat->__nsets)(__uint32_t)(__builtin_constant_p(cat->__nsets) ? (__uint32_t
)(((__uint32_t)(cat->__nsets) & 0xff) << 24 | ((
__uint32_t)(cat->__nsets) & 0xff00) << 8 | ((__uint32_t
)(cat->__nsets) & 0xff0000) >> 8 | ((__uint32_t)
(cat->__nsets) & 0xff000000) >> 24) : __swap32md
(cat->__nsets))
;
228 txt_offset = ntohl(cat->__msg_txt_offset)(__uint32_t)(__builtin_constant_p(cat->__msg_txt_offset) ?
(__uint32_t)(((__uint32_t)(cat->__msg_txt_offset) & 0xff
) << 24 | ((__uint32_t)(cat->__msg_txt_offset) &
0xff00) << 8 | ((__uint32_t)(cat->__msg_txt_offset)
& 0xff0000) >> 8 | ((__uint32_t)(cat->__msg_txt_offset
) & 0xff000000) >> 24) : __swap32md(cat->__msg_txt_offset
))
;
229
230 /* catalog must contain at least one set and no negative offsets */
231 if (nsets < 1 || hdr_offset < 0 || txt_offset < 0)
232 return (1);
233
234 remain -= sizeof (*cat);
235
236 /* check if offsets or set size overflow */
237 if (remain <= hdr_offset || remain <= ntohl(cat->__msg_txt_offset)(__uint32_t)(__builtin_constant_p(cat->__msg_txt_offset) ?
(__uint32_t)(((__uint32_t)(cat->__msg_txt_offset) & 0xff
) << 24 | ((__uint32_t)(cat->__msg_txt_offset) &
0xff00) << 8 | ((__uint32_t)(cat->__msg_txt_offset)
& 0xff0000) >> 8 | ((__uint32_t)(cat->__msg_txt_offset
) & 0xff000000) >> 24) : __swap32md(cat->__msg_txt_offset
))
||
238 remain / sizeof (*set) < nsets)
239 return (1);
240
241 set = (struct _nls_set_hdr *) ((char *) catd->__data + sizeof (*cat));
242
243 /* make sure that msg has space for at least one index */
244 if (remain - hdr_offset < sizeof(*msg))
245 return (1);
246
247 msg = (struct _nls_msg_hdr *) ((char *) catd->__data + sizeof (*cat)
248 + hdr_offset);
249
250 /* validate and retrieve largest string offset from sets */
251 off = 0;
252 for (i = 0; i < nsets; i++) {
253 index = ntohl(set[i].__index)(__uint32_t)(__builtin_constant_p(set[i].__index) ? (__uint32_t
)(((__uint32_t)(set[i].__index) & 0xff) << 24 | ((__uint32_t
)(set[i].__index) & 0xff00) << 8 | ((__uint32_t)(set
[i].__index) & 0xff0000) >> 8 | ((__uint32_t)(set[i
].__index) & 0xff000000) >> 24) : __swap32md(set[i]
.__index))
;
254 nmsgs = ntohl(set[i].__nmsgs)(__uint32_t)(__builtin_constant_p(set[i].__nmsgs) ? (__uint32_t
)(((__uint32_t)(set[i].__nmsgs) & 0xff) << 24 | ((__uint32_t
)(set[i].__nmsgs) & 0xff00) << 8 | ((__uint32_t)(set
[i].__nmsgs) & 0xff0000) >> 8 | ((__uint32_t)(set[i
].__nmsgs) & 0xff000000) >> 24) : __swap32md(set[i]
.__nmsgs))
;
255 /* set must contain at least one message */
256 if (index < 0 || nmsgs < 1)
257 return (1);
258
259 if (INT_MAX2147483647 - nmsgs < index)
260 return (1);
261 msgs = index + nmsgs;
262
263 /* avoid msg index overflow */
264 if ((remain - hdr_offset) / sizeof(*msg) < msgs)
265 return (1);
266
267 /* retrieve largest string offset */
268 for (j = index; j < nmsgs; j++) {
269 if (ntohl(msg[j].__offset)(__uint32_t)(__builtin_constant_p(msg[j].__offset) ? (__uint32_t
)(((__uint32_t)(msg[j].__offset) & 0xff) << 24 | ((
__uint32_t)(msg[j].__offset) & 0xff00) << 8 | ((__uint32_t
)(msg[j].__offset) & 0xff0000) >> 8 | ((__uint32_t)
(msg[j].__offset) & 0xff000000) >> 24) : __swap32md
(msg[j].__offset))
> INT_MAX2147483647)
270 return (1);
271 off = MAXIMUM(off, ntohl(msg[j].__offset))(((off) > ((__uint32_t)(__builtin_constant_p(msg[j].__offset
) ? (__uint32_t)(((__uint32_t)(msg[j].__offset) & 0xff) <<
24 | ((__uint32_t)(msg[j].__offset) & 0xff00) << 8
| ((__uint32_t)(msg[j].__offset) & 0xff0000) >> 8 |
((__uint32_t)(msg[j].__offset) & 0xff000000) >> 24
) : __swap32md(msg[j].__offset)))) ? (off) : ((__uint32_t)(__builtin_constant_p
(msg[j].__offset) ? (__uint32_t)(((__uint32_t)(msg[j].__offset
) & 0xff) << 24 | ((__uint32_t)(msg[j].__offset) &
0xff00) << 8 | ((__uint32_t)(msg[j].__offset) & 0xff0000
) >> 8 | ((__uint32_t)(msg[j].__offset) & 0xff000000
) >> 24) : __swap32md(msg[j].__offset))))
;
272 }
273 }
274
275 /* check if largest string offset is nul-terminated */
276 if (remain - txt_offset < off ||
277 memchr((char *) catd->__data + sizeof(*cat) + txt_offset + off,
278 '\0', remain - txt_offset - off) == NULL((void *)0))
279 return (1);
280
281 return (0);
282}
283