Недефинисано понашање
У програмирању, недефинисано понашање (УБ) је резултат извршавања кода рачунара написаног у неком програмском језику за који спецификација језика не прописује како се треба поступати са кодом. Недефинисано понашање је непредвидиво и често узрок софтверских грешака.
Понашање неких програмских језика-најпознатији С и С++ - је дефинисано у неким случајевима.[1] У стандардима ових језика, семантике појединих операција су недефинисане. Имплементацији је дозвољено да претпостави да се такве операције никада не дешавају у стандарду-у складу програмског кода; имплементацијом ће се сматрати исправно све што ради у таквим случајевима, аналогно небитним вредностима у дигиталној логици. Ова претпоставка може да прави најразличитије програмске трансформације да важе или да поједностави своје доказе о исправности, дајући флексибилност у имплементацији. То је одговорност програмера за писање кода који никада не призива дефинисано понашање, иако су имплементације компајлера слободне да покрену дијагностику када се ово деси.
На пример, у С употреба било које аутоматске променљиве пре него што је инициализована има за исход недефинисано понашање, као што то чини дељење са нулом или индексирање низа изван дефинисаних граница (види бафер преливања). У принципу, свака инстанца недефинисаног понашања оставља апстрактну машину за убијање у непознатом стању, а свако накнадно понашање је такође недефинисано. Ако није потребно да преводилац дијагностикује недефинисано понашање, програми позивајући се на недефинисано понашање могу саставити и покренути производњу исправних резултата, нетачних резултате, или имају било какво друго понашање. Због тога, недефинисано понашање могу створити грешке које је тешко открити.
Под одређеним околностима могу постојати посебна ограничења недефинисаног понашања. На пример, сет инструкција спецификације CPU може да напусти понашање неких облика која су упутством дефинисана, али ако CPU подржава заштиту меморије онда ће спецификација вероватно укључити бланко правило наводећи да ниједан корисник не може да приступи и да упутство може изазвати рупу у безбедности оперативног система; тако да ће стварном процесору бити дозвољено да корумпиране кориснике региструје као одговор на такву инструкцију, али неће му бити дозвољено да се, на пример, пребаци у режим супервизора.
У С и C++, имплементацијом дефинисано понашање се користи, где стандардни језик не одређује понашање, али примена мора изабрати понашање и документ и мора да поштује своја правила. Ови стандарди такође користе неодређено понашање да значи да је од датог скупа могућности није наведено које понашање имплементација мора да изабере, то не мора да документује избор или чак бити у складу, али мора да изабере једну могућност.
У С заједници, недефинисано понашање може бити духовито названо као "назални демони" пост о Usenet-у који је објаснио недефинисано понашање као понашање које дозвољава да компајлер не изабере ништа, чак и "да демони лете из носа ".[2]
Примери у C и C++
[уреди | уреди извор]Покушај да се измени буквални низ изазива недефинисано понашање:[3]
char *p = "wikipedia"; // лоше формирана C++11, застарела C++98/C++03
p[0] = 'W'; // недефинисано понашање
Један од начина да се спречи ово је дефинисање као низ уместо показивача.
char p[] = "wikipedia"; // ТАЧНО
p[0] = 'W';
У C++, особа може употребити стандардни стринг као у наставку:
std::string s = "wikipedia"; // ТАЧНО
s[0] = 'W';
Целобројно дељење нулом резултује у недефинисано понашање:[4]
int x = 1;
return x / 0; // недефинисано понашање
Неки показивачи операција могу довести до недефинисаног понашања:[5]
int arr[4] = {0, 1, 2, 3};
int *p = arr + 5; // недефинисано понашање
Постизање краја функције вредности повратка (осим главног ()) без повратне изјаве могу довести до недефинисаног понашања:
int f()
{
} /* недефинисано понашање */
Оригинална књига С програмског језика наводи следеће примере кода који "могу (и који то раде) производе различите резултате на различитим машинама”[6] (што би се могло сматрати само неодређеним или имплементацијом дефинисаном понашањем у данашњим условима):
printf("%d %d\n", ++n, power(2, n)); /* НЕТАЧНО */
a[i] = i++;
Касније ANSI C стандард је одлучио да напусти сличне конструкције недефинисаног, на пример, "Овај став чини недефинисану изјаву изразе као што су i = ++i + 1;
док дозвољава i = i + 1;
”.[7]
Ризици недефинисаног понашања
[уреди | уреди извор]HTML верзије 4 и раније су напустиле грешку руковања недефинисаним. Временом су странице почеле да се ослањају на неодређено опоравка грешака спровођених у популарним претраживачима. Ово је изазвало проблеме за произвођаче мање популарних претраживача који су били приморани да преокрену-инжењеринг и имплементирају баг компатибилни опоравак грешке. То је довело до де факто стандарда који је био много компликованији него што је могао да буде да је такво понашање наведено од самог почетка.
Недефинисано понашање у сервер програмима може да изазове безбедносне проблеме. Када су GCC програмери променили компајлер у 2008. години тако да је изостављено одређено преливање провере које се ослањаlo на недефинисано понашање, CERT је издао упозорење против новијих верзија компајлера.[8] Linux Weekly News је истакао да је исто понашање посматрано у PathScale C, Microsoft Visual C++ 2005 и неколико других компајлера ;[9] упозорење је касније измењено да упозори о различитим компајлерима.[10]
Референце
[уреди | уреди извор]- ^ Lattner, Chris (13. 5. 2011). „What Every C Programmer Should Know About Undefined Behavior”. LLVM Project Blog. LLVM.org. Приступљено 24. 5. 2011.
- ^ „nasal demons”. The Jargon File. Приступљено 12. 6. 2014.
- ^ ISO/IEC (2003). ISO/IEC 14882|ISO/IEC 14882:2003(E): Programming Languages - C++ §2.13.4 String literals [lex.string] para. 2
- ^ ISO/IEC (2003). ISO/IEC 14882 §5.6 Multiplicative operators [expr.mul] para. 4
- ^ ISO/IEC (2003). ISO/IEC 14882 §5.7 Additive operators [expr.add] para. 5
- ^ Kernighan & Ritchie 1978, стр. 50.
- ^ ANSI X3.159-1989 Programming Language C, footnote 26
- ^ „Vulnerability Note VU#162289 — gcc silently discards some wraparound checks”. Vulnerability Notes Database. CERT. 4. 4. 2008. Архивирано из оригинала 9. 4. 2014. г.
- ^ Corbet, Jonathan (16. 4. 2008). „GCC and pointer overflows”. Linux Weekly News.
- ^ „Vulnerability Note VU#162289 — C compilers may silently discard some wraparound checks”. Vulnerability Notes Database. CERT. 8. 10. 2008 [4 April 2008].
Литература
[уреди | уреди извор]- Kernighan, Brian W.; Ritchie, Dennis M. (1978). The C Programming Language (1st изд.). Englewood Cliffs, NJ: Prentice Hall. стр. 50. ISBN 978-0-13-110163-0.
- Питер ван дер Линден (1994). Expert C Programming. ISBN 978-0-13-177429-2.
- UB Canaries (April 2015), John Regehr (University of Utah, USA)
Спољашње везе
[уреди | уреди извор]- Corrected version of the C99 standard. Погледати секцију 6.10.6 за #pragma