2012-08-31 17 views
6

Istnieje kilka binarnych buforów o stałym rozmiarze w programie, które są używane do przechowywania danych. I memcpy służy do kopiowania bufora z jednego do drugiego. Ponieważ bufor źródłowy może być większy niż bufor docelowy. Jak wykryć przepełnienie bufora?Jak zapobiec przepełnieniu bufora memcpy?

+1

Wykryto? Znasz rozmiar bufora docelowego? Następnie wpisz kod podobny do tego memcpy (src, dst, sizeof (dst)). – BSen

+0

Porównaj rozmiar bufora źródłowego i bufora docelowego i zobacz, który jest większy? – SingerOfTheFall

+1

@Błąd, że '' sizeof'' po prostu poda rozmiar wskaźnika. – juanchopanza

Odpowiedz

8

Musisz wiedzieć, ile danych znajduje się w buforze źródłowym i ile miejsca jest dostępne w buforze docelowym.

Nie dzwoń pod numer memcpy(), jeśli w buforze docelowym nie ma wystarczającej ilości miejsca na dane, które chcesz skopiować z bufora źródłowego. (Musisz zdecydować, czy można obciąć dane, jeśli źródło jest większe od celu).

Jeśli nie wiesz, przepisz kod, aby wiedzieć, ile jest miejsca; w przeciwnym razie nie jest bezpieczny.

Należy pamiętać, że jeśli istnieje prawdopodobieństwo zachodzenia na siebie buforów źródłowego i docelowego, należy użyć raczej memmove() niż memcpy().

W języku C++ spójrz na znak zachęty, używając w pierwszej kolejności memcpy(); to jest operacja w stylu C zamiast C++.

+0

Dzięki. jaki jest właściwy sposób wykonywania kopii pamięci w C++? –

+1

@MichaelD: Przechowuj dane w 'std :: vector <>' i po prostu użyj 'vector2 = wektor1'. – MSalters

+0

Jak mogę wstawić dane do wektora? użyć funkcji push_back, aby wstawić bajt daty według bajtu? –

3

Powinieneś zawsze wiedzieć i sprawdzić rozmiar buforów src i dest!

void *memcpy(void *dest, const void *src, size_t n); 

n nigdy nie powinien być większy niż src lub dest wielkości.

0

Jeśli na przykład masz:

docelowy rozmiar 4 bajty

źródło 5 bajtów rozmiar

Możesz upewnić się skopiować co najwyżej 4 bajtów do bufora docelowego:

size_t getCopySize(size_t sourceSize, size_t destSize) 
{ 
    return (destSize <= sourceSize ? destSize : sourceSize); 
} 
memcpy(destination, source, getCopySize(sizeof(source),sizeof(destination))); 

Na podstawie Twojej aplikacji możesz również upewnić się, że pozostałe dane zostaną skopiowane później, lub możesz je pominąć, jeśli niektóre dane mogą zostać zignorowane.

4

Jak wykryć przepełnienie bufora?

Myślę, że masz trzy lub cztery możliwości (podać lub przyjąć).


Pierwszym wyborem jest zapewnienie "bezpiecznej" funkcji dla memcpy. Tego właśnie wymagam w kodzie pod moim spisem i regularnie go audytuję. Wymagam również, aby wszystkie parametry zostały zatwierdzone i wszystkie parametry zostały potwierdzone.

Asercje tworzą własny kod debugowania. Chcę, żeby programiści pisali kod; i nie chcę, żeby tracili czas na debugowanie. Tak więc wymagam od nich pisania kodu, który sam się debuguje. ASSERT również dokumentuje rzeczy dość dobrze, więc mogą skąpić w dokumentacji. W kompilacjach wersji, ASSERT są usuwane przez makra przedpporalne.

errno_t safe_memcpy(void* dest, size_t dsize, void* src, size_t ssize, size_t cnt) 
{ 
    ASSERT(dest != NULL); 
    ASSERT(src != NULL); 
    ASSERT(dsize != 0); 
    ASSERT(ssize != 0); 
    ASSERT(cnt != 0); 

    // What was the point of this call? 
    if(cnt == 0) 
     retrn 0; 

    if(dest == NULL || src == NULL) 
     return EINVALID; 

    if(dsize == 0 || ssize == 0) 
     return EINVALID; 

    ASSERT(dsize <= RSIZE_MAX); 
    ASSERT(ssize <= RSIZE_MAX); 
    ASSERT(cnt <= RSIZE_MAX); 

    if(dsize > RSIZE_MAX || ssize > RSIZE_MAX || cnt > RSIZE_MAX) 
     return EINVALID; 

    size_t cc = min(min(dsize, ssize), cnt); 
    memmove(dest, src, cc); 

    if(cc != cnt) 
     return ETRUNCATE; 

    return 0; 
} 

Jeśli safe_memcpy powraca non-0, a następnie wystąpił błąd jak zły parametr lub potencjalnego przepełnienia bufora.


Drugi wybór polega na użyciu "bezpieczniejszych" funkcji zapewnianych przez standard C. C ma "bezpieczniejsze" funkcje poprzez ISO/IEC TR 24731-1, Bounds Checking Interfaces. Na zgodnych platformach można po prostu zadzwonić pod numer gets_s i sprintf_s. Oferują one spójne zachowanie (jak zawsze zapewnienie, że ciąg jest kończony NULL) i spójne wartości zwracane (jak 0 w przypadku sukcesu lub errno_t).

errno_t err = memcpy_s(dest, dsize, src, cnt); 
... 

Niestety, gcc i glibc nie są zgodne ze standardem C. Ulrich Drepper (jeden z opiekunów glibc) nazwał interfejsy sprawdzania granic "horribly inefficient BSD crap" i nigdy nie zostały one dodane.


Trzecim wyborem jest użycie "bezpieczniejszych" interfejsów platformy, jeśli są obecne. W systemie Windows dzieje się tak, jak w przypadku ISO/IEC TR 24731-1, Bounds Checking Interfaces. Masz również bibliotekę String Safe.

W Apple i BSD nie masz "bezpieczniejszej" funkcji dla memcpy. Ale masz bezpieczniejsze funkcje łańcuchowe, takie jak strlcpy, strlcat i przyjaciele.


W systemie Linux, twoim czwartym wyborem jest użycie FORTIFY_SOURCE. FORTIFY_SOURCE używa "bezpieczniejszych" wariantów funkcji wysokiego ryzyka, takich jak memcpy, strcpy i gets. Kompilator używa bezpieczniejszych wariantów, gdy może wydedukować rozmiar bufora docelowego. Jeśli kopia przekroczyłaby docelowy rozmiar bufora, program wywoła abort(). Jeśli kompilator nie może wydedukować docelowego rozmiaru bufora, wówczas warianty "bezpieczniejsze" nie są używane.

Aby wyłączyć test FORTIFY_SOURCE, należy skompilować program pod numerem -U_FORTIFY_SOURCE lub -D_FORTIFY_SOURCE=0.

Powiązane problemy