Bug Summary

File:src/usr.bin/cvs/cvs.c
Warning:line 213, column 7
Although the value stored to 'envstr' is used in the enclosing expression, the value is never actually read from 'envstr'

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 cvs.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/usr.bin/cvs/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/cvs -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/cvs/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/usr.bin/cvs/cvs.c
1/* $OpenBSD: cvs.c,v 1.160 2021/01/27 07:18:16 deraadt Exp $ */
2/*
3 * Copyright (c) 2006, 2007 Joris Vink <joris@openbsd.org>
4 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/stat.h>
29
30#include <ctype.h>
31#include <errno(*__errno()).h>
32#include <pwd.h>
33#include <stdlib.h>
34#include <string.h>
35#include <time.h>
36#include <unistd.h>
37#include <err.h>
38
39#include "cvs.h"
40#include "remote.h"
41#include "hash.h"
42
43extern char *__progname;
44
45/* verbosity level: 0 = really quiet, 1 = quiet, 2 = verbose */
46int verbosity = 2;
47
48/* compression level used with zlib, 0 meaning no compression taking place */
49int cvs_compress = 0;
50int cvs_readrc = 1; /* read .cvsrc on startup */
51int cvs_trace = 0;
52int cvs_nolog = 0;
53int cvs_readonly = 0;
54int cvs_readonlyfs = 0;
55int cvs_nocase = 0; /* set to 1 to disable filename case sensitivity */
56int cvs_noexec = 0; /* set to 1 to disable disk operations (-n option) */
57int cvs_cmdop;
58int cvs_umask = CVS_UMASK_DEFAULT002;
59int cvs_server_active = 0;
60
61char *cvs_tagname = NULL((void *)0);
62char *cvs_defargs; /* default global arguments from .cvsrc */
63char *cvs_rootstr;
64char *cvs_rsh = CVS_RSH_DEFAULT"ssh";
65char *cvs_editor = CVS_EDITOR_DEFAULT"vi";
66char *cvs_homedir = NULL((void *)0);
67char *cvs_tmpdir = CVS_TMPDIR_DEFAULT"/tmp";
68
69struct cvsroot *current_cvsroot = NULL((void *)0);
70struct cvs_cmd *cmdp; /* struct of command we are running */
71
72int cvs_getopt(int, char **);
73__dead__attribute__((__noreturn__)) void usage(void);
74static void cvs_read_rcfile(void);
75
76struct cvs_varhead cvs_variables;
77
78struct wklhead temp_files;
79
80void sighandler(int);
81volatile sig_atomic_t cvs_quit = 0;
82volatile sig_atomic_t sig_received = 0;
83
84extern CVSENTRIES *current_list;
85
86struct hash_table created_directories;
87struct hash_table created_cvs_directories;
88
89void
90sighandler(int sig)
91{
92 sig_received = sig;
93
94 switch (sig) {
95 case SIGINT2:
96 case SIGTERM15:
97 case SIGPIPE13:
98 cvs_quit = 1;
99 break;
100 default:
101 break;
102 }
103}
104
105void
106cvs_cleanup(void)
107{
108 cvs_log(LP_TRACE4, "cvs_cleanup: removing locks");
109 worklist_run(&repo_locks, worklist_unlink);
110
111 cvs_log(LP_TRACE4, "cvs_cleanup: removing temp files");
112 worklist_run(&temp_files, worklist_unlink);
113
114 if (cvs_server_path != NULL((void *)0)) {
115 if (cvs_rmdir(cvs_server_path) == -1)
116 cvs_log(LP_ERR1,
117 "warning: failed to remove server directory: %s",
118 cvs_server_path);
119 free(cvs_server_path);
120 cvs_server_path = NULL((void *)0);
121 }
122
123 if (current_list != NULL((void *)0))
124 cvs_ent_close(current_list, ENT_SYNC1);
125}
126
127__dead__attribute__((__noreturn__)) void
128usage(void)
129{
130 (void)fprintf(stderr(&__sF[2]),
131 "usage: %s [-flnQqRrtvw] [-d root] [-e editor] [-s var=val]\n"
132 " [-T tmpdir] [-z level] command ...\n", __progname);
133 exit(1);
134}
135
136int
137cvs_build_cmd(char ***cmd_argv, char **argv, int argc)
138{
139 int cmd_argc, i, cur;
140 char *cp, *linebuf, *lp;
141
142 if (cmdp->cmd_defargs == NULL((void *)0)) {
143 *cmd_argv = argv;
144 return argc;
145 }
146
147 cur = argc + 2;
148 cmd_argc = 0;
149 *cmd_argv = xcalloc(cur, sizeof(char *));
150 (*cmd_argv)[cmd_argc++] = argv[0];
151
152 linebuf = xstrdup(cmdp->cmd_defargs);
153 for (lp = linebuf; lp != NULL((void *)0);) {
154 cp = strsep(&lp, " \t\b\f\n\r\t\v");
155 if (cp == NULL((void *)0))
156 break;
157 if (*cp == '\0')
158 continue;
159
160 if (cmd_argc == cur) {
161 cur += 8;
162 *cmd_argv = xreallocarray(*cmd_argv, cur,
163 sizeof(char *));
164 }
165
166 (*cmd_argv)[cmd_argc++] = cp;
167 }
168
169 if (cmd_argc + argc > cur) {
170 cur = cmd_argc + argc + 1;
171 *cmd_argv = xreallocarray(*cmd_argv, cur,
172 sizeof(char *));
173 }
174
175 for (i = 1; i < argc; i++)
176 (*cmd_argv)[cmd_argc++] = argv[i];
177
178 (*cmd_argv)[cmd_argc] = NULL((void *)0);
179
180 return cmd_argc;
181}
182
183int
184main(int argc, char **argv)
185{
186 char *envstr, **cmd_argv, **targv;
187 int i, ret, cmd_argc;
188 struct passwd *pw;
189 struct stat st;
190 char fpath[PATH_MAX1024];
191
192 if (pledge("stdio rpath wpath cpath fattr getpw proc exec", NULL((void *)0)) == -1)
193 err(1, "pledge");
194
195 tzset();
196
197 TAILQ_INIT(&cvs_variables)do { (&cvs_variables)->tqh_first = ((void *)0); (&
cvs_variables)->tqh_last = &(&cvs_variables)->tqh_first
; } while (0)
;
198 SLIST_INIT(&repo_locks){ ((&repo_locks)->slh_first) = ((void *)0); };
199 SLIST_INIT(&temp_files){ ((&temp_files)->slh_first) = ((void *)0); };
200
201 hash_table_init(&created_directories, 100);
202 hash_table_init(&created_cvs_directories, 100);
203
204 /* check environment so command-line options override it */
205 if ((envstr = getenv("CVS_RSH")) != NULL((void *)0))
206 cvs_rsh = envstr;
207
208 if (((envstr = getenv("CVSEDITOR")) != NULL((void *)0)) ||
209 ((envstr = getenv("VISUAL")) != NULL((void *)0)) ||
210 ((envstr = getenv("EDITOR")) != NULL((void *)0)))
211 cvs_editor = envstr;
212
213 if ((envstr = getenv("CVSREAD")) != NULL((void *)0))
Although the value stored to 'envstr' is used in the enclosing expression, the value is never actually read from 'envstr'
214 cvs_readonly = 1;
215
216 if ((envstr = getenv("CVSREADONLYFS")) != NULL((void *)0)) {
217 cvs_readonlyfs = 1;
218 cvs_nolog = 1;
219 }
220
221 if ((cvs_homedir = getenv("HOME")) == NULL((void *)0)) {
222 if ((pw = getpwuid(getuid())) != NULL((void *)0))
223 cvs_homedir = pw->pw_dir;
224 }
225
226 if ((envstr = getenv("TMPDIR")) != NULL((void *)0))
227 cvs_tmpdir = envstr;
228
229 ret = cvs_getopt(argc, argv);
230
231 argc -= ret;
232 argv += ret;
233 if (argc == 0)
234 usage();
235
236 cmdp = cvs_findcmd(argv[0]);
237 if (cmdp == NULL((void *)0)) {
238 fprintf(stderr(&__sF[2]), "Unknown command: `%s'\n\n", argv[0]);
239 fprintf(stderr(&__sF[2]), "CVS commands are:\n");
240 for (i = 0; cvs_cdt[i] != NULL((void *)0); i++)
241 fprintf(stderr(&__sF[2]), "\t%-16s%s\n",
242 cvs_cdt[i]->cmd_name, cvs_cdt[i]->cmd_descr);
243 exit(1);
244 }
245
246 /*
247 * check the tmp dir, either specified through
248 * the environment variable TMPDIR, or via
249 * the global option -T <dir>
250 */
251 if (stat(cvs_tmpdir, &st) == -1)
252 fatal("stat failed on `%s': %s", cvs_tmpdir, strerror(errno(*__errno())));
253 else if (!S_ISDIR(st.st_mode)((st.st_mode & 0170000) == 0040000))
254 fatal("`%s' is not valid temporary directory", cvs_tmpdir);
255
256 if (cvs_readrc == 1 && cvs_homedir != NULL((void *)0)) {
257 cvs_read_rcfile();
258
259 if (cvs_defargs != NULL((void *)0)) {
260 if ((targv = cvs_makeargv(cvs_defargs, &i)) == NULL((void *)0))
261 fatal("failed to load default arguments to %s",
262 __progname);
263
264 cvs_getopt(i, targv);
265 cvs_freeargv(targv, i);
266 free(targv);
267 }
268 }
269
270 /* setup signal handlers */
271 signal(SIGTERM15, sighandler);
272 signal(SIGINT2, sighandler);
273 signal(SIGHUP1, sighandler);
274 signal(SIGABRT6, sighandler);
275 signal(SIGALRM14, sighandler);
276 signal(SIGPIPE13, sighandler);
277
278 cvs_cmdop = cmdp->cmd_op;
279
280 cmd_argc = cvs_build_cmd(&cmd_argv, argv, argc);
281
282 cvs_file_init();
283
284 if (cvs_cmdop == CVS_OP_SERVER20) {
285 cmdp->cmd(cmd_argc, cmd_argv);
286 cvs_cleanup();
287 return (0);
288 }
289
290 cvs_umask = umask(0);
291 umask(cvs_umask);
292
293 if ((current_cvsroot = cvsroot_get(".")) == NULL((void *)0)) {
294 cvs_log(LP_ERR1,
295 "No CVSROOT specified! Please use the '-d' option");
296 fatal("or set the CVSROOT environment variable.");
297 }
298
299 if (cvsroot_is_remote()) {
300 cmdp->cmd(cmd_argc, cmd_argv);
301 cvs_cleanup();
302 return (0);
303 }
304
305 (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
306 current_cvsroot->cr_dir, CVS_PATH_ROOT"CVSROOT");
307
308 if (stat(fpath, &st) == -1 && cvs_cmdop != CVS_OP_INIT12) {
309 if (errno(*__errno()) == ENOENT2)
310 fatal("repository '%s' does not exist",
311 current_cvsroot->cr_dir);
312 else
313 fatal("%s: %s", current_cvsroot->cr_dir,
314 strerror(errno(*__errno())));
315 } else {
316 if (!S_ISDIR(st.st_mode)((st.st_mode & 0170000) == 0040000))
317 fatal("'%s' is not a directory",
318 current_cvsroot->cr_dir);
319 }
320
321 if (cvs_cmdop != CVS_OP_INIT12) {
322 cvs_parse_configfile();
323 cvs_parse_modules();
324 }
325
326 cmdp->cmd(cmd_argc, cmd_argv);
327 cvs_cleanup();
328
329 return (0);
330}
331
332int
333cvs_getopt(int argc, char **argv)
334{
335 int ret;
336 char *ep;
337 const char *errstr;
338
339 while ((ret = getopt(argc, argv, "b:d:e:flnQqRrs:T:tvwxz:")) != -1) {
340 switch (ret) {
341 case 'b':
342 /*
343 * We do not care about the bin directory for RCS files
344 * as this program has no dependencies on RCS programs,
345 * so it is only here for backwards compatibility.
346 */
347 cvs_log(LP_NOTICE0, "the -b argument is obsolete");
348 break;
349 case 'd':
350 cvs_rootstr = optarg;
351 break;
352 case 'e':
353 cvs_editor = optarg;
354 break;
355 case 'f':
356 cvs_readrc = 0;
357 break;
358 case 'l':
359 cvs_nolog = 1;
360 break;
361 case 'n':
362 cvs_noexec = 1;
363 cvs_nolog = 1;
364 break;
365 case 'Q':
366 verbosity = 0;
367 break;
368 case 'q':
369 if (verbosity > 1)
370 verbosity = 1;
371 break;
372 case 'R':
373 cvs_readonlyfs = 1;
374 cvs_nolog = 1;
375 break;
376 case 'r':
377 cvs_readonly = 1;
378 break;
379 case 's':
380 ep = strchr(optarg, '=');
381 if (ep == NULL((void *)0)) {
382 cvs_log(LP_ERR1, "no = in variable assignment");
383 exit(1);
384 }
385 *(ep++) = '\0';
386 if (cvs_var_set(optarg, ep) < 0)
387 exit(1);
388 break;
389 case 'T':
390 cvs_tmpdir = optarg;
391 break;
392 case 't':
393 cvs_trace = 1;
394 break;
395 case 'v':
396 printf("%s\n", CVS_VERSION"OpenCVS 4.5");
397 exit(0);
398 /* NOTREACHED */
399 case 'w':
400 cvs_readonly = 0;
401 break;
402 case 'x':
403 /*
404 * Kerberos encryption support, kept for compatibility
405 */
406 break;
407 case 'z':
408 cvs_compress = strtonum(optarg, 0, 9, &errstr);
409 if (errstr != NULL((void *)0))
410 fatal("cvs_compress: %s", errstr);
411 break;
412 default:
413 usage();
414 /* NOTREACHED */
415 }
416 }
417
418 ret = optind;
419 optind = 1;
420 optreset = 1; /* for next call */
421
422 return (ret);
423}
424
425/*
426 * cvs_read_rcfile()
427 *
428 * Read the CVS `.cvsrc' file in the user's home directory. If the file
429 * exists, it should contain a list of arguments that should always be given
430 * implicitly to the specified commands.
431 */
432static void
433cvs_read_rcfile(void)
434{
435 char rcpath[PATH_MAX1024], *buf, *lbuf, *lp, *p;
436 int cmd_parsed, cvs_parsed, i, linenum;
437 size_t len, pos;
438 struct cvs_cmd *tcmdp;
439 FILE *fp;
440
441 linenum = 0;
442
443 i = snprintf(rcpath, PATH_MAX1024, "%s/%s", cvs_homedir, CVS_PATH_RC".cvsrc");
444 if (i < 0 || i >= PATH_MAX1024) {
445 cvs_log(LP_ERRNO2, "%s", rcpath);
446 return;
447 }
448
449 fp = fopen(rcpath, "r");
450 if (fp == NULL((void *)0)) {
451 if (errno(*__errno()) != ENOENT2)
452 cvs_log(LP_NOTICE0, "failed to open `%s': %s", rcpath,
453 strerror(errno(*__errno())));
454 return;
455 }
456
457 cmd_parsed = cvs_parsed = 0;
458 lbuf = NULL((void *)0);
459 while ((buf = fgetln(fp, &len)) != NULL((void *)0)) {
460 if (buf[len - 1] == '\n') {
461 buf[len - 1] = '\0';
462 } else {
463 lbuf = xmalloc(len + 1);
464 memcpy(lbuf, buf, len);
465 lbuf[len] = '\0';
466 buf = lbuf;
467 }
468
469 linenum++;
470
471 /* skip any whitespaces */
472 p = buf;
473 while (*p == ' ')
474 p++;
475
476 /*
477 * Allow comments.
478 * GNU cvs stops parsing a line if it encounters a \t
479 * in front of a command, stick at this behaviour for
480 * compatibility.
481 */
482 if (*p == '#' || *p == '\t')
483 continue;
484
485 pos = strcspn(p, " \t");
486 if (pos == strlen(p)) {
487 lp = NULL((void *)0);
488 } else {
489 lp = p + pos;
490 *lp = '\0';
491 }
492
493 if (strcmp(p, "cvs") == 0 && !cvs_parsed) {
494 /*
495 * Global default options. In the case of cvs only,
496 * we keep the 'cvs' string as first argument because
497 * getopt() does not like starting at index 0 for
498 * argument processing.
499 */
500 if (lp != NULL((void *)0)) {
501 *lp = ' ';
502 cvs_defargs = xstrdup(p);
503 }
504 cvs_parsed = 1;
505 } else {
506 tcmdp = cvs_findcmd(p);
507 if (tcmdp == NULL((void *)0) && verbosity == 2)
508 cvs_log(LP_NOTICE0,
509 "unknown command `%s' in `%s:%d'",
510 p, rcpath, linenum);
511
512 if (tcmdp != cmdp || cmd_parsed)
513 continue;
514
515 if (lp != NULL((void *)0)) {
516 lp++;
517 cmdp->cmd_defargs = xstrdup(lp);
518 }
519 cmd_parsed = 1;
520 }
521 }
522 free(lbuf);
523
524 if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror
)(fp))
) {
525 cvs_log(LP_NOTICE0, "failed to read line from `%s'", rcpath);
526 }
527
528 (void)fclose(fp);
529}
530
531/*
532 * cvs_var_set()
533 *
534 * Set the value of the variable <var> to <val>. If there is no such variable,
535 * a new entry is created, otherwise the old value is overwritten.
536 * Returns 0 on success, or -1 on failure.
537 */
538int
539cvs_var_set(const char *var, const char *val)
540{
541 const char *cp;
542 struct cvs_var *vp;
543
544 if (var == NULL((void *)0) || *var == '\0') {
545 cvs_log(LP_ERR1, "no variable name");
546 return (-1);
547 }
548
549 /* sanity check on the name */
550 for (cp = var; *cp != '\0'; cp++)
551 if (!isalnum((unsigned char)*cp) && (*cp != '_')) {
552 cvs_log(LP_ERR1,
553 "variable name `%s' contains invalid characters",
554 var);
555 return (-1);
556 }
557
558 TAILQ_FOREACH(vp, &cvs_variables, cv_link)for((vp) = ((&cvs_variables)->tqh_first); (vp) != ((void
*)0); (vp) = ((vp)->cv_link.tqe_next))
559 if (strcmp(vp->cv_name, var) == 0)
560 break;
561
562 if (vp == NULL((void *)0)) {
563 vp = xcalloc(1, sizeof(*vp));
564
565 vp->cv_name = xstrdup(var);
566 TAILQ_INSERT_TAIL(&cvs_variables, vp, cv_link)do { (vp)->cv_link.tqe_next = ((void *)0); (vp)->cv_link
.tqe_prev = (&cvs_variables)->tqh_last; *(&cvs_variables
)->tqh_last = (vp); (&cvs_variables)->tqh_last = &
(vp)->cv_link.tqe_next; } while (0)
;
567
568 } else /* free the previous value */
569 free(vp->cv_val);
570
571 vp->cv_val = xstrdup(val);
572
573 return (0);
574}
575
576/*
577 * cvs_var_unset()
578 *
579 * Remove any entry for the variable <var>.
580 * Returns 0 on success, or -1 on failure.
581 */
582int
583cvs_var_unset(const char *var)
584{
585 struct cvs_var *vp;
586
587 TAILQ_FOREACH(vp, &cvs_variables, cv_link)for((vp) = ((&cvs_variables)->tqh_first); (vp) != ((void
*)0); (vp) = ((vp)->cv_link.tqe_next))
588 if (strcmp(vp->cv_name, var) == 0) {
589 TAILQ_REMOVE(&cvs_variables, vp, cv_link)do { if (((vp)->cv_link.tqe_next) != ((void *)0)) (vp)->
cv_link.tqe_next->cv_link.tqe_prev = (vp)->cv_link.tqe_prev
; else (&cvs_variables)->tqh_last = (vp)->cv_link.tqe_prev
; *(vp)->cv_link.tqe_prev = (vp)->cv_link.tqe_next; ; ;
} while (0)
;
590 free(vp->cv_name);
591 free(vp->cv_val);
592 free(vp);
593 return (0);
594 }
595
596 return (-1);
597}
598
599/*
600 * cvs_var_get()
601 *
602 * Get the value associated with the variable <var>. Returns a pointer to the
603 * value string on success, or NULL if the variable does not exist.
604 */
605
606const char *
607cvs_var_get(const char *var)
608{
609 struct cvs_var *vp;
610
611 TAILQ_FOREACH(vp, &cvs_variables, cv_link)for((vp) = ((&cvs_variables)->tqh_first); (vp) != ((void
*)0); (vp) = ((vp)->cv_link.tqe_next))
612 if (strcmp(vp->cv_name, var) == 0)
613 return (vp->cv_val);
614
615 return (NULL((void *)0));
616}