// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser;
import android.text.TextUtils;
import androidx.test.filters.MediumTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
/** Test to make sure browser and renderer are seperated process. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class ProcessIsolationTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
/**
* Verifies that process isolation works, i.e., that the browser and renderer processes use
* different user IDs.
*/
@Test
@MediumTest
@DisableIf.Build(sdk_is_greater_than = 22, message = "crbug.com/517611")
@Feature({"Browser", "Security"})
public void testProcessIsolationForRenderers() throws IOException {
int tabsCount = mActivityTestRule.getActivity().getCurrentTabModel().getCount();
// The ActivityManager can be used to retrieve the current processes, but the reported UID
// in the RunningAppProcessInfo for isolated processes is the same as the parent process
// (see b/7724486, closed as "Working as intended").
// So we have to resort to parsing the ps output.
String packageName = ContextUtils.getApplicationContext().getPackageName();
Assert.assertFalse(
"Failed to retrieve package name for current version of Chrome.",
TextUtils.isEmpty(packageName));
ArrayList<String> uids = new ArrayList<String>();
BufferedReader reader = null;
boolean hasBrowserProcess = false;
int rendererProcessesCount = 0;
StringBuilder sb = new StringBuilder();
try {
Process psProcess = Runtime.getRuntime().exec("ps");
reader = new BufferedReader(new InputStreamReader(psProcess.getInputStream()));
String line = reader.readLine();
Assert.assertNotNull(line);
final String[] lineSections = line.split("\\s+");
int pidIndex = -1;
for (int index = 0; index < lineSections.length; index++) {
if ("PID".equals(lineSections[index])) {
pidIndex = index;
break;
}
}
Assert.assertNotSame(-1, pidIndex);
while ((line = reader.readLine()) != null) {
sb.append(line).append('\n');
if (line.indexOf(packageName) != -1) {
final String uid = line.split("\\s+")[pidIndex];
Assert.assertNotNull("Failed to retrieve UID from " + line, uid);
if (line.indexOf("sandboxed_process") != -1) {
// Renderer process.
uids.add(uid);
rendererProcessesCount++;
} else if (line.indexOf(packageName + ".") == -1) {
// Browser process.
if (!hasBrowserProcess) {
// On certain versions of Android 'ps' itself can appear in the list of
// processes with the same name as its parent (which is the browser
// process). Only the first occurence of the browser process is kept.
// Note that it is guaranteed that the first occurence corresponds to
// the browser process rather than 'ps' (its child) since processes in
// ps' output are sorted by ascending PID.
uids.add(uid);
hasBrowserProcess = true;
}
}
}
}
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ioe) {
// Intentionally do nothing.
}
}
}
Assert.assertTrue(
"Browser process not found in ps output: \n" + sb.toString(), hasBrowserProcess);
// We should have the same number of process as tabs count. Sometimes
// there can be extra utility sandbox process so we check for greater than.
Assert.assertTrue(
"Renderer processes not found in ps output: \n" + sb.toString(),
rendererProcessesCount >= tabsCount);
Assert.assertEquals(
"Found at least two processes with the same UID in ps output: \n" + sb.toString(),
uids.size(),
new HashSet<String>(uids).size());
}
@Before
public void setUp() {
mActivityTestRule.startMainActivityFromLauncher();
}
}