https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128286

            Bug ID: 128286
           Summary: [C] Pointer counted_by under -fsanitize=bounds omits
                    checks on ptr[0], *ptr, and scaled indices ptr[2 * i]
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: 220245569 at seu dot edu.cn
                CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
  Target Milestone: ---
            Target: *-*-*

Summary
-------
Under `-fsanitize=bounds` in C, array indexing through a pointer annotated with
`__attribute__((counted_by(n)))` silently omits bounds checks for both zero
indices (`s->ptr[0]`, `*s->ptr`) and constant-scaled indices (`s->ptr[2 * i]`):

1. Zero index (`s->ptr[0]` / `*s->ptr`):
   In `gcc/c-family/c-gimplify.cc:74-80`, `is_address_with_access_with_size`
   requires the outer tree to be `POINTER_PLUS_EXPR` wrapping
`.ACCESS_WITH_SIZE`.
   For index `0`, pointer addition of `0` is folded away before `c_genericize`,
   leaving `INDIRECT_REF <.ACCESS_WITH_SIZE (...)>`. Because there is no
   `POINTER_PLUS_EXPR`, `ubsan_instrument_bounds_pointer_address` is never
   called, missing out-of-bounds dereferences when `s->n <= 0`.
2. Scaled index (`s->ptr[2 * i]`):
   In `gcc/c-family/c-ubsan.cc:669-726`, `get_index_from_offset` walks the
   `MULT_EXPR` factors of the byte offset looking for `tree_int_cst_equal
(factor, element_size)`.
   For `int *ptr` (`element_size = 4`) indexed by `2 * i`, early constant
folding
   combines `(i * 2) * 4` into `i * 8`. Because literal `4` is no longer
present,
   `get_index_from_offset` returns `NULL_TREE` and drops the bounds check!

Reproducer
----------
$ cat trigger.c
struct slice {
    int n;
    int *ptr __attribute__((counted_by(n)));
};

int read_zero(struct slice *s) {
    return s->ptr[0];
}

int read_mul2(struct slice *s, int i) {
    return s->ptr[2 * i];
}

Compile flag: gcc -O2 -fsanitize=bounds -fsanitize-trap=bounds -S trigger.c

$ cat trigger.s
read_zero:
        movq    8(%rdi), %rax
        movl    (%rax), %eax            # no ud2 bounds check when s->n <= 0!
        ret

read_mul2:
        movq    8(%rdi), %rax
        addl    %esi, %esi
        movslq  %esi, %rsi
        movl    (%rax,%rsi,4), %eax     # no ud2 bounds check for 2 * i >=
s->n!
        ret

Suggested Fix
-------------
1. Match bare `CALL_EXPR` to `IFN_ACCESS_WITH_SIZE` (offset 0) in
`c_genericize_control_r` (`gcc/c-family/c-gimplify.cc`).
2. In `get_index_from_offset` (`gcc/c-family/c-ubsan.cc:669-726`), divide
constant integer factors that are multiples of `element_size` by `element_size`
instead of requiring exact equality.

Reply via email to