[QPSIIR-80] Qualcomm TrustZone Integer Signedness bug
Posted on Thu 18 December 2014 in Advisory
Summary
Qualcomm TrustZone is prone to an
integer signedness bug that may allow to write NULL words to barely
controllable locations in memory.
The vulnerability can be triggered from Non-Secure World through the TrustZone call "tzbsp_smmu_fault_regs_dump".
This issue has been discovered in Samsung Galaxy S5 firmware, but other devices can be affected as well.
The vulnerability can be triggered from Non-Secure World through the TrustZone call "tzbsp_smmu_fault_regs_dump".
This issue has been discovered in Samsung Galaxy S5 firmware, but other devices can be affected as well.
Details
This vulnerability has been
discovered in TrustZone binary of Samsung Galaxy S5 firmware,
version 4.4.2.
The tzbsp_smmu_fault_regs_dump function can be called from Non-Secure World through the SMC instruction. It takes 4 arguments passed in R0-R3 registers.
When called with argument R0 > 1, nested function subfunc_1 is called with arguments (R0 = 0xFFFFFFFF, R1) :
The tzbsp_smmu_fault_regs_dump function can be called from Non-Secure World through the SMC instruction. It takes 4 arguments passed in R0-R3 registers.
When called with argument R0 > 1, nested function subfunc_1 is called with arguments (R0 = 0xFFFFFFFF, R1) :
FE84B9B6 tzbsp_smmu_fault_regs_dump
FE84B9B6 PUSH.W {R4-R8,LR}
FE84B9BA MOVS R6, R2
FE84B9BC MOV R8, R1
FE84B9BE MOV R7, R3
FE84B9C0 MOV.W R4, #0xFFFFFFFF
FE84B9C4 MOV R5, #0xFFFFFFEE
FE84B9C8 BEQ loc_FE84BA1A
FE84B9CA CBZ R0, loc_FE84B9D2 ; R0 > 1 : branch not taken
FE84B9CC CMP R0, #1
FE84B9CE BEQ loc_FE84B9D6 ; R0 > 1 : branch not taken
FE84B9D0 B loc_FE84B9D8 ; branch
FE84B9D2 ; ---------------------------------------------------------
FE84B9D2 loc_FE84B9D2 ; if R0 == 0
FE84B9D2 MOVS R4, #1
FE84B9D4 B loc_FE84B9D8
FE84B9D6 ; ---------------------------------------------------------
FE84B9D6 loc_FE84B9D6 ; if R0 == 1
FE84B9D6 MOVS R4, #0
FE84B9D8
FE84B9D8 loc_FE84B9D8 ; for any value of R0
FE84B9D8 MOVS R0, #1
FE84B9DA BL subfunc_0 ; kind of "is retail hardware?" test
FE84B9DE CBZ R0, loc_FE84B9EE ; not taken
FE84B9E0 MOV R1, R8
FE84B9E2 MOV R0, R4 ; R4 == #0xFFFFFFFF
FE84B9E4 BL subfunc_1
FE84B9E8 [...]
Then subfunc_1 checks if R0 value
is valid. It will Branch and return if R0 is Greater than or Equal
to 2. However, BGE instruction operates on signed integers. So R0
== -1 < 2 will pass the test and the execution will continue
:
FE853124 subfunc_1
FE853124 CMP R0, #2 ; R0 == #0xFFFFFFFF
FE853126 BGE locret_FE85314C ; signed comparison :
FE853126 ; R0(-1) < 2 so branch not taken
FE853128 MOVW R2, #0x9EE0
FE85312C ADD.W R3, R0, R0,LSL#1
FE853130 MOVT.W R2, #0xFE82
FE853134 ADD.W R0, R3, R0,LSL#3
FE853138 LDR R2, [R2,#(dword_FE829EE4 - 0xFE829EE0)]
FE85313A ADD.W R0, R2, R0,LSL#4
FE85313E LDRB R2, [R0,#4]
FE853140 CMP R2, R1 ; with R1 < R2
FE853142 BLS locret_FE85314C
FE853144 LDR R0, [R0]
FE853146 MOVS R2, #0
FE853148 B.W sub_FE856C84 ; write NULL DWORD to a barely arbitrary address (derived from R1 value)
FE85314C [...]
Finally, the last nested function
could allow to write NULL words to a limited range of memory
locations.
Fix
This bug is fixed in Lolipop
version of the firmware. Several changes have been made. First,
subfunc_1 function is not reachable anymore with an invalid R0
value:
FE84B970 tzbsp_smmu_fault_regs_dump
FE84B970 PUSH.W {R4-R8,LR}
FE84B974 MOVS R6, R2
FE84B976 MOV R8, R1
FE84B978 MOV R7, R3
FE84B97A MOV R5, #0xFFFFFFEE
FE84B97E BEQ loc_FE84B9D2
FE84B980 CBZ R0, loc_FE84B98A
FE84B982 CMP R0, #1
FE84B984 BEQ loc_FE84B98E
FE84B986 ADDS R0, R5, #2
FE84B988 B locret_FE84B7AA ; branch if R0 > 1
FE84B98A ; ---------------------------------------------------------
FE84B98A loc_FE84B98A ; if R0 == 0
FE84B98A MOVS R4, #1
FE84B98C B loc_FE84B990
FE84B98E ; ---------------------------------------------------------
FE84B98E loc_FE84B98E ; if R0 == 1
FE84B98E MOVS R4, #0
FE84B990
FE84B990 loc_FE84B990 ; if 0 <= R0 < 2
FE84B990 MOVS R0, #1
FE84B992 BL subfunc_0 ; kind of "is retail hardware?" test
FE84B996 CBZ R0, loc_FE84B9A6 ; not taken
FE84B998 MOV R1, R8
FE84B99A MOV R0, R4 ; R4 is either 0 or 1
FE84B99C BL subfunc_1
FE84B9A0 [...]
Then in (sub)subfunc_1, R0 value
is now tested with an unsigned comparison:
FE852B94 (sub)subfunc_1
FE852B94 CMP R0, #2
FE852B96 BCS loc_FE852BBA ;unsigned comparison: branch if R0 > 1
FE852B98 MOVW R2, #0x9F38
FE852B9C [...]
The bug can no longer be
triggered.
CVSS Version 2 Metrics
Access Vector: Local
Access Complexity: High
Authentication: Single
Confidentiality Impact: Complete
Integrity Impact: Complete
Availability Impact: Complete
Disclosure Timeline
2014-08-28 Intial vendor notification
2014-09-03 Vendor reply; severity of the issue rated as high
2014-00-00 Vendor has notified all OEMs
2014-12-18 Public advisory
References:
https://www.qualcomm.com/connect/contact/security/product-security/hall-of-fame