@Aaron for you:
(My AI speaks German)
Obwohl versucht wurde, mit __disable_irq() kritische Abschnitte zu schützen, entstehen durch die Platzierung der Interrupt-Freigabe und fehlendes Primask-Handling Sicherheitslücken beim gleichzeitigen Zugriff (z. B. Hauptschleife vs. Interrupts oder Multithreading).
1. Read-Modify-Write Race Condition auf Hardware-Registern
In DMAChannel::begin() werden die Interrupts direkt nach dem Reservieren der Kanalbitmaske wieder aktiviert (__enable_irq()). Danach folgen Read-Modify-Write-Zugriffe (|=) auf gemeinsame Hardware-Register:
C++
// Interrupts werden HIER bereits wieder aktiviert:
__enable_irq();
break;
// ...
// RACE CONDITION: Nicht-atomare Lese-Modifiziere-Schreib-Zugriffe!
CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON);
dchpri[...] |= DMA_DCHPRI_ECP | DMA_DCHPRI_DPA;
Das Problem:Das Register CCM_CCGR5 steuert die Taktversorgung für mehrere Peripheriegeräte im Chip. Wenn ein Interrupt genau zwischen dem
Lese- und
Schreibschritt von CCM_CCGR5 |= ... unterbricht und dort ebenfalls ein Taktregister geändert wird, geht eine der beiden Änderungen verloren.
2. Inkonsistenter Objektdaten-Zustand in release()
In release() erfolgt die Freigabe der globalen Maske
vor dem Zurücksetzen der Objekteigenschaften:
C++
void DMAChannel::release(void)
{
if (channel >= DMA_MAX_CHANNELS) return;
DMA_CERQ = channel;
__disable_irq();
dma_channel_allocated_mask &= ~(1 << channel); // Kanal global freigegeben
__enable_irq(); // Interrupts wieder an
// RACE CONDITION: Das Objekt hält kurzzeitig noch den alten Kanal,
// obwohl die Maske ihn bereits als "frei" flaggt!
channel = DMA_MAX_CHANNELS;
TCD = (TCD_t *)0;
}
Das Problem:Wenn unmittelbar nach __enable_irq() ein ISR ausgeführt wird, der begin() aufruft, kann dieser denselben Kanal sofort neu zugewiesen bekommen. Greift der aufrufende Kontext danach noch auf das alte DMAChannel-Objekt zu, bevor channel = DMA_MAX_CHANNELS ausgeführt wurde, greifen zwei Objekte auf denselben DMA-Kanal zu.
3. Zerstörung übergeordneter kritischer Sektionen (Nesting Error)
Der Code nutzt __disable_irq() und __enable_irq() bedingungslos:
C++
__disable_irq();
// ...
__enable_irq(); // Schaltet Interrupts bedingungslos wieder EIN
Das Problem:Wird begin() oder release() aus einem Kontext aufgerufen, in dem Interrupts bereits bewusst deaktiviert waren (z. B. innerhalb eines Interrupt-Handlers / ISRs oder einer eigenen kritischen Sektion), schaltet __enable_irq() die Interrupts global wieder ein. Das bricht den Schutz des aufrufenden Codes auf.
Richtiger Ansatz: Den aktuellen Status des PRIMASK-Registers sichern und wiederherstellen (
Save & Restore).
4. Fehlende volatile-Deklaration bei dma_channel_allocated_mask
C++
uint16_t dma_channel_allocated_mask = 0;
Da auf diese Variable aus normalen Funktionen und potenziell aus ISRs zugegriffen wird, muss sie als volatile deklariert sein. Andernfalls kann der Compiler Zugriffe darauf in Register cachen, wodurch Änderungen aus anderen Kontexten nicht gesehen werden.