I think this diff is CVE-2025-21479.
It looks like it only affects Adreno A7xx devices (Snapdragon 8 Gen 1 and above).
Maybe the Qualcomm bulletin is wrong?
A6xx has 4 IB levels: RB, IB1, IB2, and SDS: SDS=0x3
A7xx adds IB3: now there are 5 IB levels: RB, IB1, IB2, IB3, and SDS=0x4.
SDS is now 0x4, so masking with 0x3 would give 0x0.
I’m guessing, on an Adreno A7xx device:
if you could somehow execute commands at IB level 4 (SDS) with CP_SET_DRAW_STATE
and find a command that checks for IB level = RB (kernel-provided ring buffer)
you can trick it into bypassing the check
But I don’t know enough about Adreno to be sure.
According to the Project Zero blog post, the CP_INDIRECT_BUFFER instruction calls an indirect buffer of control processor instructions,
When an app wants to use the GPU, the kernel’s RB (kernel ring buffer) will contain a CP_INDIRECT_BUFFER command that calls a user provided Indirect Buffer- IB1.
This user buffer can call its own level of indirect buffers: IB2.
On A7xx, there’s an extra level: IB3.
Additionally, on both A6xx and A7xx, there’s SDS, which isn’t entered by indirect buffer, but by CP_SET_DRAW_STATE.
https://cs.android.com/android/platform/superproject/+/android15-qpr2-release:external/mesa3d/src/freedreno/decode/cffdec.c;l=3030;drc=0dc791ed57dacf9fe3df694d7f285a8d9f942fa7
https://cs.android.com/android/platform/superproject/+/android15-qpr2-release:external/mesa3d/src/freedreno/decode/cffdec.c;l=2283;drc=0dc791ed57dacf9fe3df694d7f285a8d9f942fa7
A6xx has RB, IB1, IB2, and SDS.
CP_SET_DRAW_STATE sets IB level to 0x3: in a650_sqe.fw v114 from the Galaxy Fold 3 firmware:
mov $03, 0x3
or $12, $12, 0x20
call #fxn1132 // there's a branch delay slot, so this isn't executed yet...
cwrite $03, [$00 + @IB_LEVEL]
But A7xx now has RB, IB1, IB2, IB3, or SDS.
CP_SET_DRAW_STATE now sets IB level to 0x4:
mov $03, 0x4
cwrite $03, [$00 + @IB_LEVEL]
0x4 & 0x3 = 0x0.
https://cs.android.com/android/platform/superproject/main/+/main:external/mesa3d/src/freedreno/registers/adreno/adreno_control_regs.xml;l=327;drc=c0867f48117dc2c18b1ae689235cb1f60b237600
So code checking the current IB level will think SDS (set draw state) is RB (kernel ring buffer).
I’m not actually sure what CP_SET_DRAW_STATE can do.
However, it’s probably not as easy as asking it to somehow call a CP_SET_PROTECTED_MODE (the obvious candidate for exploitation). That instruction doesn’t seem to check IB level?
For example, from a740_sqe.fw v168 on the Galaxy S23:
CP_SET_PROTECTED_MODE:
mov $03, $data
and $03, $03, 0x1
cread $02, [$00 + 0x05a]
clrbit $02, $02, b30
shl $03, $03, 0x1e
or $02, $02, $03
cwrite $02, [$00 + 0x05a]
cwrite $02, [$00 + 0x104]
waitin
mov $01, $data
https://gitlab.freedesktop.org/mesa/mesa/-/blob/c0f56fc64cad946d5c4fda509ef3056994c183d9/src/freedreno/afuc/README.rst#id41
so I’m not quite sure how you’re supposed to exploit this.