Skip to content

xtensa: fix OOB TLB entry access - #42

Open
hinaultd wants to merge 1 commit into
antmicro:masterfrom
hinaultd:xtensa-fix-oob-tlb-entry-access
Open

xtensa: fix OOB TLB entry access#42
hinaultd wants to merge 1 commit into
antmicro:masterfrom
hinaultd:xtensa-fix-oob-tlb-entry-access

Conversation

@hinaultd

Copy link
Copy Markdown

Backport of QEMU commit 604927e357c2 ("target/xtensa: fix OOB TLB entry access", 2023-12-15), which tlib's xtensa port predates.

The problem

r[id]tlb[01] and [iw][id]tlb take the TLB way index from a guest register. split_tlb_entry_spec() masks it with 0x7 for the ITLB and 0xf for the DTLB:

*wi = v & (dtlb ? 0xf : 0x7);

but the backing host arrays in arch/xtensa/cpu.h are

xtensa_tlb_entry itlb[7][MAX_TLB_WAY_SIZE];
xtensa_tlb_entry dtlb[10][MAX_TLB_WAY_SIZE];

so way index 7 is out of bounds for the ITLB and 10..15 are out of bounds for the DTLB. xtensa_tlb_get_entry() indexes them without any check, and get_tlb_entry() cannot fail, so rdtlb0 / rdtlb1 / wdtlb / witlb / idtlb / iitlb let guest code read and write host memory past those arrays.

The fix

Same shape as upstream:

  • split_tlb_entry_spec() returns whether the requested way is valid;
  • get_tlb_entry() returns NULL for an invalid way;
  • the rtlb0 / rtlb1 / itlb helpers check for NULL and return 0 or do nothing;
  • wtlb skips the write for an invalid way;
  • xtensa_tlb_get_entry() asserts that the way and entry indices are in range.

Affected cores

Only cores with XCHAL_HAVE_PTP_MMU, i.e. those enabling XTENSA_OPTION_MMU: dc233c, de233_fpu, test_mmuhifi_c3. Cores using region protection instead — including esp32 and esp32s3 — take the else branch of split_tlb_entry_spec(), where the way index is hardcoded to 0.

One deliberate deviation from upstream

The new bounds check in xtensa_tlb_get_entry() uses tlib_assert() rather than assert(). assert() is compiled out in the Release build, which both defeats the check where it matters and leaves the tlb local unused — the first build of this patch emitted -Wunused-variable. tlib_assert() is always active and reports through tlib_abortf(), consistent with its use elsewhere in tlib. Happy to switch it back if you'd rather stay byte-for-byte with upstream.

Testing

  • Rebuilt translate-xtensa-le.so for TARGET_ARCH=xtensa, CMAKE_BUILD_TYPE=Release: compiles without warnings.
  • Booted a real ESP-IDF image on an esp32s3 core before and after: behaviour unchanged, and the new assertion does not fire. That path reaches xtensa_tlb_get_entry() through get_physical_addr_region() with wi = 0 and ei masked to 0..7, against a region-protection TLB configured as nways = 1, way_size = {8} — in range by construction.
  • I have no MMU-core setup to exercise the faulty path itself, so the out-of-bounds access is established by code reading rather than reproduced.

The r[id]tlb[01], [iw][id]tlb opcodes use a TLB way index supplied by the
guest in a register. split_tlb_entry_spec() masks that index with 0x7 for
the ITLB and 0xf for the DTLB, yielding 0..7 and 0..15 respectively, but
the backing host arrays are declared as

    xtensa_tlb_entry itlb[7][MAX_TLB_WAY_SIZE];
    xtensa_tlb_entry dtlb[10][MAX_TLB_WAY_SIZE];

in arch/xtensa/cpu.h. Way index 7 is therefore out of bounds for the ITLB,
and 10..15 are out of bounds for the DTLB. Guest code can reach this
through rdtlb0/rdtlb1/wdtlb/witlb/idtlb/iitlb, so a malicious or simply
buggy guest can read and write host memory past those arrays.

This is a backport of QEMU commit 604927e357c2 ("target/xtensa: fix OOB
TLB entry access", 2023-12-15), which tlib's xtensa port predates:

  - split_tlb_entry_spec() now returns whether the requested way is valid,
  - get_tlb_entry() returns NULL for an invalid way,
  - the rtlb0/rtlb1/itlb helpers check for NULL and return 0 or do nothing,
  - wtlb skips the write for an invalid way,
  - xtensa_tlb_get_entry() asserts that the way and entry indices are in
    range.

Only cores with XCHAL_HAVE_PTP_MMU are affected, i.e. those that enable
XTENSA_OPTION_MMU: dc233c, de233_fpu and test_mmuhifi_c3. Cores using
region protection instead, such as esp32 and esp32s3, take the else branch
of split_tlb_entry_spec() where the way index is hardcoded to 0.

One deliberate deviation from upstream: the new bounds check in
xtensa_tlb_get_entry() uses tlib_assert() rather than assert(). assert()
is compiled out in the Release build, which both defeats the check where
it matters and leaves the tlb local variable unused, producing a
-Wunused-variable warning. tlib_assert() is always active and reports
through tlib_abortf(), consistent with its use elsewhere in tlib.

Verified by rebuilding translate-xtensa-le.so and booting a real ESP-IDF
image on an esp32s3 core: behaviour is unchanged and the new assertion
does not fire. That path goes through get_physical_addr_region(), which
calls xtensa_tlb_get_entry() directly with wi = 0 and ei masked to 0..7,
against a region-protection TLB configured as nways = 1, way_size = {8},
so the indices are in range by construction.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants