2010-03-25 29 views
5

Sto scrivendo un demone ad alto carico che dovrebbe essere eseguito su FreeBSD 8.0 e su Linux. Lo scopo principale del demone è passare i file richiesti dal loro identificatore. L'identificatore viene convertito in dimensione del nome file/file locale tramite richiesta a db. E poi io uso sequenzialmente le chiamate mmap() per passare i blocchi di file con send().controllare se l'indirizzo mmap'ed è corretto

Tuttavia a volte ci sono disallineamenti di file in db e filesize su file system (dimensioni reali < in db). In questa situazione ho inviato tutti i blocchi di dati reali e quando viene mappato il prossimo blocco di dati - mmap non restituisce errori, solo il solito indirizzo (ho controllato anche la variabile errno, è uguale a zero dopo mmap). E quando il daemon tenta di inviare questo blocco ottiene Segmentation Fault. (Questo comportamento viene emesso con certezza su FreeBSD 8.0 amd64)

Stavo usando il controllo di sicurezza prima di aprire per garantire le dimensioni con chiamata stat(). Comunque la vita reale mi mostra che il segfault può ancora essere generato in rare sessioni.

Quindi, la mia domanda è un modo per verificare se il puntatore è accessibile prima di dereferenziarlo? Quando ho aperto core in gdb, gdb dice che l'indirizzo dato è fuori limite. Probabilmente c'è un'altra soluzione che qualcuno può proporre.

#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/mman.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <time.h> 
#include <unistd.h> 

#define FILENAME  "./datafile" 

int main() 
{ 
    unsigned long i, j; 

    srand(time(NULL)); 
    unsigned long pagesize = sysconf(_SC_PAGESIZE); 

    unsigned long basesize = 4 * pagesize; 
    unsigned long cropsize = 2 * pagesize; 

    // create 4*pagesize sized file 
    int f = creat(FILENAME, 0644); 
    for (i = 0; i < basesize; i++) { 
     unsigned char c = (unsigned char)rand(); 
     if (write(f, &c, 1) < 1) { perror("write"); break; } 
    } 
    close(f); 

    f = open(FILENAME, O_RDONLY); 

    // walk trough file 
    unsigned char xor = 0; 
    unsigned long offset = 0; 
    for (j = 0; j < 4; j++) { 
     // trunc file to 2*pagesize 
     if (j == 2) truncate(FILENAME, cropsize); 

     char *data = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE, f, offset); 
     if (data == MAP_FAILED) { perror("mmap"); break; } 
     printf("mmap: %[email protected]%lu for %i\n", pagesize, offset, f); 

     for (i = 0; i < pagesize; i++) xor ^= data[i]; 

     offset += pagesize; 
    } 

    close(f); 

    return 0; 
} 

risposta

2

Naturalmente non posso provarlo da qui, ma ho il forte sospetto che basta un bug contabilità nel codice. Se si chiama mmap e si passa in una dimensione, e ci riesce, non si dovrebbe ottenere SIGSEGV.

Vi raccomando di applicare valgrind alle vostre indagini.

Su molti sistemi Linux/proc/PID/mappe vi mostrerà quali regioni sono mappate con quali permessi di accesso.

+0

Ho inserito un esempio di codice che illustra il problema in testa. Questo codice emula la creazione del file e quindi lo ridimensiona mentre è stato eseguito con mmap. Su sistema Linux ho il Bus Error al terzo passaggio, su FreeBSD c'è SegFault. – reddot