xref: /aosp_15_r20/system/linkerconfig/devicetest/src/android/linkerconfig/gts/LinkerConfigTest.java (revision e5eeaa8e05bc25a862c0c861bda7c8a6bfb42dad)
1 /*
2  * Copyright (C) 2020 Google LLC.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.linkerconfig.gts;
18 
19 import static org.hamcrest.Matchers.contains;
20 import static org.hamcrest.Matchers.empty;
21 import static org.hamcrest.Matchers.not;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertThat;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 import static org.junit.Assume.assumeTrue;
27 
28 import android.linkerconfig.gts.utils.LibraryListLoader;
29 import android.linkerconfig.gts.utils.LinkerConfigParser;
30 import android.linkerconfig.gts.utils.elements.Configuration;
31 import android.linkerconfig.gts.utils.elements.Namespace;
32 import android.linkerconfig.gts.utils.elements.Section;
33 
34 import com.android.compatibility.common.util.ApiLevelUtil;
35 import com.android.compatibility.common.util.GmsTest;
36 import com.android.tradefed.device.DeviceNotAvailableException;
37 import com.android.tradefed.device.INativeDevice;
38 import com.android.tradefed.device.ITestDevice;
39 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
40 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
41 
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 
45 import java.io.BufferedReader;
46 import java.io.File;
47 import java.io.FileReader;
48 import java.util.ArrayList;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Set;
53 import java.util.stream.Collectors;
54 
55 @RunWith(DeviceJUnit4ClassRunner.class)
56 public class LinkerConfigTest extends BaseHostJUnit4Test {
57 
58     private static final String LINKER_CONFIG_LOCATION = "/linkerconfig/ld.config.txt";
59     private static final String VENDOR_VNDK_LITE = "ro.vndk.lite";
60     private static final String VENDOR_VNDK_VERSION = "ro.vndk.version";
61     private static final String BOARD_API_LEVEL = "ro.board.api_level";
62     private static final int TARGET_MIN_VER = 30; // linkerconfig is available from R
63 
isValidVersion(ITestDevice device)64     private static boolean isValidVersion(ITestDevice device) {
65         try {
66             return ApiLevelUtil.isAtLeast(device, TARGET_MIN_VER);
67         } catch (DeviceNotAvailableException e) {
68             fail("There is no available device : " + e.getMessage());
69         }
70 
71         return false;
72     }
73 
loadConfig(INativeDevice targetDevice, String path)74     private static Configuration loadConfig(INativeDevice targetDevice, String path) {
75         File target = null;
76 
77         try {
78             target = targetDevice.pullFile(path);
79         } catch (DeviceNotAvailableException e) {
80             fail("There is no available device : " + e.getMessage());
81         }
82 
83         assertTrue("Failed to get linker configuration from expected location",
84                 target.exists());
85 
86         List<String> lines = new ArrayList<>();
87         try (BufferedReader reader = new BufferedReader(new FileReader(target))) {
88             String line;
89             while ((line = reader.readLine()) != null) {
90                 line = line.trim();
91                 if (!line.isEmpty()) {
92                     lines.add(line);
93                 }
94             }
95         } catch (Exception e) {
96             fail("Failed to read file " + path + " with error : " + e.getMessage());
97         }
98 
99         return LinkerConfigParser.parseConfiguration(lines);
100     }
101 
verifyVendorSection(Section section, Set<String> systemAvailableLibraries)102     private static void verifyVendorSection(Section section, Set<String> systemAvailableLibraries) {
103         List<Namespace> systemNamespaces = section.namespaces.values().stream()
104                 .filter(ns -> ns.searchPaths.stream()
105                         .anyMatch(searchPath -> searchPath.startsWith("/system")
106                                 && !searchPath.startsWith("/system/vendor")))
107                 .distinct()
108                 .collect(Collectors.toList());
109 
110         assertFalse("System namespace should not be visible",
111                 systemNamespaces.parallelStream().anyMatch(ns -> ns.isVisible));
112 
113         section.namespaces.values().forEach((ns) -> {
114             boolean isVendorNamespace = false;
115             for (String libPath : ns.searchPaths) {
116                 if (libPath.startsWith(("/vendor")) || libPath.startsWith("/odm")) {
117                     isVendorNamespace = true;
118                     break;
119                 }
120             }
121 
122             if (!isVendorNamespace) {
123                 return;
124             }
125 
126             assertThat(
127                     "Vendor libs and System libs should not exist in same namespace : " + ns.name,
128                     systemNamespaces, not(contains(ns)));
129 
130             ns.links.values().forEach((link) -> {
131                 if (systemNamespaces.contains(link.to)) {
132                     assertFalse(
133                             "It is not allowed to link all shared libs from non-system namespace "
134                                     + link.from
135                                     + "to system namespace " + link.to,
136                             link.allowAll);
137 
138                     link.libraries.forEach(library -> {
139                         assertTrue("Library " + library + " is not allowed to use",
140                                 systemAvailableLibraries.contains(library));
141                     });
142                 }
143             });
144         });
145     }
146 
147 
loadSystemAvailableLibraries(INativeDevice targetDevice, String vendorVndkVersion)148     private static Set<String> loadSystemAvailableLibraries(INativeDevice targetDevice,
149             String vendorVndkVersion) {
150         Set<String> libraries = new HashSet<>();
151 
152         // Add Sanitizer libraries
153         libraries.addAll(LibraryListLoader.getLibrariesFromFile(targetDevice,
154                 "/system/etc/sanitizer.libraries.txt", true));
155 
156         // Add LLNDK libraries
157         if (vendorVndkVersion == null || vendorVndkVersion.isEmpty()) {
158             libraries.addAll(LibraryListLoader.getLibrariesFromFile(targetDevice,
159                 "/system/etc/llndk.libraries.txt", true));
160         } else {
161             libraries.addAll(LibraryListLoader.getLibrariesFromFile(targetDevice,
162                 "/apex/com.android.vndk.v" + vendorVndkVersion + "/etc/llndk.libraries."
163                         + vendorVndkVersion + ".txt", true));
164         }
165 
166         // Add Stub libraries
167         libraries.addAll(LibraryListLoader.STUB_LIBRARIES);
168 
169         // Allowed on userdebug/eng for debugging.
170         libraries.add("libfdtrack.so");
171 
172         // Add VNDK core variant libraries
173         libraries.addAll(LibraryListLoader.getLibrariesFromFile(targetDevice,
174                     "/system/etc/vndkcorevariant.libraries.txt", false));
175 
176         return libraries;
177     }
178 
179     @GmsTest(requirement = "GMS-3.5-014")
180     @Test
shouldHaveLinkerConfigAtExpectedLocation()181     public void shouldHaveLinkerConfigAtExpectedLocation() {
182         ITestDevice targetDevice = getDevice();
183 
184         if (!isValidVersion(targetDevice)) {
185             return;
186         }
187 
188         try {
189             File linkerConfigFile = targetDevice.pullFile(LINKER_CONFIG_LOCATION);
190             assertTrue("Failed to get linker configuration from expected location",
191                     linkerConfigFile.exists());
192         } catch (DeviceNotAvailableException e) {
193             fail("Target device is not available : " + e.getMessage());
194         }
195     }
196 
197     @GmsTest(requirement = "GMS-3.5-014")
198     @Test
shouldNotAccessSystemFromVendorExceptVndk()199     public void shouldNotAccessSystemFromVendorExceptVndk() {
200         ITestDevice targetDevice = getDevice();
201         boolean vndkLiteEnabled = false;
202 
203         try {
204             vndkLiteEnabled = Boolean.parseBoolean(targetDevice.getProperty(VENDOR_VNDK_LITE));
205         } catch (DeviceNotAvailableException e) {
206             fail("Target device is not available : " + e.getMessage());
207         }
208 
209         if (!isValidVersion(targetDevice) || vndkLiteEnabled) {
210             return;
211         }
212 
213         String vendorVndkVersion = "";
214         try {
215             vendorVndkVersion = targetDevice.getProperty(VENDOR_VNDK_VERSION);
216         } catch (DeviceNotAvailableException e) {
217             fail("Target device is not available : " + e.getMessage());
218         }
219 
220         int boardApiLevel = 0;
221         try {
222             boardApiLevel = Integer.parseInt(targetDevice.getProperty(BOARD_API_LEVEL));
223         } catch (DeviceNotAvailableException e) {
224             fail("Target device is not available : " + e.getMessage());
225         } catch (NumberFormatException e) {
226             // fallback with 0
227             boardApiLevel = 0;
228         }
229 
230         assumeTrue(boardApiLevel >= 202404 || (vendorVndkVersion != null &&
231                 !vendorVndkVersion.isEmpty()));
232 
233         Configuration conf = loadConfig(targetDevice, LINKER_CONFIG_LOCATION);
234 
235         List<Section> vendorSections = conf.dirToSections.entrySet().stream()
236                 .filter(item -> item.getKey().startsWith("/vendor") || item.getKey().startsWith(
237                         ("/odm")))
238                 .map(Map.Entry::getValue)
239                 .distinct()
240                 .collect(Collectors.toList());
241 
242         assertThat("No section for vendor", vendorSections, not(empty()));
243 
244         Set<String> availableLibraries = loadSystemAvailableLibraries(targetDevice,
245                 vendorVndkVersion);
246 
247         for (Section section : vendorSections) {
248             verifyVendorSection(section, availableLibraries);
249         }
250     }
251 }
252