Back to Blog
medium SEVERITY8 min read

Integer Overflow in Shared Memory Bounds Check: How a Missing Cast Opened the Door to Arbitrary Memory Writes

A subtle but dangerous integer overflow vulnerability was discovered in `lib/rpmi_shmem.c`, where bounds checks on shared memory operations could be silently bypassed due to 32-bit arithmetic overflow. By carefully crafting `offset` and `len` values, an OS-level or hypervisor-level caller could direct firmware writes to arbitrary memory addresses — including interrupt vector tables and security-critical configuration structures. The fix was elegantly simple: casting operands to 64-bit before add

O
By Orbis AppSec
Published May 28, 2026Reviewed June 3, 2026

Answer Summary

This is an integer overflow vulnerability (CWE-190) in C firmware code (`lib/rpmi_shmem.c`), where a 32-bit addition of `offset + len` during a shared memory bounds check could silently wrap around to a small value, bypassing the check entirely. An OS-level or hypervisor-level caller could craft values that pass the check yet direct firmware writes to arbitrary memory addresses. The fix is to cast both operands to 64-bit (`uint64_t`) before the addition so the result cannot overflow and the bounds check correctly rejects out-of-range inputs.

Vulnerability at a Glance

cweCWE-190
fixCast `offset` and `len` to `uint64_t` before addition so the sum cannot wrap and the bounds check is enforced correctly
riskArbitrary firmware memory write, including interrupt vector tables and security-critical configuration structures
languageC
root cause32-bit arithmetic overflow in `offset + len` bypasses the shared memory bounds check in `lib/rpmi_shmem.c`
vulnerabilityInteger Overflow in Bounds Check

Integer Overflow in Shared Memory Bounds Check: How a Missing Cast Opened the Door to Arbitrary Memory Writes

Introduction

There's a class of bugs that security researchers find particularly insidious: vulnerabilities that look correct at first glance. The code has a bounds check. The developer clearly intended to validate input. And yet, due to a subtle arithmetic property, that check can be made to silently fail — leaving the system wide open.

That's exactly what happened in lib/rpmi_shmem.c, a component of the RPMI (RISC-V Platform Management Interface) shared memory implementation. A missing type cast meant that a bounds check designed to prevent out-of-range memory access could be trivially bypassed using integer overflow arithmetic. The result? An OS-level or hypervisor-level caller could instruct firmware to write to any memory address — including interrupt vector tables, firmware code regions, and security-critical configuration structures.

This post breaks down the vulnerability, explains the exploit path, and walks through the elegant one-line fix that closes the door.


What Is This Vulnerability?

At its core, this is a 32-bit integer overflow vulnerability in a memory bounds check. The affected code lives in three functions — rpmi_shmem_read, rpmi_shmem_write, and rpmi_shmem_fill — all of which perform the same flawed comparison:

// Vulnerable code (before fix)
if ((offset + len) > shmem->size) {
    return RPMI_ERR_BAD_RANGE;
}

Both offset and len are rpmi_uint32_t (32-bit unsigned integers). When you add two 32-bit unsigned integers together in C, the result is also computed as a 32-bit unsigned integer. If the sum exceeds 0xFFFFFFFF, it wraps around to zero — and the bounds check passes even though the actual access would be far outside the permitted range.

This is a classic CWE-190: Integer Overflow or Wraparound vulnerability, and it's one of the most common root causes of security bugs in low-level systems code.


The Vulnerability Explained

The RPMI Shared Memory Context

RPMI is a platform management interface used in RISC-V systems. It allows OS-level software and firmware to communicate through a shared memory window. The shared memory region has a defined size, and operations that read, write, or fill portions of it are supposed to be constrained to that window.

The write path is particularly sensitive. Looking at the broader context described in the vulnerability report, the write operation ultimately calls:

rpmi_env_memcpy((void *)(unsigned long)addr, out, len);

Here, addr is derived from values in the shared memory region — values that are writable by the OS or hypervisor. If the bounds check can be bypassed, an attacker controls both the destination address and the length of a memcpy into firmware memory.

How the Integer Overflow Works

Let's make this concrete. Suppose shmem->size is 0x1000 (4KB — a typical small shared memory window).

An attacker crafts the following values:
- offset = 0xFFFFF100 (a very large 32-bit value)
- len = 0x00000F00

Now observe what happens in the vulnerable check:

// 32-bit arithmetic:
// 0xFFFFF100 + 0x00000F00 = 0x100000000
// But in 32-bit: 0x100000000 mod 2^32 = 0x00000000

if ((0xFFFFF100 + 0x00000F00) > 0x1000) {
    // Becomes: if (0x00000000 > 0x1000)
    // Which is: if (false)
    // Bounds check PASSES! No error returned.
}

The sum overflows back to 0x00000000, which is less than shmem->size. The bounds check passes, and the operation proceeds with an offset value that is nearly 4GB beyond the start of the shared memory window.

Real-World Impact

In the context of firmware running on a RISC-V platform, this is severe:

  • Interrupt Vector Table Corruption: Writing to the IVT could redirect all interrupts to attacker-controlled code, effectively hijacking the firmware's control flow.
  • Firmware Code Overwrite: Overwriting firmware instructions allows persistent code execution at the highest privilege level.
  • Security Configuration Tampering: Structures controlling secure boot, memory protection units (MPUs), or cryptographic key storage could be silently modified.
  • Privilege Escalation: An OS-level caller (normally unprivileged relative to firmware) could escalate to firmware-level control.

The trust boundary violation here is significant: the shared memory interface is designed so that OS software cannot directly access firmware memory. This bug punches a hole straight through that boundary.


The Fix

The fix is a single-character change per affected line, but its implications are profound. The solution is to widen the arithmetic to 64 bits before performing the addition, preventing the overflow from occurring.

Before (Vulnerable)

// rpmi_shmem_read
if ((offset + len) > shmem->size) {

// rpmi_shmem_write
if ((offset + len) > shmem->size) {

// rpmi_shmem_fill
if ((offset + len) > shmem->size) {

After (Fixed)

// rpmi_shmem_read
if (((rpmi_uint64_t)offset + len) > shmem->size) {

// rpmi_shmem_write
if (((rpmi_uint64_t)offset + len) > shmem->size) {

// rpmi_shmem_fill
if (((rpmi_uint64_t)offset + len) > shmem->size) {

Why This Works

By casting offset to rpmi_uint64_t (a 64-bit unsigned integer) before the addition, C's type promotion rules ensure the entire expression is evaluated in 64-bit arithmetic. The maximum possible sum of two 32-bit values is 0x1FFFFFFFE — well within the range of a 64-bit integer — so no overflow can occur.

Let's revisit the attack scenario with the fix applied:

// 64-bit arithmetic:
// (uint64_t)0xFFFFF100 + 0x00000F00 = 0x100000000

if ((0x100000000ULL) > 0x1000) {
    // if (true) -> RPMI_ERR_BAD_RANGE is returned
    // Attack blocked!
}

The check now correctly identifies that the access would be out of range and returns an error before any memory operation occurs.

The Full Diff

- if ((offset + len) > shmem->size) {
+ if (((rpmi_uint64_t)offset + len) > shmem->size) {

Applied consistently across all three functions (rpmi_shmem_read, rpmi_shmem_write, rpmi_shmem_fill), this change closes the vulnerability completely.


Prevention & Best Practices

This vulnerability is a textbook example of why integer arithmetic in security-sensitive code deserves extra scrutiny. Here's how to prevent similar issues:

1. Always Consider Arithmetic Width in Bounds Checks

When performing addition or multiplication on values that will be compared against a larger type or used as memory offsets, explicitly widen the operands first.

// Risky: result computed in 32-bit
if ((uint32_t_a + uint32_t_b) > limit) { ... }

// Safe: result computed in 64-bit
if (((uint64_t)uint32_t_a + uint32_t_b) > limit) { ... }

// Also safe: use a safe addition macro/function
if (safe_add_u32(a, b, &result) || result > limit) { ... }

2. Use Compiler Warnings and Sanitizers

Modern compilers and sanitizers can catch many integer overflow issues:

# Enable overflow sanitizer during testing
gcc -fsanitize=undefined,integer ...

# Enable relevant warnings
gcc -Wall -Wextra -Wconversion -Wsign-conversion ...

The UndefinedBehaviorSanitizer (UBSan) with -fsanitize=integer will trap unsigned overflow at runtime during testing, helping surface these bugs before they reach production.

3. Use Safe Integer Libraries

For C/C++ projects, consider using safe integer arithmetic helpers:

  • __builtin_add_overflow() (GCC/Clang): Returns true if overflow would occur
  • intsafe.h (Windows): Provides safe arithmetic functions
  • safe_iop: A portable safe integer operations library for C
// Using GCC/Clang built-in overflow detection
uint32_t result;
if (__builtin_add_overflow(offset, len, &result) || result > shmem->size) {
    return RPMI_ERR_BAD_RANGE;
}

4. Validate All Externally-Supplied Values

Any value that crosses a trust boundary — from OS to firmware, from user to kernel, from network to application — must be treated as potentially adversarial. In this case:

  • offset and len come from shared memory writable by the OS
  • They should be validated individually before being combined
  • Consider adding upper-bound checks on each value separately
// Defense in depth: validate individually AND combined
if (offset >= shmem->size || len > shmem->size ||
    ((rpmi_uint64_t)offset + len) > shmem->size) {
    return RPMI_ERR_BAD_RANGE;
}

5. Code Review Checklist for Memory Operations

When reviewing code that performs memory reads, writes, or copies, always ask:

  • [ ] Are all arithmetic operations on sizes/offsets performed in a type wide enough to hold the result?
  • [ ] Can any combination of valid-looking inputs cause wraparound?
  • [ ] Are all inputs from untrusted sources validated before use in arithmetic?
  • [ ] Is the validation performed before any memory operation?

Relevant Security Standards

  • CWE-190: Integer Overflow or Wraparound
  • CWE-787: Out-of-bounds Write
  • CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
  • CERT C Rule INT30-C: Ensure that unsigned integer operations do not wrap
  • OWASP: Input Validation, Memory Management

Conclusion

This vulnerability is a powerful reminder that the presence of a bounds check is not the same as the correctness of a bounds check. A developer clearly intended to prevent out-of-range access — the check was there. But a subtle arithmetic property meant that the check could be silently defeated with crafted input values.

The key takeaways:

  1. Integer overflow in bounds checks is a real and exploitable vulnerability class, particularly in firmware and low-level systems code where trust boundaries are enforced through software rather than hardware.

  2. The fix was minimal but meaningful: casting one operand to a wider type before addition costs nothing in performance and completely closes the attack surface.

  3. Consistency matters: the same pattern appeared in three functions. Finding and fixing all instances — not just the one initially reported — is essential for a complete remediation.

  4. Trust boundaries demand rigorous validation: any value that crosses from a less-trusted to a more-trusted context must be treated as potentially adversarial, even when it "looks like" a simple integer.

Security in firmware is particularly high-stakes: bugs at this level can undermine every security guarantee built on top of it. Taking the time to reason carefully about integer arithmetic in memory operations is not premature optimization — it's fundamental correctness.


This vulnerability was identified and fixed as part of an automated security scanning process. Automated tools are increasingly effective at finding this class of bug — but understanding why the fix works is what enables developers to write secure code from the start.

Frequently Asked Questions

What is an integer overflow in a bounds check?

An integer overflow in a bounds check occurs when two values are added using a data type too narrow to hold the result, causing the sum to wrap around to a small number. In a bounds check like `if (offset + len > size)`, if `offset + len` overflows a 32-bit integer, the wrapped result may be smaller than `size`, making the check pass even though the actual memory range extends far beyond the buffer.

How do you prevent integer overflow in bounds checks in C?

Always perform arithmetic in a type wide enough to hold the maximum possible result before comparing. For 32-bit inputs, cast to `uint64_t` (or use `size_t` if appropriate) before adding. You can also use compiler intrinsics like `__builtin_add_overflow()` or safe integer libraries to detect overflow at runtime.

What CWE is integer overflow in a bounds check?

CWE-190 (Integer Overflow or Wraparound) is the primary identifier. When the overflow specifically leads to a buffer over-read or over-write, CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer) also applies.

Is checking `offset + len > size` enough to prevent out-of-bounds writes?

No — not if `offset` and `len` are 32-bit values and the addition is performed in 32-bit arithmetic. The sum can overflow and wrap to a small value that passes the check. You must ensure the addition is performed in a wider type (e.g., `(uint64_t)offset + len > size`) to make the check reliable.

Can static analysis detect integer overflow in bounds checks?

Yes. Tools like Semgrep, Coverity, CodeChecker, and clang's UndefinedBehaviorSanitizer (UBSan) can flag arithmetic operations on narrow types used directly in comparisons. Orbis AppSec detected this specific instance automatically and opened a pull request with the fix.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #88

Related Articles

critical

How integer overflow in CsoundMYFLTArray constructor happens in C++ and how to fix it

A critical integer overflow vulnerability was discovered in `Java/cs_glue.cpp` at line 324, where the `CsoundMYFLTArray` constructor multiplied a user-controlled integer `n` by `sizeof(MYFLT)` without checking for overflow before passing the result to `malloc`. An attacker supplying a value near `INT_MAX` could trigger the overflow, causing an undersized heap allocation that subsequent writes would overflow. The fix adds an explicit `SIZE_MAX / sizeof(MYFLT)` guard and replaces `malloc` with `ca

high

How integer overflow in malloc happens in C bipartite matching and how to fix it

A high-severity integer overflow vulnerability was discovered in the bipartite matching algorithm implementation where unchecked multiplication operations for memory allocation could wrap around, causing undersized buffer allocations and subsequent heap overflow. The fix replaces vulnerable `malloc(sizeof(int) * V)` patterns with safe `calloc(V, sizeof(int))` calls and adds proper bounds validation to prevent exploitation.

high

How integer truncation heap overflow happens in C++ UEFI ACPI parsing and how to fix it

A high-severity integer truncation vulnerability was discovered in `Mobility.Uefi.Acpi.cpp` where heap allocation sizes were stored in a 16-bit integer (`MO_UINT16`), causing silent truncation when the computed size exceeded 65535 bytes. This led to undersized heap allocations followed by out-of-bounds writes, exploitable by an attacker who can influence ACPI SRAT table contents in virtualized environments. The fix promotes the size variable to `MO_UINTN` (platform-native width) to prevent trunc

critical

How integer overflow in buffer size calculation happens in C and how to fix it

A critical integer overflow vulnerability was discovered in the `nsh_setvar()` function in `nshlib/nsh_vars.c`, where the buffer size calculation `newsize = pstate->varsz + varlen` could wrap around, causing a heap buffer overflow. The fix adds overflow checking before the addition, preventing attackers with shell access from corrupting memory by setting variables with crafted names and values.

critical

How integer overflow happens in C reliable.c and how to fix it

A critical integer overflow vulnerability was discovered in `reliable.c` at line 1299, where the `packet_buffer_size` calculation used signed `int` arithmetic that could wrap to a negative or undersized value when large `fragment_size` values were involved. By casting each operand to `size_t` before multiplication, the fix eliminates the overflow risk entirely and ensures the allocated buffer is always large enough to hold the reassembled packet data.

critical

How integer overflow in buffer size calculation happens in C++ and how to fix it

A critical integer overflow vulnerability was discovered in OpenCV's HAL filter implementation where multiplying image dimensions without overflow protection could allocate dangerously undersized buffers. An attacker supplying crafted image dimensions (e.g., 65536×65536) could trigger heap corruption through out-of-bounds writes. The fix promotes the calculation to 64-bit arithmetic with a single cast.