Edge-Triggered epoll and starvation - Unix

This is a discussion on Edge-Triggered epoll and starvation - Unix ; Hello gurus! I have a question about epoll and possible starvation. As epoll(4) says, when epoll is used in Edge-Triggered mode it's very important to read all the data available in the buffer. Otherwise next epoll_wait call may block forever ...

+ Reply to Thread
Results 1 to 2 of 2

Thread: Edge-Triggered epoll and starvation

  1. Edge-Triggered epoll and starvation

    Hello gurus!

    I have a question about epoll and possible starvation.
    As epoll(4) says, when epoll is used in Edge-Triggered mode
    it's very important to read all the data available in the
    buffer.
    Otherwise next epoll_wait call may block forever (if there
    are no changes on monitored file descriptor).

    Lets suppose, that epoll_wait detected new events on three
    file descriptors 5, 6 and 7.
    So, we should handle I/O on all of them.

    Read handling loop may be implemented as follows:

    void handle_read_event(int fd)
    {
    while(1)
    {
    int r = read(fd, buf, size);
    if(r==-1)
    {
    if(errno == EINTR) continue;
    if(errno == EAGAIN) break;
    handle_read_error();
    }
    else
    {
    use_data(buf, r);
    }
    }

    }

    We are reading as much data as possible (since epoll-ET is used).
    Lets now suppose that peer is much more faster and we are unable
    to read data at the same rate (fast producer, slow consumer).
    In such a case the loop presented above may be potentially
    infinite.
    So, this leads to starvation and DoS.
    Really, we are unable to handle new events as well as I/O on already
    ready descriptors (6 and 7 in the example).

    Are my conclusions correct?
    Have anyone seen something similar to this scenario in real life?

    Thank you beforehand!

  2. Re: Edge-Triggered epoll and starvation

    On May 30, 11:02*pm, Krivenok Dmitry
    wrote:

    > Read handling loop may be implemented as follows:
    >
    > void handle_read_event(int fd)
    > {
    > * while(1)
    > * * {
    > * * * int r = read(fd, buf, size);
    > * * * if(r==-1)
    > * * * * {
    > * * * * * if(errno == EINTR) continue;
    > * * * * * if(errno == EAGAIN) break;
    > * * * * * handle_read_error();
    > * * * * }
    > * * * else
    > * * * * {
    > * * * * * use_data(buf, r);
    > * * * * }
    > * * }
    >
    > }
    >
    > We are reading as much data as possible (since epoll-ET is used).
    > Lets now suppose that peer is much more faster and we are unable
    > to read data at the same rate (fast producer, slow consumer).
    > In such a case the loop presented above may be potentially
    > infinite.
    > So, this leads to starvation and DoS.
    > Really, we are unable to handle new events as well as I/O on already
    > ready descriptors (6 and 7 in the example).
    >
    > Are my conclusions correct?
    > Have anyone seen something similar to this scenario in real life?


    I don't think anyone would write code this bad, but yes, if you were
    willing to do an unlimited amount of work on a single connection, your
    code would break this way. There are many more sensible things you
    could do:

    1) You could simply *stop* calling 'read'. There will be no problem
    because you know the socket is still ready, so you should call 'read'
    later when you're willing to do work, not 'epoll'.

    2) You could close the connection as soon as you are reasonably sure
    it is an attack.

    DS

+ Reply to Thread