Skip to content

Accesses to alloca buffers in translated code can cause UB #1954

Open

Description

@AlexTMjugador

Currently, c2rust translates functions that call alloca to set up and read from a [c2rust_]alloca_allocations: Vec<Vec<u8>> variable:

// If `alloca` was used in the function body, include a variable to hold the
// allocations.
if let Some(alloca_allocations_name) = self
.function_context
.borrow_mut()
.alloca_allocations_name
.take()
{
// let mut c2rust_alloca_allocations: Vec<Vec<u8>> = Vec::new();
let inner_vec = mk().path_ty(vec![mk().path_segment_with_args(
"Vec",
mk().angle_bracketed_args(vec![mk().ident_ty("u8")]),
)]);
let outer_vec = mk().path_ty(vec![mk().path_segment_with_args(
"Vec",
mk().angle_bracketed_args(vec![inner_vec]),
)]);
let alloca_allocations_stmt = mk().local_stmt(Box::new(mk().local(
mk().mutbl().ident_pat(alloca_allocations_name),
Some(outer_vec),
Some(mk().call_expr(mk().path_expr(vec!["Vec", "new"]), vec![])),
)));
body_stmts.push(alloca_allocations_stmt);
}

However, when the Vec<u8> backing buffer of an allocation is used to access types with stricter alignment requirements than u8, such as f32, undefined behavior can occur.

I ran into this while experimenting with transpiling libvorbis to Rust for use by my vorbis_rs library, where Miri correctly reported the following in the place where an alloca buffer was used:

    error: Undefined Behavior: accessing memory based on pointer with alignment 1, but alignment 4 is required
     --> packages/vorbis_c2rust_sys/src/lib/psy.rs:573:4
      |
      = note: Undefined Behavior occurred here
      |
      = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
      = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
      = note: this is on thread `issue_17`
      = note: stack backtrace:
              0: vorbis_c2rust_sys::lib::psy::setup_tone_curves
                  at packages/vorbis_c2rust_sys/src/lib/psy.rs:573:5: 573:48
              1: vorbis_c2rust_sys::lib::psy::_vp_psy_init
                  at packages/vorbis_c2rust_sys/src/lib/psy.rs:902:20: 909:3
              2: vorbis_c2rust_sys::lib::block::_vds_shared_init
                  at packages/vorbis_c2rust_sys/src/lib/block.rs:728:4: 735:5
              3: vorbis_c2rust_sys::lib::block::vorbis_analysis_init
                  at packages/vorbis_c2rust_sys/src/lib/block.rs:836:5: 836:53
              4: vorbis_rs::encoder::encoder_util::VorbisEncodingState::new
                  at packages/vorbis_rs/src/common/error.rs:258:22: 258:38
              5: vorbis_rs::VorbisEncoderBuilder::<&mut std::vec::Vec<u8>>::build
                  at packages/vorbis_rs/src/encoder/encoder_impl.rs:241:35: 241:72
              6: issue_17
                  at packages/vorbis_rs/tests/regression_tests.rs:11:20: 17:10
              7: issue_17::{closure#0}
                  at packages/vorbis_rs/tests/regression_tests.rs:9:14: 9:14

    note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

I have a hunch that the most appropriate fix would be to use unaligned read methods when reading from an alloca buffer.

Activity

  1. Rua commented on Jul 27, 2026

    @Rua
    Contributor

    How does C deal with this? It doesn't have any information about the required alignment, so does it just assume a relatively high alignment by default?

  2. AlexTMjugador commented on Jul 27, 2026

    @AlexTMjugador
    Author

    I think C deals with this by simply not providing a consistent contractual guarantee, since alloca is not a standard function.

    That said, Microsoft's C runtime documents that _alloca returns memory that is "suitably aligned for storage of any type of object". By contrast, the Linux alloca(3) man page makes no guarantees about alignment, but notes that it is implemented "by necessity" in terms of the __builtin_alloca compiler intrinsic, which I verified is how musl implements it. FreeBSD's man page doesn't mention either alignment nor any compiler intrinsics.

    In turn, Clang's documentation for __builtin_alloca likewise says nothing about alignment. However, Clang also provides a __builtin_alloca_with_align intrinsic with well-defined alignment semantics, and I found code suggesting that the unqualified intrinsic assumes the biggest required alignment.

    Naturally, GCC also has an equivalent pair of intrinsics, although their documentation is even sparser than Clang's. I also found code suggesting that the unqualified intrinsic assumes the biggest required alignment.

    So, in practice, I think C code that uses alloca and targets common C library implementations and compilers is safe with respect to alignment because these implementations have converged on treating alloca as providing a suitable alignment for any object, even though this behavior is largely an informal convention rather than a well-documented contractual guarantee, except in Microsoft's C runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions