Skip to content

[Security Vulnerability Disclosing] A PMP Bypassing Vulnerability on SecureIbex during the Instruction Fetch Stage Can Disrupt the State of MMIO Devices #2353

@zxz2004626

Description

@zxz2004626

Observed Behavior

Hi @nasahlpa , we want to disclose a Physical Memory Protection (PMP) violation on Ibex with the configuration of OpenTitan. On the SoC, MMIO devices are accessed through physical addresses. Accessing devices may cause side effects. For instance, a single read operation on the UART will change the state of its internal queue. Since Ibex does not have an MMU, all the physical memory address management needs to be carried out through PMP. So, if a memory access request is sent outside the core while violating the PMP rule, it will cause damage to the hardware functionality.

We ran the following testcase. At the boot_entry section, we make pmp preparation on address space 0x40000000-0x40000fff and set it to RWX=0. Then we use mret to return to U-mode. The U_entry section is run under U-mode. In this section, we prepare the UART MMIO address 0x40000000 and jump to it.

boot_entry:
80000080:   li t0,3
80000084:   csrs 0x7c0,t0
80000088:   lui t0,0x10000
8000008c:   addi t0,t0,511
80000090:   csrw pmpaddr0,t0                   # t0 = 0x100001FF, protect physical address 0x40000000-0x40000fff
80000094:   li t0,24
80000098:   csrw pmpcfg0,t0                    # t0 = 0x18, set the mode R/W/X=0
8000009c:   lui t0,0x20000
800000a0:   addi t0,t0,1023
800000a4:   csrw pmpaddr1,t0                   # t0 = 0x200003FF,  protect physical address 0x80000000-0x80000fff
800000a8:   lui t0,0x2
800000ac:   addi t0,t0,-232
800000b0:   csrw pmpcfg0,t0                    # t0 = 0x1F18, set 0x80000000-0x80000fff executable at U-mode
800000b4:   auipc t0,0x0
800000b8:   addi t0,t0,28
800000bc:   csrw mepc,t0                       # t0 = 0x800000d0, set mepc to U_entry
800000c0:   lui t0,0x2
800000c4:   addi t0,t0,-2048
800000c8:   csrc mstatus,t0                    # t0 = 0x1800, set MPP t0 zero (U-mode)
800000cc:   mret

U_entry:
800000d0:   lui t0,0x40000
800000d4:   jr t0                              # t0 = 0x40000000, UART MMIO address

Then we consider the running result. In the waveform shown below, G1 represents the commit stage signals, including the commit PC (rvfi_pc_rdata), commit instructions (rvfi_insn), and commit valid (rvfi_valid) signals. G2 represents the instruction-fetching signals, including instruction request address (instr_addr_o), instruction reply data valid signal (instr_rvalid_i), and instruction reply data itself (instr_rdata_i). G3 represents the PMP-related and exception signals. including PMP check result (pmp_err_if_i), PMP check address (pmp_req_addr), ID-stage error signal (instr_fetch_err_o), and the ID-stage exception signal (id_exception_o). G4 represents the present privilege mode (priv_mode_i). G5 represents the customized MMIO device internal signals, including the MMIO request valid signal (auto_magic_in_a_valid) and MMIO reply data (auto_magic_in_d_bits_data). The reply data of this device is always 0xdeadbeaf.

Image

When the instruction jr t0 at 0x800000d4 is executed, the pc_if will be set to the target address 0x40000000. At this point, the PMP module will check this address and find that a PMP violation occurred in this instruction fetch request (25000 ps). The code for the check: assign pmp_req_addr[PMP_I] = {2'b00, pc_if};

However, this PMP violation signal (pmp_err_if_i) won't trigger an exception and cancel the instruction request immediately. Instead, it was passed to the ID-stage and output as instr_fetch_err_o. The ibex_controller at ID-stage then use this signal to generate id_exception_o. Finally, Ibex switches its privilege into M-mode and traps into the exception handler.

From the time pc_if was set to the MMIO device address to the time exception was triggered, there is a window for instruction fetch. During this window, Ibex uses MMIO addresses, which are protected by PMP to trigger instruction requests. The request will be sent to the bus and forwarded to the MMIO device. Finally, the MMIO device will consider that a read operation has been completed and update its internal state. Overall, we make an MMIO device access at U-mode successfully despite to the PMP.

Expected Behavior

In conclusion, the instruction fetch request using the MMIO device address shouldn't be sent from the Ibex core. The solution that came into my mind is that the PMP exception triggered at the IF-stage, and the PMP error signal can cancel the instruction request signal. Although there can still be further distinctions made at the bus periphery in OpenTitan, Ibex could be used at any SoC, and the general SoC won't make this distinction between I/D.

Steps to reproduce the issue

We attach the source code of this testcase and the Makefile to compile the testcase. You can run the testcase at the OpenTitan configuration and observe the instruction fetching signals.

My Environment

EDA tool and version:
Synopsys VCS 2025.06-SP1 and Synopsys Verdi 2025.06-SP1

Operating system:
Rocky Linux 9.7 (Blue Onyx)

Version of the Ibex source code:
0c233f5

rv32_test-ap-Ibex-MMIO-Fetch-J-type.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions