Skip to content

Integer overflow vulnerabilities in ELF loader and OP-TEE memory mapping on guest-derived usize values #9

@github-actions

Description

@github-actions

Summary

Scanning the codebase for integer overflow vulnerabilities on usize values derived from untrusted guest input (ELF binary headers and OP-TEE guest syscall parameters) found multiple locations where direct arithmetic operators (+, -) are used instead of wrapping methods. In Rust debug builds these will panic; in release builds with overflow checks enabled they are also undefined behaviour triggers.


Findings

1. litebox_shim_linux/src/loader/elf.rs and litebox_shim_optee/src/loader/elf.rsCRITICAL

Both shims have identical vulnerable code in MapMemory::reserve(). len and align come from ELF program header fields (p_memsz/p_vaddr range and p_align) which are fully controlled by a malicious guest binary.

Lines 77, 91-92, 95, 99-100 (both files):

// Line 77 — overflow: if align is a large power-of-two (e.g., 2^63), the sum overflows
let mapping_len = len + (align.max(PAGE_SIZE) - PAGE_SIZE);

// Line 90 — next_multiple_of can overflow/panic in debug if mapping_ptr is near usize::MAX
let ptr = mapping_ptr.next_multiple_of(align);
// Line 91 — overflow: ptr and len are ELF-derived
let end = ptr + len;
// Line 92 — overflow: mapping_ptr from mmap, mapping_len ELF-derived
let mapping_end = mapping_ptr + mapping_len;
// Line 95 — underflow: if next_multiple_of overflowed, ptr < mapping_ptr
.sys_munmap(MutPtr::from_usize(mapping_ptr), ptr - mapping_ptr)?;
// Line 99 — underflow: if end/mapping_end overflowed
.sys_munmap(MutPtr::from_usize(end), mapping_end - end)?;

Attack vector: A crafted ELF with p_align = 2^63 (largest valid power-of-two usize) causes mapping_len to overflow.

Fix:

let mapping_len = len.wrapping_add(align.max(PAGE_SIZE).wrapping_sub(PAGE_SIZE));
// ...
let end = ptr.wrapping_add(len);
let mapping_end = mapping_ptr.wrapping_add(mapping_len);
// ...
.sys_munmap(MutPtr::from_usize(mapping_ptr), ptr.wrapping_sub(mapping_ptr))?;
// ...
.sys_munmap(MutPtr::from_usize(end), mapping_end.wrapping_sub(end))?;

2. litebox_common_linux/src/loader.rsHIGH

The ELF segment loading loop computes virtual addresses by adding a base_addr (the mmap result from reserve()) to ELF-provided p_vaddr, p_filesz, and p_memsz. While line 410 checks p_vaddr.checked_add(p_memsz), it does not protect against base_addr + p_vaddr overflowing.

Lines 420–423, 443:

// Line 420 — overflow: base_addr (mmap result) + p_vaddr (ELF-controlled)
let adjusted_vaddr = base_addr + p_vaddr;
// Line 422 — overflow: adjusted_vaddr + p_filesz (ELF-controlled)
let file_end = page_align_up(adjusted_vaddr + p_filesz);
// Line 423 — overflow: adjusted_vaddr + p_memsz (ELF-controlled)
let load_end = page_align_up(adjusted_vaddr + p_memsz);
// ...
// Line 443 — overflow: same as line 422
let unaligned_file_end = adjusted_vaddr + p_filesz;

page_align_up uses next_multiple_of(PAGE_SIZE) which will also panic in debug on an already-overflowed input.

Fix:

let adjusted_vaddr = base_addr.wrapping_add(p_vaddr);
let file_end = page_align_up(adjusted_vaddr.wrapping_add(p_filesz));
let load_end = page_align_up(adjusted_vaddr.wrapping_add(p_memsz));
// ...
let unaligned_file_end = adjusted_vaddr.wrapping_add(p_filesz);

Note: overflow guards (checked arithmetic + early return) should be added before these calculations rather than just wrapping, to avoid mapping garbage addresses in release builds.


3. litebox_shim_optee/src/syscalls/ldelf.rsMEDIUM

Both sys_map_zi and sys_map_bin validate total_size using checked_add (lines 52–56) and check orig_addr + total_size (line 57). However, after sys_mmap (lines 68–70 / 196–205) the variable addr is rebound to the kernel-chosen mmap result address, which may differ from the guest-provided address when the guest passes addr = 0. Subsequent arithmetic on this new addr is not protected.

sys_map_zi (lines 71, 81–82):

// Line 71 — overflow: addr is now the mmap result, pad_begin is guest-controlled
let padded_start = addr.as_usize() + pad_begin;
// Line 81 — overflow: padded_start + num_bytes, both indirectly guest-controlled
let pad_end_start = (padded_start + num_bytes).next_multiple_of(PAGE_SIZE);
// Line 82 — overflow: addr (mmap result) + total_size (guest-derived)
let region_end = addr.as_usize() + total_size;
// Line 86 — safe: guarded by `if pad_end_start < region_end`

sys_map_bin (lines 206, 234, 252–253):

// Line 206 — same as line 71
let padded_start = addr.as_usize() + pad_begin;
// Line 234 — overflow: num_bytes + padded_start could overflow before the subtraction
(num_bytes + padded_start - align_down(padded_start, PAGE_SIZE)).next_multiple_of(PAGE_SIZE)
// Line 252 — same as line 81
let pad_end_start = (padded_start + num_bytes).next_multiple_of(PAGE_SIZE);
// Line 253 — same as line 82
let region_end = addr.as_usize() + total_size;
// Line 257 — safe: guarded by `if pad_end_start < region_end`

Fix for both functions:

let padded_start = addr.as_usize().wrapping_add(pad_begin);
let pad_end_start = padded_start.wrapping_add(num_bytes).next_multiple_of(PAGE_SIZE);
let region_end = addr.as_usize().wrapping_add(total_size);
// Line 234 fix:
num_bytes.wrapping_add(padded_start).wrapping_sub(align_down(padded_start, PAGE_SIZE))
    .next_multiple_of(PAGE_SIZE)

Impact

All vulnerabilities can cause debug-build panics when loading a maliciously crafted guest ELF binary or when a malicious OP-TEE TA issues crafted ldelf syscalls. In release builds without overflow checks, the corrupted addresses could lead to incorrect memory mappings.

Recommended Fix Priority

  1. CRITICALelf.rs:77 (both shims): exploitable with a well-crafted p_align power-of-two value
  2. HIGHloader.rs:420–423,443: exploitable with crafted p_vaddr/p_filesz/p_memsz values combined with a non-zero base_addr
  3. MEDIUMldelf.rs arithmetic: exploitable only when the kernel mmap returns a high address (unusual but possible)

Generated by Integer Overflow Scanner

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions