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

Commit 1b37daa8 authored by Alan Stokes's avatar Alan Stokes Committed by Andreas Gampe
Browse files

Log SHA256 of secondary dex files during reconcile.

(cherry picked from commit a0023604)

Bug: 63927552
Test: Exercised manually. Added unit test for DexManager.

Merged-In: Ic8e9ea4da8eb5c22fbe088a59a406e36bc2eb6fa
Change-Id: Ic8e9ea4da8eb5c22fbe088a59a406e36bc2eb6fa
parent 779f5616
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -489,6 +489,16 @@ public class Installer extends SystemService {
        }
    }

    public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
            @Nullable String volumeUuid, int flags) throws InstallerException {
        if (!checkBeforeRemote()) return new byte[0];
        try {
            return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
    }

    public void invalidateMounts() throws InstallerException {
        if (!checkBeforeRemote()) return;
        try {
+5 −1
Original line number Diff line number Diff line
@@ -285,6 +285,7 @@ import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PermissionsState.PermissionState;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.DexLogger;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
@@ -2461,7 +2462,10 @@ public class PackageManagerService extends IPackageManager.Stub
        mInstaller = installer;
        mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                "*dexopt*");
        mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
        DexManager.Listener dexManagerListener = DexLogger.getListener(this,
                installer, mInstallLock);
        mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
                dexManagerListener);
        mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
        mOnPermissionChangeListeners = new OnPermissionChangeListeners(
+116 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.pm.dex;

import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.RemoteException;

import android.util.ArraySet;
import android.util.ByteStringUtils;
import android.util.EventLog;
import android.util.PackageUtils;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;

import java.io.File;
import java.util.Set;

import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;

/**
 * This class is responsible for logging data about secondary dex files.
 * The data logged includes hashes of the name and content of each file.
 */
public class DexLogger implements DexManager.Listener {
    private static final String TAG = "DexLogger";

    // Event log tag & subtag used for SafetyNet logging of dynamic
    // code loading (DCL) - see b/63927552.
    private static final int SNET_TAG = 0x534e4554;
    private static final String DCL_SUBTAG = "dcl";

    private final IPackageManager mPackageManager;
    private final Object mInstallLock;
    @GuardedBy("mInstallLock")
    private final Installer mInstaller;

    public static DexManager.Listener getListener(IPackageManager pms,
            Installer installer, Object installLock) {
        return new DexLogger(pms, installer, installLock);
    }

    private DexLogger(IPackageManager pms, Installer installer, Object installLock) {
      mPackageManager = pms;
      mInstaller = installer;
      mInstallLock = installLock;
    }

    /**
     * Compute and log hashes of the name and content of a secondary dex file.
     */
    @Override
    public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
            String dexPath, int storageFlags) {
        int ownerUid = appInfo.uid;

        byte[] hash = null;
        synchronized(mInstallLock) {
            try {
                hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
                        ownerUid, appInfo.volumeUuid, storageFlags);
            } catch (InstallerException e) {
                Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
                        " : " + e.getMessage());
            }
        }
        if (hash == null) {
            return;
        }

        String dexFileName = new File(dexPath).getName();
        String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
        // Valid SHA256 will be 256 bits, 32 bytes.
        if (hash.length == 32) {
            message = message + ' ' + ByteStringUtils.toHexString(hash);
        }

        EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, ownerUid, message);

        if (dexUseInfo.isUsedByOtherApps()) {
            Set<String> otherPackages = dexUseInfo.getLoadingPackages();
            Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
            for (String otherPackageName : otherPackages) {
                try {
                    int otherUid = mPackageManager.getPackageUid(
                        otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
                    if (otherUid != -1 && otherUid != ownerUid) {
                        otherUids.add(otherUid);
                    }
                } catch (RemoteException ignore) {
                    // Can't happen, we're local.
                }
            }
            for (int otherUid : otherUids) {
                EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, otherUid, message);
            }
        }
    }
}
+19 −4
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ public class DexManager {
    private final Object mInstallLock;
    @GuardedBy("mInstallLock")
    private final Installer mInstaller;
    private final Listener mListener;

    // Possible outcomes of a dex search.
    private static int DEX_SEARCH_NOT_FOUND = 0;  // dex file not found
@@ -96,14 +97,24 @@ public class DexManager {
     */
    private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();

    public interface Listener {
        /**
         * Invoked just before the secondary dex file {@code dexPath} for the specified application
         * is reconciled.
         */
        void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
                String dexPath, int storageFlags);
    }

    public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
            Installer installer, Object installLock) {
            Installer installer, Object installLock, Listener listener) {
      mPackageCodeLocationsCache = new HashMap<>();
      mPackageDexUsage = new PackageDexUsage();
      mPackageManager = pms;
      mPackageDexOptimizer = pdo;
      mInstaller = installer;
      mInstallLock = installLock;
      mListener = listener;
    }

    /**
@@ -389,7 +400,7 @@ public class DexManager {
                : mPackageDexOptimizer;
        String packageName = options.getPackageName();
        PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
        if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
        if (useInfo.getDexUseInfoMap().isEmpty()) {
            if (DEBUG) {
                Slog.d(TAG, "No secondary dex use for package:" + packageName);
            }
@@ -433,7 +444,7 @@ public class DexManager {
     */
    public void reconcileSecondaryDexFiles(String packageName) {
        PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
        if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
        if (useInfo.getDexUseInfoMap().isEmpty()) {
            if (DEBUG) {
                Slog.d(TAG, "No secondary dex use for package:" + packageName);
            }
@@ -481,12 +492,16 @@ public class DexManager {
                continue;
            }

            if (mListener != null) {
                mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
            }

            boolean dexStillExists = true;
            synchronized(mInstallLock) {
                try {
                    String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
                    dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
                            pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
                            info.uid, isas, info.volumeUuid, flags);
                } catch (InstallerException e) {
                    Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
                            " : " + e.getMessage());
+41 −6
Original line number Diff line number Diff line
@@ -17,12 +17,15 @@
package com.android.server.pm.dex;

import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import com.android.server.pm.Installer;

import dalvik.system.DelegateLastClassLoader;
import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
@@ -36,8 +39,13 @@ import java.util.List;
import java.util.Map;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -45,6 +53,12 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
@@ -56,6 +70,12 @@ public class DexManagerTests {
    private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
            DelegateLastClassLoader.class.getName();

    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
    @Mock Installer mInstaller;
    @Mock IPackageManager mPM;
    private final Object mInstallLock = new Object();
    @Mock DexManager.Listener mListener;

    private DexManager mDexManager;

    private TestData mFooUser0;
@@ -90,7 +110,8 @@ public class DexManagerTests {
        mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
                DELEGATE_LAST_CLASS_LOADER_NAME);

        mDexManager = new DexManager(null, null, null, null);
        mDexManager = new DexManager(
            mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener);

        // Foo and Bar are available to user0.
        // Only Bar is available to user1;
@@ -440,6 +461,20 @@ public class DexManagerTests {

    }

    @Test
    public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
        List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);

        when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
                .thenReturn(mFooUser0.mPackageInfo);

        mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());

        verify(mListener, times(fooSecondaries.size()))
                .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
                        any(DexUseInfo.class), anyString(), anyInt());
    }

    private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
@@ -492,12 +527,12 @@ public class DexManagerTests {
    }

    private PackageUseInfo getPackageUseInfo(TestData testData) {
        assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
        return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
        assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
        return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
    }

    private void assertNoUseInfo(TestData testData) {
        assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
        assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
    }

    private static PackageInfo getMockPackageInfo(String packageName, int userId) {
@@ -555,8 +590,8 @@ public class DexManagerTests {

        List<String> getSecondaryDexPathsFromProtectedDirs() {
            List<String> paths = new ArrayList<>();
            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
            paths.add(mPackageInfo.applicationInfo.deviceProtectedDataDir + "/secondary6.dex");
            paths.add(mPackageInfo.applicationInfo.credentialProtectedDataDir + "/secondary7.dex");
            return paths;
        }