Merge tag 'android-security-13.0.0_r8' into int/13/fp3
Android Security 13.0.0 Release 8 (10286630)
* tag 'android-security-13.0.0_r8':
Enforce program stack limits on function parameters.
Change-Id: Id7310d4be98f3716ec9e06073e505705aa016681
diff --git a/gm/alpha_image.cpp b/gm/alpha_image.cpp
index 95e23f8..6edafe1 100644
--- a/gm/alpha_image.cpp
+++ b/gm/alpha_image.cpp
@@ -17,6 +17,7 @@
#include "include/core/SkPaint.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkShader.h"
+#include "tools/Resources.h"
static SkBitmap make_alpha_image(int w, int h) {
SkBitmap bm;
@@ -84,3 +85,41 @@
paint.setShader(image->makeShader(SkSamplingOptions()));
canvas->drawRect({ 0, 0, 64, 64 }, paint);
}
+
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+// For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was
+// inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior
+// for some Android apps (b/231400686). This GM verifies that our Android framework workaround
+// produces the old result (mandrill with a round-rect border).
+DEF_SIMPLE_GM(alpha_bitmap_is_coverage_ANDROID, canvas, 128, 128) {
+ SkBitmap maskBitmap;
+ maskBitmap.allocPixels(SkImageInfo::MakeA8(128, 128));
+ {
+ SkCanvas maskCanvas(maskBitmap);
+ maskCanvas.clear(SK_ColorWHITE);
+
+ SkPaint maskPaint;
+ maskPaint.setAntiAlias(true);
+ maskPaint.setColor(SK_ColorWHITE);
+ maskPaint.setBlendMode(SkBlendMode::kClear);
+ maskCanvas.drawRoundRect({0, 0, 128, 128}, 16, 16, maskPaint);
+ }
+
+ SkBitmap offscreenBitmap;
+ offscreenBitmap.allocN32Pixels(128, 128);
+ {
+ SkCanvas offscreenCanvas(offscreenBitmap);
+ offscreenCanvas.drawImage(GetResourceAsImage("images/mandrill_128.png"), 0, 0);
+
+ SkPaint clearPaint;
+ clearPaint.setAntiAlias(true);
+ clearPaint.setBlendMode(SkBlendMode::kClear);
+ // At tip-of-tree (or at any time on the GPU backend), this draw produces full coverage,
+ // completely erasing the mandrill. With the workaround enabled, the alpha border is treated
+ // as coverage, so we only apply kClear to those pixels, just erasing the outer border.
+ offscreenCanvas.drawImage(maskBitmap.asImage(), 0, 0, SkSamplingOptions{}, &clearPaint);
+ }
+
+ canvas->drawImage(offscreenBitmap.asImage(), 0, 0);
+}
+#endif
diff --git a/gm/runtimeshader.cpp b/gm/runtimeshader.cpp
index 94b3e0d..bd865ce 100644
--- a/gm/runtimeshader.cpp
+++ b/gm/runtimeshader.cpp
@@ -889,3 +889,50 @@
// Now draw the offscreen surface back to our original canvas:
canvas->drawImage(surface->makeImageSnapshot(), 0, 0);
}
+
+// skbug.com/13598 GPU was double applying the local matrix.
+DEF_SIMPLE_GM(local_matrix_shader_rt, canvas, 256, 256) {
+ SkString passthrough(R"(
+ uniform shader s;
+ half4 main(float2 p) { return s.eval(p); }
+ )");
+ auto [rte, error] = SkRuntimeEffect::MakeForShader(passthrough, {});
+ if (!rte) {
+ SkDebugf("%s\n", error.c_str());
+ return;
+ }
+
+ auto image = GetResourceAsImage("images/mandrill_128.png");
+ auto imgShader = image->makeShader(SkSamplingOptions{});
+
+ auto r = SkRect::MakeWH(image->width(), image->height());
+
+ auto lm = SkMatrix::RotateDeg(90.f, {image->width()/2.f, image->height()/2.f});
+
+ SkPaint paint;
+
+ // image
+ paint.setShader(imgShader);
+ canvas->drawRect(r, paint);
+
+ // passthrough(image)
+ canvas->save();
+ canvas->translate(image->width(), 0);
+ paint.setShader(rte->makeShader(nullptr, &imgShader, 1));
+ canvas->drawRect(r, paint);
+ canvas->restore();
+
+ // localmatrix(image)
+ canvas->save();
+ canvas->translate(0, image->height());
+ paint.setShader(imgShader->makeWithLocalMatrix(lm));
+ canvas->drawRect(r, paint);
+ canvas->restore();
+
+ // localmatrix(passthrough(image)) This was the bug.
+ canvas->save();
+ canvas->translate(image->width(), image->height());
+ paint.setShader(rte->makeShader(nullptr, &imgShader, 1)->makeWithLocalMatrix(lm));
+ canvas->drawRect(r, paint);
+ canvas->restore();
+}
diff --git a/include/config/SkUserConfigManual.h b/include/config/SkUserConfigManual.h
index 5f11d96..528e406 100644
--- a/include/config/SkUserConfigManual.h
+++ b/include/config/SkUserConfigManual.h
@@ -32,4 +32,8 @@
#define SK_DISABLE_DAA // skbug.com/6886
#define SK_ABORT(...) __android_log_assert(nullptr, "skia", ##__VA_ARGS__)
+
+ // TODO (b/239048372): Remove this flag when we can safely migrate apps to the
+ // new behavior.
+ #define SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE
#endif // SkUserConfigManual_DEFINED
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index f68f8ee..b910d06 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -273,6 +273,22 @@
/////////////////////// these are not virtual, just helpers
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) {
+ if (clip.quickReject(mask.fBounds)) {
+ return;
+ }
+
+ SkRegion::Cliperator clipper(clip, mask.fBounds);
+
+ while (!clipper.done()) {
+ const SkIRect& cr = clipper.rect();
+ this->blitMask(mask, cr);
+ clipper.next();
+ }
+}
+#endif
+
void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) {
SkRegion::Cliperator clipper(clip, rect);
diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h
index 0ed7cd6..ec58c5a 100644
--- a/src/core/SkBlitter.h
+++ b/src/core/SkBlitter.h
@@ -132,6 +132,9 @@
}
///@name non-virtual helpers
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+ void blitMaskRegion(const SkMask& mask, const SkRegion& clip);
+#endif
void blitRectRegion(const SkIRect& rect, const SkRegion& clip);
void blitRegion(const SkRegion& clip);
///@}
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 60d18e9..30cbaf2 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -705,6 +705,39 @@
}
}
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
+ if (srcM.fBounds.isEmpty()) {
+ return;
+ }
+
+ const SkMask* mask = &srcM;
+
+ SkMask dstM;
+ if (paint.getMaskFilter() &&
+ as_MFB(paint.getMaskFilter())
+ ->filterMask(&dstM, srcM, fMatrixProvider->localToDevice(), nullptr)) {
+ mask = &dstM;
+ }
+ SkAutoMaskFreeImage ami(dstM.fImage);
+
+ SkAutoBlitterChoose blitterChooser(*this, nullptr, paint);
+ SkBlitter* blitter = blitterChooser.get();
+
+ SkAAClipBlitterWrapper wrapper;
+ const SkRegion* clipRgn;
+
+ if (fRC->isBW()) {
+ clipRgn = &fRC->bwRgn();
+ } else {
+ wrapper.init(*fRC, blitter);
+ clipRgn = &wrapper.getRgn();
+ blitter = wrapper.getBlitter();
+ }
+ blitter->blitMaskRegion(*mask, *clipRgn);
+}
+#endif
+
static SkScalar fast_len(const SkVector& vec) {
SkScalar x = SkScalarAbs(vec.fX);
SkScalar y = SkScalarAbs(vec.fY);
@@ -925,6 +958,94 @@
this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
}
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkSamplingOptions& sampling,
+ const SkPaint& paint) const {
+ SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
+
+ // nothing to draw
+ if (fRC->isEmpty()) {
+ return;
+ }
+
+ SkMatrix ctm = fMatrixProvider->localToDevice();
+ if (SkTreatAsSprite(ctm, bitmap.dimensions(), sampling, paint))
+ {
+ int ix = SkScalarRoundToInt(ctm.getTranslateX());
+ int iy = SkScalarRoundToInt(ctm.getTranslateY());
+
+ SkPixmap pmap;
+ if (!bitmap.peekPixels(&pmap)) {
+ return;
+ }
+ SkMask mask;
+ mask.fBounds.setXYWH(ix, iy, pmap.width(), pmap.height());
+ mask.fFormat = SkMask::kA8_Format;
+ mask.fRowBytes = SkToU32(pmap.rowBytes());
+ // fImage is typed as writable, but in this case it is used read-only
+ mask.fImage = (uint8_t*)pmap.addr8(0, 0);
+
+ this->drawDevMask(mask, paint);
+ } else { // need to xform the bitmap first
+ SkRect r;
+ SkMask mask;
+
+ r.setIWH(bitmap.width(), bitmap.height());
+ ctm.mapRect(&r);
+ r.round(&mask.fBounds);
+
+ // set the mask's bounds to the transformed bitmap-bounds,
+ // clipped to the actual device and further limited by the clip bounds
+ {
+ SkASSERT(fDst.bounds().contains(fRC->getBounds()));
+ SkIRect devBounds = fDst.bounds();
+ devBounds.intersect(fRC->getBounds().makeOutset(1, 1));
+ // need intersect(l, t, r, b) on irect
+ if (!mask.fBounds.intersect(devBounds)) {
+ return;
+ }
+ }
+
+ mask.fFormat = SkMask::kA8_Format;
+ mask.fRowBytes = SkAlign4(mask.fBounds.width());
+ size_t size = mask.computeImageSize();
+ if (0 == size) {
+ // the mask is too big to allocated, draw nothing
+ return;
+ }
+
+ // allocate (and clear) our temp buffer to hold the transformed bitmap
+ SkAutoTMalloc<uint8_t> storage(size);
+ mask.fImage = storage.get();
+ memset(mask.fImage, 0, size);
+
+ // now draw our bitmap(src) into mask(dst), transformed by the matrix
+ {
+ SkBitmap device;
+ device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
+ mask.fImage, mask.fRowBytes);
+
+ SkCanvas c(device);
+ // need the unclipped top/left for the translate
+ c.translate(-SkIntToScalar(mask.fBounds.fLeft),
+ -SkIntToScalar(mask.fBounds.fTop));
+ c.concat(ctm);
+
+ // We can't call drawBitmap, or we'll infinitely recurse. Instead
+ // we manually build a shader and draw that into our new mask
+ SkPaint tmpPaint;
+ tmpPaint.setAntiAlias(paint.isAntiAlias());
+ tmpPaint.setDither(paint.isDither());
+ SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap, sampling);
+ SkRect rr;
+ rr.setIWH(bitmap.width(), bitmap.height());
+ c.drawRect(rr, paintWithShader);
+ }
+ this->drawDevMask(mask, paint);
+ }
+}
+#endif
+
static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
const SkRect& srcR) {
SkRect dstR;
@@ -998,6 +1119,16 @@
SkDraw draw(*this);
draw.fMatrixProvider = &matrixProvider;
+ // For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was
+ // inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior
+ // for some Android apps (b/231400686). Thus: keep the old behavior in the framework.
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+ if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
+ draw.drawBitmapAsMask(bitmap, sampling, *paint);
+ return;
+ }
+#endif
+
SkPaint paintWithShader = make_paint_with_image(*paint, bitmap, sampling);
const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
if (dstBounds) {
diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h
index dc5fe8e..c87948c 100644
--- a/src/core/SkDraw.h
+++ b/src/core/SkDraw.h
@@ -98,6 +98,10 @@
SkMask* mask, SkMask::CreateMode mode,
SkStrokeRec::InitStyle style);
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+ void drawDevMask(const SkMask& mask, const SkPaint&) const;
+#endif
+
enum RectType {
kHair_RectType,
kFill_RectType,
@@ -117,6 +121,9 @@
SkPoint* strokeSize);
private:
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+ void drawBitmapAsMask(const SkBitmap&, const SkSamplingOptions&, const SkPaint&) const;
+#endif
void drawFixedVertices(const SkVertices* vertices,
sk_sp<SkBlender> blender,
const SkPaint& paint,
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 3e5543c..1c08fb8 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -1133,13 +1133,16 @@
get_xformed_uniforms(fEffect.get(), fUniforms, args.fDstColorInfo->colorSpace());
SkASSERT(uniforms);
+ // We handle the pre-local matrix at this level so strip it out.
+ GrFPArgs fpArgs = args;
+ fpArgs.fPreLocalMatrix = nullptr;
auto [success, fp] = make_effect_fp(fEffect,
"runtime_shader",
std::move(uniforms),
/*inputFP=*/nullptr,
/*destColorFP=*/nullptr,
SkMakeSpan(fChildren),
- args);
+ fpArgs);
if (!success) {
return nullptr;
}
diff --git a/src/gpu/v1/Device.cpp b/src/gpu/v1/Device.cpp
index a0a4728..bc25933 100644
--- a/src/gpu/v1/Device.cpp
+++ b/src/gpu/v1/Device.cpp
@@ -409,15 +409,6 @@
GrPrimitiveType primitiveType = point_mode_to_primitive_type(mode);
const SkMatrixProvider* matrixProvider = this;
-#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
- SkTLazy<SkPostTranslateMatrixProvider> postTranslateMatrixProvider;
- // This offsetting in device space matches the expectations of the Android framework for non-AA
- // points and lines.
- if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) {
- static const SkScalar kOffset = 0.063f; // Just greater than 1/16.
- matrixProvider = postTranslateMatrixProvider.init(*matrixProvider, kOffset, kOffset);
- }
-#endif
GrPaint grPaint;
if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint,
diff --git a/src/gpu/v1/SurfaceDrawContext.cpp b/src/gpu/v1/SurfaceDrawContext.cpp
index 5bf6a3b..cfd6a04 100644
--- a/src/gpu/v1/SurfaceDrawContext.cpp
+++ b/src/gpu/v1/SurfaceDrawContext.cpp
@@ -1470,6 +1470,10 @@
void SurfaceDrawContext::drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable,
const SkRect& bounds) {
+ ASSERT_SINGLE_OWNER
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(this->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawDrawable", fContext);
GrOp::Owner op(DrawableOp::Make(fContext, std::move(drawable), bounds));
SkASSERT(op);
this->addOp(std::move(op));