1#!/bin/bash
2
3# Copyright (c) 2010 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7# This utility finds the different processes in a running instance of Chrome.
8# It then attempts to identify their types (e.g. browser, extension, plugin,
9# zygote, renderer). It also prints out information on whether a sandbox is
10# active and what type of sandbox has been identified.
11
12# This script is likely to only work on Linux or systems that closely mimick
13# Linux's /proc filesystem.
14[ -x /proc/self/exe ] || {
15  echo "This script cannot be run on your system" >&2
16  exit 1
17}
18
19# Find the browser's process id. If there are multiple active instances of
20# Chrome, the caller can provide a pid on the command line. The provided pid
21# must match a process in the browser's process hierarchy. When using the
22# zygote inside of the setuid sandbox, renderers are in a process tree separate
23# from the browser process. You cannot use any of their pids.
24# If no pid is provided on the command line, the script will randomly pick
25# one of the running instances.
26if [ $# -eq 0 ]; then
27  pid=$(ls -l /proc/*/exe 2>/dev/null |
28        sed '/\/chrome\( .deleted.\)\?$/s,.*/proc/\([^/]*\)/exe.*,\1,;t;d' |
29        while read p; do
30          xargs -0 </proc/$p/cmdline 2>/dev/null|grep -q -- --type= && continue
31          echo "$p"
32          break
33        done)
34else
35  pid="$1"
36fi
37ls -l "/proc/$pid/exe" 2>/dev/null|egrep -q '/chrome( .deleted.)?$' || {
38  echo "Cannot find any running instance of Chrome" >&2; exit 1; }
39while :; do
40  ppid="$(ps h --format ppid --pid "$pid" 2>/dev/null)"
41  [ -n "$ppid" ] || {
42    echo "Cannot find any running instance of Chrome" >&2; exit 1; }
43  ls -l "/proc/$ppid/exe" 2>/dev/null|egrep -q '/chrome( .deleted.)?$' &&
44    pid="$ppid" || break
45done
46xargs -0 </proc/$p/cmdline 2>/dev/null|grep -q -- --type= && {
47  echo "Cannot find any running instance of Chrome" >&2; exit 1; }
48
49# Iterate over child processes and try to identify them
50identify() {
51  local child cmd foundzygote plugin seccomp type
52  foundzygote=0
53  for child in $(ps h --format pid --ppid $1); do
54    cmd="$(xargs -0 </proc/$child/cmdline|sed 's/ -/\n-/g')" 2>/dev/null
55    type="$(echo "$cmd" | sed 's/--type=//;t1;d;:1;q')"
56    case $type in
57      '')
58        echo "Process $child is part of the browser"
59        identify "$child"
60        ;;
61      extension)
62        echo "Process $child is an extension"
63        ;;
64      plugin)
65        plugin="$(echo "$cmd" |
66                 sed 's/--plugin-path=//;t1;d;:1
67                      s,.*/lib,,;s,.*/npwrapper[.]lib,,;s,^np,,;s,[.]so$,,;q')"
68        echo "Process $child is a \"$plugin\" plugin"
69        identify "$child"
70        ;;
71      renderer|worker|gpu-process)
72        # The seccomp sandbox has exactly one child process that has no other
73        # threads. This is the trusted helper process.
74        seccomp="$(ps h --format pid --ppid $child|xargs)"
75        if [ -d /proc/$child/cwd/. ]; then
76          if [ $(echo "$seccomp" | wc -w) -eq 1 ] &&
77             [ $(ls /proc/$seccomp/task 2>/dev/null | wc -w) -eq 1 ] &&
78             ls -l /proc/$seccomp/exe 2>/dev/null |
79               egrep -q '/chrome( .deleted.)?$'; then
80            echo "Process $child is a sandboxed $type (seccomp helper:" \
81                 "$seccomp)"
82          else
83            echo "Process $child is a $type"
84            identify "$child"
85          fi
86        else
87          if [ $(echo "$seccomp" | wc -w) -eq 1 ]; then
88            echo "Process $child is a setuid sandboxed $type (seccomp" \
89                 "helper: $seccomp)"
90          else
91            echo "Process $child is a $type; setuid sandbox is active"
92            identify "$child"
93          fi
94        fi
95        ;;
96      zygote)
97        foundzygote=1
98        echo "Process $child is the zygote"
99        identify "$child"
100        ;;
101      *)
102        echo "Process $child is of unknown type \"$type\""
103        identify "$child"
104        ;;
105    esac
106  done
107  return $foundzygote
108}
109
110cmpcmdline() {
111  # Checks that the command line arguments for pid $1 are a superset of the
112  # commandline arguments for pid $2.
113  # Any additional function arguments $3, $4, ... list options that should
114  # be ignored for the purpose of this comparison.
115  local pida="$1"
116  local pidb="$2"
117  shift; shift
118  local super=("$@" $(xargs -0 </proc/"$pida"/cmdline)) 2>/dev/null
119  local sub=($(xargs -0 </proc/"$pidb"/cmdline)) 2>/dev/null
120  local i j
121  [ ${#sub[*]} -eq 0 -o ${#super[*]} -eq 0 ] && return 1
122  for i in $(seq 0 $((${#sub[*]}-1))); do
123    for j in $(seq 0 $((${#super[*]}-1))); do
124      [ "x${sub[$i]}" = "x${super[$j]}" ] && continue 2
125    done
126    return 1
127  done
128  return 0
129}
130
131
132echo "The browser's main pid is: $pid"
133if identify "$pid"; then
134  # The zygote can make it difficult to locate renderers, as the setuid
135  # sandbox causes it to be reparented to "init". When this happens, we can
136  # no longer associate it with the browser with 100% certainty. We make a
137  # best effort by comparing command line strings.
138  for i in $(ps h --format pid --ppid 1); do
139    if cmpcmdline "$pid" "$i" "--type=zygote"; then
140      echo -n "Process $i is the zygote"
141      [ -d /proc/$i/cwd/. ] || echo -n "; setuid sandbox is active"
142      echo
143      identify "$i"
144    fi
145  done
146fi
147