TrustZone Break-in Vulnerabilities in Ampere UEFI MM Drivers (Buffer Overflow and Stack Information Leak)
嗯,用户让我用中文总结一篇文章,控制在一百个字以内,而且不需要特定的开头。首先,我需要理解文章的内容。文章讲的是ARM Ampere Management Mode (MM) Boot Error Record Table (BERT)驱动中的缓冲区溢出和堆栈信息泄露漏洞。 这个漏洞影响的是运行在Secure世界中的S-EL0级别的固件。BERT驱动用于持久化之前的启动周期中的硬件故障信息。非安全EL1软件通过SMC指令和共享缓冲区与BERT驱动通信。 问题出在从NS-EL1传递到S-EL0的block_size参数没有适当的检查,导致了OOB写入或读取,可能引发权限提升或信息泄露。Ampere已经修复了这个漏洞,并发布了安全公告。 接下来,我需要将这些关键点浓缩到100字以内。要确保涵盖漏洞类型、影响范围、攻击途径以及修复情况。 可能的结构:描述漏洞类型、受影响部分、攻击方式、潜在影响、修复情况。 比如:“ARM BERT驱动存在缓冲区溢出和堆栈信息泄露漏洞,影响Secure世界中的固件。非安全EL1软件通过SMC指令与BERT驱动通信时,block_size参数缺乏验证,导致OOB写入或读取,可能引发权限提升或信息泄露。Ampere已修复该漏洞并发布安全公告。” 这样大约70字左右,符合要求。 </think> ARM BERT驱动存在缓冲区溢出和堆栈信息泄露漏洞,影响Secure世界中的固件。非安全EL1软件通过SMC指令与BERT驱动通信时,block_size参数缺乏验证,导致OOB写入或读取,可能引发权限提升或信息泄露。Ampere已修复该漏洞并发布安全公告。 2026-1-5 23:59:30 Author: github.com(查看原文) 阅读量:0 收藏

Summary

A buffer overflow and stack information leak affecting the ARM Ampere Management Mode (MM) Boot Error Record Table (BERT) driver. This code is bundled into the ARM Unified Extensible Firmware Interface (UEFI) firmware and runs in the Secure world at Exception Level 0 (S-EL0). The BERT driver is used to persist unhandled hardware faults (e.g. errors that stem from memory, CPU, system bus, overheating, failing motherboard, or inadequate power supply) from a previous boot cycle. Non-Secure EL1 (NS-EL1) software communicates with this driver using the Secure Monitor Call (SMC) instruction and a predefined shared buffer for message passing. Upon system reboot NS-EL1 would communicate with the BERT driver to check for prior failures and obtain detailed information from the log.

Values passed from NS-EL1 to S-EL0 defining the block_size of payload_data lack proper checks. This allows for an Out-of-Bounds (OOB) write on payload_data that could be used for privilege escalation from the normal world to the secure world or an OOB read of stack data from the secure world.

Ampere has addressed the vulnerability and posted a security bulletin.

Analysis

NS-EL1 software interacts with the MM BERT driver using the Firmware Framework for A-Profile (FFA) specification through the SMC instruction. The SMC instruction is used to switch to and from the Non-Secure and Secure worlds and FFA is used for dispatching to specific drivers and services. A shared buffer (initialized during the UEFI Driver Execution Environment [DXE] phase as mNsCommBufferMemRegion in ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c) is used to communicate messages between NS-EL1 and the S-EL0 BERT driver.

The handler for the BERT MM driver can be reached using the following GUID using the FFA specification. BertMmHeader shows the general layout of the communication buffer. Before the BertMmHandler dispatch routine is called the MM subsystem copies the shared message buffer to a private memory region to prevent Time-of-Check to Time-of-Use (TOCTOU) issues. As drivers uniquely define the underlying structures in the communicated messages each must perform validation before using the supplied data.

The code snippets included were created through analysis with Ghidra using names contained in string references from the UEFI image. Known functions and variables from the EDK2 repository were also applied.

#define BERT_MM_GUID \
    {0xCB01E6A2, 0xB22C, 0x4955, {0xAB, 0x75, 0xAC, 0x65, 0xEF, 0xFA, 0xE9, 0xD8}}

#define BERT_MM_MIN_SIZE 0xe
#define BERT_MM_MAX_SIZE 0x1000

typedef struct {
  UINT8             function;
  UINT8             unknown_0;
  UINT32            size;
  RETURN_STATUS     status;
  UINT8             data[1];
} BertMmHeader;

The BertMmHeader structure includes a function field for dispatching to specific sub-handlers and a UINT32 size to represent how much data should be returned. BERT_MM_MAX_SIZE defines the maximum size that could be returned.

uint8_t *bert_latest;

EFI_STATUS
BertMmHandler(EFI_HANDLE DispatchHandle,void *RegisterContext, void *CommBuffer, uint64_t *CommBufferSize) {
  // ...
  BertMmHeader *header;

  header = (BertMmHeader *)CommBuffer;

  if (CommBuffer == NULL || CommBufferSize == 0x0) {
    return 0;
  }
  if (*CommBufferSize < BERT_MM_MIN_SIZE) { 
    if ((size & 0xff) == 0) {
      return EFI_ACCESS_DENIED;
    }
    __LINE__ = 0x21;
    Debug(DEBUG_ERROR,"%a %d Communication buffer size invalid!\n","BertMmHandler",__LINE__);
    return EFI_ACCESS_DENIED;
  }

  switch(header->function) {
    case 1:
    if (BERT_MM_MAX_SIZE < *CommBufferSize - 0xe) {
      if ((size & 0xff) == 0) {
        return EFI_ACCESS_DENIED;
      }
      __LINE__ = 0x2d;
      goto LAB_000025ac;
    }
    block_size = header->size;
    if (block_size == 0) {
      return EFI_INVALID_PARAMETER;
    }
    else {
      if (mfs == 0x0) {
        GetProtocol();
      }
      payload_data = &header->data;
      if ((*bert_latest >> 1 & 1) == 0) {
        pbVar9 = bert_latest + 8;
        i = 0;
        do {
          if (((bert_latest[i * 0x17] & 1) != 0) &&
             ((bert_latest[i * 0x17] & 4) != 0)) {
            path = pbVar9;
          }
          i = i + 1;
          pbVar9 = pbVar9 + 0x17;
        } while (i != 3);
        result = mfs->open(mfs, &file, path, 1);
        if (result < 0) {
          result = -0x7ffffffffffffff9;
          return result;
        }
        mfs->read(mfs, file, 0, payload_data, &block_size);
        mfs->close(mfs, file);

      // ...
      }
      else {
        block_buffer buffer;
        buffer.unknown_0 = 1;
        buffer.size0 = 0x20;
        buffer.unknown_2 = 1;
        buffer.unknown_3 = 0;
        buffer.size1 = 0x20;

        CopyMem(payload_data, buffer, block_size);
        // ...

When BertMmHandler is called, some initial checks are performed on the CommBuffer and CommBufferSize. When the header->function is 1 the *CommBufferSize is checked to ensure it’s less than or equal to BERT_MM_MAX_SIZE after this the code branches based on a field in the bert_latest structure. In both cases though the block_size is never validated to be less than BERT_MM_MAX_SIZE. payload_data is either populated from the contents of a file using mfs->read or a newly initialized stack based structure using CopyMem.

In the case of CopyMem the size used is block_size and is never checked to be less than or equal to the stack based block_buffer structure size or payload_data size. When block_size is greater than the size of block_buffer this results in stack based OOB read with the data being returned to NS-EL1 in payload_data. When block_size is greater than payload_data an OOB write occurs with stack data copied to memory past the end of payload_data.

For mFs->read &block_size is used to determine how many bytes should be written into the payload_data buffer. If the file being read and block_size are both larger than payload_data an OOB write occurs using the contents of the file to write past the end of payload_data.

Timeline

Date reported: 2025-09-19
Date fixed: 2025-12-18
Date disclosed: 2025-12-18


文章来源: https://github.com/google/security-research/security/advisories/GHSA-46qj-g894-vrxr
如有侵权请联系:admin#unsafe.sh