1 /* 2 * Copyright (C) 2016 The Android Open Source Project 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.security.cts; 18 19 import com.android.ddmlib.NullOutputReceiver; 20 import com.android.tradefed.device.CollectingOutputReceiver; 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.testtype.DeviceTestCase; 24 import com.android.tradefed.log.LogUtil.CLog; 25 26 import android.platform.test.annotations.RootPermissionTest; 27 28 import java.io.BufferedOutputStream; 29 import java.io.File; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.OutputStream; 34 import java.util.Scanner; 35 import java.util.concurrent.TimeUnit; 36 37 public class AdbUtils { 38 39 /** Runs a commandline on the specified device 40 * 41 * @param command the command to be ran 42 * @param device device for the command to be ran on 43 * @return the console output from running the command 44 */ 45 public static String runCommandLine(String command, ITestDevice device) throws Exception { 46 return device.executeShellCommand(command); 47 } 48 49 /** 50 * Pushes and runs a binary to the selected device 51 * 52 * @param pathToPoc a string path to poc from the /res folder 53 * @param device device to be ran on 54 * @return the console output from the binary 55 */ 56 public static String runPoc(String pocName, ITestDevice device) throws Exception { 57 device.executeShellCommand("chmod +x /data/local/tmp/" + pocName); 58 return device.executeShellCommand("/data/local/tmp/" + pocName); 59 } 60 61 /** 62 * Pushes and runs a binary to the selected device 63 * 64 * @param pocName a string path to poc from the /res folder 65 * @param device device to be ran on 66 * @param timeout time to wait for output in seconds 67 * @return the console output from the binary 68 */ 69 public static String runPoc(String pocName, ITestDevice device, int timeout) throws Exception { 70 device.executeShellCommand("chmod +x /data/local/tmp/" + pocName); 71 CollectingOutputReceiver receiver = new CollectingOutputReceiver(); 72 device.executeShellCommand("/data/local/tmp/" + pocName, receiver, timeout, TimeUnit.SECONDS, 0); 73 String output = receiver.getOutput(); 74 return output; 75 } 76 77 /** 78 * Pushes and runs a binary to the selected device and ignores any of its output. 79 * 80 * @param pocName a string path to poc from the /res folder 81 * @param device device to be ran on 82 * @param timeout time to wait for output in seconds 83 */ 84 public static void runPocNoOutput(String pocName, ITestDevice device, int timeout) 85 throws Exception { 86 device.executeShellCommand("chmod +x /data/local/tmp/" + pocName); 87 NullOutputReceiver receiver = new NullOutputReceiver(); 88 device.executeShellCommand("/data/local/tmp/" + pocName, receiver, timeout, 89 TimeUnit.SECONDS, 0); 90 } 91 92 /** 93 * Enables malloc debug on a given process. 94 * 95 * @param processName the name of the process to run with libc malloc debug 96 * @param device the device to use 97 * @return true if enabling malloc debug succeeded 98 */ 99 public static boolean enableLibcMallocDebug(String processName, ITestDevice device) throws Exception { 100 device.executeShellCommand("setprop libc.debug.malloc.program " + processName); 101 device.executeShellCommand("setprop libc.debug.malloc.options \"backtrace guard\""); 102 /** 103 * The pidof command is being avoided because it does not exist on versions before M, and 104 * it behaves differently between M and N. 105 * Also considered was the ps -AoPID,CMDLINE command, but ps does not support options on 106 * versions before O. 107 * The [^]] prefix is being used for the grep command to avoid the case where the output of 108 * ps includes the grep command itself. 109 */ 110 String cmdOut = device.executeShellCommand("ps -A | grep '[^]]" + processName + "'"); 111 /** 112 * .hasNextInt() checks if the next token can be parsed as an integer, not if any remaining 113 * token is an integer. 114 * Example command: $ ps | fgrep mediaserver 115 * Out: media 269 1 77016 24416 binder_thr 00f35142ec S /system/bin/mediaserver 116 * The second field of the output is the PID, which is needed to restart the process. 117 */ 118 Scanner s = new Scanner(cmdOut).useDelimiter("\\D+"); 119 if(!s.hasNextInt()) { 120 CLog.w("Could not find pid for process: " + processName); 121 return false; 122 } 123 124 String result = device.executeShellCommand("kill -9 " + s.nextInt()); 125 if(!result.equals("")) { 126 CLog.w("Could not restart process: " + processName); 127 return false; 128 } 129 130 TimeUnit.SECONDS.sleep(1); 131 return true; 132 } 133 134 /** 135 * Pushes and installs an apk to the selected device 136 * 137 * @param pathToApk a string path to apk from the /res folder 138 * @param device device to be ran on 139 * @return the output from attempting to install the apk 140 */ 141 public static String installApk(String pathToApk, ITestDevice device) throws Exception { 142 143 String fullResourceName = pathToApk; 144 File apkFile = File.createTempFile("apkFile", ".apk"); 145 try { 146 apkFile = extractResource(fullResourceName, apkFile); 147 return device.installPackage(apkFile, true); 148 } finally { 149 apkFile.delete(); 150 } 151 } 152 153 /** 154 * Extracts a resource and pushes it to the device 155 * 156 * @param fullResourceName a string path to resource from the res folder 157 * @param deviceFilePath the remote destination absolute file path 158 * @param device device to be ran on 159 */ 160 public static void pushResource(String fullResourceName, String deviceFilePath, 161 ITestDevice device) throws Exception { 162 File resFile = File.createTempFile("CTSResource", ""); 163 try { 164 resFile = extractResource(fullResourceName, resFile); 165 device.pushFile(resFile, deviceFilePath); 166 } finally { 167 resFile.delete(); 168 } 169 } 170 171 /** 172 * Extracts the binary data from a resource and writes it to a temp file 173 */ 174 private static File extractResource(String fullResourceName, File file) throws Exception { 175 try (InputStream in = AdbUtils.class.getResourceAsStream(fullResourceName); 176 OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { 177 if (in == null) { 178 throw new IllegalArgumentException("Resource not found: " + fullResourceName); 179 } 180 byte[] buf = new byte[65536]; 181 int chunkSize; 182 while ((chunkSize = in.read(buf)) != -1) { 183 out.write(buf, 0, chunkSize); 184 } 185 return file; 186 } 187 188 } 189 /** 190 * Utility function to help check the exit code of a shell command 191 */ 192 public static int runCommandGetExitCode(String cmd, ITestDevice device) throws Exception { 193 return Integer.parseInt( 194 AdbUtils.runCommandLine( "(" + cmd + ") > /dev/null 2>&1; echo $?", 195 device).replaceAll("[^0-9]", "")); 196 } 197 198 /** 199 * Pushes and runs a binary to the selected device and checks exit code 200 * Return code 113 is used to indicate the vulnerability 201 * 202 * @param pocName a string path to poc from the /res folder 203 * @param device device to be ran on 204 * @param timeout time to wait for output in seconds 205 */ 206 public static boolean runPocCheckExitCode(String pocName, ITestDevice device, 207 int timeout) throws Exception { 208 device.executeShellCommand("chmod +x /data/local/tmp/" + pocName); 209 CollectingOutputReceiver receiver = new CollectingOutputReceiver(); 210 device.executeShellCommand("/data/local/tmp/" + pocName + " > /dev/null 2>&1; echo $?", 211 receiver, timeout, TimeUnit.SECONDS, 0); 212 213 String returnStr = null; 214 int returnNum = 0; 215 216 try{ 217 returnStr = receiver.getOutput().replaceAll("[^0-9]", ""); 218 }catch(NullPointerException e){ 219 return false; 220 } 221 try{ 222 returnNum = Integer.parseInt(returnStr); 223 }catch(NumberFormatException e){ 224 return false; 225 } 226 227 //Refer to go/asdl-sts-guide Test section for knowing the significance of 113 code 228 return returnNum == 113; 229 } 230 } 231