Mineru-si schimba jobul, dar naravul ba

Posted in Codare cu premeditare on July 18th, 2011 by Mihnea

Mi s-a facut observatie ca n-am analizat obiectiv codul minerului din articolul mentionat anterior. Cind i-am corectat lucrarea i-am dat direct doi pentru copiat, n-am gasit de cuviinta sa ma uit cu atentie la partea originala, ca oricum nu prea mai aveam de unde sa-i scad din nota. Gresit! Iar pierdeam una din ocaziile (deloc rare, ce-i drept) in care 4 linii de cod contin mai multe greseli decit litere.

Sa  privim, deci, ce solutie propune Silviu pentru spinoasa problema a buffer overflow-ului:

#ifdef _WIN32
   #define usprintf(x, ...) _snwprintf(x, _countof(x) - 1, ##__VA_ARGS__); x[_countof(x)-1] = 0
#else
   #define usprintf(x, ...) snwprintf(x, _countof(x) - 1, ##__VA_ARGS__); x[_countof(x)-1] = 0
#endif

Ce ne izbeste din prima e o mare surpriza: Silviu stie ca _snprintf() din VC++ nu pune 0 la sfirsitul buffer-ului daca n-are loc! Nu v-ati fi asteptat, nu? Valea Jiului, always surprising! Ei bine, nu va grabiti cu laudele. Eu nu cred minerul a dezvoltat aceasta solutie cu de la sine putere.

In primul rind, problema pe care incearca el s-o rezolve nu exista. Pentru wide char ai swprintf(), care ia si capacitatea buffer-ului, exista pe toate compilatoarele din era noastra si pune si 0 la sfirsit tot timpul. VC++ are mici dubii legate de prototipul ei in C, dar nu si in C++, deci abatajul nu ar fi afectat. Orice om normal ar fi folosit functia aia si gata. In schimb, Capitanul Copypaste a vazut pe undeva pe net sau in proiectul la care lucreaza urmatorul fragment care “rezolva” problema lipsei varului snprintf() din VC++:

#ifdef _WIN32
   #define cevaprintf(x, ...) _snprintf(x, sizeof(x) - 1, ##__VA_ARGS__); x[sizeof(x)-1] = 0
#else
   #define cevaprintf(x, ...) snprintf(x, sizeof(x) - 1, ##__VA_ARGS__); x[sizeof(x)-1] = 0
#endif

Nici ala care a facut chestia asta nu e foarte breaz, dar n-ar fi prima data cind minerul se inspira de la un alt bou. Oricum, ce e important e ca Silviu a luat codul asta, a pus “w” acolo ca sa fie Unicode si a schimbat sizeof() cu _countof() ca i-am zis noi mai demult de chestia asta. Doar ca:

  • snwprintf() nu prea exista (cica ar fi prin Borland, daca intereseaza pe cineva asemenea relicve). Ca si data trecuta cind a scris cod “cross-platform”, ortacul nu s-a ostenit sa vada daca ce a debitat in a doua ramura a #ifdef-ului chiar se compileaza.
  • _countof() nu exista decit in VC++. Da, se poate copia printr-un header si folosi si-n alte compilatoare, dar minerul nu stie asta.
  • nu asa se fac macro-uri compuse din mai multe instructiuni. Astept cu interes sa scrie un coleg de-al lui if(cacat) usprintf(ceva); else usprintf(altceva); si sa se intrebe de ce nu se compileaza.
  • snprintf() pune singur 0 la sfirsit tot timpul, nu-i nevoie de lucru manual. Ipoteticul snwprintf(), numit in realitate swprintf(), face si el acelasi lucru. Dar ma rog, cum am stabilit deja, daca stii de swprintf() nu mai faci deloc cacaturile astea.
  • daca ar fi sa fim pedantici, ne-am lega de faptul ca # trebuie sa fie tot timpul pe prima coloana, nu ai voie sa pui tab-uri in fata lui. Din nefericire compilatoarele sint indulgente in problema asta.
  • nu vad de ce ai vrea sa chemi tu sizeof() sau _countof() pentru utilizatorul macro-ului. Poate ca sa te asiguri ca respectivul utilizator nu poate folosi ce-ai facut daca are un pointer chior, chiar daca stie capacitatea array-ului la care pointeaza.
  • nu vad de ce ai face chestia asta ca macro, in loc sa faci o functie ordinara, care ar merge si cind nu folosesti ultimul racnet de compilator cu suport pentru macro-uri variadice, n-ar avea probleme fara acolade etc.; evident, asta presupunind ca n-ar exista deja functia aia.

Misto e ca de data asta Silviu nu poate baga scuza aia imbecila cu “e cod didactic, nu trebuie sa mearga”. Conform spuselor lui, asta e cod pe care l-a scris in aplicatia pe care o distruge pe bani la Saguaro. Muie Silviu!

PS: Bonus story.

 

Tags: , , , , , , ,