summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/IntentFilter.java15
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java20
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java140
-rw-r--r--services/core/java/com/android/server/pm/ResolveIntentHelper.java6
5 files changed, 151 insertions, 36 deletions
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index e2907224d66e..86d061cecb88 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -21,6 +21,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.Flags;
import android.net.Uri;
@@ -186,6 +189,18 @@ public class IntentFilter implements Parcelable {
private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
/**
+ * An intent with action set as null used to always pass the action test during intent
+ * filter matching. This causes a lot of confusion and unexpected intent matches.
+ * Null action intents should be blocked when the intent sender application targets V or higher.
+ *
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long BLOCK_NULL_ACTION_INTENTS = 293560872;
+
+ /**
* The filter {@link #setPriority} value at which system high-priority
* receivers are placed; that is, receivers that should execute before
* application code. Applications should never use filters with this or
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 76c4feada74f..a1d75b258e33 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -463,6 +463,7 @@ import com.android.server.net.NetworkManagementInternal;
import com.android.server.os.NativeTombstoneManager;
import com.android.server.pm.Computer;
import com.android.server.pm.Installer;
+import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -15876,6 +15877,11 @@ public class ActivityManagerService extends IActivityManager.Stub
registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
resolvedType, false /*defaultOnly*/, userId);
}
+ if (registeredReceivers != null) {
+ PackageManagerServiceUtils.applyNullActionBlocking(
+ mPlatformCompat, snapshot, registeredReceivers,
+ true, intent, callingUid);
+ }
}
BroadcastQueue.traceEnd(cookie);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 2005b17e82a6..6a25f64b697d 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -584,8 +584,8 @@ public class ComputerEngine implements Computer {
list = new ArrayList<>(1);
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- mInjector.getCompatibility(), mComponentResolver,
- list, false, intent, resolvedType, filterCallingUid);
+ mInjector.getCompatibility(), this, list, false, intent,
+ resolvedType, filterCallingUid);
}
}
} else {
@@ -609,13 +609,15 @@ public class ComputerEngine implements Computer {
}
list = lockedResult.result;
}
+ PackageManagerServiceUtils.applyNullActionBlocking(
+ mInjector.getCompatibility(), this, list, false, intent, filterCallingUid);
}
if (originalIntent != null) {
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- mInjector.getCompatibility(), mComponentResolver,
- list, false, originalIntent, resolvedType, filterCallingUid);
+ mInjector.getCompatibility(), this, list, false, originalIntent,
+ resolvedType, filterCallingUid);
}
return skipPostResolution ? list : applyPostResolutionFilter(
@@ -698,20 +700,22 @@ public class ComputerEngine implements Computer {
list = new ArrayList<>(1);
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- mInjector.getCompatibility(), mComponentResolver,
- list, false, intent, resolvedType, callingUid);
+ mInjector.getCompatibility(), this, list, false, intent,
+ resolvedType, callingUid);
}
}
} else {
list = queryIntentServicesInternalBody(intent, resolvedType, flags,
userId, callingUid, instantAppPkgName);
+ PackageManagerServiceUtils.applyNullActionBlocking(
+ mInjector.getCompatibility(), this, list, false, intent, callingUid);
}
if (originalIntent != null) {
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- mInjector.getCompatibility(), mComponentResolver,
- list, false, originalIntent, resolvedType, callingUid);
+ mInjector.getCompatibility(), this, list, false, originalIntent,
+ resolvedType, callingUid);
}
return list;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9484d0d7b52b..5f04a0bd3264 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -27,6 +27,7 @@ import static android.system.OsConstants.O_RDWR;
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH;
+import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH;
import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
import static com.android.server.pm.PackageInstallerSession.APP_METADATA_FILE_ACCESS_MODE;
import static com.android.server.pm.PackageInstallerSession.getAppMetadataSizeLimit;
@@ -115,6 +116,7 @@ import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.AndroidPackageSplit;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.resolution.ComponentResolverApi;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import dalvik.system.VMRuntime;
@@ -1198,9 +1200,77 @@ public class PackageManagerServiceUtils {
return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
- // Static to give access to ComputeEngine
+ private static ParsedMainComponent componentInfoToComponent(
+ ComponentInfo info, ComponentResolverApi resolver, boolean isReceiver) {
+ if (info instanceof ActivityInfo) {
+ if (isReceiver) {
+ return resolver.getReceiver(info.getComponentName());
+ } else {
+ return resolver.getActivity(info.getComponentName());
+ }
+ } else if (info instanceof ServiceInfo) {
+ return resolver.getService(info.getComponentName());
+ } else {
+ // This shall never happen
+ throw new IllegalArgumentException("Unsupported component type");
+ }
+ }
+
+ /**
+ * Under the correct conditions, remove components if the intent has null action.
+ *
+ * `compat` and `snapshot` may be null when this method is called in ActivityManagerService
+ * CTS tests. The code in this method will properly avoid control flows using these arguments.
+ */
+ public static void applyNullActionBlocking(
+ @Nullable PlatformCompat compat, @Nullable PackageDataSnapshot snapshot,
+ List componentList, boolean isReceiver, Intent intent, int filterCallingUid) {
+ if (ActivityManager.canAccessUnexportedComponents(filterCallingUid)) return;
+
+ final Computer computer = (Computer) snapshot;
+ ComponentResolverApi resolver = null;
+
+ final boolean enforce = android.security.Flags.blockNullActionIntents()
+ && (compat == null || compat.isChangeEnabledByUidInternal(
+ IntentFilter.BLOCK_NULL_ACTION_INTENTS, filterCallingUid));
+
+ for (int i = componentList.size() - 1; i >= 0; --i) {
+ boolean match = true;
+
+ Object c = componentList.get(i);
+ if (c instanceof ResolveInfo resolveInfo) {
+ if (computer == null) {
+ // PackageManagerService is not started
+ return;
+ }
+ if (resolver == null) {
+ resolver = computer.getComponentResolver();
+ }
+ final ParsedMainComponent comp = componentInfoToComponent(
+ resolveInfo.getComponentInfo(), resolver, isReceiver);
+ if (!comp.getIntents().isEmpty() && intent.getAction() == null) {
+ match = false;
+ }
+ } else if (c instanceof IntentFilter) {
+ if (intent.getAction() == null) {
+ match = false;
+ }
+ }
+
+ if (!match) {
+ ActivityManagerUtils.logUnsafeIntentEvent(
+ UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH,
+ filterCallingUid, intent, null, enforce);
+ if (enforce) {
+ Slog.w(TAG, "Blocking intent with null action: " + intent);
+ componentList.remove(i);
+ }
+ }
+ }
+ }
+
public static void applyEnforceIntentFilterMatching(
- PlatformCompat compat, ComponentResolverApi resolver,
+ PlatformCompat compat, PackageDataSnapshot snapshot,
List<ResolveInfo> resolveInfos, boolean isReceiver,
Intent intent, String resolvedType, int filterCallingUid) {
if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return;
@@ -1208,13 +1278,19 @@ public class PackageManagerServiceUtils {
// Do not enforce filter matching when the caller is system or root
if (ActivityManager.canAccessUnexportedComponents(filterCallingUid)) return;
+ final Computer computer = (Computer) snapshot;
+ final ComponentResolverApi resolver = computer.getComponentResolver();
+
final Printer logPrinter = DEBUG_INTENT_MATCHING
? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM)
: null;
- final boolean enforce = android.security.Flags.enforceIntentFilterMatch()
+ final boolean enforceMatch = android.security.Flags.enforceIntentFilterMatch()
&& compat.isChangeEnabledByUidInternal(
ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, filterCallingUid);
+ final boolean blockNullAction = android.security.Flags.blockNullActionIntents()
+ && compat.isChangeEnabledByUidInternal(
+ IntentFilter.BLOCK_NULL_ACTION_INTENTS, filterCallingUid);
for (int i = resolveInfos.size() - 1; i >= 0; --i) {
final ComponentInfo info = resolveInfos.get(i).getComponentInfo();
@@ -1224,40 +1300,53 @@ public class PackageManagerServiceUtils {
continue;
}
- final ParsedMainComponent comp;
- if (info instanceof ActivityInfo) {
- if (isReceiver) {
- comp = resolver.getReceiver(info.getComponentName());
- } else {
- comp = resolver.getActivity(info.getComponentName());
- }
- } else if (info instanceof ServiceInfo) {
- comp = resolver.getService(info.getComponentName());
- } else {
- // This shall never happen
- throw new IllegalArgumentException("Unsupported component type");
- }
+ final ParsedMainComponent comp = componentInfoToComponent(info, resolver, isReceiver);
if (comp == null || comp.getIntents().isEmpty()) {
continue;
}
- boolean match = false;
- for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
- IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
- if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) {
- match = true;
- break;
+ Boolean match = null;
+
+ if (intent.getAction() == null) {
+ ActivityManagerUtils.logUnsafeIntentEvent(
+ UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH,
+ filterCallingUid, intent, resolvedType, enforceMatch && blockNullAction);
+ if (blockNullAction) {
+ // Skip intent filter matching if blocking null action
+ match = false;
}
}
- if (!match) {
+
+ if (match == null) {
+ // Check if any intent filter matches
+ for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
+ IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
+ if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) {
+ match = true;
+ break;
+ }
+ }
+ }
+
+ // At this point, the value `match` has the following states:
+ // null : Intent does not match any intent filter
+ // false: Null action intent detected AND blockNullAction == true
+ // true : The intent matches at least one intent filter
+
+ if (match == null) {
ActivityManagerUtils.logUnsafeIntentEvent(
UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH,
- filterCallingUid, intent, resolvedType, enforce);
+ filterCallingUid, intent, resolvedType, enforceMatch);
+ match = false;
+ }
+
+ if (!match) {
+ // All non-matching intents has to be marked accordingly
if (android.security.Flags.enforceIntentFilterMatch()) {
intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
- if (enforce) {
+ if (enforceMatch) {
Slog.w(TAG, "Intent does not match component's intent filter: " + intent);
Slog.w(TAG, "Access blocked: " + comp.getComponentName());
if (DEBUG_INTENT_MATCHING) {
@@ -1271,7 +1360,6 @@ public class PackageManagerServiceUtils {
}
}
-
/**
* Do NOT use for intent resolution filtering. That should be done with
* {@link DomainVerificationManagerInternal#filterToApprovedApp(Intent, List, int, Function)}.
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index b664e39cfd1c..309a4481e9de 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -458,7 +458,7 @@ final class ResolveIntentHelper {
list = new ArrayList<>(1);
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- mPlatformCompat, componentResolver, list, true, intent,
+ mPlatformCompat, computer, list, true, intent,
resolvedType, filterCallingUid);
}
}
@@ -479,12 +479,14 @@ final class ResolveIntentHelper {
list = result;
}
}
+ PackageManagerServiceUtils.applyNullActionBlocking(
+ mPlatformCompat, computer, list, true, intent, filterCallingUid);
}
if (originalIntent != null) {
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- mPlatformCompat, componentResolver,
+ mPlatformCompat, computer,
list, true, originalIntent, resolvedType, filterCallingUid);
}