O condiție de cursă , de asemenea o competiție [ 1] , este o eroare de proiectare într-un sistem sau o aplicație cu mai multe fire , în care funcționarea sistemului sau a aplicației depinde de ordinea în care sunt executate părți ale codului. Eroarea și-a primit numele de la o eroare de proiectare similară în circuitele electronice (vezi curse de semnal ).
Termenul de condiție de rasă se referă la jargonul ingineresc și este rezultatul unei traduceri literale neglijentă a echivalentului englezesc. Într-un mediu academic mai riguros, se obișnuiește să se folosească termenul de incertitudine a concurenței .
O condiție de cursă este o eroare „plutitoare” ( heisenbug ), care apare în momente aleatorii și „dispare” atunci când încercați să o localizați.
Datorită accesului necontrolat la memoria partajată, o condiție de cursă poate duce la erori complet diferite care pot apărea în momente imprevizibile și o încercare de a replica eroarea în scopuri de depanare cu condiții de operare similare poate eșua.
Principalele consecințe pot fi:
Aparatul de radioterapie Therac-25 a fost primul dispozitiv medical din Statele Unite care s-a bazat exclusiv pe software pentru siguranță . Acest dispozitiv a funcționat în trei moduri:
Aceste trei moduri au fost stabilite de un disc rotativ, în care era o gaură cu magneți de deviere pentru terapia electronică și o țintă cu un difuzor pentru raze X. Din cauza unei condiții de cursă între programul de control și motorul tastaturii , uneori s-a întâmplat ca în modul de terapie cu raze X, discul să fie în poziția „terapie cu electroni” și pacientul să fie iradiat direct cu un fascicul de electroni de 25 MeV, care a dus la supraexpunere. În același timp, senzorii au afișat „Doza zero”, astfel încât operatorul să poată repeta procedura, exacerbând situația. Ca urmare, cel puțin doi pacienți au murit.
O parte a codului a fost preluată de la Therac-6 și Therac-20. În același timp, Therac-6 nu avea terapie cu raze X, iar Therac-20 avea măsuri de securitate hardware care împiedicau pornirea radiațiilor atunci când discul se afla într-o poziție greșită.
Luați în considerare un exemplu de cod (în Java ).
volatil int x ; // Thread 1: while ( ! stop ) { x ++ ; … } // Thread 2: while ( ! stop ) { if ( x % 2 == 0 ) System . afară . println ( "x=" + x ); … }Fie x=0. Să presupunem că programul este executat în următoarea ordine:
Cel mai simplu mod de a rezolva acest lucru este să copiați variabila x într-o variabilă locală. Iată codul corectat:
// Thread 2: while ( ! stop ) { int cached_x = x ; if ( cached_x % 2 == 0 ) Sistem . afară . println ( "x=" + cached_x ); … }Desigur, această metodă funcționează numai atunci când există o singură variabilă și copierea se face într-o singură instrucțiune de mașină.
O metodă de soluție mai complexă și „costisitoare”, dar și mai universală este sincronizarea firelor , și anume:
int x ; // Thread 1: while ( ! stop ) { synchronized ( someObject ) { x ++ ; } … } // Thread 2: while ( ! stop ) { synchronized ( someObject ) { if ( x % 2 == 0 ) System . afară . println ( "x=" + x ); } … }Aici, ceea ce se întâmplă înainte de semantică nu necesită cuvântul cheie volatile.
Să presupunem că există două variabile (și cuvântul cheie volatilenu are efect), iar al doilea thread System.out.printlnare în schimb o procesare mai complexă. În acest caz, ambele metode sunt nesatisfăcătoare: prima, deoarece o variabilă se poate modifica în timp ce cealaltă este copiată; a doua este că prea mult cod este sincronizat.
Aceste metode pot fi combinate prin copierea variabilelor „periculoase” într-un bloc sincronizat. Pe de o parte, aceasta va elimina restricția asupra unei instrucțiuni de mașină, pe de altă parte, vă va permite să scăpați de blocurile de sincronizare prea mari.
volatil int x1 , x2 ; // Thread 1: while ( ! stop ) { synchronized ( someObject ) { x1 ++ ; x2 ++ ; } … } // Thread 2: while ( ! stop ) { int cached_x1 , cached_x2 ; sincronizat ( someObject ) { cached_x1 = x1 ; cached_x2 = x2 ; } if (( cached_x1 + cached_x2 ) % 100 == 0 ) DoSomethingComplicated ( cached_x1 , cached_x2 ); … }Nu există modalități evidente de a detecta și remedia condițiile de cursă. Cel mai bun mod de a scăpa de curse este să proiectați corect un sistem multitasking.
Există o clasă de bug-uri (și tipuri de atacuri care le exploatează) care permit unui program neprivilegiat să influențeze funcționarea altor programe prin posibilitatea de a schimba resursele publice (de obicei fișiere temporare ; fișierul este disponibil pentru scris de către tot sau o parte din utilizatorii sistemului din cauza unei greșeli a unui programator.
Programul atacator poate distruge conținutul fișierului, provocând blocarea programului victimă sau, prin modificarea datelor, forțează programul să efectueze o acțiune la nivelul privilegiilor sale.
Din acest motiv, software-ul cu cerințe serioase de securitate, cum ar fi browserul web , utilizează numere aleatoare de calitate criptografică pentru a denumi fișierele temporare.