Inginerii de la Apple au anunțat că sunt pregătiți să testeze modul „-fbounds-safety” pentru compilatorul Clang, care oferă garanții de siguranță în muncă cu buffere în codul scris în limbajul C. Modul este inclus în fork-ul LLVM, susținut de Apple pentru proiectul Swift. În continuare, se preconizează transferul treptat al funcționalității „-fbounds-safety” în baza de cod principal a LLVM/Clang.
Se remarcă faptul că mecanismul de protecție propus este deja utilizat activ în produsele Apple, cum ar fi nucleul XNU, firmware-urile, bibliotecile pentru lucrul cu sunetul și decodoarele de imagini. Activarea modulului „-fbounds-safety” reduce performanța aplicațiilor în medie cu 5% (variație de la -1% la 29%), crește dimensiunea codului cu 9.1% (variație de la -1.4% la 38%) și încetinește compilarea cu 11%.
Utilizarea modulului „-fbounds-safety” pentru detectarea automată a depășirii limitei zonei de memorie asociate cu un pointer necesită adăugarea de anotații speciale în cod și includerea fișierului de antet „ptrcheck.h”. Esența metodei de protecție propuse constă în atașarea automată a verificărilor respectării limitelor admise, adăugate pe baza anotațiilor setate manual sau a dimensiunilor cunoscute de compilator.
Spre deosebire de utilizarea pointerilor extinși (wide pointer) în cod, unde, pe lângă adresă, există informații despre limita superioară și inferioară a bufferului, utilizarea modulului „-fbounds-safety” nu afectează ABI (Application Binary Interface), nu schimbă formatul pointerilor exportați și nu necesită refacerea întregului proiect. În modul „-fbounds-safety”, pointerii extinși sunt utilizați doar în zone care nu se suprapun cu ABI, iar pentru pointerii care afectează ABI, se folosesc pointeri obișnuiți cu înlocuirea verificărilor, formate pe baza anotațiilor cu informații despre limite.
Anotațiile trebuie atașate la pointeri în câmpurile structurilor și parametrii funcțiilor care indică spre un array de obiecte, precum și la variabilele globale cu pointeri. Pentru pointerii în variabilele locale, adăugarea anotațiilor nu este necesară, deoarece aceștia sunt deja procesați ca pointeri extinși, care includ informații despre limitele admise. Sugestiile pentru construcțiile din cod pentru care este necesară adăugarea anotațiilor sunt generate de compilator atunci când se execută cu flag-ul „-fbounds-safety”.
Modelul de protecție bazat pe „-fbounds-safety” poate fi implementat treptat, fișier cu fișier, fără a întrerupe dezvoltarea întregului proiect. Adăugarea protecției într-un proiect constă în specificarea notelor într-un anumit fișier cu cod, eliminarea avertismentelor compilatorului și efectuarea testării funcționării programului, după care acești pași se repetă pentru următorul fișier. Codul cu notele adăugate rămâne compatibil cu codul C obișnuit și cu compilatoarele care nu suportă „-fbounds-safety” (la compilarea cu alte compilatoare sau când se compilează fără flagul „-fbounds-safety”, verificările suplimentare de limită pur și simplu nu vor fi adăugate).
În timpul executării programului, în cazul în care se detectează o accesare în afara limitelor permise, se generează o excepție și programul își încheie execuția. O încheiere anormală poate avea loc și în cazul specificării unor note incorrecte, astfel încât folosirea „-fbounds-safety” necesită o testare suplimentară a funcționării programului.
În exemplul de mai jos, parametrul „int *p” are inserată nota „__counted_by(n)”, care adaugă o verificare suplimentară asupra limitelor permise, activă în timpul execuției. Dacă se încearcă compilarea codului în modul „-fbounds-safety” fără a specifica această notă, compilatorul va afișa un avertisment referitor la lipsa informațiilor despre limitele tabloului la procesarea expresiei „p[i] = 0”. #include void init_buf(int *__counted_by(n) p, int n) { for (int i = 0; i < n; ++i) p[i] = 0; // în modul „-fbounds-safety” compilatorul va insera automat o verificare similară cu codul „if (i = n) trap();” }
Pentru variabilele locale cu pointeri, verificările sunt atașate automat, de exemplu: void foo(int i){ char *buf = (char *)malloc(10); // pentru pointerul buf vor fi salvate informații despre limite buf[i] = 0xff; // va fi inserată automat verificarea „if (buf + i = buf + 10) trap();” }
Atunci când este posibil, compilatorul efectuează optimizări și exclude adăugarea de cod inutil, dacă codul conține deja verificările necesare. De exemplu: for (size_t i = 0; i < count; ++i) { buf[i] = i; // verificarea "if (i = count) trap()" nu va fi adăugată, deoarece mai sus este deja o condiție „i < count" și i nu poate fi mai mic de 0. }
Notele de bază:
- „__counted_by(N)” — definește dimensiunea bufferului în elemente de tip țintă.
- „__sized_by(N)” — definește dimensiunea bufferului în octeți.
- » __ended_by(P)» — stabilește limita superioară a buffer-ului.
- «__null_terminated» — ia în considerare caracterul nul ca sfârșit al buffer-ului.
- «__single» — leagă un pointer de un singur obiect. Se aplică în mod implicit pentru pointere care influențează ABI, dacă adnotarea nu este specificată explicit.
- «__bidi_indexable» — pointer extins cu informații despre limitele superioară și inferioară. Se aplică în mod implicit pentru pointere care nu influențează ABI.
- «__indexable» — pointer extins cu informații despre limita superioară.
- «__unsafe_indexable» — pointer fără verificarea limitelor (pentru portabilitate cu cod nesigur, de exemplu, pentru obținerea pointerelor din cod extern).
Sursa: opennet.ro
