This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch fix-release-build
in repository efl.

View the commit online.

commit 6b7b4d8f4995514818ba21c503b8fc98d816e08c
Author: Cedric BAIL <[email protected]>
AuthorDate: Sun Aug 2 19:37:38 2026 -0600

    evas: fix out of bounds read in the gaussian blur line ramps
    
    Both ramps walk a window that is wider than the line whenever
    len < 2 * radius + 1, and neither bounded the read.
    
    The left ramp takes j up to k + radius taps, with j doubling as the
    source position, so for k near left it reads past the end of the line.
    The right ramp is worse: it always takes 2 * radius - k taps starting
    from where the middle section left off, and when the middle did not run
    at all - which is exactly the len < 2 * radius + 1 case - it starts from
    the beginning of the line and runs off the end of it.
    
    Bound both by the source position. For a line at least one full window
    long the new conditions are never false, so nothing changes for
    well-formed input; the weights that are skipped are also left out of the
    per-output divider, which is what the ramps already do at the other edge,
    so short lines now normalise correctly instead of averaging in whatever
    followed them in memory.
    
    Found by a differential test that could not compare these shapes at all:
    both implementations read the same out of bounds addresses, but the first
    call's writes perturbed what the second one found there.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 .../filters/blur/blur_gaussian_alpha_.c            | 16 +++++--
 .../filters/blur/blur_gaussian_rgba_.c             | 19 ++++++--
 .../software_generic/filters/blur/blur_weights_.c  | 55 ++++++++++++++++++++++
 3 files changed, 84 insertions(+), 6 deletions(-)

diff --git a/src/modules/evas/engines/software_generic/filters/blur/blur_gaussian_alpha_.c b/src/modules/evas/engines/software_generic/filters/blur/blur_gaussian_alpha_.c
index 5a72fa5f77..ecee0a4b14 100644
--- a/src/modules/evas/engines/software_generic/filters/blur/blur_gaussian_alpha_.c
+++ b/src/modules/evas/engines/software_generic/filters/blur/blur_gaussian_alpha_.c
@@ -21,6 +21,9 @@ FUNCTION_NAME(const DATA8* restrict srcdata, DATA8* restrict dstdata,
    const int diameter = 2 * radius + 1;
    const int left = MIN(radius, len);
    const int right = MIN(radius, (len - radius));
+   const int mid = len - (2 * radius);
+   /* where the right ramp starts reading, i.e. how far the middle advanced */
+   const int roff = (mid > 0) ? mid : 0;
    const DATA8* restrict s;
    const DATA8* restrict src;
    DATA8* restrict dst;
@@ -36,7 +39,10 @@ FUNCTION_NAME(const DATA8* restrict srcdata, DATA8* restrict dstdata,
              acc = 0;
              divider = 0;
              s = src;
-             for (j = 0; j <= k + radius; j++, s += STEP)
+             /* j is also the source position here, so the second condition is
+              * what keeps a window wider than the line from running off the
+              * end of it. It can only bite when len < 2 * radius + 1. */
+             for (j = 0; (j <= k + radius) && (j < len); j++, s += STEP)
                {
                   acc += (*s) * weights[j + radius - k];
                   divider += weights[j + radius - k];
@@ -46,7 +52,8 @@ FUNCTION_NAME(const DATA8* restrict srcdata, DATA8* restrict dstdata,
           }
 
         // middle
-        for (k = radius; k < (len - radius); k++, src += STEP, dst += STEP)
+        k = radius;
+        for (; k < (len - radius); k++, src += STEP, dst += STEP)
           {
              acc = 0;
              s = src;
@@ -61,7 +68,10 @@ FUNCTION_NAME(const DATA8* restrict srcdata, DATA8* restrict dstdata,
              acc = 0;
              divider = 0;
              s = src;
-             for (j = 0; j < 2 * radius - k; j++, s += STEP)
+             /* src sits at roff + k, so roff + k + j is the source position;
+              * as in the left ramp this only clamps when the window is wider
+              * than the line */
+             for (j = 0; (j < 2 * radius - k) && ((roff + k + j) < len); j++, s += STEP)
                {
                   acc += (*s) * weights[j];
                   divider += weights[j];
diff --git a/src/modules/evas/engines/software_generic/filters/blur/blur_gaussian_rgba_.c b/src/modules/evas/engines/software_generic/filters/blur/blur_gaussian_rgba_.c
index 3a862fea8b..77a328f034 100644
--- a/src/modules/evas/engines/software_generic/filters/blur/blur_gaussian_rgba_.c
+++ b/src/modules/evas/engines/software_generic/filters/blur/blur_gaussian_rgba_.c
@@ -19,6 +19,9 @@ FUNCTION_NAME(const DATA32* restrict srcdata, DATA32* restrict dstdata,
    const int diameter = 2 * radius + 1;
    const int left = MIN(radius, len);
    const int right = MIN(radius, (len - radius));
+   const int mid = len - (2 * radius);
+   /* where the right ramp starts reading, i.e. how far the middle advanced */
+   const int roff = (mid > 0) ? mid : 0;
    const DATA32* restrict src;
    DATA32* restrict dst;
    int i, j, k;
@@ -34,7 +37,11 @@ FUNCTION_NAME(const DATA32* restrict srcdata, DATA32* restrict dstdata,
              int acc[4] = {0};
              int divider = 0;
              const DATA32* restrict s = src;
-             for (j = 0; j <= k + radius; j++, s += STEP)
+
+             /* j is also the source position here, so the second condition is
+              * what keeps a window wider than the line from running off the
+              * end of it. It can only bite when len < 2 * radius + 1. */
+             for (j = 0; (j <= k + radius) && (j < len); j++, s += STEP)
                {
                   const int weightidx = j + radius - k;
                   acc[ALPHA] += A_VAL(s) * weights[weightidx];
@@ -51,10 +58,12 @@ FUNCTION_NAME(const DATA32* restrict srcdata, DATA32* restrict dstdata,
           }
 
         // middle
-        for (k = len - (2 * radius); k > 0; k--, src += STEP, dst += STEP)
+        k = mid;
+        for (; k > 0; k--, src += STEP, dst += STEP)
           {
              int acc[4] = {0};
              const DATA32* restrict s = src;
+
              for (j = 0; j < diameter; j++, s += STEP)
                {
                   acc[ALPHA] += A_VAL(s) * weights[j];
@@ -74,7 +83,11 @@ FUNCTION_NAME(const DATA32* restrict srcdata, DATA32* restrict dstdata,
              int acc[4] = {0};
              int divider = 0;
              const DATA32* restrict s = src;
-             for (j = 0; j < 2 * radius - k; j++, s += STEP)
+
+             /* src sits at roff + k, so roff + k + j is the source position;
+              * as in the left ramp this only clamps when the window is wider
+              * than the line */
+             for (j = 0; (j < 2 * radius - k) && ((roff + k + j) < len); j++, s += STEP)
                {
                   acc[ALPHA] += A_VAL(s) * weights[j];
                   acc[RED]   += R_VAL(s) * weights[j];
diff --git a/src/modules/evas/engines/software_generic/filters/blur/blur_weights_.c b/src/modules/evas/engines/software_generic/filters/blur/blur_weights_.c
new file mode 100644
index 0000000000..4bf0641bbf
--- /dev/null
+++ b/src/modules/evas/engines/software_generic/filters/blur/blur_weights_.c
@@ -0,0 +1,55 @@
+/* @file blur_weights_.c
+ * The weight curve shared by the gaussian blur and by its differential test.
+ *
+ * It lives in its own file so the test cannot drift from the curve the filter
+ * actually uses: testing a NEON kernel against a C kernel is only meaningful
+ * if both are fed the weights production will feed them.
+ *
+ * Base curve: f(x) = sin(x + pi/2) / 2 + 1/2, normalised so that
+ * sum(weights) == 1 << *pow2_divider, which is what lets the middle of the
+ * blur divide with a shift instead of a division.
+ */
+
+#ifndef EVAS_BLUR_WEIGHTS_H
+#define EVAS_BLUR_WEIGHTS_H
+
+#include <math.h>
+
+static inline void
+evas_blur_weights_get(int *weights, int *pow2_divider, int radius)
+{
+   const int diameter = 2 * radius + 1;
+   double x, divider, sum = 0.0;
+   double dweights[diameter];
+   int k, nextpow2, isum = 0;
+   const int FAKE_PI = 3.0;
+
+   /* Base curve:
+    * f(x) = sin(x+pi/2)/2+1/2
+    */
+
+   for (k = 0; k < diameter; k++)
+     {
+        x = ((double) k / (double) (diameter - 1)) * FAKE_PI * 2.0 - FAKE_PI;
+        dweights[k] = ((sin(x + M_PI_2) + 1.0) / 2.0) * 1024.0;
+        sum += dweights[k];
+     }
+
+   // Now we need to normalize to have a 2^N divider.
+   nextpow2 = log2(2 * sum);
+   divider = (double) (1 << nextpow2);
+
+   for (k = 0; k < diameter; k++)
+     {
+        weights[k] = round(dweights[k] * divider / sum);
+        isum += weights[k];
+     }
+
+   // Final correction. The difference SHOULD be small...
+   weights[radius] += (int) divider - isum;
+
+   if (pow2_divider)
+     *pow2_divider = nextpow2;
+}
+
+#endif

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to