lyfeng
lyfeng

Reputation: 76

Invalid argument when read() from timerfd

I have a program that creates a timer using timerfd_create() and the timer is specified with an interval so that the process is notified regularly. Then this timer is registered to epoll. The error is Invalid argument when executing read() in the handler, and the errno is 22. This error appears when I run this program on my Raspberry Pi (Raspbian, Linux 4.9.80), but everything is fine when I run it on my laptop (Arch, Linux 4.15.15).

The related codes are pasted below. Any help is very much appreciated.

void epset_reg(int epfd, int fd, u32 events)
{
    struct epoll_event ev;

    memset(&ev, 0, sizeof(ev));
    ev.data.fd = fd;
    ev.events = events;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0)
        handle_err("epoll_ctl");
}

int init_timer(u32 interval)
{
    int tfd;
    struct itimerspec tspec;

    /* specify the timer */
    tspec.it_value.tv_sec = 1;
    tspec.it_value.tv_nsec = 0;
    tspec.it_interval.tv_sec = interval;
    tspec.it_interval.tv_nsec = 0;

    /* create timerfd */
    if ((tfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0)
        handle_err("timerfd_create");

    /* arm (start) the periodic timer */
    if (timerfd_settime(tfd, TFD_TIMER_ABSTIME, &tspec, NULL) < 0)
        handle_err("timerfd_settime");

    return tfd;
}

void handler(int tfd)
{
    u64 exp;

    /* THE ERROR ! */
    if (read(tfd, &exp, sizeof(exp)) < 0)
        handle_err("read");

    /* irrelevant parts */
}

int main()
{
    int epfd, tfd, sock, nfds, i;
    struct epoll_event events[MAX_EVENTS];

    /* create new epoll instance */
    if ((epfd = epoll_create1(0)) < 0)
        handle_err("epoll_create1");

    /* obtain timerfd */
    tfd = init_timer(TIMER_INTERVAL);
    /* obtain socket to listen */
    sock = init_socket(CC_PORT);

    /* register sock and tfd to epoll set */
    epset_reg(epfd, tfd, EPOLLIN);
    epset_reg(epfd, sock, EPOLLIN | EPOLLET);

    for (;;) {
        if ((nfds = epoll_wait(epfd, events, MAX_EVENTS, -1)) < 0)
            handle_err("epoll_wait");

        for (i = 0; i < nfds; ++i) {
            if ((events[i].events & EPOLLERR) ||
                (events[i].events & EPOLLHUP) ||
                (!(events[i].events & EPOLLIN))) {
                fprintf(stderr, "epoll\n");
                close(events[i].data.fd);
                continue;
            }

            if (events[i].data.fd == tfd)
                handler(tfd);
            else if (events[i].data.fd == sock)
                accept_conn(sock, epfd);
            else
                handle_message(events[i].data.fd);
        }
    }
}

The complete program is hosted on https://github.com/iamlazynic/centralized_wlan/tree/master/cc inside cc.c and main.c.

Except for suggestion on this problem, it would be great if there is advice on how to debug in the situation. Thanks!

Upvotes: 0

Views: 1089

Answers (2)

John Szakmeister
John Szakmeister

Reputation: 47032

After working through this, we discovered that the typedef for a u64 was wrong and the code was attempting to read to few bytes, which will result in an EINVAL error code. The typedef was:

typedef unsigned long int u64;

But on a 32-bit system, this is likely on 4 bytes in size. Instead, we used stdint.h and the uint64_t type (which takes care to typedef things a little more carefully for the platform you're using) and now has the correct size.

Upvotes: 1

jchsonez
jchsonez

Reputation: 11

The following is the synopsis of read function.

ssize_t read(int fildes, void *buf, size_t nbyte);

The second is the buf pointer and the third is the size to read. You set the size to 64 bytes. But the pointer is the address of exp(8bytes) which is assigned at stack. I think it can corrupt the stack.

Upvotes: 1

Related Questions