CVE-2022-38266: Divide By Zero (CWE-369)
value: An issue in the Leptonica linked library (v1.79.0) allows attackers to cause an arithmetic exception leading to a Denial of Service (DoS) via a crafted JPEG file.
CWE description: The product divides a value by zero.
CVSS v3 base score: 6.5
Published: 2022-09-09T22:15Z
Repository: https://github.com/DanBloomberg/leptonica
Language: C
Files changed: src/convolve.c
Commit date: 2020-10-28 17:37:30 -0700
Commit message: Issue 26393: morphapp_fuzzer: Divide-by-zero in blockconvLow
* Removed the code that allowed divide by zero for tiny pix
* Ditto for 4 other block convolution functions.

Diff: diff --git a/src/convolve.c b/src/convolve.c
index 72afa941..fda032ba 100644
--- a/src/convolve.c
+++ b/src/convolve.c
@@ -112,21 +112,22 @@ static void blocksumLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpl,
  *             Top-level grayscale or color block convolution           *
  *----------------------------------------------------------------------*/
 /*!
  * \brief   pixBlockconv()
  *
- * \param[in]    pix 8    or 32 bpp; or 2, 4 or 8 bpp with colormap
+ * \param[in]    pix      8 or 32 bpp; or 2, 4 or 8 bpp with colormap
  * \param[in]    wc, hc   half width/height of convolution kernel
  * \return  pixd, or NULL on error
  *
  * <pre>
  * Notes:
  *      (1) The full width and height of the convolution kernel
  *          are (2 * wc + 1) and (2 * hc + 1)
- *      (2) Returns a copy if both wc and hc are 0
+ *      (2) Returns a copy if either wc or hc are 0
  *      (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
- *          where (w,h) are the dimensions of pixs.
+ *          where (w,h) are the dimensions of pixs.  Otherwise,
+ *          return a copy.
  * </pre>
  */
 PIX  *
 pixBlockconv(PIX     *pix,
              l_int32  wc,
@@ -137,21 +138,18 @@ PIX     *pixs, *pixd, *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc;
 
     PROCNAME("pixBlockconv");
 
     if (!pix)
         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
-    if (wc < 0) wc = 0;
-    if (hc < 0) hc = 0;
+    if (wc <= 0 || hc <= 0)
+        return pixCopy(NULL, pix);
     pixGetDimensions(pix, &w, &h, &d);
     if (w < 2 * wc + 1 || h < 2 * hc + 1) {
-        wc = L_MIN(wc, (w - 1) / 2);
-        hc = L_MIN(hc, (h - 1) / 2);
-        L_WARNING("kernel too large; reducing!\n", procName);
-        L_INFO("wc = %d, hc = %d\n", procName, wc, hc);
+        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
+                procName, w, wc, h, hc);
+        return pixCopy(NULL, pix);  /* no-op */
     }
-    if (wc == 0 && hc == 0)   /* no-op */
-        return pixCopy(NULL, pix);
 
         /* Remove colormap if necessary */
     if ((d == 2 || d == 4 || d == 8) && pixGetColormap(pix)) {
         L_WARNING("pix has colormap; removing\n", procName);
         pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
@@ -203,13 +201,14 @@ PIX     *pixs, *pixd, *pixr, *pixrc, *pixg, *pixgc, *pixb, *pixbc;
  * Notes:
  *      (1) If accum pix is null, make one and destroy it before
  *          returning; otherwise, just use the input accum pix.
  *      (2) The full width and height of the convolution kernel
  *          are (2 * wc + 1) and (2 * hc + 1).
- *      (3) Returns a copy if both wc and hc are 0.
+ *      (3) Returns a copy if either wc or hc are 0
  *      (4) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
- *          where (w,h) are the dimensions of pixs.
+ *          where (w,h) are the dimensions of pixs.  Otherwise,
+ *          return a copy.
  * </pre>
  */
 PIX *
 pixBlockconvGray(PIX     *pixs,
                  PIX     *pixacc,
@@ -225,17 +224,15 @@ PIX       *pixd, *pixt;
     if (!pixs)
         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
     pixGetDimensions(pixs, &w, &h, &d);
     if (d != 8)
         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
-    if (wc < 0) wc = 0;
-    if (hc < 0) hc = 0;
-    if (wc == 0 && hc == 0)   /* no-op */
+    if (wc <= 0 || hc <= 0)   /* no-op */
         return pixCopy(NULL, pixs);
     if (w < 2 * wc + 1 || h < 2 * hc + 1) {
-        L_WARNING("kernel too large; returning a copy\n", procName);
-        L_INFO("w = %d, wc = %d, h = %d, hc = %d\n", procName, w, wc, h, hc);
+        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
+                procName, w, wc, h, hc);
         return pixCopy(NULL, pixs);
     }
 
     if (pixacc) {
         if (pixGetDepth(pixacc) == 32) {
@@ -610,11 +607,11 @@ l_uint32  *lines, *lined, *linedp;
  * Notes:
  *      (1) The full width and height of the convolution kernel
  *          are (2 * wc + 1) and (2 * hc + 1).
  *      (2) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
  *          where (w,h) are the dimensions of pixs.
- *      (3) Returns a copy if both wc and hc are 0.
+ *      (3) Returns a copy if either wc or hc are 0.
  *      (3) Adds mirrored border to avoid treating the boundary pixels
  *          specially.  Note that we add wc + 1 pixels to the left
  *          and wc to the right.  The added width is 2 * wc + 1 pixels,
  *          and the particular choice simplifies the indexing in the loop.
  *          Likewise, add hc + 1 pixels to the top and hc to the bottom.
@@ -648,20 +645,17 @@ PIX       *pixsb, *pixacc, *pixd;
     if (!pixs)
         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
     pixGetDimensions(pixs, &w, &h, &d);
     if (d != 8)
         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
-    if (wc < 0) wc = 0;
-    if (hc < 0) hc = 0;
+    if (wc <= 0 || hc <= 0)  /* no-op */
+        return pixCopy(NULL, pixs);
     if (w < 2 * wc + 1 || h < 2 * hc + 1) {
-        wc = L_MIN(wc, (w - 1) / 2);
-        hc = L_MIN(hc, (h - 1) / 2);
-        L_WARNING("kernel too large; reducing!\n", procName);
-        L_INFO("wc = %d, hc = %d\n", procName, wc, hc);
-    }
-    if (wc == 0 && hc == 0)   /* no-op */
+        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
+                procName, w, wc, h, hc);
         return pixCopy(NULL, pixs);
+    }
 
     if ((pixsb = pixAddMirroredBorder(pixs, wc + 1, wc, hc + 1, hc)) == NULL)
         return (PIX *)ERROR_PTR("pixsb not made", procName, NULL);
     pixacc = pixBlockconvAccum(pixsb);
     pixDestroy(&pixsb);
@@ -705,13 +699,14 @@ PIX       *pixsb, *pixacc, *pixd;
  *
  * <pre>
  * Notes:
  *      (1) The full width and height of the convolution kernel
  *          are (2 * wc + 1) and (2 * hc + 1)
- *      (2) Returns a copy if both wc and hc are 0
+ *      (2) Returns a copy if either wc or hc are 0.
  *      (3) Require that w >= 2 * wc + 1 and h >= 2 * hc + 1,
- *          where (w,h) are the dimensions of pixs.
+ *          where (w,h) are the dimensions of pixs.  Otherwise,
+ *          return a copy.
  *      (4) For nx == ny == 1, this defaults to pixBlockconv(), which
  *          is typically about twice as fast, and gives nearly
  *          identical results as pixBlockconvGrayTile().
  *      (5) If the tiles are too small, nx and/or ny are reduced
  *          a minimum amount so that the tiles are expanded to the
@@ -739,23 +734,20 @@ PIXTILING  *pt;
 
     PROCNAME("pixBlockconvTiled");
 
     if (!pix)
         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
-    if (wc < 0) wc = 0;
-    if (hc < 0) hc = 0;
-    pixGetDimensions(pix, &w, &h, &d);
-    if (w < 2 * wc + 3 || h < 2 * hc + 3) {
-        wc = L_MAX(0, L_MIN(wc, (w - 3) / 2));
-        hc = L_MAX(0, L_MIN(hc, (h - 3) / 2));
-        L_WARNING("kernel too large; reducing!\n", procName);
-        L_INFO("wc = %d, hc = %d\n", procName, wc, hc);
-    }
-    if (wc == 0 && hc == 0)   /* no-op */
+    if (wc <= 0 || hc <= 0)   /* no-op */
         return pixCopy(NULL, pix);
     if (nx <= 1 && ny <= 1)
         return pixBlockconv(pix, wc, hc);
+    pixGetDimensions(pix, &w, &h, &d);
+    if (w < 2 * wc + 3 || h < 2 * hc + 3) {
+        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
+                procName, w, wc, h, hc);
+        return pixCopy(NULL, pix);
+    }
 
         /* Test to see if the tiles are too small.  The required
          * condition is that the tile dimensions must be at least
          * (wc + 2) x (hc + 2). */
     xrat = w / nx;
@@ -844,13 +836,14 @@ PIXTILING  *pt;
  *          are (2 * wc + 1) and (2 * hc + 1)
  *      (2) Assumes that the input pixs is padded with (wc + 1) pixels on
  *          left and right, and with (hc + 1) pixels on top and bottom.
  *          The returned pix has these stripped off; they are only used
  *          for computation.
- *      (3) Returns a copy if both wc and hc are 0
- *      (4) Require that w > 2 * wc + 1 and h > 2 * hc + 1,
- *          where (w,h) are the dimensions of pixs.
+ *      (3) Returns a copy if either wc or hc are 0.
+ *      (4) Require that w > 2 * wc + 3 and h > 2 * hc + 3,
+ *          where (w,h) are the dimensions of pixs.  Otherwise,
+ *          return a copy.
  * </pre>
  */
 PIX *
 pixBlockconvGrayTile(PIX     *pixs,
                      PIX     *pixacc,
@@ -868,20 +861,17 @@ PIX       *pixt, *pixd;
     if (!pixs)
         return (PIX *)ERROR_PTR("pix not defined", procName, NULL);
     pixGetDimensions(pixs, &w, &h, &d);
     if (d != 8)
         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
-    if (wc < 0) wc = 0;
-    if (hc < 0) hc = 0;
+    if (wc <= 0 || hc <= 0)  /* no-op */
+        return pixCopy(NULL, pixs);
     if (w < 2 * wc + 3 || h < 2 * hc + 3) {
-        wc = L_MAX(0, L_MIN(wc, (w - 3) / 2));
-        hc = L_MAX(0, L_MIN(hc, (h - 3) / 2));
-        L_WARNING("kernel too large; reducing!\n", procName);
-        L_INFO("wc = %d, hc = %d\n", procName, wc, hc);
-    }
-    if (wc == 0 && hc == 0)
+        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
+                procName, w, wc, h, hc);
         return pixCopy(NULL, pixs);
+    }
     wd = w - 2 * wc;
     hd = h - 2 * hc;
 
     if (pixacc) {
         if (pixGetDepth(pixacc) == 32) {

Vulnerable code:  * \param[in]    pix 8    or 32 bpp; or 2, 4 or 8 bpp with colormap
 *      (2) Returns a copy if both wc and hc are 0
 *          where (w,h) are the dimensions of pixs.
    if (wc < 0) wc = 0;
    if (hc < 0) hc = 0;
        wc = L_MIN(wc, (w - 1) / 2);
        hc = L_MIN(hc, (h - 1) / 2);
        L_WARNING("kernel too large; reducing!\n", procName);
        L_INFO("wc = %d, hc = %d\n", procName, wc, hc);
    if (wc == 0 && hc == 0)   /* no-op */
        return pixCopy(NULL, pix);
 *      (3) Returns a copy if both wc and hc are 0.
 *          where (w,h) are the dimensions of pixs.
    if (wc < 0) wc = 0;
    if (hc < 0) hc = 0;
    if (wc == 0 && hc == 0)   /* no-op */
        L_WARNING("kernel too large; returning a copy\n", procName);
        L_INFO("w = %d, wc = %d, h = %d, hc = %d\n", procName, w, wc, h, hc);
 *      (3) Returns a copy if both wc and hc are 0.
    if (wc < 0) wc = 0;
    if (hc < 0) hc = 0;
        wc = L_MIN(wc, (w - 1) / 2);
        hc = L_MIN(hc, (h - 1) / 2);
        L_WARNING("kernel too large; reducing!\n", procName);
        L_INFO("wc = %d, hc = %d\n", procName, wc, hc);
    }
    if (wc == 0 && hc == 0)   /* no-op */
 *      (2) Returns a copy if both wc and hc are 0
 *          where (w,h) are the dimensions of pixs.
    if (wc < 0) wc = 0;
    if (hc < 0) hc = 0;
    pixGetDimensions(pix, &w, &h, &d);
    if (w < 2 * wc + 3 || h < 2 * hc + 3) {
        wc = L_MAX(0, L_MIN(wc, (w - 3) / 2));
        hc = L_MAX(0, L_MIN(hc, (h - 3) / 2));
        L_WARNING("kernel too large; reducing!\n", procName);
        L_INFO("wc = %d, hc = %d\n", procName, wc, hc);
    }
    if (wc == 0 && hc == 0)   /* no-op */
 *      (3) Returns a copy if both wc and hc are 0
 *      (4) Require that w > 2 * wc + 1 and h > 2 * hc + 1,
 *          where (w,h) are the dimensions of pixs.
    if (wc < 0) wc = 0;
    if (hc < 0) hc = 0;
        wc = L_MAX(0, L_MIN(wc, (w - 3) / 2));
        hc = L_MAX(0, L_MIN(hc, (h - 3) / 2));
        L_WARNING("kernel too large; reducing!\n", procName);
        L_INFO("wc = %d, hc = %d\n", procName, wc, hc);
    }
    if (wc == 0 && hc == 0)
Fixed code:  * \param[in]    pix      8 or 32 bpp; or 2, 4 or 8 bpp with colormap
 *      (2) Returns a copy if either wc or hc are 0
 *          where (w,h) are the dimensions of pixs.  Otherwise,
 *          return a copy.
    if (wc <= 0 || hc <= 0)
        return pixCopy(NULL, pix);
        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
                procName, w, wc, h, hc);
        return pixCopy(NULL, pix);  /* no-op */
 *      (3) Returns a copy if either wc or hc are 0
 *          where (w,h) are the dimensions of pixs.  Otherwise,
 *          return a copy.
    if (wc <= 0 || hc <= 0)   /* no-op */
        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
                procName, w, wc, h, hc);
 *      (3) Returns a copy if either wc or hc are 0.
    if (wc <= 0 || hc <= 0)  /* no-op */
        return pixCopy(NULL, pixs);
        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
                procName, w, wc, h, hc);
    }
 *      (2) Returns a copy if either wc or hc are 0.
 *          where (w,h) are the dimensions of pixs.  Otherwise,
 *          return a copy.
    if (wc <= 0 || hc <= 0)   /* no-op */
    pixGetDimensions(pix, &w, &h, &d);
    if (w < 2 * wc + 3 || h < 2 * hc + 3) {
        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
                procName, w, wc, h, hc);
        return pixCopy(NULL, pix);
    }
 *      (3) Returns a copy if either wc or hc are 0.
 *      (4) Require that w > 2 * wc + 3 and h > 2 * hc + 3,
 *          where (w,h) are the dimensions of pixs.  Otherwise,
 *          return a copy.
    if (wc <= 0 || hc <= 0)  /* no-op */
        return pixCopy(NULL, pixs);
        L_ERROR("kernel is too large: w = %d, wc = %d, h = %d, hc = %d\n",
                procName, w, wc, h, hc);
    }