Bug Summary

File:pr/Linux6.7_x86_64_gcc_glibc_PTH_64_DBG.OBJ/config/../../config/nsinstall.c
Warning:line 425, column 13
Access to field 'd_ino' results in a dereference of a null pointer (loaded from variable 'ep')

Annotated Source Code

Press '?' to see keyboard shortcuts

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/nspr/Linux6.7_x86_64_gcc_glibc_PTH_64_DBG.OBJ/config -fcoverage-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nspr/Linux6.7_x86_64_gcc_glibc_PTH_64_DBG.OBJ/config -resource-dir /usr/lib/llvm-18/lib/clang/18 -U NDEBUG -D DEBUG_jenkins -D PACKAGE_NAME="" -D PACKAGE_TARNAME="" -D PACKAGE_VERSION="" -D PACKAGE_STRING="" -D PACKAGE_BUGREPORT="" -D PACKAGE_URL="" -D DEBUG=1 -D HAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1 -D HAVE_VISIBILITY_PRAGMA=1 -D XP_UNIX=1 -D _GNU_SOURCE=1 -D HAVE_FCNTL_FILE_LOCKING=1 -D HAVE_POINTER_LOCALTIME_R=1 -D LINUX=1 -D HAVE_DLADDR=1 -D HAVE_GETTID=1 -D HAVE_LCHOWN=1 -D HAVE_SETPRIORITY=1 -D HAVE_STRERROR=1 -D HAVE_SYSCALL=1 -D HAVE_SECURE_GETENV=1 -D _REENTRANT=1 -D FORCE_PR_LOG -D _PR_PTHREADS -U HAVE_CVAR_BUILT_ON_SEM -internal-isystem /usr/lib/llvm-18/lib/clang/18/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 -ferror-limit 19 -fgnuc-version=4.2.1 -fno-inline -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-07-17-052436-2337001-1 -x c ../../config/nsinstall.c
1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6/*
7** Netscape portable install command.
8**
9** Brendan Eich, 7/20/95
10*/
11#include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
12#include <assert.h>
13#include <fcntl.h>
14#include <grp.h>
15#include <pwd.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19#include <utime.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <dirent.h>
23#include <errno(*__errno_location ()).h>
24#include <stdarg.h>
25#ifdef USE_REENTRANT_LIBC
26#include "libc_r.h"
27#endif /* USE_REENTRANT_LIBC */
28
29#include "pathsub.h"
30
31#define HAVE_FCHMOD
32
33
34/*
35 * Does getcwd() take NULL as the first argument and malloc
36 * the result buffer?
37 */
38#if !defined(DARWIN)
39#define GETCWD_CAN_MALLOC
40#endif
41
42#if defined(LINUX1) || defined(__GNU__) || defined(__GLIBC__2)
43#include <getopt.h>
44#endif
45
46#ifdef QNX
47#define d_ino d_stat.st_ino
48#endif
49
50static void
51usage(void)
52{
53 fprintf(stderrstderr,
54 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
55 " %*s [-DdltR] file [file ...] directory\n",
56 program, (int)strlen(program), "");
57 exit(2);
58}
59
60static int
61mkdirs(char *path, mode_t mode)
62{
63 char *cp;
64 struct stat sb;
65 int res;
66
67 while (*path == '/' && path[1] == '/') {
68 path++;
69 }
70 for (cp = strrchr(path, '/'); cp && cp != path && cp[-1] == '/'; cp--)
71 ;
72 if (cp && cp != path) {
73 *cp = '\0';
74 if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)((((sb.st_mode)) & 0170000) == (0040000))) &&
75 mkdirs(path, mode) < 0) {
76 return -1;
77 }
78 *cp = '/';
79 }
80 res = mkdir(path, mode);
81 if ((res != 0) && (errno(*__errno_location ()) == EEXIST17)) {
82 return 0;
83 }
84 else {
85 return res;
86 }
87}
88
89static uid_t
90touid(char *owner)
91{
92 struct passwd *pw;
93 uid_t uid;
94 char *cp;
95
96 pw = getpwnam(owner);
97 if (pw) {
98 return pw->pw_uid;
99 }
100 uid = strtol(owner, &cp, 0);
101 if (uid == 0 && cp == owner) {
102 fail("cannot find uid for %s", owner);
103 }
104 return uid;
105}
106
107static gid_t
108togid(char *group)
109{
110 struct group *gr;
111 gid_t gid;
112 char *cp;
113
114 gr = getgrnam(group);
115 if (gr) {
116 return gr->gr_gid;
117 }
118 gid = strtol(group, &cp, 0);
119 if (gid == 0 && cp == group) {
120 fail("cannot find gid for %s", group);
121 }
122 return gid;
123}
124
125int
126main(int argc, char **argv)
127{
128 int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
129 mode_t mode = 0755;
130 char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ8192];
131 uid_t uid;
132 gid_t gid;
133 struct stat sb, tosb;
134 struct utimbuf utb;
135
136 program = argv[0];
137 cwd = linkname = linkprefix = owner = group = 0;
138 onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
139
140 while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF(-1)) {
141 switch (opt) {
142 case 'C':
143 cwd = optarg;
144 break;
145 case 'D':
146 onlydir = 1;
147 break;
148 case 'd':
149 dodir = 1;
150 break;
151 case 'l':
152 dolink = 1;
153 break;
154 case 'L':
155 linkprefix = optarg;
156 lplen = strlen(linkprefix);
157 dolink = 1;
158 break;
159 case 'R':
160 dolink = dorelsymlink = 1;
161 break;
162 case 'm':
163 mode = strtoul(optarg, &cp, 8);
164 if (mode == 0 && cp == optarg) {
165 usage();
166 }
167 break;
168 case 'o':
169 owner = optarg;
170 break;
171 case 'g':
172 group = optarg;
173 break;
174 case 't':
175 dotimes = 1;
176 break;
177 default:
178 usage();
179 }
180 }
181
182 argc -= optind;
183 argv += optind;
184 if (argc < 2 - onlydir) {
185 usage();
186 }
187
188 todir = argv[argc-1];
189 if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)((((sb.st_mode)) & 0170000) == (0040000))) &&
190 mkdirs(todir, 0777) < 0) {
191 fail("cannot make directory %s", todir);
192 }
193 if (onlydir) {
194 return 0;
195 }
196
197 if (!cwd) {
198#ifdef GETCWD_CAN_MALLOC
199 cwd = getcwd(0, PATH_MAX4096);
200#else
201 cwd = malloc(PATH_MAX4096 + 1);
202 cwd = getcwd(cwd, PATH_MAX4096);
203#endif
204 }
205 xchdir(todir);
206#ifdef GETCWD_CAN_MALLOC
207 todir = getcwd(0, PATH_MAX4096);
208#else
209 todir = malloc(PATH_MAX4096 + 1);
210 todir = getcwd(todir, PATH_MAX4096);
211#endif
212 xchdir(cwd);
213 tdlen = strlen(todir);
214
215 uid = owner ? touid(owner) : -1;
216 gid = group ? togid(group) : -1;
217
218 while (--argc > 0) {
219 name = *argv++;
220 len = strlen(name);
221 base = xbasename(name);
222 bnlen = strlen(base);
223 toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
224 sprintf(toname, "%s/%s", todir, base);
225 exists = (lstat(toname, &tosb) == 0);
226
227 if (dodir) {
228 /* -d means create a directory, always */
229 if (exists && !S_ISDIR(tosb.st_mode)((((tosb.st_mode)) & 0170000) == (0040000))) {
230 (void) unlink(toname);
231 exists = 0;
232 }
233 if (!exists && mkdir(toname, mode) < 0) {
234 fail("cannot make directory %s", toname);
235 }
236 if ((owner || group) && chown(toname, uid, gid) < 0) {
237 fail("cannot change owner of %s", toname);
238 }
239 } else if (dolink) {
240 if (*name == '/') {
241 /* source is absolute pathname, link to it directly */
242 linkname = 0;
243 } else {
244 if (linkprefix) {
245 /* -L implies -l and prefixes names with a $cwd arg. */
246 len += lplen + 1;
247 linkname = (char*)xmalloc(len + 1);
248 sprintf(linkname, "%s/%s", linkprefix, name);
249 } else if (dorelsymlink) {
250 /* Symlink the relative path from todir to source name. */
251 linkname = (char*)xmalloc(PATH_MAX4096);
252
253 if (*todir == '/') {
254 /* todir is absolute: skip over common prefix. */
255 lplen = relatepaths(todir, cwd, linkname);
256 strcpy(linkname + lplen, name);
257 } else {
258 /* todir is named by a relative path: reverse it. */
259 reversepath(todir, name, len, linkname);
260 xchdir(cwd);
261 }
262
263 len = strlen(linkname);
264 }
265 name = linkname;
266 }
267
268 /* Check for a pre-existing symlink with identical content. */
269 if (exists &&
270 (!S_ISLNK(tosb.st_mode)((((tosb.st_mode)) & 0170000) == (0120000)) ||
271 readlink(toname, buf, sizeof buf) != len ||
272 strncmp(buf, name, len) != 0)) {
273 (void) (S_ISDIR(tosb.st_mode)((((tosb.st_mode)) & 0170000) == (0040000)) ? rmdir : unlink)(toname);
274 exists = 0;
275 }
276 if (!exists && symlink(name, toname) < 0) {
277 fail("cannot make symbolic link %s", toname);
278 }
279#ifdef HAVE_LCHOWN1
280 if ((owner || group) && lchown(toname, uid, gid) < 0) {
281 fail("cannot change owner of %s", toname);
282 }
283#endif
284
285 if (linkname) {
286 free(linkname);
287 linkname = 0;
288 }
289 } else {
290 /* Copy from name to toname, which might be the same file. */
291 fromfd = open(name, O_RDONLY00);
292 if (fromfd < 0 || fstat(fromfd, &sb) < 0) {
293 fail("cannot access %s", name);
294 }
295 if (exists && (!S_ISREG(tosb.st_mode)((((tosb.st_mode)) & 0170000) == (0100000)) || access(toname, W_OK2) < 0)) {
296 (void) (S_ISDIR(tosb.st_mode)((((tosb.st_mode)) & 0170000) == (0040000)) ? rmdir : unlink)(toname);
297 }
298 tofd = open(toname, O_CREAT0100 | O_WRONLY01, 0666);
299 if (tofd < 0) {
300 fail("cannot create %s", toname);
301 }
302
303 bp = buf;
304 while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
305 while ((wc = write(tofd, bp, cc)) > 0) {
306 if ((cc -= wc) == 0) {
307 break;
308 }
309 bp += wc;
310 }
311 if (wc < 0) {
312 fail("cannot write to %s", toname);
313 }
314 }
315 if (cc < 0) {
316 fail("cannot read from %s", name);
317 }
318
319 if (ftruncate(tofd, sb.st_size) < 0) {
320 fail("cannot truncate %s", toname);
321 }
322 if (dotimes) {
323 utb.actime = sb.st_atimest_atim.tv_sec;
324 utb.modtime = sb.st_mtimest_mtim.tv_sec;
325 if (utime(toname, &utb) < 0) {
326 fail("cannot set times of %s", toname);
327 }
328 }
329#ifdef HAVE_FCHMOD
330 if (fchmod(tofd, mode) < 0)
331#else
332 if (chmod(toname, mode) < 0)
333#endif
334 fail("cannot change mode of %s", toname);
335 if ((owner || group) && fchown(tofd, uid, gid) < 0) {
336 fail("cannot change owner of %s", toname);
337 }
338
339 /* Must check for delayed (NFS) write errors on close. */
340 if (close(tofd) < 0) {
341 fail("cannot write to %s", toname);
342 }
343 close(fromfd);
344 }
345
346 free(toname);
347 }
348
349 free(cwd);
350 free(todir);
351 return 0;
352}
353
354/*
355** Pathname subroutines.
356**
357** Brendan Eich, 8/29/95
358*/
359
360char *program;
361
362void
363fail(char *format, ...)
364{
365 int error;
366 va_list ap;
367
368#ifdef USE_REENTRANT_LIBC
369 R_STRERROR_INIT_R();
370#endif
371
372 error = errno(*__errno_location ());
373 fprintf(stderrstderr, "%s: ", program);
374 va_start(ap, format)__builtin_va_start(ap, format);
375 vfprintf(stderrstderr, format, ap);
376 va_end(ap)__builtin_va_end(ap);
377 if (error)
378
379#ifdef USE_REENTRANT_LIBC
380 R_STRERROR_R(errno(*__errno_location ()));
381 fprintf(stderrstderr, ": %s", r_strerror_r);
382#else
383 fprintf(stderrstderr, ": %s", strerror(errno(*__errno_location ())));
384#endif
385
386 putc('\n', stderrstderr);
387 exit(1);
388}
389
390char *
391getcomponent(char *path, char *name)
392{
393 if (*path == '\0') {
394 return 0;
395 }
396 if (*path == '/') {
397 *name++ = '/';
398 } else {
399 do {
400 *name++ = *path++;
401 } while (*path != '/' && *path != '\0');
402 }
403 *name = '\0';
404 while (*path == '/') {
405 path++;
406 }
407 return path;
408}
409
410char *
411ino2name(ino_t ino, char *dir)
412{
413 DIR *dp;
414 struct dirent *ep;
415 char *name;
416
417 dp = opendir("..");
418 if (!dp) {
9
Assuming 'dp' is non-null
10
Taking false branch
419 fail("cannot read parent directory");
420 }
421 for (;;) {
11
Loop condition is true. Entering loop body
422 if (!(ep = readdir(dp))) {
12
Value assigned to 'ep'
13
Assuming 'ep' is null
14
Assuming pointer value is null
15
Taking true branch
423 fail("cannot find current directory");
424 }
425 if (ep->d_ino == ino) {
16
Access to field 'd_ino' results in a dereference of a null pointer (loaded from variable 'ep')
426 break;
427 }
428 }
429 name = xstrdup(ep->d_name);
430 closedir(dp);
431 return name;
432}
433
434void *
435xmalloc(size_t size)
436{
437 void *p = malloc(size);
438 if (!p) {
439 fail("cannot allocate %u bytes", size);
440 }
441 return p;
442}
443
444char *
445xstrdup(char *s)
446{
447 return strcpy((char*)xmalloc(strlen(s) + 1), s);
448}
449
450char *
451xbasename(char *path)
452{
453 char *cp;
454
455 while ((cp = strrchr(path, '/')) && cp[1] == '\0') {
456 *cp = '\0';
457 }
458 if (!cp) {
459 return path;
460 }
461 return cp + 1;
462}
463
464void
465xchdir(char *dir)
466{
467 if (chdir(dir) < 0) {
468 fail("cannot change directory to %s", dir);
469 }
470}
471
472int
473relatepaths(char *from, char *to, char *outpath)
474{
475 char *cp, *cp2;
476 int len;
477 char buf[NAME_MAX256];
478
479 assert(*from == '/' && *to == '/')((void) sizeof ((*from == '/' && *to == '/') ? 1 : 0)
, __extension__ ({ if (*from == '/' && *to == '/') ; else
__assert_fail ("*from == '/' && *to == '/'", "../../config/nsinstall.c"
, 479, __extension__ __PRETTY_FUNCTION__); }))
;
480 for (cp = to, cp2 = from; *cp == *cp2; cp++, cp2++)
481 if (*cp == '\0') {
482 break;
483 }
484 while (cp[-1] != '/') {
485 cp--, cp2--;
486 }
487 if (cp - 1 == to) {
488 /* closest common ancestor is /, so use full pathname */
489 len = strlen(strcpy(outpath, to));
490 if (outpath[len] != '/') {
491 outpath[len++] = '/';
492 outpath[len] = '\0';
493 }
494 } else {
495 len = 0;
496 while ((cp2 = getcomponent(cp2, buf)) != 0) {
497 strcpy(outpath + len, "../");
498 len += 3;
499 }
500 while ((cp = getcomponent(cp, buf)) != 0) {
501 sprintf(outpath + len, "%s/", buf);
502 len += strlen(outpath + len);
503 }
504 }
505 return len;
506}
507
508void
509reversepath(char *inpath, char *name, int len, char *outpath)
510{
511 char *cp, *cp2;
512 char buf[NAME_MAX256];
513 struct stat sb;
514
515 cp = strcpy(outpath + PATH_MAX4096 - (len + 1), name);
516 cp2 = inpath;
517 while ((cp2 = getcomponent(cp2, buf)) != 0) {
1
Loop condition is true. Entering loop body
518 if (strcmp(buf, ".") == 0) {
2
Assuming the condition is false
3
Taking false branch
519 continue;
520 }
521 if (strcmp(buf, "..") == 0) {
4
Assuming the condition is true
5
Taking true branch
522 if (stat(".", &sb) < 0) {
6
Assuming the condition is false
7
Taking false branch
523 fail("cannot stat current directory");
524 }
525 name = ino2name(sb.st_ino, "..");
8
Calling 'ino2name'
526 len = strlen(name);
527 cp -= len + 1;
528 strcpy(cp, name);
529 cp[len] = '/';
530 free(name);
531 xchdir("..");
532 } else {
533 cp -= 3;
534 memcpy(cp, "../", 3);
535 xchdir(buf);
536 }
537 }
538 strcpy(outpath, cp);
539}