• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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