Skip to content

CWE-118: Incorrect Access of Indexable Resource ('Range Error')

Overview

Incorrect Access of Indexable Resource is MITRE's broad umbrella term for any operation on an indexable resource - an array, buffer, string, stream, or similar structure - that reads or writes outside the bounds the resource actually has. It is the general "range error" concept, one level above the concrete failure modes (out-of-bounds read, out-of-bounds write, out-of-range pointer offset, uninitialized pointer access) that actually appear in code and in scan findings. An off-by-one is a frequent cause of all four but is not one of them: MITRE files CWE-193 under the Incorrect Calculation pillar, outside this hierarchy, and records it as something that can precede a bounds violation rather than be one.

Relationship to Other CWEs

CWE-118 is a MITRE Class. MITRE marks it, and its direct child CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer), DISCOURAGED for mapping findings and recommends a more specific descendant instead. Several of those descendants have their own pages here:

If a scanner or code review names CWE-118 directly and none of those pages fit, use this page for the general concept; otherwise prefer the more specific one.

Risk

Critical: Reading or writing outside an indexable resource's actual bounds can corrupt adjacent memory or data, expose information the caller was never meant to see, crash the process, or - when the affected resource is a memory buffer and the language performs no automatic bounds checking - enable arbitrary code execution. Severity depends on which failure mode applies, which is why the descendant pages above go into more depth than this one.

Remediation Steps

Core Principle: Never trust an index, offset, or length to be in range - validate it against the resource's actual current size before every access, and prefer APIs that enforce this automatically over ones that don't.

Trace the Data Path

  • Source: Any index, offset, or length value influenced by user input, file data, network data, or a calculation derived from them
  • Sink: The indexed access itself - an array/buffer read or write, a substring or slice operation, a stream seek
  • Missing Controls: No check that the index/offset falls within [0, size), or a size/length calculation that can be wrong (off-by-one, integer overflow, stale cached size) before the access happens

Use Bounds-Checked Abstractions (Primary Defense)

Prefer indexable types and APIs that track their own size and refuse an out-of-range access over raw, manually-tracked structures. Where both a checked and an unchecked access method exist on the same type, default to the checked one and reserve the unchecked form for call sites where the index has already been proven in range.

Validate Every Access Against the Resource's Current Size (Defense in Depth)

  • Check index >= 0 && index < size before every access, and for a range access reject when length > size - offset (after confirming offset <= size) rather than accepting on offset + length <= size - the second form performs the addition before the comparison, so a large enough offset wraps the sum back into range and the check passes
  • Recompute or re-check the size at the point of access rather than trusting a value cached earlier - the resource can shrink or be reallocated between the two points
  • Where an index is held in an unsigned type, validate it at the point where it was converted from input rather than at the access: after the conversion a negative value has already become a large positive one, and nothing downstream can tell the two apart

Test with Boundary and Malicious Inputs

  • The last valid index, and the first invalid index one past it
  • Negative indices, where the type permits them
  • Indices or lengths derived from attacker-controlled calculations, including values crafted to overflow the calculation itself
  • Re-scan with the security scanner to confirm the finding is resolved

Common Vulnerable Patterns

// VULNERABLE - pseudo-code
resource = allocate(fixed_size)
index = get_index_from_input()      // no validation
value = resource[index]             // reads or writes outside resource if index is out of range

Why this is vulnerable: the index and the bound are two independent values, and nothing in the expression connects them. resource knows how large it is and index knows nothing about resource, so the access is performed on the caller's assertion that the two are compatible - an assertion made by a line that was deleted, or never written, or written in a different function that no longer runs first.

This is the umbrella entry for the family, so a finding filed here usually means the tool could not determine the direction. That distinction matters for impact and not for the fix: a read discloses adjacent data (CWE-125), a write corrupts it (CWE-787), and both come from the same missing comparison against the resource's real size, at both ends of the range. The resource need not be memory either - an offset into a stream, a slice of a decoded buffer, or a record number in a file all fail the same way, and in those cases the consequence is the wrong data returned rather than a crash, which is considerably harder to notice.

Secure Patterns

// SECURE - pseudo-code
resource = allocate(fixed_size)
index = get_index_from_input()
if index < 0 or index >= resource.size:
    reject("index out of range")
value = resource[index]

Why this works: Validating the index against the resource's actual size immediately before the access means an out-of-range value - whether negative, too large, or the result of a bad calculation - is rejected instead of reaching the read or write. A resource type that enforces this bounds check internally (throwing or refusing on invalid access) is preferred over a hand-written check like this one, because it can't be skipped by a call site that forgets to add it.

Common Pitfalls

  • Fixing the reported access without checking for the same pattern nearby: A scanner or review typically flags one instance of a missing bounds check, but the same unchecked-index pattern often repeats across sibling operations in the same function or module - fixing only the flagged line leaves the others exploitable.
  • Clamping the index into range instead of rejecting it: Replacing an out-of-range index with the nearest valid one (index = min(index, size - 1)) avoids the crash or memory corruption, but silently accesses the wrong element instead of refusing the invalid request - it hides a logic error rather than fixing the security issue, and can itself produce incorrect, security-relevant results.
  • Trusting a length or count that travels with the data it describes: A size prefix embedded in a network message or file format is still attacker-controlled data, not a validated bound - it must be checked against the resource's real allocated size, not treated as authoritative on its own.
  • Assuming a managed language's automatic bounds checking is the whole fix: Languages that throw on an out-of-range access prevent memory corruption, but the exception from that check becomes a denial of service if the caller does not handle it - catching and rejecting the invalid input gracefully is still necessary.

Additional Resources