1#!/bin/bash
2
3# Copyright 2016 The Fuchsia Authors
4#
5# Use of this source code is governed by a MIT-style
6# license that can be found in the LICENSE file or at
7# https://opensource.org/licenses/MIT
8
9# This script will perform static analyses provided by Clang Static analyzers
10# on Zircon. It requires either a prebuilt Clang toolchan or a Clang toolchain
11# built from official Clang repository. For instructions on how to obtain a
12# prebuilt toolchain or build the toolchain from scratch, please refer to
13# document at
14# https://fuchsia.googlesource.com/zircon/+/master/docs/getting_started.md
15
16set -eu
17
18ORIGINAL_CWD="$(pwd)"
19
20# These are the default checkers for clang and are always on by defualt,
21# unless they are explicitley disabled
22CHECKERS_DEFAULT_ON="\
23  apiModeling.google.GTest \
24  core.CallAndMessage \
25  core.DynamicTypePropagation \
26  core.DivideZero \
27  core.NonNullParamChecker \
28  core.NullDereference \
29  core.StackAddressEscape \
30  core.UndefinedBinaryOperatorResult \
31  core.VLASize \
32  core.uninitialized.ArraySubscript \
33  core.uninitialized.Assign \
34  core.uninitialized.Branch \
35  core.uninitialized.CapturedBlockVariable \
36  core.uninitialized.UndefReturn \
37  cplusplus.NewDelete \
38  cplusplus.NewDeleteLeaks \
39  cplusplus.SelfAssignment \
40  deadcode.DeadStores \
41  nullability.NullPassedToNonnull \
42  nullability.NullReturnedFromNonnull \
43  security.insecureAPI.UncheckedReturn \
44  security.insecureAPI.getpw \
45  security.insecureAPI.gets \
46  security.insecureAPI.mkstemp \
47  security.insecureAPI.mktemp \
48  security.insecureAPI.vfork \
49  unix.API \
50  unix.Malloc \
51  unix.MallocSizeof \
52  unix.MismatchedDeallocator \
53  unix.Vfork \
54  unix.cstring.BadSizeArg \
55  unix.cstring.NullArg \
56"
57
58# Zircon specific checkers
59# Content may change in the future
60# TODO: This checker will only work after https://reviews.llvm.org/D36024 lands.
61CHECKERS_ZIRCON="alpha.zircon.ZirconHandleChecker"
62
63# Checkers that should be enabled
64CHECKERS_TO_ENABLE=""
65
66# Checkers that should be disabled
67CHECKERS_TO_DISABLE=""
68
69# Checkers Args that should be passed to Clang Static Analyzer
70CHECKERS=""
71
72func_disable_checkers() {
73  # Disable default checkers
74  for i in $CHECKERS_TO_DISABLE; do
75    CHECKERS="$CHECKERS -disable-checker $i"
76  done
77}
78
79func_enable_checkers() {
80  # Enable the checkers stored in $CHECKERS_TO_ENABLE
81  for i in $CHECKERS_TO_ENABLE; do
82    CHECKERS="$CHECKERS -enable-checker $i"
83  done
84}
85
86func_test_exist() {
87  if [ ! -e "$1" ]; then
88    echo "$1 does not exist! Please check your input. Aborting!"
89    exit -1
90  fi
91}
92
93func_trap_handler() {
94  cd "$ORIGINAL_CWD"
95  exit -1
96}
97
98# Register trap
99trap func_trap_handler SIGHUP SIGINT SIGTERM EXIT
100
101# Analyzer run mode. clang|zircon|all
102RUN_MODE="clang"
103# Path to python version of scan_build
104SCAN_BUILD_PY=""
105# Path to CC wrapper of scan_build_py
106SCAN_BUILD_PY_CC=""
107# Path to CXX wrapper of scan_build_py
108SCAN_BUILD_PY_CXX=""
109# Build target
110BUILD_TARGET=""
111
112# Path to clang
113CLANG_PATH=""
114
115# Path to clang++
116CLANGXX_PATH=""
117
118# Path to toolchain
119TOOLCHAIN_PREFIX=""
120
121# Path to directory that contains scan-build-py/bin
122SCAN_BUILD_PY_PREFIX=""
123
124# Do not process unit tests flag
125DISABLE_UTEST=false
126
127function func_help {
128  echo "help:"
129  echo "-p <toolchain prefix>     : path to the directory containing bin/clang"
130  echo "-s <scan-build-py prefix> : path to the directory bin/scan-build"
131  echo "-o <output dir>           : path to output dir (default: AnalysisResult)"
132  echo "-m <clang|zircon|all>    : run mode. clang mode will only enable checkers that"
133  echo "                            is enabled by default. zircon mode will only enable"
134  echo "                            zircon related checkers. all mode will enable both"
135  echo "                            default checkers and zircon related checkers."
136  echo "                            Default value is clang."
137  echo "-t <target>               : build target. Default is x86"
138  echo "-n                        : do not run analyzer on unit tests"
139  echo "-h                        : for help"
140  exit 1
141}
142
143SOURCE="${BASH_SOURCE[0]}"
144while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
145  SCRIPT_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
146  SOURCE="$(readlink "$SOURCE")"
147  # if $SOURCE was a relative symlink, we need to resolve it relative to the
148  # path where the symlink file was located
149  [[ "$SOURCE" != /* ]] && SOURCE="$SCRIPT_DIR/$SOURCE"
150done
151SCRIPT_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
152ZIRCON_ROOT="$SCRIPT_DIR/.."
153
154# Path to output analysis results
155OUT_DIR="$ZIRCON_ROOT/AnalysisResult"
156
157# Read args from command line
158while getopts "p:s:t:o:m:hn" opt; do
159  case $opt in
160    p) TOOLCHAIN_PREFIX="$OPTARG";;
161    s) SCAN_BUILD_PY_PREFIX="$OPTARG";;
162    t) BUILD_TARGET="$OPTARG";;
163    o) OUT_DIR="$OPTARG";;
164    m) RUN_MODE="${OPTARG,,}";;
165    h) func_help;;
166    n) DISABLE_UTEST=true;;
167    \?)
168      echo "Inavlid option"
169      func_help
170  esac
171done
172
173# Determine the clang prefix
174if [ -z "$TOOLCHAIN_PREFIX" ]; then
175  # User did not provide toolchain prefix
176  # Assume user prefer prebuilt toolchain
177  PREBUILT_DIR="$ZIRCON_ROOT/prebuilt/downloads"
178  # Determine OS type
179  OS_STR="$(uname)"
180  if [ "$OS_STR" = "Darwin" ]; then
181    PREBUILT_DIR="$PREBUILT_DIR/clang+llvm-x86_64-darwin"
182  elif [ "$OS_STR" = "Linux" ]; then
183    PREBUILT_DIR="$PREBUILT_DIR/clang+llvm-x86_64-linux"
184  fi
185  if [ ! -d "$PREBUILT_DIR" ]; then
186    echo "Toolchain prefix is not defined and prebuilt toolchain has not yet been downloaded."
187    echo "Abort!"
188    exit -1
189  fi
190  TOOLCHAIN_PREFIX="$PREBUILT_DIR"
191fi
192
193CLANG_PATH="$TOOLCHAIN_PREFIX/bin/clang"
194CLANGXX_PATH="$TOOLCHAIN_PREFIX/bin/clang++"
195
196# Check if clang exists
197func_test_exist "$CLANG_PATH"
198func_test_exist "$CLANGXX_PATH"
199
200# Looking for scan-build-py
201# Prebuild does not have scan-build-py
202if [ -z "$SCAN_BUILD_PY_PREFIX" ]; then
203  # SCAN_BUILD_PY_PREFIX not defined
204  # Try fuchsia/third_party
205  SCAN_BUILD_PY_PREFIX="$ZIRCON_ROOT/../third_party/llvm/tools/clang/tools/scan-build-py"
206fi
207
208if [ ! -d "$SCAN_BUILD_PY_PREFIX" ]; then
209  echo "scan-build-py is not found at $SCAN_BUILD_PY_PREFIX, Aborting!"
210  exit -1
211fi
212
213SCAN_BUILD_PY="$SCAN_BUILD_PY_PREFIX/bin/scan-build"
214SCAN_BUILD_PY_CC="$SCAN_BUILD_PY_PREFIX/bin/analyze-cc"
215SCAN_BUILD_PY_CXX="$SCAN_BUILD_PY_PREFIX/bin/analyze-c++"
216
217# Test if scan-build exists
218func_test_exist "$SCAN_BUILD_PY"
219func_test_exist "$SCAN_BUILD_PY_CC"
220func_test_exist "$SCAN_BUILD_PY_CXX"
221
222# Construct Checker Args that should be passed to scan-build
223if [ "$RUN_MODE" = "zircon" ]; then
224  CHECKERS_TO_DISABLE="${CHECKERS_TO_DISABLE} ${CHECKERS_DEFAULT_ON}"
225  CHECKERS_TO_ENABLE="${CHECKERS_TO_ENABLE} ${CHECKERS_ZIRCON}"
226elif [ "$RUN_MODE" = "all" ]; then
227  CHECKERS_TO_ENABLE="${CHECKERS_TO_ENABLE} ${CHECKERS_ZIRCON}"
228fi
229func_disable_checkers
230func_enable_checkers
231
232# All clear, perform analysis
233# Change dir to zircon
234cd "$ZIRCON_ROOT"
235# Run scan-build on make
236if [ ! -z "${BUILD_TARGET}" ]; then
237  make USE_CLANG=true "${BUILD_TARGET}" clean &> /dev/null
238else
239  make USE_CLANG=true clean &> /dev/null
240fi
241
242CMDL="${SCAN_BUILD_PY}"
243CMDL="${CMDL} --use-cc ${CLANG_PATH}"
244CMDL="${CMDL} --use-c++ ${CLANGXX_PATH}"
245CMDL="${CMDL} --use-analyzer ${CLANGXX_PATH}"
246CMDL="${CMDL} -o ${OUT_DIR}"
247CMDL="${CMDL} ${CHECKERS}"
248CMDL="${CMDL} make USE_CLANG=true"
249if [ "${DISABLE_UTEST}" = true ]; then
250  CMDL="$CMDL DISABLE_UTEST=true"
251fi
252CMDL="$CMDL CC=${SCAN_BUILD_PY_CC}"
253CMDL="$CMDL CXX=${SCAN_BUILD_PY_CXX}"
254CMDL="$CMDL -j32"
255if [ ! -z "${BUILD_TARGET}" ]; then
256  CMDL="$CMDL ${BUILD_TARGET}"
257fi
258
259# Execute
260$CMDL
261
262exit 0;
263