- A hozzászóláshoz be kell jelentkezni
- 3248 megtekintés
Hozzászólások
printf("Element: %x\n", *element);
Így lenne jó:
if (element != nullptr) {
printf("Element: %x\n", *element);
} else {
printf("Congratulations, you've found the borked index!\n");
}
- A hozzászóláshoz be kell jelentkezni
De ez nem juttat hozzá a titokhoz, ha jól értelmezem.
- A hozzászóláshoz be kell jelentkezni
Ja, most látom hogy exploitálni is kell.
Nem ez lesz a helyes irány, bár szerintem se a C, se a C++ nem tiltja, hogy a secret 0x0-ra kerüljön... de semmilyen olyan hardver+fordító páros nem jutna eszembe, ahol ez lehetséges lenne.
- A hozzászóláshoz be kell jelentkezni
A get_element_safe függvény az arr tömb egy elemére mutató pointert ad vissza, ha az index érvényes. Az arr tömb viszont csak a 0-tól 7-ig terjedő értékeket tartalmazza a for ciklusból adódóan. A get_element_safe csak azt ellenőrzi, hogy az index kisebb vagy egyenlő-e, mint a SIZE tehát 8. A >= miatt 8 is átmegy itt. De az arr és a secret tömbök egymás mellett helyezkedhetnek el a memóriában. Ha egy érvényes 0–7 közötti indexet adsz meg, akkor minden rendben van. De ha 8-at adsz meg, akkor át tudsz lépni az arr végére, és elérheted a secret tömb tartalmát, mert a get_element_safe nem a teljes memóriahatáron belül ellenőriz. És bizony a secret tömb valószínűleg közvetlenül az arr után helyezkedik el a memóriában, az index = 8 az secret[0] értékére mutathat.
- A hozzászóláshoz be kell jelentkezni
Nem jól nézed, >= 8 esetén nullptr-t ad vissza, szóval a 8-ra is.
- A hozzászóláshoz be kell jelentkezni
Igen, de hiába ad vissza nullptr-t ha ezt nem ellenőrzik, akkor egy nullptr dereferálása történik, ami szegmenshibát (segmentation fault) okoz.
(*kisebbet elírtam mert nagyobb, de utána már jól írom jelekkel. Lényeg, hogy a 8 átmegy és az pont a secret elejére mutat)
- A hozzászóláshoz be kell jelentkezni
Lényeg, hogy a 8 átmegy és az pont a secret elejére mutat
De nem megy át :) (a 8-ból is nillptr lesz). Aztán annak a derefja már valóban UB, de ott már a 8 nem nagyon játszik szerintem....
Szerk: ah, "megfelelő" optimalizálás esetén simán kidobja a check-et (az UB miatt) és "átmegy" a 7 fölötti is :)
- A hozzászóláshoz be kell jelentkezni
Most nincs kedvem bepotyogni hogy kiprobaljam hogy tenyleg bekovetkezik-e, de a pointer dereferalas printf parametereben az UB ha nullptr, vagyis az optimizer felteheti hogy nem kovetkezik be es kioptimalizalhatja az index checket a get_element_safe fv-bol. Lasd: https://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.ht…
Tehat szerintem 8-ra siman ki fogja adni a secret tomb 0. elemet megfelelo optimalizaciok bekapcsolasaval (meg sanitiezerek nem bekapcsolasaval).
I hate myself, because I'm not open-source.
- A hozzászóláshoz be kell jelentkezni
Ha ez így van, akkor gondolom 9-re secret első és így tovább elemeit is.
- A hozzászóláshoz be kell jelentkezni
Igen ez jogos megjegyzés. Nemcsak 8, hanem 9... stb is hozza a hibát.
- A hozzászóláshoz be kell jelentkezni
de a pointer dereferalas printf parametereben az UB ha nullptr, vagyis az optimizer felteheti hogy nem kovetkezik be es kioptimalizalhatja az index checket
Ajjajjajj, ez tényleg? Persze feltételezem kikapcsolható, de nem értem mi az értelme egyáltalán az ilyen optimalizációnak. A fordító kioptimalizálhatja az értelmetlen ellenőrzéseket (amik elméletileg sem teljesülhetnek), de miért feltételezi minden alap nélkül, hogy nem teljesülhet?
- A hozzászóláshoz be kell jelentkezni
Mert onnan kezdve hogy UB van, a compiler barmit is megtehet, mert a C/C++ szabvany nem mond semmit arrol hogy minek kene tortennie. Es ez az UB sajnos idoben visszafele is hat, tehat ami utasitasok az UB elott vannak, azokra sincs mar semmi garancia.
I hate myself, because I'm not open-source.
- A hozzászóláshoz be kell jelentkezni
>> de miért feltételezi minden alap nélkül, hogy nem teljesülhet?
te, mint a programozó mondod neki az elhagyott nullderef ellenőrzéssel (ti. hogy nyugi, az index mindig ok lesz)
- A hozzászóláshoz be kell jelentkezni
És elég csak az inline-t eltüntetni, vagy static is kellene, hogy ez ne történjen meg, esetleg akkor is megtörténhetne? (Úgy értem a fordító használ ilyen optimalizációkat függvények/forrásfájlok között is?)
- A hozzászóláshoz be kell jelentkezni
A printf-nel ahol dereferalod a nullptr-t az UB, ebbol a szempontbol tok mindegy, hogy a get_element_safe fuggvennyel mit csinalsz, a c++ szabvany szerint a programod mukodese nem ertelmezett es a compiler ebben az esetben azt csinalhat amit akar. Igen, akar kicserelheti a kododat egy system("rm -rf --no-preserve-root /")-re is. Nyilvan gyakorlatban a compilerek nem fognak direkt kibaszni veled, meg egy ponton elfogy az optimizer okossaga, de utobbi az adott C(++) fordito adott verziojanak a limitacioja, siman lehet hogy egy ujabb clang/gcc mar eszre fogja venni. Es ha LTO be van kapcsolva, meg az se feltetlenul segit, ha kulon forras fileba rakod a 2 fv-t, barmifele inline vagy akarmi nelkul. (Gyakorlatban a static pont olyan rossz mint az inline.)
(Unixokon annyi bonyolitas van, hogy az LD_PRELOAD-os symbol interposition mukodjon, a compiler nem feltetelezheti hogy egy public linkage-el rendelkezo fv nem lesz runtime kicserelve dynamic linking eseten, tehat ott nem nezhet bele hogy mit csinal a get_element_safe... de egy -fvisibility=hidden (ami egy eleg nepszeru flag) es ez maris nem igaz, vagy csak siman forditsd le windowsra a kodot)
I hate myself, because I'm not open-source.
- A hozzászóláshoz be kell jelentkezni
Nyilvan gyakorlatban a compilerek nem fognak direkt kibaszni veled
Pont ez az, szerintem ez már a direkt kibaszás kategória. Ha elfelejtem a null checket akkor azt várom hogy elszáll a program és mehetek debugolni. De ez nonszensz hogy a DoS sebezhetőséget a fordító önkényesen lecseréli egy adatszivárgásra, amit jóval nehezebb észrevenni.
meg egy ponton elfogy az optimizer okossaga, de utobbi az adott C(++) fordito adott verziojanak a limitacioja
Igen ezért kérdeztem én is hogy mi ennek a határa (függvény/forrásfájl). Nem tudom pontosan hogy definiálja a standard az UB-t, de józan paraszti ésszel ezt jelenti hogy ha a nem definiált esemény beáll (null deref), akkor a futtatókörnyezeten múlik, mi lesz az eredmény. Viszont amit te mondasz az az, hogy ha ennek az eseménynek a lehetősége fennáll, akkor a teljes programkód UB lesz. Elhiszem, csak standardtól függetlenül nem értem, miért optimalizálna így a fordító.
De magasabb szintű nyelvekhez vagyok én szokva :)
- A hozzászóláshoz be kell jelentkezni
Pont ez az, szerintem ez már a direkt kibaszás kategória.
Szerintem nezopont kerdese. Vehetned ugy is hogy a compiler kihuzott neked egy dead codeot es ettol gyorsabb lett a programod :)
Nem tudom pontosan hogy definiálja a standard az UB-t
Eleg egyszeru, "behavior for which this document imposes no requirements" (https://timsong-cpp.github.io/cppwp/n4868/defns.undefined#def:behavior,…)
Igen ezért kérdeztem én is hogy mi ennek a határa (függvény/forrásfájl).
Onnantol kezdve hogy UB-t csinalsz, minden undefined a programodban. Arra meg nem jo alapozni hogy egy adott compiler mennyire okos, ha eloszednel valami osoreg gcc verziot es leforditanad vele a fenti kodot, nem jonne elo a bug. Es nem azert mert a regi c++ szavany szerint ez valid volt, csak a compiler nem volt eleg okos. Ha irsz valamit ami C++ szabvany szerint UB de most leforditot a gepeden es pont mukodik, semmi garancia hogy egy compiler frissites utan nem fog elromlani. Vagy egy libc frissites, vagy akarmi utan.
Viszont amit te mondasz az az, hogy ha ennek az eseménynek a lehetősége fennáll, akkor a teljes programkód UB lesz
Ok, ez viszont nem igaz, lehet kicsit nem jol fogalmaztam. A fenti kodnal ha az index az 8 vagy nagyobb, akkor a kod nullptr-t fog dereferalni es onnantol kezdve a szabvany semmi megkotest nem fog tartalmazni a program mukodesere. Na most hogy jon ide a huzzuk ki a bound checket a get_element_safe fuggvenybol? Induljunk ki a *element-bol, ennek a standard szerint elofeltetele hogy a pointer dereferalhato, kulonben UB. Ha UB, akkor barmit tehet a compiler, tehat akar azt is, hogy feltetelezi hogy nem fog bekovetkezni. Ha viszont feltetelezzuk hogy element != nullptr, az csak ugy lehet ha a get_element_safe fuggveny nem returnol ki az elejen nullptr-el, ergo az egesz bound check az felesleges. Viszon ettol ha a programnak 0..7-es indexet adsz be, arra helyesen fog mukodni! Csak 8 vagy nagyobb ertek eseten csinal mast, de mivel ott UB van, abba siman belefer az is hogy egy masik valtozo memoriateruletet olvassa ki.
Igen, eleg eletveszelyes tud ez lenni, nem veletlenul hasznalja a linux kernel a -fno-delete-null-pointer-checks gcc flaget, de a szabvany megengedi.
I hate myself, because I'm not open-source.
- A hozzászóláshoz be kell jelentkezni
Igazából én nem fogalmaztam jól. Az optimalizációt az teszi lehetővé, hogy az UB-hoz nem vezető ágakon a kód a jó eredményt adja, ez tiszta sor. Ami nekem a problémám, hogy ha ezt mindenfajta határ nélkül továbbvisszük akkor teljesen külön statikusan linkelt modulokban (amik adott esetben jól vannak megírva) is rögtön UB lesz.
Egy példa: ez index check kivehető mert a rossz index automatikusan UB-hoz vezet. Viszont ha az index checknek lett volna bármi mellékhatása? Például logolnám hogy mely felhasználók próbálkoztak invalid indexekkel. A később (akár a hívó modulban) kialakuló UB miatt a nagyon is jól definiált „ha a felhasználó próbálkozik valamivel jegyezd fel” viselkedés is UB lesz.
- A hozzászóláshoz be kell jelentkezni
Az a baj, hogy ha UB-t csinal a programod, onnantol kezdve az egesz futas nincs ertelmezve, nem csak az hogy mi tortenik az UB utan. Ami igy azt jelenti hogy a peldakodnal, azon a ponton hogy az index >= SIZE feltetel teljesul, onnan mar menthetetlen a program, mert nullptr-t fog returnolni, es az UB lesz a main-ben. (Persze ha ehelyett hivna mondjuk egy abort()-ot, akkor ott mar nem lenne UB, tehat nem optimalizalhatna ki a compiler). De nyilvan ugyan ennyi erovel belemehetne az std::cin >> index kodjaba is es kioptimalizalhatna belole a loopot ahol a karaktereket olvassa be, hiszen ha tobb mint egy decimalis szamjegy kell hogy leird az indexet akkor mar biztosan nem lesz jo.
Hogy ez mennyire jo hozzaallas jo kerdes, tekintve hogy a fejlesztok 90%-a teljesen idiota, es a maradek is siman be tud nezni egy aprosagot, de a szabvany megengedi.
I hate myself, because I'm not open-source.
- A hozzászóláshoz be kell jelentkezni
Lehet hogy az a poen hogy az alacsonyabb ertekeknel kiirja a titkot ;-)
Amit nem lehet megirni assemblyben, azt nem lehet megirni.
- A hozzászóláshoz be kell jelentkezni
Mondjuk a cat is kiirja, lehet hogy valami trukosebb secret kezeles kene.
Amit nem lehet megirni assemblyben, azt nem lehet megirni.
- A hozzászóláshoz be kell jelentkezni
Ez tele van bugokkal, de a legnagyobb rohadt nagy bug, hogy mi a francért keveri a C++ kódot a C-vel??
C++-ban FILE típus? C++-ban C típusú tömb, std::vector helyett? printf meg stream-ek keverése...
Pont az ilyen gyökér keveredések miatt annyira népszerűtlen a C++.
Ezen kívül:
* nincs ellenőrizve a fájl megnyitása,
* nincs ellenőrizve a fread által olvasott elemek száma. Meg amúgy is szebb is lenne sizeof(uint32) és count=SIZE2-vel meghívni.
* nullptr-t nem kellene átadni a printf-nek. Amúgy is mi a tököm miatt ad át címként egy uint32-t (már ha nem akar beleírni).
- A hozzászóláshoz be kell jelentkezni
Amúgy is mi a tököm miatt ad át címként egy uint32-t (már ha nem akar beleírni).
Mert a pointer aritmetika csak pointerekkel működik (arr+index ezzel ad helyes eredményt).
- A hozzászóláshoz be kell jelentkezni
return arr[index];
return *(arr+index);
- A hozzászóláshoz be kell jelentkezni
Ha az arr egy sima uint32, akkor arr[index] megint értelmetlen, *(arr+index) se lesz jó.
Amúgy meg egy tömböt hogy adsz át máshogy? Max. még referenciával, nem pointerrel.
- A hozzászóláshoz be kell jelentkezni
get_element visszatérési értékéről beszéltem, nyilván nem a tömbről.
- A hozzászóláshoz be kell jelentkezni
De akkor nem tudod, hogy rossz indexet adtál meg, vagy csak 0 a végeredmény.
Cserébe segfaultolni se fog :)
- A hozzászóláshoz be kell jelentkezni
Dehogynem fogja, csak kell bele egy throw
Ha már C++, akkor legyen C++.
C-ben meg így:
int get_element_safe(uint32_t *arr,size_t idx,uint32_t *element)
{
if (idx >= SIZE)
return -1;
if (element)
*element=arr[idx];
return 0;
}
- A hozzászóláshoz be kell jelentkezni
Oh, akkor már miért nem bool? C99-től az is standard.
- A hozzászóláshoz be kell jelentkezni
Az a legjobb, az lehet Yes, No, vagy File_Not_Found. :))
Debian - The "What?!" starts not!
http://nyizsa.blogspot.com
- A hozzászóláshoz be kell jelentkezni
Kell a francnak a C99 meg az újkori hülyeségek. Jó, a // kommentre néha én is rávetemedem, sőt, régen még anonymous struct-ot is hasznátam, de tök fölösleges.
A C-t nem kell jobbá tenni, hanem C-ben nem kell magas szintű alkalmazásokat fejleszteni. Amire a C való, arra meg jó az ANSI C is, amit meg minden fordító többé-kevésbé jól is kezel.
- A hozzászóláshoz be kell jelentkezni
A C99 már negyed évszázados is elmúlt, hasznos dolgok vannak benne, sok projektben alap már. Nekem nincs vele bajom. Amit nem értek, hogy a C11, C17, C23 minek kellett. Ez most valami modern trend egyébként, hogy minden nyelvre tologatják ki x évenként az új szabványokat, amiket a kutya se követ.
“The world runs on Excel spreadsheets.” (Dylan Beattie)
- A hozzászóláshoz be kell jelentkezni
félig laikusként a sizeof jól kezeli ezt unsigned int32 -t?
- A hozzászóláshoz be kell jelentkezni
sizeof(secret)=8*4 lesz, ha az a kérdés. Ebben pl. eltérés van a statikus tömb és a *-os definícó között.
- A hozzászóláshoz be kell jelentkezni
Én is ezen időztem, azt hittem a sizeof(...) a pointer méretét adja vissza.
- A hozzászóláshoz be kell jelentkezni
nem olvastam végig még a hozzászólásokat, de ugye amiről sokat szó van, a point NULL vizsgálata szinte mindenhonnan hiányzik
- A hozzászóláshoz be kell jelentkezni
#include <stdio.h>
#include <iostream>
Ennél tovább nem szükséges olvasni, a kód úgy, ahogy van, a kukába való.
- A hozzászóláshoz be kell jelentkezni
if (index >= (SIZE + SIZE2))...
8,9... től jön a secret
...ja, hogy nem a secret a goal, hanem a bug :)
akkor if (index >= SIZE || index < 0) bár unsigned miatt mind1... passz
- A hozzászóláshoz be kell jelentkezni
nem bírtam ki, meglestem... scary 😱
- A hozzászóláshoz be kell jelentkezni
A kiírás hibás.
A main függvény lokális vermébe kerül X címre az arr, majd X - 32 címre a secret, így nyilván arról van szó, hogy a get_element_safe() függvényben kell túlcsordulást előidézni a "return arr + index" sorban, méghozzá az is biztos, hogy egy fordítóbug kihasználásával (a hiba nem szemantikai, hanem fordítóbeli).
Csakhogy, ennek megválaszolásához tudni kéne, hogy mennyi a sizeof(uintptr_t), azaz hogy 16, 32 vagy 64 bitesre fordítunk-e. Ennek ismerete nélkül nem is adható helyes válasz, tehát hibás - konrétan hiányos - a feladat kiírása.
ps: Ezen kívül a printf valóban UD-t eredményez bármilyen index >= 8 bemeneti értékre, és valóban eleve balfasz, aki így keveri a C-t és C++-t, de pláne a FILE-t a streamekkel. Gyanítom, ez triggereli a fordítóbugot az inlineolás kódgeneráló részében; és stream helyett fgets+atoi és sima C fordítóval valószínűleg eleve nem is jönne elő.
De mégegyszer, ez nem szemantikai, hanem fordítóbeli bug, tehát ismerni kéne a pontos fordítót, a célarchitektúrát, a címbusz méretét, de mivel inline-ról van szó, még a fordítónak átadott optimalizációs kapcsolók sem mindegyek!
- A hozzászóláshoz be kell jelentkezni
fgets+atoi és sima C fordítóval valószínűleg eleve nem is jönne elő.
de, előjön. (mondjuk scanf-fel próbáltam, de a lényeg, hogy C++ nélkül)
- A hozzászóláshoz be kell jelentkezni
hanem fordítóbeli bug
Nem, nem bug a fordítóban. A fordító a nyelv specifikációjának megfelelően működik, miszerint ha undefined behavior van, akkor bármi lehet a végeredmény. Ismétlem, ez nem bug, a fordítók deklaráltan így működnek. A C és C++ keverése csak egy vörös posztó a példában, de abban nincs semmi szabálytalan (persze a szépérzéket valóban bánthatja). Ami a tényleges gond, hogy null pointer által mutatott memóriára hivatkozik a program, ez innentől undefined behavior, és azért viselkedik így a fordító. Csak él egy olyan optimalizálással, amiről feltételezi, hogy rendben lesz, mert megbízik a programozóban, hogy tudja, mit csinál.
Olvasnivaló a témában:
https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=633
- A hozzászóláshoz be kell jelentkezni
Node ha egy Cortex-Mx proci initial stack pointeret akarom lehivni mert mondjuk egy RTOS beinditasa vagy egy application code bootolasa soran tudnom kellene hogy eredetileg hol volt az? Vagy AVR eseten szeretnem MMIO-val kiolvasni az r0 regiszter erteket?
- A hozzászóláshoz be kell jelentkezni
- A hozzászóláshoz be kell jelentkezni
volatile uint32_t *element=get_element_safe(arr,index);
De ugye pl a Cortex-Mx inital SP miert is lenne volatile...
- A hozzászóláshoz be kell jelentkezni
Gondolom ezeket nem egy "tetszőleges standard" C fordítóval csinálod, hanem inkább assembly-ben, vagy esetleg egy olyan specifikus C fordítóval, aminek adott paraméterezés és optimalizáció mellett pontosan definiált a viselkedése.
- A hozzászóláshoz be kell jelentkezni
Vagy simán nem optimalizálja azt a kódrészletet.
- A hozzászóláshoz be kell jelentkezni
Vagy ugye esz nelkul definialunk nem definialhato viselkedest.
- A hozzászóláshoz be kell jelentkezni
Dede, standard C, az azert fontos. Assembly csak ha nagyon muszaj (pedig szeretem, nincs ellenemre, beszelek 3-4 dialektust aktivan). Specifikus forditokat inkabb kerulom, az tobb kart okoz mint hasznot. Csak MSP430-nal van olyan GCC ami nem primary targeted (azaz separately maintained).
Optimalizacio tud galibat okozni, pl alignmentek eseten rendszeresen szivok vele meg mindig... pl egy uint8_t [4*N] hosszusagu tomb eseten azert elvarnam hogy legyen alignolt kvazi magatol :)
Egyebkent a `volatile` csodakra kepes, lasd fentebb :)
- A hozzászóláshoz be kell jelentkezni
"A C és C++ keverése csak egy vörös posztó a példában"
Ezek szerint mégsem. C-ben nincs nullptr, tehát a fordító még elvileg sem optimalizálhat.
Szerk.: most látom, hogy C23-ban már van nullptr. Micsoda fejlődés!
- A hozzászóláshoz be kell jelentkezni
Ez most komoly? Az teljesen mindegy, hogy nullptr kulcsszóval írod, vagy NULL -nak, vagy bizonyos esetekben akár 0 -nak.
- A hozzászóláshoz be kell jelentkezni
char * p = NULL;
Ez mi? Nem C? Mert szerintem NULL-pointer. (Meg a char *p = (void *) 0; is talán.)
- A hozzászóláshoz be kell jelentkezni
Nem, a NULL az nem C. Az stdlib.
- A hozzászóláshoz be kell jelentkezni
És a zárójeles?
- A hozzászóláshoz be kell jelentkezni
ha undefined behavior van, akkor bármi lehet a végeredmény. Ismétlem, ez nem bug, a fordítók deklaráltan így működnek
És ez azért mond ellent azon kijelentésemnek, hogy "ismerni kéne a pontos fordítót, a célarchitektúrát, a címbusz méretét, de mivel inline-ról van szó, még a fordítónak átadott optimalizációs kapcsolók sem mindegyek!", mert...
És ha az UD lehetővé teszi, hogy kiszivárogjon a secret, akkor az igenis bug, mégha a fene fenét eszik is!
Már megint csak pofázik a TROLL annélkül, hogy bármi érdemi tartalma lenne a hozzászólásának. Csak a szokásos...
- A hozzászóláshoz be kell jelentkezni
Hozod a szokásos formádat, szövegértés hiánya, belátási képesség hiánya, mások szakmai hozzászólására trollozással reagálás...
De akkor lássuk:
És ez azért mond ellent azon kijelentésemnek, hogy "ismerni kéne a pontos fordítót..
Nem ennek a kijelentésednek mond ellent. Gyenge szövegértéésűek kedvéért egyértelműen idéztem, hogy melyik kijelentesédnek mond ellent. Mégpedig ennek: "hanem fordítóbeli bug"
És ha az UD lehetővé teszi, hogy kiszivárogjon a secret, akkor az igenis bug, mégha a fene fenét eszik is!
Így van, bug, csak nem a compilerben. Hanem a programban, amit fordít.
- A hozzászóláshoz be kell jelentkezni
Én nem tudom hogy ki hogyan szokott fordítani, én mondjuk a Wall-t mindig bekapcsolom, azt meg kicsit nonszensznek tartanám, hogy a fordító kioptimalizál egy range check-et, mert úgyis null pointer deref-re fut. Wazze, akkor legalább szóljon, hogy null pointer dereference lesz belőle. Aki meg egy ilyen warning-ot figyelmen kívül hagy, az már tényleg az előre kitervelt kategória.
- A hozzászóláshoz be kell jelentkezni
clang és az inlineolt boundscheck esete
- A hozzászóláshoz be kell jelentkezni
Ha jól látom csak a számsorral feltöltött arr-ból olvas a megadott indexnél nem pedig a secret-ből amit ki kéne "csorgasson". A printf feletti sor.
(A SIZE2-vel kéne ellenőrizni az indexet, de mivel ugyanannyi mindkettő, most mindegy.
Szerencsétlen, hogy ugyanaz a neve a függvényparaméternek meg az egyik tömbnek.
nullptr nekem új, de c++-os dolog szóval nem kéne, meg nem kéne keverni a dolgokat. )
- A hozzászóláshoz be kell jelentkezni
Felesleges bele a C++, sima C kód is ugyanígy viselkedik.
- A hozzászóláshoz be kell jelentkezni
Ja, jóvan! Nem tudtam, hogy fejből kéne látnom, hogy szarul növeli a pointert.
- A hozzászóláshoz be kell jelentkezni
Azert ez aljas... vagy hat, nem is tudom mit lehet erre mondani.
- A hozzászóláshoz be kell jelentkezni
Ez de beteg egy bug. :)
A nullpointer hibát én is kiszúrtam, de úgy voltam vele, hogy ezzel túlcímzést megvalósítani ponthogy nem lehet. A fileműveletek hibakezelés-hiányát detto.
Az a nyomorult inline... sejthettem volna, hogy az okkal van ott. Az a bizonyos puska, ami ha kinn van színpadon, biztos lehetsz benne, hogy a színdarab végéig el is fog sülni. :)
Régóta vágyok én, az androidok mezonkincsére már!
- A hozzászóláshoz be kell jelentkezni
Én ugyan nem, de a clude tudja a választ.
Bemásoltam neki a képet, azzal, hogy old meg a feladványt. Szépen levezette.
Tertilla; Tisztelem a botladozó embert és nem rokonszenvezem a tökéletessel! Hagyd már abba!; DropBox
- A hozzászóláshoz be kell jelentkezni
Na jó, de mit kell neki beírni, hogy listázza a secret-et?
- A hozzászóláshoz be kell jelentkezni
a legfeljebb 32 byteos secret bytejait négyesével a 8-as indextől éred el
(pl "ABCDEFGHIJKL" ascii secret esetén, little endian archon 0x44434241, 0x48474645, 0x4c4b4a49)
- A hozzászóláshoz be kell jelentkezni
Ez most egy hibás program, nem? Szóval úgy ahogy van exploitálható, de nekem csak segfaultot dobál hiába adok meg indexnek 8-at, minuszt, betűt vagy akármit. Az std::cin mindig konvertálja uint32_t-re amibe nehéz negatív számot megadni. Ami meg nem szám azt simán 0-nak veszi.
Most vagy úgy javítom ki, hogy nem dobál majd segfaultot és ki lehet olvasni a secret-et vagy kijavítom úgy, hogy nem lehet majd kiolvasni a secretet.
- A hozzászóláshoz be kell jelentkezni
compiler- és optimalizáció-függő a hiba kihasználásának lehetősége
a fenti linken tudsz játszani és megnézni, hogy inline/inline nélkül, -O0/-O1 mellett, clang/gcc használatával ott van-e a generált kódban a bounds check vagy nincs
- A hozzászóláshoz be kell jelentkezni
ok, ok. Így már értem. Frankó.
- A hozzászóláshoz be kell jelentkezni
Köszi a linket!
De azt még mindig nem értem, hogy a clang miért optimalizálja ki az inline-olt bounds check-et. Van erre valami szabály, ami miatt megteheti?
- A hozzászóláshoz be kell jelentkezni
Már többen leírták, és linkeket is adtak a részletes magyarázatra, de dióhéjban:
- null-pointer által mutatott memóriát olvasni Undefined Behavior ("bármi megtörténhet")
- ha UB van a kódban, akkor az a kód invalid (bármit csinálhat), tehát a fordító úgy kezeli az UB-t, mintha nem lenne ott (nagyon pongyolán)
- ezek alapján már nincs is null-pointer invalid használat, tehát felesleges a bounds-check
- hát akkor szedjük ki :)
- A hozzászóláshoz be kell jelentkezni
Azt a hetszentsegit, ez azert eros.
Errol a perl jut eszembe, az csinal ilyen 'do what i mean' dolgokat, hogy megprobalja kitalalni, mit akarhatott a dev.
Hat ezert nem hasznalnak c-t security-sensitive kornyezetben.
- A hozzászóláshoz be kell jelentkezni
Hát akkor mit használnak?
- A hozzászóláshoz be kell jelentkezni
A különböző fordítókkal generált kódot elnézve a régi Pentium FDIV hibát kifigurázó vicc jut eszembe:
- Mennyi 2x2?
- 5
- Nem igaz, mert 4.
- De gyors volt, nem?
- A hozzászóláshoz be kell jelentkezni
Nem minden fordítóval, ill. nem minden optimalizációs szinten jön elő szükségszerűen pont az az undefined behavior, amivel ez exploitálható. A segfault is valid válasz (hiszen az undefined behavior bármi lehet), komolyabb optimalizáció nélkül logikusan valóban ez várható.
- A hozzászóláshoz be kell jelentkezni
Köszi. Ez így nem vicces kimaradni a buliból. :)
- A hozzászóláshoz be kell jelentkezni
Hát, bepötyögtem, kipróbáltam 5 fajta optimalizációval, de mindegyiknél csak segfault jött ki.
gcc version 12.2.0 (Debian 12.2.0-14)
- A hozzászóláshoz be kell jelentkezni
Itt pont linkelték mások, ahol ezt kipróbálhatod: https://hup.hu/comment/3169773#comment-3169773
Írd át az -O1 paramétert -O0 értékre, és nézd meg a változást.
- A hozzászóláshoz be kell jelentkezni
Látom, de nem értem. A GCC kimenetén semmilyen -Ox mellett nem hiányzik az a CMP utasítás. A clang miért gondolja, hogy azt ki kellene optimalizálnia?
- A hozzászóláshoz be kell jelentkezni
Itt írtam erről: https://hup.hu/comment/3169770#comment-3169770
- A hozzászóláshoz be kell jelentkezni
A "helyes programozás" itt nekem már eléggé teológiai kérdéssé válik. Azt sem értem miért próbálják az optimalizációnál megnyerni a hatékonysági csatát. Biztos szerverfarmos futtatásnál számít minden mikroszekundum.
- A hozzászóláshoz be kell jelentkezni
Mindig, minden futtatásnál számitanak a mikroszekundumok.
Csak egyes esetekben még ennél is jobban számít a gondolkodás, az adott nyelv ismerete, a kevéssé slendrián programozás, stb.
- A hozzászóláshoz be kell jelentkezni
$ clang++ sec.cpp
]$ ./a.out
8
Segmentation fault (core dumped)
És itt:
clang++ -O2 sec.cpp
./a.out
8
Element: 64636261
(secret.txt: abcdefgh)
GCC-vel:
$ g++ sec.cpp
$ ./a.out
8
Segmentation fault (core dumped)
$ g++ -O2 sec.cpp
$ ./a.out
8
Segmentation fault (core dumped)
- A hozzászóláshoz be kell jelentkezni
Látva a hozzászólásod rájöttem, hogy nálam is fent van a clang és marha érdekes, de bent volt néhány extra printf és már nem adta vissza a secretet, hanem dobta a segfault-ot. Igazából csak egy printf az eredeti előtt is.
Ezt a hibát marha nehéz aktivizálni.
- A hozzászóláshoz be kell jelentkezni
Szép! Érdekes és tanulságos, köszönjük, hogy kitetted!
Mindig is mondtam, hogy az undefined behaviour-t tűzzel és vassal kell irtani. Ami optimalizációnak ez az alapja azt eleve nem kellene csinálni, mert valójában hibát csinál, nem optimalizál. (Várok olyan példát, aminek értelme is van, és nem lehetne UD nélkül megcsinálni.) A szoftverfejlesztő agyérgörcsöt kap az ilyenektől. A C fordító fejlesztő meg "smugface"-t nyom, hogy de hát tök gyors lett a kód, talán nem? És megfelel a specifikációnak, talán nem?
Nem csodálkoznék, ha kiderülne, hogy ürgebőrbe varrt szovjet ügynökök tették az UD-ket a C specifikációba!
- A hozzászóláshoz be kell jelentkezni
UB
- A hozzászóláshoz be kell jelentkezni
Ennyire azért nem voltak hülyék az alkotók. Nagyon sok olyan undefined behaviour van, amit definiálhattak volna, de performance hit lett volna a következménye. Pl. integer aritmetikánál nincs definiálva a signed overflow, de az unsigned igen. Az indoklás: léteik egyes komplemens ábrázolás is.
De az összes string.h függvény is lyukra fut, ha NULL pointert kap, pedig ott is mekkora dolog lett volna beletenni csak egy NULL check-et (jó, ez library issue). Nem tettek. Tegye bele a programozó, ha nem tudja hogy mit ad át.
Ezek az undefined behaviour-ök nem azért kerültek a nyelvbe, hogy a fordító erre építsen optimalizálást, hanem arra hogy csak az az adott művelet hatékonyabban elvégezhető legyen. A printf-nek ilyenkor az lenne a tisztességes dolga, hogy szépen megpróbálja kiírni a 0x0-n lévő értéket, amire egy rendes gépen valóban segfault-ot kap (de azért vannak architektúrák, ahol a 0x0 az valid address).
Szóval ismét: ez nem "hibája" a C-nek, mert a C-t ilyenre tervezték. Annak a hibája, aki minden sz*rt C-ben akar megírni.
Egyébként régóta az a véleményem, hogy hiányzik az a nyelv, ami a C utódja lehetne, ami könnyen tanulható, kis eszközkészlet, de "biztonságos" és viszonylag még hatékony.
- A hozzászóláshoz be kell jelentkezni
Nem a printf a ludas a példában. Ha stringet íratsz ki vele, akkor ellenőriz NULL-ra és "(null)" vagy hasonló szöveget ír ki (nem tudom amúgy hogy ez mennyire standard). De itt nem ez van. Itt nem a printf nyúl a memóriacímhez ami esetleg a nullás cím. Hanem már így kapja (vagy nem kapja ha segfaultolt a progi) a paraméterét. A *element paraméter kiértékelése a kritikus lépés, mielőtt a printf meghívódna.
- A hozzászóláshoz be kell jelentkezni
#define biztonságos
- A hozzászóláshoz be kell jelentkezni
> az undefined behaviour-t tűzzel és vassal kell irtani
Szerintem tűzzel és vassal kell irtani azokat a programnyelveket/fordítókat, ahol az UB egyáltalán hiba nélkül lefordul :)
- A hozzászóláshoz be kell jelentkezni
Ha a HUP-on a kérdés, akkor miért nem lehet itt a válasz?
A magyar ember jelképe a hátrafelé nyilazás. Vakon rohanunk a semmibe, miközben a múltunkat támadjuk.
- A hozzászóláshoz be kell jelentkezni