Search code examples
clinuxmmap

Bus Error when trying to write data into memory mapped file


I'm seeing a bus error when I try to update the memory mapped region in a file. I Googled and come to know this happens when one attempts to perform read/write beyond mapped region. I already cross-checked and nothing seems to be wrong here. Here is a code:

typedef int fd_t;
#define free_pg_bitmap_size 1

typedef struct db_file_hdr_ {

    uint64_t allocated_pg_bitmap[free_pg_bitmap_size];

} db_file_hdr_t;


void *
disk_io_file_mmap (fd_t fd, uint64_t start_offset, uint64_t end_offset) {

    void *pptr = mmap (NULL, end_offset - start_offset, 
                                    PROT_READ | PROT_WRITE, MAP_SHARED, 
                                    fd, start_offset);

    assert (pptr);
    return pptr;
}

fd_t
db_file_open (const char* path) {
    FILE *file = fopen (path, "w+");
    if (!file) assert(0);
    fd_t fd = fileno (file);
    return fd;
}

static void 
db_file_init_hdr (fd_t fd) {
    int i;
    db_file_hdr_t *db_hdr = (db_file_hdr_t *)disk_io_file_mmap 
                          (fd, 0, sizeof (db_file_hdr_t));

    for (i = 0 ; i < free_pg_bitmap_size; i++) {
        db_hdr->allocated_pg_bitmap[i] = 0;         **<<<<< Bus Error here**
    }
    disk_file_unmap ( (void *)db_hdr, sizeof (db_file_hdr_t));
}


void
disk_io_create_disk_file (const char* path, fsize size) {

fd_t fd;

FILE *file = fopen (path, "w+");

if (!file) assert(0);

fd = fileno (file);

if (ftruncate(fd, size) == -1) {
    assert(0);
}

assert (fsync(fd) != -1);
fclose (file);
}

void
db_file_create_db_file (const char* path, uint64_t size) {

fd_t fd;

disk_io_create_disk_file (path, 
    sizeof (db_file_hdr_t) + 
    (DB_FILE_DEF_PAGE_CNT *  DB_PAGE_DEF_SIZE));

fd = db_file_open  (path);

/* initialize the Hdr of the DB file*/
db_file_init_hdr (fd);

close (fd);
}

int 
main (int argc, char **argv) {

/* Create a DB file on disk*/
db_file_create_db_file ("sample1.db", 0);

Solution

    1. You are checking for mmap failure incorrectly: mmap returns MAP_FAILED, which is not NULL. Also, as others have commented, using assert() to check for errors is fundamentally wrong.
    2. You right-size the file with ftruncate, but then reopen it with fopen (path, "w+"); in db_file_open(). From man fopen:
    w+     Open for reading and writing.
           The file is created if it does not exist, otherwise it is truncated.
           The stream is positioned at the beginning of the file.
    

    The otherwise it is truncated is the key part -- you negate the effect of the earlier ftruncate by performing openat(AT_FDCWD, "sample1.db", O_RDWR|O_CREAT|O_TRUNC, 0666) (that's what strace shows on my system -- doubtless coming from the fopen implementation).

    Since you've just emptied the file (truncated to 0 size), SIGBUS is entirely expected.

    You can verify that this is in fact happening with ls -l sample1.db after the program crashes.