Czy malloc () przydziela ciągły blok pamięci?
Mam kawałek kodu napisany przez bardzo starego programistę :-). brzmi to mniej więcej tak
typedef struct ts_request
{
ts_request_buffer_header_def header;
char package[1];
} ts_request_def; ts_request_def* request_buffer =
malloc(sizeof(ts_request_def) + (2 * 1024 * 1024));
programista pracuje głównie nad koncepcją przepełnienia bufora. Wiem, że kod wygląda na wątpliwy. Więc moje pytania to:
- Czy malloc zawsze przydziela ciągły blok pamięci ponieważ w tym kodzie, jeśli bloki nie są ciągłe, kod będzie długo nie działać
- Wykonując
free (request_buffer)
, zwolni wszystkie bajty przydzielone przez malloc, to znaczysizeof (ts_request_def) + (2 * 1024 * 1024)
, lub tylko bajty rozmiaru strukturysizeof (ts_request_def)
- Czy widzisz jakieś oczywiste problemy z tym podejściem, muszę omówić to z moim szefem i chciałbym wskazać wszelkie luki w tym podejściu
Nie znaleziono powiązanych wyników
Zaproszony:
Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się
14 odpowiedzi
Anonimowy użytkownik
Potwierdzenie od:
Pozwala na to najnowszy standard C, ISO/IEC 9899: 1999 (nieformalnie C99)
elastyczne elementy tablicy
http://www.comeaucomputing.com ... rrays
.
Przykładem może być:
Ta ustandaryzowana funkcja pozwoliła Ci uniknąć używania popularnego, ale niestandardowego rozszerzenia implementacji, które opisałeś w swoim pytaniu. Ściśle mówiąc, używanie nieelastycznego elementu tablicy i dostęp poza nim jest niezdefiniowanym zachowaniem, ale wiele implementacji dokumentuje to i zachęca do tego.
Oprócz,
gcc
http://gcc.gnu.org/
przyznaje
tablice o zerowej długości
http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
jako rozszerzenie. Tablice o zerowej długości nie są dozwolone w standardzie C, ale gcc wprowadził tę funkcję, zanim C99 dał nam elastyczne składowe tablicy.
W odpowiedzi na komentarz wyjaśnię, dlaczego poniższy fragment kodu jest technicznie niezdefiniowanym zachowaniem. Numery punktów, które cytuję, odnoszą się do C99 (ISO/IEC 9899: 1999)
Po pierwsze, 6.5.2.1 # 2 pokazuje, że a [i] jest identyczne z (* ((a) + (i))), więc x- & > arr [23] jest równoważne z (* ((x - & gt ; arr) + (23))). Teraz 6.5.6 # 8 (O dodawaniu wskaźników i liczb całkowitych) mówi:
„Jeśli zarówno operand wskaźnika, jak i wynik wskazują na elementy tego samego obiektu tablicy lub jeden po ostatnim elemencie obiektu tablicy, obliczenia nie powinny przepełniać; w przeciwnym razie
zachowanie jest nieokreślone
."
Z tego powodu, ponieważ x- & > arr [23] nie znajduje się wewnątrz tablicy, zachowanie jest niezdefiniowane. Nadal możesz myśleć, że to jest w porządku, ponieważ malloc () sugeruje, że tablica została teraz rozszerzona, ale nie jest to do końca prawdą. Dodatek informacyjny J.2 (zawierający przykłady nieokreślonych zachowań) zawiera dalsze wyjaśnienia na przykładzie:
Indeks tablicy jest poza zakresem, mimo że obiekt wydaje się być dostępny za pomocą
podany indeks (jak w wyrażeniu lwartość a [1] [7] z podaną deklaracją int
a[4][5]) (6.5.6).
Anonimowy użytkownik
Potwierdzenie od:
Anonimowy użytkownik
Potwierdzenie od:
2) Tak, będzie. Alokacja pamięci wewnętrznej będzie śledzić ilość pamięci przydzielonej za pomocą tej wartości wskaźnika i całkowicie ją zwolnić.
3) jest to trochę hack językowy i trochę wątpliwe, czy jest używany. Nadal jest podatny na przepełnienia bufora, po prostu atakujący może zająć trochę więcej czasu, aby znaleźć ładunek, który go wyzwala. Koszt „ochrony” jest również dość wysoki (czy naprawdę potrzebujesz> 2 MB na bufor żądań?). Jest też bardzo brzydki, chociaż Twój szef może nie doceniać tego argumentu :)
Anonimowy użytkownik
Potwierdzenie od:
Jeśli próbujesz pokazać swojemu szefowi, że jesteś mądrzejszy niż „bardzo stary programista”, ten kod nie jest dla Ciebie. Stara szkoła niekoniecznie jest zła. Wygląda na to, że gość ze starej szkoły wie wystarczająco dużo o zarządzaniu pamięcią;)
Anonimowy użytkownik
Potwierdzenie od:
Myślę, że jest mało prawdopodobne, aby zrobił to dokładnie, ponieważ jeśli tego chciał, mógłby to zrobić za pomocą uproszczonego równoważnego kodu, który nie wymaga żadnych sztuczek;
Założę się, że faktycznie robi coś takiego;
To, co chce osiągnąć, to przydzielenie żądania ze zmiennym rozmiarem pakietu X. Oczywiście deklarowanie rozmiaru tablicy zmiennej jest nielegalne, więc omija to sztuczką. Wygląda na to, że wie, co mi robi, sztuczka polega na zbliżeniu się do szanowanego i praktycznego końca skali oszustwa C.
Anonimowy użytkownik
Potwierdzenie od:
Fakt, że nie podoba ci się to z jakiegokolwiek powodu, nie wystarczy, aby się z nim spierać lub w pełni uzasadnić jego przepisanie. Przyjrzałbym się bliżej użyciu, spróbował zrozumieć, o czym myślał oryginalny programista, przyjrzałbym się przepełnieniu bufora (jak wskazał workmad3) w kodzie używającym tej pamięci.
Istnieje wiele typowych błędów, które możesz znaleźć. Na przykład, czy kod sprawdza, czy funkcja malloc () się powiodła?
Anonimowy użytkownik
Potwierdzenie od:
Ale jeśli masz na myśli więcej problemów z alokacją pamięci niż struct, to wcale nie jest zły projekt C (nie powiedziałbym nawet, że to oldschoolowe TO ...;))
Jeszcze ostatnia uwaga - chodzi o to, że znak [1] polega na tym, że kończący NULL będzie zawsze w zadeklarowanej strukturze, co oznacza, że w buforze może znajdować się 2 * 1024 * 1024 znaki i nie trzeba brać pod uwagę Wartości NULL z „+1”. To może brzmieć jak mały wyczyn, ale chciałem tylko to wskazać.
Anonimowy użytkownik
Potwierdzenie od:
Ma tę zaletę, że upraszcza zarządzanie pamięcią, a tym samym zapobiega ryzyku wycieków pamięci. Wystarczy uwolnić blok przewijania malloc. Z dodatkowym buforem potrzebujesz dwóch darmowych. Należy jednak zdefiniować i użyć funkcji destruktora, aby hermetyzować tę operację, aby zawsze można było zmienić jej zachowanie, na przykład przełączyć się na bufor buforowy lub dodać dodatkowe operacje, które mają być wykonane po usunięciu struktury.
Dostęp do elementów tablicy jest również nieco wydajniejszy, ale w przypadku nowoczesnych komputerów ma to coraz mniejsze znaczenie.
Kod będzie również działał poprawnie, jeśli wyrównanie pamięci zmieni się w strukturze z różnymi kompilatorami, ponieważ zdarza się to dość często.
Jedynym potencjalnym problemem, jaki widzę, jest to, że kompilator zmienia kolejność, w jakiej przechowywane są zmienne składowe, ponieważ ta sztuczka wymaga, aby pole pakietu pozostało ostatnie w sklepie. Nie wiem, czy standard C zabrania permutacji.
Należy również zauważyć, że przydzielony bufor prawdopodobnie będzie większy niż wymagany o co najmniej jeden bajt, z dodatkowymi bajtami wypełniającymi, jeśli takie istnieją.
Anonimowy użytkownik
Potwierdzenie od:
Anonimowy użytkownik
Potwierdzenie od:
Na przykład sprawdź bardzo popularną strukturę nagłówka BITMAP.
http://msdn.microsoft.com/en-u ... .aspx
http://msdn.microsoft.com/en-u ... .aspx
Ostatnią ćwiartką RBG jest tablica o rozmiarze 1, która zależy od tej konkretnej techniki.
Anonimowy użytkownik
Potwierdzenie od:
zawsze zwalnia całą przydzieloną pamięć za jednym razem.
Anonimowy użytkownik
Potwierdzenie od:
Zobacz to pytanie StackOverflow (czy ktoś może wyjaśnić tę definicję struktury dirent w solaris?)
https://coderoad.ru/563045/
.
Anonimowy użytkownik
Potwierdzenie od:
Co do hańby (tj. Pytanie 3), co programista próbuje zrobić z tą przydzieloną pamięcią?
Anonimowy użytkownik
Potwierdzenie od:
To jest to samo co
Mógłbyś pomyśleć, że alokuje 2 porcje pamięci, ale w Twoim umyśle są to „struktury”, „niektóre bufory”. Ale malloc w ogóle tego nie widzi.