Bush
Bush

Reputation: 2533

Getting segmentation fault SIGSEGV in memcpy after mmap

I wrote a simple Android native function that get a filename and some more arguments and read the file by mmapping (mmap) it's memory.

Because it's mmap, I don't really need to call "read()" so I just memcpy() from the address returned from the mmap().

But, somewhere I'm getting a SIGSEGV probably because I'm trying to access a memory which I not permitted. But I don't understand why, I already asked all file's memory to be mapped!

I'm attaching my code and the error I got:

EDIT

I fixed the unterminating loop, but still getting SIGSEGV after 25001984 bytes have been read. The function works on those arguments: jn_bytes = 100,000,000 jbuffer_size = 8192 jshared=jpopulate=jadvice=0

void Java_com_def_benchmark_Benchmark_testMmapRead(JNIEnv* env, jobject javaThis,
        jstring jfile_name, unsigned int jn_bytes, unsigned int jbuffer_size, jboolean jshared, jboolean jpopulate, jint jadvice) {
    const char *file_name = env->GetStringUTFChars(jfile_name, 0);

    /* *** start count  *** */
    int fd = open(file_name, O_RDONLY);
    //get the size of the file
    size_t length = lseek(fd, 0L, SEEK_END);
    lseek(fd, 0L, SEEK_SET);
    length = length>jn_bytes?jn_bytes:length;

    // man 2 mmap: MAP_POPULATE is only supported for private mappings since Linux 2.6.23
    int flags =  0;
    if (jshared) flags |= MAP_SHARED; else flags |= MAP_PRIVATE;
    if(jpopulate) flags |= MAP_POPULATE;
    //int flags = MAP_PRIVATE;
    int *  addr = reinterpret_cast<int *>(mmap(NULL, length , PROT_READ, flags , fd, 0));
    if (addr == MAP_FAILED) {
        __android_log_write(ANDROID_LOG_ERROR, "NDK_FOO_TAG", strerror(errno));
        return;
    }
    int * initaddr = addr;
    if(jadvice > 0)
        madvise(addr,length,jadvice==1?(MADV_SEQUENTIAL|MADV_WILLNEED):(MADV_DONTNEED));
    close(fd);

    char buffer[jbuffer_size];
    void *ret_val = buffer;
    int read_length = length;
    while(ret_val == buffer || read_length<jbuffer_size) {
/*****GETTING SIGSEGV SOMWHERE HERE IN THE WHILE************/
        ret_val = memcpy(buffer, addr,jbuffer_size);
        addr+=jbuffer_size;
        read_length -= jbuffer_size;
    }
    munmap(initaddr,length);
    /* stop count */
    env->ReleaseStringUTFChars(jfile_name, file_name);
}

and the error log:

    15736^done
(gdb) 
15737 info signal SIGSEGV
&"info signal SIGSEGV\n"
~"Signal        Stop\tPrint\tPass to program\tDescription\n"
~"SIGSEGV       Yes\tYes\tYes\t\tSegmentation fault\n"
15737^done
(gdb) 
15738-stack-list-arguments 0 0 0
15738^done,stack-args=[frame={level="0",args=[]}]
(gdb) 
15739-stack-list-locals 0
15739^done,locals=[]
(gdb) 

Upvotes: 1

Views: 3018

Answers (4)

Paul R
Paul R

Reputation: 212969

There is a big problem here:

    addr+=jbuffer_size;

You're bumping addr by sizeof(int) * jbuffer_size bytes whereas you just want to increment it by jbuffer_size bytes.

My guess is sizeof(int) is 4 on your system, hence you crash at around 25% of the way through your loop, because you're incrementing addr by a factor of 4x too much on each iteration.

Upvotes: 3

ymett
ymett

Reputation: 2454

The condition in while(ret_val == buffer || read_length<jbuffer_size) is wrong. ret_val == buffer will always be true, and if read_length<jbuffer_size is true when the loop is reached, it will always remain true because read_length is only ever reduced (well, until it underflows INT_MIN).

Upvotes: 1

hmjd
hmjd

Reputation: 121971

The while loop is infinite:

while(ret_val == buffer || read_length<jbuffer_size) {
    ret_val = memcpy(buffer, addr,jbuffer_size);
    addr+=jbuffer_size;
    read_length -= jbuffer_size;
}

as memcpy() always returns the desintation buffer so ret_val == buffer will always be true (and is therefore useless as part of the terminating condition). This means that addr is being incremented by jbuffer_size bytes on every iteration of the loop and is passed to memcpy(), resuting in accessing invalid memory.

Upvotes: 1

john
john

Reputation: 8027

This loop never terminates because ret_val always equals buffer

void *ret_val = buffer;
int read_length = length;
while(ret_val == buffer || read_length<jbuffer_size) {
    /*****GETTING SIGSEGV SOMWHERE HERE IN THE WHILE************/
    ret_val = memcpy(buffer, addr,jbuffer_size);
    addr+=jbuffer_size;
    read_length -= jbuffer_size;
}

memcpy always returns it's first argument, so ret_val never changes.

Upvotes: 1

Related Questions