1 // Copyright 2021 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.library_loader; 6 7 import android.os.Bundle; 8 9 import androidx.test.filters.SmallTest; 10 11 import org.junit.After; 12 import org.junit.Assert; 13 import org.junit.Before; 14 import org.junit.Rule; 15 import org.junit.Test; 16 import org.junit.runner.RunWith; 17 import org.mockito.ArgumentMatchers; 18 import org.mockito.Mock; 19 import org.mockito.Mockito; 20 import org.mockito.junit.MockitoJUnit; 21 import org.mockito.junit.MockitoRule; 22 import org.mockito.quality.Strictness; 23 import org.robolectric.annotation.Config; 24 25 import org.chromium.base.library_loader.Linker.PreferAddress; 26 import org.chromium.base.metrics.RecordHistogram; 27 import org.chromium.base.metrics.UmaRecorderHolder; 28 import org.chromium.base.test.BaseRobolectricTestRunner; 29 30 /** Tests for {@link Linker}. */ 31 @RunWith(BaseRobolectricTestRunner.class) 32 @Config(manifest = Config.NONE) 33 @SuppressWarnings("GuardedBy") // doNothing().when(...).methodLocked() cannot resolve |mLock|. 34 public class LinkerTest { 35 @Mock Linker.Natives mNativeMock; 36 37 @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); 38 39 @Before setUp()40 public void setUp() { 41 UmaRecorderHolder.resetForTesting(); 42 Linker.setLinkerNativesForTesting(mNativeMock); 43 } 44 45 @After tearDown()46 public void tearDown() { 47 Linker.setLinkerNativesForTesting(null); 48 } 49 anyLibInfo()50 static Linker.LibInfo anyLibInfo() { 51 return ArgumentMatchers.any(Linker.LibInfo.class); 52 } 53 54 @Test 55 @SmallTest testConsumer()56 public void testConsumer() { 57 // Set up. 58 Linker linker = Mockito.spy(new Linker()); 59 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 60 61 // Exercise. 62 long someAddress = 1 << 12; 63 linker.ensureInitialized( 64 /* asRelroProducer= */ false, PreferAddress.RESERVE_HINT, someAddress); 65 66 // Verify. 67 Assert.assertFalse(linker.mRelroProducer); 68 Mockito.verify(mNativeMock).reserveMemoryForLibrary(anyLibInfo()); 69 Assert.assertNotEquals(null, linker.mLocalLibInfo); 70 Assert.assertEquals(someAddress, linker.mLocalLibInfo.mLoadAddress); 71 } 72 73 @Test 74 @SmallTest testProducer()75 public void testProducer() { 76 // Set up. 77 Linker linker = Mockito.spy(new Linker()); 78 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 79 80 // Exercise. 81 linker.ensureInitialized(/* asRelroProducer= */ true, PreferAddress.RESERVE_RANDOM, 0); 82 83 // Verify. 84 Assert.assertTrue(linker.mRelroProducer); 85 Mockito.verify(mNativeMock).findMemoryRegionAtRandomAddress(anyLibInfo()); 86 Assert.assertNotEquals(null, linker.mLocalLibInfo); 87 } 88 89 @Test 90 @SmallTest testConsumerReserveRandom()91 public void testConsumerReserveRandom() { 92 // Set up. 93 Linker linker = Mockito.spy(new Linker()); 94 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 95 96 // Exercise. 97 linker.ensureInitialized(/* asRelroProducer= */ false, PreferAddress.RESERVE_RANDOM, 0); 98 99 // Verify. 100 Mockito.verify(mNativeMock).findMemoryRegionAtRandomAddress(anyLibInfo()); 101 } 102 103 @Test 104 @SmallTest testReservingZeroFallsBackToRandom()105 public void testReservingZeroFallsBackToRandom() { 106 // Set up. 107 Linker linker = Mockito.spy(new Linker()); 108 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 109 110 // Exercise. 111 linker.ensureInitialized(/* asRelroProducer= */ false, PreferAddress.RESERVE_HINT, 0); 112 113 // Verify. 114 Mockito.verify(mNativeMock).findMemoryRegionAtRandomAddress(anyLibInfo()); 115 } 116 117 @Test 118 @SmallTest testAppZygoteProducingRelro()119 public void testAppZygoteProducingRelro() { 120 // Set up. 121 Linker linker = Mockito.spy(new Linker()); 122 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 123 // The lookup of the region succeeds. 124 Mockito.when(mNativeMock.findRegionReservedByWebViewZygote(anyLibInfo())).thenReturn(true); 125 Mockito.when(linker.isNonZeroLoadAddress(anyLibInfo())).thenReturn(true); 126 127 // Exercise. 128 linker.ensureInitialized(/* asRelroProducer= */ true, PreferAddress.FIND_RESERVED, 0); 129 130 // Verify. 131 Mockito.verify(mNativeMock).findRegionReservedByWebViewZygote(anyLibInfo()); 132 Mockito.verify(mNativeMock, Mockito.never()).findMemoryRegionAtRandomAddress(anyLibInfo()); 133 Mockito.verify(mNativeMock, Mockito.never()).reserveMemoryForLibrary(anyLibInfo()); 134 } 135 136 @Test 137 @SmallTest testAppZygoteFailsToFindReservedAddressRange()138 public void testAppZygoteFailsToFindReservedAddressRange() { 139 // Set up. 140 Linker linker = Mockito.spy(new Linker()); 141 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 142 // The lookup of the region fails. 143 Mockito.when(mNativeMock.findRegionReservedByWebViewZygote(anyLibInfo())).thenReturn(false); 144 145 // Exercise. 146 linker.ensureInitialized(/* asRelroProducer= */ true, PreferAddress.FIND_RESERVED, 0); 147 148 // Verify. 149 Mockito.verify(mNativeMock).findRegionReservedByWebViewZygote(anyLibInfo()); 150 Mockito.verify(mNativeMock).findMemoryRegionAtRandomAddress(anyLibInfo()); 151 } 152 153 @Test 154 @SmallTest testRelroSharingStatusHistogram()155 public void testRelroSharingStatusHistogram() { 156 // Set up. 157 Linker linker = Mockito.spy(new Linker()); 158 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 159 Mockito.when(mNativeMock.getRelroSharingResult()).thenReturn(1); 160 Linker.LibInfo libInfo = Mockito.spy(new Linker.LibInfo()); 161 long someAddress = 1 << 12; 162 libInfo.mLoadAddress = someAddress; 163 // Set a fake RELRO FD so that it is not silently ignored when taking the LibInfo from the 164 // (simulated) outside. 165 libInfo.mRelroFd = 1023; 166 // Create the bundle following the _internal_ format of the Linker. Not great, but shorter 167 // than factoring out this logic from the Linker only for testing. 168 Bundle relros = libInfo.toBundle(); 169 Bundle b = new Bundle(); 170 b.putBundle(Linker.SHARED_RELROS, relros); 171 172 // Exercise. 173 linker.ensureInitialized( 174 /* asRelroProducer= */ false, PreferAddress.RESERVE_HINT, someAddress); 175 linker.pretendLibraryIsLoadedForTesting(); 176 linker.takeSharedRelrosFromBundle(b); 177 178 // Verify. 179 Assert.assertEquals( 180 1, 181 RecordHistogram.getHistogramTotalCountForTesting( 182 "ChromiumAndroidLinker.RelroSharingStatus2")); 183 } 184 185 @Test 186 @SmallTest testBrowserExpectingRelroFromZygote()187 public void testBrowserExpectingRelroFromZygote() { 188 // Set up. 189 Linker linker = Mockito.spy(new Linker()); 190 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 191 // The lookup of the region succeeds. 192 Mockito.when(mNativeMock.findRegionReservedByWebViewZygote(anyLibInfo())).thenReturn(true); 193 Mockito.when(linker.isNonZeroLoadAddress(anyLibInfo())).thenReturn(true); 194 195 // Exercise. 196 linker.ensureInitialized(/* asRelroProducer= */ false, PreferAddress.FIND_RESERVED, 0); 197 198 // Verify. 199 Mockito.verify(mNativeMock).findRegionReservedByWebViewZygote(anyLibInfo()); 200 Mockito.verify(mNativeMock, Mockito.never()).findMemoryRegionAtRandomAddress(anyLibInfo()); 201 Mockito.verify(mNativeMock, Mockito.never()).reserveMemoryForLibrary(anyLibInfo()); 202 } 203 204 @Test 205 @SmallTest testPrivilegedProcessWithHint()206 public void testPrivilegedProcessWithHint() { 207 // Set up. 208 Linker linker = Mockito.spy(new Linker()); 209 Mockito.doNothing().when(linker).loadLinkerJniLibraryLocked(); 210 // The lookup of the region succeeds. 211 Mockito.when(mNativeMock.findRegionReservedByWebViewZygote(anyLibInfo())).thenReturn(true); 212 Mockito.when(linker.isNonZeroLoadAddress(anyLibInfo())).thenReturn(true); 213 214 // Exercise. 215 long someAddress = 1 << 12; 216 linker.ensureInitialized( 217 /* asRelroProducer= */ false, PreferAddress.FIND_RESERVED, someAddress); 218 219 // Verify. 220 Mockito.verify(mNativeMock).findRegionReservedByWebViewZygote(anyLibInfo()); 221 // Unfortunately there does not seem to be an elegant way to set |mLoadAddress| without 222 // extracting creation of mLocalLibInfo from ensureInitialized(). Hence no checks are 223 // present here involving the exact value of |mLoadAddress|. 224 } 225 } 226