1.. SPDX-License-Identifier: GPL-2.0 2 3.. include:: ../disclaimer-ita.rst 4 5:Original: :ref:`Documentation/process/deprecated.rst <deprecated>` 6:Translator: Federico Vaga <federico.vaga@vaga.pv.it> 7 8.. _it_deprecated: 9 10============================================================================== 11Interfacce deprecate, caratteristiche del linguaggio, attributi, e convenzioni 12============================================================================== 13 14In un mondo perfetto, sarebbe possibile prendere tutti gli usi di 15un'interfaccia deprecata e convertirli in quella nuova, e così sarebbe 16possibile rimuovere la vecchia interfaccia in un singolo ciclo di sviluppo. 17Tuttavia, per via delle dimensioni del kernel, la gerarchia dei manutentori e 18le tempistiche, non è sempre possibile fare questo tipo di conversione tutta 19in una volta. Questo significa che nuove istanze di una vecchia interfaccia 20potrebbero aggiungersi al kernel proprio quando si sta cercando di rimuoverle, 21aumentando così il carico di lavoro. Al fine di istruire gli sviluppatori su 22cosa è considerato deprecato (e perché), è stata create la seguente lista a cui 23fare riferimento quando qualcuno propone modifiche che usano cose deprecate. 24 25__deprecated 26------------ 27Nonostante questo attributo marchi visibilmente un interfaccia come deprecata, 28`non produce più alcun avviso durante la compilazione 29<https://git.kernel.org/linus/771c035372a036f83353eef46dbb829780330234>`_ 30perché uno degli obiettivi del kernel è quello di compilare senza avvisi; 31inoltre, nessuno stava agendo per rimuovere queste interfacce. Nonostante l'uso 32di `__deprecated` in un file d'intestazione sia opportuno per segnare una 33interfaccia come 'vecchia', questa non è una soluzione completa. L'interfaccia 34deve essere rimossa dal kernel, o aggiunta a questo documento per scoraggiarne 35l'uso. 36 37BUG() e BUG_ON() 38---------------- 39Al loro posto usate WARN() e WARN_ON() per gestire le 40condizioni "impossibili" e gestitele come se fosse possibile farlo. 41Nonostante le funzioni della famiglia BUG() siano state progettate 42per asserire "situazioni impossibili" e interrompere in sicurezza un 43thread del kernel, queste si sono rivelate essere troppo rischiose 44(per esempio, in quale ordine rilasciare i *lock*? Ci sono stati che 45sono stati ripristinati?). Molto spesso l'uso di BUG() 46destabilizza il sistema o lo corrompe del tutto, il che rende 47impossibile un'attività di debug o anche solo leggere un rapporto 48circa l'errore. Linus ha un'opinione molto critica al riguardo: 49`email 1 50<https://lore.kernel.org/lkml/CA+55aFy6jNLsywVYdGp83AMrXBo_P-pkjkphPGrO=82SPKCpLQ@mail.gmail.com/>`_, 51`email 2 52<https://lore.kernel.org/lkml/CAHk-=whDHsbK3HTOpTF=ue_o04onRwTEaK_ZoJp_fjbqq4+=Jw@mail.gmail.com/>`_ 53 54Tenete presente che la famiglia di funzioni WARN() dovrebbe essere 55usato solo per situazioni che si suppone siano "impossibili". Se 56volete avvisare gli utenti riguardo a qualcosa di possibile anche se 57indesiderato, usare le funzioni della famiglia pr_warn(). Chi 58amministra il sistema potrebbe aver attivato l'opzione sysctl 59*panic_on_warn* per essere sicuri che il sistema smetta di funzionare 60in caso si verifichino delle condizioni "inaspettate". (per esempio, 61date un'occhiata al questo `commit 62<https://git.kernel.org/linus/d4689846881d160a4d12a514e991a740bcb5d65a>`_) 63 64Calcoli codificati negli argomenti di un allocatore 65---------------------------------------------------- 66Il calcolo dinamico delle dimensioni (specialmente le moltiplicazioni) non 67dovrebbero essere fatto negli argomenti di funzioni di allocazione di memoria 68(o simili) per via del rischio di overflow. Questo può portare a valori più 69piccoli di quelli che il chiamante si aspettava. L'uso di questo modo di 70allocare può portare ad un overflow della memoria di heap e altri 71malfunzionamenti. (Si fa eccezione per valori numerici per i quali il 72compilatore può generare avvisi circa un potenziale overflow. Tuttavia, anche in 73questi casi è preferibile riscrivere il codice come suggerito di seguito). 74 75Per esempio, non usate ``count * size`` come argomento:: 76 77 foo = kmalloc(count * size, GFP_KERNEL); 78 79Al suo posto, si dovrebbe usare l'allocatore a due argomenti:: 80 81 foo = kmalloc_array(count, size, GFP_KERNEL); 82 83Nello specifico, kmalloc() può essere sostituta da kmalloc_array(), e kzalloc() 84da kcalloc(). 85 86Se questo tipo di allocatore non è disponibile, allora dovrebbero essere usate 87le funzioni del tipo *saturate-on-overflow*:: 88 89 bar = vmalloc(array_size(count, size)); 90 91Un altro tipico caso da evitare è quello di calcolare la dimensione di una 92struttura seguita da un vettore di altre strutture, come nel seguente caso:: 93 94 header = kzalloc(sizeof(*header) + count * sizeof(*header->item), 95 GFP_KERNEL); 96 97Invece, usate la seguente funzione:: 98 99 header = kzalloc(struct_size(header, item, count), GFP_KERNEL); 100 101.. note:: Se per caso state usando struct_size() su una struttura dati che 102 in coda contiene un array di lunghezza zero o uno, allora siete 103 invitati a riorganizzare il vostro codice usando il 104 `flexible array member <#zero-length-and-one-element-arrays>`_. 105 106Per altri calcoli, usate le funzioni size_mul(), size_add(), e size_sub(). Per 107esempio, al posto di:: 108 109 foo = krealloc(current_size + chunk_size * (count - 3), GFP_KERNEL); 110 111dovreste scrivere: 112 113 foo = krealloc(size_add(current_size, 114 size_mul(chunk_size, 115 size_sub(count, 3))), GFP_KERNEL); 116 117Per maggiori dettagli fate riferimento a array3_size() e flex_array_size(), ma 118anche le funzioni della famiglia check_mul_overflow(), check_add_overflow(), 119check_sub_overflow(), e check_shl_overflow(). 120 121simple_strtol(), simple_strtoll(), simple_strtoul(), simple_strtoull() 122---------------------------------------------------------------------- 123Le funzioni simple_strtol(), simple_strtoll(), 124simple_strtoul(), e simple_strtoull() ignorano volutamente 125i possibili overflow, e questo può portare il chiamante a generare risultati 126inaspettati. Le rispettive funzioni kstrtol(), kstrtoll(), 127kstrtoul(), e kstrtoull() sono da considerarsi le corrette 128sostitute; tuttavia va notato che queste richiedono che la stringa sia 129terminata con il carattere NUL o quello di nuova riga. 130 131strcpy() 132-------- 133La funzione strcpy() non fa controlli agli estremi del buffer 134di destinazione. Questo può portare ad un overflow oltre i limiti del 135buffer e generare svariati tipi di malfunzionamenti. Nonostante l'opzione 136`CONFIG_FORTIFY_SOURCE=y` e svariate opzioni del compilatore aiutano 137a ridurne il rischio, non c'è alcuna buona ragione per continuare ad usare 138questa funzione. La versione sicura da usare è strscpy(), tuttavia va 139prestata attenzione a tutti quei casi dove viene usato il valore di 140ritorno di strcpy(). La funzione strscpy() non ritorna un puntatore 141alla destinazione, ma un contatore dei byte non NUL copiati (oppure 142un errno negativo se la stringa è stata troncata). 143 144strncpy() su stringe terminate con NUL 145-------------------------------------- 146L'utilizzo di strncpy() non fornisce alcuna garanzia sul fatto che 147il buffer di destinazione verrà terminato con il carattere NUL. Questo 148potrebbe portare a diversi overflow di lettura o altri malfunzionamenti 149causati, appunto, dalla mancanza del terminatore. Questa estende la 150terminazione nel buffer di destinazione quando la stringa d'origine è più 151corta; questo potrebbe portare ad una penalizzazione delle prestazioni per 152chi usa solo stringe terminate. La versione sicura da usare è 153strscpy(), tuttavia va prestata attenzione a tutti quei casi dove 154viene usato il valore di ritorno di strncpy(). La funzione strscpy() 155non ritorna un puntatore alla destinazione, ma un contatore dei byte 156non NUL copiati (oppure un errno negativo se la stringa è stata 157troncata). Tutti i casi che necessitano di estendere la 158terminazione con NUL dovrebbero usare strscpy_pad(). 159 160Se il chiamate no usa stringhe terminate con NUL, allore strncpy() 161può continuare ad essere usata, ma i buffer di destinazione devono essere 162marchiati con l'attributo `__nonstring <https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html>`_ 163per evitare avvisi durante la compilazione. 164 165strlcpy() 166--------- 167La funzione strlcpy(), per prima cosa, legge interamente il buffer di 168origine, magari leggendo più di quanto verrà effettivamente copiato. Questo 169è inefficiente e può portare a overflow di lettura quando la stringa non è 170terminata con NUL. La versione sicura da usare è strscpy(), tuttavia 171va prestata attenzione a tutti quei casi dove viene usato il valore di 172ritorno di strlcpy(), dato che strscpy() ritorna un valore di errno 173negativo quanto la stringa viene troncata. 174 175Segnaposto %p nella stringa di formato 176-------------------------------------- 177 178Tradizionalmente, l'uso del segnaposto "%p" nella stringa di formato 179esponne un indirizzo di memoria in dmesg, proc, sysfs, eccetera. Per 180evitare che questi indirizzi vengano sfruttati da malintenzionati, 181tutto gli usi di "%p" nel kernel rappresentano l'hash dell'indirizzo, 182rendendolo di fatto inutilizzabile. Nuovi usi di "%p" non dovrebbero 183essere aggiunti al kernel. Per una rappresentazione testuale di un 184indirizzo usate "%pS", l'output è migliore perché mostrerà il nome del 185simbolo. Per tutto il resto, semplicemente non usate "%p". 186 187Parafrasando la `guida 188<https://lore.kernel.org/lkml/CA+55aFwQEd_d40g4mUCSsVRZzrFPUJt74vc6PPpb675hYNXcKw@mail.gmail.com/>`_ 189di Linus: 190 191- Se il valore hash di "%p" è inutile, chiediti se il puntatore stesso 192 è importante. Forse dovrebbe essere rimosso del tutto? 193- Se credi davvero che il vero valore del puntatore sia importante, 194 perché alcuni stati del sistema o i livelli di privilegi di un 195 utente sono considerati "special"? Se pensi di poterlo giustificare 196 (in un commento e nel messaggio del commit) abbastanza bene da 197 affrontare il giudizio di Linus, allora forse potrai usare "%px", 198 assicurandosi anche di averne il permesso. 199 200Potete disabilitare temporaneamente l'hashing di "%p" nel caso in cui questa 201funzionalità vi sia d'ostacolo durante una sessione di debug. Per farlo 202aggiungete l'opzione di debug "`no_hash_pointers 203<https://git.kernel.org/linus/5ead723a20e0447bc7db33dc3070b420e5f80aa6>`_" alla 204riga di comando del kernel. 205 206Vettori a dimensione variabile (VLA) 207------------------------------------ 208 209Usare VLA sullo stack produce codice molto peggiore rispetto a quando si usano 210vettori a dimensione fissa. Questi `problemi di prestazioni <https://git.kernel.org/linus/02361bc77888>`_, 211tutt'altro che banali, sono già un motivo valido per eliminare i VLA; in 212aggiunta sono anche un problema per la sicurezza. La crescita dinamica di un 213vettore nello stack potrebbe eccedere la memoria rimanente in tale segmento. 214Questo può portare a dei malfunzionamenti, potrebbe sovrascrivere 215dati importanti alla fine dello stack (quando il kernel è compilato senza 216`CONFIG_THREAD_INFO_IN_TASK=y`), o sovrascrivere un pezzo di memoria adiacente 217allo stack (quando il kernel è compilato senza `CONFIG_VMAP_STACK=y`). 218 219Salto implicito nell'istruzione switch-case 220------------------------------------------- 221 222Il linguaggio C permette ai casi di un'istruzione `switch` di saltare al 223prossimo caso quando l'istruzione "break" viene omessa alla fine del caso 224corrente. Tuttavia questo rende il codice ambiguo perché non è sempre ovvio se 225l'istruzione "break" viene omessa intenzionalmente o è un baco. Per esempio, 226osservando il seguente pezzo di codice non è chiaro se lo stato 227`STATE_ONE` è stato progettato apposta per eseguire anche `STATE_TWO`:: 228 229 switch (value) { 230 case STATE_ONE: 231 do_something(); 232 case STATE_TWO: 233 do_other(); 234 break; 235 default: 236 WARN("unknown state"); 237 } 238 239Dato che c'è stata una lunga lista di problemi `dovuti alla mancanza dell'istruzione 240"break" <https://cwe.mitre.org/data/definitions/484.html>`_, oggigiorno non 241permettiamo più che vi sia un "salto implicito" (*fall-through*). Per 242identificare un salto implicito intenzionale abbiamo adottato la pseudo 243parola chiave 'fallthrough' che viene espansa nell'estensione di gcc 244`__attribute__((fallthrough))` `Statement Attributes 245<https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html>`_. 246(Quando la sintassi C17/C18 `[[fallthrough]]` sarà più comunemente 247supportata dai compilatori C, analizzatori statici, e dagli IDE, 248allora potremo usare quella sintassi per la pseudo parola chiave) 249 250Quando la sintassi [[fallthrough]] sarà più comunemente supportata dai 251compilatori, analizzatori statici, e ambienti di sviluppo IDE, 252allora potremo usarla anche noi. 253 254Ne consegue che tutti i blocchi switch/case devono finire in uno dei seguenti 255modi: 256 257* ``break;`` 258* `fallthrough;`` 259* ``continue;`` 260* ``goto <label>;`` 261* ``return [expression];`` 262 263Array di lunghezza zero o con un solo elemento 264---------------------------------------------- 265All'interno del kernel ricorre spesso la necessita di avere membri 266di dimensione variabile all'interno di una struttura dati. In questi 267casi il codice del kernel dovrebbe usare sempre i `"flexible array 268member" <https://en.wikipedia.org/wiki/Flexible_array_member>`_. La 269tecnica degli array a lunghezza nulla o di un solo elemento non 270dovrebbe essere più usata. 271 272Nel codice C più vecchio, la dichiarazione di un membro di dimensione 273variabile in coda ad una struttura dati veniva fatto dichiarando un 274array di un solo elemento posizionato alla fine della struttura dati:: 275 276 struct something { 277 size_t count; 278 struct foo items[1]; 279 }; 280 281Questo ha portato ad un calcolo di sizeof() traballante (dovrebbe 282rimuovere la dimensione del singolo elemento in coda per calcolare la 283dimensione esatta dell' "intestazione"). Per evitare questi problemi è 284stata introdotta un' `estensione a GNU C 285<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_ che 286permettesse la dichiarazione di array a lungezza zero:: 287 288 struct something { 289 size_t count; 290 struct foo items[0]; 291 }; 292 293Ma questo ha portato nuovi problemi, e non ha risolto alcuni dei 294problemi che affliggono entrambe le tecniche: per esempio 295l'impossibilità di riconoscere se un array di quel tipo viene usato 296nel mezzo di una struttura dati e _non_ alla fine (potrebbe accadere 297sia direttamente, sia indirettamente quando si usano le unioni o le 298strutture di strutture). 299 300Lo standard C99 introduce i "flexible array members". Questi array non 301hanno una dimensione nella loro dichiarazione:: 302 303 struct something { 304 size_t count; 305 struct foo items[]; 306 }; 307 308Questo è il modo con cui ci si aspetta che vengano dichiarati gli 309elementi di lunghezza variabile in coda alle strutture dati. Permette 310al compilatore di produrre errori quando gli array flessibili non si 311trovano alla fine della struttura dati, il che permette di prevenire 312alcuni tipi di bachi dovuti a `comportamenti inaspettati 313<https://git.kernel.org/linus/76497732932f15e7323dc805e8ea8dc11bb587cf>`_. 314Inoltre, permette al compilatore di analizzare correttamente le 315dimensioni degli array (attraverso sizeof(), `CONFIG_FORTIFY_SOURCE`, 316e `CONFIG_UBSAN_BOUNDS`). Per esempio, non esiste alcun meccanismo in 317grado di avvisarci che il seguente uso di sizeof() dia sempre come 318zero come risultato:: 319 320 struct something { 321 size_t count; 322 struct foo items[0]; 323 }; 324 325 struct something *instance; 326 327 instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL); 328 instance->count = count; 329 330 size = sizeof(instance->items) * instance->count; 331 memcpy(instance->items, source, size); 332 333Il valore di ``size`` nell'ultima riga sarà ``zero``, quando uno 334invece si aspetterebbe che il suo valore sia la dimensione totale in 335byte dell'allocazione dynamica che abbiamo appena fatto per l'array 336``items``. Qui un paio di esempi reali del problema: `collegamento 1 337<https://git.kernel.org/linus/f2cd32a443da694ac4e28fbf4ac6f9d5cc63a539>`_, 338`collegamento 2 339<https://git.kernel.org/linus/ab91c2a89f86be2898cee208d492816ec238b2cf>`_. 340Invece, `i flexible array members hanno un tipo incompleto, e quindi 341sizeof() non può essere applicato 342<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_; dunque ogni 343uso scorretto di questo operatore verrà identificato immediatamente 344durante la compilazione. 345 346Per quanto riguarda gli array di un solo elemento, bisogna essere 347consapevoli che `questi array occupano almeno quanto lo spazio di un 348singolo oggetti dello stesso tipo 349<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_, e quindi 350contribuiscono al calcolo della dimensione della struttura che li 351contiene. In questo caso è facile commettere errori quando si vuole 352calcolare la dimensione totale della memoria totale da allocare per 353una struttura dati:: 354 355 struct something { 356 size_t count; 357 struct foo items[1]; 358 }; 359 360 struct something *instance; 361 362 instance = kmalloc(struct_size(instance, items, count - 1), GFP_KERNEL); 363 instance->count = count; 364 365 size = sizeof(instance->items) * instance->count; 366 memcpy(instance->items, source, size); 367 368In questo esempio ci siamo dovuti ricordare di usare ``count - 1`` in 369struct_size(), altrimenti avremmo --inavvertitamente-- allocato 370memoria per un oggetti ``items`` in più. Il modo più pulito e meno 371propenso agli errori è quello di usare i `flexible array member`, in 372combinazione con struct_size() e flex_array_size():: 373 374 struct something { 375 size_t count; 376 struct foo items[]; 377 }; 378 379 struct something *instance; 380 381 instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL); 382 instance->count = count; 383 384 memcpy(instance->items, source, flex_array_size(instance, items, instance->count)); 385