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.