Loading core/java/com/android/internal/os/ZygoteConnection.java +3 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,9 @@ class ZygoteConnection { throw ex; } if (peer.getUid() != Process.SYSTEM_UID) { throw new ZygoteSecurityException("Only system UID is allowed to connect to Zygote."); } isEof = false; } Loading core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp +53 −28 Original line number Diff line number Diff line Loading @@ -341,6 +341,18 @@ jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, j return result; } static uid_t getSocketPeerUid(int socket, const std::function<void(const std::string&)>& fail_fn) { struct ucred credentials; socklen_t cred_size = sizeof credentials; if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1 || cred_size != sizeof credentials) { fail_fn(CREATE_ERROR("Failed to get socket credentials, %s", strerror(errno))); } return credentials.uid; } // Read all lines from the current command into the buffer, and then reset the buffer, so // we will start reading again at the beginning of the command, starting with the argument // count. And we don't need access to the fd to do so. Loading Loading @@ -398,18 +410,12 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( fail_fn_z("Failed to retrieve session socket timeout"); } struct ucred credentials; socklen_t cred_size = sizeof credentials; if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1 || cred_size != sizeof credentials) { fail_fn_1(CREATE_ERROR("ForkMany failed to get initial credentials, %s", strerror(errno))); uid_t peerUid = getSocketPeerUid(session_socket, fail_fn_1); if (peerUid != static_cast<uid_t>(expected_uid)) { return JNI_FALSE; } bool first_time = true; do { if (credentials.uid != expected_uid) { return JNI_FALSE; } n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n); n_buffer->reset(); int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds, Loading Loading @@ -439,22 +445,48 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( // Clear buffer and get count from next command. n_buffer->clear(); for (;;) { bool valid_session_socket = true; // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect. int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) { if (n_buffer->getCount(fail_fn_z) != 0) { break; } // else disconnected; } else { // Session socket was disconnected valid_session_socket = false; close(session_socket); } } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) { fail_fn_z( CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res)); } int new_fd = -1; do { // We've now seen either a disconnect or connect request. close(session_socket); int new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr)); new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr)); if (new_fd == -1) { fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno))); } uid_t newPeerUid = getSocketPeerUid(new_fd, fail_fn_1); if (newPeerUid != static_cast<uid_t>(expected_uid)) { ALOGW("Dropping new connection with a mismatched uid %d\n", newPeerUid); close(new_fd); new_fd = -1; } else { // If we still have a valid session socket, close it now if (valid_session_socket) { close(session_socket); } valid_session_socket = true; } } while (!valid_session_socket); // At this point we either have a valid new connection (new_fd > 0), or // an existing session socket we can poll on if (new_fd == -1) { // The new connection wasn't valid, and we still have an old one; retry polling continue; } if (new_fd != session_socket) { // Move new_fd back to the old value, so that we don't have to change Java-level data // structures to reflect a change. This implicitly closes the old one. Loading @@ -474,13 +506,6 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s", session_socket, strerror(errno))); } if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) { fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno))); } if (cred_size != sizeof credentials) { fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d", cred_size, static_cast<int>(sizeof credentials))); } } first_time = false; } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n)); Loading Loading
core/java/com/android/internal/os/ZygoteConnection.java +3 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,9 @@ class ZygoteConnection { throw ex; } if (peer.getUid() != Process.SYSTEM_UID) { throw new ZygoteSecurityException("Only system UID is allowed to connect to Zygote."); } isEof = false; } Loading
core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp +53 −28 Original line number Diff line number Diff line Loading @@ -341,6 +341,18 @@ jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, j return result; } static uid_t getSocketPeerUid(int socket, const std::function<void(const std::string&)>& fail_fn) { struct ucred credentials; socklen_t cred_size = sizeof credentials; if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1 || cred_size != sizeof credentials) { fail_fn(CREATE_ERROR("Failed to get socket credentials, %s", strerror(errno))); } return credentials.uid; } // Read all lines from the current command into the buffer, and then reset the buffer, so // we will start reading again at the beginning of the command, starting with the argument // count. And we don't need access to the fd to do so. Loading Loading @@ -398,18 +410,12 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( fail_fn_z("Failed to retrieve session socket timeout"); } struct ucred credentials; socklen_t cred_size = sizeof credentials; if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1 || cred_size != sizeof credentials) { fail_fn_1(CREATE_ERROR("ForkMany failed to get initial credentials, %s", strerror(errno))); uid_t peerUid = getSocketPeerUid(session_socket, fail_fn_1); if (peerUid != static_cast<uid_t>(expected_uid)) { return JNI_FALSE; } bool first_time = true; do { if (credentials.uid != expected_uid) { return JNI_FALSE; } n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n); n_buffer->reset(); int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds, Loading Loading @@ -439,22 +445,48 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( // Clear buffer and get count from next command. n_buffer->clear(); for (;;) { bool valid_session_socket = true; // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect. int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) { if (n_buffer->getCount(fail_fn_z) != 0) { break; } // else disconnected; } else { // Session socket was disconnected valid_session_socket = false; close(session_socket); } } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) { fail_fn_z( CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res)); } int new_fd = -1; do { // We've now seen either a disconnect or connect request. close(session_socket); int new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr)); new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr)); if (new_fd == -1) { fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno))); } uid_t newPeerUid = getSocketPeerUid(new_fd, fail_fn_1); if (newPeerUid != static_cast<uid_t>(expected_uid)) { ALOGW("Dropping new connection with a mismatched uid %d\n", newPeerUid); close(new_fd); new_fd = -1; } else { // If we still have a valid session socket, close it now if (valid_session_socket) { close(session_socket); } valid_session_socket = true; } } while (!valid_session_socket); // At this point we either have a valid new connection (new_fd > 0), or // an existing session socket we can poll on if (new_fd == -1) { // The new connection wasn't valid, and we still have an old one; retry polling continue; } if (new_fd != session_socket) { // Move new_fd back to the old value, so that we don't have to change Java-level data // structures to reflect a change. This implicitly closes the old one. Loading @@ -474,13 +506,6 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s", session_socket, strerror(errno))); } if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) { fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno))); } if (cred_size != sizeof credentials) { fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d", cred_size, static_cast<int>(sizeof credentials))); } } first_time = false; } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n)); Loading