0

I repost the question with full source code.

I'm struggling on Libaio for asynchronous file I/O.

I think it's too strange to understand.

If I malloc array of struct & call io_prep_pwrite like following :

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libaio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <assert.h>

#include "client_file.h"
#include "config.h"

#define MAX_AIO_SUBMIT 128
#define AIO_STRIPE 131072 // 1024*128 = 128KB


int open_rfile(char* filename, int* filesize, char* fileowner){

    struct stat st;
    struct passwd * pwd;

    int fd = open(filename, O_RDONLY);
    if (fd<0){
        perror("open file fail\n");
    }

    stat(filename, &st);
    *filesize = (int)st.st_size;
    pwd = getpwuid(st.st_uid);
    strcpy(fileowner, pwd->pw_name);

    return fd;
}

io_context_t ctx;
struct iocb* piocbarr[128];

//fd is opened before calling this function
int write_file(int fd, unsigned char* buf, int size, int offset){

memset(&ctx, 0x00, sizeof(ctx));

if (io_setup(128, &ctx) !=0){
    printf("io_setup error!\n");
    exit(1);
}

int num_iocb = (size+AIO_STRIPE -1) / AIO_STRIPE; //num_iocb is 8 when size is 1M

//temporary abortion
    assert(num_iocb<MAX_AIO_SUBMIT);

 //   struct iocb piocbs[8];

    struct iocb* piocbsz;
    piocbsz = malloc(sizeof(struct iocb)* num_iocb);
    memset(piocbarr, 0, sizeof(piocbarr));

    int remain = size;
    int i;

    printf("ret %p\n", __builtin_return_address(0));

    for (i=0;i<num_iocb;i++){

        io_prep_pwrite(piocbsz+i, fd, (void *)buf, (size_t)AIO_STRIPE<remain ? AIO_STRIPE : remain , (long long)offset);

        piocbarr[i] = piocbsz+i;

        printf("%p %p %d %d \n", buf, piocbarr[i], offset, remain);
        
        buf+=AIO_STRIPE;
        offset+=AIO_STRIPE;
        remain -= AIO_STRIPE;

    }
    printf("ret %p\n", __builtin_return_address(0));
    
    if (io_submit(ctx, num_iocb, piocbarr)!=num_iocb){
        perror("io submit error\n");
        goto exit;
    }
    printf("ret %p\n", __builtin_return_address(0));

    struct io_event e;
    memset(&e, 0, sizeof(struct io_event));
    struct timespec timeout;
    memset(&timeout, 0, sizeof(timeout));
    timeout.tv_sec = 1;
    int cnt = 0;
    printf("ret %p\n", __builtin_return_address(0));

    while(1){
        if (io_getevents(ctx, num_iocb, num_iocb, &e, &timeout) == num_iocb){
            break;
        }

        if (++cnt==3){
            perror("aio timeout!");
            break;
        }
    }

    printf("ret %p\n", __builtin_return_address(0));
exit:

    free(piocbsz);
    io_destroy(ctx);
    return 0;

}

and call this function like this :

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#include "client_file.h"

#define TESTFILE "aiotest"
#define MEGA (1024*1024)

int main(){

    int fd=open_wfile(TESTFILE);

    char buf[1*MEGA];
    int i;
    char a = 'A';
    int offset = 0;

    for (i=0;i<1024;i++){
        memset(buf+offset, a, 1024);
        offset+=1024;
        if ((++a) > 'z') a = 'A';
    }

    write_file(fd, buf, 1*MEGA, 0);


    close(fd);

    free(buf);
    return 0;
}

and I call the function with size = 1048576(1MB) like above, then

output:

ret 0x400b6a
760F8010 2578420 131072 917504
76118010 2578460 262144 786432
76138010 25784A0 393216 655360
76158010 25784E0 524288 524288
76178010 2578520 655360 393216
76198010 2578560 786432 262144
761B8010 25785A0 917504 131072
761D8010 25785E0 1048576 0

ret 0x400b6a
ret 0x400b6a
ret 0x400b6a
ret 0x2578460

I get SIGSEGV(segmentation fault) at the end of this function.

The output log said that return address is corrupted by the value which is the input of second io_prep_pwrite function call(0x2578460).

if I use array which is not created by malloc, It works fine(but I guess there also is stack corruption).

I changed just a few line like this :

//struct iocb* piocbs;
//piocbs = malloc(sizeof(*piocbs)*num_iocb); // I also tried sizeof(struct iocb) and check the size.

struct iocb piocbs[10];

...(same with above);

//free(piocbs);

I guess that stack is corrupted after calling io_getevents function because I set the wrong value to the iocb blocks(piocbs), Someone can help me understanding this?

H.U.N.
  • 95
  • 9
  • There is *still* a lot missing from the code you posted, and it is impossible for us to reproduce your error(s). However, have you checked this line: `int num_iocb = (size+AIO_STRIPE -1) / AIO_STRIPE;` and actually printed out what the calculated value of `num_iocb` ***really*** is? If it is less than 8, then you will have a problem. – Adrian Mole Jul 31 '20 at 10:07
  • Some remarks: `struct iocb test; piocbs[i] = test;` doesn't make sense, you are copying an uninitialized stack variable (by value) into a slot in your array. Also, you should use `%p` for printing pointers. – vgru Jul 31 '20 at 10:21
  • Why don't you post the full, exact code you are using? From this code, I'd say it's likely that `io_destroy(ctx);` accesses `piocbarr`, where you store pointers to `piocbs`. In that case, swapping `free(piocbs);` and `io_destroy(ctx);` should fix the issue. Also, it's `__builtin_return_address`, not `__bulitin_return_address` (built-in), so this is clearly not your actual code. – vgru Jul 31 '20 at 10:41
  • I apologize for not posting all my exact source code. I copied&pasted the source code. Thank you. – H.U.N. Aug 03 '20 at 06:23
  • Oh.. I find the cause of stack corruption. The 4th argument of io_getevents function is suggested to be array of io_event structure! I edit & test then I get correct results and there's no SIGSEGV. Thank you all! – H.U.N. Aug 03 '20 at 06:47

0 Answers0