Stare de cursă

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.

Consecințe posibile

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:

Cazul Therac-25

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:

  1. Terapia cu electroni : pistolul cu electroni iradiază direct pacientul; computerul setează energia electronilor de la 5 la 25 MeV .
  2. Terapia cu raze X : Un tun cu electroni iradiază o țintă de tungsten și pacientul este iradiat cu fascicule de raze X care trec printr-un difuzor în formă de con . În acest mod, energia electronului este constantă: 25 MeV .
  3. În al treilea mod, nu a existat nicio radiație. Un reflector de oțel este plasat în calea electronilor (în caz de accident), iar radiația este simulată de lumină . Acest mod este folosit pentru a direcționa cu precizie fasciculul către locul dureros.

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ă.

Exemplu

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:

  1. Declarația if din firul 2 testează x pentru paritate.
  2. Declarația „ x++ ” din firul 1 crește x cu unu.
  3. Declarația de ieșire din firul 2 scoate „ x=1 ” chiar dacă variabila pare a fi verificată de paritate.

Soluții

Copie locală

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ă.

Sincronizare

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.

Mod combinat

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.

Hackuri prin exploatarea condițiilor de cursă

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.

Note

  1. Raymond, Eric S. Arta programării Unix / trad. din engleza. - M . : Editura " Williams ", 2005. - S. 202. - 544 p. — ISBN 5-8459-0791-8 .
  2. ↑ 1 2 3 4 Greg Kroah-Hartman, Alessandro Rubini, Jonathan Corbet. Capitolul 5. Condiții de concurență și cursă // Drivere de dispozitiv Linux . - editia a 3-a. - O'Reilly Media, Inc., 2005. - ISBN 0596005903 . Arhivat pe 12 aprilie 2019 la Wayback Machine

Vezi și