Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

`Deque::make_contiguous` sets a wrong `back` for a wrapped, non-full deque: `len()` grows and elements are dropped twice

Abierto Apto para principiantes
#694 4 comentarios 0 reacciones 0 asignados Ver en GitHub

@sgued ya está trabajando en esto.

Desde el 26/10/2026.

  • #695 de @sgued — abierto

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
84/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
rust
Área
backend, testing

Línea de trabajo

Empieza en src/deque.rs, en Deque::make_contiguous, y sigue la rama no llena con vuelta descrita en el issue. Comprueba cómo se relacionan front, back y len() antes y después del movimiento; luego ejecuta o añade un caso de regresión con capacidad 8, front en 4 y 5 elementos. El trabajo está terminado cuando len() sigue siendo 5 después de make_contiguous() y cada elemento se destruye una sola vez.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción


First of all, thank you so much for maintaining heapless; while applying our testing tool across multiple crates during its evaluation, we encountered an issue that I would like to bring to your attention.

Deque::make_contiguous on a wrapped deque that is not full can set the back cursor to the wrong slot. After that, len() reports more elements than the deque holds, and safe code reads and drops slots that hold no value.

I notice the following code may have an unsound problem: https://github.com/rust-embedded/heapless/blob/1d3dace9d0ec77f8546da87b0b94873a07b98ad9/src/deque.rs#L404-L427

This is a different case from #676 / #677 (full deque). The branch below is reached by a deque that is not full.

Describe the bug

} else if free >= back_len {
    // from: FGH....ABCDE
    // to:   ...ABCDEFGH.
    unsafe {
        ptr::copy(buffer_ptr.add(self.front), buffer_ptr.add(self.back), front_len);
        ptr::copy_nonoverlapping(buffer_ptr, buffer_ptr.add(self.back + front_len), back_len);
    }

    self.front = back;
    self.back = 0;
}

After the copies the elements are in back..back + len, so back should be back + len. The code sets it to 0. Then len() returns capacity - front instead of len. This is right only when the elements happen to end at the last slot.

Example with capacity 8 and 5 elements in slots 4,5,6,7,0: the elements move to slots 1..6. front becomes 1 and back becomes 0, so len() returns 8 - 1 = 7. iter(), pop_*(), truncate(), clear() and Drop then treat 2 extra slots as elements. With Box elements this is a double free.

Tested on the latest release, heapless 0.9.3 (main 1d3dace has the same code).

To Reproduce

  1. Push and pop 4 times on a Deque<_, 8>, so that front is 4.
  2. Push 5 elements. They wrap into slots 4,5,6,7,0, and the deque is not full.
  3. Call make_contiguous().
use heapless::Deque;

fn main() {
    let mut dq: Deque<Box<u64>, 8> = Deque::new();

    // move `front` to slot 4
    for _ in 0..4 {
        dq.push_back(Box::new(0)).unwrap();
        dq.pop_front();
    }
    // 5 elements in slots 4,5,6,7,0: wrapped, not full
    for i in 0..5 {
        dq.push_back(Box::new(i)).unwrap();
    }

    println!("len before = {}", dq.len());
    dq.make_contiguous();
    println!("len after  = {}", dq.len());
} // drop: 7 slots are dropped, 2 of them twice

make_contiguous should not change len(): len after = 5, and each box should be freed once.

output (RUSTFLAGS="-Zsanitizer=address" cargo +nightly-2026-04-03 run --release --target x86_64-unknown-linux-gnu):

len before = 5
len after  = 7
==ERROR: AddressSanitizer: attempting double-free on 0x1078225e00d0 in thread T0:
    #0 free
    #1 heapless_deque_make_contiguous_back_cursor::main

Suggested fix

Set back to the end of the moved elements instead of 0 (src/deque.rs):

    self.front = back;
    // the elements now span `front..front + len`, not up to the end of the buffer
    self.back = self.to_physical_index(len);

to_physical_index wraps at the capacity, so when the elements end at the last slot it still gives 0, as before.

I applied this to a copy of 0.9.3. The PoC prints len after = 5 with no AddressSanitizer report.

Thank you for your time.

Lenguaje dominante
Rust
Estrellas
2k
Forks
253
Merge medio
3 d 2 h
PR fusionados (30 d)
1

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de rust-embedded/heapless

Todos los issues de rust-embedded/heapless

Issues similares

Más issues de Rust

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.