Increase netlink uevent rcvbuf size to 16M

Increase size of the NETLINK_KOBJECT_UEVENT socket receive buffer to
16M.  Also, use SO_RCVBUFFORCE to override any limits set by
/proc/sys/net/core/rmem_max.

We had a couple of instances, where we lost critical uevent messages due
to receive buffer overflows.

Bug: 119933843
Change-Id: I6aab183aa0194e173f9175b47c6beb0835cf6675
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 8cf2128..d6765b7 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -87,8 +87,8 @@
 }
 
 UeventListener::UeventListener() {
-    // is 2MB enough? udev uses 128MB!
-    device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
+    // is 16MB enough? udev uses 128MB!
+    device_fd_.reset(uevent_open_socket(16 * 1024 * 1024, true));
     if (device_fd_ == -1) {
         LOG(FATAL) << "Could not open uevent socket";
     }
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index 2dfceed..721de7c 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -95,6 +95,8 @@
 int uevent_open_socket(int buf_sz, bool passcred) {
     struct sockaddr_nl addr;
     int on = passcred;
+    int buf_sz_readback = 0;
+    socklen_t optlen = sizeof(buf_sz_readback);
     int s;
 
     memset(&addr, 0, sizeof(addr));
@@ -105,11 +107,21 @@
     s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
     if (s < 0) return -1;
 
-    /* buf_sz should be less than net.core.rmem_max for this to succeed */
-    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||
+          getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {
         close(s);
         return -1;
     }
+    /* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we
+     * want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in
+     * case we don't have CAP_NET_ADMIN. This is the case, for example, for
+     * healthd. */
+    if (buf_sz_readback < 2 * buf_sz) {
+        if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {
+            close(s);
+            return -1;
+        }
+    }
 
     setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));