Skip to content

An index into a fixed-size array is not checked: a[4] on an int[3] writes past it #3113

Closed

Description

@nicolas-maman

A slice index is bounds-checked: s[i] outside 0 ..< s.len is a named panic (language reference, Slices). An index into a fixed-size array T[N] is not checked at all. The generated C is a plain a[i], so an index outside 0 ..< N reads and writes whatever memory lies past the array:

fn at(a: int[3], i: int) -> int {
    return a[i]
}

main() {
    a = [1, 2, 3]
    b = [7, 8, 9]
    i = 4
    a[i] = 99                              // writes past a, into the frame
    println("${at(a, 5)} ${b[0]} ${b[1]}")
}

This prints 1089384752 7 8 and exits 0 (0.839.0 and the #3101 branch, Windows, gcc -O2). Nothing panics, and the write lands in the stack frame. The same holds for an array field (c.w[i] on w: long[80]), an array parameter, an actor's array state, and a const table (SIGMA[i]).

The reference says what a slice index does but never says what an array index does, so this reads as a gap rather than a decision. A language that panics on a bad slice index but corrupts memory on a bad array index gives a program no safety it can rely on.

Fix:

  • Every index into a T[N] is checked against N, as a slice index is. Outside 0 ..< N it is the same named panic, with the source line, the index and the length. The check covers reads, writes, compound assignments and ++/--, through locals, fields, parameters, actor state and const tables.
  • A constant index outside the range is a compile error.
  • The reference states the rule beside the slice rule.
  • Performance is measured before and after on the code that indexes arrays in hot loops: the cryptography modules (blake2's SIGMA, gost's 2048-entry table, the round constants) and the cross-language benchmarks. With a constant N and a loop bounded by it, the C compiler drops the check itself; any case where it does not is reported with its numbers.

Found on #3101, while moving #3107's crypto buffers onto arrays.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions