Że mam tablicę C znaków char buf[15]
. Powiedz zmienna int set_me = 0
ma swoje dane przechowywane w pamięci bezpośrednio po char buf[15]
. Gdybym przepełnił buf
się ciągiem znaków "aaabbbcccdddeee\xef\xbe\xad\xde"
, czy set_me
typ danych zmieniłby się z liczby całkowitej na tablicę znaków?
8
Odpowiedzi:
Nie.
„Typ danych” zmiennej ma znaczenie tylko w kodzie źródłowym (a nawet tylko w niektórych językach). Mówi kompilatorowi, jak traktować zmienną.
Te typy danych wysokiego poziomu nie istnieją jako takie w skompilowanym (natywnym) kodzie. Mogą wpływać na to, jakie instrukcje generuje kompilator, ale same instrukcje nie dbają o to, czy dane reprezentują znak, czy liczbę.
Zmienne nie istnieją w sprzęcie. W sprzęcie masz lokalizacje pamięci i instrukcje, które na nich działają.
Zmienna może być postrzegana jako widok danych w miejscu pamięci - jeśli spojrzysz na tę samą pamięć nieco inaczej (inna zmienna różnego typu odnosząca się do tej samej lokalizacji), ta sama wartość binarna może mieć inne znaczenie .
Na przykład bajt 0x41 może być interpretowany jako znak zakodowany w UTF-8
A
. Można to również interpretować jako liczbę całkowitą jednobajtową65
. Może być również interpretowany jako jeden bajt w wielobajtowej liczbie całkowitej lub liczbie zmiennoprzecinkowej, lub jeden bajt w wielobajtowym kodowaniu znaków. To może być bitset0b1000001
. Wszystkie z tego samego bajtu w tej samej lokalizacji w pamięci. W języku C można zobaczyć ten efekt, rzutując na te różne typy.Kiedy masz „przepełnienie bufora”, robisz coś poza granicami tego, czego może oczekiwać Twój kompilator lub język. Ale jeśli chodzi o sprzęt 1 , zapisujesz bajty (pojedyncze lub wielokrotne) w miejscu w pamięci. Lokalizacja pamięci nie ma „typu”. W rzeczywistości sprzęt nie wie nawet, że określony zestaw bajtów tworzy tablicę lub bufor w kodzie.
Ilekroć następnym razem uzyskasz dostęp do tej lokalizacji pamięci w kodzie, instrukcje będą działać zgodnie z pierwotną definicją. np. jeśli spodziewali się tam liczby, będą działać na dowolnych bajtach danych, tak jakby byli liczbą.
Na przykład, zakładając, że
int
jest to 4-bajtowa (32-bitowa) liczba całkowita ze znakiem:Widać
int
teraz0xEFBEADDE
, że lokalizacja pamięci zawiera teraz , zakładając, że system big-endian 2 . To jest podpisany 32-bitowy int-272716322
. Teraz, jeśli interpretujesz tę samą pamięć jako int bez znaku int (uint
), byłoby to4022250974
zamiast tego. Dla dokładnie tych samych danych w pamięci znaczenie zależy całkowicie od tego, jak je przeglądasz.1 Istnieją pewne mechanizmy, które uniemożliwiają zapisywanie w chronionych obszarach pamięci i powodują awarię programu, jeśli spróbujesz to zrobić.
2 x86 jest właściwie little-endianem, co oznacza, że interpretujesz bajty składające się na większą wartość wstecz. Tak więc na x86 miałbyś zamiast tego
0xDEADBEEF
dać podpisany-559038737
lub niepodpisany3735928559
.źródło
0xdeadbeef
w architekturze x86 zajmowałoby mniej miejsca w pamięci niż jej dziesiętny odpowiednik3735928559
?0xDEADBEEF
jest przechowywany w pamięci jako0x30 0x78 0x44 0x45 0x41 0x44 0x42 0x45 0x45 0x46
.0xEFBEADDE
. Być może nieco to przeredagować. W przeciwnym razie jest to znakomita odpowiedź - szczególnie podoba mi się analogia „widoku” i pomysł „mrużącego oczy” :)Z perspektywy C odpowiedź brzmiałaby „Kto wie? Jest to niezdefiniowane zachowanie”.
Typy to koncepcja C, a nie sprzęt. Ale reguły C nie mają zastosowania, jeśli twój program ma Niezdefiniowane zachowanie, to jest dosłowne znaczenie Niezdefiniowane zachowanie w standardzie C. Przepełnienia buforów są jedną z nich.
Początkowo napisałem „zasady C już nie obowiązują”, ale w rzeczywistości Niezdefiniowane zachowanie działa wstecz. Reguły C nie mają zastosowania do programu, który będzie miał niezdefiniowane zachowanie w przyszłości.
źródło