There is an ION memory buffer type confusion vulnerability in the Exynos ION kernel driver. The vulnerability can cause zero initialised memory to be treated as a valid pointer and cause a kernel NULL pointer exception. Untrusted applications can abuse this bug to cause a kernel crash and carry out DOS attacks agains the device.

Vulnerability Details

The vulnerable code is in ion_iovmm_map in drivers/staging/android/ion/ion_exynos.c, the function is used to map an ion buffer into the bus’s io address space, to make it available for dma capable external devices and returns this dma address. The function has a fast path for buffers marked with ION_FLAG_PROTECTED and returns their associated, preinitialised prot->dma_addr pointers. See the code snippet below:

dma_addr_t ion_iovmm_map(struct dma_buf_attachment *attachment,
       off_t offset, size_t size,
       enum dma_data_direction direction, int prop)
{
  struct ion_buffer *buffer = attachment->dmabuf->priv;
  dma_addr_t iova;

  if (IS_ENABLED(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION) &&
      (buffer->flags & ION_FLAG_PROTECTED)) {
    // 1. this could be an uninitialised pointer
    struct ion_buffer_prot_info *prot = buffer->priv_virt;

    // 2. which is dereferenced and the read value is returned
    iova = prot->dma_addr;
  } else {
    iova = __ion_iovmm_map(attachment, offset, size,
               direction, prop);
  }

  return iova;
}

This optimization is based on the assumption that either the ion allocators initialize the buffer’s priv_virt pointer for protected buffers or disallow the use of this flag. This assumption is true for most heap implementations except the so called rbin heap. Any ion buffer allocated from the ION_HEAP_TYPE_CUSTOM2 heap uses this allocator. The rbin heap allocator (ion_rbin_heap_allocate in drivers/staging/android/ion/ion_rbin_heap.c) simply ignores this flag, however it never initializes the priv_virt pointer.

As a result, if an rbin heap allocated ion buffer is passed to the ion_iovmm_map function, it uses the uninitialized priv_virt pointer. Even though the ion_iovmm_map is not exposed to the user space directly, there are different kernel drivers that pass user supplied ion buffers to this function.

I have statically verified that such call chains exist in the kernel for the following devices:

  • /dev/vertex10: npu_memory_map -> ion_iovmm_map
  • /dev/tsmux: tsmux_ioctl -> tsmux_ioctl_m2m_map_buf -> ion_iovmm_map
  • /dev/g2d /dev/fimg2d: g2d_ioctl -> … -> g2d_get_dmabuf -> ion_iovmm_map
  • /dev/dsp: dsp_ioctl -> … -> dsp_memory_map_buffer -> ion_iovmm_map
  • /dev/m2m1shot_scaler0: m2m1shot_ioctl -> … -> m2m1shot_dma_addr_map -> ion_iovmm_map
  • /dev/jsqz: jsqz_ioctl -> … -> jsqz_dma_addr_map -> ion_iovmm_map

This is not an exhaustive list, there might be further unexplored call paths that expose the vulnerability.

While access to these device drivers is restricted by selinux to varying degrees, together they provide a quite significant attack surface. The most notable is /dev/dsp. This node has the vendor_dsp_device label and that is exposed to quite a number of unprivileged selinux contexts, including untrusted applications. The /dev/ion device has the ion_device selinux label which is one of the least restricted selinux types, it is also available to untrusted applications.

As a direct result an untrusted application can allocate an ion buffer from the rbin heap and pass it to the /dev/dsp device. The device will try to access the uninitialized kernel pointer, which in this case will cause a null pointer dereference inside the kernel and a subsequent kernel panic.

Affected Devices (Verified)

Samsung S20 Exynos 990, SM-G980F

Fix

Samsung OTA images, released after September 2021, contain the fix for the vulnerability.

Timeline

  • 2021.04.27. Bug reported to Samsung Mobile Security
  • 2021.05.21. Samsung Mobile Security confirmes the vulnerability, SVE-2021-21620 is assigned
  • 2021.09.01. Samsung releases security bulletin, CVE-2021-25458 is assigned, OTA firmware update distribution begins