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