Android: fix Bitmap memory leaks in CameraCaptureManager.snap#41902
Android: fix Bitmap memory leaks in CameraCaptureManager.snap#41902obviyus merged 2 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes two native Bitmap memory leaks in
The implementation is correct:
Confidence Score: 5/5
Last reviewed commit: f4cc719 |
f4cc719 to
a232193
Compare
a232193 to
4c002a1
Compare
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 Unbounded memory allocation during JPEG compression retries (camera.snap) can crash app
DescriptionThe
Vulnerable code: val out = ByteArrayOutputStream()
if (!bitmap.compress(Bitmap.CompressFormat.JPEG, q, out)) {
...
}
out.toByteArray()RecommendationPrevent unbounded buffering during encode attempts. Option A (recommended): use a size-limiting OutputStream so compression aborts once the limit is exceeded, avoiding huge buffers and the Example: class TooLargeException: RuntimeException()
class MaxSizeOutputStream(
private val delegate: java.io.OutputStream,
private val maxBytes: Int,
): java.io.OutputStream() {
private var count = 0
override fun write(b: Int) {
if (count + 1 > maxBytes) throw TooLargeException()
delegate.write(b)
count += 1
}
override fun write(b: ByteArray, off: Int, len: Int) {
if (count + len > maxBytes) throw TooLargeException()
delegate.write(b, off, len)
count += len
}
}
// inside encode:
val out = ByteArrayOutputStream()
val limited = MaxSizeOutputStream(out, maxBytes + 1)
val ok = try {
bitmap.compress(Bitmap.CompressFormat.JPEG, q, limited)
} catch (_: TooLargeException) {
true // treat as "encoded but too large" and let the limiter reduce quality/scale
}
if (!ok) error("UNAVAILABLE: failed to encode JPEG")
return out.toByteArray()Then adapt Option B: enforce a hard upper bound on Also consider moving encoding off the main thread to reduce ANR risk. Analyzed PR: #41902 at commit Last updated on: 2026-03-22T03:38:32Z |
Summary
CameraCaptureManager.snap()leaks native Bitmap memory on every camera snap call. After capturing, rotating, and scaling the image, the intermediaterotatedand finalscaledBitmaps are never recycled:rotated.scale()creates a new Bitmap but the originalrotatedis leaked.JpegSizeLimiter.compressToLimitreturns, thescaledBitmap is never recycled.Fix
rotatedimmediately afterscale()produces a different Bitmap.try/finallyto ensurescaled.recycle()is always called.The
encodelambda insidecompressToLimitalready handles its own intermediate Bitmaps correctly (recycling whenbitmap !== scaled); this fix only addresses the outer scope leaks.Same pattern as #41888 (PhotosHandler) and #41889 (CanvasController).
Test plan
snap()now has a matchingrecycle().rotateBitmapByExifalready recycles the input when creating a new rotated Bitmap, so no double-recycle.encodelambda's identity check (bitmap !== scaled) remains correct after the fix.Made with Cursor