Merge "binder: FdTrigger ensure POLLERR / POLLNVAL is checked" am: 82db3188af am: 28f0c8d19e am: e43b6d23bd am: 1e72c716f0 am: bca0bde472

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1835920

Change-Id: I5b94e32ebe4ca2b54b632bdfe5b1eb234ab7ecbf
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index 49f83ff..5e22593 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -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 @@
 }
 
 status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) {
-    while (true) {
-        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;
-        }
-        if (pfd[1].revents & POLLHUP) {
-            return DEAD_OBJECT;
-        }
-        return pfd[0].revents & event ? OK : DEAD_OBJECT;
+    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;
     }
+    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;
+    }
+    // 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