Skip to content

Commit a65a5b8

Browse files
committed
Time channel attack on SVG Filters
https://bugs.webkit.org/show_bug.cgi?id=118689 Reviewed by Simon Fraser. Source/WebCore: The time channel attack can happen if the attacker applies FEColorMatrix or FEConvolveMatrix and provides a matrix which is filled with subnormal floating point values. Performing floating-point operations on subnormals is very expensive unless the pixel in the source graphics is black (or zero). By measuring the time a filter takes to be applied, the attacker can know whether the pixel he wants to steal from an iframe is black or white. By repeating the same process on all the pixels in the iframe, the attacker can reconstruct the whole page of the iframe. To fix this issue, the values in the matrices of these filters will clamped to FLT_MIN. We do not want to consume too much time calculating filtered pixels because of such tiny values. The difference between applying FLT_MIN and applying a subnormal should not be even noticeable. Normalizing the floating-point matrices should happen only at the beginning of the filter platformApplySoftware(). * platform/graphics/filters/FEColorMatrix.cpp: (WebCore::FEColorMatrix::platformApplySoftware): * platform/graphics/filters/FEConvolveMatrix.cpp: (WebCore::FEConvolveMatrix::fastSetInteriorPixels): (WebCore::FEConvolveMatrix::fastSetOuterPixels): (WebCore::FEConvolveMatrix::platformApplySoftware): * platform/graphics/filters/FEConvolveMatrix.h: * platform/graphics/filters/FilterEffect.h: (WebCore::FilterEffect::normalizedFloats): Source/WTF: Performing arithmetic operations on subnormal floating-point numbers is very expensive. Normalizing the floating-point number to the minimum normal value should accelerate the calculations and there won't be a noticeable difference in the result since all the subnormal values and the minimum normal value are all very close to zero. * wtf/MathExtras.h: (normalizedFloat): Canonical link: https://commits.webkit.org/186785@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@214125 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 9b4ddbc commit a65a5b8

File tree

7 files changed

+84
-14
lines changed

7 files changed

+84
-14
lines changed

Source/WTF/ChangeLog

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
2017-03-17 Said Abou-Hallawa <sabouhallawa@apple.com>
2+
3+
Time channel attack on SVG Filters
4+
https://bugs.webkit.org/show_bug.cgi?id=118689
5+
6+
Reviewed by Simon Fraser.
7+
8+
Performing arithmetic operations on subnormal floating-point numbers is
9+
very expensive. Normalizing the floating-point number to the minimum normal
10+
value should accelerate the calculations and there won't be a noticeable
11+
difference in the result since all the subnormal values and the minimum
12+
normal value are all very close to zero.
13+
14+
* wtf/MathExtras.h:
15+
(normalizedFloat):
16+
117
2017-03-11 Filip Pizlo <fpizlo@apple.com>
218

319
Air should be powerful enough to support Tmp-splitting

Source/WTF/wtf/MathExtras.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,15 @@ inline bool isWithinIntRange(float x)
209209
return x > static_cast<float>(std::numeric_limits<int>::min()) && x < static_cast<float>(std::numeric_limits<int>::max());
210210
}
211211

212+
inline float normalizedFloat(float value)
213+
{
214+
if (value > 0 && value < std::numeric_limits<float>::min())
215+
return std::numeric_limits<float>::min();
216+
if (value < 0 && value > -std::numeric_limits<float>::min())
217+
return -std::numeric_limits<float>::min();
218+
return value;
219+
}
220+
212221
template<typename T> inline bool hasOneBitSet(T value)
213222
{
214223
return !((value - 1) & value) && value;

Source/WebCore/ChangeLog

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,36 @@
1+
2017-03-17 Said Abou-Hallawa <sabouhallawa@apple.com>
2+
3+
Time channel attack on SVG Filters
4+
https://bugs.webkit.org/show_bug.cgi?id=118689
5+
6+
Reviewed by Simon Fraser.
7+
8+
The time channel attack can happen if the attacker applies FEColorMatrix
9+
or FEConvolveMatrix and provides a matrix which is filled with subnormal
10+
floating point values. Performing floating-point operations on subnormals
11+
is very expensive unless the pixel in the source graphics is black (or
12+
zero). By measuring the time a filter takes to be applied, the attacker
13+
can know whether the pixel he wants to steal from an iframe is black or
14+
white. By repeating the same process on all the pixels in the iframe, the
15+
attacker can reconstruct the whole page of the iframe.
16+
17+
To fix this issue, the values in the matrices of these filters will clamped
18+
to FLT_MIN. We do not want to consume too much time calculating filtered
19+
pixels because of such tiny values. The difference between applying FLT_MIN
20+
and applying a subnormal should not be even noticeable. Normalizing the
21+
floating-point matrices should happen only at the beginning of the filter
22+
platformApplySoftware().
23+
24+
* platform/graphics/filters/FEColorMatrix.cpp:
25+
(WebCore::FEColorMatrix::platformApplySoftware):
26+
* platform/graphics/filters/FEConvolveMatrix.cpp:
27+
(WebCore::FEConvolveMatrix::fastSetInteriorPixels):
28+
(WebCore::FEConvolveMatrix::fastSetOuterPixels):
29+
(WebCore::FEConvolveMatrix::platformApplySoftware):
30+
* platform/graphics/filters/FEConvolveMatrix.h:
31+
* platform/graphics/filters/FilterEffect.h:
32+
(WebCore::FilterEffect::normalizedFloats):
33+
134
2017-03-17 Jer Noble <jer.noble@apple.com>
235

336
Explicitly resize the audio buffer in RealtimeOutgoingAudioSource.

Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,21 +153,22 @@ void FEColorMatrix::platformApplySoftware()
153153

154154
IntRect imageRect(IntPoint(), resultImage->logicalSize());
155155
RefPtr<Uint8ClampedArray> pixelArray = resultImage->getUnmultipliedImageData(imageRect);
156+
Vector<float> values = normalizedFloats(m_values);
156157

157158
switch (m_type) {
158159
case FECOLORMATRIX_TYPE_UNKNOWN:
159160
break;
160161
case FECOLORMATRIX_TYPE_MATRIX:
161-
effectType<FECOLORMATRIX_TYPE_MATRIX>(pixelArray.get(), m_values);
162+
effectType<FECOLORMATRIX_TYPE_MATRIX>(pixelArray.get(), values);
162163
break;
163164
case FECOLORMATRIX_TYPE_SATURATE:
164-
effectType<FECOLORMATRIX_TYPE_SATURATE>(pixelArray.get(), m_values);
165+
effectType<FECOLORMATRIX_TYPE_SATURATE>(pixelArray.get(), values);
165166
break;
166167
case FECOLORMATRIX_TYPE_HUEROTATE:
167-
effectType<FECOLORMATRIX_TYPE_HUEROTATE>(pixelArray.get(), m_values);
168+
effectType<FECOLORMATRIX_TYPE_HUEROTATE>(pixelArray.get(), values);
168169
break;
169170
case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
170-
effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(pixelArray.get(), m_values);
171+
effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(pixelArray.get(), values);
171172
setIsAlphaImage(true);
172173
break;
173174
}

Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ ALWAYS_INLINE void FEConvolveMatrix::fastSetInteriorPixels(PaintingData& paintin
267267

268268
for (int y = yEnd + 1; y > yStart; --y) {
269269
for (int x = clipRight + 1; x > 0; --x) {
270-
int kernelValue = m_kernelMatrix.size() - 1;
270+
int kernelValue = paintingData.kernelMatrix.size() - 1;
271271
int kernelPixel = startKernelPixel;
272272
int width = m_kernelSize.width();
273273

@@ -278,11 +278,11 @@ ALWAYS_INLINE void FEConvolveMatrix::fastSetInteriorPixels(PaintingData& paintin
278278
totals[3] = 0;
279279

280280
while (kernelValue >= 0) {
281-
totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
282-
totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
283-
totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
281+
totals[0] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
282+
totals[1] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
283+
totals[2] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++));
284284
if (!preserveAlphaValues)
285-
totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel));
285+
totals[3] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel));
286286
++kernelPixel;
287287
--kernelValue;
288288
if (!--width) {
@@ -347,7 +347,7 @@ void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, in
347347

348348
for (int y = height; y > 0; --y) {
349349
for (int x = width; x > 0; --x) {
350-
int kernelValue = m_kernelMatrix.size() - 1;
350+
int kernelValue = paintingData.kernelMatrix.size() - 1;
351351
int kernelPixelX = startKernelPixelX;
352352
int kernelPixelY = startKernelPixelY;
353353
int width = m_kernelSize.width();
@@ -361,12 +361,12 @@ void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, in
361361
while (kernelValue >= 0) {
362362
int pixelIndex = getPixelValue(paintingData, kernelPixelX, kernelPixelY);
363363
if (pixelIndex >= 0) {
364-
totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex));
365-
totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 1));
366-
totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 2));
364+
totals[0] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex));
365+
totals[1] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 1));
366+
totals[2] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 2));
367367
}
368368
if (!preserveAlphaValues && pixelIndex >= 0)
369-
totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 3));
369+
totals[3] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 3));
370370
++kernelPixelX;
371371
--kernelValue;
372372
if (!--width) {
@@ -436,6 +436,7 @@ void FEConvolveMatrix::platformApplySoftware()
436436
paintingData.width = paintSize.width();
437437
paintingData.height = paintSize.height();
438438
paintingData.bias = m_bias * 255;
439+
paintingData.kernelMatrix = normalizedFloats(m_kernelMatrix);
439440

440441
// Drawing fully covered pixels
441442
int clipRight = paintSize.width() - m_kernelSize.width();

Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class FEConvolveMatrix : public FilterEffect {
8383
int width;
8484
int height;
8585
float bias;
86+
Vector<float> kernelMatrix;
8687
};
8788

8889
FEConvolveMatrix(Filter&, const IntSize&, float, float,

Source/WebCore/platform/graphics/filters/FilterEffect.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "FloatRect.h"
2727
#include "IntRect.h"
2828
#include <runtime/Uint8ClampedArray.h>
29+
#include <wtf/MathExtras.h>
2930
#include <wtf/RefCounted.h>
3031
#include <wtf/RefPtr.h>
3132
#include <wtf/Vector.h>
@@ -170,6 +171,14 @@ class FilterEffect : public RefCounted<FilterEffect> {
170171
void forceValidPreMultipliedPixels();
171172

172173
void clipAbsolutePaintRect();
174+
175+
static Vector<float> normalizedFloats(const Vector<float>& values)
176+
{
177+
Vector<float> normalizedValues(values.size());
178+
for (size_t i = 0; i < values.size(); ++i)
179+
normalizedValues[i] = normalizedFloat(values[i]);
180+
return normalizedValues;
181+
}
173182

174183
private:
175184
std::unique_ptr<ImageBuffer> m_imageBufferResult;

0 commit comments

Comments
 (0)