Copiare stringhe e blocchi di memoria è il pane quotidiano di chi programma a basso livello, eppure proprio una di queste operazioni di routine si è rivelata negli anni una trappola ricorrente. Con Linux 7.2 arriva il punto finale di una bonifica imponente: la funzione strncpy(), presente da decenni nel linguaggio C, sparisce del tutto dal kernel dopo circa 6 anni di lavoro e oltre 360 modifiche sparse tra sottosistemi, driver e architetture hardware diverse.
Dietro questa scelta ci sono problemi tutt’altro che teorici. Mantenere il kernel Linux significa gestire migliaia di contributori e più di 30 milioni di righe di codice, quindi togliere di mezzo un’API così radicata non è qualcosa che si fa con una manovra rapida. Serve una revisione lenta, attenta, fatta passo dopo passo. La rimozione di una funzione storica è sempre un’operazione delicata, ma negli ultimi anni il progetto ha spinto molto su hardening del codice, prevenzione delle vulnerabilità di memoria e riduzione delle superfici d’attacco. L’addio a strncpy() segue esattamente questa direzione.
Perché strncpy era una funzione tanto insidiosa
L’implementazione originale di strncpy() nasceva per gestire campi a lunghezza fissa, tipo quelli dei vecchi filesystem Unix o di strutture dati che andavano riempite con byte nulli. Il comportamento, però, è parecchio controintuitivo. Se la stringa di partenza supera la dimensione indicata, la funzione copia i byte richiesti ma non assicura la presenza del carattere terminatore NUL. Il risultato è una sequenza di caratteri che sembra una stringa C valida, ma non lo è davvero. E quando altre funzioni danno per scontato quel terminatore, il rischio di leggere memoria oltre i limiti previsti cresce parecchio.
C’è poi un secondo problema, meno visibile ma altrettanto fastidioso: il riempimento automatico dell’area di destinazione. Se la stringa sorgente è più corta della dimensione richiesta, strncpy() azzera tutti i byte rimanenti. In certi casi può tornare utile, ma molto spesso si traduce in lavoro inutile per la CPU, con un impatto sulle prestazioni che non serve a nulla. Nel kernel, dove alcune operazioni vengono eseguite milioni di volte al secondo, questi dettagli pesano eccome.
Sei anni di lavoro e oltre 360 commit
La scomparsa di strncpy() non è arrivata di colpo. Gli sviluppatori hanno dovuto passare al setaccio ogni singolo utilizzo nel sorgente e capire cosa avesse in mente il programmatore al momento di scriverlo. Il punto è proprio questo: la stessa funzione veniva usata per cose molto diverse tra loro. Copiare stringhe terminate dal carattere NUL, quel byte che segnala la fine di una stringa. Gestire dati binari di lunghezza fissa. Riempire i buffer con byte aggiuntivi quando serviva, il cosiddetto padding. Oppure semplici operazioni di copia della memoria.
Stando ai dati diffusi da chi ha lavorato al progetto, la rimozione ha richiesto circa 362 commit distribuiti nell’arco di 6 anni. L’ultimo passaggio ha cancellato sia l’API principale sia le implementazioni specifiche tenute in piedi da alcune architetture, tra cui x86, PowerPC, Alpha, m68k e Xtensa. Ogni sostituzione ha obbligato i manutentori a dire chiaramente quale comportamento volevano ottenere, senza ambiguità.
Togliere del tutto strncpy() dal kernel significa eliminare una categoria di errori che gli sviluppatori consideravano fin troppo frequente. Molte API storiche sopravvivono per ragioni di compatibilità, ma questo non vuol dire che siano la scelta giusta per i progetti nuovi. Il fatto che una funzione compaia negli standard del linguaggio non la rende automaticamente adatta agli attuali requisiti di sicurezza e affidabilità.
Per un progetto come Linux, che fa girare server, dispositivi embedded, infrastrutture cloud e supercomputer, un intervento così pesante e fatto a posteriori ripaga abbondantemente la fatica spesa.