Loading core/java/com/android/internal/os/ZygoteConnection.java +3 −0 Original line number Original line Diff line number Diff line Loading @@ -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; } } Loading core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp +53 −29 Original line number Original line Diff line number Diff line Loading @@ -353,6 +353,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. Loading Loading @@ -411,19 +423,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 != 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, Loading Loading @@ -453,22 +458,48 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( // Clear buffer and get count from next command. // Clear buffer and get count from next command. n_buffer->clear(); n_buffer->clear(); for (;;) { for (;;) { bool valid_session_socket = true; // 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. int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); 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. Loading @@ -488,13 +519,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)); Loading media/java/android/media/session/ParcelableListBinder.java +11 −2 Original line number Original line Diff line number Diff line Loading @@ -45,6 +45,7 @@ public class ParcelableListBinder<T extends Parcelable> extends Binder { private static final int END_OF_PARCEL = 0; private static final int END_OF_PARCEL = 0; private static final int ITEM_CONTINUED = 1; private static final int ITEM_CONTINUED = 1; private final Class<T> mListElementsClass; private final Consumer<List<T>> mConsumer; private final Consumer<List<T>> mConsumer; private final Object mLock = new Object(); private final Object mLock = new Object(); Loading @@ -61,9 +62,11 @@ public class ParcelableListBinder<T extends Parcelable> extends Binder { /** /** * Creates an instance. * Creates an instance. * * * @param listElementsClass the class of the list elements. * @param consumer a consumer that consumes the list received * @param consumer a consumer that consumes the list received */ */ public ParcelableListBinder(@NonNull Consumer<List<T>> consumer) { public ParcelableListBinder(Class<T> listElementsClass, @NonNull Consumer<List<T>> consumer) { mListElementsClass = listElementsClass; mConsumer = consumer; mConsumer = consumer; } } Loading @@ -83,7 +86,13 @@ public class ParcelableListBinder<T extends Parcelable> extends Binder { mCount = data.readInt(); mCount = data.readInt(); } } while (i < mCount && data.readInt() != END_OF_PARCEL) { while (i < mCount && data.readInt() != END_OF_PARCEL) { mList.add(data.readParcelable(null)); Object object = data.readParcelable(null); if (mListElementsClass.isAssignableFrom(object.getClass())) { // Checking list items are of compaitible types to validate against malicious // apps calling it directly via reflection with non compilable items. // See b/317048338 for more details mList.add((T) object); } i++; i++; } } if (i >= mCount) { if (i >= mCount) { Loading services/core/java/com/android/server/media/MediaSessionRecord.java +8 −6 Original line number Original line Diff line number Diff line Loading @@ -1100,7 +1100,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR @Override @Override public IBinder getBinderForSetQueue() throws RemoteException { public IBinder getBinderForSetQueue() throws RemoteException { return new ParcelableListBinder<QueueItem>((list) -> { return new ParcelableListBinder<QueueItem>( QueueItem.class, (list) -> { synchronized (mLock) { synchronized (mLock) { mQueue = list; mQueue = list; } } Loading services/core/java/com/android/server/pm/PackageManagerService.java +20 −3 Original line number Original line Diff line number Diff line Loading @@ -5917,9 +5917,26 @@ public class PackageManagerService implements PackageSender, TestUtilityService packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet); packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet); }); }); if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) { if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) { Binder.withCleanCallingIdentity(() -> Binder.withCleanCallingIdentity(() -> { mPreferredActivityHelper.clearPackagePreferredActivities(packageName, mPreferredActivityHelper.clearPackagePreferredActivities(packageName, UserHandle.USER_ALL)); UserHandle.USER_ALL); // Send the ACTION_PACKAGE_CHANGED when the mimeGroup has changes final Computer snapShot = snapshotComputer(); final ArrayList<String> components = new ArrayList<>( Collections.singletonList(packageName)); final int appId = packageState.getAppId(); final int[] userIds = resolveUserIds(UserHandle.USER_ALL); final String reason = "The mimeGroup is changed"; for (int i = 0; i < userIds.length; i++) { final PackageUserStateInternal pkgUserState = packageState.getUserStates().get(userIds[i]); if (pkgUserState != null && pkgUserState.isInstalled()) { final int packageUid = UserHandle.getUid(userIds[i], appId); sendPackageChangedBroadcast(snapShot, packageName, true /* dontKillApp */, components, packageUid, reason); } } }); } } scheduleWriteSettings(); scheduleWriteSettings(); Loading Loading
core/java/com/android/internal/os/ZygoteConnection.java +3 −0 Original line number Original line Diff line number Diff line Loading @@ -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; } } Loading
core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp +53 −29 Original line number Original line Diff line number Diff line Loading @@ -353,6 +353,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. Loading Loading @@ -411,19 +423,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 != 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, Loading Loading @@ -453,22 +458,48 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( // Clear buffer and get count from next command. // Clear buffer and get count from next command. n_buffer->clear(); n_buffer->clear(); for (;;) { for (;;) { bool valid_session_socket = true; // 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. int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); 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. Loading @@ -488,13 +519,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)); Loading
media/java/android/media/session/ParcelableListBinder.java +11 −2 Original line number Original line Diff line number Diff line Loading @@ -45,6 +45,7 @@ public class ParcelableListBinder<T extends Parcelable> extends Binder { private static final int END_OF_PARCEL = 0; private static final int END_OF_PARCEL = 0; private static final int ITEM_CONTINUED = 1; private static final int ITEM_CONTINUED = 1; private final Class<T> mListElementsClass; private final Consumer<List<T>> mConsumer; private final Consumer<List<T>> mConsumer; private final Object mLock = new Object(); private final Object mLock = new Object(); Loading @@ -61,9 +62,11 @@ public class ParcelableListBinder<T extends Parcelable> extends Binder { /** /** * Creates an instance. * Creates an instance. * * * @param listElementsClass the class of the list elements. * @param consumer a consumer that consumes the list received * @param consumer a consumer that consumes the list received */ */ public ParcelableListBinder(@NonNull Consumer<List<T>> consumer) { public ParcelableListBinder(Class<T> listElementsClass, @NonNull Consumer<List<T>> consumer) { mListElementsClass = listElementsClass; mConsumer = consumer; mConsumer = consumer; } } Loading @@ -83,7 +86,13 @@ public class ParcelableListBinder<T extends Parcelable> extends Binder { mCount = data.readInt(); mCount = data.readInt(); } } while (i < mCount && data.readInt() != END_OF_PARCEL) { while (i < mCount && data.readInt() != END_OF_PARCEL) { mList.add(data.readParcelable(null)); Object object = data.readParcelable(null); if (mListElementsClass.isAssignableFrom(object.getClass())) { // Checking list items are of compaitible types to validate against malicious // apps calling it directly via reflection with non compilable items. // See b/317048338 for more details mList.add((T) object); } i++; i++; } } if (i >= mCount) { if (i >= mCount) { Loading
services/core/java/com/android/server/media/MediaSessionRecord.java +8 −6 Original line number Original line Diff line number Diff line Loading @@ -1100,7 +1100,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR @Override @Override public IBinder getBinderForSetQueue() throws RemoteException { public IBinder getBinderForSetQueue() throws RemoteException { return new ParcelableListBinder<QueueItem>((list) -> { return new ParcelableListBinder<QueueItem>( QueueItem.class, (list) -> { synchronized (mLock) { synchronized (mLock) { mQueue = list; mQueue = list; } } Loading
services/core/java/com/android/server/pm/PackageManagerService.java +20 −3 Original line number Original line Diff line number Diff line Loading @@ -5917,9 +5917,26 @@ public class PackageManagerService implements PackageSender, TestUtilityService packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet); packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet); }); }); if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) { if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) { Binder.withCleanCallingIdentity(() -> Binder.withCleanCallingIdentity(() -> { mPreferredActivityHelper.clearPackagePreferredActivities(packageName, mPreferredActivityHelper.clearPackagePreferredActivities(packageName, UserHandle.USER_ALL)); UserHandle.USER_ALL); // Send the ACTION_PACKAGE_CHANGED when the mimeGroup has changes final Computer snapShot = snapshotComputer(); final ArrayList<String> components = new ArrayList<>( Collections.singletonList(packageName)); final int appId = packageState.getAppId(); final int[] userIds = resolveUserIds(UserHandle.USER_ALL); final String reason = "The mimeGroup is changed"; for (int i = 0; i < userIds.length; i++) { final PackageUserStateInternal pkgUserState = packageState.getUserStates().get(userIds[i]); if (pkgUserState != null && pkgUserState.isInstalled()) { final int packageUid = UserHandle.getUid(userIds[i], appId); sendPackageChangedBroadcast(snapShot, packageName, true /* dontKillApp */, components, packageUid, reason); } } }); } } scheduleWriteSettings(); scheduleWriteSettings(); Loading