clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nsinstall.c -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 -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nss/coreconf/nsinstall -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nss/coreconf/nsinstall -resource-dir /usr/lib/llvm-19/lib/clang/19 -D HAVE_STRERROR -D LINUX -D linux -D XP_UNIX -D XP_UNIX -D DEBUG -U NDEBUG -D _DEFAULT_SOURCE -D _BSD_SOURCE -D _POSIX_SOURCE -D SDB_MEASURE_USE_TEMP_DIR -D _REENTRANT -D DEBUG -U NDEBUG -D _DEFAULT_SOURCE -D _BSD_SOURCE -D _POSIX_SOURCE -D SDB_MEASURE_USE_TEMP_DIR -D _REENTRANT -D NSS_DISABLE_SSE3 -D NSS_NO_INIT_SUPPORT -D USE_UTIL_DIRECTLY -D NO_NSPR_10_SUPPORT -D SSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES -I ../../../dist/Linux6.12_x86_64_gcc_glibc_PTH_64_DBG.OBJ/include -I ../../../dist/public/coreconf -I ../../../dist/private/coreconf -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=c99 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-01-06-222215-268153-1 -x c nsinstall.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */ |
9 | #include <assert.h> |
10 | #include <fcntl.h> |
11 | #include <string.h> |
12 | #if defined(_WINDOWS) |
13 | #include <windows.h> |
14 | typedef unsigned int mode_t; |
15 | #else |
16 | #include <grp.h> |
17 | #include <pwd.h> |
18 | #include <errno.h> |
19 | #include <stdlib.h> |
20 | #include <unistd.h> |
21 | #include <utime.h> |
22 | #endif |
23 | #include <sys/types.h> |
24 | #include <sys/stat.h> |
25 | #include "pathsub.h" |
26 | |
27 | #define HAVE_LCHOWN |
28 | |
29 | #if defined(AIX) || defined(HPUX) || defined(LINUX) || defined(NTO) || defined(DARWIN) || defined(__riscos__) |
30 | #undef HAVE_LCHOWN |
31 | #endif |
32 | |
33 | #define HAVE_FCHMOD |
34 | |
35 | #ifdef LINUX |
36 | #include <getopt.h> |
37 | #endif |
38 | |
39 | #if defined(NCR) |
40 | #if !defined(S_ISLNK) && defined(S_IFLNK) |
41 | #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK) |
42 | #endif |
43 | #endif |
44 | |
45 | #ifdef GETCWD_CANT_MALLOC |
46 | |
47 | |
48 | |
49 | |
50 | static char * |
51 | getcwd_do_malloc(char *path, int len) { |
52 | |
53 | if (!path) { |
54 | path = malloc(PATH_MAX +1); |
55 | if (!path) return NULL; |
56 | } |
57 | return getcwd(path, PATH_MAX); |
58 | } |
59 | #define GETCWD getcwd_do_malloc |
60 | #else |
61 | #define GETCWD getcwd |
62 | #endif |
63 | |
64 | |
65 | static void |
66 | usage(void) |
67 | { |
68 | fprintf(stderr, |
69 | "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n" |
70 | " %*s [-DdltR] file [file ...] directory\n", |
71 | program, (int)strlen(program), ""); |
72 | exit(2); |
73 | } |
74 | |
75 | |
76 | static int |
77 | mkdirs(char *path, mode_t mode) |
78 | { |
79 | char * cp; |
80 | int rv; |
81 | struct stat sb; |
82 | |
83 | if (!path || !path[0]) |
84 | fail("Null pointer or empty string passed to mkdirs()"); |
85 | while (*path == '/' && path[1] == '/') |
86 | path++; |
87 | for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--); |
88 | if (cp && cp != path) { |
89 | *cp = '\0'; |
90 | if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) && |
91 | mkdirs(path, mode) < 0) { |
92 | return -1; |
93 | } |
94 | *cp = '/'; |
95 | } |
96 | rv = mkdir(path, mode); |
97 | if (rv) { |
98 | if (errno != EEXIST) |
99 | fail("mkdirs cannot make %s", path); |
100 | fprintf(stderr, "directory creation race: %s\n", path); |
101 | if (!stat(path, &sb) && S_ISDIR(sb.st_mode)) |
102 | rv = 0; |
103 | } |
104 | return rv; |
105 | } |
106 | |
107 | static uid_t |
108 | touid(char *owner) |
109 | { |
110 | struct passwd *pw; |
111 | uid_t uid; |
112 | char *cp; |
113 | |
114 | if (!owner || !owner[0]) |
115 | fail("Null pointer or empty string passed to touid()"); |
116 | pw = getpwnam(owner); |
117 | if (pw) |
118 | return pw->pw_uid; |
119 | uid = strtol(owner, &cp, 0); |
120 | if (uid == 0 && cp == owner) |
121 | fail("cannot find uid for %s", owner); |
122 | return uid; |
123 | } |
124 | |
125 | static gid_t |
126 | togid(char *group) |
127 | { |
128 | struct group *gr; |
129 | gid_t gid; |
130 | char *cp; |
131 | |
132 | if (!group || !group[0]) |
| 1 | Assuming 'group' is null | |
|
133 | fail("Null pointer or empty string passed to togid()"); |
134 | gr = getgrnam(group); |
135 | if (gr) |
| |
| |
136 | return gr->gr_gid; |
137 | gid = strtol(group, &cp, 0); |
| 4 | | Null pointer passed to 1st parameter expecting 'nonnull' |
|
138 | if (gid == 0 && cp == group) |
139 | fail("cannot find gid for %s", group); |
140 | return gid; |
141 | } |
142 | |
143 | void * const uninit = (void *)0xdeadbeef; |
144 | |
145 | int |
146 | main(int argc, char **argv) |
147 | { |
148 | char * base = uninit; |
149 | char * bp = uninit; |
150 | char * cp = uninit; |
151 | char * cwd = 0; |
152 | char * group = 0; |
153 | char * linkname = 0; |
154 | char * linkprefix = 0; |
155 | char * name = uninit; |
156 | char * owner = 0; |
157 | char * todir = uninit; |
158 | char * toname = uninit; |
159 | |
160 | int bnlen = -1; |
161 | int cc = 0; |
162 | int dodir = 0; |
163 | int dolink = 0; |
164 | int dorelsymlink = 0; |
165 | int dotimes = 0; |
166 | int exists = 0; |
167 | int fromfd = -1; |
168 | int len = -1; |
169 | int lplen = 0; |
170 | int onlydir = 0; |
171 | int opt = -1; |
172 | int tdlen = -1; |
173 | int tofd = -1; |
174 | int wc = -1; |
175 | |
176 | mode_t mode = 0755; |
177 | |
178 | uid_t uid = -1; |
179 | gid_t gid = -1; |
180 | |
181 | struct stat sb; |
182 | struct stat tosb; |
183 | struct utimbuf utb; |
184 | char buf[BUFSIZ]; |
185 | |
186 | program = strrchr(argv[0], '/'); |
187 | if (!program) |
188 | program = strrchr(argv[0], '\\'); |
189 | program = program ? program+1 : argv[0]; |
190 | |
191 | |
192 | while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) { |
193 | switch (opt) { |
194 | case 'C': cwd = optarg; break; |
195 | case 'D': onlydir = 1; break; |
196 | case 'd': dodir = 1; break; |
197 | case 'l': dolink = 1; break; |
198 | case 'L': |
199 | linkprefix = optarg; |
200 | lplen = strlen(linkprefix); |
201 | dolink = 1; |
202 | break; |
203 | case 'R': dolink = dorelsymlink = 1; break; |
204 | case 'm': |
205 | mode = strtoul(optarg, &cp, 8); |
206 | if (mode == 0 && cp == optarg) |
207 | usage(); |
208 | break; |
209 | case 'o': owner = optarg; break; |
210 | case 'g': group = optarg; break; |
211 | case 't': dotimes = 1; break; |
212 | default: usage(); |
213 | } |
214 | } |
215 | |
216 | argc -= optind; |
217 | argv += optind; |
218 | if (argc < 2 - onlydir) |
219 | usage(); |
220 | |
221 | todir = argv[argc-1]; |
222 | if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) && |
223 | mkdirs(todir, 0777) < 0) { |
224 | fail("cannot mkdir -p %s", todir); |
225 | } |
226 | if (onlydir) |
227 | return 0; |
228 | |
229 | if (!cwd) { |
230 | cwd = GETCWD(0, PATH_MAX); |
231 | if (!cwd) |
232 | fail("could not get CWD"); |
233 | } |
234 | |
235 | |
236 | xchdir(todir); |
237 | todir = GETCWD(0, PATH_MAX); |
238 | if (!todir) |
239 | fail("could not get CWD in todir"); |
240 | tdlen = strlen(todir); |
241 | |
242 | |
243 | xchdir(cwd); |
244 | |
245 | uid = owner ? touid(owner) : -1; |
246 | gid = group ? togid(group) : -1; |
247 | |
248 | while (--argc > 0) { |
249 | name = *argv++; |
250 | len = strlen(name); |
251 | base = xbasename(name); |
252 | bnlen = strlen(base); |
253 | size_t toname_len = tdlen + 1 + bnlen + 1; |
254 | toname = (char*)xmalloc(toname_len); |
255 | snprintf(toname, toname_len, "%s/%s", todir, base); |
256 | retry: |
257 | exists = (lstat(toname, &tosb) == 0); |
258 | |
259 | if (dodir) { |
260 | |
261 | if (exists && !S_ISDIR(tosb.st_mode)) { |
262 | int rv = unlink(toname); |
263 | if (rv) |
264 | fail("cannot unlink %s", toname); |
265 | exists = 0; |
266 | } |
267 | if (!exists && mkdir(toname, mode) < 0) { |
268 | |
269 | if (errno == EEXIST && !stat(toname, &sb) && |
270 | S_ISDIR(sb.st_mode)) { |
271 | fprintf(stderr, "directory creation race: %s\n", toname); |
272 | goto retry; |
273 | } |
274 | fail("cannot make directory %s", toname); |
275 | } |
276 | if ((owner || group) && chown(toname, uid, gid) < 0) |
277 | fail("cannot change owner of %s", toname); |
278 | } else if (dolink) { |
279 | if (*name == '/') { |
280 | |
281 | linkname = 0; |
282 | } else { |
283 | if (linkprefix) { |
284 | |
285 | len += lplen + 1; |
286 | linkname = (char*)xmalloc(len + 1); |
287 | snprintf(linkname, len+1, "%s/%s", linkprefix, name); |
288 | } else if (dorelsymlink) { |
289 | |
290 | linkname = (char*)xmalloc(PATH_MAX); |
291 | |
292 | if (*todir == '/') { |
293 | |
294 | lplen = relatepaths(todir, cwd, linkname); |
295 | strcpy(linkname + lplen, name); |
296 | } else { |
297 | |
298 | reversepath(todir, name, len, linkname); |
299 | xchdir(cwd); |
300 | } |
301 | |
302 | len = strlen(linkname); |
303 | } |
304 | name = linkname; |
305 | } |
306 | |
307 | |
308 | if (exists && |
309 | (!S_ISLNK(tosb.st_mode) || |
310 | readlink(toname, buf, sizeof buf) != len || |
311 | strncmp(buf, name, len) != 0)) { |
312 | int rmrv; |
313 | rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); |
314 | if (rmrv < 0) { |
315 | fail("destination exists, cannot remove %s", toname); |
316 | } |
317 | exists = 0; |
318 | } |
319 | if (!exists && symlink(name, toname) < 0) { |
320 | if (errno == EEXIST) { |
321 | fprintf(stderr, "symlink creation race: %s\n", toname); |
322 | fail("symlink was attempted in working directory %s " |
323 | "from %s to %s.\n", cwd, name, toname); |
324 | goto retry; |
325 | } |
326 | diagnosePath(toname); |
327 | fail("cannot make symbolic link %s", toname); |
328 | } |
329 | #ifdef HAVE_LCHOWN |
330 | if ((owner || group) && lchown(toname, uid, gid) < 0) |
331 | fail("cannot change owner of %s", toname); |
332 | #endif |
333 | |
334 | if (linkname) { |
335 | free(linkname); |
336 | linkname = 0; |
337 | } |
338 | } else { |
339 | |
340 | fromfd = open(name, O_RDONLY); |
341 | if (fromfd < 0 || fstat(fromfd, &sb) < 0) |
342 | fail("cannot access %s", name); |
343 | if (exists && |
344 | (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) { |
345 | int rmrv; |
346 | rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); |
347 | if (rmrv < 0) { |
348 | fail("destination exists, cannot remove %s", toname); |
349 | } |
350 | } |
351 | tofd = open(toname, O_CREAT | O_WRONLY, 0666); |
352 | if (tofd < 0) |
353 | fail("cannot create %s", toname); |
354 | |
355 | bp = buf; |
356 | while ((cc = read(fromfd, bp, sizeof buf)) > 0) { |
357 | while ((wc = write(tofd, bp, cc)) > 0) { |
358 | if ((cc -= wc) == 0) |
359 | break; |
360 | bp += wc; |
361 | } |
362 | if (wc < 0) |
363 | fail("cannot write to %s", toname); |
364 | } |
365 | if (cc < 0) |
366 | fail("cannot read from %s", name); |
367 | |
368 | if (ftruncate(tofd, sb.st_size) < 0) |
369 | fail("cannot truncate %s", toname); |
370 | if (dotimes) { |
371 | utb.actime = sb.st_atime; |
372 | utb.modtime = sb.st_mtime; |
373 | if (utime(toname, &utb) < 0) |
374 | fail("cannot set times of %s", toname); |
375 | } |
376 | #ifdef HAVE_FCHMOD |
377 | if (fchmod(tofd, mode) < 0) |
378 | #else |
379 | if (chmod(toname, mode) < 0) |
380 | #endif |
381 | fail("cannot change mode of %s", toname); |
382 | |
383 | if ((owner || group) && fchown(tofd, uid, gid) < 0) |
384 | fail("cannot change owner of %s", toname); |
385 | |
386 | |
387 | if (close(tofd) < 0) |
388 | fail("close reports write error on %s", toname); |
389 | close(fromfd); |
390 | } |
391 | |
392 | free(toname); |
393 | } |
394 | |
395 | free(cwd); |
396 | free(todir); |
397 | return 0; |
398 | } |
399 | |