← Documents Documentation/translations/it_IT/process/deprecated.rst GitHub 원문 ↗

Linux 6.18.37 · Translations

Deprecated interface와 coding pattern

Kernel에서 피해야 할 BUG(), unsafe string API, allocator 산술, VLA, switch fall-through와 구식 array 관용구를 설명합니다.

Source pathDocumentation/translations/it_IT/process/deprecated.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

deprecated.rst:1-409

새 kernel code에서 사용하지 않아야 할 interface와 language pattern, 그 위험과 안전한 대체 방법을 실제 코드 예제로 정리합니다.

`BUG()`·`BUG_ON()` 대신 복구 가능한 error path, overflow-safe allocation helper, `kstrto*()`·`strscpy()`, 명시적 pointer format, flexible array와 `struct_size()`·`DECLARE_FLEX_ARRAY()`를 권장합니다.

같은 v6.18.37 영어 정식 문서의 마지막 `kmalloc_obj` 계열 절은 이탈리아어판에 없으므로 로컬 원문 우선 원칙에 따라 전문 번역에는 넣지 않고 버전 차이로만 기록합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
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 ==============================================================================
11 Interfacce deprecate, caratteristiche del linguaggio, attributi, e convenzioni
12 ==============================================================================
13
14 In un mondo perfetto, sarebbe possibile prendere tutti gli usi di
15 un'interfaccia deprecata e convertirli in quella nuova, e così sarebbe
16 possibile rimuovere la vecchia interfaccia in un singolo ciclo di sviluppo.
17 Tuttavia, per via delle dimensioni del kernel, la gerarchia dei manutentori e
18 le tempistiche, non è sempre possibile fare questo tipo di conversione tutta
19 in una volta. Questo significa che nuove istanze di una vecchia interfaccia
20 potrebbero aggiungersi al kernel proprio quando si sta cercando di rimuoverle,
21 aumentando così il carico di lavoro. Al fine di istruire gli sviluppatori su
22 cosa è considerato deprecato (e perché), è stata create la seguente lista a cui
23 fare riferimento quando qualcuno propone modifiche che usano cose deprecate.
24
25 __deprecated
26 ------------
27 Nonostante questo attributo marchi visibilmente un interfaccia come deprecata,
28 `non produce più alcun avviso durante la compilazione
29 <https://git.kernel.org/linus/771c035372a036f83353eef46dbb829780330234>`_
30 perché uno degli obiettivi del kernel è quello di compilare senza avvisi;
31 inoltre, nessuno stava agendo per rimuovere queste interfacce. Nonostante l'uso
32 di `__deprecated` in un file d'intestazione sia opportuno per segnare una
33 interfaccia come 'vecchia', questa non è una soluzione completa. L'interfaccia
34 deve essere rimossa dal kernel, o aggiunta a questo documento per scoraggiarne
35 l'uso.
36
37 BUG() e BUG_ON()
38 ----------------
39 Al loro posto usate WARN() e WARN_ON() per gestire le
40 condizioni "impossibili" e gestitele come se fosse possibile farlo.
41 Nonostante le funzioni della famiglia BUG() siano state progettate
42 per asserire "situazioni impossibili" e interrompere in sicurezza un
43 thread del kernel, queste si sono rivelate essere troppo rischiose
44 (per esempio, in quale ordine rilasciare i *lock*? Ci sono stati che
45 sono stati ripristinati?). Molto spesso l'uso di BUG()
46 destabilizza il sistema o lo corrompe del tutto, il che rende
47 impossibile un'attività di debug o anche solo leggere un rapporto
48 circa 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
54 Tenete presente che la famiglia di funzioni WARN() dovrebbe essere
55 usato solo per situazioni che si suppone siano "impossibili". Se
56 volete avvisare gli utenti riguardo a qualcosa di possibile anche se
57 indesiderato, usare le funzioni della famiglia pr_warn(). Chi
58 amministra il sistema potrebbe aver attivato l'opzione sysctl
59 *panic_on_warn* per essere sicuri che il sistema smetta di funzionare
60 in caso si verifichino delle condizioni "inaspettate". (per esempio,
61 date un'occhiata al questo `commit
62 <https://git.kernel.org/linus/d4689846881d160a4d12a514e991a740bcb5d65a>`_)
63
64 Calcoli codificati negli argomenti di un allocatore
65 ----------------------------------------------------
66 Il calcolo dinamico delle dimensioni (specialmente le moltiplicazioni) non
67 dovrebbero 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ù
69 piccoli di quelli che il chiamante si aspettava. L'uso di questo modo di
70 allocare può portare ad un overflow della memoria di heap e altri
71 malfunzionamenti. (Si fa eccezione per valori numerici per i quali il
72 compilatore può generare avvisi circa un potenziale overflow. Tuttavia, anche in
73 questi casi è preferibile riscrivere il codice come suggerito di seguito).
74
75 Per esempio, non usate ``count * size`` come argomento::
76
77 foo = kmalloc(count * size, GFP_KERNEL);
78
79 Al suo posto, si dovrebbe usare l'allocatore a due argomenti::
80
81 foo = kmalloc_array(count, size, GFP_KERNEL);
82
83 Nello specifico, kmalloc() può essere sostituta da kmalloc_array(), e kzalloc()
84 da kcalloc().
85
86 Se questo tipo di allocatore non è disponibile, allora dovrebbero essere usate
87 le funzioni del tipo *saturate-on-overflow*::
88
89 bar = dma_alloc_coherent(dev, array_size(count, size), &dma, GFP_KERNEL);
90
91 Un altro tipico caso da evitare è quello di calcolare la dimensione di una
92 struttura 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
97 Invece, 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
106 Per altri calcoli, usate le funzioni size_mul(), size_add(), e size_sub(). Per
107 esempio, al posto di::
108
109 foo = krealloc(current_size + chunk_size * (count - 3), GFP_KERNEL);
110
111 dovreste scrivere:
112
113 foo = krealloc(size_add(current_size,
114 size_mul(chunk_size,
115 size_sub(count, 3))), GFP_KERNEL);
116
117 Per maggiori dettagli fate riferimento a array3_size() e flex_array_size(), ma
118 anche le funzioni della famiglia check_mul_overflow(), check_add_overflow(),
119 check_sub_overflow(), e check_shl_overflow().
120
121 simple_strtol(), simple_strtoll(), simple_strtoul(), simple_strtoull()
122 ----------------------------------------------------------------------
123 Le funzioni simple_strtol(), simple_strtoll(),
124 simple_strtoul(), e simple_strtoull() ignorano volutamente
125 i possibili overflow, e questo può portare il chiamante a generare risultati
126 inaspettati. Le rispettive funzioni kstrtol(), kstrtoll(),
127 kstrtoul(), e kstrtoull() sono da considerarsi le corrette
128 sostitute; tuttavia va notato che queste richiedono che la stringa sia
129 terminata con il carattere NUL o quello di nuova riga.
130
131 strcpy()
132 --------
133 La funzione strcpy() non fa controlli agli estremi del buffer
134 di destinazione. Questo può portare ad un overflow oltre i limiti del
135 buffer e generare svariati tipi di malfunzionamenti. Nonostante l'opzione
136 `CONFIG_FORTIFY_SOURCE=y` e svariate opzioni del compilatore aiutano
137 a ridurne il rischio, non c'è alcuna buona ragione per continuare ad usare
138 questa funzione. La versione sicura da usare è strscpy(), tuttavia va
139 prestata attenzione a tutti quei casi dove viene usato il valore di
140 ritorno di strcpy(). La funzione strscpy() non ritorna un puntatore
141 alla destinazione, ma un contatore dei byte non NUL copiati (oppure
142 un errno negativo se la stringa è stata troncata).
143
144 strncpy() su stringe terminate con NUL
145 --------------------------------------
146 L'utilizzo di strncpy() non fornisce alcuna garanzia sul fatto che
147 il buffer di destinazione verrà terminato con il carattere NUL. Questo
148 potrebbe portare a diversi overflow di lettura o altri malfunzionamenti
149 causati, appunto, dalla mancanza del terminatore. Questa estende la
150 terminazione nel buffer di destinazione quando la stringa d'origine è più
151 corta; questo potrebbe portare ad una penalizzazione delle prestazioni per
152 chi usa solo stringe terminate. La versione sicura da usare è
153 strscpy(), tuttavia va prestata attenzione a tutti quei casi dove
154 viene usato il valore di ritorno di strncpy(). La funzione strscpy()
155 non ritorna un puntatore alla destinazione, ma un contatore dei byte
156 non NUL copiati (oppure un errno negativo se la stringa è stata
157 troncata). Tutti i casi che necessitano di estendere la
158 terminazione con NUL dovrebbero usare strscpy_pad().
159
160 Se il chiamate no usa stringhe terminate con NUL, allore strncpy()
161 può continuare ad essere usata, ma i buffer di destinazione devono essere
162 marchiati con l'attributo `__nonstring <https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html>`_
163 per evitare avvisi durante la compilazione.
164
165 strlcpy()
166 ---------
167 La funzione strlcpy(), per prima cosa, legge interamente il buffer di
168 origine, magari leggendo più di quanto verrà effettivamente copiato. Questo
169 è inefficiente e può portare a overflow di lettura quando la stringa non è
170 terminata con NUL. La versione sicura da usare è strscpy(), tuttavia
171 va prestata attenzione a tutti quei casi dove viene usato il valore di
172 ritorno di strlcpy(), dato che strscpy() ritorna un valore di errno
173 negativo quanto la stringa viene troncata.
174
175 Segnaposto %p nella stringa di formato
176 --------------------------------------
177
178 Tradizionalmente, l'uso del segnaposto "%p" nella stringa di formato
179 esponne un indirizzo di memoria in dmesg, proc, sysfs, eccetera. Per
180 evitare che questi indirizzi vengano sfruttati da malintenzionati,
181 tutto gli usi di "%p" nel kernel rappresentano l'hash dell'indirizzo,
182 rendendolo di fatto inutilizzabile. Nuovi usi di "%p" non dovrebbero
183 essere aggiunti al kernel. Per una rappresentazione testuale di un
184 indirizzo usate "%pS", l'output è migliore perché mostrerà il nome del
185 simbolo. Per tutto il resto, semplicemente non usate "%p".
186
187 Parafrasando la `guida
188 <https://lore.kernel.org/lkml/CA+55aFwQEd_d40g4mUCSsVRZzrFPUJt74vc6PPpb675hYNXcKw@mail.gmail.com/>`_
189 di 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
200 Potete disabilitare temporaneamente l'hashing di "%p" nel caso in cui questa
201 funzionalità vi sia d'ostacolo durante una sessione di debug. Per farlo
202 aggiungete l'opzione di debug "`no_hash_pointers
203 <https://git.kernel.org/linus/5ead723a20e0447bc7db33dc3070b420e5f80aa6>`_" alla
204 riga di comando del kernel.
205
206 Vettori a dimensione variabile (VLA)
207 ------------------------------------
208
209 Usare VLA sullo stack produce codice molto peggiore rispetto a quando si usano
210 vettori a dimensione fissa. Questi `problemi di prestazioni <https://git.kernel.org/linus/02361bc77888>`_,
211 tutt'altro che banali, sono già un motivo valido per eliminare i VLA; in
212 aggiunta sono anche un problema per la sicurezza. La crescita dinamica di un
213 vettore nello stack potrebbe eccedere la memoria rimanente in tale segmento.
214 Questo può portare a dei malfunzionamenti, potrebbe sovrascrivere
215 dati importanti alla fine dello stack (quando il kernel è compilato senza
216 `CONFIG_THREAD_INFO_IN_TASK=y`), o sovrascrivere un pezzo di memoria adiacente
217 allo stack (quando il kernel è compilato senza `CONFIG_VMAP_STACK=y`).
218
219 Salto implicito nell'istruzione switch-case
220 -------------------------------------------
221
222 Il linguaggio C permette ai casi di un'istruzione `switch` di saltare al
223 prossimo caso quando l'istruzione "break" viene omessa alla fine del caso
224 corrente. Tuttavia questo rende il codice ambiguo perché non è sempre ovvio se
225 l'istruzione "break" viene omessa intenzionalmente o è un baco. Per esempio,
226 osservando 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
239 Dato 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
241 permettiamo più che vi sia un "salto implicito" (*fall-through*). Per
242 identificare un salto implicito intenzionale abbiamo adottato la pseudo
243 parola 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
247 supportata dai compilatori C, analizzatori statici, e dagli IDE,
248 allora potremo usare quella sintassi per la pseudo parola chiave)
249
250 Quando la sintassi [[fallthrough]] sarà più comunemente supportata dai
251 compilatori, analizzatori statici, e ambienti di sviluppo IDE,
252 allora potremo usarla anche noi.
253
254 Ne consegue che tutti i blocchi switch/case devono finire in uno dei seguenti
255 modi:
256
257 * ``break;``
258 * `fallthrough;``
259 * ``continue;``
260 * ``goto <label>;``
261 * ``return [expression];``
262
263 Array di lunghezza zero o con un solo elemento
264 ----------------------------------------------
265 All'interno del kernel ricorre spesso la necessita di avere membri
266 di dimensione variabile all'interno di una struttura dati. In questi
267 casi il codice del kernel dovrebbe usare sempre i `"flexible array
268 member" <https://en.wikipedia.org/wiki/Flexible_array_member>`_. La
269 tecnica degli array a lunghezza nulla o di un solo elemento non
270 dovrebbe essere più usata.
271
272 Nel codice C più vecchio, la dichiarazione di un membro di dimensione
273 variabile in coda ad una struttura dati veniva fatto dichiarando un
274 array 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
281 Questo ha portato ad un calcolo di sizeof() traballante (dovrebbe
282 rimuovere la dimensione del singolo elemento in coda per calcolare la
283 dimensione esatta dell' "intestazione"). Per evitare questi problemi è
284 stata introdotta un' `estensione a GNU C
285 <https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_ che
286 permettesse la dichiarazione di array a lungezza zero::
287
288 struct something {
289 size_t count;
290 struct foo items[0];
291 };
292
293 Ma questo ha portato nuovi problemi, e non ha risolto alcuni dei
294 problemi che affliggono entrambe le tecniche: per esempio
295 l'impossibilità di riconoscere se un array di quel tipo viene usato
296 nel mezzo di una struttura dati e _non_ alla fine (potrebbe accadere
297 sia direttamente, sia indirettamente quando si usano le unioni o le
298 strutture di strutture).
299
300 Lo standard C99 introduce i "flexible array members". Questi array non
301 hanno una dimensione nella loro dichiarazione::
302
303 struct something {
304 size_t count;
305 struct foo items[];
306 };
307
308 Questo è il modo con cui ci si aspetta che vengano dichiarati gli
309 elementi di lunghezza variabile in coda alle strutture dati. Permette
310 al compilatore di produrre errori quando gli array flessibili non si
311 trovano alla fine della struttura dati, il che permette di prevenire
312 alcuni tipi di bachi dovuti a `comportamenti inaspettati
313 <https://git.kernel.org/linus/76497732932f15e7323dc805e8ea8dc11bb587cf>`_.
314 Inoltre, permette al compilatore di analizzare correttamente le
315 dimensioni degli array (attraverso sizeof(), `CONFIG_FORTIFY_SOURCE`,
316 e `CONFIG_UBSAN_BOUNDS`). Per esempio, non esiste alcun meccanismo in
317 grado di avvisarci che il seguente uso di sizeof() dia sempre come
318 zero 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
333 Il valore di ``size`` nell'ultima riga sarà ``zero``, quando uno
334 invece si aspetterebbe che il suo valore sia la dimensione totale in
335 byte dell'allocazione dinamica 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>`_.
340 Invece, `i flexible array members hanno un tipo incompleto, e quindi
341 sizeof() non può essere applicato
342 <https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_; dunque ogni
343 uso scorretto di questo operatore verrà identificato immediatamente
344 durante la compilazione.
345
346 Per quanto riguarda gli array di un solo elemento, bisogna essere
347 consapevoli che `questi array occupano almeno quanto lo spazio di un
348 singolo oggetti dello stesso tipo
349 <https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_, e quindi
350 contribuiscono al calcolo della dimensione della struttura che li
351 contiene. In questo caso è facile commettere errori quando si vuole
352 calcolare la dimensione totale della memoria totale da allocare per
353 una 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
368 In questo esempio ci siamo dovuti ricordare di usare ``count - 1`` in
369 struct_size(), altrimenti avremmo --inavvertitamente-- allocato
370 memoria per un oggetti ``items`` in più. Il modo più pulito e meno
371 propenso agli errori è quello di usare i `flexible array member`, in
372 combinazione 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
386 Ci 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 di
388 intestazione UAPI). Uno è quando l'array flessibile è l'unico elemento di una
389 struttura, e l'altro quando è parte di un unione. Per motivi non tecnici, entrambi
390 i casi d'uso non sono permessi dalla specifica C99. Per esempio, per
391 convertire il seguente codice::
392
393 struct something {
394 ...
395 union {
396 struct type1 one[0];
397 struct type2 two[0];
398 };
399 };
400
401 La 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

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Deprecated pattern 목록이 필요한 이유

1-24

이상적으로는 deprecated API의 모든 사용을 새 API로 바꾸고 한 development cycle 안에 old API를 완전히 제거할 수 있어야 한다. 그러나 kernel 규모, maintainer hierarchy, 일정 때문에 한 번에 전환하기 어려운 경우가 많다.

기존 사용을 제거하는 동안 새 사용이 들어오면 API 제거 작업량이 오히려 늘어난다. 무엇이 왜 deprecated되었는지 developer에게 알리고 새 사용이 kernel inclusion 대상으로 제안될 때 가리킬 기준으로 이 목록을 만들었다.

.. SPDX-License-Identifier: GPL-2.0

.. include:: ../disclaimer-ita.rst

:Original: :ref:`Documentation/process/deprecated.rst <deprecated>`
:Translator: Federico Vaga <federico.vaga@vaga.pv.it>

.. _it_deprecated:

==============================================================================
Interfacce deprecate, caratteristiche del linguaggio, attributi, e convenzioni
==============================================================================

In un mondo perfetto, sarebbe possibile prendere tutti gli usi di
un'interfaccia deprecata e convertirli in quella nuova, e così sarebbe
possibile rimuovere la vecchia interfaccia in un singolo ciclo di sviluppo.
Tuttavia, per via delle dimensioni del kernel, la gerarchia dei manutentori e
le tempistiche, non è sempre possibile fare questo tipo di conversione tutta
in una volta. Questo significa che nuove istanze di una vecchia interfaccia
potrebbero aggiungersi al kernel proprio quando si sta cercando di rimuoverle,
aumentando così il carico di lavoro. Al fine di istruire gli sviluppatori su
cosa è considerato deprecato (e perché), è stata create la seguente lista a cui
fare riferimento quando qualcuno propone modifiche che usano cose deprecate.

__deprecated attribute의 한계

25-36

__deprecated는 interface가 deprecated되었음을 source에서 시각적으로 표시하지만 이제 build warning을 생성하지 않는다. Kernel은 warning 없이 build하는 것을 목표로 하고, warning이 있어도 실제로 old interface를 제거하는 사람이 없었기 때문이다.

Header에서 old API를 표시하는 데에는 유용하지만 완전한 해결책은 아니다. Interface를 kernel에서 완전히 제거하거나 이 문서에 추가하여 향후 사용을 막아야 한다.

__deprecated
------------
Nonostante questo attributo marchi visibilmente un interfaccia come deprecata,
`non produce più alcun avviso durante la compilazione
<https://git.kernel.org/linus/771c035372a036f83353eef46dbb829780330234>`_
perché uno degli obiettivi del kernel è quello di compilare senza avvisi;
inoltre, nessuno stava agendo per rimuovere queste interfacce. Nonostante l'uso
di `__deprecated` in un file d'intestazione sia opportuno per segnare una
interfaccia come 'vecchia', questa non è una soluzione completa. L'interfaccia
deve essere rimossa dal kernel, o aggiunta a questo documento per scoraggiarne
l'uso.

BUG()와 BUG_ON() 대신 복구 가능한 처리

37-63

BUG()와 BUG_ON() 대신 WARN()과 WARN_ON()을 사용하고 “불가능한” error condition도 가능한 한 정상적으로 처리한다. BUG family는 impossible-state assertion과 kernel thread의 안전한 종료를 목표로 했지만 실제로는 lock 해제 순서와 state 복원 여부를 보장하기 어려워 위험하다.

BUG()는 흔히 system을 불안정하게 하거나 완전히 망가뜨려 debugging과 유효한 crash report 수집까지 불가능하게 한다.

WARN family는 도달하지 않을 것으로 예상하는 상황에만 사용한다. 도달 가능하지만 바람직하지 않은 상황을 알리려면 pr_warn family를 사용한다. System owner가 unreachable condition 뒤 실행을 계속하지 않도록 panic_on_warn sysctl을 설정했을 수 있다.

BUG() e BUG_ON()
----------------
Al loro posto usate WARN() e WARN_ON() per gestire le
condizioni "impossibili" e gestitele come se fosse possibile farlo.
Nonostante le funzioni della famiglia BUG() siano state progettate
per asserire "situazioni impossibili" e interrompere in sicurezza un
thread del kernel, queste si sono rivelate essere troppo rischiose
(per esempio, in quale ordine rilasciare i *lock*? Ci sono stati che
sono stati ripristinati?). Molto spesso l'uso di BUG()
destabilizza il sistema o lo corrompe del tutto, il che rende
impossibile un'attività di debug o anche solo leggere un rapporto
circa l'errore.  Linus ha un'opinione molto critica al riguardo:
`email 1
<https://lore.kernel.org/lkml/CA+55aFy6jNLsywVYdGp83AMrXBo_P-pkjkphPGrO=82SPKCpLQ@mail.gmail.com/>`_,
`email 2
<https://lore.kernel.org/lkml/CAHk-=whDHsbK3HTOpTF=ue_o04onRwTEaK_ZoJp_fjbqq4+=Jw@mail.gmail.com/>`_

Tenete presente che la famiglia di funzioni WARN() dovrebbe essere
usato solo per situazioni che si suppone siano "impossibili".  Se
volete avvisare gli utenti riguardo a qualcosa di possibile anche se
indesiderato, usare le funzioni della famiglia pr_warn().  Chi
amministra il sistema potrebbe aver attivato l'opzione sysctl
*panic_on_warn* per essere sicuri che il sistema smetta di funzionare
in caso si verifichino delle condizioni "inaspettate". (per esempio,
date un'occhiata al questo `commit
<https://git.kernel.org/linus/d4689846881d160a4d12a514e991a740bcb5d65a>`_)

Allocator argument의 open-coded arithmetic

64-120

Dynamic size 계산, 특히 multiplication을 memory allocator argument 안에서 직접 수행하면 overflow로 값이 wrap되어 caller 예상보다 작은 allocation이 생길 수 있다. 이를 사용하면 heap linear overflow와 다른 잘못된 동작이 발생한다. Compiler가 overflow를 경고할 수 있는 literal은 예외일 수 있지만 이 경우도 helper로 바꾸는 편이 낫다.

count * size를 직접 넘기지 않는다.

/* 나쁜 예 */
foo = kmalloc(count * size, GFP_KERNEL);

/* 권장 */
foo = kmalloc_array(count, size, GFP_KERNEL);

kmalloc()은 kmalloc_array(), kzalloc()은 kcalloc()로 교체한다. Two-factor allocator가 없으면 overflow 시 saturate하는 helper를 사용한다.

bar = dma_alloc_coherent(dev, array_size(count, size), &dma,
                         GFP_KERNEL);

Trailing array가 있는 structure 크기도 sizeof와 multiplication으로 직접 계산하지 않는다.

/* 나쁜 예 */
header = kzalloc(sizeof(*header) + count * sizeof(*header->item),
                 GFP_KERNEL);

/* 권장 */
header = kzalloc(struct_size(header, item, count), GFP_KERNEL);

Zero-length 또는 one-element trailing array를 가진 structure에 struct_size()를 사용 중이라면 flexible array member로 먼저 전환한다.

다른 계산은 size_mul(), size_add(), size_sub()를 조합한다.

/* 나쁜 예 */
foo = krealloc(current_size + chunk_size * (count - 3), GFP_KERNEL);

/* 권장 */
foo = krealloc(size_add(current_size,
                        size_mul(chunk_size,
                                 size_sub(count, 3))), GFP_KERNEL);

그 밖에 array3_size(), flex_array_size(), check_mul_overflow(), check_add_overflow(), check_sub_overflow(), check_shl_overflow() family도 사용한다.

Calcoli codificati negli argomenti di un allocatore
----------------------------------------------------
Il calcolo dinamico delle dimensioni (specialmente le moltiplicazioni) non
dovrebbero essere fatto negli argomenti di funzioni di allocazione di memoria
(o simili) per via del rischio di overflow. Questo può portare a valori più
piccoli di quelli che il chiamante si aspettava. L'uso di questo modo di
allocare può portare ad un overflow della memoria di heap e altri
malfunzionamenti. (Si fa eccezione per valori numerici per i quali il
compilatore può generare avvisi circa un potenziale overflow. Tuttavia, anche in
questi casi è preferibile riscrivere il codice come suggerito di seguito).

Per esempio, non usate ``count * size`` come argomento::

	foo = kmalloc(count * size, GFP_KERNEL);

Al suo posto, si dovrebbe usare l'allocatore a due argomenti::

	foo = kmalloc_array(count, size, GFP_KERNEL);

Nello specifico, kmalloc() può essere sostituta da kmalloc_array(), e kzalloc()
da kcalloc().

Se questo tipo di allocatore non è disponibile, allora dovrebbero essere usate
le funzioni del tipo *saturate-on-overflow*::

	bar = dma_alloc_coherent(dev, array_size(count, size), &dma, GFP_KERNEL);

Un altro tipico caso da evitare è quello di calcolare la dimensione di una
struttura seguita da un vettore di altre strutture, come nel seguente caso::

	header = kzalloc(sizeof(*header) + count * sizeof(*header->item),
			 GFP_KERNEL);

Invece, usate la seguente funzione::

	header = kzalloc(struct_size(header, item, count), GFP_KERNEL);

.. note:: Se per caso state usando struct_size() su una struttura dati che
	  in coda contiene un array di lunghezza zero o uno, allora siete
	  invitati a riorganizzare il vostro codice usando il
	  `flexible array member <#zero-length-and-one-element-arrays>`_.

Per altri calcoli, usate le funzioni size_mul(), size_add(), e size_sub(). Per
esempio, al posto di::

       foo = krealloc(current_size + chunk_size * (count - 3), GFP_KERNEL);

dovreste scrivere:

       foo = krealloc(size_add(current_size,
                               size_mul(chunk_size,
                                        size_sub(count, 3))), GFP_KERNEL);

Per maggiori dettagli fate riferimento a array3_size() e flex_array_size(), ma
anche le funzioni della famiglia check_mul_overflow(), check_add_overflow(),
check_sub_overflow(), e check_shl_overflow().

simple_strto*()와 string copy API

121-174

simple_strtol(), simple_strtoll(), simple_strtoul(), simple_strtoull()은 overflow를 명시적으로 무시하므로 caller가 예상하지 못한 결과를 받을 수 있다. 각각 kstrtol(), kstrtoll(), kstrtoul(), kstrtoull()로 교체한다. 새 API는 string이 NUL 또는 newline으로 끝나야 한다.

strcpy()

strcpy()는 destination bound를 검사하지 않아 buffer 끝을 넘는 linear overflow를 만들 수 있다. CONFIG_FORTIFY_SOURCE=y와 compiler flag가 위험을 줄이지만 새 사용을 추가할 이유는 없다. strscpy()로 교체한다. strcpy()는 destination pointer를 return하지만 strscpy()는 복사한 non-NUL byte 수 또는 truncation 시 negative errno를 return하므로 return value 사용을 함께 수정해야 한다.

NUL-terminated string에 strncpy() 사용

strncpy()는 destination의 NUL termination을 보장하지 않아 linear read overflow 등을 일으킬 수 있다. Source가 destination보다 짧으면 전체 destination을 NUL padding하여 불필요한 성능 비용도 낸다.

NUL termination이 필요하면 strscpy()를 사용하고 return 의미 차이를 처리한다. NUL padding이 필요하면 strscpy_pad()를 쓴다. Non-NUL-terminated string이면 strtomem()을 사용하고 향후 compiler warning을 피하려고 destination에 __nonstring attribute를 붙인다. Padding도 필요하면 strtomem_pad()를 쓴다.

strlcpy()

strlcpy()는 strlen()과 같은 return value를 만들기 위해 source 전체를 먼저 읽는다. 이 read는 destination size limit을 넘을 수 있어 비효율적이며 source가 NUL-terminated가 아니면 linear read overflow를 만들 수 있다. strscpy()로 교체하되 truncation 시 negative errno를 return하는 차이를 처리한다.

simple_strtol(), simple_strtoll(), simple_strtoul(), simple_strtoull()
----------------------------------------------------------------------
Le funzioni simple_strtol(), simple_strtoll(),
simple_strtoul(), e simple_strtoull() ignorano volutamente
i possibili overflow, e questo può portare il chiamante a generare risultati
inaspettati. Le rispettive funzioni kstrtol(), kstrtoll(),
kstrtoul(), e kstrtoull() sono da considerarsi le corrette
sostitute; tuttavia va notato che queste richiedono che la stringa sia
terminata con il carattere NUL o quello di nuova riga.

strcpy()
--------
La funzione strcpy() non fa controlli agli estremi del buffer
di destinazione. Questo può portare ad un overflow oltre i limiti del
buffer e generare svariati tipi di malfunzionamenti. Nonostante l'opzione
`CONFIG_FORTIFY_SOURCE=y` e svariate opzioni del compilatore aiutano
a ridurne il rischio, non c'è alcuna buona ragione per continuare ad usare
questa funzione. La versione sicura da usare è strscpy(), tuttavia va
prestata attenzione a tutti quei casi dove viene usato il valore di
ritorno di strcpy().  La funzione strscpy() non ritorna un puntatore
alla destinazione, ma un contatore dei byte non NUL copiati (oppure
un errno negativo se la stringa è stata troncata).

strncpy() su stringe terminate con NUL
--------------------------------------
L'utilizzo di strncpy() non fornisce alcuna garanzia sul fatto che
il buffer di destinazione verrà terminato con il carattere NUL. Questo
potrebbe portare a diversi overflow di lettura o altri malfunzionamenti
causati, appunto, dalla mancanza del terminatore. Questa estende la
terminazione nel buffer di destinazione quando la stringa d'origine è più
corta; questo potrebbe portare ad una penalizzazione delle prestazioni per
chi usa solo stringe terminate. La versione sicura da usare è
strscpy(), tuttavia va prestata attenzione a tutti quei casi dove
viene usato il valore di ritorno di strncpy().  La funzione strscpy()
non ritorna un puntatore alla destinazione, ma un contatore dei byte
non NUL copiati (oppure un errno negativo se la stringa è stata
troncata). Tutti i casi che necessitano di estendere la
terminazione con NUL dovrebbero usare strscpy_pad().

Se il chiamate no usa stringhe terminate con NUL, allore strncpy()
può continuare ad essere usata, ma i buffer di destinazione devono essere
marchiati con l'attributo `__nonstring <https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html>`_
per evitare avvisi durante la compilazione.

strlcpy()
---------
La funzione strlcpy(), per prima cosa, legge interamente il buffer di
origine, magari leggendo più di quanto verrà effettivamente copiato. Questo
è inefficiente e può portare a overflow di lettura quando la stringa non è
terminata con NUL. La versione sicura da usare è strscpy(), tuttavia
va prestata attenzione a tutti quei casi dove viene usato il valore di
ritorno di strlcpy(), dato che strscpy() ritorna un valore di errno
negativo quanto la stringa viene troncata.

%p format specifier

175-205

전통적으로 format string의 %p는 dmesg, proc, sysfs 등에서 실제 address를 노출하는 취약점을 만들었다. 현재 kernel의 %p는 exploit을 막으려고 hash된 값을 출력하므로 address로 사용할 수 없다. 새 %p 사용을 추가하지 않는다.

Text address에는 유용한 symbol name을 출력하는 %pS가 더 적절할 수 있다. 그 밖의 대부분은 pointer를 아예 출력하지 않는다.

  • Hash된 %p 값이 쓸모없다면 pointer 자체가 정말 필요한지 검토하고 가능하면 출력 전체를 제거한다.
  • 실제 pointer가 반드시 필요하다면 특정 system state나 privilege level을 특별히 신뢰할 이유를 묻는다. Comment와 commit log로 충분히 정당화하고 sensible permission을 보장할 수 있을 때만 %px를 검토한다.
  • %p hashing이 debugging을 방해하면 임시로 no_hash_pointers debug boot flag를 사용할 수 있다.
Segnaposto %p nella stringa di formato
--------------------------------------

Tradizionalmente, l'uso del segnaposto "%p" nella stringa di formato
esponne un indirizzo di memoria in dmesg, proc, sysfs, eccetera.  Per
evitare che questi indirizzi vengano sfruttati da malintenzionati,
tutto gli usi di "%p" nel kernel rappresentano l'hash dell'indirizzo,
rendendolo di fatto inutilizzabile.  Nuovi usi di "%p" non dovrebbero
essere aggiunti al kernel.  Per una rappresentazione testuale di un
indirizzo usate "%pS", l'output è migliore perché mostrerà il nome del
simbolo.  Per tutto il resto, semplicemente non usate "%p".

Parafrasando la `guida
<https://lore.kernel.org/lkml/CA+55aFwQEd_d40g4mUCSsVRZzrFPUJt74vc6PPpb675hYNXcKw@mail.gmail.com/>`_
di Linus:

- Se il valore hash di "%p" è inutile, chiediti se il puntatore stesso
  è importante. Forse dovrebbe essere rimosso del tutto?
- Se credi davvero che il vero valore del puntatore sia importante,
  perché alcuni stati del sistema o i livelli di privilegi di un
  utente sono considerati "special"? Se pensi di poterlo giustificare
  (in un commento e nel messaggio del commit) abbastanza bene da
  affrontare il giudizio di Linus, allora forse potrai usare "%px",
  assicurandosi anche di averne il permesso.

Potete disabilitare temporaneamente l'hashing di "%p" nel caso in cui questa
funzionalità vi sia d'ostacolo durante una sessione di debug. Per farlo
aggiungete l'opzione di debug "`no_hash_pointers
<https://git.kernel.org/linus/5ead723a20e0447bc7db33dc3070b420e5f80aa6>`_" alla
riga di comando del kernel.

Variable Length Array

206-218

Stack VLA는 static-size stack array보다 훨씬 나쁜 machine code를 만든다. 성능 문제뿐 아니라 security risk도 있다. Stack array가 dynamic하게 커지면 stack segment의 남은 memory를 넘을 수 있다.

그 결과 crash, CONFIG_THREAD_INFO_IN_TASK=y가 없을 때 stack 끝의 민감한 content overwrite, CONFIG_VMAP_STACK=y가 없을 때 stack 인접 memory overwrite가 생길 수 있다.

Vettori a dimensione variabile (VLA)
------------------------------------

Usare VLA sullo stack produce codice molto peggiore rispetto a quando si usano
vettori a dimensione fissa. Questi `problemi di prestazioni <https://git.kernel.org/linus/02361bc77888>`_,
tutt'altro che banali, sono già un motivo valido per eliminare i VLA; in
aggiunta sono anche un problema per la sicurezza. La crescita dinamica di un
vettore nello stack potrebbe eccedere la memoria rimanente in tale segmento.
Questo può portare a dei malfunzionamenti, potrebbe sovrascrivere
dati importanti alla fine dello stack (quando il kernel è compilato senza
`CONFIG_THREAD_INFO_IN_TASK=y`), o sovrascrivere un pezzo di memoria adiacente
allo stack (quando il kernel è compilato senza `CONFIG_VMAP_STACK=y`).

Implicit switch fall-through 금지

219-262

C는 case 끝에 break가 없으면 다음 case로 fall through한다. 하지만 누락이 의도인지 bug인지 알기 어려워 code가 모호해진다.

switch (value) {
case STATE_ONE:
    do_something();
case STATE_TWO:
    do_other();
    break;
default:
    WARN("unknown state");
}

Missing break로 생긴 defect가 많아 implicit fall-through는 허용하지 않는다. 의도적인 경우 GCC __attribute__((__fallthrough__))로 확장되는 pseudo-keyword macro fallthrough를 사용한다. C17/C18 [[fallthrough]]를 compiler, analyzer, IDE가 더 널리 지원하면 macro도 그 syntax로 바꿀 수 있다.

모든 switch/case block은 다음 중 하나로 끝나야 한다.

  • break;
  • fallthrough;
  • continue;
  • goto <label>;
  • return [expression];
Salto implicito nell'istruzione switch-case
-------------------------------------------

Il linguaggio C permette ai casi di un'istruzione `switch` di saltare al
prossimo caso quando l'istruzione "break" viene omessa alla fine del caso
corrente. Tuttavia questo rende il codice ambiguo perché non è sempre ovvio se
l'istruzione "break" viene omessa intenzionalmente o è un baco. Per esempio,
osservando il seguente pezzo di codice non è chiaro se lo stato
`STATE_ONE` è stato progettato apposta per eseguire anche `STATE_TWO`::

  switch (value) {
  case STATE_ONE:
          do_something();
  case STATE_TWO:
          do_other();
          break;
  default:
          WARN("unknown state");
  }

Dato che c'è stata una lunga lista di problemi `dovuti alla mancanza dell'istruzione
"break" <https://cwe.mitre.org/data/definitions/484.html>`_, oggigiorno non
permettiamo più che vi sia un "salto implicito" (*fall-through*). Per
identificare un salto implicito intenzionale abbiamo adottato la pseudo
parola chiave 'fallthrough' che viene espansa nell'estensione di gcc
`__attribute__((fallthrough))` `Statement Attributes
<https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html>`_.
(Quando la sintassi C17/C18 `[[fallthrough]]` sarà più comunemente
supportata dai compilatori C, analizzatori statici, e dagli IDE,
allora potremo usare quella sintassi per la pseudo parola chiave)

Quando la sintassi [[fallthrough]] sarà più comunemente supportata dai
compilatori, analizzatori statici, e ambienti di sviluppo IDE,
allora potremo usarla anche noi.

Ne consegue che tutti i blocchi switch/case devono finire in uno dei seguenti
modi:

* ``break;``
* `fallthrough;``
* ``continue;``
* ``goto <label>;``
* ``return [expression];``

Zero-length와 one-element array를 flexible array로 전환

263-316

Structure 끝에 dynamic-size element 집합을 두어야 하는 경우 kernel code는 항상 flexible array member를 사용한다. 오래된 one-element 또는 zero-length array style은 더 이상 사용하지 않는다.

옛 C code는 trailing element를 one-element array로 선언했다.

struct something {
    size_t count;
    struct foo items[1];
};

Header size를 구할 때 trailing element 하나의 크기를 빼야 하므로 sizeof 계산이 취약했다. GNU C zero-length extension은 이 문제를 피하려 했지만 다른 문제를 만들었고 array가 structure 마지막이 아닌 위치에 잘못 쓰이는 것도 검출하지 못했다.

struct something {
    size_t count;
    struct foo items[0];
};

C99 flexible array member는 size를 완전히 생략한다. Kernel이 기대하는 선언은 이 형태다.

struct something {
    size_t count;
    struct foo items[];
};

Compiler는 flexible array가 structure 마지막에 없으면 error를 내어 undefined behavior 유입을 막는다. sizeof(), CONFIG_FORTIFY_SOURCE, CONFIG_UBSAN_BOUNDS도 array size를 올바르게 분석할 수 있다.

Array di lunghezza zero o con un solo elemento
----------------------------------------------
All'interno del kernel ricorre spesso la necessita di avere membri
di dimensione variabile all'interno di una struttura dati. In questi
casi il codice del kernel dovrebbe usare sempre i `"flexible array
member" <https://en.wikipedia.org/wiki/Flexible_array_member>`_. La
tecnica degli array a lunghezza nulla o di un solo elemento non
dovrebbe essere più usata.

Nel codice C più vecchio, la dichiarazione di un membro di dimensione
variabile in coda ad una struttura dati veniva fatto dichiarando un
array di un solo elemento posizionato alla fine della struttura dati::

        struct something {
                size_t count;
                struct foo items[1];
        };

Questo ha portato ad un calcolo di sizeof() traballante (dovrebbe
rimuovere la dimensione del singolo elemento in coda per calcolare la
dimensione esatta dell' "intestazione"). Per evitare questi problemi è
stata introdotta un' `estensione a GNU C
<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_ che
permettesse la dichiarazione di array a lungezza zero::

        struct something {
                size_t count;
                struct foo items[0];
        };

Ma questo ha portato nuovi problemi, e non ha risolto alcuni dei
problemi che affliggono entrambe le tecniche: per esempio
l'impossibilità di riconoscere se un array di quel tipo viene usato
nel mezzo di una struttura dati e _non_ alla fine (potrebbe accadere
sia direttamente, sia indirettamente quando si usano le unioni o le
strutture di strutture).

Lo standard C99 introduce i "flexible array members". Questi array non
hanno una dimensione nella loro dichiarazione::

        struct something {
                size_t count;
                struct foo items[];
        };

Questo è il modo con cui ci si aspetta che vengano dichiarati gli
elementi di lunghezza variabile in coda alle strutture dati.  Permette
al compilatore di produrre errori quando gli array flessibili non si
trovano alla fine della struttura dati, il che permette di prevenire
alcuni tipi di bachi dovuti a `comportamenti inaspettati
<https://git.kernel.org/linus/76497732932f15e7323dc805e8ea8dc11bb587cf>`_.
Inoltre, permette al compilatore di analizzare correttamente le
dimensioni degli array (attraverso sizeof(), `CONFIG_FORTIFY_SOURCE`,
e `CONFIG_UBSAN_BOUNDS`). Per esempio, non esiste alcun meccanismo in

Zero-length array의 sizeof() 함정

317-345

Zero-length array에 sizeof()를 적용하면 항상 0이지만 compiler warning이 없다. 다음 code에서 trailing items에 allocation한 전체 byte 수를 계산한다고 생각할 수 있으나 size는 0이 되어 memcpy가 아무 것도 복사하지 않는다.

struct something {
    size_t count;
    struct foo items[0];
};

struct something *instance;

instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL);
instance->count = count;

size = sizeof(instance->items) * instance->count;
memcpy(instance->items, source, size);

Flexible array member는 incomplete type이어서 sizeof()를 적용할 수 없으므로 이런 오용을 build time에 즉시 발견한다.

grado di avvisarci che il seguente uso di sizeof() dia sempre come
zero come risultato::

        struct something {
                size_t count;
                struct foo items[0];
        };

        struct something *instance;

        instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL);
        instance->count = count;

        size = sizeof(instance->items) * instance->count;
        memcpy(instance->items, source, size);

Il valore di ``size`` nell'ultima riga sarà ``zero``, quando uno
invece si aspetterebbe che il suo valore sia la dimensione totale in
byte dell'allocazione dinamica che abbiamo appena fatto per l'array
``items``. Qui un paio di esempi reali del problema: `collegamento 1
<https://git.kernel.org/linus/f2cd32a443da694ac4e28fbf4ac6f9d5cc63a539>`_,
`collegamento 2
<https://git.kernel.org/linus/ab91c2a89f86be2898cee208d492816ec238b2cf>`_.
Invece, `i flexible array members hanno un tipo incompleto, e quindi
sizeof() non può essere applicato
<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_; dunque ogni
uso scorretto di questo operatore verrà identificato immediatamente
durante la compilazione.

One-element array allocation과 올바른 helper

346-385

One-element array는 element 하나만큼 실제 공간을 차지하므로 enclosing structure 크기에 포함된다. Dynamic allocation size를 계산할 때 오류를 만들기 쉽다.

struct something {
    size_t count;
    struct foo items[1];
};

struct something *instance;

instance = kmalloc(struct_size(instance, items, count - 1), GFP_KERNEL);
instance->count = count;

size = sizeof(instance->items) * instance->count;
memcpy(instance->items, source, size);

count - 1을 기억하지 않으면 item 하나를 더 allocation한다. 가장 명확하고 오류가 적은 방법은 flexible array member와 struct_size(), flex_array_size()를 함께 쓰는 것이다.

struct something {
    size_t count;
    struct foo items[];
};

struct something *instance;

instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL);
instance->count = count;

memcpy(instance->items, source,
       flex_array_size(instance, items, instance->count));
Per quanto riguarda gli array di un solo elemento, bisogna essere
consapevoli che `questi array occupano almeno quanto lo spazio di un
singolo oggetti dello stesso tipo
<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_, e quindi
contribuiscono al calcolo della dimensione della struttura che li
contiene. In questo caso è facile commettere errori quando si vuole
calcolare la dimensione totale della memoria totale da allocare per
una struttura dati::

        struct something {
                size_t count;
                struct foo items[1];
        };

        struct something *instance;

        instance = kmalloc(struct_size(instance, items, count - 1), GFP_KERNEL);
        instance->count = count;

        size = sizeof(instance->items) * instance->count;
        memcpy(instance->items, source, size);

In questo esempio ci siamo dovuti ricordare di usare ``count - 1`` in
struct_size(), altrimenti avremmo --inavvertitamente-- allocato
memoria per un oggetti ``items`` in più. Il modo più pulito e meno
propenso agli errori è quello di usare i `flexible array member`, in
combinazione con struct_size() e flex_array_size()::

        struct something {
                size_t count;
                struct foo items[];
        };

        struct something *instance;

        instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL);
        instance->count = count;

        memcpy(instance->items, source, flex_array_size(instance, items, instance->count));

DECLARE_FLEX_ARRAY()가 필요한 두 경우

386-409

Flexible array가 struct 안에 혼자 있거나 union member인 두 경우에는 DECLARE_FLEX_ARRAY() helper를 사용한다. UAPI header에서는 __DECLARE_FLEX_ARRAY()라는 이름을 쓴다. C99 specification은 기술적 이유 없이 이 배치를 금지하므로 helper가 workaround를 제공한다.

/* 오래된 형태 */
struct something {
    ...
    union {
        struct type1 one[0];
        struct type2 two[0];
    };
};

/* 권장 형태 */
struct something {
    ...
    union {
        DECLARE_FLEX_ARRAY(struct type1, one);
        DECLARE_FLEX_ARRAY(struct type2, two);
    };
};
Ci sono due casi speciali dove è necessario usare la macro DECLARE_FLEX_ARRAY()
(da notare che la stessa macro è chiamata __DECLARE_FLEX_ARRAY() nei file di
intestazione UAPI). Uno è quando l'array flessibile è l'unico elemento di una
struttura, e l'altro quando è parte di un unione. Per motivi non tecnici, entrambi
i casi d'uso non sono permessi dalla specifica C99. Per esempio, per
convertire il seguente codice::

    struct something {
        ...
        union {
            struct type1 one[0];
            struct type2 two[0];
        };
    };

La macro di supporto dev'essere usata::

    struct something {
        ...
        union {
            DECLARE_FLEX_ARRAY(struct type1, one);
            DECLARE_FLEX_ARRAY(struct type2, two);
        };
    };