We have identified a new out-of-bound write vulnerability in Mediatek’s Linux Kernel driver implementation of cellular-to-application processor communication interface (CCCI). The vulnerability can be exploited by a malicious (compromised) baseband runtime to achieve arbitrary code execution in the Linux Kernel.

The vulnerability we are disclosing in this advisory affected a wide range of Mediatek devices, including phones on the newest chipsets (Dimensity 700, 1000, etc). The July 2022 issue of the Mediatek Security Bulletin contains this vulnerability as CVE-2022-21765.

Vulnerability Details

There is a vmalloc out-of-bound write vulnerability in the kernel implementation of the modem-kernel communication interface. The out-of-bound write can be used to write controlled data, with controlled size, at a controlled location within the kernel’s vmalloc memory region.

The modem and the kernel exchange messages through a series of ring-buffers that reside in a shared memory region. The meta data of these ring-buffers are also stored in-line within the shared memory, thus both the kernel and modem can freely modify them. The vulnerability is the result of the kernel inherently trusting these values and failing to verify them.

When the kernel sends a message to the modem, ultimately the ccci_ringbuf_write() function is called (defined in drivers/misc/mediatek/eccci/hif/ccci_ringbuf.c) to write the content of the message into the shared memory. The offsets and length fields are taken from the ring-buffer header (struct ccci_ringbuf), which is also stored in the shared memory, and they are used without further verification.

The message is written to the ringbuf->buffer + ringbuf->rx_control.length + ringbuf->tx_control.write address, where the buffer contains the kernel virtual address of the shared memory buffer while the rx-length and tx-write fields can be arbitrary 32 bit values, controlled by the modem. The tx-write is only checked against tx-length field which is also read from the modem. The extract of the vulnerable function is presented below.

int ccci_ringbuf_write(int md_id, struct ccci_ringbuf *ringbuf,
  unsigned char *data, int data_len)
{
  int aligned_data_len;
  unsigned int read, write, length;
  unsigned char *tx_buffer;
  unsigned char *h_ptr;

  [...]
  // [0] Offsets and lengths are retrieved from SHMEM
  read = (unsigned int)(ringbuf->tx_control.read);
  write = (unsigned int)(ringbuf->tx_control.write);
  length = (unsigned int)(ringbuf->tx_control.length);

  // [1] Tx buffer location is calculated
  tx_buffer = ringbuf->buffer + ringbuf->rx_control.length;
  header[1] = data_len;
  h_ptr = (unsigned char *)header;

  // [2] Header is written to tx_buffer + write offset
  CCIF_RBF_WRITE(tx_buffer, h_ptr, CCIF_HEADER_LEN, write, length);
  write += CCIF_HEADER_LEN;

  // [3] length value is also controlled
  if (write >= length)
    write -= length;
  // [4] message data is written to tx_buffer + write offset
  CCIF_RBF_WRITE(tx_buffer, data, data_len, write, length);

  [...]
  return data_len;
}

#define CCIF_RBF_WRITE(bufaddr, data_addr, data_size, write_pos, buflen)\
do {\
  // [5] Since the length is controlled this check can always pass
  if (write_pos + data_size < buflen) {\
    rbf_memcpy((unsigned char *)(bufaddr) + write_pos,\
      (unsigned char *)data_addr, data_size);\
  } else {\
    rbf_memcpy((unsigned char *)(bufaddr) + write_pos,\
      (unsigned char *)data_addr,  buflen - write_pos);\
    data_addr = (unsigned char *)data_addr + buflen - write_pos;\
    rbf_memcpy((unsigned char *)(bufaddr), \
      (unsigned char *)data_addr,\
      data_size - (buflen - write_pos));\
  } \
} while (0)

As a result a compromised modem can force the kernel to write the message data into any chosen offset, between 0 and 2 * UINT_MAX, from the ringbuf->buffer kernel virtual address. The buffer pointer, points into the md-ap shared memory, which is iomapped into the kernel address space. Iomap allocates kernel virtual addresses from the vmalloc address space, just like vmap and vmalloc family functions. The vmalloc allocations are not affected by KASLR, their address only depends the order of allocations. The shared memory is mapped during kernel boot, when the allocations are still deterministic, as a result it has a fixed kernel virtual address. The vulnerability can be used to overwrite any vmapped, vmalloced or iomapped memory that was created after the shared memory mapping.

Affected Devices

All Mediatek chipsets containing the CCCI kernel driver, namely: MT6580, MT6735, MT6737, MT6739, MT6753, MT6761, MT6765, MT6768, MT6771, MT6779, MT6781, MT6785, MT6833, MT6853, MT6873, MT6877, MT6879, MT6883, MT6885, MT6889, MT6893, MT6895, MT6983, MT8321, MT8666, MT8667, MT8675, MT8765, MT8766, MT8768, MT8786, MT8788, MT8789, MT8791, MT8797

Fix

Mediatek OTA images, released after July 2022, contain the fix for the vulnerability.

Timeline

  • 2022.01.31. Bug reported to Mediatek PSIRT
  • 2022.04.19. Mediatek confirms vulnerability
  • 2022.05.03. Mediatek confirms CVE
  • 2022.07.04. Mediatek releases security bulletin
  • 2023.08.26. TASZK informs Mediatek of advisory disclosure plan
  • 2023.08.26. Mediatek requests two more months of delay
  • 2023.11.03. Vulnerability released at Hardwear.io
  • 2023.11.26. Advisory release