CVE-2026-27280 AI-generated analysis
platform/external/dng_sdk (Adobe DNG SDK), pixel buffer reordering and DNG render pipeline
Updates external/dng_sdk from DNG SDK 1.7.1 build 2471 to build 2502. In OptimizeOrder (dng_pixel_buffer.cpp), unchecked 32-bit pointer-offset arithmetic is replaced with checked 64-bit arithmetic. dng_render_task::ProcessArea now indexes its temporary RGBTables/mask buffers in source-area coordinates and explicitly requires the src and dst areas to be the same size.
Root cause
In source/dng_pixel_buffer.cpp OptimizeOrder, the source and destination extents (sRange/dRange) were computed in uint32. The pointer rebase for negative steps was computed as (int32)(count - 1) * step * (int32)pixelSize in 32-bit signed arithmetic, so large counts, steps or pixel sizes could overflow and move sPtr/dPtr to the wrong address. A count of 0 made (count - 1) wrap, and negating a step equal to INT32_MIN was not checked. In source/dng_render.cpp dng_render_task::ProcessArea, the RGBTables mask and background-mask code indexed the temporary buffers with dstArea.t/dstArea.l and dstArea.W(), while the row loop and the per-row RGBTables buffer use srcArea. Nothing required srcBuffer.fArea and dstBuffer.fArea to be the same size. The material does not say which of these bundled changes is the one tracked as CVE-2026-27280.
How the fix works
- source/dng_pixel_buffer.cpp:OptimizeOrder: returns early if count0, count1 or count2 is 0, which prevents the (count - 1) wrap. sRange and dRange are now computed in uint64, with explicit casts of Abs_int32(step) and (count - 1).
- source/dng_pixel_buffer.cpp adds three static helpers. ComputeRebaseDelta returns an int64 from SafeInt64Mult of (count - 1), step and pixelSize. OffsetPointer applies an int64 delta to const and non-const pointers. NegateStepChecked calls ThrowOverflow if step == INT32_MIN. All six rebase blocks (the dStep0/1/2 and sStep0/1/2 branches) now use these helpers instead of the inline int32 multiplication and raw negation. <climits> is included for INT32_MIN.
- source/dng_render.cpp:dng_render_task::ProcessArea: adds DNG_REQUIRE (srcArea.Size () == dstArea.Size (), "area size mismatch"). It introduces cols = srcArea.W() and colsSigned = ConvertUint32ToInt32(cols) and uses them throughout. buffer.fPlaneStep now uses ConvertUint32ToInt32 instead of a raw int32 cast. The two tempMaskBuffer DirtyPixel calls and the background-mask row loop (row range, ConstPixel_real32, DirtyPixel_real32, column count) switch from dstArea to srcArea coordinates. The per-row RGBTables buffer rect is renamed srcRowArea. The ProfileGainTableMap selection and the exposureWeightGain computation move out of the row loop; they do not depend on the row.
- source/dng_point.h adds a unary operator- for dng_point, using SafeInt32Sub(0, v/h), and one for dng_point_real64. In source/dng_big_table.cpp, dng_image_table::PutCompressedStream now uses -fImage->Bounds().TL() instead of dng_point(0,0) - TL().
- The remaining changes are not security-related. dng_camera_profile.cpp calls ClearFingerprint() in SetFourColorBayer, SetProfileGainTableMap and both SetMaskedRGBTables overloads. dng_negative::AspectRatio is renamed BaseAspectRatio, and its callers in dng_negative.cpp ConvertToProxy and dng_render.cpp Render are updated. dng_show_message_f gets a printf format attribute on Mac/iPhone builds. The build version strings go to 2502, and README.version changes from 1.4.0 to 1.7.1.
Completeness
- OptimizeOrder: all six negative-step rebase sites (dimensions 0 to 2, in both the dRange >= sRange branch and the else branch) now use checked 64-bit arithmetic. Both range sums are widened. A zero count is handled by an early return, not by a throw.
- ProcessArea: within the diff hunks, the dstArea-based indexing of the temporary RGBTables/mask buffers is replaced with srcArea. dstArea.l remains in the final write into dstBuffer (DirtyPixel_real32 (..., dstArea.l, dstPlane)), and the new DNG_REQUIRE enforces equal area sizes for that translation. The rest of the function is not shown.
- In ProcessArea, buffer.fRowStep = SafeInt32Mult (buffer.fPlaneStep, int32 (buffer.fPlanes)) still uses a raw int32 cast of fPlanes (fNumTableTransformPlanes), while the cast of fArea.W() next to it was changed to ConvertUint32ToInt32.
- The commit is a vendor SDK rebase (2471 -> 2502). The material has no changelog that isolates the CVE-specific change, so the other bundled fixes cannot be judged one by one.
Behavior changes
- OptimizeOrder now throws (ThrowOverflow) when a step equals INT32_MIN. When any count is 0, it returns without reordering. Overflow in the 64-bit rebase delta is handled by SafeInt64Mult, whose behaviour is not shown in the material.
- dng_render_task::ProcessArea now fails a DNG_REQUIRE when the source and destination tile areas differ in size.
- Negating a dng_point with the new unary operator- goes through SafeInt32Sub(0, ...) for each component.
- Camera profile fingerprints are cleared when SetFourColorBayer, SetProfileGainTableMap or SetMaskedRGBTables is called.
- The ProfileGainTableMap lookup now runs once per ProcessArea call instead of once per row.
- dng_negative::AspectRatio is renamed BaseAspectRatio, so any out-of-tree caller using the old name will fail to compile.
Per major
- Android 14: same fix: platform/external/dng_sdk 52ff589c0c55 'Update to DNG SDK 1.7.1 2502' in android-security-14.0.0_r29 (Change-Id I98050384600e0f4ac77f4378e00eee9be4830aa7); 6/8 lines of the patched region detected at that tag.
- Android 15: same fix: identical diff as a23ee6aeb0ee in android-security-15.0.0_r17; 6/8 lines of the patched region detected.
- Android 16: same fix: identical diff as c4ffcdb3285d in android-security-16.0.0_r8; 6/8 lines of the patched region detected.
- Android 17: not affected per the bulletin, which does not list 17. 6/8 lines of the patched region are detected at android-security-17.0.0_r1. The material does not show which SDK build 17 carries.
Unknowns
- Which change in the 2471 -> 2502 rebase is the one assigned CVE-2026-27280 (OptimizeOrder arithmetic, ProcessArea coordinate handling, or dng_point negation).
- Why the bulletin rates this Critical EoP. The material gives no crash details and does not show which Android process hosts the vulnerable code path.
- How buffer.fArea is defined in the RGBTables block of ProcessArea, which determines whether and how far the old dstArea-based indexing could go out of bounds.
- How SafeInt64Mult and the single-argument ConvertUint32ToInt32 behave on overflow; their implementations are not in the material.
- Why only 6/8 lines of the patched region are detected at each tag.