- A hozzászóláshoz be kell jelentkezni
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
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
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
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
#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
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