Commit message: Security fixes: expect final changes for release 1.75.3.
* Fixed a debian security issue with fscanf() reading a string with
  possible buffer overflow.
* There were also a few similar situations with sscanf().
CVE: CVE-2018-7186
CWE: CWE-119
Vulnerability description: Leptonica before 1.75.3 does not limit the number of characters in a %s format argument to fscanf or sscanf, which allows remote attackers to cause a denial of service (stack-based buffer overflow) or possibly have unspecified other impact via a long string, as demonstrated by the gplotRead and ptaReadStream functions.

Project: leptonica
File: xtractprotos.c

int main(int    argc,
         char **argv)
 {
 char        *filein, *str, *tempfile, *prestring, *outprotos, *protostr;
 const char  *spacestr = " ";
char         buf[L_BUFSIZE];
 l_uint8     *allheaders;
 l_int32      i, maxindex, in_line, nflags, protos_added, firstfile, len, ret;
 size_t       nbytes;
L_BYTEA     *ba, *ba2;
SARRAY      *sa, *safirst;
static char  mainName[] = "xtractprotos";

    if (argc == 1) {
        fprintf(stderr,
                "xtractprotos [-prestring=<string>] [-protos=<where>] "
                "[list of C files]\n"
                "where the prestring is prepended to each prototype, and \n"
                "protos can be either 'inline' or the name of an output "
                "prototype file\n");
        return 1;
    }

    /* ---------------------------------------------------------------- */
    /* Parse input flags and find prestring and outprotos, if requested */
    /* ---------------------------------------------------------------- */
    prestring = outprotos = NULL;
    in_line = FALSE;
    nflags = 0;
    maxindex = L_MIN(3, argc);
    for (i = 1; i < maxindex; i++) {
         if (argv[i][0] == '-') {
             if (!strncmp(argv[i], "-prestring", 10)) {
                 nflags++;
                ret = sscanf(argv[i] + 1, "prestring=%490s", buf);
                 if (ret != 1) {
                     fprintf(stderr, "parse failure for prestring\n");
                     return 1;
                 }
                if ((len = strlen(buf)) > L_BUFSIZE - 3) {
                     L_WARNING("prestring too large; omitting!\n", mainName);
                 } else {
                     buf[len] = ' ';
                    buf[len + 1] = '\0';
                    prestring = stringNew(buf);
                 }
             } else if (!strncmp(argv[i], "-protos", 7)) {
                 nflags++;
                ret = sscanf(argv[i] + 1, "protos=%490s", buf);
                 if (ret != 1) {
                     fprintf(stderr, "parse failure for protos\n");
                     return 1;
                }
                outprotos = stringNew(buf);
                if (!strncmp(outprotos, "inline", 7))
                    in_line = TRUE;
            }
        }
    }

    if (argc - nflags < 2) {
        fprintf(stderr, "no files specified!\n");
        return 1;
    }


    /* ---------------------------------------------------------------- */
    /*                   Generate the prototype string                  */
    /* ---------------------------------------------------------------- */
    ba = l_byteaCreate(500);

         /* First the extern C head */
     sa = sarrayCreate(0);
     sarrayAddString(sa, (char *)"/*", L_COPY);
    snprintf(buf, L_BUFSIZE,
              " *  These prototypes were autogen'd by xtractprotos, v. %s",
              version);
     sarrayAddString(sa, buf, L_COPY);
    sarrayAddString(sa, (char *)" */", L_COPY);
    sarrayAddString(sa, (char *)"#ifdef __cplusplus", L_COPY);
    sarrayAddString(sa, (char *)"extern \"C\" {", L_COPY);
    sarrayAddString(sa, (char *)"#endif  /* __cplusplus */\n", L_COPY);
    str = sarrayToString(sa, 1);
    l_byteaAppendString(ba, str);
    lept_free(str);
    sarrayDestroy(&sa);

        /* Then the prototypes */
    firstfile = 1 + nflags;
    protos_added = FALSE;
    if ((tempfile = l_makeTempFilename()) == NULL) {
        fprintf(stderr, "failure to make a writeable temp file\n");
        return 1;
    }
    for (i = firstfile; i < argc; i++) {
        filein = argv[i];
         len = strlen(filein);
         if (filein[len - 1] == 'h')  /* skip .h files */
             continue;
        snprintf(buf, L_BUFSIZE, "cpp -ansi -DNO_PROTOS %s %s",
                  filein, tempfile);
         ret = system(buf);  /* cpp */
         if (ret) {
            fprintf(stderr, "cpp failure for %s; continuing\n", filein);
            continue;
        }

        if ((str = parseForProtos(tempfile, prestring)) == NULL) {
            fprintf(stderr, "parse failure for %s; continuing\n", filein);
            continue;
        }
        if (strlen(str) > 1) {  /* strlen(str) == 1 is a file without protos */
            l_byteaAppendString(ba, str);
            protos_added = TRUE;
        }
        lept_free(str);
    }
    lept_rmfile(tempfile);
    lept_free(tempfile);

        /* Lastly the extern C tail */
    sa = sarrayCreate(0);
    sarrayAddString(sa, (char *)"\n#ifdef __cplusplus", L_COPY);
    sarrayAddString(sa, (char *)"}", L_COPY);
    sarrayAddString(sa, (char *)"#endif  /* __cplusplus */", L_COPY);
    str = sarrayToString(sa, 1);
    l_byteaAppendString(ba, str);
    lept_free(str);
    sarrayDestroy(&sa);

    protostr = (char *)l_byteaCopyData(ba, &nbytes);
    l_byteaDestroy(&ba);


    /* ---------------------------------------------------------------- */
    /*                       Generate the output                        */
    /* ---------------------------------------------------------------- */
    if (!outprotos) {  /* just write to stdout */
        fprintf(stderr, "%s\n", protostr);
        lept_free(protostr);
        return 0;
    }

        /* If no protos were found, do nothing further */
    if (!protos_added) {
        fprintf(stderr, "No protos found\n");
        lept_free(protostr);
        return 1;
    }

        /* Make the output files */
    ba = l_byteaInitFromFile("allheaders_top.txt");
    if (!in_line) {
        snprintf(buf, sizeof(buf), "#include \"%s\"\n", outprotos);
        l_byteaAppendString(ba, buf);
        l_binaryWrite(outprotos, "w", protostr, nbytes);
    } else {
        l_byteaAppendString(ba, protostr);
    }
    ba2 = l_byteaInitFromFile("allheaders_bot.txt");
    l_byteaJoin(ba, &ba2);
    l_byteaWrite("allheaders.h", ba, 0, 0);
    l_byteaDestroy(&ba);
    lept_free(protostr);
    return 0;
}

Project: leptonica
File: gplot.c

gplotAddPlot(GPLOT       *gplot,
             NUMA        *nax,
             NUMA        *nay,
              l_int32      plotstyle,
              const char  *plottitle)
 {
char       buf[L_BUFSIZE];
 char       emptystring[] = "";
 char      *datastr, *title;
 l_int32    n, i;
l_float32  valx, valy, startx, delx;
SARRAY    *sa;

    PROCNAME("gplotAddPlot");

    if (!gplot)
        return ERROR_INT("gplot not defined", procName, 1);
    if (!nay)
        return ERROR_INT("nay not defined", procName, 1);
    if (plotstyle < 0 || plotstyle >= NUM_GPLOT_STYLES)
        return ERROR_INT("invalid plotstyle", procName, 1);

    if ((n = numaGetCount(nay)) == 0)
        return ERROR_INT("no points to plot", procName, 1);
    if (nax && (n != numaGetCount(nax)))
        return ERROR_INT("nax and nay sizes differ", procName, 1);
    if (n == 1 && plotstyle == GPLOT_LINES) {
        L_INFO("only 1 pt; changing style to points\n", procName);
        plotstyle = GPLOT_POINTS;
    }

        /* Save plotstyle and plottitle */
    numaGetParameters(nay, &startx, &delx);
    numaAddNumber(gplot->plotstyles, plotstyle);
    if (plottitle) {
        title = stringNew(plottitle);
        sarrayAddString(gplot->plottitles, title, L_INSERT);
    } else {
        sarrayAddString(gplot->plottitles, emptystring, L_COPY);
    }
 
         /* Generate and save data filename */
     gplot->nplots++;
    snprintf(buf, L_BUFSIZE, "%s.data.%d", gplot->rootname, gplot->nplots);
     sarrayAddString(gplot->datanames, buf, L_COPY);
 
         /* Generate data and save as a string */
    sa = sarrayCreate(n);
    for (i = 0; i < n; i++) {
        if (nax)
            numaGetFValue(nax, i, &valx);
         else
             valx = startx + i * delx;
         numaGetFValue(nay, i, &valy);
        snprintf(buf, L_BUFSIZE, "%f %f\n", valx, valy);
         sarrayAddString(sa, buf, L_COPY);
     }
     datastr = sarrayToString(sa, 0);
    sarrayAddString(gplot->plotdata, datastr, L_INSERT);
    sarrayDestroy(&sa);

    return 0;
}

Project: leptonica
File: gplot.c

 gplotGenCommandFile(GPLOT  *gplot)
 {
char     buf[L_BUFSIZE];
 char    *cmdstr, *plottitle, *dataname;
 l_int32  i, plotstyle, nplots;
 FILE    *fp;

    PROCNAME("gplotGenCommandFile");

    if (!gplot)
        return ERROR_INT("gplot not defined", procName, 1);

        /* Remove any previous command data */
    sarrayClear(gplot->cmddata);
 
         /* Generate command data instructions */
     if (gplot->title) {   /* set title */
        snprintf(buf, L_BUFSIZE, "set title '%s'", gplot->title);
         sarrayAddString(gplot->cmddata, buf, L_COPY);
     }
     if (gplot->xlabel) {   /* set xlabel */
        snprintf(buf, L_BUFSIZE, "set xlabel '%s'", gplot->xlabel);
         sarrayAddString(gplot->cmddata, buf, L_COPY);
     }
     if (gplot->ylabel) {   /* set ylabel */
        snprintf(buf, L_BUFSIZE, "set ylabel '%s'", gplot->ylabel);
         sarrayAddString(gplot->cmddata, buf, L_COPY);
     }
 
         /* Set terminal type and output */
     if (gplot->outformat == GPLOT_PNG) {
        snprintf(buf, L_BUFSIZE, "set terminal png; set output '%s'",
                  gplot->outname);
     } else if (gplot->outformat == GPLOT_PS) {
        snprintf(buf, L_BUFSIZE, "set terminal postscript; set output '%s'",
                  gplot->outname);
     } else if (gplot->outformat == GPLOT_EPS) {
        snprintf(buf, L_BUFSIZE,
                  "set terminal postscript eps; set output '%s'",
                  gplot->outname);
     } else if (gplot->outformat == GPLOT_LATEX) {
        snprintf(buf, L_BUFSIZE, "set terminal latex; set output '%s'",
                  gplot->outname);
     }
     sarrayAddString(gplot->cmddata, buf, L_COPY);
 
     if (gplot->scaling == GPLOT_LOG_SCALE_X ||
         gplot->scaling == GPLOT_LOG_SCALE_X_Y) {
        snprintf(buf, L_BUFSIZE, "set logscale x");
         sarrayAddString(gplot->cmddata, buf, L_COPY);
     }
     if (gplot->scaling == GPLOT_LOG_SCALE_Y ||
         gplot->scaling == GPLOT_LOG_SCALE_X_Y) {
        snprintf(buf, L_BUFSIZE, "set logscale y");
         sarrayAddString(gplot->cmddata, buf, L_COPY);
     }
 
    nplots = sarrayGetCount(gplot->datanames);
    for (i = 0; i < nplots; i++) {
        plottitle = sarrayGetString(gplot->plottitles, i, L_NOCOPY);
         dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY);
         numaGetIValue(gplot->plotstyles, i, &plotstyle);
         if (nplots == 1) {
            snprintf(buf, L_BUFSIZE, "plot '%s' title '%s' %s",
                      dataname, plottitle, gplotstylenames[plotstyle]);
         } else {
             if (i == 0)
                snprintf(buf, L_BUFSIZE, "plot '%s' title '%s' %s, \\",
                      dataname, plottitle, gplotstylenames[plotstyle]);
             else if (i < nplots - 1)
                snprintf(buf, L_BUFSIZE, " '%s' title '%s' %s, \\",
                      dataname, plottitle, gplotstylenames[plotstyle]);
             else
                snprintf(buf, L_BUFSIZE, " '%s' title '%s' %s",
                      dataname, plottitle, gplotstylenames[plotstyle]);
         }
         sarrayAddString(gplot->cmddata, buf, L_COPY);
    }

        /* Write command data to file */
    cmdstr = sarrayToString(gplot->cmddata, 1);
    if ((fp = fopenWriteStream(gplot->cmdname, "w")) == NULL) {
        LEPT_FREE(cmdstr);
        return ERROR_INT("cmd stream not opened", procName, 1);
    }
    fwrite(cmdstr, 1, strlen(cmdstr), fp);
    fclose(fp);
    LEPT_FREE(cmdstr);
    return 0;
}

Project: leptonica
File: gplot.c

 gplotMakeOutput(GPLOT  *gplot)
 {
char     buf[L_BUFSIZE];
 char    *cmdname;
 l_int32  ignore;
 
    PROCNAME("gplotMakeOutput");

    if (!gplot)
        return ERROR_INT("gplot not defined", procName, 1);

    gplotGenCommandFile(gplot);
    gplotGenDataFiles(gplot);
     cmdname = genPathname(gplot->cmdname, NULL);
 
 #ifndef _WIN32
    snprintf(buf, L_BUFSIZE, "gnuplot %s", cmdname);
 #else
    snprintf(buf, L_BUFSIZE, "wgnuplot %s", cmdname);
 #endif  /* _WIN32 */
 
 #ifndef OS_IOS /* iOS 11 does not support system() */
    ignore = system(buf);  /* gnuplot || wgnuplot */
#endif /* !OS_IOS */

    LEPT_FREE(cmdname);
    return 0;
}

Project: leptonica
File: gplot.c

 gplotRead(const char  *filename)
 {
char     buf[L_BUFSIZE];
 char    *rootname, *title, *xlabel, *ylabel, *ignores;
 l_int32  outformat, ret, version, ignore;
 FILE    *fp;
GPLOT   *gplot;

    PROCNAME("gplotRead");

    if (!filename)
        return (GPLOT *)ERROR_PTR("filename not defined", procName, NULL);

    if ((fp = fopenReadStream(filename)) == NULL)
        return (GPLOT *)ERROR_PTR("stream not opened", procName, NULL);

    ret = fscanf(fp, "Gplot Version %d\n", &version);
    if (ret != 1) {
        fclose(fp);
        return (GPLOT *)ERROR_PTR("not a gplot file", procName, NULL);
    }
    if (version != GPLOT_VERSION_NUMBER) {
        fclose(fp);
         return (GPLOT *)ERROR_PTR("invalid gplot version", procName, NULL);
     }
 
    ignore = fscanf(fp, "Rootname: %511s\n", buf);  /* L_BUFSIZE - 1 */
     rootname = stringNew(buf);
     ignore = fscanf(fp, "Output format: %d\n", &outformat);
    ignores = fgets(buf, L_BUFSIZE, fp);   /* Title: ... */
     title = stringNew(buf + 7);
     title[strlen(title) - 1] = '\0';
    ignores = fgets(buf, L_BUFSIZE, fp);   /* X axis label: ... */
     xlabel = stringNew(buf + 14);
     xlabel[strlen(xlabel) - 1] = '\0';
    ignores = fgets(buf, L_BUFSIZE, fp);   /* Y axis label: ... */
     ylabel = stringNew(buf + 14);
     ylabel[strlen(ylabel) - 1] = '\0';
 
    gplot = gplotCreate(rootname, outformat, title, xlabel, ylabel);
    LEPT_FREE(rootname);
    LEPT_FREE(title);
    LEPT_FREE(xlabel);
    LEPT_FREE(ylabel);
    if (!gplot) {
        fclose(fp);
        return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL);
    }
    sarrayDestroy(&gplot->cmddata);
    sarrayDestroy(&gplot->datanames);
    sarrayDestroy(&gplot->plotdata);
     sarrayDestroy(&gplot->plottitles);
     numaDestroy(&gplot->plotstyles);
 
    ignore = fscanf(fp, "Commandfile name: %511s\n", buf);  /* L_BUFSIZE - 1 */
     stringReplace(&gplot->cmdname, buf);
     ignore = fscanf(fp, "\nCommandfile data:");
     gplot->cmddata = sarrayReadStream(fp);
    ignore = fscanf(fp, "\nDatafile names:");
    gplot->datanames = sarrayReadStream(fp);
    ignore = fscanf(fp, "\nPlot data:");
    gplot->plotdata = sarrayReadStream(fp);
    ignore = fscanf(fp, "\nPlot titles:");
    gplot->plottitles = sarrayReadStream(fp);
    ignore = fscanf(fp, "\nPlot styles:");
     gplot->plotstyles = numaReadStream(fp);
 
     ignore = fscanf(fp, "Number of plots: %d\n", &gplot->nplots);
    ignore = fscanf(fp, "Output file name: %511s\n", buf);
     stringReplace(&gplot->outname, buf);
     ignore = fscanf(fp, "Axis scaling: %d\n", &gplot->scaling);
 
    fclose(fp);
    return gplot;
}

Project: leptonica
File: ptabasic.c

 ptaReadStream(FILE  *fp)
 {
char       typestr[128];  /* hardcoded below in fscanf */
 l_int32    i, n, ix, iy, type, version;
 l_float32  x, y;
 PTA       *pta;

    PROCNAME("ptaReadStream");

    if (!fp)
        return (PTA *)ERROR_PTR("stream not defined", procName, NULL);

    if (fscanf(fp, "\n Pta Version %d\n", &version) != 1)
         return (PTA *)ERROR_PTR("not a pta file", procName, NULL);
     if (version != PTA_VERSION_NUMBER)
         return (PTA *)ERROR_PTR("invalid pta version", procName, NULL);
    if (fscanf(fp, " Number of pts = %d; format = %127s\n", &n, typestr) != 2)
         return (PTA *)ERROR_PTR("not a pta file", procName, NULL);
     if (!strcmp(typestr, "float"))
         type = 0;
    else  /* typestr is "integer" */
        type = 1;

    if ((pta = ptaCreate(n)) == NULL)
        return (PTA *)ERROR_PTR("pta not made", procName, NULL);
    for (i = 0; i < n; i++) {
        if (type == 0) {  /* data is float */
            if (fscanf(fp, "   (%f, %f)\n", &x, &y) != 2) {
                ptaDestroy(&pta);
                return (PTA *)ERROR_PTR("error reading floats", procName, NULL);
            }
            ptaAddPt(pta, x, y);
        } else {   /* data is integer */
            if (fscanf(fp, "   (%d, %d)\n", &ix, &iy) != 2) {
                ptaDestroy(&pta);
                return (PTA *)ERROR_PTR("error reading ints", procName, NULL);
            }
            ptaAddPt(pta, ix, iy);
        }
    }

    return pta;
}

Project: leptonica
File: sel1.c

 selReadStream(FILE  *fp)
 {
 char    *selname;
char     linebuf[L_BUFSIZE];
 l_int32  sy, sx, cy, cx, i, j, version, ignore;
 SEL     *sel;
 
    PROCNAME("selReadStream");

    if (!fp)
        return (SEL *)ERROR_PTR("stream not defined", procName, NULL);

    if (fscanf(fp, "  Sel Version %d\n", &version) != 1)
        return (SEL *)ERROR_PTR("not a sel file", procName, NULL);
     if (version != SEL_VERSION_NUMBER)
         return (SEL *)ERROR_PTR("invalid sel version", procName, NULL);
 
    if (fgets(linebuf, L_BUFSIZE, fp) == NULL)
         return (SEL *)ERROR_PTR("error reading into linebuf", procName, NULL);
     selname = stringNew(linebuf);
    sscanf(linebuf, "  ------  %200s  ------", selname);
 
     if (fscanf(fp, "  sy = %d, sx = %d, cy = %d, cx = %d\n",
             &sy, &sx, &cy, &cx) != 4) {
        LEPT_FREE(selname);
        return (SEL *)ERROR_PTR("dimensions not read", procName, NULL);
    }

    if ((sel = selCreate(sy, sx, selname)) == NULL) {
        LEPT_FREE(selname);
        return (SEL *)ERROR_PTR("sel not made", procName, NULL);
    }
    selSetOrigin(sel, cy, cx);

    for (i = 0; i < sy; i++) {
        ignore = fscanf(fp, "    ");
        for (j = 0; j < sx; j++)
            ignore = fscanf(fp, "%1d", &sel->data[i][j]);
        ignore = fscanf(fp, "\n");
    }
    ignore = fscanf(fp, "\n");

    LEPT_FREE(selname);
    return sel;
}

Project: leptonica
File: sel1.c

selaGetCombName(SELA    *sela,
                l_int32  size,
                 l_int32  direction)
 {
 char    *selname;
char     combname[L_BUFSIZE];
 l_int32  i, nsels, sx, sy, found;
 SEL     *sel;
 
    PROCNAME("selaGetCombName");

    if (!sela)
        return (char *)ERROR_PTR("sela not defined", procName, NULL);
    if (direction != L_HORIZ && direction != L_VERT)
        return (char *)ERROR_PTR("invalid direction", procName, NULL);
 
         /* Derive the comb name we're looking for */
     if (direction == L_HORIZ)
        snprintf(combname, L_BUFSIZE, "sel_comb_%dh", size);
     else  /* direction == L_VERT */
        snprintf(combname, L_BUFSIZE, "sel_comb_%dv", size);
 
     found = FALSE;
     nsels = selaGetCount(sela);
    for (i = 0; i < nsels; i++) {
        sel = selaGetSel(sela, i);
        selGetParameters(sel, &sy, &sx, NULL, NULL);
        if (sy != 1 && sx != 1)  /* 2-D; not a comb */
            continue;
        selname = selGetName(sel);
        if (!strcmp(selname, combname)) {
            found = TRUE;
            break;
        }
    }

    if (found)
        return stringNew(selname);
    else
        return (char *)ERROR_PTR("sel not found", procName, NULL);
}