Summary

There is a vulnerability in the Huawei Kirin SoC’s DDR Controller (DMSS) Access Permission system which allows the Linux kernel to bypass memory access restrictions and directly compromise multiple privileged subsystems of the SoC. As demonstrated by CVE-2021-3710, CVE-2021-39991, CVE-2021-37115, and CVE-2021-39986, read and write of critical system memory, including secure memory regions, is possible via those subsystems. Therefore, this vulnerability combined with one of CVE-2021-3710, CVE-2021-39991, CVE-2021-37115, or CVE-2021-39986 results in a fully realized chain of elevation of privileges from a kernel-level write primitive to total control of the secure world (TEE). The vulnerability was fixed in February 2022.

Vulnerability Details

We have identified a vulnerability in the access control of the ACPU’s normal world (e.g. under which the Linux kernel runs) in Huawei Kirin SoCs. The overly permissive rules allow the code running in the context of a normal-world kernel to control the modem’s EDMA engine. This results in the ability to pivot from a kernel write primitive to code execution on the modem or the LPMCU core.

The control registers of the modem’s DMA engine are on a fixed, unrandomized physical address, not affected by KASLR.

The addresses of EDMA instances are self-evident from kernel sources soc_acpu_baseaddr_interfaces.h:

#define SOC_ACPU_EDMA1_MDM_BASE_ADDR (0xE0210000)
#define SOC_ACPU_EDMA0_MDM_BASE_ADDR (0xE0204000)

Empirically, the programming model of the EDMA is very similar to the previously studied ASP-DMA and IOMCU DMA, so we used the kernel source file drivers/hisi/hi64xx/asp_dma.c to understand how to program transactions:

#define ASP_DMA_CX_LLI(j)               (0x0800+(0x40*j))
#define ASP_DMA_CX_BINDX(j)             (0x0804+(0x40*j))
#define ASP_DMA_CX_CINDX(j)             (0x0808+(0x40*j))
#define ASP_DMA_CX_CNT1(j)              (0x080C+(0x40*j))
#define ASP_DMA_CX_CNT0(j)              (0x0810+(0x40*j))
#define ASP_DMA_CX_SRC_ADDR(j)          (0x0814+(0x40*j))
#define ASP_DMA_CX_DES_ADDR(j)          (0x0818+(0x40*j))
#define ASP_DMA_CX_CONFIG(j)            (0x081C+(0x40*j))
#define ASP_DMA_CX_AXI_CONF(j)          (0x0820+(0x40*j))

int asp_dma_config(...) {
  ...
  /* disable dma channel */
  _dmac_reg_clr_bit(ASP_DMA_CX_CONFIG(dma_channel), 0);

  _dmac_reg_write(ASP_DMA_CX_CNT0(dma_channel), lli_cfg->a_count);

  /* set dma src/des addr */
  _dmac_reg_write(ASP_DMA_CX_SRC_ADDR(dma_channel), lli_cfg->src_addr);
  _dmac_reg_write(ASP_DMA_CX_DES_ADDR(dma_channel), lli_cfg->des_addr);
  ...
}

int asp_dma_start(...) {
  ...
  _dmac_reg_write(ASP_DMA_CX_CONFIG(dma_channel), lli_cfg->config);
  ...
}

This DMA engine is mainly used as a data mover between the Cortex-R8 modem processor and DSP subsystem. The kernel does not act as a master of this DMA engine, as there is no ioremap of the physical address for either EDMAs and functions to configure these EDMAs are not part of the kernel code. So we can conclude that the kernel should not be able to access the control registers of the EDMAs. But the reality is that the kernel can in fact control the EDMA peripherial and thus can perform memory transactions in the name of the modem. This is the root cause of the vulnerability.

For an effective usage of the EDMA, on top of the immediate control abilities, we also need to find a common memory area that is shared between the DMA engine and the kernel. In this case the “modem-shared” memory is a convenient choice, because as its name suggests this memory is shared exactly between the kernel and the modem, so by design should be readable/writeable from both of them. The address of this region can be extracted from the device tree but also is in the global_ddr_map.h file:

#define HISI_RESERVED_MODEM_SHARE_PHYMEM_BASE 0x10980000

The access control of the EDMA is rather permissive, as it can directly overwrite the code section of the modem, which makes custom code execution possible. Furthermore the EDMA can also perform read and write operations on the LPMCU SRAM region 0xFFF50000.

For a detailed description of the vulnerability impact, see our presentation.

Affected Devices (Verified)

  • Kirin 990
    • Huawei Mate 30 Pro (LIO)
    • Huawei P40 Pro (ELS)
    • Huawei P40 (ANA)

Fix

Huawei OTA images, released after February 2022, contain the fix for the vulnerability.

Timeline

  • 2021.08.05. Bug reported to Huawei PSIRT
  • 2021.09.08. Huawei PSIRT confirms vulnerability, does not provide severity rating
  • 2021.09.21. Additional reporting to Huawei PSIRT shows Kirin 9000 is vulnerable
  • 2021.10.19. Update Requested
  • 2021.10.20. Huawei confirms final assessment and High severity rating
  • 2022.02.03. Huawei promises a later response
  • 2022.02.25. Huawei confirms CVE released in security bulletin, confirms disclosure allowed in May