2012-08-31 10 views
6

Ci sono alcuni buffer binari con dimensioni fisse in un programma che vengono utilizzati per memorizzare i dati. E memcpy è usato per copiare il buffer da uno all'altro. Poiché il buffer di origine potrebbe essere più grande del buffer di destinazione. Come posso rilevare se c'è overflow del buffer?Come evitare l'overflow del buffer memcpy?

+1

Rileva? Conosci la dimensione del buffer di destinazione? Quindi scrivere il codice come questa memcpy (src, dst, sizeof (dst)) – BSen

+0

Confrontare le dimensioni del buffer di origine e del buffer di destinazione e vedere quale è più grande? – SingerOfTheFall

+1

@BSen che '' sizeof'' darà solo la dimensione di un puntatore. – juanchopanza

risposta

8

È necessario conoscere la quantità di dati presente nel buffer di origine e la quantità di spazio disponibile nel buffer di destinazione.

Non chiamare memcpy() se non c'è spazio sufficiente nel buffer di destinazione per tutti i dati che si desidera copiare dal buffer di origine. (È necessario decidere se è corretto troncare i dati se la fonte è più grande della destinazione.)

Se non si conosce, riscrivere il codice in modo da sapere quanto spazio ci sia; altrimenti, non è sicuro.

Nota: se è possibile che i buffer di origine e di destinazione si sovrappongano, è necessario utilizzare memmove() anziché memcpy().

In C++, guardare in primo luogo utilizzando memcpy() in primo luogo; questa è un'operazione in stile C piuttosto che C++.

+0

Grazie. qual è il modo corretto di fare la copia di memoria in C++? –

+1

@MichaelD: memorizza i tuoi dati in un 'std :: vector <>', e usa semplicemente 'vector2 = vector1'. – MSalters

+0

Come posso inserire dati nel vettore? usa push_back per inserire data byte per byte? –

3

Si dovrebbe sempre conoscere e controllare la dimensione dei buffer src e dest!

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

n non dovrebbe mai essere più grande di quanto src o dest dimensioni.

0

Se ad esempio si dispone di:

destinazione 4 byte dimensioni

fonte 5 byte dimensioni

Si può fare in modo di copiare, al massimo, 4 byte di buffer di destinazione:

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

In base all'applicazione, è possibile anche assicurarsi che i dati rimanenti vengano copiati in un secondo momento, oppure è possibile saltarlo se alcuni dati possono essere ignorati.

4

Come posso rilevare se c'è overflow del buffer?

Penso che tu abbia tre o quattro scelte (dare o prendere).


La prima scelta consiste nel fornire una funzione di "sicurezza" per memcpy. Questo è quello che richiedo nel codice sotto la mia competenza, e regolarmente controllo per questo. Richiedo inoltre che tutti i parametri siano convalidati e che tutti i parametri siano asseriti.

Le asserzioni creano un codice di auto-debug. Voglio che gli sviluppatori scrivano il codice; e non voglio che perdano tempo nel debugging. Quindi chiedo loro di scrivere il codice che esegue il debug di se stesso. ASSERT documenta anche le cose piuttosto bene, in modo da poter lesinare sulla documentazione. Nelle build di rilascio, gli ASSERT vengono rimossi dalle macro di preporcessor.

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; 
} 

Se i safe_memcpy rendimenti non-0, poi c'è stato un errore come un brutto parametro o potenziale buffer overflow.


La seconda scelta è l'uso di funzioni "più sicure" fornite dallo standard C. C ha funzioni "più sicure" tramite ISO/IEC TR 24731-1, Bounds Checking Interfaces. Su piattaforme conformi, puoi semplicemente chiamare gets_s e sprintf_s. Offrono un comportamento coerente (come garantire sempre una stringa terminata con NULL) e valori di ritorno coerenti (come 0 in caso di successo o errno_t).

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

Sfortunatamente gcc e glibc non sono conformi allo standard C. Ulrich Drepper (uno dei manutentori di glibc) ha chiamato bounds controllando le interfacce "horribly inefficient BSD crap" e non sono mai stati aggiunti.


La terza scelta è quella di utilizzare interfacce "più sicuri" della piattaforma, se presente. Su Windows, sembra essere uguale a quelli di ISO/IEC TR 24731-1, Bounds Checking Interfaces. Hai anche la libreria String Safe.

Su Apple e BSD, non si dispone di una funzione "più sicura" per memcpy. Ma hai funzioni di stringa più sicure come strlcpy, strlcat e amici.


Su Linux, la quarta scelta è utilizzare FORTIFY_SOURCE. FORTIFY_SOURCE utilizza varianti "più sicure" di funzioni ad alto rischio come memcpy, strcpy e gets. Il compilatore utilizza le varianti più sicure quando può dedurre la dimensione del buffer di destinazione. Se la copia supera la dimensione del buffer di destinazione, il programma chiama abort(). Se il compilatore non può dedurre la dimensione del buffer di destinazione, le varianti "più sicure" non vengono utilizzate.

Per disabilitare FORTIFY_SOURCE per il test, è necessario compilare il programma con -U_FORTIFY_SOURCE o -D_FORTIFY_SOURCE=0.

Problemi correlati