anthropics / anthropics/claudes-c-compiler

ice: aligned(sizeof(struct T)) aborts when the struct size is not a power of two

Abierto
#271 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Rust
Estrellas
2.8k
Forks
247
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

## Summary

`__attribute__((aligned(sizeof(T))))` where `T` is a struct or union whose size is
not a power of two reaches the backend as a bogus alignment and aborts the
compiler instead of producing a diagnostic.

Split out of #262 review. #270 fixes the `_Alignas` and `aligned` cases the parser can
see. This one takes a different route to the same assertion and was left untouched
there deliberately.

## Reproducer

```c
struct Wide { long a; double b; }; /* 12 bytes on i686, 16 on x86-64 */
int g __attribute__((aligned(sizeof(struct Wide)))) = 12;

int main(void) { return 0; }
```

```console
$ ccc-i686 -c s.c -o s.o
thread '' panicked at src/backend/common.rs:1116:9:
alignment must be power of 2
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
ccc: internal error: alignment must be power of 2
```

Clang rejects the same file:

```console
$ clang -target i386-unknown-linux-gnu -std=c11 -c s.c
s.c:2:22: error: requested alignment is not a power of 2
2 | int g __attribute__((aligned(sizeof(struct Wide)))) = 12;
| ^ ~~~~~~~~~~~~~~~~~~~
```

`ccc-x86` compiles it, because `sizeof(struct Wide)` is 16 there. The bug only
shows on targets where the struct size is not a power of two, which makes it easy
to miss.

## Why the parser check does not catch it

`Parser::try_sizeof_type_spec` returns `None` for struct, union, enum and typedef
types, because their size is not reliably known at parse time. So for
`aligned(sizeof(struct T))` the parser records no number at all; it stores the
type in `parsed_alignment_sizeof_type` and the real value is recomputed later,
from full layout information.

That recomputation happens in six places, none of which validate the result
before using it as an alignment:

- `src/frontend/sema/analysis.rs:393` (typedef path)
- `src/ir/lowering/stmt.rs:233`, `:272`, `:440`
- `src/ir/lowering/global_decl.rs:132`, `:318`

Each has the shape:

```rust
if let Some(ref sizeof_ts) = decl.alignment_sizeof_type {
let real_sizeof = /* size with full layout info */;
align = Some(align.map_or(real_sizeof, |a| a.max(real_sizeof)));
}
```

A size is not an alignment, and nothing checks that it happens to be a power of
two before it reaches `PtrDirective::align_arg`.

Note the contrast with `sizeof(long double)`, which *is* computed at parse time
(`try_sizeof_type_spec` returns `Some(12)` when `ptr_size == 4`) and so is caught
by the parser-level check in #270. The two paths diverge purely on whether the
parser can size the type.

## Suggested fix

Validate at each recomputation site (a positive power of two, or zero) and emit
`requested alignment is not a positive power of 2` rather than passing the value
on. Lowering diagnostics are already checked before codegen
(`src/driver/pipeline.rs:1067`), as are sema's (`:992`), so an error there aborts
cleanly.

Worth a maintainer call on one point before anyone writes it: rejecting this
changes behaviour on 32-bit targets for code that compiles today on 64-bit ones.
`aligned(sizeof(some_struct))` is not a rare idiom, and on i686 every instance of
it with a non-power-of-two struct becomes an error. That matches GCC and Clang,
but it is a visible change to what builds, so it seemed worth separating from the
crash fix rather than folding in.

I'm happy to send the PR if you'd like it fixed this way.

## Environment

- `main` at 6f1b99a
- Reproduced with `ccc-i686`; `ccc-x86`, `ccc-arm` and `ccc-riscv` are unaffected
for this particular struct because `sizeof` is 16 there

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.