brintos

brintos / linux-shallow public Read only

0
0
Text · 18.3 KiB · d4ab76e Raw
410 lines · plain
1.. SPDX-License-Identifier: GPL-2.02 3.. include:: ../disclaimer-ita.rst4 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 convenzioni12==============================================================================13 14In un mondo perfetto, sarebbe possibile prendere tutti gli usi di15un'interfaccia deprecata e convertirli in quella nuova, e così sarebbe16possibile rimuovere la vecchia interfaccia in un singolo ciclo di sviluppo.17Tuttavia, per via delle dimensioni del kernel, la gerarchia dei manutentori e18le tempistiche, non è sempre possibile fare questo tipo di conversione tutta19in una volta. Questo significa che nuove istanze di una vecchia interfaccia20potrebbero aggiungersi al kernel proprio quando si sta cercando di rimuoverle,21aumentando così il carico di lavoro. Al fine di istruire gli sviluppatori su22cosa è considerato deprecato (e perché), è stata create la seguente lista a cui23fare riferimento quando qualcuno propone modifiche che usano cose deprecate.24 25__deprecated26------------27Nonostante questo attributo marchi visibilmente un interfaccia come deprecata,28`non produce più alcun avviso durante la compilazione29<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'uso32di `__deprecated` in un file d'intestazione sia opportuno per segnare una33interfaccia come 'vecchia', questa non è una soluzione completa. L'interfaccia34deve essere rimossa dal kernel, o aggiunta a questo documento per scoraggiarne35l'uso.36 37BUG() e BUG_ON()38----------------39Al loro posto usate WARN() e WARN_ON() per gestire le40condizioni "impossibili" e gestitele come se fosse possibile farlo.41Nonostante le funzioni della famiglia BUG() siano state progettate42per asserire "situazioni impossibili" e interrompere in sicurezza un43thread del kernel, queste si sono rivelate essere troppo rischiose44(per esempio, in quale ordine rilasciare i *lock*? Ci sono stati che45sono stati ripristinati?). Molto spesso l'uso di BUG()46destabilizza il sistema o lo corrompe del tutto, il che rende47impossibile un'attività di debug o anche solo leggere un rapporto48circa l'errore.  Linus ha un'opinione molto critica al riguardo:49`email 150<https://lore.kernel.org/lkml/CA+55aFy6jNLsywVYdGp83AMrXBo_P-pkjkphPGrO=82SPKCpLQ@mail.gmail.com/>`_,51`email 252<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 essere55usato solo per situazioni che si suppone siano "impossibili".  Se56volete avvisare gli utenti riguardo a qualcosa di possibile anche se57indesiderato, usare le funzioni della famiglia pr_warn().  Chi58amministra il sistema potrebbe aver attivato l'opzione sysctl59*panic_on_warn* per essere sicuri che il sistema smetta di funzionare60in caso si verifichino delle condizioni "inaspettate". (per esempio,61date un'occhiata al questo `commit62<https://git.kernel.org/linus/d4689846881d160a4d12a514e991a740bcb5d65a>`_)63 64Calcoli codificati negli argomenti di un allocatore65----------------------------------------------------66Il calcolo dinamico delle dimensioni (specialmente le moltiplicazioni) non67dovrebbero essere fatto negli argomenti di funzioni di allocazione di memoria68(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 di70allocare può portare ad un overflow della memoria di heap e altri71malfunzionamenti. (Si fa eccezione per valori numerici per i quali il72compilatore può generare avvisi circa un potenziale overflow. Tuttavia, anche in73questi 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 usate87le funzioni del tipo *saturate-on-overflow*::88 89	bar = dma_alloc_coherent(dev, array_size(count, size), &dma, GFP_KERNEL);90 91Un altro tipico caso da evitare è quello di calcolare la dimensione di una92struttura 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 che102	  in coda contiene un array di lunghezza zero o uno, allora siete103	  invitati a riorganizzare il vostro codice usando il104	  `flexible array member <#zero-length-and-one-element-arrays>`_.105 106Per altri calcoli, usate le funzioni size_mul(), size_add(), e size_sub(). Per107esempio, 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(), ma118anche 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 volutamente125i possibili overflow, e questo può portare il chiamante a generare risultati126inaspettati. Le rispettive funzioni kstrtol(), kstrtoll(),127kstrtoul(), e kstrtoull() sono da considerarsi le corrette128sostitute; tuttavia va notato che queste richiedono che la stringa sia129terminata con il carattere NUL o quello di nuova riga.130 131strcpy()132--------133La funzione strcpy() non fa controlli agli estremi del buffer134di destinazione. Questo può portare ad un overflow oltre i limiti del135buffer e generare svariati tipi di malfunzionamenti. Nonostante l'opzione136`CONFIG_FORTIFY_SOURCE=y` e svariate opzioni del compilatore aiutano137a ridurne il rischio, non c'è alcuna buona ragione per continuare ad usare138questa funzione. La versione sicura da usare è strscpy(), tuttavia va139prestata attenzione a tutti quei casi dove viene usato il valore di140ritorno di strcpy().  La funzione strscpy() non ritorna un puntatore141alla destinazione, ma un contatore dei byte non NUL copiati (oppure142un errno negativo se la stringa è stata troncata).143 144strncpy() su stringe terminate con NUL145--------------------------------------146L'utilizzo di strncpy() non fornisce alcuna garanzia sul fatto che147il buffer di destinazione verrà terminato con il carattere NUL. Questo148potrebbe portare a diversi overflow di lettura o altri malfunzionamenti149causati, appunto, dalla mancanza del terminatore. Questa estende la150terminazione nel buffer di destinazione quando la stringa d'origine è più151corta; questo potrebbe portare ad una penalizzazione delle prestazioni per152chi usa solo stringe terminate. La versione sicura da usare è153strscpy(), tuttavia va prestata attenzione a tutti quei casi dove154viene usato il valore di ritorno di strncpy().  La funzione strscpy()155non ritorna un puntatore alla destinazione, ma un contatore dei byte156non NUL copiati (oppure un errno negativo se la stringa è stata157troncata). Tutti i casi che necessitano di estendere la158terminazione 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 essere162marchiati 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 di168origine, magari leggendo più di quanto verrà effettivamente copiato. Questo169è inefficiente e può portare a overflow di lettura quando la stringa non è170terminata con NUL. La versione sicura da usare è strscpy(), tuttavia171va prestata attenzione a tutti quei casi dove viene usato il valore di172ritorno di strlcpy(), dato che strscpy() ritorna un valore di errno173negativo quanto la stringa viene troncata.174 175Segnaposto %p nella stringa di formato176--------------------------------------177 178Tradizionalmente, l'uso del segnaposto "%p" nella stringa di formato179esponne un indirizzo di memoria in dmesg, proc, sysfs, eccetera.  Per180evitare 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 dovrebbero183essere aggiunti al kernel.  Per una rappresentazione testuale di un184indirizzo usate "%pS", l'output è migliore perché mostrerà il nome del185simbolo.  Per tutto il resto, semplicemente non usate "%p".186 187Parafrasando la `guida188<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 stesso192  è 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 un195  utente sono considerati "special"? Se pensi di poterlo giustificare196  (in un commento e nel messaggio del commit) abbastanza bene da197  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 questa201funzionalità vi sia d'ostacolo durante una sessione di debug. Per farlo202aggiungete l'opzione di debug "`no_hash_pointers203<https://git.kernel.org/linus/5ead723a20e0447bc7db33dc3070b420e5f80aa6>`_" alla204riga di comando del kernel.205 206Vettori a dimensione variabile (VLA)207------------------------------------208 209Usare VLA sullo stack produce codice molto peggiore rispetto a quando si usano210vettori 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; in212aggiunta sono anche un problema per la sicurezza. La crescita dinamica di un213vettore nello stack potrebbe eccedere la memoria rimanente in tale segmento.214Questo può portare a dei malfunzionamenti, potrebbe sovrascrivere215dati importanti alla fine dello stack (quando il kernel è compilato senza216`CONFIG_THREAD_INFO_IN_TASK=y`), o sovrascrivere un pezzo di memoria adiacente217allo stack (quando il kernel è compilato senza `CONFIG_VMAP_STACK=y`).218 219Salto implicito nell'istruzione switch-case220-------------------------------------------221 222Il linguaggio C permette ai casi di un'istruzione `switch` di saltare al223prossimo caso quando l'istruzione "break" viene omessa alla fine del caso224corrente. Tuttavia questo rende il codice ambiguo perché non è sempre ovvio se225l'istruzione "break" viene omessa intenzionalmente o è un baco. Per esempio,226osservando il seguente pezzo di codice non è chiaro se lo stato227`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'istruzione240"break" <https://cwe.mitre.org/data/definitions/484.html>`_, oggigiorno non241permettiamo più che vi sia un "salto implicito" (*fall-through*). Per242identificare un salto implicito intenzionale abbiamo adottato la pseudo243parola chiave 'fallthrough' che viene espansa nell'estensione di gcc244`__attribute__((fallthrough))` `Statement Attributes245<https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html>`_.246(Quando la sintassi C17/C18 `[[fallthrough]]` sarà più comunemente247supportata 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 dai251compilatori, 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 seguenti255modi:256 257* ``break;``258* `fallthrough;``259* ``continue;``260* ``goto <label>;``261* ``return [expression];``262 263Array di lunghezza zero o con un solo elemento264----------------------------------------------265All'interno del kernel ricorre spesso la necessita di avere membri266di dimensione variabile all'interno di una struttura dati. In questi267casi il codice del kernel dovrebbe usare sempre i `"flexible array268member" <https://en.wikipedia.org/wiki/Flexible_array_member>`_. La269tecnica degli array a lunghezza nulla o di un solo elemento non270dovrebbe essere più usata.271 272Nel codice C più vecchio, la dichiarazione di un membro di dimensione273variabile in coda ad una struttura dati veniva fatto dichiarando un274array 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 (dovrebbe282rimuovere la dimensione del singolo elemento in coda per calcolare la283dimensione esatta dell' "intestazione"). Per evitare questi problemi è284stata introdotta un' `estensione a GNU C285<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_ che286permettesse 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 dei294problemi che affliggono entrambe le tecniche: per esempio295l'impossibilità di riconoscere se un array di quel tipo viene usato296nel mezzo di una struttura dati e _non_ alla fine (potrebbe accadere297sia direttamente, sia indirettamente quando si usano le unioni o le298strutture di strutture).299 300Lo standard C99 introduce i "flexible array members". Questi array non301hanno 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 gli309elementi di lunghezza variabile in coda alle strutture dati.  Permette310al compilatore di produrre errori quando gli array flessibili non si311trovano alla fine della struttura dati, il che permette di prevenire312alcuni tipi di bachi dovuti a `comportamenti inaspettati313<https://git.kernel.org/linus/76497732932f15e7323dc805e8ea8dc11bb587cf>`_.314Inoltre, permette al compilatore di analizzare correttamente le315dimensioni degli array (attraverso sizeof(), `CONFIG_FORTIFY_SOURCE`,316e `CONFIG_UBSAN_BOUNDS`). Per esempio, non esiste alcun meccanismo in317grado di avvisarci che il seguente uso di sizeof() dia sempre come318zero 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 uno334invece si aspetterebbe che il suo valore sia la dimensione totale in335byte dell'allocazione dinamica che abbiamo appena fatto per l'array336``items``. Qui un paio di esempi reali del problema: `collegamento 1337<https://git.kernel.org/linus/f2cd32a443da694ac4e28fbf4ac6f9d5cc63a539>`_,338`collegamento 2339<https://git.kernel.org/linus/ab91c2a89f86be2898cee208d492816ec238b2cf>`_.340Invece, `i flexible array members hanno un tipo incompleto, e quindi341sizeof() non può essere applicato342<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_; dunque ogni343uso scorretto di questo operatore verrà identificato immediatamente344durante la compilazione.345 346Per quanto riguarda gli array di un solo elemento, bisogna essere347consapevoli che `questi array occupano almeno quanto lo spazio di un348singolo oggetti dello stesso tipo349<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_, e quindi350contribuiscono al calcolo della dimensione della struttura che li351contiene. In questo caso è facile commettere errori quando si vuole352calcolare la dimensione totale della memoria totale da allocare per353una 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`` in369struct_size(), altrimenti avremmo --inavvertitamente-- allocato370memoria per un oggetti ``items`` in più. Il modo più pulito e meno371propenso agli errori è quello di usare i `flexible array member`, in372combinazione 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 386Ci sono due casi speciali dove è necessario usare la macro DECLARE_FLEX_ARRAY()387(da notare che la stessa macro è chiamata __DECLARE_FLEX_ARRAY() nei file di388intestazione UAPI). Uno è quando l'array flessibile è l'unico elemento di una389struttura, e l'altro quando è parte di un unione. Per motivi non tecnici, entrambi390i casi d'uso non sono permessi dalla specifica C99. Per esempio, per391convertire il seguente codice::392 393    struct something {394        ...395        union {396            struct type1 one[0];397            struct type2 two[0];398        };399    };400 401La macro di supporto dev'essere usata::402 403    struct something {404        ...405        union {406            DECLARE_FLEX_ARRAY(struct type1, one);407            DECLARE_FLEX_ARRAY(struct type2, two);408        };409    };410