AMD displeja draivera funkcija dcn42_create_resource_pool() atmiņu piešķīra ar izsaukumu kzalloc(sizeof(struct dcn401_resource_pool), GFP_KERNEL), taču rezultātu ierakstīja mainīgajā ar tipu struct dcn42_resource_pool *. Abām struktūrām ir vienāds saturs un vienāds izmērs, tāpēc nekas neavarēja un neviens neko nepamanīja. Kļūda parādījās tikai tad, kad Kīss Kuks (Kees Cook) pāri visam kodola kokam vēlreiz palaida skriptu, kas vecos kmalloc() izsaukumus pārraksta uz tipizēto kmalloc_obj().
Labojumu Kuks nosūtīja 3. septembrī. Tā ir viena rinda: dcn401 vietā dcn42. Kopā ar to Linuss Torvalds 6. septembrī savā kokā ievilka pārējo pāreju, kas gatavota Linux 7.3-rc2 laidienam. Tajā ir 300 failu, 637 pievienotas rindas un 726 izņemtas. Koda paliek par 89 rindām mazāk.
Viens nepareizs struktūras nosaukums
Vecās kmalloc() saimes funkcijas atgriež void *. Kompilators tāpēc nekad nesalīdzina to, cik baitu programmētājs pieprasīja, ar to, kāda tipa mainīgajā rezultāts nonāk. dcn42 gadījumā sizeof() iekšpusē bija citas draivera paaudzes struktūra. Pārbaudes nav, tāpēc kods būvējās un strādāja.
Jaunais makro kmalloc_obj() atgriež norādi uz to tipu, kas padots pirmajā argumentā. Kad skripts izsaukumu mēģināja pārrakstīt, piešķires abas puses vairs nesakrita un kompilators to pateica. Labojuma aprakstā Kuks norāda, ka kļūda ir nekaitīga, jo abu struktūru saturs un izmērs sakrīt.
Ko tieši pārraksta skripts
Pāreju veic Coccinelle skripts scripts/coccinelle/api/kmalloc_objs.cocci, kas kodola kokā ir kopš februāra. Tas aiztiek trīs izsaukumu formas:
kmalloc(sizeof(TIPS), ...)kļūst parkmalloc_obj(TIPS, ...)kmalloc_array(SKAITS, sizeof(TIPS), ...)kļūst parkmalloc_objs(TIPS, SKAITS, ...)kmalloc(struct_size(PTR, FAM, SKAITS), ...)kļūst parkmalloc_flex(*PTR, FAM, SKAITS, ...)
Skalāros tipus skripts apzināti neaiztiek, jo tur katrs gadījums jāpārbauda atsevišķi. Pārrakstītajā variantā pazūd gan sizeof, gan tipa nosaukuma atkārtošana. Tieši tā atkārtošana AMD kodā bija sagājusi greizi.
Sintakse ļauj mainīgo pieteikt arī ar __auto_type ptr = kmalloc_obj(struct foo, gfp);, tāpēc tipa nosaukums tekstā paliek vienā vietā.
Kodola izstrādātājam pāreja nozīmē mazāk rakstāmā. Rinda
ptr = kmalloc_array(count, sizeof(*ptr), gfp);
kļūst par
ptr = kmalloc_objs(*ptr, count, gfp);
Vecais paraugs prasa, lai sizeof() saturs un mainīgā tips sakristu, taču neko no tā nepārbauda. Tieši šī plaisa AMD dcn42 kodā palika atvērta līdz šai nedēļai. Pēc septembra pārrakstīšanas šī forma kodolā vairs klusi neiziet cauri.
Kāpēc vajadzēja otro gājienu
Pirmā pāreja notika Linux 7.0 sagatavošanas laikā. Tā bija krietni lielāka: 20 055 pievienotas rindas un 20 913 izņemtas. Kopš tā laika kodolā ienākuši jauni kmalloc() izsaukumi. Bez tiem pirmajā reizē neaiztikta palika vesela veco izsaukumu grupa. Iemesls bija paša rīka ierobežojums.
Coccinelle nesaprot cleanup.h makro saimi scoped_...(), tāpēc tajos ietvertos izsaukumus tas izlaida. Kuks problēmu apgāja ar spatch argumentu --macro-file: atsevišķā failā visi scoped_...() makro tiek pārtulkoti uz Coccinelle iekšējo YACFE_ITERATOR. Tā ir tuvākā pieejamā vadības plūsmas norāde, ko rīks saprot.
Rezultātu Kuks pārbaudīja ar allmodconfig būvējumu astoņās arhitektūrās: x86, arm64, arm, loongarch, mips, powerpc, riscv un s390. Jaunu brīdinājumu nebija.
Ko maksā šāda pārrakstīšana
Skripts koda vietas maina mehāniski, taču ar sekām jātiek galā pašiem apakšsistēmu uzturētājiem. Februāra pārejai nākamajās dienās sekoja virkne atsevišķu labojumu. xz atspiešanas kodā salūza arm fdt būvējums. KCSAN testā vajadzēja pielabot piešķires tipu, ko tests sagaida. Vienā gadījumā nācās izslēgt __builtin_counted_by_ref Clang kompilatoram, citā apturēt salauztu pārpildes apstrādi elastīgo masīvu piešķirēs.
Uzturētājiem šādi koka mēroga ieraksti nozīmē arī konfliktus. 300 failu skar gandrīz visas apakšsistēmas, tāpēc katram izstrādātājam, kam attiecīgajā failā ir savs ielāps sagatavošanā, to nāksies pārcelt uz jauno bāzi. Torvalds pāreju ievilka rc2 posmā, kad apvienošanas logs jau ir slēgts. Skripta ģenerētiem ierakstiem tas ir ierasts risinājums, jo pretējā gadījumā tie sadurtos ar visu, kas logā ienāk paralēli.
Coccinelle skripts paliek kokā, tāpēc nākamā palaišana ir atkārtojama. Kuks septembra ierakstā to raksturo kā kārtējo jau esošo noteikumu palaišanu.
Kas paredzēts tālāk
Tipa informācija piešķīrējam ļauj darīt to, kas ar void * nebija iespējams. Kuks to uzrakstīja jau 14. janvāra ierakstā, ar kuru kmalloc_obj() nonāca kodolā:
Tagad kļūst iespējama iekšēja piešķirtā tipa introspekcija, kas piešķīrējam turpmāk ļauj izvēlēties tipam atbilstošu izlīdzināšanu un veidot tipam jutīgu aizsardzību.
Ierakstā minēti divi konkrēti soļi. Pirmais ir __alignof(*ptr) padošana iekšējiem piešķīrējiem, lai tie izvēlētos tipam derīgu izlīdzināšanu. Otrais ir katras piešķires nobīdes nejaušināšana sadalījuma grozā tā, lai izlīdzināšanas prasības paliktu spēkā. Nejaušināta nobīde apgrūtina uzbrukumus, kas balstās uz to, ka blakus esošu objektu adreses ir paredzamas.
Torvalda kokā ievilktais tags saucas kmalloc_obj-v7.3-rc2. Tajā ir divi ieraksti: 300 failu pāreja un viena rinda AMD displeja draiverī.
Avoti
- treewide: refresh kmalloc_obj() conversions, Kees Cook, 2026. gada 2. septembris
- drm/amd/display: Fix harmless type mismatch in allocation, Kees Cook, 3. septembris
- slab: Introduce kmalloc_obj() and family, Kees Cook, 14. janvāris
- Merge tag kmalloc_obj-v7.3-rc2, Linus Torvalds, 6. septembris
- Linux 7.3-rc2 Transitions More kmalloc Calls To The Superior kmalloc_obj, Phoronix
Komentāri
Šim rakstam vēl nav komentāru. Esi pirmais, kurš dalās ar savu viedokli.