Анықталмаған мінез-құлық - Undefined behavior

Жылы компьютерлік бағдарламалау, анықталмаған мінез-құлық (UB) - бағдарламасын орындау нәтижесі мінез-құлық болжанбайтын болып тағайындалады тіл спецификациясы оған компьютер коды ұстанады. Бұл басқаша анықталмаған мінез-құлық, ол үшін тіл спецификациясы нәтиженің және іске асырудың басқа компонентінің құжаттамасына бағдарланған мінез-құлықты белгілемейді платформа (мысалы ABI немесе аудармашы құжаттама).

Ішінде C қоғамдастығы, анықталмаған мінез-құлықты әзілмен «деп атауға боладымұрын жындары«, кейін comp.std.c хабарлама, бұл анықталмаған мінез-құлықты компиляторға қалаған нәрсесін жасауға, тіпті «мұрыннан жындарды шығаруға» мүмкіндік беру деп түсіндірді.[1]

Шолу

Кейбіреулер бағдарламалау тілдері бағдарламаның басқаша жұмыс жасауына мүмкіндік беру немесе тіпті басқа басқару ағынына қарағанда бастапқы код, егер ол бірдей пайдаланушыға көрінетін болса жанама әсерлері, егер бағдарламаны орындау кезінде анықталмаған мінез-құлық ешқашан болмайды. Анықталмаған мінез-құлық дегеніміз - бағдарлама сәйкес келмейтін шарттар тізімінің атауы.

-Ның алғашқы нұсқаларында C, мінез-құлықтың анықталмаған басты артықшылығы - орындаушы өндірісі құрастырушылар машиналардың алуан түрлілігі үшін: белгілі бір конструкцияны машинаның ерекшелігіне сәйкестендіруге болады, ал компилятор жанама әсерлерді тіл таңдап алған семантикамен сәйкестендіру үшін жұмыс уақыты үшін қосымша код жасаудың қажеті жоқ. Бағдарламаның бастапқы коды нақты компиляторды алдын ала біле отырып жазылған платформалар оны қолдайтынын.

Алайда, платформалардың прогрессивті стандартталуы мұны, әсіресе С-ның жаңа нұсқаларында, аз артықшылыққа айналдырды. Енді анықталмаған мінез-құлық жағдайлары бір мағыналы болып табылады қателер кодта, мысалы массивті оның шегінен тыс индекстеу. Анықтама бойынша жұмыс уақыты анықталмаған мінез-құлық ешқашан болмайды деп болжай алады; сондықтан кейбір жарамсыз жағдайларды тексеру қажет емес. Үшін құрастырушы, бұл әр түрлі дегенді білдіреді бағдарламалық түрлендірулер күшіне енеді немесе олардың дұрыстығының дәлелдері жеңілдетіледі; бұл әртүрлі түрлерге мүмкіндік береді мерзімінен бұрын оңтайландыру және микро-оңтайландыру, егер бағдарлама күйі осындай шарттардың кез келгеніне жауап берсе, дұрыс емес әрекетке әкеледі. Компилятор бағдарламашыға ескертусіз бастапқы кодта болуы мүмкін анық тексерулерді де жоя алады; мысалы, анықталмаған мінез-құлықты оның болған-болмағаны туралы тексеру арқылы анықтау, анықтама бойынша. Бұл портативті қауіпсіз опцияны бағдарламалауды қиындатады немесе мүмкін емес етеді (портативті емес шешімдер кейбір құрылымдар үшін мүмкін).

Компилятордың қазіргі дамуы әдетте компилятордың өнімділігін микро-оңтайландырудың айналасында жасалған, тіпті жалпы жұмыс үстелдері мен ноутбуктар нарығында (мысалы, amd64 сияқты) пайдаланылатын платформаларда да бағалайды. Сондықтан анықталмаған мінез-құлық компилятордың жұмысын жақсартуға кең орын береді, өйткені белгілі бір бастапқы код туралы мәлімдеме үшін бастапқы кодты кез келген уақытта жұмыс уақытында салыстыруға рұқсат етіледі.

C және C ++ үшін компиляторға осы жағдайларда компиляция-уақыт диагностикасын беруге рұқсат етіледі, бірақ ол талап етілмейді: егер мұндай жағдайларда орындалса, дұрыс деп саналады маңызды емес шарттар цифрлық логикада. Бағдарламашының міндеті - ешқашан анықталмаған мінез-құлықты тудырмайтын кодты жазу, бірақ компилятор іске асыруларына диагностика жасауға рұқсат етіледі. Қазіргі кезде компиляторларда осындай диагностиканы жүргізуге мүмкіндік беретін жалаушалар бар, мысалы, -сансыздандыру «анықталмаған мінез-құлық тазартқышын» қосады (UBSan ) gcc 4.9[2] және шыңғыру. Алайда, бұл жалауша әдепкі болып табылмайды және оны қосу кодты кім жасайтынын таңдау болып табылады.

Кейбір жағдайларда анықталмаған мінез-құлыққа қатысты арнайы шектеулер болуы мүмкін. Мысалы, нұсқаулар жинағы сипаттамалары а Орталық Есептеуіш Бөлім нұсқаулықтың кейбір түрлерінің әрекеттерін анықталмаған күйде қалдыруы мүмкін, бірақ егер процессор қолдау көрсетсе жадты қорғау онда спецификация қолданушыға қол жетімді нұсқаулық саңылау тудырмайтындығы туралы көрпе ережесін қосуы мүмкін операциялық жүйе қауіпсіздік; сондықтан мұндай нұсқаулыққа жауап ретінде нақты CPU-ға пайдаланушының регистрлерін бүлдіруге рұқсат етіледі, бірақ, мысалы, супервайзер режимі.

Орындалу уақыты платформа анықталмаған мінез-құлыққа қатысты кейбір шектеулер мен кепілдіктер бере алады, егер құралдар тізбегі немесе жұмыс уақыты ішінен нақты құрылымдар табылған нақты құжат бастапқы код жұмыс кезінде қол жетімді нақты нақты механизмдермен салыстырылады. Мысалы, ан аудармашы тілдің спецификациясында анықталмаған кейбір операциялар үшін белгілі бір мінез-құлықты құжаттауы мүмкін, ал сол тілге арналған басқа аудармашылар немесе компиляторлар мүмкін емес. A құрастырушы өндіреді орындалатын код нақты үшін ABI толтыру мағыналық алшақтық компилятор нұсқасына тәуелді тәсілдермен: сол компилятор нұсқасына арналған құжаттама және ABI спецификациясы анықталмаған әрекетке шектеулер ұсына алады. Бағдарламалық жасақтаманың осы егжей-тегжейлеріне сүйенупортативті дегенмен, егер бағдарламалық жасақтама белгілі бір жұмыс уақытынан тыс пайдаланылмаса, портативтілік алаңдаушылық туғызбайды.

Анықталмаған мінез-құлық бағдарламаның бұзылуына немесе тіпті сәтсіздіктерге әкелуі мүмкін, оларды табу және бағдарламаны қалыпты жұмыс істейтін етіп жасау, мысалы, деректердің үнсіз жоғалуы және дұрыс емес нәтижелер шығару.

Артықшылықтары

Операцияны анықталмаған тәртіп ретінде құжаттау компиляторларға бұл әрекет ешқашан сәйкес бағдарламада болмайды деп ойлауға мүмкіндік береді. Бұл компиляторға код туралы көбірек ақпарат береді және бұл ақпарат оңтайландыру мүмкіндіктеріне әкелуі мүмкін.

Си тіліне мысал:

int ақымақ(қол қойылмаған char х){     int мәні = 2147483600; / * 32-биттік int және 8-биттік шаманы қабылдау * /     мәні += х;     егер (мәні < 2147483600)        бар();     қайту мәні;}

Мәні х теріс болуы мүмкін емес және қол қойылғанын ескере отырып толып жатқан бүтін сан С-де анықталмаған мінез-құлық болып табылады, компилятор оны қабылдауы мүмкін мәні <2147483600 әрқашан жалған болады. Осылайша егер операторға, оның ішінде функцияны шақыру бар, компилятор елемеуі мүмкін, өйткені сынақ өрнегі егер жоқ жанама әсерлері және оның жағдайы ешқашан қанағаттандырылмайды. Код мағыналық жағынан баламалы:

int ақымақ(қол қойылмаған char х){     int мәні = 2147483600;     мәні += х;     қайту мәні;}

Егер компилятор таңбалы толып кеткен деп қабылдауға мәжбүр болса орау мінез-құлық, онда жоғарыдағы трансформация заңды болмас еді.

Мұндай оңтайландыру коды неғұрлым күрделі және басқа оңтайландырулар сияқты болған кезде оны байқау қиынға соғады астарлау, орын алады. Мысалы, басқа функция жоғарыда аталған функцияны шақыруы мүмкін:

жарамсыз жұмыс_тапсырмалары(қол қойылмаған char *ptrx) {    int з;    з = ақымақ(*ptrx);    уақыт (*ptrx > 60) {        жұмыс_бір_тапсырма(ptrx, з);    }}

Компилятор опцияны оңтайландыру үшін еркін уақыт- өтінім беру арқылы осында айналдыру құндылықтар ауқымын талдау: тексеру арқылы foo (), ол көрсетілген бастапқы мән екенін біледі ptrx мүмкін 47-ден аспауы мүмкін (өйткені анықталмаған мінез-құлықты тудыруы мүмкін foo ()), сондықтан бастапқы тексеру * ptrx> 60 сәйкес бағдарламада әрқашан жалған болады. Нәтижеден бастап әрі қарай жүру з қазір ешқашан пайдаланылмайды және foo () жанама әсерлері жоқ, компилятор оңтайландыруы мүмкін run_tasks () дереу оралатын бос функция болуы керек. Жоғалу уақытцикл әсіресе таңқаларлық болуы мүмкін foo () а анықталады бөлек құрастырылған нысан файлы.

Белгіленген бүтін санның толып кетуіне жол бермеудің тағы бір артықшылығы - айнымалының мәнін сақтау мен басқаруға мүмкіндік береді. процессор тіркелімі бұл бастапқы кодтағы айнымалының өлшемінен үлкен. Мысалы, егер бастапқы кодта көрсетілген айнымалының түрі жергілікті регистрдің енінен тар болса (мысалы «int « үстінде 64 бит машина, жалпы сценарий), содан кейін компилятор айнымалы үшін қол қойылған 64 биттік бүтін санды қауіпсіз қолдана алады машина коды ол кодтың анықталған әрекетін өзгертпей шығарады. Егер бағдарлама 32 биттік бүтін санның толып кетуіне байланысты болса, онда компилятор 64 биттік машинаға компиляция жасағанда қосымша логика енгізуі керек еді, өйткені көптеген машиналық командалардың толып кету әрекеті регистрдің еніне байланысты.[3]

Анықталмаған мінез-құлық сонымен қатар компиляторлар мен компиляторлардың уақытты тексеруіне мүмкіндік береді статикалық бағдарламалық талдау.[дәйексөз қажет ]

Тәуекелдер

C және C ++ стандарттарында анықталмаған мінез-құлықтың бірнеше формалары бар, олар компиляторды енгізу кезінде еркіндікті жоғарылатады және егер бар болса, жұмыс уақытының анықталмаған әрекеті есебінен компиляция уақытын тексереді. Атап айтқанда, ISO стандартына сәйкес анықталмаған мінез-құлықтың жалпы көздерін көрсететін қосымша бар.[4] Сонымен қатар, компиляторлардан анықталмаған тәртіпке негізделген кодты диагностикалау талап етілмейді. Демек, бағдарламашыларға, тіпті тәжірибелі адамдарға, анықталмаған мінез-құлыққа қателесіп немесе жүздеген парақты қамти алатын тіл ережелерін жетік білмегендіктен сенім арту әдеттегі жағдай. Бұл басқа компилятор немесе басқа параметрлер қолданылған кезде пайда болатын қателерге әкелуі мүмкін. Тестілеу немесе түсініксіз динамикалық анықталмаған мінез-құлық тексерулерімен қосылады, мысалы Қоңырау тазартқыштар, компилятормен немесе статикалық анализаторлармен анықталмаған анықталмаған мінез-құлықты ұстауға көмектеседі.[5]

Анықталмаған мінез-құлық әкелуі мүмкін қауіпсіздік бағдарламалық жасақтаманың осалдығы. Мысалы, буфердің толып кетуі және қауіпсіздіктің басқа да осалдықтары веб-шолғыштар анықталмаған мінез-құлыққа байланысты. The 2038 жыл байланысты тағы бір мысал қол қойылған толып жатқан бүтін сан. Қашан GCC әзірлеушілер 2008 жылы компиляторын өзгертті, осылайша анықталмаған мінез-құлыққа негізделген толып жатқан тексерулер алынып тасталды, CERT компилятордың жаңа нұсқаларына қарсы ескерту шығарды.[6] Linux апталық жаңалықтары дәл осындай мінез-құлық байқалғанын атап өтті PathScale C, Microsoft Visual C ++ 2005 және басқа бірнеше құрастырушылар;[7] кейінірек әр түрлі компиляторлар туралы ескерту үшін ескертуге өзгертулер енгізілді.[8]

C және C ++ тілдеріндегі мысалдар

С-да анықталмаған мінез-құлықтың негізгі формаларын кеңінен жіктеуге болады:[9] жад қауіпсіздігінің кеңістікті бұзуы, уақытша жад қауіпсіздігін бұзу толып кету, қатаң сәйкестендіру ережелерін бұзу, туралауды бұзу, нәтижесіз модификациялау, деректер жарыстары және енгізу-шығаруды жүзеге асырмайтын және тоқтатпайтын циклдар.

С-да кез-келгенін қолдану автоматты айнымалы ол инициалданбағанға дейін бүтін сан сияқты анықталмаған мінез-құлықты береді нөлге бөлу, массивті оның белгіленген шектерінен тыс индекстеу, қол қойылған бүтін сандық толтыру (қараңыз) буферден асып кету ), немесе нөл көрсеткіш кейінге қалдыру. Жалпы алғанда, анықталмаған мінез-құлықтың кез-келген данасы абстрактілі орындау машинасын белгісіз күйде қалдырады және бүкіл бағдарламаның әрекетін анықталмауға мәжбүр етеді.

А өзгертуге тырысу жол сөзбе-сөз анықталмаған мінез-құлықты тудырады:[10]

char *б = «уикипедия»; // жарамды С, C ++ 98 / C ++ 03-де ескірген, C ++ 11 жағдайында дұрыс қалыптаспағанб[0] = 'W'; // анықталмаған мінез-құлық

Бүтін нөлге бөлу нәтижесі анықталмаған тәртіпке әкеледі:[11]

int х = 1;қайту х / 0; // анықталмаған мінез-құлық

Белгілі бір көрсеткіш әрекеттері анықталмаған әрекетке әкелуі мүмкін:[12]

int arr[4] = {0, 1, 2, 3};int *б = arr + 5;  // шекарадан тыс индекстеу үшін анықталмаған мінез-құлықб = 0;int а = *б;        // нөлдік көрсеткішті анықтауға арналған анықталмаған тәртіп

C және C ++ тілдерінде реляциялық салыстыру көрсеткіштер нысандарға (салыстырудан кіші немесе үлкенірек үшін) қатаң түрде анықталады, егер көрсеткіштер сол объектінің мүшелерін немесе сол элементтерді көрсетсе массив.[13] Мысал:

int негізгі(жарамсыз){  int а = 0;  int б = 0;  қайту &а < &б; / * анықталмаған тәртіп * /}

Мәнді қайтаратын функцияның соңына жету (басқа) негізгі ()) қайтару операторынсыз анықталмаған әрекетке әкеліп соқтырады, егер функционалдық шақырудың мәнін қоңырау шалушы қолданса:[14]

int f(){}  / * егер функционалдық шақырудың мәні қолданылса, анықталмаған мінез-құлық * /

Екі нысанды өзгерту реттілік нүктелері бірнеше рет анықталмаған мінез-құлықты тудырады.[15] C ++ 11 жағдайындағы реттілік нүктелеріне байланысты анықталмаған мінез-құлықты тудыратын айтарлықтай өзгерістер бар.[16] Келесі мысал C ++ және C-де анықталмаған мінез-құлықты тудырады.

мен = мен++ + 1; // анықталмаған мінез-құлық

Нысанды екі дәйектілік нүктесінің арасында түрлендіру кезінде объектінің мәнін сақтау үшін мәнді анықтаудан басқа кез келген мақсатта оқу да анықталмаған тәртіп болып табылады.[17]

а[мен] = мен++; // анықталмаған мінез-құлықprintf(«% d% d n", ++n, күш(2, n)); // сонымен қатар анықталмаған мінез-құлық

C / C ++ тілінде ығысу теріс сан болатын немесе осы мәндегі биттердің жалпы санынан көп немесе тең болатын биттер саны мәні анықталмаған әрекетке әкеледі. Ең қауіпсіз тәсілі (компилятор жеткізушісіне қарамастан) әрдайым ауысатын биттер санын сақтау (дұрыс операнд << және >> разрядтық операторлар ) шегінде: <0, өлшемі (мән) * CHAR_BIT - 1> (қайда мәні сол жақтағы операнд).

int сан = -1;қол қойылмаған int вал = 1 << сан; // теріс санға ауысу - анықталмаған тәртіпсан = 32; // немесе кез келген сан 31-ден үлкенвал = 1 << сан; // '1' әріптік мәні 32 биттік бүтін сан түрінде теріледі - бұл жағдайда 31 биттен артық ауысу анықталмаған тәртіп болып табыладысан = 64; // немесе кез келген сан 63-тен үлкенқол қойылмаған ұзақ ұзақ val2 = 1ULL << сан; // '1ULL' әріптік мәні 64 биттік бүтін сан түрінде теріледі - бұл жағдайда 63 биттен артық ауысу анықталмаған әрекет болып табылады

Сондай-ақ қараңыз

Әдебиеттер тізімі

  1. ^ «мұрын жындары». Жаргон файлы. Алынған 12 маусым 2014.
  2. ^ GCC анықталмаған мінез-құлық тазартқышы - ubsan
  3. ^ https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7#file-gistfile1-txt-L166
  4. ^ ISO / IEC 9899: 2011 §J.2.
  5. ^ Джон Регер. «2017 жылғы анықталмаған мінез-құлық, cppcon 2017».
  6. ^ «VU № 162289 осалдығы туралы ескерту - gcc қораптың кейбір тексерулерін үнсіз тастайды». Осалдық туралы ескертулер дерекқоры. CERT. 4 сәуір 2008. мұрағатталған түпнұсқа 9 сәуірде 2008 ж.
  7. ^ Джонатан Корбет (16 сәуір 2008). «GCC және көрсеткіш толып кетті». Linux апталық жаңалықтары.
  8. ^ «VU № 162289 осалдығы туралы ескерту - С компиляторлары кейбір тексерулерді үнсіз тастауы мүмкін». Осалдық туралы ескертулер дерекқоры. CERT. 8 қазан 2008 [4 сәуір 2008].
  9. ^ Паскаль Куок және Джон Регер (4 шілде 2017). «Academia блогына ендірілген 2017 жылғы анықталмаған мінез-құлық».
  10. ^ ISO /IEC (2003). ISO / IEC 14882: 2003 (E): бағдарламалау тілдері - C ++ §2.13.4 ішекті литералдар [lex.string] параграф. 2018-04-21 121 2
  11. ^ ISO /IEC (2003). ISO / IEC 14882: 2003 (E): бағдарламалау тілдері - C ++ §5.6 Көбейткіш операторлар [expr.mul] параграф. 4
  12. ^ ISO /IEC (2003). ISO / IEC 14882: 2003 (E): бағдарламалау тілдері - C ++ §5.7 аддитивті операторлар [expr.add] параграф. 5
  13. ^ ISO /IEC (2003). ISO / IEC 14882: 2003 (E): бағдарламалау тілдері - C ++ §5.9 Реляциялық операторлар [мән] параграф. 2018-04-21 121 2
  14. ^ ISO /IEC (2007). ISO / IEC 9899: 2007 (E): бағдарламалау тілдері - C §6.9 Сыртқы анықтамалар параграф. 1
  15. ^ ANSI X3.159-1989 Бағдарламалау тілі C, ескерту 26
  16. ^ «Бағалау тәртібі - cppreference.com». en.cppreference.com. 2016-08-09 қабылданды.
  17. ^ ISO /IEC (1999). ISO / IEC 9899: 1999 (E): бағдарламалау тілдері - C §6.5 Өрнектер параграф. 2018-04-21 121 2

Әрі қарай оқу

Сыртқы сілтемелер