clean_dcache_by_address misses the last cache line when addr is not line-aligned

Aperta Adatta ai principianti
#708 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 2 giorni

@jseketa ci sta già lavorando.

Dal 3/11/2026.

  • #709 di @jseketa — aperta

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
79/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
rust
Ambito
embedded-iot

Direzione di ricerca

Individua clean_dcache_by_address e clean_invalidate_dcache_by_address, che secondo il report usano lo stesso calcolo del numero di linee; segui gli helper di slice e di riferimento che chiamano la funzione di pulizia. Inizia controllando la dimensione della linea di cache e l’arrotondamento degli indirizzi in quelle implementazioni. Il lavoro è completato quando i buffer non allineati hanno tutte le linee di cache interessate pulite e il comportamento ai limiti è coerente con il calcolo dell’ultimo byte riportato.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

cortex-m 0.7.10 (same code on main), Cortex-M7.

Steps to reproduce (on a PC, the line count only):

  1. cargo new lines && cd lines
  2. Put the program below in src/main.rs.
  3. cargo run -q

What the program does:

  1. Copies the line count of clean_dcache_by_address.
  2. Takes a 1460-byte buffer at three addresses.
  3. The first address is on a line.
  4. The second is 20 bytes into a line.
  5. The third is 28 bytes into a line.
  6. Computes the range the function cleans for each.
  7. Prints how many bytes at the end of the buffer are not cleaned.

Expected: 0 bytes not cleaned for every address.

The docs say the function cleans "until all of slice has been cleaned".
The docs only recommend alignment.

Got:

addr 0x24000000 size 1460: cleans 0x24000000..0x240005c0, buffer ends 0x240005b4, last 0 bytes not cleaned
addr 0x24000014 size 1460: cleans 0x24000000..0x240005c0, buffer ends 0x240005c8, last 8 bytes not cleaned
addr 0x2400001c size 1460: cleans 0x24000000..0x240005c0, buffer ends 0x240005d0, last 16 bytes not cleaned

On hardware (STM32H753, Cortex-M7, cortex-m 0.7.9, which has the same code):

  1. A 1460-byte buffer on the stack.
  2. The CPU writes the buffer.
  3. clean_dcache_by_slice on the buffer.
  4. The USART's TX DMA sends the buffer.

Expected: the receiver gets the bytes the CPU wrote.

Got: the last 8 bytes were stale in 609 of 685 writes.

With the buffer aligned to 32 bytes: 0 wrong.

Cause:

  1. clean_dcache_by_address rounds addr down to its cache line: let mut addr = addr & mask;
  2. It counts the lines from size alone: let num_lines = ((size - 1) / line_size) + 1;
  3. A buffer that starts inside a line reaches one line further than size alone says.
  4. That last line is never cleaned.
  5. The DMA reads old bytes from RAM there.

Also affected:

  • clean_invalidate_dcache_by_address has the same computation.
  • clean_dcache_by_slice calls clean_dcache_by_address.
  • clean_dcache_by_ref calls clean_dcache_by_address.

Not affected in the same way:

  • invalidate_dcache_by_address has the same computation.
  • But it is unsafe.
  • And it requires an aligned addr and size.

Fix:

  1. Round addr down to its line: the first line.
  2. Round the last byte, addr + size - 1, down to its line: the last line.
  3. Count the lines from the first line to the last line.
let first = addr & mask;
let last = addr.saturating_add(size - 1) & mask;
let num_lines = (last - first) / line_size + 1;

Why the last byte and not the end:

  • The end, addr + size, rounded up can overflow at the top of the address space.
  • The last byte's address always fits.
  • saturating_add stops a range that runs past the top at the top.

Workaround until a release has the fix:

  • Align the buffer to 32 bytes.
  • Or round addr down and addr + size up yourself before the call.

Program:

// The line count of cortex-m 0.7.10's SCB::clean_dcache_by_address, next to the lines a buffer touches.
const LINE: usize = 32;

fn cleaned(addr: usize, size: usize) -> (usize, usize) {
    let num_lines = ((size - 1) / LINE) + 1; // as in clean_dcache_by_address
    let start = addr & !(LINE - 1);
    (start, start + num_lines * LINE) // [start, end) the function cleans
}

fn main() {
    let size = 1460;
    for addr in [0x2400_0000, 0x2400_0014, 0x2400_001c] {
        let (start, end) = cleaned(addr, size);
        let missed = (addr + size).saturating_sub(end);
        println!("addr {addr:#x} size {size}: cleans {start:#x}..{end:#x}, buffer ends {:#x}, last {missed} bytes not cleaned", addr + size);
    }
}
Lingua principale
Rust
Stelle
1k
Fork
202
Merge medio
1g 4h
PR unite (30g)
2

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di rust-embedded/cortex-m

Tutte le issue di rust-embedded/cortex-m

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.