CVE-2018-7186 (published 2018-02-16T16:29Z)

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.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-787
cwe_name: Out-of-bounds Write
description: The product writes data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/DanBloomberg/leptonica
commit_details: repo_url: https://github.com/DanBloomberg/leptonica
author: Dan Bloomberg
author_date: 2018-02-15 15:19:04-08:00
msg: 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().
repository: repo_url: https://github.com/DanBloomberg/leptonica
repo_name: DanBloomberg/leptonica
description: Leptonica is an open source library containing software that is broadly useful for image processing and image analysis applications. The official github repository for Leptonica is: danbloomberg/leptonica.  See leptonica.org for more documentation.
repo_language: C
owner: DanBloomberg
file_changes: filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -63,9 +63,10 @@ www.leptonica.org
 
 
 ## Open Source Projects that use Leptonica ##
-  * [php](http://en.wikipedia.org/wiki/PHP)  (scripting language for dynamic web pages)
   * [tesseract](https://github.com/tesseract-ocr/tesseract/) (optical character recognition)
+  * [OpenCV](https://github.com/opencv/opencv) (computer vision library)
   * [jbig2enc](http://www.imperialviolet.org/jbig2.html) (encodes multipage binary image documents with jbig2 compression)
+  * [php](http://en.wikipedia.org/wiki/PHP)  (scripting language for dynamic web pages)
 
 ## Major contributors to Leptonica ##
   * Tom Powers: Tom has supported Leptonica on Windows for many years.  He has made many contributions to code quality and documentation, including the beautiful "unofficial documentation" on the web site. Without his effort, Leptonica would not run today on Windows.

programming_language: Markdown
filename: xtractprotos.c
change_type: ModificationType.MODIFY
diff: @@ -87,7 +87,7 @@
 #include <string.h>
 #include "allheaders.h"
 
-static const l_int32  L_BUF_SIZE = 512;
+static const l_int32  L_BUFSIZE = 512;  /* hardcoded below in sscanf() */
 static const char *version = "1.5";
 
 
@@ -96,7 +96,7 @@ int main(int    argc,
 {
 char        *filein, *str, *tempfile, *prestring, *outprotos, *protostr;
 const char  *spacestr = " ";
-char         buf[L_BUF_SIZE];
+char         buf[L_BUFSIZE];
 l_uint8     *allheaders;
 l_int32      i, maxindex, in_line, nflags, protos_added, firstfile, len, ret;
 size_t       nbytes;
@@ -125,12 +125,12 @@ static char  mainName[] = "xtractprotos";
         if (argv[i][0] == '-') {
             if (!strncmp(argv[i], "-prestring", 10)) {
                 nflags++;
-                ret = sscanf(argv[i] + 1, "prestring=%s", buf);
+                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_BUF_SIZE - 3) {
+                if ((len = strlen(buf)) > L_BUFSIZE - 3) {
                     L_WARNING("prestring too large; omitting!\n", mainName);
                 } else {
                     buf[len] = ' ';
@@ -139,7 +139,7 @@ static char  mainName[] = "xtractprotos";
                 }
             } else if (!strncmp(argv[i], "-protos", 7)) {
                 nflags++;
-                ret = sscanf(argv[i] + 1, "protos=%s", buf);
+                ret = sscanf(argv[i] + 1, "protos=%490s", buf);
                 if (ret != 1) {
                     fprintf(stderr, "parse failure for protos\n");
                     return 1;
@@ -165,7 +165,7 @@ static char  mainName[] = "xtractprotos";
         /* First the extern C head */
     sa = sarrayCreate(0);
     sarrayAddString(sa, (char *)"/*", L_COPY);
-    snprintf(buf, L_BUF_SIZE,
+    snprintf(buf, L_BUFSIZE,
              " *  These prototypes were autogen'd by xtractprotos, v. %s",
              version);
     sarrayAddString(sa, buf, L_COPY);
@@ -190,7 +190,7 @@ static char  mainName[] = "xtractprotos";
         len = strlen(filein);
         if (filein[len - 1] == 'h')  /* skip .h files */
             continue;
-        snprintf(buf, L_BUF_SIZE, "cpp -ansi -DNO_PROTOS %s %s",
+        snprintf(buf, L_BUFSIZE, "cpp -ansi -DNO_PROTOS %s %s",
                  filein, tempfile);
         ret = system(buf);  /* cpp */
         if (ret) {

programming_language: C
filename: gplot.c
change_type: ModificationType.MODIFY
diff: @@ -50,9 +50,9 @@
  *          l_int32     gplotWrite()
  *
  *
- *     Utility for programmatic plotting using gnuplot 7.3.2 or later
+ *     Utility for programmatic plotting using gnuplot 4.6 or later
  *     Enabled:
- *         ~ output to png (color), ps (mono), x11 (color), latex (mono)
+ *         ~ output to png (color), ps and eps (mono), latex (mono)
  *         ~ optional title for graph
  *         ~ optional x and y axis labels
  *         ~ multiple plots on one frame
@@ -100,7 +100,7 @@
 #include <string.h>
 #include "allheaders.h"
 
-static const l_int32  L_BUF_SIZE = 512;
+static const l_int32  L_BUFSIZE = 512;  /* hardcoded below in fscanf */
 
 const char  *gplotstylenames[] = {"with lines",
                                   "with points",
@@ -142,7 +142,7 @@ gplotCreate(const char  *rootname,
             const char  *ylabel)
 {
 char    *newroot;
-char     buf[L_BUF_SIZE];
+char     buf[L_BUFSIZE];
 l_int32  badchar;
 GPLOT   *gplot;
 
@@ -169,16 +169,16 @@ GPLOT   *gplot;
     newroot = genPathname(rootname, NULL);
     gplot->rootname = newroot;
     gplot->outformat = outformat;
-    snprintf(buf, L_BUF_SIZE, "%s.cmd", rootname);
+    snprintf(buf, L_BUFSIZE, "%s.cmd", rootname);
     gplot->cmdname = stringNew(buf);
     if (outformat == GPLOT_PNG)
-        snprintf(buf, L_BUF_SIZE, "%s.png", newroot);
+        snprintf(buf, L_BUFSIZE, "%s.png", newroot);
     else if (outformat == GPLOT_PS)
-        snprintf(buf, L_BUF_SIZE, "%s.ps", newroot);
+        snprintf(buf, L_BUFSIZE, "%s.ps", newroot);
     else if (outformat == GPLOT_EPS)
-        snprintf(buf, L_BUF_SIZE, "%s.eps", newroot);
+        snprintf(buf, L_BUFSIZE, "%s.eps", newroot);
     else if (outformat == GPLOT_LATEX)
-        snprintf(buf, L_BUF_SIZE, "%s.tex", newroot);
+        snprintf(buf, L_BUFSIZE, "%s.tex", newroot);
     gplot->outname = stringNew(buf);
     if (title) gplot->title = stringNew(title);
     if (xlabel) gplot->xlabel = stringNew(xlabel);
@@ -266,7 +266,7 @@ gplotAddPlot(GPLOT       *gplot,
              l_int32      plotstyle,
              const char  *plottitle)
 {
-char       buf[L_BUF_SIZE];
+char       buf[L_BUFSIZE];
 char       emptystring[] = "";
 char      *datastr, *title;
 l_int32    n, i;
@@ -303,7 +303,7 @@ SARRAY    *sa;
 
         /* Generate and save data filename */
     gplot->nplots++;
-    snprintf(buf, L_BUF_SIZE, "%s.data.%d", gplot->rootname, 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 */
@@ -314,7 +314,7 @@ SARRAY    *sa;
         else
             valx = startx + i * delx;
         numaGetFValue(nay, i, &valy);
-        snprintf(buf, L_BUF_SIZE, "%f %f\n", valx, valy);
+        snprintf(buf, L_BUFSIZE, "%f %f\n", valx, valy);
         sarrayAddString(sa, buf, L_COPY);
     }
     datastr = sarrayToString(sa, 0);
@@ -378,7 +378,7 @@ gplotSetScaling(GPLOT   *gplot,
 l_int32
 gplotMakeOutput(GPLOT  *gplot)
 {
-char     buf[L_BUF_SIZE];
+char     buf[L_BUFSIZE];
 char    *cmdname;
 l_int32  ignore;
 
@@ -392,9 +392,9 @@ l_int32  ignore;
     cmdname = genPathname(gplot->cmdname, NULL);
 
 #ifndef _WIN32
-    snprintf(buf, L_BUF_SIZE, "gnuplot %s", cmdname);
+    snprintf(buf, L_BUFSIZE, "gnuplot %s", cmdname);
 #else
-    snprintf(buf, L_BUF_SIZE, "wgnuplot %s", cmdname);
+    snprintf(buf, L_BUFSIZE, "wgnuplot %s", cmdname);
 #endif  /* _WIN32 */
 
 #ifndef OS_IOS /* iOS 11 does not support system() */
@@ -415,7 +415,7 @@ l_int32  ignore;
 l_int32
 gplotGenCommandFile(GPLOT  *gplot)
 {
-char     buf[L_BUF_SIZE];
+char     buf[L_BUFSIZE];
 char    *cmdstr, *plottitle, *dataname;
 l_int32  i, plotstyle, nplots;
 FILE    *fp;
@@ -430,43 +430,43 @@ FILE    *fp;
 
         /* Generate command data instructions */
     if (gplot->title) {   /* set title */
-        snprintf(buf, L_BUF_SIZE, "set title '%s'", gplot->title);
+        snprintf(buf, L_BUFSIZE, "set title '%s'", gplot->title);
         sarrayAddString(gplot->cmddata, buf, L_COPY);
     }
     if (gplot->xlabel) {   /* set xlabel */
-        snprintf(buf, L_BUF_SIZE, "set xlabel '%s'", gplot->xlabel);
+        snprintf(buf, L_BUFSIZE, "set xlabel '%s'", gplot->xlabel);
         sarrayAddString(gplot->cmddata, buf, L_COPY);
     }
     if (gplot->ylabel) {   /* set ylabel */
-        snprintf(buf, L_BUF_SIZE, "set ylabel '%s'", gplot->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_BUF_SIZE, "set terminal png; set output '%s'",
+        snprintf(buf, L_BUFSIZE, "set terminal png; set output '%s'",
                  gplot->outname);
     } else if (gplot->outformat == GPLOT_PS) {
-        snprintf(buf, L_BUF_SIZE, "set terminal postscript; set output '%s'",
+        snprintf(buf, L_BUFSIZE, "set terminal postscript; set output '%s'",
                  gplot->outname);
     } else if (gplot->outformat == GPLOT_EPS) {
-        snprintf(buf, L_BUF_SIZE,
+        snprintf(buf, L_BUFSIZE,
                  "set terminal postscript eps; set output '%s'",
                  gplot->outname);
     } else if (gplot->outformat == GPLOT_LATEX) {
-        snprintf(buf, L_BUF_SIZE, "set terminal latex; set output '%s'",
+        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_BUF_SIZE, "set logscale x");
+        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_BUF_SIZE, "set logscale y");
+        snprintf(buf, L_BUFSIZE, "set logscale y");
         sarrayAddString(gplot->cmddata, buf, L_COPY);
     }
 
@@ -476,17 +476,17 @@ FILE    *fp;
         dataname = sarrayGetString(gplot->datanames, i, L_NOCOPY);
         numaGetIValue(gplot->plotstyles, i, &plotstyle);
         if (nplots == 1) {
-            snprintf(buf, L_BUF_SIZE, "plot '%s' title '%s' %s",
+            snprintf(buf, L_BUFSIZE, "plot '%s' title '%s' %s",
                      dataname, plottitle, gplotstylenames[plotstyle]);
         } else {
             if (i == 0)
-                snprintf(buf, L_BUF_SIZE, "plot '%s' title '%s' %s, \\",
+                snprintf(buf, L_BUFSIZE, "plot '%s' title '%s' %s, \\",
                      dataname, plottitle, gplotstylenames[plotstyle]);
             else if (i < nplots - 1)
-                snprintf(buf, L_BUF_SIZE, " '%s' title '%s' %s, \\",
+                snprintf(buf, L_BUFSIZE, " '%s' title '%s' %s, \\",
                      dataname, plottitle, gplotstylenames[plotstyle]);
             else
-                snprintf(buf, L_BUF_SIZE, " '%s' title '%s' %s",
+                snprintf(buf, L_BUFSIZE, " '%s' title '%s' %s",
                      dataname, plottitle, gplotstylenames[plotstyle]);
         }
         sarrayAddString(gplot->cmddata, buf, L_COPY);
@@ -820,7 +820,7 @@ NUMA    *nay;
 GPLOT *
 gplotRead(const char  *filename)
 {
-char     buf[L_BUF_SIZE];
+char     buf[L_BUFSIZE];
 char    *rootname, *title, *xlabel, *ylabel, *ignores;
 l_int32  outformat, ret, version, ignore;
 FILE    *fp;
@@ -844,16 +844,16 @@ GPLOT   *gplot;
         return (GPLOT *)ERROR_PTR("invalid gplot version", procName, NULL);
     }
 
-    ignore = fscanf(fp, "Rootname: %s\n", buf);
+    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_BUF_SIZE, fp);   /* Title: ... */
+    ignores = fgets(buf, L_BUFSIZE, fp);   /* Title: ... */
     title = stringNew(buf + 7);
     title[strlen(title) - 1] = '\0';
-    ignores = fgets(buf, L_BUF_SIZE, fp);   /* X axis label: ... */
+    ignores = fgets(buf, L_BUFSIZE, fp);   /* X axis label: ... */
     xlabel = stringNew(buf + 14);
     xlabel[strlen(xlabel) - 1] = '\0';
-    ignores = fgets(buf, L_BUF_SIZE, fp);   /* Y axis label: ... */
+    ignores = fgets(buf, L_BUFSIZE, fp);   /* Y axis label: ... */
     ylabel = stringNew(buf + 14);
     ylabel[strlen(ylabel) - 1] = '\0';
 
@@ -872,7 +872,7 @@ GPLOT   *gplot;
     sarrayDestroy(&gplot->plottitles);
     numaDestroy(&gplot->plotstyles);
 
-    ignore = fscanf(fp, "Commandfile name: %s\n", buf);
+    ignore = fscanf(fp, "Commandfile name: %511s\n", buf);  /* L_BUFSIZE - 1 */
     stringReplace(&gplot->cmdname, buf);
     ignore = fscanf(fp, "\nCommandfile data:");
     gplot->cmddata = sarrayReadStream(fp);
@@ -886,7 +886,7 @@ GPLOT   *gplot;
     gplot->plotstyles = numaReadStream(fp);
 
     ignore = fscanf(fp, "Number of plots: %d\n", &gplot->nplots);
-    ignore = fscanf(fp, "Output file name: %s\n", buf);
+    ignore = fscanf(fp, "Output file name: %511s\n", buf);
     stringReplace(&gplot->outname, buf);
     ignore = fscanf(fp, "Axis scaling: %d\n", &gplot->scaling);
 

programming_language: C
filename: ptabasic.c
change_type: ModificationType.MODIFY
diff: @@ -689,7 +689,7 @@ PTA   *pta;
 PTA *
 ptaReadStream(FILE  *fp)
 {
-char       typestr[128];
+char       typestr[128];  /* hardcoded below in fscanf */
 l_int32    i, n, ix, iy, type, version;
 l_float32  x, y;
 PTA       *pta;
@@ -703,7 +703,7 @@ PTA       *pta;
         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 = %s\n", &n, typestr) != 2)
+    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;

programming_language: C
filename: sel1.c
change_type: ModificationType.MODIFY
diff: @@ -141,7 +141,7 @@
 #include <string.h>
 #include "allheaders.h"
 
-static const l_int32  L_BUF_SIZE = 256;
+static const l_int32  L_BUFSIZE = 256;  /* hardcoded below in sscanf */
 static const l_int32  INITIAL_PTR_ARRAYSIZE = 50;  /* n'import quoi */
 static const l_int32  MANY_SELS = 1000;
 
@@ -978,7 +978,7 @@ selaGetCombName(SELA    *sela,
                 l_int32  direction)
 {
 char    *selname;
-char     combname[L_BUF_SIZE];
+char     combname[L_BUFSIZE];
 l_int32  i, nsels, sx, sy, found;
 SEL     *sel;
 
@@ -991,9 +991,9 @@ SEL     *sel;
 
         /* Derive the comb name we're looking for */
     if (direction == L_HORIZ)
-        snprintf(combname, L_BUF_SIZE, "sel_comb_%dh", size);
+        snprintf(combname, L_BUFSIZE, "sel_comb_%dh", size);
     else  /* direction == L_VERT */
-        snprintf(combname, L_BUF_SIZE, "sel_comb_%dv", size);
+        snprintf(combname, L_BUFSIZE, "sel_comb_%dv", size);
 
     found = FALSE;
     nsels = selaGetCount(sela);
@@ -1041,7 +1041,7 @@ static void
 selaComputeCompositeParameters(const char  *fileout)
 {
 char    *str, *nameh1, *nameh2, *namev1, *namev2;
-char     buf[L_BUF_SIZE];
+char     buf[L_BUFSIZE];
 l_int32  size, size1, size2, len;
 SARRAY  *sa;
 SELA    *selabasic, *selacomb;
@@ -1060,7 +1060,7 @@ SELA    *selabasic, *selacomb;
             nameh2 = stringNew("");
             namev2 = stringNew("");
         }
-        snprintf(buf, L_BUF_SIZE,
+        snprintf(buf, L_BUFSIZE,
                  "      { %d, %d, %d, \"%s\", \"%s\", \"%s\", \"%s\" },",
                  size, size1, size2, nameh1, nameh2, namev1, namev2);
         sarrayAddString(sa, buf, L_COPY);
@@ -1409,7 +1409,7 @@ SEL  *
 selReadStream(FILE  *fp)
 {
 char    *selname;
-char     linebuf[L_BUF_SIZE];
+char     linebuf[L_BUFSIZE];
 l_int32  sy, sx, cy, cx, i, j, version, ignore;
 SEL     *sel;
 
@@ -1423,10 +1423,10 @@ SEL     *sel;
     if (version != SEL_VERSION_NUMBER)
         return (SEL *)ERROR_PTR("invalid sel version", procName, NULL);
 
-    if (fgets(linebuf, L_BUF_SIZE, fp) == NULL)
+    if (fgets(linebuf, L_BUFSIZE, fp) == NULL)
         return (SEL *)ERROR_PTR("error reading into linebuf", procName, NULL);
     selname = stringNew(linebuf);
-    sscanf(linebuf, "  ------  %s  ------", selname);
+    sscanf(linebuf, "  ------  %200s  ------", selname);
 
     if (fscanf(fp, "  sy = %d, sx = %d, cy = %d, cx = %d\n",
             &sy, &sx, &cy, &cx) != 4) {

programming_language: C++