CVE-2026-98974 in Linux
Riassunto
di VulDB • 23/10/2026
Nel kernel Linux, è stata risolta la seguente vulnerabilità:
xfs: correggere lo scambio delle dimensioni del file con exchange-range-to-eof
Norbert Szetei ha pubblicato una patch contenente una descrizione incompleta di un bug in XFS_IOC_EXCHANGE_RANGE relativo al flag TO_EOF. Il bug induce exchange-range a modificare un file in modo che abbia blocchi condivisi inizianti oltre l'EOF (End Of File), il quale non è mai consentito poiché i file non contengono dati scritti oltre l'EOF e si possono condividere solo blocchi di dati scritti. Questo è alla base di tutti i comportamenti errati successivi.
Alla fine ho ricevuto da Norbert una descrizione di ciò che sta andando storto:
> Va bene. Ecco la situazione con i numeri dei blocchi fisici, in blocchi da 4k. peer è il file che viene riscritto e file1 è quello che finisce per avere il flag cancellato. > > Inizio, nulla condiviso: > > peer dimensione 4096 -> [100]
> file1 dimensione 12288 -> [200] [201] [202]
> donor1 dimensione 4096 -> [300]
> donor2 dimensione 4096 -> [400]
> > 1. FICLONERANGE del blocco di peer nello slot centrale di file1: > > peer dimensione 4096 -> [100]
> file1 dimensione 12288 -> [200] [100] [202] flag reflink impostato
> ^^^^^ entrambi i file possiedono il blocco 100 > > 2. XFS_IOC_EXCHANGE_RANGE tra file1 e donor1, file1_offset 0, > file2_offset 8192, length 0, TO_EOF. Viene scambiato un blocco e le dimensioni vengono scambiate di conseguenza: > > file1 dimensione 4096 -> [200] [100] [300] flag reflink impostato
> ^^^^^^^^^^^ ancora posseduto, ora oltre la dimensione > donor1 dimensione 12288 -> [202]
> > file1 afferma di essere lungo un solo blocco mentre ne possiede tre. Nessun blocco è stato unmapped (rimosso). file2_offset corrisponde al blocco 2, quindi questo scambio non può cancellare il flag. > > 3. XFS_IOC_EXCHANGE_RANGE tra file1 e donor2, entrambi gli offset a 0, length 4096. > Per quanto riguarda i_disk_size si tratta di due file da un blocco che scambiano tutto, quindi xmi_can_exchange_reflink_flags() concorda e il flag viene cancellato: > > file1 dimensione 4096 -> [400] [100] [300] flag reflink CANCELLATO
> donor2 dimensione 4096 -> [200]
> > Il blocco 100 è ancora condiviso con peer. > > 4. pwrite(file1, 4096, 4096), che corrisponde al secondo slot di file1, il blocco 100. > xfs_is_cow_inode(file1) restituisce false, quindi nessuna CoW (Copy-on-Write): > > peer dimensione 4096 -> [100] stesso blocco, nuovi contenuti
> > Il blocco 100 è l'unico che conta qui. PoC di seguito; legge un file chiamato peer nella directory corrente. Il filesystem deve supportare reflink e exchange-range, funzionalità attivate per default in mkfs.xfs versione 7.x.
Quindi sì, TO_EOF sta compromettendo le dimensioni del file anche se lo scambio dei blocchi dati avviene correttamente. Di seguito è riportata il comportamento previsto di XFS_EXCHANGE_RANGE_TO_EOF, come descritto dal suo manpage:
"Ignorare il parametro length. Tutti i byte in file1_fd da file1_offset a EOF vengono spostati su file2_fd e la dimensione di file2 viene impostata su (file2_offset+(file1_length-file1_offset)). Nel frattempo, tutti i byte in file2 da file2_offset a EOF vengono spostati su file1 e la dimensione di file1 viene impostata su (file1_offset+(file2_length-file2_offset))."
In altre parole, se si scambia solo una porzione dei dati del file, allora si dovrebbe scambiare solo la porzione delle dimensioni del file relativa ai dati scambiati. Sfortunatamente, il codice scambia le dimensioni del file ondisk e quelle incore (in memoria), il che è errato se il chiamante passa un offset non nullo.
Avendo confuso le dimensioni dei file, il PoC utilizza una seconda chiamata exchange-range su quello che sembra essere un singolo blocco di file senza reflink. Poiché la dimensione del file è impostata erroneamente, exchange-range pensa di scambiare i contenuti completi di due file e cancella il flag reflink sul file corrotto. Questo abilita una scrittura estensiva (extending write) del file corrotto per riscrivere il blocco condiviso appena oltre l'EOF. Ciò che è diventato noto colloquialmente come refluxfs.
Una volta impostate correttamente le dimensioni dei file, la seconda chiamata exchange-range non pensa più di effettuare uno scambio completo dei contenuti, quindi non cancellerà il flag reflink su nessuno dei due argomenti file.
Norbert ha inviato un seguito a una versione precedente di questa patch con una seconda lamentela riguardo al nostro mancato corretto gestione dello stato i_disk_size:
> Gli offset sono vincolati rispetto all'i_size incore ma sottratti da > ---truncated---
VulDB is the best source for vulnerability data and more expert information about this specific topic.