From owner-freebsd-bugs Sun Jul 2 7:50:14 2000 Delivered-To: freebsd-bugs@freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.ORG [204.216.27.21]) by hub.freebsd.org (Postfix) with ESMTP id 4DBD937BCFA for ; Sun, 2 Jul 2000 07:50:01 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.9.3/8.9.2) id HAA06454; Sun, 2 Jul 2000 07:50:01 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from teapot.egroups.net (teapot.egroups.net [63.204.207.250]) by hub.freebsd.org (Postfix) with SMTP id 1407D37BBD7 for ; Sun, 2 Jul 2000 07:40:19 -0700 (PDT) (envelope-from kbyanc@teapot.egroups.com) Received: (qmail 9659 invoked from network); 2 Jul 2000 14:40:14 -0000 Received: (QMFILT: 1.0); 02 Jul 2000 15:40:14 -0000 Received: from dhcp147.corp.onelist.com (HELO kbyanc.corp.ONElist.com) (192.168.10.147) by teapot.egroups.net with SMTP; 2 Jul 2000 14:40:14 -0000 Received: (from kbyanc@localhost) by kbyanc.corp.ONElist.com (8.9.3/8.9.3) id HAA70176; Sun, 2 Jul 2000 07:40:14 -0700 (PDT) (envelope-from kbyanc@teapot.egroups.com) Message-Id: <200007021440.HAA70176@kbyanc.corp.ONElist.com> Date: Sun, 2 Jul 2000 07:40:14 -0700 (PDT) From: kbyanc@posi.net Reply-To: kbyanc@posi.net To: FreeBSD-gnats-submit@freebsd.org X-Send-Pr-Version: 3.2 Subject: bin/19642: patch to merge OpenBSD changes to patch(1) Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >Number: 19642 >Category: bin >Synopsis: patch to merge OpenBSD changes to patch(1) >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Sun Jul 02 07:50:01 PDT 2000 >Closed-Date: >Last-Modified: >Originator: Kelly Yancey >Release: FreeBSD 5.0-CURRENT i386 >Organization: eGroups.com -- http://www.eGroups.com/ >Environment: FreeBSD backroom.corp.ONElist.com 5.0-CURRENT FreeBSD 5.0-CURRENT #1: Fri Jun 30 13:47:43 PDT 2000 root@backroom.corp.ONElist.com:/usr/src/sys/compile/BACKROOM i386 >Description: Not surprisingly, OpenBSD has fixed a number of potential security holes in patch(1), this patch merges those changes into FreeBSD's patch(1). Specifically, the attached patch catches a number of buffer overflow cases as well as the standard mktemp race conditions. Beyond just security, OpenBSD has also generally cleaned up the code a bit, which changes I have also included. Basically, this just entails replacing magic numbers with #defines and answering -Wall's warnings (note, however -Wall still complains about a few non-issues). As a followup to this patch, with permission, I would like to also remove all the obsolete $Log entries at the tops of the source files. Finally, for the record, I am somewhat confused about the presence of contrib/patch also. The version in contrib is a much newer (albeit entirely GPL'ed) version of patch. However, as best I can tell it is completely unreferenced. The version of patch in gnu/usr.bin/patch (the version this diff was taken against) is the version installed as part of `make world`. It would appear that the copy in contrib/patch is superfluous. -Kelly >How-To-Repeat: >Fix: Index: gnu/usr.bin/patch/backupfile.c =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/backupfile.c,v retrieving revision 1.4 diff -u -r1.4 backupfile.c --- gnu/usr.bin/patch/backupfile.c 1998/01/21 14:37:14 1.4 +++ gnu/usr.bin/patch/backupfile.c 2000/07/02 13:00:16 @@ -216,7 +216,7 @@ char *str1, *str2; { char *newstr; - char str1_length = strlen (str1); + int str1_length = strlen (str1); newstr = malloc (str1_length + strlen (str2) + 1); if (newstr == 0) Index: gnu/usr.bin/patch/common.h =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/common.h,v retrieving revision 1.7 diff -u -r1.7 common.h --- gnu/usr.bin/patch/common.h 1999/09/05 17:31:55 1.7 +++ gnu/usr.bin/patch/common.h 2000/07/02 14:13:22 @@ -29,8 +29,9 @@ #define Fclose (void)fclose #define Fflush (void)fflush #define Sprintf (void)sprintf -#define Mktemp (void)mktemp +#define Snprintf (void)snprintf #define Strcpy (void)strcpy +#define Strlcpy (void)strlcpy #define Strcat (void)strcat /* NeXT declares malloc and realloc incompatibly from us in some of @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #undef malloc @@ -117,8 +119,9 @@ EXT bool ok_to_create_file INIT(FALSE); EXT char *bestguess INIT(Nullch); /* guess at correct filename */ +#define REJNAMESIZ 128 EXT char *outname INIT(Nullch); -EXT char rejname[128]; +EXT char rejname[REJNAMESIZ]; EXT char *origprae INIT(Nullch); @@ -150,11 +153,12 @@ #define UNI_DIFF 5 EXT int diff_type INIT(0); +#define DEFSIZ 128 EXT bool do_defines INIT(FALSE); /* patch using ifdef, ifndef, etc. */ -EXT char if_defined[128]; /* #ifdef xyzzy */ -EXT char not_defined[128]; /* #ifndef xyzzy */ +EXT char if_defined[DEFSIZ]; /* #ifdef xyzzy */ +EXT char not_defined[DEFSIZ]; /* #ifndef xyzzy */ EXT char else_defined[] INIT("#else\n");/* #else */ -EXT char end_defined[128]; /* #endif xyzzy */ +EXT char end_defined[DEFSIZ]; /* #endif xyzzy */ EXT char *revision INIT(Nullch); /* prerequisite revision, if any */ @@ -172,7 +176,7 @@ char *strcpy(); char *strcat(); #endif -char *mktemp(); +int mkstemp(); #ifdef HAVE_UNISTD_H #include #else Index: gnu/usr.bin/patch/getopt.c =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/getopt.c,v retrieving revision 1.4 diff -u -r1.4 getopt.c --- gnu/usr.bin/patch/getopt.c 1998/01/21 14:37:18 1.4 +++ gnu/usr.bin/patch/getopt.c 2000/07/02 14:14:11 @@ -445,7 +445,7 @@ int exact = 0; int ambig = 0; const struct option *pfound = NULL; - int indfound; + int indfound = 0; while (*s && *s != '=') s++; Index: gnu/usr.bin/patch/inp.c =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/inp.c,v retrieving revision 1.10 diff -u -r1.10 inp.c --- gnu/usr.bin/patch/inp.c 1999/09/05 17:31:55 1.10 +++ gnu/usr.bin/patch/inp.c 2000/07/02 14:05:44 @@ -18,7 +18,7 @@ /* Input-file-with-indexable-lines abstract type */ -static long i_size; /* size of the input file */ +static off_t i_size; /* size of the input file */ static char *i_womp; /* plan a buffer for entire file */ static char **i_ptr; /* pointers to lines in i_womp */ @@ -117,7 +117,9 @@ s = lbuf + 20; strncpy(s, filename, pathlen); -#define try(f,a1,a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0) +#define try(f,a1,a2) \ + (Snprintf(s + pathlen, sizeof lbuf - (s + pathlen - lbuf), \ + f, a1, a2), stat(s, &cstat) == 0) if (( try("RCS/%s%s", filebase, RCSSUFFIX) || try("RCS/%s%s", filebase, "") || try( "%s%s", filebase, RCSSUFFIX)) @@ -127,13 +129,14 @@ (statfailed || ( (filestat.st_dev ^ cstat.st_dev) | (filestat.st_ino ^ cstat.st_ino)))) { - Sprintf(buf, output_elsewhere?CHECKOUT:CHECKOUT_LOCKED, filename); - Sprintf(lbuf, RCSDIFF, filename); + Snprintf(buf, sizeof(buf), + output_elsewhere?CHECKOUT:CHECKOUT_LOCKED, filename); + Snprintf(lbuf, sizeof(lbuf), RCSDIFF, filename); cs = "RCS"; } else if ( try("SCCS/%s%s", SCCSPREFIX, filebase) || try( "%s%s", SCCSPREFIX, filebase)) { - Sprintf(buf, output_elsewhere?GET:GET_LOCKED, s); - Sprintf(lbuf, SCCSDIFF, s, filename); + Snprintf(buf, sizeof(buf), output_elsewhere?GET:GET_LOCKED, s); + Snprintf(lbuf, sizeof(lbuf), SCCSDIFF, s, filename); cs = "SCCS"; } else if (statfailed) fatal2("can't find %s\n", filename); @@ -172,15 +175,16 @@ #ifdef lint i_womp = Nullch; #else - i_womp = malloc((MEM)(i_size+2)); /* lint says this may alloc less than */ + i_womp = (char *)malloc((MEM)(i_size+2)); + /* lint says this may alloc less than */ /* i_size, but that's okay, I think. */ #endif if (i_womp == Nullch) return FALSE; - if ((ifd = open(filename, 0)) < 0) + if ((ifd = open(filename, O_RDONLY)) < 0) pfatal2("can't open file %s", filename); #ifndef lint - if (read(ifd, i_womp, (int)i_size) != i_size) { + if (read(ifd, i_womp, (size_t)i_size) != i_size) { Close(ifd); /* probably means i_size > 15 or 16 bits worth */ free(i_womp); /* at this point it doesn't matter if i_womp was */ return FALSE; /* undersized. */ @@ -296,8 +300,8 @@ Fseek(ifp, 0L, 0); /* rewind file */ lines_per_buf = BUFFERSIZE / maxlen; tireclen = maxlen; - tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); - tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); + tibuf[0] = (char *)malloc((MEM)(BUFFERSIZE + 1)); + tibuf[1] = (char *)malloc((MEM)(BUFFERSIZE + 1)); if (tibuf[1] == Nullch) fatal1("out of memory\n"); for (i=1; ; i++) { @@ -315,7 +319,7 @@ } Fclose(ifp); Close(tifd); - if ((tifd = open(TMPINNAME, 0)) < 0) { + if ((tifd = open(TMPINNAME, O_RDONLY)) < 0) { pfatal2("can't reopen file %s", TMPINNAME); } } @@ -342,7 +346,7 @@ else { tiline[whichbuf] = baseline; #ifndef lint /* complains of long accuracy */ - Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0); + Lseek(tifd, (off_t)baseline / lines_per_buf * BUFFERSIZE, 0); #endif if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) pfatal2("error reading tmp file %s", TMPINNAME); Index: gnu/usr.bin/patch/patch.c =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/patch.c,v retrieving revision 1.16 diff -u -r1.16 patch.c --- gnu/usr.bin/patch/patch.c 1999/09/05 17:31:55 1.16 +++ gnu/usr.bin/patch/patch.c 2000/07/02 13:10:48 @@ -115,7 +115,11 @@ bool patch_match(); bool similar(); void re_input(); +#ifdef __GNUC__ +void my_exit() __attribute__((noreturn)); +#else void my_exit(); +#endif /* TRUE if -E was specified on command line. */ static int remove_empty_files = FALSE; @@ -170,22 +174,31 @@ TMPOUTNAME = (char *) malloc (tmpname_len); strcpy (TMPOUTNAME, tmpdir); strcat (TMPOUTNAME, "/patchoXXXXXX"); - Mktemp(TMPOUTNAME); + if ((i = mkstemp(TMPOUTNAME)) < 0) + pfatal2("can't create %s", TMPOUTNAME); + Close(i); TMPINNAME = (char *) malloc (tmpname_len); strcpy (TMPINNAME, tmpdir); strcat (TMPINNAME, "/patchiXXXXXX"); - Mktemp(TMPINNAME); + if ((i = mkstemp(TMPINNAME)) < 0) + pfatal2("can't create %s", TMPINNAME); + Close(i); TMPREJNAME = (char *) malloc (tmpname_len); strcpy (TMPREJNAME, tmpdir); strcat (TMPREJNAME, "/patchrXXXXXX"); - Mktemp(TMPREJNAME); + if ((i = mkstemp(TMPREJNAME)) < 0) + pfatal2("can't create %s", TMPREJNAME); + Close(i); TMPPATNAME = (char *) malloc (tmpname_len); strcpy (TMPPATNAME, tmpdir); strcat (TMPPATNAME, "/patchpXXXXXX"); - Mktemp(TMPPATNAME); + if ((i = mkstemp(TMPPATNAME)) < 0) + pfatal2("can't create %s", TMPPATNAME); + Close(i); + } { @@ -383,7 +396,9 @@ if (failed) { failtotal += failed; if (!*rejname) { - Strcpy(rejname, outname); + if (strlcpy(rejname, outname, sizeof(rejname) - sizeof(".rej")) + >= sizeof(rejname) - sizeof(".rej")) + fatal2("filename %s is too long\n", outname); addext(rejname, ".rej", '#'); } if (skip_rest_of_patch) { @@ -519,9 +534,11 @@ do_defines = TRUE; if (!isalpha((unsigned char)*optarg) && '_' != *optarg) fatal1("argument to -D is not an identifier\n"); - Sprintf(if_defined, "#ifdef %s\n", optarg); - Sprintf(not_defined, "#ifndef %s\n", optarg); - Sprintf(end_defined, "#endif /* %s */\n", optarg); + Snprintf(if_defined, sizeof if_defined, "#ifdef %s\n", optarg); + Snprintf(not_defined, sizeof not_defined, "#ifndef %s\n", + optarg); + Snprintf(end_defined, sizeof end_defined, "#endif /* %s */\n", + optarg); break; case 'e': diff_type = ED_DIFF; @@ -557,7 +574,7 @@ strippath = 0; break; case 'r': - Strcpy(rejname, optarg); + Strlcpy(rejname, optarg, sizeof(rejname)); break; case 'R': reverse = TRUE; Index: gnu/usr.bin/patch/pch.c =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/pch.c,v retrieving revision 1.16 diff -u -r1.16 pch.c --- gnu/usr.bin/patch/pch.c 1999/09/05 17:31:55 1.16 +++ gnu/usr.bin/patch/pch.c 2000/07/02 14:11:19 @@ -1051,8 +1051,8 @@ else indent++; } - if (buf != s) - Strcpy(buf, s); + if (buf != s && strlcpy(buf, s, sizeof(buf)) >= sizeof(buf)) + fatal1("buffer too small in pgets()\n"); } return ret; } Index: gnu/usr.bin/patch/util.c =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/util.c,v retrieving revision 1.7 diff -u -r1.7 util.c --- gnu/usr.bin/patch/util.c 1998/01/21 14:37:25 1.7 +++ gnu/usr.bin/patch/util.c 2000/07/02 13:44:48 @@ -4,7 +4,11 @@ #include "util.h" #include "backupfile.h" +#ifdef __GNUC__ +void my_exit() __attribute__((noreturn)); +#else void my_exit(); +#endif #ifndef HAVE_STRERROR static char * @@ -27,7 +31,7 @@ move_file(from,to) char *from, *to; { - char bakname[512]; + char bakname[MAXPATHLEN]; Reg1 char *s; Reg2 int i; Reg3 int fromfd; @@ -39,7 +43,7 @@ if (debug & 4) say2("Moving %s to stdout.\n", from); #endif - fromfd = open(from, 0); + fromfd = open(from, O_RDONLY); if (fromfd < 0) pfatal2("internal error, can't reopen %s", from); while ((i=read(fromfd, buf, sizeof buf)) > 0) @@ -50,14 +54,16 @@ } if (origprae) { - Strcpy(bakname, origprae); - Strcat(bakname, to); + if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || + strlcat(bakname, to, sizeof(bakname)) >= sizeof(bakname)) + fatal2("filename %s too long for buffer\n", origprae); } else { #ifndef NODIR char *backupname = find_backup_file_name(to); if (backupname == (char *) 0) fatal1("out of memory\n"); - Strcpy(bakname, backupname); + if (strlcpy(bakname, backupname, sizeof(bakname)) >= sizeof(bakname)) + fatal2("filename %s too long for buffer\n", backupname); free(backupname); #else /* NODIR */ Strcpy(bakname, to); @@ -111,7 +117,7 @@ to, from, strerror(errno)); return -1; } - fromfd = open(from, 0); + fromfd = open(from, O_RDONLY); if (fromfd < 0) pfatal2("internal error, can't reopen %s", from); while ((i=read(fromfd, buf, sizeof buf)) > 0) @@ -137,7 +143,7 @@ tofd = creat(to, 0666); if (tofd < 0) pfatal2("can't create %s", to); - fromfd = open(from, 0); + fromfd = open(from, O_RDONLY); if (fromfd < 0) pfatal2("internal error, can't reopen %s", from); while ((i=read(fromfd, buf, sizeof buf)) > 0) @@ -160,7 +166,7 @@ s = "Oops"; t = s; while (*t++); - rv = malloc((MEM) (t - s)); + rv = (char *)malloc((MEM) (t - s)); if (rv == Nullch) { if (using_plan_a) out_of_mem = TRUE; @@ -236,7 +242,7 @@ int r; bool tty2 = isatty(2); - Sprintf(buf, pat, arg1, arg2, arg3); + Snprintf(buf, sizeof buf, pat, arg1, arg2, arg3); Fflush(stderr); write(2, buf, strlen(buf)); if (tty2) { /* might be redirected to a file */ @@ -247,7 +253,7 @@ write(1, buf, strlen(buf)); r = read(1, buf, sizeof buf); } - else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) { + else if ((ttyfd = open("/dev/tty", O_RDWR)) >= 0 && isatty(ttyfd)) { /* might be deleted or unwriteable */ write(ttyfd, buf, strlen(buf)); r = read(ttyfd, buf, sizeof buf); @@ -319,45 +325,25 @@ Reg1 char *filename; bool striplast; { - char tmpbuf[256]; - Reg2 char *s = tmpbuf; - char *dirv[20]; /* Point to the NULs between elements. */ - Reg3 int i; - Reg4 int dirvp = 0; /* Number of finished entries in dirv. */ - - /* Copy `filename' into `tmpbuf' with a NUL instead of a slash - between the directories. */ - while (*filename) { - if (*filename == '/') { - filename++; - dirv[dirvp++] = s; - *s++ = '\0'; - } - else { - *s++ = *filename++; - } - } - *s = '\0'; - dirv[dirvp] = s; - if (striplast) - dirvp--; - if (dirvp < 0) - return; - - strcpy(buf, "mkdir"); - s = buf; - for (i=0; i<=dirvp; i++) { - struct stat sbuf; - - if (stat(tmpbuf, &sbuf) && errno == ENOENT) { - while (*s) s++; - *s++ = ' '; - strcpy(s, tmpbuf); - } - *dirv[i] = '/'; + + char *tmpbuf; + + if ((tmpbuf = strdup(filename)) == NULL) + fatal1("out of memory\n"); + + if (striplast) { + char *s = strrchr(tmpbuf, '/'); + if (s == NULL) + return; /* nothing to be done */ + *s = '\0'; } - if (s != buf) - system(buf); + + strcpy(buf, "/bin/mkdir -p "); + if (strlcat(buf, tmpbuf, sizeof(buf)) >= sizeof(buf)) + fatal2("buffer too small to hold %.20s...\n", tmpbuf); + + if (system(buf)) + pfatal2("%.40s failed", buf); } /* Make filenames more reasonable. */ @@ -374,7 +360,7 @@ char tmpbuf[200]; int sleading = strip_leading; - if (!at) + if (!at || *at == '\0') return Nullch; while (isspace((unsigned char)*at)) at++; @@ -410,17 +396,15 @@ if (stat(name, &filestat) && !assume_exists) { char *filebase = basename(name); - int pathlen = filebase - name; - - /* Put any leading path into `tmpbuf'. */ - strncpy(tmpbuf, name, pathlen); + char *filedir = dirname(name); -#define try(f, a1, a2) (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0) - if ( try("RCS/%s%s", filebase, RCSSUFFIX) - || try("RCS/%s%s", filebase, "") - || try( "%s%s", filebase, RCSSUFFIX) - || try("SCCS/%s%s", SCCSPREFIX, filebase) - || try( "%s%s", SCCSPREFIX, filebase)) +#define try(f, a1, a2, a3) (Snprintf(tmpbuf, sizeof(tmpbuf), f, a1, a2, a3), \ + stat(tmpbuf, &filestat) == 0) + if ( try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) + || try("%s/RCS/%s%s", filedir, filebase, "") + || try( "%s/%s%s", filedir, filebase, RCSSUFFIX) + || try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) + || try( "%s/%s%s", filedir, SCCSPREFIX, filebase)) return name; free(name); name = Nullch; Index: gnu/usr.bin/patch/util.h =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/util.h,v retrieving revision 1.7 diff -u -r1.7 util.h --- gnu/usr.bin/patch/util.h 1999/09/05 17:31:55 1.7 +++ gnu/usr.bin/patch/util.h 2000/07/02 13:52:55 @@ -86,3 +86,5 @@ void ignore_signals(); void makedirs(); char *basename(); +char *dirname(); + Index: gnu/usr.bin/patch/version.c =================================================================== RCS file: /home/cvs/src/gnu/usr.bin/patch/version.c,v retrieving revision 1.6 diff -u -r1.6 version.c --- gnu/usr.bin/patch/version.c 1999/09/05 17:31:55 1.6 +++ gnu/usr.bin/patch/version.c 2000/07/02 13:54:27 @@ -13,7 +13,11 @@ #include "patchlevel.h" #include "version.h" +#ifdef __GNUC__ +void my_exit() __attribute__((noreturn)); +#else void my_exit(); +#endif /* Print out the version number and die. */ >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message