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

Commit 5c96d2c1 authored by Martijn Coenen's avatar Martijn Coenen Committed by Android (Google) Code Review
Browse files

Merge "Verify UID of incoming Zygote connections." into main

parents 22936fce 2ffc7cb2
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -93,6 +93,9 @@ class ZygoteConnection {
            throw ex;
            throw ex;
        }
        }


        if (peer.getUid() != Process.SYSTEM_UID) {
            throw new ZygoteSecurityException("Only system UID is allowed to connect to Zygote.");
        }
        isEof = false;
        isEof = false;
    }
    }


+53 −29
Original line number Original line Diff line number Diff line
@@ -354,6 +354,18 @@ jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, j
  return result;
  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
// 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
// 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.
// count. And we don't need access to the fd to do so.
@@ -413,19 +425,12 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
    fail_fn_z("Failed to retrieve session socket timeout");
    fail_fn_z("Failed to retrieve session socket timeout");
  }
  }


  struct ucred credentials;
  uid_t peerUid = getSocketPeerUid(session_socket, fail_fn_1);
  socklen_t cred_size = sizeof credentials;
  if (peerUid != static_cast<uid_t>(expected_uid)) {
  if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
    return JNI_FALSE;
      || cred_size != sizeof credentials) {
    fail_fn_1(CREATE_ERROR("ForkRepeatedly failed to get initial credentials, %s",
                           strerror(errno)));
  }
  }

  bool first_time = true;
  bool first_time = true;
  do {
  do {
    if (credentials.uid != static_cast<uid_t>(expected_uid)) {
      return JNI_FALSE;
    }
    n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
    n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
    n_buffer->reset();
    n_buffer->reset();
    int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds,
    int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds,
@@ -453,6 +458,7 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
      }
      }
    }
    }
    for (;;) {
    for (;;) {
      bool valid_session_socket = true;
      // Clear buffer and get count from next command.
      // Clear buffer and get count from next command.
      n_buffer->clear();
      n_buffer->clear();
      // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
      // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
@@ -463,17 +469,42 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
      if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
      if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
        if (n_buffer->getCount(fail_fn_z) != 0) {
        if (n_buffer->getCount(fail_fn_z) != 0) {
          break;
          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) {
      } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) {
        fail_fn_z(
        fail_fn_z(
            CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res));
            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.
        // We've now seen either a disconnect or connect request.
      close(session_socket);
        new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr));
      int new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr));
        if (new_fd == -1) {
        if (new_fd == -1) {
          fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)));
          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) {
      if (new_fd != session_socket) {
        // Move new_fd back to the old value, so that we don't have to change Java-level data
        // 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.
        // structures to reflect a change. This implicitly closes the old one.
@@ -493,13 +524,6 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
        fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s",
        fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s",
                               session_socket, strerror(errno)));
                               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;
    first_time = false;
  } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n));
  } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n));