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.