Hi,

sdiff makes a false assumption about diffargv:

/*
 * Allocate memory for diff arguments and NULL.
 * Each flag has at most one argument, so doubling argc gives an
 * upper limit of how many diff args can be passed.  argv[0],
 * file1, and file2 won't have arguments so doubling them will
 * waste some memory; however we need an extra space for the
 * NULL at the end, so it sort of works out.
 */

This is unfortunately not true, because we can easily specify multiple
single-letter arguments in one argument, like:

$ sdiff -aaaaaa<insert a lot of args here> file1 file2
Segmentation fault

The allocation of diffargv should therefore happen dynamically while
parsing arguments.

While doing so, I created another char array, holding all single-letter
arguments that cannot have optargs on their own.  This makes sure that we
do not create more diffargv elements than the system could possibly
handle.


Tobias

Index: sdiff.c
===================================================================
RCS file: /cvs/src/usr.bin/sdiff/sdiff.c,v
retrieving revision 1.28
diff -u -p -r1.28 sdiff.c
--- sdiff.c     7 Jun 2009 13:29:50 -0000       1.28
+++ sdiff.c     30 Mar 2013 15:49:53 -0000
@@ -153,67 +153,63 @@ FAIL:
        exit(2);
 }
 
+static char **
+addarg(char *arg)
+{
+       static char **diffargv = NULL;
+       static int diffargc = 0;
+
+       diffargv = realloc(diffargv, (diffargc + 1) * sizeof(char **));
+       if (diffargv == NULL)
+               err(2, "diff arguments");
+       diffargv[diffargc++] = arg;
+
+       return diffargv;
+}
+
 int
 main(int argc, char **argv)
 {
        FILE *diffpipe, *file1, *file2;
-       size_t diffargc = 0, wflag = WIDTH;
+       size_t shortoptslen = 0, wflag = WIDTH;
        int ch, fd[2], status;
        pid_t pid;
        const char *outfile = NULL;
        char **diffargv, *diffprog = "diff", *filename1, *filename2,
-           *tmp1, *tmp2, *s1, *s2;
+           *shortopts = NULL, *tmp1, *tmp2, *s1, *s2;
 
        /*
         * Process diff flags.
         */
-       /*
-        * Allocate memory for diff arguments and NULL.
-        * Each flag has at most one argument, so doubling argc gives an
-        * upper limit of how many diff args can be passed.  argv[0],
-        * file1, and file2 won't have arguments so doubling them will
-        * waste some memory; however we need an extra space for the
-        * NULL at the end, so it sort of works out.
-        */
-       if (!(diffargv = calloc(argc, sizeof(char **) * 2)))
-               err(2, "main");
 
        /* Add first argument, the program name. */
-       diffargv[diffargc++] = diffprog;
+       diffargv = addarg(diffprog);
 
        while ((ch = getopt_long(argc, argv, "aBbdEHI:ilo:stWw:",
            longopts, NULL)) != -1) {
                const char *errstr;
+               char c = '\0';
 
                switch (ch) {
                case 'a':
-                       diffargv[diffargc++] = "-a";
-                       break;
                case 'B':
-                       diffargv[diffargc++] = "-B";
-                       break;
                case 'b':
-                       diffargv[diffargc++] = "-b";
-                       break;
                case 'd':
-                       diffargv[diffargc++] = "-d";
-                       break;
                case 'E':
-                       diffargv[diffargc++] = "-E";
+               case 'H':
+               case 'i':
+               case 't':
+                       c = ch;
                        break;
                case 'F':
                        diffargv[0] = diffprog = optarg;
                        break;
-               case 'H':
-                       diffargv[diffargc++] = "-H";
-                       break;
                case 'I':
                        Iflag = 1;
-                       diffargv[diffargc++] = "-I";
-                       diffargv[diffargc++] = optarg;
+                       diffargv = addarg("-I");
+                       diffargv = addarg(optarg);
                        break;
-               case 'i':
-                       diffargv[diffargc++] = "-i";
+                       c = 'i';
                        break;
                case 'l':
                        lflag = 1;
@@ -222,16 +218,13 @@ main(int argc, char **argv)
                        outfile = optarg;
                        break;
                case 'S':
-                       diffargv[diffargc++] = "--strip-trailing-cr";
+                       diffargv = addarg("--strip-trailing-cr");
                        break;
                case 's':
                        sflag = 1;
                        break;
-               case 't':
-                       diffargv[diffargc++] = "-t";
-                       break;
                case 'W':
-                       diffargv[diffargc++] = "-w";
+                       c = 'w';
                        break;
                case 'w':
                        wflag = strtonum(optarg, WIDTH_MIN,
@@ -243,6 +236,22 @@ main(int argc, char **argv)
                        usage();
                }
 
+               if (c != '\0') {
+                       if (shortopts == NULL) {
+                               shortopts = malloc(3 * sizeof(char *));
+                               if (shortopts == NULL)
+                                       errx(2, "diff arguments");
+                               shortoptslen = 3;
+                               shortopts[0] = '-';
+                       } else {
+                               shortopts = realloc(shortopts,
+                                   ++shortoptslen * sizeof(char *));
+                               if (shortopts == NULL)
+                                       errx(2, "diff arguments");
+                       }
+                       shortopts[shortoptslen - 2] = c;
+                       shortopts[shortoptslen - 1] = '\0';
+               }
        }
        argc -= optind;
        argv += optind;
@@ -280,10 +289,12 @@ main(int argc, char **argv)
                        filename2 = tmp2;
        }
 
-       diffargv[diffargc++] = filename1;
-       diffargv[diffargc++] = filename2;
+       if (shortopts != NULL)
+               diffargv = addarg(shortopts);
+       diffargv = addarg(filename1);
+       diffargv = addarg(filename2);
        /* Add NULL to end of array to indicate end of array. */
-       diffargv[diffargc++] = NULL;
+       diffargv = addarg(NULL);
 
        /* Subtract column divider and divide by two. */
        width = (wflag - 3) / 2;

Reply via email to