Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0b37bd1 authored by Yifan Hong's avatar Yifan Hong
Browse files

binder: FdTrigger ensure POLLERR / POLLNVAL is checked

... by aborting if incoming events is 0, so that
it returns DEAD_OBJECT when POLLERR / POLLNVAL
is hit on one of the FDs. Otherwise, if
incoming events is 0, trigerrablePoll() may
block forever.

Also abort if poll() with infinite timeout returns 0. This
way we can remove the while loop, making the code clearer.

Also add a test on triggerablePoll.

Test: binderRpcTest
Fixes: 200999583
Change-Id: I2ae6c4ef2ee825e77e20b43e859df192238be724
parent f24415a9
Loading
Loading
Loading
Loading
+49 −15
Original line number Diff line number Diff line
@@ -17,11 +17,13 @@
#define LOG_TAG "FdTrigger"
#include <log/log.h>

#include "FdTrigger.h"

#include <poll.h>

#include <android-base/macros.h>

#include "FdTrigger.h"
#include "RpcState.h"
namespace android {

std::unique_ptr<FdTrigger> FdTrigger::make() {
@@ -42,21 +44,53 @@ bool FdTrigger::isTriggered() {
}

status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) {
    while (true) {
    LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", fd.get());
    pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
                 {.fd = mRead.get(), .events = 0, .revents = 0}};
    int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
    if (ret < 0) {
        return -errno;
    }
        if (ret == 0) {
            continue;
        }
    LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", fd.get());

    // At least one FD has events. Check them.

    // Detect explicit trigger(): DEAD_OBJECT
    if (pfd[1].revents & POLLHUP) {
        return DEAD_OBJECT;
    }
        return pfd[0].revents & event ? OK : DEAD_OBJECT;
    // See unknown flags in trigger FD's revents (POLLERR / POLLNVAL).
    // Treat this error condition as UNKNOWN_ERROR.
    if (pfd[1].revents != 0) {
        ALOGE("Unknown revents on trigger FD %d: revents = %d", pfd[1].fd, pfd[1].revents);
        return UNKNOWN_ERROR;
    }

    // pfd[1].revents is 0, hence pfd[0].revents must be set, and only possible values are
    // a subset of event | POLLHUP | POLLERR | POLLNVAL.

    // POLLNVAL: invalid FD number, e.g. not opened.
    if (pfd[0].revents & POLLNVAL) {
        return BAD_VALUE;
    }

    // Error condition. It wouldn't be possible to do I/O on |fd| afterwards.
    // Note: If this is the write end of a pipe then POLLHUP may also be set simultaneously. We
    //   still want DEAD_OBJECT in this case.
    if (pfd[0].revents & POLLERR) {
        LOG_RPC_DETAIL("poll() incoming FD %d results in revents = %d", pfd[0].fd, pfd[0].revents);
        return DEAD_OBJECT;
    }

    // Success condition; event flag(s) set. Even though POLLHUP may also be set,
    // treat it as a success condition to ensure data is drained.
    if (pfd[0].revents & event) {
        return OK;
    }

    // POLLHUP: Peer closed connection. Treat as DEAD_OBJECT.
    // This is a very common case, so don't log.
    return DEAD_OBJECT;
}

} // namespace android