Svace is an automatic defect detection tool for source code written in C, C++, Java, Go, Kotlin and C# languages. The detection algorithms are based on static analysis. This software is copyrighted by Ivannikov Institute for System Programming of the Russian Academy of Sciences (ISP RAS).
Svace is a continuously evolving product based on long term research. It uses a number of unique state-of-the-art analysis technologies and adopts the front-end of several open compilers to support the latest language standards. Svace provides ease of integration with any build systems and allows to accurately capture the exact code being built and the details of its compilation. Svace analysis engine covers all program execution paths and considers function calls in particular. The analysis is fully parallel and scalable up to projects with tens of millions lines of code. Several third-party analyzers are integrated into Svace to widen the scope of detected issues. Analysis results are represented through detailed reports, where the traces provided for each reported issue support code navigation.
This User Guide includes the following main sections:
Typically, analyzing source code with Svace involves four stages:
Svace includes internal compilers to generate intermediate representation for analysis. For C/C++ Svace uses a modified version of Clang to produce the LLVM intermediate representation (bitcode) files. For Java, javac compiler from OpenJDK is used to produce bytecode. For C#, Roslyn is used.
The analysis tool uses the intermediate files and other data (including source code) produced and stored at the build phase to generate analysis result files (in various formats including plain text, XML and JSON).
History of previous analysis runs allows one to indicate (for a new run) which issues were new to this run and which issues detected on this run were previously marked as incorrectly detected (false positives).
Svace is released in 3 separate distributions:
svace build
requires disabling System
Integrity Protection).The analyzer has no external dependencies and may be used right after unpacking.
This section describes how to perform analysis through command line interface, explaining the process of analysis in detail.
Svace stores all the data related to a project being analyzed in a special project folder. This folder can be created using a svace init tool.
Svace provides the svace build tool to wrap a normal building command. This tool intercepts invocations of compilers supported by Svace (C/C++: GCC, ARMCC, Clang, icc, MSVC and multiple others; Java: OpenJDK, Oracle JDK and ECJ (including their usage via API)) and transparently runs Svace internal compilers along with the original compiler command.
For SBS make sure that sbs
is in PATH
. The
following command should now work from a source package directory:
svace build sbs -b
Similar procedure is for building a Tizen package using GBS. For
example, to build a libxml2
package:
svace build gbs build -A armv7l --binary-list libxml2
Use svace analyze command line tool.
A Svace distribution can additionally include Clang Static Analyzer and SpotBugs. Also, several lightweight defect detectors are implemented in Svace javac compiler. By default, all those tools are run before the main analysis, using intermediate data produced during the build phase as input. Defect reports produced by those tools are included into the final analysis results.
Svace supports configuration options that may affect the analysis time. Default options should be suitable for most scenarios, but it is possible to tune several parameters manually. By using the [#svaceconfig config] tool it is possible to change them:
svace config THREAD_NUMBER 4
In the example above the THREAD_NUMBER configuration variable sets
the number of parallel threads used for analysis as 4. By default, the
THREAD_NUMBER
option has value auto
and the
analyzer selects threads number depending on current processor loading.
Value max
sets threads number equal to the processor
kernels. And you may use any positive integer number. Usually analysis
is faster if THREAD_NUMBER
is max
. But every
thread spend some amount of memory and more threads will spend more
memory. So on machines with low memory it is better to reduce threads
number.
When the svace history tool is used after an analysis, analysis results are added to history storage. History storage for a project is located in its Svace directory. A history storage contains the list of known warnings found in the project, and snapshots of the relevant source code.
History storage allows to recognize some issues detected on a new analysis run as instances of previously detected issues (even if the source code and line numbers changed between the analyses). If an issue was marked as a ‘false positive’ after some analysis run, it will remain marked so in new analysis results.
Analysis results may be viewed through a web-browser using a builtin server (see svace server) or a standalone Svacer history server, or opened with an editor.
Each issue detected by Svace is associated with the following information:
The analysis results are presented as a tree. The top level lists warnings by type, the second level lists the warnings themselves.
Each warning record shows location in the source code and warning
message that may include names of the relevant expressions. For each
warning, subnodes in the tree may indicate multiple source locations
playing different roles in the warning (for example, for DEREF_AFTER_NULL, one location shows where
the pointer is dereferenced, and the other location shows where it was
compared to NULL
). Together they form a warning trace that
usually helps to understand the program flow from the defect occurrence
to its manifestation point, or other useful information related to the
warning.
When using a web-server warning status can be set by selecting appropriate value from the drop-down list, and is indicated by the color of the issue. There are the following options:
The command line tools can be accessed through a wrapper tool
svace
located in svace/bin/
(it’s recommended
to add this directory to PATH
, so that svace
becomes accessible as a command). Command line options for
svace
are as follows:
usage: svace <command> [args]
svace --version [--quiet | --verbose]
svace [--help]
This is the command-line interface for Svace static analysis tool.
Type 'svace help <command>' to get help on a specific command.
optional arguments:
--help Show this help message and exit.
-V, --version Show version information and exit. If used together
with '--quiet', only version number in X.Y.Z format is
printed. If used together with '--verbose', source
code revision is printed too.
-v, --verbose Increase output verbosity.
-q, --quiet Reduce output verbosity.
commands:
main:
analyze Run Svace static analyzer to search for defects in
source code, given the data gathered during 'svace
build'.
build Capture invocations of supported compilers performed
by a given command and generate intermediate data for
a subsequent analysis.
config Manage general configuration settings.
init Create a Svace project directory.
warning Control which warning types are enabled during
analysis.
experimental:
add Add new sources to current build.
admin Manage object pool in a project directory.
clean Remove old annotations.
context-spec Manage context in which particular function will be
analyzed.
del Delete given sources from project.
export-build Export a build object into an external directory.
fa-diff Compare warning results for fast analysis mode for
oldand new svace distributives.
history Interact with a warning database.
merge-build Merge multiple build objects into a single build
object.
nhistory Interact with a new warning database.
server Configure or run Svace server.
show Show results in a Svace server (localhost:8060).
spec Manage user-provided specifications for third-party
library functions.
miscellaneous:
compare-details Compare details for 2 svres files.
compare-svres Compare warnings from two .svres files ignoring
checker names and generate comparison stats.
convert-svres Convert analysis results files into newer or older
format versions.
csa-stat Display Clang Static Analyzer checker statistics
summary.
diff-svres Show diff for 2 svres files.
filter-svres Extract the issues enabled in given warning settings
file from a given .svres.
ignore-test Check svace.ignore filter.
import-sqlite Convert svace sqlite format db to svres.
match-svres Match two svres files and show identifiers of the
matched warnings from the first svres file.
merge-svres Merge multiple .svres files with analysis results for
different projects into a single file.
mti Calculate message template id from warning messages
taken from svres files.
mti-csharp Calculate message template id from warning messages
taken from svres files.
quality-report Print report about warnings quality for reviewed svres
files(s).
review-rebase Rebase warning review from one svres file to other.
split-svres Split a multi-project .svres file into separate files
with analysis results for individual projects.
suppress Run stand-alone suppression tool.
svres2json Converter from Svace results format to JSON format and
vice versa.
svres2sarif Converter from Svace results format to Sarif 2.1
format.
warning-selector Pick particular warnings and write them to an svres
file.
for debug and development:
cgoc Tool to compare call graph logs.
cgop Tool to calculate sub-call graph order.
usage: svace init [options] [DIR]
Create an empty Svace project directory or reinitialize an existing one. By
default, this command creates a '.svace-dir' subdirectory in DIR (but see
--bare). If DIR is not specified, the value of SVACE_DIR environment variable
(or, if unset, the current directory) is used instead. If the project
directory already exists, this command can reinitialize its permissions
according to the options without changing the stored data. Note that only
permissions of the directory itself can be changed. Permissions of
subdirectories should be changed manually if desired.
optional arguments:
--bare Initialize the specified directory as a Svace project directory
instead of creating '.svace-dir' subdirectory in it.
--shared Make the project directory group-writable and set the set-
group-id bit on it if needed. This allows users belonging to
the current user's primary group to share this project
directory.
-q, --quiet Print only error messages (to stderr).
--help Show this help message and exit.
This tool launches a specified custom build command, intercepts invocations of supported compilers and uses Svace internal compilers to produce intermediate representation of the same source code that is being built by the given command.
Command line options are as follows:
usage: svace build [options] <build-command>
svace build [options] --update [TARGET...]
Run <build-command> and capture all invocations of supported compilers during
its execution. Create intermediate representation files using information from
the intercepted compiler invocations.
<build-command> can include options and arguments. To capture multiple
commands, wrap the commands in a single script.
Typical usage:
svace build make -j4 all
svace build gradle --no-daemon build
positional arguments:
build-command
optional arguments:
-h, --help show this help message and exit
--svace-dir SVACE_DIR
Specify a Svace directory, overriding current
directory and SVACE_DIR environment variable.
--init Initialize the selected Svace project directory before
doing anything else. This is a shortcut for avoiding
manual 'svace init' if all you need is its default
behavior.
--clear-build-dir Remove redundant contents of the build directory after
build.
--enable-language LANG, --disable-language LANG
Enable or disable compilation interception for the
specified programming language. Supported languages
are: all, cxx, go, kotlin. By default, all supported
languages are enabled.
-L LANGS, --languages LANGS
Enable compilation interception for languages in the
specified comma-separated list and disable it for
other languages. The special value 'all' enables all
supported languages (which is the default).
--debug Enable verbose logging useful for debugging and making
bug reports.
--enable-spotbugs, --disable-spotbugs
Run SpotBugs checkers for intercepted Java
compilations during the build phase. By default, this
feature is disabled.
--disable-java-agent Don't inject Java agent into intercepted JVM
processes. If this option is used, Java compilations
performed via compiler APIs (e.g., javac API) are not
intercepted. Since Svace Java agent requires Java 1.6,
this option must be used if build process involves
Java 1.5 or older.
--spotbugs-memory SPOTBUGS_MEMORY
Specify maximum size of Java heap (in MB) that
SpotBugs may use (default: 2048).
--kotlinc-memory KOTLINC_MEMORY
Specify maximum size of Java heap (in MB) that Kotlin
compiler may use (default: 2048).
--max-jobs MAX_JOBS The maximum number of processes that Svace can use to
handle intercepted compilation commands. For full
builds, it is supported only on Windows. If combined
with --update, it is supported on all platforms. The
default value is the number of logical processors on
the system.
--captured-nothing-status STATUS
If the build command completed successfully and no
serious Svace failures were detected, but no
compilations were successfully captured, exit with the
specified status (255 by default).
--low-ready-units-ratio PERCENTAGE
If the ratio of ready units to all units (as shown
with --verbose) is less than the specified integer
percentage for any programming language with at least
one intercepted compilation, print a warning and exit
with the status specified with --low-ready-units-
status option (but still generate all data necessary
for analysis). This option doesn't apply if no units
were captured at all (see --captured-nothing-status).
The default value is 0 (i.e. no capture ratio is
considered low).
--low-ready-units-status STATUS
Set the exit status for --low-ready-units-ratio option
(254 by default).
--verbose Report more detailed statistics after build.
experimental options:
-t TARGET, --target TARGET
Specify the target platform that Clang should use for
bitcode generation. Valid values are: auto, aarch64,
arm, hexagon, mips, mips64, powerpc, powerpc64,
riscv32, riscv64, sparc, sparc64, x64, x86. Values
other than 'auto' override automatic target detection.
This option is intended for debugging only.
--enable-ptrace Use ptrace-based interception for statically-linked
executables. By default, Svace uses LD_PRELOAD-based
interception, which works only for dynamically linked
executables. But sometimes compiler toolchains are
statically linked, or there are other statically
linked processes that break LD_PRELOAD-based
interception (for example, by removing LD_PRELOAD
environment variable). This option enables a hybrid
mode: Svace will start in LD_PRELOAD mode, but will
switch to ptrace-based mode each time it detects a
launch of a statically linked executable, and will
switch back if that executable runs a dynamically
linked executable.
--enable-ptrace-all Use ptrace-based interception for all executables and
disable LD_PRELOAD-based interception. See --enable-
ptrace for more details.
--prepend-preload-lib
By default, the Svace library for function call
interception is appended to LD_PRELOAD environment
variable, ensuring that necessary environment
variables are propagated across the process tree even
if other LD_PRELOAD-libraries are used in the course
of the build process. However, if such libraries
modify arguments of created processes in a way that
makes them unrecognizable to the Svace code that
detects "interesting" processes, the Svace library
should be prepended instead, ensuring that it sees the
same arguments that were passed by the application. In
particular, this is needed for Scratchbox environment.
--disable-comp Disable all compiler addons
--disable-misc Disable all non-compiler addons
--clang-opts CLANG_OPTS
Add specified options to the Clang and Clang Static
Analyzer run by Svace. The options should be separated
by ';'.
--spotbugs-opts SPOTBUGS_OPTS
Add specified options to the SpotBugs run by Svace.
The options should be separated by ';'.
--hash-ar-content Calculate archive hash based only on names of files in
the archive, excluding timestamp, UID, GID and
content. Use in case of undeterministic mode of ar or
ranlib and in case of prebuilt archives with prebuilt
object files (that may differ because of another
platform etc.). DO NOT USE in case of rebuilding the
same archive more than once in the same build (e.g.
debug and release mode).
--enable-huge-source-workaround
Clang can't handle translation units larger than 2 GB
after preprocessing because it uses signed 32-bit
integer type internally for identifying source
locations. Sometimes such huge TUs occur in practice
because the same autogenerated header file is included
multiple times without guards. In this case source
locations are allocated for each inclusion, which may
cause an overflow resulting in a hang or crash. This
option makes it so source locations are allocated only
once per header file, which may help avoid the
overflow in this case. This option is incompatible
with '--enable-csa-deps'.
--chroot-interception-failure-mode {fail,warn,ignore}
Specify how to react if Svace fails to set up
interception upon encountering a transition into a
chroot jail. If set to 'fail', terminate the affected
process immediately and report a fatal error.
Otherwise, disable interception for the affected
process and its descendants and, if set to 'warn',
also report a warning when 'svace build' completes.
This may be useful in cases when chroot() is used for
purposes other than running compilations in a chroot
jail, making interception failure irrelevant. The
default value is 'fail'.
deprecated options (may be removed in a future release):
--clear-bitcode-dir An alias for --clear-build-dir.
Environment variables:
SVACE_DIR Overrides the default value for --svace-dir
SVACE_ENABLE_CSA_STAT
Overrides the default value for --enable-csa-stat.
Must be set to 'yes' or 'no'.
Usage: svace config [OPTIONS] [<key> [<value>]]
svace config [OPTIONS] --unset <key>
Manage configuration settings. In the first variant (without --unset), if
only <key> is specified, get current value assigned to that key (--parents
must be specified to take system-wide and user-specific configuration into
account). If both <key> and <value> are specified, set <key> to <value>. For
boolean keys, possible values are 'true' and 'false'. Some keys that control
internal (development) settings are 'hidden' and don't show by default, but
can be listed with --show-hidden.
Common config options (shared by 'warning' and 'config' tools):
--global : Work with user-specific configuration
~/.svace/<config-file>, rather than the local
configuration $SVACE_DIR/<config-file>
--system : Work with system-wide configuration
/path/to/distro/config/<config-file>, rather than the
local configuration $SVACE_DIR/<config-file>
-f, --file FILE : Use specified config file instead of the one implied by
options --global, --system, or current svace dir.
If FILE is '-', read from stdin and write to stdout
(implies --quiet).
--unset : Remove the entry for specified key from config file (so
that the default value or value specified in parent
config files will be used instead).
--unset-all : Clear the config file.
-l, --list : List the entries specified in config file(s).
-a, --all : List entries for all keys, including those not specified
in config files. Don't list hidden keys, unless
--show-hidden is specified. Implies --parents.
--show-hidden : When listing keys with --all or --list, show hidden keys
as well.
-i, --info : For each listed key, print a short description.
-p, --parents : When reading from config file, take into account the
settings specified in parent files. By default, apply
system-wide, user-specific and then Svace directory
or explicitly specified settings to form the list of
modified settings and their values.
If --global is specified, apply only system-wide and
user-specific settings; if --system is specified,
apply only system-wide settings.
--svace-dir PATH : Specify a Svace directory, overriding current directory
and $SVACE_DIR environment variable.
-q, --quiet : Only print error messages (to stderr) and specifically
requested output (to stdout).
--get-val : Only print the value of specified key (without 'key = ').
Usage: svace warning [OPTIONS] [<warn-type> [<state>]]
svace warning [OPTIONS] --unset <warn-type>
Manage warning settings (which warning types are enabled/disabled). In the
first variant (without --unset), if only <warn-type> (key) is specified, get
current state (value) of that warning type (--parents must be specified to
take system-wide and user-specific configuration into account). If both
<warn-type> and <state> are specified, set <warn-type> to <state>. Possible
values for <state> are 'true', 'false' and 'hidden' (disables a warning type
and hides it from output of '--all').
Special warning name 'all' can be used to enable/disable all warning types
at once, except for warning types hidden by default.
Use special warning name 'hidden' to enable/disable warning types hidden by
default.
Common config options (shared by 'warning' and 'config' tools):
--global : Work with user-specific configuration
~/.svace/<config-file>, rather than the local
configuration $SVACE_DIR/<config-file>
--system : Work with system-wide configuration
/path/to/distro/config/<config-file>, rather than the
local configuration $SVACE_DIR/<config-file>
-f, --file FILE : Use specified config file instead of the one implied by
options --global, --system, or current svace dir.
If FILE is '-', read from stdin and write to stdout
(implies --quiet).
--unset : Remove the entry for specified key from config file (so
that the default value or value specified in parent
config files will be used instead).
--unset-all : Clear the config file.
-l, --list : List the entries specified in config file(s).
-a, --all : List entries for all keys, including those not specified
in config files. Don't list hidden keys, unless
--show-hidden is specified. Implies --parents.
--show-hidden : When listing keys with --all or --list, show hidden keys
as well.
-i, --info : For each listed key, print a short description.
-p, --parents : When reading from config file, take into account the
settings specified in parent files. By default, apply
system-wide, user-specific and then Svace directory
or explicitly specified settings to form the list of
modified settings and their values.
If --global is specified, apply only system-wide and
user-specific settings; if --system is specified,
apply only system-wide settings.
--svace-dir PATH : Specify a Svace directory, overriding current directory
and $SVACE_DIR environment variable.
-q, --quiet : Only print error messages (to stderr) and specifically
requested output (to stdout).
--get-val : Only print the value of specified key (without 'key = ').
Warning tool specific options:
--preset NAME : Work with specific preset of options
Warning type properties (displayed when option --info is given;
see User Guide for more details):
Situation : What kind of situation is indicated by the issue.
Possible values:
Quality
Security
CodingStyle
Suppressed
Duplicate
Severity : Potential danger represented by the issue, if it's correctly
detected. Possible values:
Critical
Major
Normal
Minor
Undefined
Reliability: How often the issues of this type are detected correctly
(in different projects). Possible values:
VeryHigh
High
Average
Low
VeryLow
Unknown
Language : For which languages warnings of this type are detected.
Possible values:
CXX
JAVA
KOTLIN
CSHARP
GO
NONE
Detection tools: Where the checker for the issue is implemented.
Possible values:
SvEng
SvaceCppSpecific
CSA
SpotBugs
Goa
TagsJavac
Roslyn
Kotlinc
Group : A group which this warning type belongs to.
Examples:
# Get the state of warning type DEREF_AFTER_NULL, taking parent configuration
# into account:
svace warning -p DEREF_AFTER_NULL
# Disable DEREF_AFTER_NULL for all analysis invocations by current user:
svace warning --global DEREF_AFTER_NULL false
# Disable DEREF_AFTER_NULL for java language:
svace warning JAVA.DEREF_AFTER_NULL false
# Disable all warning for language CSHARP:
svace warning CSHARP false
# Disable autofixes for all warnings (but do not change warnings themselfs):
svace warning AUTOFIX false
# List all critical warnings:
svace warning CRITICAL
# List all warnings with autofixes:
svace warning AUTOFIX
# List all warnings of buffer overflow group:
svace warning TAINTED_SECTION
or
svace warning TAINTED
# List all warnings by wildcard (only * is allowed):
svace warning DEREF_*_NULL*
# List all warning settings specified for current user, including a short
# description for each warning type:
svace warning -lpi --global
# Dump all information about the configuration that would be used during
# analysis:
svace warning -lap --show-hidden
This is the main analysis tool. It takes as input the data produced
by svace build
and generates
analysis result files. Command line options are as follows:
usage: svace analyze [options]
Analyze the selected project directory with Svace.
By default, a project directory based on the current working directory
is selected, and analysis is performed for the last build object produced by
'svace build', which is specified in file <project-dir>/bitcode/build-object.
Use '--svace-dir' to specify a different project directory and '--build'
to specify a different build object.
optional arguments:
-h, --help show this help message and exit
--svace-dir DIR Specify a Svace directory, overriding current
directory and SVACE_DIR environment variable.
-b HASH, --build HASH
A build object to be analyzed. By default, the last
build object produced by 'svace build' is used.
-n NAME, --name NAME Associate given name with the analyzed project (used
for naming files with results and some other files).
By default, the name of the project directory is used.
--memory NUM_MB Specify maximum amount of RAM (in MB) that Svace may
use for analysis. More precisely, this option controls
the maximum size of JVM heap. If set to 'auto' (the
default), Svace attempts to detect the maximum usable
JVM heap size at the time of analysis start and uses
the value close to the detected maximum.
--enable-language LANG, --disable-language LANG
Enable or disable analysis for the specified
programming language. Supported languages are: all,
cxx, go, kotlin. Enabling a language also enables
working with the relevant tools (e.g., enabling C/C++
turns on analysis with Clang Static Analyzer). Default
values set by this option are overridden by
--import-<tool>, --skip-import-<tool> and
--skip-<lang_or_tool>-analysis options. By default,
analysis of all supported languages is enabled.
-L LANGS, --languages LANGS
Enable analysis for languages in the specified comma-
separated list and disable it for other languages. The
special value 'all' enables all supported languages
(which is the default).
--target TARGET Select the target architecture of C/C++ code to
analyze. If it's set to 'all' (the default), each
target is analyzed in turn independently of other
targets. Valid values are: all, aarch64, arm, hexagon,
mips, mips64, powerpc, powerpc64, riscv32, riscv64,
sparc, sparc64, x64, x86.
--preset NAME Use the requested warnings preset during the analysis.
--server Enable server analysis mode. In this mode computed
function annotations and possibly other information
will be saved to disk to be used by the annotation
server for supporting the client fast analysis mode.
--client Enable fast analysis mode on the client desktop. In
this mode, the data required for the unknown functions
analysis will be queried from the annotation server.
--import-csa, --skip-import-csa
Import Clang Static Analyzer results generated at the
build phase. Has no effect if CSA is run at the
analysis phase (see --skip-csa-analysis).
--import-spotbugs, --skip-import-spotbugs
Import analysis results of SpotBugs generated at the
build phase. Has no effect if SpotBugs is run at the
analysis phase (see --skip-spotbugs-analysis).
--import-kotlinc, --skip-import-kotlinc
Import analysis results of Kotlin compiler generated
at the build phase. Has no effect if kotlinc is run at
the analysis phase (see --skip-kotlinc-analysis (not
implemented yet)).
--import-goa, --skip-import-goa
Import analysis results of Goa generated at the build
phase. Has no effect if Goa is run at the analysis
phase (see --run-goa-analysis).
--skip-c-analysis Don't analyze C/C++ code with Svace engine. Note that
Clang Static Analyzer is not affected by this option.
--skip-sveng-analysis
Don't run Svace engine analysis.
--skip-csa-analysis Don't analyze C/C++ code with Clang Static Analyzer.
You may still see CSA results if it was enabled at the
build phase and its results were imported.
--skip-spotbugs-analysis
Don't analyze Java code with SpotBugs. You may still
see SpotBugs results if it was enabled at the build
phase and its results were imported.
--run-goa-analysis Analyze Golang code with Goa.
--csa-opts OPTS Add specified options to the Clang Static Analyzer run
by Svace. The options should be separated by
semicolons (';'). To specify a literal semicolon,
prepend a backslash ('\') to it. To specify a sequence
of literal backslashes before a literal semicolon,
repeat each of the literal backslashes twice.
--set-config KEY=VALUE
Set the value of config option KEY to VALUE for this
analysis run only. Takes precedence over config files.
--set-warn WARN=(true|false)
Enable or disable warning type WARN for this analysis
run only. Takes precedence over config files.
-w FILE, --warnings FILE
Load warning settings (information about
enabled/disabled warning types) from FILE instead of
warn-settings.txt from Svace project directory. Note
that defaults from system and global settings are
still applied as usual.
-q, --quiet Show error messages only.
-v, --verbose Show detailed information about analysis and its
status.
--log-level LOG_LEVEL
Log level. May have values: quiet, brief, default and
verbose.
--version Show Svace engine version information and exit. If
used together with '--quiet', only version number in
X.Y.Z format is printed. The Svace engine version is
normally the same as the version of the Svace
distribution as shown by 'svace --version'.
experimental options:
-t HASH, --task HASH A task object to be analyzed.
--raw-files Analyze files with intermediate representation in the
build directory ($SVACE_DIR/bitcode by default)
instead of analyzing a build object. This option may
be useful for debugging, but normally shouldn't be
used.
--with-cache Use cache for analysis intermediate results. Enabling
cache may increased analysis speed of modified
project. Require additional disk space.
--analyze-storage-dir SVACE_ANALYZE_STORAGE_DIR
Specify a folder for storing analysis data that are
shared between analysis. Analysis with cache, fast
analysis and incremental analyses use this folder. By
default analysis uses folder $SVACE_DIR/analyze-
storage.
--incremental Enable incremental analysis mode. It implents the same
behavior as a --client option. However, the results of
analysis is almost the same as for full analysis.
Demand a project to have been analyzed with server
option yet.
--build-dir DIR Specify a build directory produced by 'svace build' to
use for analysis. The default is $SVACE_DIR/bitcode.
Valid only together with --raw-files.
-s DIR, --source DIR Search for source code in DIR. Project directory is
used by default (as specified, even if the actual
Svace directory is its subdirectory .svace-dir). Valid
only together with --raw-files.
-o DIR, --output DIR Save analysis results in DIR. By default, the results
are saved in $SVACE_DIR/analyze-res. The analysis
result files are $PRJ_NAME.svres and $PRJ_NAME.txt,
where $PRJ_NAME is the name of the analyzed project.
Valid only together with --raw-files.
--fix Run autofix during analysis.
--autofix-backend {svace}
Autofix backend to use. The default is 'svace'.
--emit-autofix-input Emit autofix input as a separate file (for debug
purposes).
svace history list [--svace-dir <project-dir>] [--branch <branch>]
[--class <warning type>] [--status <warning status>]
svace history update [--svace-dir <project-dir>] [--branch <branch>]
--warning <warning ID> [--user <user name>]
[-status <warning status>] [--comment <comment>]
svace history import [--svace-dir <project-dir>] [--branch <branch>]
(--task <task ID> | --build <build ID>)
--results <results ID> [--name <snapshot name>]
[--user <user name>]
svace history branch (list |
clone --target <branch name> [--source <branch name>] |
delete --branch <branch name> [--force] |
sync <branch name> <branch name>)
[--svace-dir <project-dir>] [--branch <branch>]
svace history merge-svres svres-id...
svace history split-build build-id
Interact with a warning database. Allows to import new analysis results,
list analysis results and modify warning states from the command line.
Common options:
--svace-dir PATH : Svace project directory to work with.
If not set, current working directory will be used.
--branch NAME : History branch to work with.
If not set, default ('master') branch will be used.
Operations and their options:
list : Show list of actual warnings in a history branch.
These warnings can be filtered.
--class NAME : Display only warnings of selected class.
Can be used several times, showing multiple warning
classes. If not set, all warnings are shown.
--status STATUS : Display only warnings having selected status.
Can be used several times, showing warnings of
multiple statuses. If not set, all warnings are shown.
--format FORMAT : Format output for each warning according to
a format string. Supported format specifiers:
%h: Warning ID (hash)
%c: Warning class
%s: Warning status
%m: Warning message
%l: Warning location
%u: User comment
%A: User-defined attributes
--current-snapshot VAL : If VAL is false, display only warnings that
didn't appear on the current (last) snapshot
(but were detected on one of the old snapshots).
If it's true, display only warnings detected on the
current snapshot (this is the default).
--previous-snapshot VAL : If VAL is false, display only warnings that
didn't appear on the previous snapshot
(but were detected either on one of the old snapshots,
or on the current snapshot).
If it's true, display only warnings detected on the
previous snapshot.
update : Modify a warning in the history storage.
--user NAME : Username related to this warning modification.
If not set, current user name will be used.
--warning ID : The 32-symbol ID for warning which must be updated.
--status STATUS : New status for the selected warning.
May be one of 'bug', 'scope', 'goal', 'unclear' or
'default' (case-insensitive).
Other statuses are interpreted as 'default'.
--comment STRING : New comment string for the selected warning.
import : Import analysis results into history storage.
When neither build/task nor results object are specified,
import the last analysis results.
--user NAME : Username related to this warning modification.
If not set, current user name will be used.
--results ID : The 32-symbol ID for the imported analysis results.
--build ID : The 32-symbol ID for the build which was analyzed
to produce imported analysis results.
--task ID : The 32-symbol ID for the task which was analyzed
to produce imported analysis results.
--name NAME : Snapshot name related to these analysis results.
import-data : Internal tool mostly for debugging.
Allows to import analysis results and source code snapshots,
into svace storage system.
--type : 'svres', 'build', 'source-build' or 'empty-build'.
'svres' allows you to import analysis results in svres format,
'empty-build' produces build-object with no data in it,
'build' imports build object previously exported using
Svace 'export-build' command, 'source-build' allows you to import
some directory as a source code snapshot. The resulting build-object
will obviously contain no bitcode, so it won't be analyzable;
however it can be useful for importing data into history system.
--path : path to file (if importing 'svres') or directory (if importing
a source code snapshot or build object) you want to import.
--orig-path : used for 'source-build' only, and is optional. Indicates
the path where the imported source tree should be available at.
E.g.: you have file /home/user/project/file.c, you use:
--path /home/user/project/, --orig-path /var/build
the file will be imported and used by history system
like it was located at /var/build/file.c
If you are under *nix OS and you want to emulate build-object
generated by windows system, change orig-path this way:
'C:\Some\Dir\' => '/c_/Some/Dir/', and also check next flag.
--with-dxr : used for 'source-build' only, and is optional. Allows to provide
a directory with source code markup in csv format. Svace requires
file names in the same format svace generates dxr files itself. Also
Svace requires empty .csv.stamp files along with .csv data files.
--merge-metrics : used for 'build' only, and is optional. If set to 'true' merges
imported build object metrics with the current ones.
--move : used for 'build' only, and is optional. If set to 'true' moves
imported files instead of copying them. This allows to speed up
the import but the export folder will be cleared after this.
branch : Manage branches. Must be followed by one of
the following sub-commands:
list : List all branches in the project directory
(including remote branches).
clone : Create a new branch and optionally makes its state
equal to another branch.
--target NAME : Branch name to be created.
--source NAME : Branch to be cloned.
If not set, created branch will have no content.
delete : Delete specified branch.
--branch NAME : Name of the branch to be deleted.
force : Allows to delete 'master' branch.
(svace history branch delete force --branch master)
sync NAME NAME : Sync warning states from two given branches.
merge-svres : Takes multiple analysis results and merges them all
in one large analysis results.
split-build : Takes build object and splits it into multiple build objects
where each resulting build object has only some target-related data.
Examples:
# List warnings of class MEMORY_LEAK with Goal and Default statuses:
svace history list --class MEMORY_LEAK --status Default --status Goal
# List warnings of class MEMORY_LEAK that disappeared on the last snapshot:
svace history list --class MEMORY_LEAK --current-snapshot false \
--previous-snapshot true
# Change status of a selected warning:
svace history update --user developer1 --warning 123..132 --status Bug \
--comment 'Array index isn't checked before access'
# Import existing analysis results into history database:
svace history import --user developer1 --build 123..231 --results 323..321'
# Clone existing branch into a new one:
svace history branch clone --source master --target backports_2.0
usage: svace server [--server-dir DIR] <subcommand> [args]
optional arguments:
--server-dir DIR Specify a Svace server directory. By default, the current
directory is used.
--help Show this help message and exit.
subcommands:
subcommand
init Create a Svace server directory.
start Start the history access server.
single-start Start the history access server in single-project mode.
admin Manage a Svace server.
show-api-docs Display documentation for Svace history API.
Svace analysis has special mode which allow to reuse analysis results from previous run. It is intended for cases where project is regular analyzed by Svace.
Build program
svace init
svace build make
Analyze program with special parameter --use-cache
.
Svace will store results of intermediate operation to analysis
cache.
svace analyze --use-cache
Than program may be changed. Program should be rebuilded and analyzed:
make clean
svace build make
svace analyze --use-cache
Svace will used stored results from previos analysis run. If Svace already has results for intermediate operations than it will be used them. Analysis time should be increased.
Parameter --use-cache
must be used every time. If it
will be omitted than results from cache won’t be used.
Svace stores results to folder
.svace-dir/analyze-storage
. Analysis stores all results not
only results from previous run. Such mode may be efficitly used for
different projects branches. All result will be in cache.
For using analysis cache project must be build in the same folder.
Time increasing depends on project and changes. We expect that second analysis is in average 10 times faster. Storing results to cache may slightly decrease analysis time compared with regular analysis.
Cache may require sufficient disk space. In average it is comparable with the size of svace folder after build. After every analysis the cache size grows.
Folder .svace-dir/analyze-storage
where cache is placed
may be changed by analysis parameter --analyze-storage-dir
.
For example:
svace analyze --with-cache --analyze-storage-dir CACHE_DIR
The parameter change storage directory not only for analysis with cache but also for fast analysis.
We have operations which takes some times. It has input data and output data. Output data are fully defined by input data. Operation is fully independent from other parts of analyzer.
For input data we may quickly calculate hash. Output data are relatively small and may be serialized to disk.
Analysis ??does not know?? that it is run in special mode. All functions are performed the same way as with regular analysis except of dedicated operations. For such operation Svace creates hash for input data. Then Svace checks cache with for hash. If cache is not empty than operation is skipped and output data are loaded from cache. If cache is empty then operations is performed and output data are stored to cache.
Described scheme is very simple and reliable.
CSA analysis. It does not have inter module analysis and that`s why every module are analyzed independently. We recomend to use cache for this analysis.
Tags-javac analysis. It is also does not have inter module analysis. It is based on compilation unit (run of javac). We recomend to use it if build script run many compilation units. If only one compilation unit is used then cache will be obsolete after each source file modification.
Spotbugs analysis. It has similar limitation as Tags-javac analysis. We recommend to use it if build script run many compilation units.
Module parsing. Here input is a module (llvm bitcode file, Java/Kotlin bytecode file). Output is a set of loaded functions. The mode allows to skip module parsing. Moreover Svace uses module parsing twice inside every analysis: for preliminary phase and for main phase. In some cases uses of such parsing may increase even first analysis because modules will be parsed only on preliminary phase. It has drawback ??? required disk size. Output data here is all procedures of projects. In some cases it may be bigger than whole svace directory. We recommend to use it if large disk are used.
Function analysis in main phase. It is more complicated analysis which depends on many data: function code, call graph, summaries of called functions, results of preliminary phase. If some function is changed then all functions that are above at call graph must be reanalzyed. Svace analysis spends most of time for function analysis. Enabling cache for function analysis may significally increase analysis time. This analysis depends from preliminary phase. But now we do not have way to define that preliminary phase may change function analysis. We recommend to use it if some changes with regular analysis are acceptable.
There are two ways to enable analysis with cache. First way is to use option PERSISTENT_MODE:
svace config PERSISTENT_MODE true
After setting this option all futher analysis will be done with cache until this option will be disabled.
Second way is to use parameter –use-cache:
svace analyze --use-cache
This parameter temporary enable option PERSISTENT_MODE inside svace analyzer. Never configurations file will be changed. This option should be used every time when analysis is run. It is possible to enable/disable particular analysis by follow options:
PERSISTENT_MODE.CSA
??? for CSA analysis
PERSISTENT_MODE.TAGS_JAVAC
??? for tags-javac
analysis
PERSISTENT_MODE.SPOTBUGS
??? for spotbugs analysis
PERSISTENT_MODE.MODULE_PARSING
??? for parsing
module.
PERSISTENT_MODE.FUNC_ANALYSIS
??? for function
analysis.
Above options are enabled by default.
Also it is possible to disable logging by using option
PERSISTENT_MODE.LOGGING
.
Cache is stored at .svace-dir/analyze-storage
. Currect
implementations are based on file system. No data bases are used. Every
operations store data to its own folder:
.svace-dir/analyze-storage/csa
??? CSA analysis
.svace-dir/analyze-storage/tags-javac
??? tags-javac
analysis
.svace-dir/analyze-storage/spotbugs
??? Spotbugs
analysis
.svace-dir/analyze-storage/procedures
??? results of
procedure parsing
.svace-dir/analyze-storage/structures
??? part of
procedure parsing that store information about structures and classes
for C/C++ analysis
.svace-dir/analyze-storage/classes
??? part of procedure
parsing that store information about classes for C/C++ analysis
.svace-dir/analyze-storage/proc-analysis
??? result of
procedure analysis
Logs are put to
.svace-dir/analyze-res/logs/<proj>-persistent.log
If cache is enabled then Svace print to console:
Persistent mode is enabled. Some results may be got from cache.
Cache is ??flat??. It stores all information from different analysis to the same place. It may be used for analysing different branches of some project. Formally it may be used for analysing different projects but it is very unlikely that cache will be useful in this case.
The mode is based on file hashes. Even minor changes of 1 bit will lead to hash changes. Any changes lead to cache missing.
Project must be build in the same folder. Changing build folder lead to changing hashes.
Modification of C/C header files lead to changes all modules which are using those headers.
Compiler options also changes hashes.
Svace never clean folder .svace-dir/analyze-res
. For now
we do not know what clear mode is needed for users.
Svace settings are not taked into account. If you change option which changes analysis then cache will be incorrect. It is responsibility of user to clean the cache. It is implemented so because many settings are not changes results. For some options it will be desirable to use cache with slightly other results instead of spending more time for analysis.
Svace version are not checked. We do not recommend to use different Svace version with the same cache.
Cache may be ineffective for spotbugs and tags-javac analysis. The reason ??? compilation unit is used for hash creating. Many Java projects have only one compilation unit.
Analysis with cache may produce different results for main function analysis if they depends from preliminary phase. Preliminary phase collects data about global arrays and constants, performs pointer analysis. Now we can`t define what changes may affect analysis of separate function. If preliminary phase is disabled (USE_PRELIMINARY_PHASE is false) then results should be the same.
Cache does not check it integrity.
It allows to analyze project on remote server.
We have two machines remove-server and local-machine. On remove server we have to create svace directory in some folder:
mkdir server
cd server
svace init
Then run svace remote server:
svace server single-start
This command print to console:
Remote analysis server started at port 55135
Now we build project on local machine:
svace init
svace build make
If we would like to analyze project on local machine we run command
svace analyze
but now we want to analyze on server. For doung it we have to run follow command:
svace remote ???host remove-server analyze
After running it command Svace pass all needed data to remote server
and performs analysis there. Command remote
returns back
control when analysis on server finish. After it put results to svres
file on local machine to regular place:
.svace-dir/analyze-res/<proj>.svres
.
It is possible to review analysis progress via web browser by
reference: http://remote-server:8060/status
.
Remote analysis may be useful in follow cases:
Data are passed to server by network using port 55135. It may be
changed by modifing option SERVER_PORT in file
.svace-dir/conf/svace.conf
on remote machine. For changing
port on local machine parameter port
should be used:
svace remote ???host remove-server ???port 10000 analyze
Data are send efficiently. Sending data takes small time compared with analysis time. Sending is limited by network throughput.
Transferring is based on file hashes. Only those files that are needed are passed. If remote server already has such file it won`t be sending again. That is why transferring is especially efficient if project regular is built and analyzed on remote server.
Remote analysis server
It may performs several analysis. By default limit is 3. It is
possible to change such behavior by option
SIMULTANEOUS_ANALYSIS_LIMIT
in file
.svace-dir/conf/svace.conf
.
If limit is reached client will wait when some analysis will finish.
All analysis data except results (svres-file) are stored on server. Server put data to different folders inside svace-directory and never clear it.
This section demonstrates the process of using the tool with an example (analysis of proftpd-1.3.3).
Let’s assume that Svace is located in ~/svace/
, on an
Ubuntu system.
A copy of the proftpd-1.3.3 distributive can be downloaded from the
Internet at ftp://ftp.proftpd.org/distrib/source/proftpd-1.3.3.tar.bz2.
Extract the archive in ~/projects/proftpd-1.3.3/
.
The package can be built using configure/make. To obtain LLVM bitcode
files for analysis by svace, svace project folder must be initialized
first and then building commands need to be wrapped in svace build
command as follows:
cd ~/projects/proftpd-1.3.3
./configure
~/svace/bin/svace init
~/svace/bin/svace build make
If the operation completes successfully, a text like the following
will be displayed by build
:
Now Svace will preprocess captured data.
Processing source code markup data...
[**********************************************************************] 100%
Assembled build object: [BUILD]6715d4e4c908f8e62a54f174796282c48ce2d3a5
83 C/C++ units are ready.
As a result, the folder ~/projects/proftpd-1.3.3/.svace-dir/bitcode/ should now contain files with intermediate representation, for example the file
~/projects/proftpd-1.3.3/.svace-dir/bitcode/auth.e7d9dbb866bdbd6c2456bf8f3212d727.bc
Further analysis will only require files within Svace project folder (~/projects/proftpd-1.3.3/.svace-dir), so the other byproducts of the building process can be safely removed.
Assuming that folder ~/projects/proftpd-1.3.3 contains the svace project folder with intermediate representation files obtained on the previous step, analysis can be launched with the following command:
~/svace/bin/svace analyze --svace-dir ~/projects/proftpd-1.3.3
As a result (it should take a couple of minutes), two files will be created:
~/projects/proftpd-1.3.3/.svace-dir/analyze-res/proftpd-1.3.3.txt
~/projects/proftpd-1.3.3/.svace-dir/analyze-res/proftpd-1.3.3.svres
First file should contain (for example) the following warning of type DEREF_OF_NULL:
* DEREF_OF_NULL: Pointer 'symhold' that can have only NULL value (checked at line 152), is dereferenced at line 153.
dereference at mod_ls.c:153
null at mod_ls.c:152
source line: "*symhold = show_symlinks_hold;"
Corresponding source file fragment (from ~/projects/proftpd-1.3.3/modules/mod_ls.c:152):
if (!symhold)
*symhold = show_symlinks_hold;
For example, consider the warning NULL_AFTER_DEREF/proftpd-1.3.3/auth.c:1227. Below is the corresponding excerpt from the plain text file ~/projects/proftpd-1.3.3/.svace-dir/analyze-res/proftpd-1.3.3.txt:
* NULL_AFTER_DEREF: Pointer 'login_name' which was dereferenced at line 1222 is compared to NULL value at line 1227.
null check at auth.c:1227
dereference at auth.c:1222
source line: "if ((!login_name || !anon_c) &&"
The relevant part of the source code is as follows:
1222: if (*login_name &&
&&
auth_alias_only *auth_alias_only == TRUE)
*login_name = NULL;
1227: if ((!login_name || !anon_c) &&
) {
anon_name*anon_name = NULL;
}
On line 1222, variable login_name
is dereferenced. If
login_name
is NULL
, this will be a null
pointer dereference. On line 1227, this pointer is compared to
NULL
, which suggests that it indeed can be NULL on line
1222. If it can’t, then the check on line 1227 is redundant.
Warning types classify individual warnings (issues) emitted by Svace. Each warning type specifies the kind of defect being sought, typical situations in which such issues are found, and possible sources of false positives. Some warning types have subtypes, allowing more fine-grained classification of the detected issues.
Each warning type is characterized by several properties, which can
be inspected by running the warning tool
with option --info
. These properties can be helpful when
deciding which warning types to inspect and when interpreting the
results classified under various warning types.
Severity is an estimate for the potential danger represented by issues of the warning type that are detected correctly. Possible values of this property are Critical, Major, Normal, Minor and Undefined.
Reliability is an estimate for how often the issues of a given type are detected correctly. The estimate is based on results obtained on many projects, and actual reliability of results on a given project may significantly deviate from the average. Possible values are VeryHigh, High, Average, Low, VeryLow and Unknown. When there are only a few issues detected for a warning type, it may be worthwhile inspecting even warning types rated Low or VeryLow, but for warning types that are frequently detected, focusing on results of VeryHigh, High and Average reliability is more important.
In addition to normal warnings that indicate a defect, there are warnings with different aims, which are described with this property. Suppressed and Duplicate warning types normally shouldn’t be used, but are included for completeness.
Most Svace checkers are implemented in the main analysis framework, which is indicated by SvaceMain detection tool property of the warning type. A few of the checkers that detect simpler situations are implemented in Clang, and have SvaceClang as their detection tool. Issues imported from SpotBugs are marked as having SpotBugs as the detection tool (and have warning type prefix FB.).
Some warnings have common subtypes, that are indicated by adding a suffix after a dot to the warning type. For example, the TAINTED_INT warning type has a suppressed subtype TAINTED_INT.MIGHT.
This subtype indicates that the checker knows of ways in which the warning might be false, and of ways in which the warning might be true, but can’t prove it either way. This is a “weaker” warning subtype, and sometimes the results falling under it should be skipped, but sometimes the detected issues turn out to be correct. Since the checker can’t prove that the issues are correct, even under the heuristic assumptions used to find them, reliability of these warning types is generally lower and can’t be significantly improved.
This subtype specifies that the runtime error or memory corruption happens inside a user-written procedure called from the code that causes the problem. In other words, the problem is caused by incorrect (unsafe) invocation of a procedure.
This subtype indicates that memory locations involved in these warning reports have global scope.
This subtype indicates that severity of warning may be less than for major type.
This subtype indicates that a warning is reported may require very strict error policy to analyzed source code.
This subtype indicates that a warning is reported by an extended version of the checker.
This subtype indicates that a warning is reported for an exception path.
This subtype indicates that a warning is related to a value returned from a function.
This subtype indicates that a warning is related with a library function.
This subtype indicates that the issue is reported for a specific loop iteration.
This subtype indicates that the issue is reported for a specific condition branch.
This subtype indicates that a warning is reported in test code.
This subtype has the meaning of .PROC in C# null dereference checkers.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Major | High | true |
KOTLIN | Quality | Critical | High | true |
CSHARP | Quality | Normal | Unknown | true |
This checker finds situations where a pointer is dereferenced, but
can only have NULL
value, and so the operation of
dereference can never be run without causing a runtime error. A special
case where the NULL
value is explicitly assigned is
categorized as DEREF_OF_NULL.CONST.
More generally, checkers in the DEREF_OF_NULL group find situations
where a dereferenced pointer can be assigned NULL
value on
one of the possible execution paths.
Subtype .PROC (except C#) or .ARGUMENT (only C#) may be applied to the warnings of this group.
struct process {
char* user;
};
void test(struct process* ps, FILE* log) {
if (!ps)
(log, "No information for user: %s", ps->user);
fprintf}
Dereference of structure pointer ps
, if reachable, can
only lead to a null pointer dereference.
fun example(s: String?) {
if (s == null) {
(s)
derefAnyway}
}
fun derefAnyway(s: String?) {
(s!!.length)
print}
fun possibleFix(s: String?) {
(s?.length)
print}
Function example
illustrates the defect: variable
s
of nullable type is compared with null directly and is
dereferenced by derefAnyway
function call. Function
possibleFix
illustrates a possible fix: use null safe
operator ?.
and remove redundant derefAnyway
call.
The example given is too syntheti??, because it’s quite difficult to
create Kotlin code where Svace will report a
[[#DEREF_OF_NULL|DEREF_OF_NULL]] warning, Kotlin compiler will try to
prevent you from writing such code by reporting warnings and errors. For
example, Kotlin compiler will report a compilation error for
exampleOfCompilationError
function below.
fun exampleOfCompilationError(s: String?) {
if (s == null) {
!!.length // kotlin compiler report a compilation error here
s}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | true |
CXX | Quality | Major | Average | true |
GO | Quality | Major | Average | true |
KOTLIN | Quality | Critical | Average | true |
This checker is similar to DEREF_OF_NULL checker, but can find
situations where a pointer is assigned to NULL
under some
condition and then dereferenced under another condition which is not
incompatible with the first one.
import java.io.File
fun handleCollection(collection: Collection<Any>?) {
for (elem in collection!!) {
// ...
}
}
fun handleCollectionCorrect(collection: Collection<Any>?) {
?.forEach { elem ->
collection// ...
}
}
fun example(f: File) {
val files = f.listFiles()?.asList()
(files)
handleCollection}
fun possibleFix(f: File) {
val files = f.listFiles()?.asList()
(files)
handleCollectionCorrect}
Function example
illustrates the defect:
files
may be null, but it will be dereferenced inside
handleCollection
function call. Function
possibleFix
illustrates a possible fix: use safe
implementation handleCollectionCorrect
to iterate over
collection. Safe call of forEach
is used inside
handleCollectionCorrect
function.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Major | High | true |
GO | Quality | Normal | Unknown | true |
KOTLIN | Quality | Critical | High | true |
This checker finds situations where a pointer is dereferenced, but
can only have NULL
value, because it was explicitly
assigned a NULL
value.
void test() {
int* ptr = NULL;
int x;
// ...
= *ptr;
x }
class Holder(str: String) {
var nullable: Int? = null
}
fun example() {
val h = Holder("Create and forget to init 'nullable'")
.nullable!!.dec()
h}
fun possibleFix() {
val h = Holder("Create and forget to init 'nullable'")
.nullable?.dec()
h}
Function example
illustrates the defect:
dec
is called for value which may be null. Function
possibleFix
illustrates a possible fix: use safe call.
Sometimes, the pointer is assigned a non-null value as a side effect of a function call. If the analysis is unable to see such a side effect (normally, side effects are analyzed), this warning may be emitted incorrectly.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | true |
CXX | Quality | Major | Average | true |
GO | Quality | Critical | High | true |
KOTLIN | Quality | Major | Average | true |
This checker finds situations where a pointer is assigned
NULL
value, and is subsequently dereferenced. In these
situations, the dereferenced pointer is not necessarily
NULL
, but a pointer that was assigned NULL
value is necessarily dereferenced.
void proc(int* p, int flag) {
if (flag == 7)
= 0; // NULL value assigned to `p`.
p
*p = 7; // If NULL value is assigned to `p`, it's necessarily dereferenced here.
}
data class Wrapper(val value: Int)
fun example(w: Wrapper?): Int {
val mayBeNull = w?.value
return mayBeNull!!
}
fun possibleFix(w: Wrapper?): Int? {
val mayBeNull = w?.value
return mayBeNull
}
Function example
illustrates the defect:
mayBeNull
may have null value and is cast to non-nullable
type by !!
operator. Function possibleFix
illustrates a possible fix: return nullable type.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Normal | High | true |
GO | Quality | Major | Unknown | true |
KOTLIN | Quality | Normal | High | true |
This checker finds situations where a function conditionally
dereferences a pointer, and this function is called with
NULL
assigned to that pointer.
void foo(int x, int* p) {
if (x > 7)
return;
*p = 3;
}
void main() {
(30, NULL);
foo}
data class SimpleData(val value: Int)
fun example(): Int {
return helper(null, null)
}
fun helper(a: SimpleData?, b: SimpleData?): Int {
if (a != null) {
return a.value + 2
} else {
return b!!.value + 2
}
}
fun exampleFix(): Int {
return helperFix(null, null)
}
fun helperFix(a: SimpleData?, b: SimpleData?): Int {
?.let { return a.value + 2 }
a?.let { return b.value + 2 }
bthrow IllegalStateException()
}
Function example
illustrates the defect:
helper
function is called with both null
arguments and its second argument is cast to non-nullable type by
!!
operator inside helper
function. Function
helperFix
illustrates a possible fix: use null safe
operator ?.
.
Sometimes, the condition under which the function dereferences the
pointer is not satisfiable at the call site where NULL
value is assigned to that pointer. The checker emits the warning even if
this possibility wasn’t ruled out, which leads to false positives in
such cases.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Minor | Unknown | false |
CXX | Quality | Normal | Unknown | false |
KOTLIN | Quality | Normal | Unknown | false |
This checker finds null pointer dereferences that are less reliable than those found by the DEREF_OF_NULL checker. Additionally, for Kotlin it reports situations where a value with a nullable annotation is dereferenced. Note that a nullable annotation is supported as a language feature (a nullable type) in case of Kotlin.
fun getLen(s: String?) = s!!.length
fun getLenCorrect(s: String?) = s?.length ?: throw IllegalStateException()
fun getLenOf(s: Any?) = (s as String).length
fun getLenOfCorrect(s: Any?) = (s as? String)?.length ?: throw IllegalStateException()
Functions getLen
and getLenOf
illustrate
the defect: s
may have null value and is cast to
non-nullable type by !!
and as
operators
respectively. Functions getLenCorrect
and
getLenOfCorrect
illustrate possible fixes: use null safe
?.
and as?
operators.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | false |
This warning finds situations where a pointer returned by a memory
allocation function, such as malloc(), is dereferenced without being
checked for NULL
value.
void test() {
char* buf = (char*) malloc(10);
[0] = '\0';
buf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Unknown | true |
CXX | Quality | Major | Unknown | true |
KOTLIN | Quality | Major | Unknown | true |
CSHARP | Quality | Major | Unknown | true |
This detector finds situations where a pointer returned by a library
function is dereferenced without being checked for NULL
value.
Following is the partial list of library functions that are
recognized by this detector as being able to return NULL
value for reasons not under user’s control:
Examples from C standard library:
localtime
,fdopen
,fopen
,popen
,getenv
,opendir
,dlopen
,dlsym
,freopen
,tempnam
,tmpfile
,tmpnam
,ptsname
,getutent
,getutid
,getutline
,pututline
,getcwd
,getwd
.Examples from Java libraries:
java.io.File.listRoots()
java.io.File.getParent()
java.io.File.getParentFile()
java.io.File.listFiles().tryLock()
java.net.DatagramSocket.getLocalAddress()
java.nio.channels.FileChannel
javax.crypto.Cipher.update(byte[])
javax.crypto.Cipher.update(byte[], int, int)
android.os.Bundle.getBundle(String)
void example() {
char *s = getenv("RANDFILE");
if (s[0] == '\0') {
// ...
}
}
void possible_fix() {
char *s = getenv("RANDFILE");
if (s != NULL && s[0] == '\0') {
// ...
}
}
Function example
illustrates the defect: function
getenv
may return NULL
, which will be
dereferenced by array access at index 0. Function
possible_fix
illustrates a possible fix: check the result
of getenv
call.
import java.io.File
fun example(f: File) = f.parentFile.listFiles()
fun possibleFix(f: File) = f.parentFile?.listFiles()
Function example
illustrates the defect: property
parentFile
may return null, which will be dereferenced by
listFiles
call. Function possibleFix
illustrates a possible fix: use safe call of listFiles
function.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Unknown | true |
CXX | Quality | Major | Unknown | true |
KOTLIN | Quality | Major | Unknown | true |
CSHARP | Quality | Normal | Unknown | true |
This detector is a subtype of DEREF_OF_NULL.RET.LIB detector. DEREF_OF_NULL.RET.LIB.PROC finds situations where a pointer returned by a library function may have NULL value and is dereferenced within a function call.
void example() {
FILE *f;
= fopen("test.txt", "r");
f (f);
fclose}
void possible_fix() {
FILE *f;
= fopen("test.txt", "r");
f if (f != NULL) {
(f);
fclose}
}
Function example
illustrates the defect: function
fopen
may return NULL, which will be dereferenced within
fclose
call. Function possible_fix
illustrates
a possible fix: check the result of fopen
call.
import java.io.File
fun handleFiles(arr: Array<File>?) = arr!!.count { f -> f.isFile && !f.isDirectory }
fun exampleProc(f: File) = handleFiles(f.listFiles())
fun handleFilesCorrect(arr: Array<File>?) = arr?.count { f -> f.isFile && !f.isDirectory } ?: 0
fun possibleFixProc(f: File) = handleFilesCorrect(f.listFiles())
Function exampleProc
illustrates the defect: call of
listFiles
function may return null, which will be
dereferenced within the call of user defined function
handleFiles
. Function possibleFixProc
illustrates a possible fix: use safe implementation
handleFilesCorrect
to iterate over files. Safe call of
count
is used inside handleFilesCorrect
function, also it returns zero if arr
is null.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Normal | Unknown | false |
This is an obsolete detector. Both DEREF_OF_NULL.RET.USER and DEREF_OF_NULL.RET.USER.PROC have been replaced with DEREF_OF_NULL.RET detector.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Major | High | false |
CSHARP | Quality | Normal | Unknown | false |
This is an obsolete detector. Both [[#DEREF_OF_NULL.RET.USER|DEREF_OF_NULL.RET.USER]] and [[#DEREF_OF_NULL.RET.USER.PROC|DEREF_OF_NULL.RET.USER.PROC]] have been replaced with [[#DEREF_OF_NULL.RET|DEREF_OF_NULL.RET]] detector.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Unknown | false |
CXX | Quality | Normal | Unknown | false |
GO | Suppressed | Major | Average | false |
KOTLIN | Quality | Normal | Unknown | true |
This detector finds situations where a pointer returned by a
user-written procedure that returns NULL
on some execution
paths, is dereferenced without being checked for NULL
value.
#define USE_PREFIX 1
#define MAX_SIZE 1024
char* buf[MAX_SIZE];
char* get_login(const char* url, int flag) {
if (flag == USE_PREFIX && url[0] != '%' && url[1] != '%')
return NULL;
return fill_from_bd(buf, MAX_SIZE);
}
void use(const char* url) {
char* login = get_login(url, USE_PREFIX);
int len = strlen(login);
// ...
}
data class Wrapper(val value: Int)
class Helper(val wr: Wrapper?) {
fun getMaybeNull() = wr?.value
}
fun deref(n: Int?) = n!!.times(3)
fun example(h: Helper): Int {
val num = h.getMaybeNull()
return deref(num)
}
fun derefCorrect(n: Int?) = n?.times(3)
fun possibleFix(h: Helper): Int? {
val num = h.getMaybeNull()
return derefCorrect(num)
}
Function example
illustrates the defect:
num
may be null after getMaybeNull
call and it
is dereferenced by call of user defined function deref
.
Function possibleFix
illustrates a possible fix: use safe
implementation derefCorrect
. Safe call of
times
is used inside derefCorrect
function.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Major | High | true |
GO | Quality | Normal | Unknown | true |
KOTLIN | Quality | Critical | High | true |
CSHARP | Quality | Critical | High | true |
This checker finds situations where first, a pointer is compared to
NULL
(which indicates that it could have a
NULL
value), and then it is dereferenced
(unconditionally).
A subtype DEREF_AFTER_NULL.LOOP is emitted when the conditional
expression comparing the pointer to NULL
is part of a
loop.
A (suppressed) .MACRO subtype of this warning is emitted if it is suspected that the warning is a false positive, caused by the conditional expressions being implemented within a macro. Such checks can be too general and not always reflect possible value range of index variables.
int test(char* str1, char* str2, int len) {
if (!str1) {
= 0;
len } else {
= strlen(str1);
len }
return strcmp(str1, str2);
}
If str1
can be NULL
, so that the
conditional isn’t trivial, then call of strcmp
will lead to
a null pointer dereference.
data class Client(val name: String, val age: Int)
fun example(arg: Client?) {
val age = arg?.age
("Client '${arg!!.name}' is $age years old")
println}
fun possibleFix(arg: Client?) {
val age = arg?.age
?.let{ println("Client '${arg.name}' is $age years old") }
age}
Function example
illustrates the defect:
age
is got from arg
with null check, but
name
is got avoiding null check. Function
possibleFix
illustrates a possible fix. Note that Kotlin
compiler knows that arg
is not null into let
code block.
This warning can cause false positives if the conditional expression has a side effect of terminating the program, but that wasn’t noticed by the tool. For example:
if (!str1) {
();
my_exit}
return strcmp(str1, str2);
If instead of my_exit(), a standard exit(int) is called, no false warnings will be emitted. The same holds if my_exit() transparently calls exit(int) or one of the other functions annotated as terminating.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | true |
CXX | Quality | Major | Average | true |
GO | Quality | Major | Average | true |
KOTLIN | Quality | Critical | Average | true |
This checker is a version of the DEREF_AFTER_NULL with path sensitivity. Unlike DEREF_AFTER_NULL, it gives warning when the variable after comparing with null can be dereferenced, and there is a combination of the input parameters, what may cause an error.
import java.text.DateFormat
import java.util.*
fun example(date: String?): Date {
?.let{ println("parsing: '$date'") }
dateval df = DateFormat.getDateInstance(DateFormat.LONG, Locale.KOREA)
return df.parse(date)
}
fun possibleFix(date: String): Date {
("parsing: '$date'")
printlnval df = DateFormat.getDateInstance(DateFormat.LONG, Locale.KOREA)
return df.parse(date) // svace: not_emitted DEREF_AFTER_NULL*
}
Function example
illustrates the defect:
date
may be null according to safe call of let
block, but it has been dereferenced onwards by passing as first
parameter into parse
method. Function
possibleFix
illustrates a possible fix: change function
contract. First argument of possibleFix
function is
non-nullable.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Major | High | true |
KOTLIN | Quality | Major | High | true |
CSHARP | Quality | Major | Unknown | true |
The checker NULL_AFTER_DEREF finds situations where first, a pointer
is dereferenced, and then it is compared to null (which indicates that
it could have a NULL
value).
A (suppressed) .MACRO subtype of this warning is emitted if it is suspected that the warning is a false positive, caused by the conditional expressions being implemented within a macro. Such checks can be too general and not always reflect possible value range of index variables.
struct bank {
char* name;
char* pool;
};
void test(struct bank* c, char* name) {
(name, "<Global>");
strcpy
if (name)
->name = pstrdup(c->pool, name);
c}
If name
can be NULL
, so that the
conditional isn’t trivial, then call of strcpy
will lead to
a null pointer dereference.
import java.text.DateFormat
import java.util.Date
import java.util.Locale
fun example(date: String?): Boolean {
val df = DateFormat.getDateInstance(DateFormat.LONG, Locale.KOREA)
val ret = df.parse(date)
?.let{ println("'$date' has been parsed") } // svace: emitted NULL_AFTER_DEREF
datereturn ret.after(Date(2020, 12, 10))
}
fun possibleFix(date: String?): Boolean {
val ret = date?.let {
val df = DateFormat.getDateInstance(DateFormat.LONG, Locale.KOREA)
.parse(date).also { println("'$date' has been parsed") }
df}
return ret?.after(Date(2020, 12, 10)) ?: false
}
Function example
illustrates the defect:
date
may be null according to safe call of let
block but it has been dereferenced earlier by passing as first parameter
into parse
method. Function possibleFix
illustrates a possible fix: wrap more code into let
block.
In some cases, the dereference indicated in the warning points to a
function that returns the pointer. Such warnings mean that the function
that produced the pointer unconditionally dereferences it, and so the
returned pointer can’t be NULL
. In some cases, the code
that checks the returned value for quality to NULL
is
protective or follows the specification that allows NULL
value as possible behavior. In other cases, presence of the check
indicates an incorrect assumption about the function.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Unknown | false |
CXX | CodingStyle | Minor | Average | false |
The checker is like NULL_AFTER_DEREF,
but it finds situations where pointer was dereferenced in a function and
then returned. If the caller of that function still compares the
returned value to NULL
, they believe that it’s possible for
the returned value to be NULL
, while it’s actually not.
char* get_buf(void) {
char* buf = (char*) malloc(100);
[0] = 'q';
bufreturn buf;
}
void foo(void) {
char* res = get_buf();
if (res) // NULL_AFTER_DEREF.RET: `res` can't be NULL.
[1] = 'w';
res}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | true |
Since address of a local variable can’t be null comparing it with null value is redundant.
int foo() {
int x = 0;
if (&x) // COMPARE_LOCAL_ADDR, maybe it should be `if (x)`.
return 0;
return 1;
}
Tainted input checkers find situations where a value obtained from external sources (network, files, environment variables) is used in an operation sensitive to incorrect or malicious parameters. The externally controlled value is called tainted, the location where tainted value is obtained is called source, and the location where tainted value is dangerously used is called sink. Checkers in this group consider tainted values of integer or C string types. TAINTED_INT.LOOP considers loop bounds as sinks.
Sources for TAINTED_INT:
int getch(void);
int _getch(void);
int getchar(void);
int atoi(const char* arg);
long atol(const char* arg);
long long atoll(const char* arg);
long strtol(const char *restrict nptr, char **restrict endptr, int base);
long long strtoll(const char *restrict nptr, char **restrict endptr, int base);
unsigned long strtoul(const char *restrict nptr, char **restrict endptr, int base);
unsigned long long strtoull(const char *restrict nptr, char **restrict endptr, int base);
elements of tainted integer arrays
Sinks for TAINTED_INT:
char *fgets(char *s, int num, FILE *stream);
void* memset(void * ptr, int value, size_t num);
ssize_t pwrite(int d, const void *buf, size_t nbytes, off_t offset);
ssize_t write(int d, const void *buf, size_t nbytes);
void *calloc(size_t num, size_t size);
void *malloc(size_t size);
void *xmalloc(unsigned long size);
void *realloc(void *ptr, size_t size);
void *xrealloc (void *ptr, size_t size);
Sources for TAINTED_PTR:
ssize_t recv(int s, void *buf, size_t len, int flags);
ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
char *fgets(char *s, int num, FILE *stream);
size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);
char *gets(char *s);
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
char *getenv(const char* key);
ssize_t read(int d, void *buf, size_t nbytes);
(...) scanf
Sinks for TAINTED_PTR:
FILE *fopen(const char *filename, const char *mode);
FILE *freopen(const char *filename, const char *mode, FILE *stream);
FILE *popen(const char *command, const char *mode);
char* strcat(char *s, const char * append);
char* strcpy(char *dst, const char *src);
char* stpcpy(char *dst, const char *src);
Format string sinks:
void err(int eval, const char *fmt, ...);
void verr(int eval, const char *fmt, va_list args);
void errx(int eval, const char *fmt, ...);
void verrx(int eval, const char *fmt, va_list args);
void warn(const char *fmt, ...) ;
void vwarn(const char *fmt, va_list args);
void warnx(const char *fmt, ...);
void vwarnx(const char *fmt, va_list args);
void error(int status, int errnum, const char *fmt, ...);
int fprintf(FILE *stream, const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int printf(const char *format, ...);
int scanf(const char *format, ...);
int sprintf(char *s, const char *format, ...) ;
int snprintf(char *str, size_t size, const char *format, ...);
int asprintf(char **ret, const char *format, ...);
int sscanf(const char *s, const char *format, ...);
int vscanf(const char *format, va_list ap);
int vsscanf(const char *str, const char *format, va_list ap);
int vfscanf(FILE *stream, const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vprintf(const char *format, va_list ap);
int vsprintf(char *s, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
int vasprintf(char **ret, const char *format, va_list ap);
void setproctitle(const char *fmt, ...);
void syslog(int priority, const char *message, ...);
void Tcl_Panic(const char* format, ...);
void panic(const char* format, ...)
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Average | true |
CXX | Quality | Critical | Average | true |
GO | Suppressed | Major | Average | true |
The checker TAINTED_INT finds situations where an integer value received from external source (from a file or from the network; such value is called tainted value) is used in an operation where uncontrolled value may cause problems, such as allocation of memory of given size, or reading the given number of bytes from a file.
There is a .MIGHT variant of this warning (TAINTED_INT.MIGHT).
This warning has the following subtypes:
isdigit
).size_t size;
char* res;
void test() {
char* env = getenv("QQQ");
= strtoul(env, NULL, 10);
size
// Allocating a buffer of unbounded number of bytes.
= (char*) malloc(size);
res }
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Suppressed | Major | Unknown | true |
A subtype of TAINTED_INT, where a tainted
value is used in character type functions from header
<ctype.h>
(isdigit, isspace, etc.). ISO C requires
that ctype functions work for unsigned char values and for EOF. Other
values may lead to undefined behavior, but many library implementations
also support negative signed char and some other values. This warning is
suppressed if tainted integer is known to have values only in interval
[-128; 255].
char ch = '0';
int val;
void int_type() {
("%d", &val);
scanfif (isdigit(val)) // Here the program may crash; TAINTED_INT.CTYPE is emitted.
= (char) val;
ch }
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Suppressed | Critical | Average | true |
CXX | Quality | Critical | High | true |
A subtype of TAINTED_INT, where the tainted value is used as a bound in a loop, and thus may cause the program to hang.
size_t size;
char* res;
void test() {
int i;
char* env = getenv("QQQ");
= strtoul(env, NULL, 10);
size
for (i = 0; i < size; i++) {
// ...
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Average | true |
CXX | Quality | Major | Average | true |
GO | Suppressed | Major | Unknown | true |
KOTLIN | Quality | Critical | Average | true |
The checker TAINTED_ARRAY_INDEX finds situations where an integer value received from external source (from a file or from the network) is used as an index in accessing an array, without ensuring that it’s within bounds. This warning is a subtype of TAINTED_INT.
There is a .MIGHT variant of this warning (TAINTED_ARRAY_INDEX.MIGHT).
void array_index() {
int buf[256];
int index = getchar();
if (index < 256) {
// Index may still be negative!
[index] = 7;
buf}
}
fun example(number: String) {
val num: Int = number.toInt()
val x: IntArray = intArrayOf(1, 2, 3)
if (num < 3) {
(x[num])
print}
}
fun possibleFix(number: String) {
val num: Int = number.toInt()
val x: IntArray = intArrayOf(1, 2, 3)
if (num >=0 && num < 3) {
(x[num])
print}
}
Function example
illustrates the defect:
num
is converted from string value. Only the right bound of
num
is checked and num
may be still negative
when it’s used as array index. Function possibleFix
illustrates a possible fix: check the left bound of num
as
well.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Suppressed | Major | High | true |
CXX | Quality | Critical | High | true |
This checker is very similar to TAINTED_ARRAY_INDEX, except that it finds situations where array access with tainted index value happens through a pointer (TAINTED_ARRAY_INDEX is emitted only when the array is identified explicitly by name).
There is a .MIGHT variant of this warning (TAINTED_INT.PTR.MIGHT).
void set_base(char* base) { // Here 'base' an unknown pointer that probably points to an array.
char* env = getenv("QQQ");
size_t size = strtoul(env, NULL, 10);
[size] = '\0'; // Accessing an array through pointer `base` by tainted index `size`.
base}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | VeryHigh | true |
CXX | Quality | Critical | VeryHigh | true |
GO | Suppressed | Critical | High | true |
The checker TAINTED_PTR finds situations where a string received from external source (from a file or from the network) is used in an operation where uncontrolled string (or unbounded size of the string) may cause problems, such as copying to a fixed-size array.
There is a .MIGHT variant of this warning (TAINTED_PTR.MIGHT).
char* env;
char buf[100];
void test() {
= getenv("VAR3");
env
// Copying a string of unbounded size to a fixed-size buffer.
(buf, env);
strcpy}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Suppressed | Critical | Unknown | true |
This checker finds uses of library function sprintf()
that may overflow the destination buffer, because some values (integer
values or strings) printed according to a constant format string are
tainted and unchecked.
char buf[100];
char dst[16];
void test() {
(buf, 50, stdin);
fgets(dst, "* %s\n", buf);
sprintf}
size_t size;
char dst[5];
void test() {
char* env = getenv("QQQ");
= strtoul(env, NULL, 10);
size
(dst, "%d", size);
sprintf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Low | true |
CXX | Quality | Critical | High | true |
The checker TAINTED_PTR.FORMAT_STRING finds situations where a string
received from external source (from a file or from the network) is used
as a format string parameter in functions of printf()
family. This vulnerability may be used in a format string
attack.
void fmt_str(int s, char* buf, int len, int flags) {
(s, buf, len, flags);
recv(buf);
printf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Low | true |
CXX | Quality | Major | Low | true |
GO | Quality | Critical | VeryLow | true |
The checker STATIC_OVERFLOW finds situations where a fixed-size array is accessed at a constant index outside its range.
void buf_overflow() {
char buf[1024];
[1024] = 0;
buf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | High | true |
CXX | Quality | Major | High | true |
GO | Quality | Critical | Unknown | true |
The checker DYNAMIC_OVERFLOW finds situations where a dynamic array is accessed at a constant index outside its range.
void buf_overflow() {
char *buf = (char *) malloc(1024);
[1024] = 0;
buf(buf);
free}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Unknown | true |
CXX | Quality | Major | Low | true |
This checker finds buffer overflow situations where a buffer is accessed (with potential overflow) in the same procedure where it’s locally defined.
void access_buf(int index) {
int buf[10];
[index] = 7;
buf}
void run() {
int i;
if (i >= 10)
(i);
access_buf}
Here, the situation is detected interprocedurally (the condition that
index
is outside bounds is specified outside function
access_buf
). Warning of this type is emitted, because
buffer access operation is in the same procedure as definition of the
buffer.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Suppressed | Critical | VeryLow | false |
CXX | Quality | Major | VeryLow | false |
This checker finds buffer overflow situations where a buffer is passed into another procedure before being accessed (with potential overflow).
void access_buf(int* buf) {
[10] = 0;
buf}
void run() {
int buf[10];
(buf);
access_buf}
Since buf
is only 10 elements long, the access operation
in procedure access_buf
overflows the buffer. This
situation is detected interprocedurally, since the size of the buffer is
not available inside procedure access_buf
.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Unknown | false |
CXX | Quality | Major | Unknown | false |
GO | Quality | Critical | VeryLow | false |
This checker finds overflows for heap buffers where a buffer is passed into another procedure before being accessed (with potential overflow).
void access_buf(int* buf)
{
[10]=0;
buf}
void test() {
int* buff = new int[10];
(buff); // Overflow.
access_buf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | VeryHigh | true |
This checker finds situation where a function from
sprintf()
family is used to write to a buffer of known
size, but the constant format string and possible values of the
arguments are such that the buffer could overflow as a result of the
call.
void static_overflow(char* val) {
char buf[6];
(buf,"%d!", val);
sprintf}
The checker will emit a warning for this example, unless
val
is known to be in the interval [-999, 9999] (up to 4
characters for the value, one for symbol !
and one for
NULL
terminator at the end of the string, total of up to 6
characters).
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Average | true |
This checker finds situation where a function from
scanf()
family is used to read data into a buffer of known
size, but the constant format string and possible values of the source
of the data are such that the buffer could overflow as a result of the
call.
void static_overflow(FILE* f) {
char buf[50];
(f, "%s", buf); // `buf` can overflow.
fscanf}
Since the number of bytes read from the stream f
is not
restricted, the buffer can overflow. One way to fix this defect is to
specify the maximum number of characters to be read explicitly:
void avoid_overflow(FILE* f) {
char buf[50];
(f, "%49s", buf); // `buf` can't overflow: bounds specified in format string.
fscanf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Major | Average | true |
The checker CHECK_AFTER_OVERFLOW finds situations where first, a buffer is accessed with a certain index, and then this index is compared to some value that indicates that the index may lie outside the buffer’s range.
char buf[256];
int test(int i) {
[i] = 0;
buf
if (i != 256)
return 1;
return 0;
}
In this example, the check at the end shows that i is expected to sometimes have a value of 256, in which case the buffer access above will be out of bounds.
A (suppressed) .MACRO subtype of this warning is emitted if it is suspected that the warning is a false positive, caused by the conditional expressions being implemented within a macro. Such checks can be too general and not always reflect possible value range of index variables.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Unknown | true |
CXX | Quality | Major | Average | true |
The checker OVERFLOW_AFTER_CHECK finds situations where first, a variable is compared to some value, indicating what the possible values of the variable are, and then it’s used to access a buffer in such a way that one of the possible values indicated by the check lies out of bounds.
char buf[256];
void overflow(int i) {
if (i > 255)
("i > 255");
printf
[i] = 0;
buf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Average | true |
CXX | Quality | Major | Average | true |
GO | Quality | Critical | Average | true |
Issues of this type are detected when the value of an index used to access a buffer is checked with a bound that is less strict than necessary. A bound on an index value can be used to ensure the absence of buffer overflows due to index value being too high or too low, but if the bound is itself too high or too low, a buffer overflow can still occur. For example, if a buffer has 10 elements, index values up to 9 are permitted, but checking that the index is less than 20 allows values between 10 and 19 that would lead to buffer overflow.
int buf[10];
if (i < 20)
[i] = 3; // Possible buffer overflow.
buf
if (i >= -1)
[i] = 5; // Possible buffer overflow.
buf
for (i = 0; i < 100; ++i)
[i] = 7; // Possible buffer overflow. buf
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | true |
The checker BUFFER_SIZE_MISMATCH finds situations where a size
parameter passed to “safe” versions of standard functions (such as
strncpy
or memset
) is unsafe (out of local
buffer bounds).
char dst[10];
char src[11];
void test() {
(dst, src, sizeof(src));
strncpy}
The last parameter for strncpy()
should’ve been
sizeof(dst) - 1
.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Unknown | true |
The checker BUFFER_SIZE_MISMATCH.STRICT finds situations where a size
parameter passed to “safe” versions of standard functions (such as
memset_s
, sprintf_s
) is not constant and may
be out of bounds.
char dst[10];
void test_bad(int size, char* src) {
(dst, size, src, strlen(src));
memcpy_s}
void test_good(char* src) {
(dst, 10, src, strlen(src));
memcpy_s}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Normal | Unknown | true |
The checker DYNAMIC_SIZE_MISMATCH.STRICT finds situations where a
size parameter passed to “safe” versions of standard functions (such as
memset_s
, sprintf_s
) is not constant and may
be out of bounds. Unlike BUFFER_SIZE_MISMATCH.STRICT it
emits warnings for buffer from heap.
void test_bad(int size, char* src) {
char* dst = malloc(100);
(dst, size, src, strlen(src));
memcpy_s}
void test_good(char* src) {
char* dst = malloc(10);
(dst, 10, src, strlen(src));
memcpy_s}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Unknown | true |
CXX | Quality | Major | High | true |
The checker DYNAMIC_SIZE_MISMATCH finds situations where a size
parameter passed to “safe” versions of standard functions (such as
strncpy
or memcpy
) is unsafe (out of dynamic
buffer bounds).
char *dst;
char src[11];
void test() {
= (char *)malloc(10);
dst (dst, src, sizeof(src));
strncpy}
The last parameter for strncpy()
should’ve been
sizeof(dst) - 1
.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Average | true |
This checker reports warnings for instances of pointer assigned memory allocations where the pointer’s target type is larger than the block allocated.
struct Person {
int age;
char name[20];
};
struct Person *foo(void) {
struct Person *ptr;
= (Person *) malloc(sizeof(ptr)); // Mismatched allocation size.
ptr return ptr;
}
In this example, the allocation is too small for the pointer’s target type ??? **sizeof(*ptr)** was probably intended.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Low | false |
This checker reports warnings for C++ allocations using operator
new
when direct initialization is used incorrectly instead
of the dynamic array size specification.
int *a = new int(10); // Should be `new int[10]`.
This allocates memory for a single integer and initializes it with value 10 instead of allocating memory for 10 integer numbers.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Average | true |
This checker reports the same issue as ALLOC_SIZE_MISMATCH but for
memset
and similar functions instead of allocations.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | VeryHigh | true |
This checker finds situations where function readlink
(from libc) is used incorrectly. Function readlink
returns
-1 on error, or the number of bytes written in the buffer, but doesn’t
write terminating NULL
, so that it may return value equal
to buffer size.
char buf[128];
void overflow() {
int len = readlink("/mnt/modules/pass1", buf, sizeof(buf));
if (len != -1) {
// `len` may be = `sizeof(buf)` = 128.
[len] = 0; // Emitted READLINK_OVERFLOW.
buf}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
The checker NONTERMINATED_STRING finds situations where “safe” versions of standard functions working with C strings enable creation of nonterminated strings as a result.
char dst[10];
char src[15];
void cp_str() {
(dst, src, sizeof(dst));
strncpy}
Even though buffer overflow won’t occur directly in this call, it may create a non-terminated string, which may lead to buffer overflow later, when the string is accessed again. For example, if src is a 10-character string, the call will copy all 10 characters in dst, but there is no space left for a null terminator.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | High | true |
This checker finds situations where “safe” versions of standard functions working with C strings enable creation of non-null-terminated strings as a result of using length parameter coming from untrusted source.
char dst[10];
void cp_str() {
char* src = getenv("qqq");
(dst, src, sizeof(dst));
strncpy}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | false |
This checker is similar to NONTERMINATED_STRING, but doesn’t assume that destination memory is zero-filled. It is reported when copying a smaller string into a bigger buffer without adding a null terminator. This can lead to unintended addition of a suffix to the copied string or nonterminated string if the destination buffer wasn’t null-terminated.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | VeryHigh | true |
This checker finds situations where a call to a string copying function can lead to a buffer overflow if the source string is larger than the destination buffer.
char buf[100];
void test1(char* param) {
(buf, param);
strcpy}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | false |
The checker finds situations of potential overflow where string length is used to check possible size of buffer.
void example(char* q) {
int len = strlen(q);
if (len > 0) {
struct S*s = (struct S*)q;
->x = 1; // Potential overflow if len < sizeof(struct S).
s}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | VeryLow | true |
This checker finds situation where a pointer to non-heap memory could be passed to a memory deallocation function.
int buf[5];
int* ptr;
void test(int cond) {
if (cond)
= (int*) malloc(10);
ptr else
= buf;
ptr // ...
(ptr); // `ptr` could reference non-heap array `buf`.
free}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Average | false |
This checker finds situation where a pointer passed to a memory deallocation function was obtained as a result of an arithmetic operation. Though sometimes the correct address of the beginning of a memory allocation block could be reconstructed using arithmetic operations, this is potentially a serious problem.
void foo(int* ptr) {
int* start = ptr-30;
(start);
free}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
It emits warnings for arithmetic operation on the results of heap allocation functions. This might be a common typo when a closing brace is mistakenly placed before a part of the arithmetic expression for allocation size calculation.
char *mystrcpy(char *str) {
char *ret = malloc(strlen(str)) + 1; // Typo.
for (; *str;) *ret++ = *str++;
char *copy = ret;
*copy = 0;
return ret;
}
char *mystrcpy(char *str) {
char *ret = malloc(strlen(str) + 1); // Correct size allocation for the whole string and zero terminator.
for (; *str;) *ret++ = *str++;
char *copy = ret;
*copy = 0;
return ret;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | false |
This checker finds situations where a NULL
pointer is
passed to the library function free()
. While it is totally
okay, the code is useless as in this case no operation is performed, and
it might mean a problem with the program logic.
void test(int z, char* x) {
if (z == 7) {
= NULL;
x (x);
free}
}
void test(int x, char* z) {
if (z == NULL) {
= 7;
x (z);
free}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Undefined | Unknown | true |
This checker finds situations where a memory location is accessed through a pointer that has just been deallocated.
void test(char* pval, char x) {
(pval);
free= *pval;
x }
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Average | true |
This checker finds situations where the value of a pointer to a memory location that has been deallocated, is accessed.
void after_free(char* pval, char* pval2) {
(pval);
free= pval + 2;
pval2 }
char* foo(char* x) {
(x);
freereturn x;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | true |
Subtype of USE_AFTER_FREE for situations where a pointer is passed to the realloc function that invalidates it but the original pointer value is used afterwards.
void boo() {
int *newp = (int *) realloc(p, SIZE); // `p` is released.
[0] = 1; // Use of `p`.
p(newp);
free}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Undefined | Unknown | true |
This checker finds situations where a pointer referencing deallocated memory is passed to a function.
void foo(char* p);
void run(char* p) {
(p);
free(p);
foo}
Here, pointer p
passed to a call of foo
references memory deallocated by function free
. One way to
fix this defect is to set the deallocated pointer to
NULL
:
(p);
free= NULL;
p (p); // No warning. foo
struct proc {
char* mem;
int a;
};
void foo(struct proc* p);
void run(struct proc* p) {
(p->mem);
free(p); // `p->mem` is referenced by p.
foo}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Undefined | Unknown | true |
This checker finds situations where a pointer referencing deallocated memory is deallocated again.
char* foo(char* x) {
(x);
free(x);
free}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Average | false |
This checker is designed to detect dangling pointers and resources, i.e. such pointers and resources that can be available from the caller context after they have been freed or released inside the callee.
int naive_pop(struct list *l)
{
if (l == NULL) {
return 0;
}
int *p = l->ptr;
int ret = *p;
(p); // No removing of the freed pointer from the list, it remains there as a
free// dangling pointer that could crash the program on its dereference.
return ret;
}
typedef struct _St {
char *p1;
char *p2;
} St;
void ex(St *s) {
if (cond1()) {
(s->p1);
free->p1 = NULL; // Correct: the deallocated pointer is cleared.
sreturn;
}
if (cond2()) {
(s->p1); // Potential defect: the deallocated pointer may be used
free// outside of the function.
return;
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | true |
Statistical version of DANGLING_POINTER. Svace emits this subtype when for some cases externally accessible memory pointing to it is reassigned and for others it isn’t.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Average | true |
This checker detects possible mismatch when using function
strlen
to determine the size of a newly allocated buffer
based on the length of an existing string.
char* alloc_same_plus_1(char* str) {
char* res = (char*) malloc(strlen(str+1)); // Svace emits INCORRECT_STRLEN.
[0] = '\0';
resreturn res;
}
strlen(str+1) return value that is less than
strlen(str)
. Programmer probably wanted to write
(strlen(str) + 1)
to allocate 1 byte more than the length
of str
; also, str+1
skips NULL
terminator if str
was empty, leading to undefined
behavior.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | true |
This checker detects memory leak situations, where memory was allocated, and then all references to that memory were lost.
void mem_leak() {
char* ptr1 = (char*)malloc(10);
= 0; // Memory is leaked here.
ptr1 }
void mem_leak() {
char* str = strdup("hello");
= "qqq"; // Memory allocated by function strdup is leaked here.
str }
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker reports cases of using of the same source and destination buffers in function calls where it is prohibited such as memcpy.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | High | true |
CXX | Quality | Major | High | true |
GO | Quality | Normal | Unknown | true |
KOTLIN | Quality | Major | High | true |
CSHARP | Quality | Major | High | true |
This checker finds situations where a file descriptor, file handle or a socket descriptor are lost, because local variables that held their value went out of scope or were re-assigned.
void func1() {
FILE* f = fopen("qqq.c", "w");
}
void func2() {
FILE* f = fopen("qqq.c", "w");
= 0;
f }
In some cases it might be possible for the programmer to predict the value of a descriptor returned by a function that allocates it. For example, after closing standard descriptor 1, the next allocated descriptor will have value 1. In such cases, even if the returned value is not recorded, the predicted value can still be used to deallocate resources. For these situations, Svace emits warnings of subtype HANDLE_LEAK.STRICT.
import java.io.*;
public class HandleLeakTest {
public static void example(File f) {
try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(f))) {
.writeObject("test");
output} catch (IOException ignored) { }
}
public static void possibleFix(File f) {
try (FileOutputStream fs = new FileOutputStream(f); ObjectOutputStream output = new ObjectOutputStream(fs)) {
.writeObject("test");
output} catch (IOException ignored) { }
}
}
Function example
illustrates the defect. The underlying
FileOutputStream
is not declared in a variable. It will
never be closed directly in the generated finally block, it will be
closed only through the close
method of the wrapping
ObjectOutputStream
. The problem is, that if an exception is
thrown from the ObjectOutputStream
constructor, its
close
method will not be called and therefore the
underlying FileOutputStream
will not be closed. Function
possibleFix
illustrates a possible fix: assign the result
of FileOutputStream
in variable and use it to construct
ObjectOutputStream
. Note that the result of
FileInputStream
constructor call will be lost if
IOException
happens but this exception is handled inside
example
method. If exception goes out of the method scope,
HANDLE_LEAK.EXCEPTION will be
emitted.
fun example(bytes: ByteArray) {
val stream = File("data.txt").inputStream()
.read(bytes)
stream}
fun possibleFix(bytes: ByteArray) {
("data.txt").inputStream().use { it.read(bytes) }
File}
Function example
illustrates the defect: file input
stream was created for data.txt
but it wasn’t closed
properly. Function possibleFix
illustrates a possible fix:
call use
function which executes the given function block
on this resource and then closes it down correctly whether an exception
is thrown or not.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Normal | Unknown | true |
KOTLIN | Quality | Normal | Unknown | true |
CSHARP | Quality | Normal | Unknown | true |
This checker finds situations where a file descriptor, file handle or a socket descriptor are lost because of exception, which went out of the function scope.
import java.io.*;
public class HandleLeakTest {
public static void example(File f) throws IOException {
try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(f))) {
.writeObject("test");
output}
}
public static void possibleFix(File f) throws IOException {
try (FileOutputStream fs = new FileOutputStream(f); ObjectOutputStream output = new ObjectOutputStream(fs)) {
.writeObject("test");
output}
}
}
Function example
illustrates the defect. The underlying
FileOutputStream
is not declared in a variable. It will
never be closed directly in the generated finally block, it will be
closed only through the close
method of the wrapping
ObjectOutputStream
. The problem is, that if an exception is
thrown from the ObjectOutputStream
constructor, its
close
method will not be called and therefore the
underlying FileOutputStream
will not be closed. Function
possibleFix
illustrates a possible fix: assign the result
of FileOutputStream
in variable and use it to construct
ObjectOutputStream
. Note that the result of
FileInputStream
constructor call will be lost if
IOException
happens and this exception goes out of the
method scope. If exception is handled, HANDLE_LEAK will be emitted.
import java.io.*
import java.lang.IllegalStateException
import java.util.zip.*
fun example(f: File, com: String) {
try {
val zip = ZipOutputStream(FileOutputStream(f))
.putNextEntry(ZipEntry("putNextEntry may throw exception"))
zip.close()
zip} catch (ex: Exception) {
throw IllegalStateException(ex.message)
}
}
fun possibleFix(f: File, com: String) {
(FileOutputStream(f)).use { zip ->
ZipOutputStream.putNextEntry(ZipEntry("putNextEntry may throw exception"))
zip}
}
Function example
illustrates the defect: file output
stream was created for f
but it will not be closed if
putNextEntry
call raises an exception. Function
possibleFix
illustrates a possible fix: call
use
function which executes the given function block on
this resource and then closes it down correctly whether an exception is
thrown or not.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | High | true |
CXX | Quality | Major | High | true |
GO | Quality | Normal | Unknown | true |
KOTLIN | Quality | Major | High | true |
Path sensitive version of HANDLE_LEAK.
import java.io.*;
class HandleLeakTest {
private static void readAndPrint(InputStream s) throws IOException {
System.out.println(s.read());
}
public static void example(String source, boolean isFile) throws IOException {
final InputStream stream;
if (isFile) {
= new FileInputStream(source); // FileInputStream acquired
stream } else {
= new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
stream }
readAndPrint(stream);
if (!isFile) {
.close();
stream}
// leaked when the function terminates
}
public static void possibleIncorrectFix(String source, boolean isFile) throws IOException {
final InputStream stream;
if (isFile) {
= new FileInputStream(source); // FileInputStream acquired
stream } else {
= new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
stream }
readAndPrint(stream); // leaked after IOException is thrown
if (isFile) {
.close();
stream}
}
}
Function example
illustrates the defect: depending on
the function parameter isFile
, either the
!FileInputStream
or the !ByteArrayInputStream
is acquired. The !FileInputStream
should be closed before
function terminates, but the !ByteArrayInputStream
shouldn’t. The condition to determine if close
should be
called is incorrect. So, the !FileInputStream
leaks when
the example
function terminates. Function
possibleIncorrectFix
illustrates a possible but incorrect
fix: use correct condition to determine if close
should be
called. If you apply this naive solution, the Svace will still report a
HANDLE_LEAK.EX.EXCEPTION
warning. The fully correct fix is provided in the Java example for the
HANDLE_LEAK.EX.EXCEPTION
detector.
import java.io.*
@Throws(IOException::class)
fun handleStream(stream: InputStream) {
val chunk = ByteArray(4)
.read(chunk)
stream// do some stuff
}
fun example(source: String, isFile: Boolean) {
val stream = if (isFile) File(source).inputStream() else source.byteInputStream() // FileInputStream is acquired
(stream)
handleStreamif (!isFile) {
.close()
stream}
// leaked when the function terminates
}
fun possibleIncorrectFix(source: String, isFile: Boolean) {
val stream = if (isFile) File(source).inputStream() else source.byteInputStream() // FileInputStream is acquired
(stream) // leaked after IOException is thrown
handleStreamif (isFile) {
.close()
stream}
}
Function example
illustrates the defect: depending on
the function parameter isFile
, either the
!FileInputStream
or the !ByteArrayInputStream
is acquired. The !FileInputStream
should be closed before
function terminates, but the !ByteArrayInputStream
shouldn’t. The condition to determine if close
should be
called is incorrect. So, the !FileInputStream
leaks when
the example
function terminates. Function
possibleIncorrectFix
illustrates a possible but incorrect
fix: use correct condition to determine if close
should be
called. If you apply this naive solution, the Svace will still report a
HANDLE_LEAK.EX.EXCEPTION
warning. The fully correct fix is provided in the Kotlin example for the
HANDLE_LEAK.EX.EXCEPTION
detector.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Normal | Unknown | true |
KOTLIN | Quality | Critical | High | true |
Path sensitive version of HANDLE_LEAK.EXCEPTION.
import java.io.*;
class HandleLeakTest {
private static void printAndRead(InputStream s) throws IOException {
System.out.println(s.read());
}
public static void example(String source, boolean isFile) throws IOException {
final InputStream stream;
if (isFile) {
= new FileInputStream(source); // FileInputStream is acquired
stream } else {
= new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
stream }
printAndRead(stream); // leaked after IOException is thrown
if (isFile) {
.close();
stream}
}
public static void possibleFix(String source, boolean isFile) {
InputStream stream = null;
try {
if (isFile) {
= new FileInputStream(source); // FileInputStream is acquired
stream } else {
= new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
stream }
// do some stuff
} catch (IOException e) {
// handle exception
} finally {
if (stream != null && isFile) {
try {
.close(); // FileInputStream is closed
stream} catch (IOException e) { /* ... */ }
}
}
}
}
Function example
illustrates the defect: depending on
the function parameter isFile
, either the
!FileInputStream
or the !ByteArrayInputStream
is acquired. The !FileInputStream
should be closed before
function terminates, but the !ByteArrayInputStream
shouldn’t. The condition to determine if close
should be
called is correct, but the readAndPrint
call may cause an
exception. So, the !FileInputStream
leaks when the
readAndPrint
call raises an IOException
.
+Function possibleFix
illustrates a possible fix: handle
all possible exceptions and release the stream
in a finally
block.
import java.io.*
@Throws(IOException::class)
fun handleStream(stream: InputStream) {
val chunk = ByteArray(4)
.read(chunk)
stream// do some stuff
}
fun example(source: String, isFile: Boolean) {
val stream = if (isFile) File(source).inputStream() else source.byteInputStream() // FileInputStream is acquired
(stream) // leaked after IOException is thrown
handleStreamif (isFile) {
.close()
stream}
}
fun possibleFix(source: String, isFile: Boolean) {
val stream = if (isFile) File(source).inputStream() else source.byteInputStream() // FileInputStream is acquired
.use { handleStream(it) } // FileInputStream is closed
stream}
Function example
illustrates the defect: depending on
the function parameter isFile
, either the
!FileInputStream
or the !ByteArrayInputStream
is acquired. The !FileInputStream
should be closed before
function terminates, but the !ByteArrayInputStream
shouldn’t. The condition to determine if close
should be
called is correct, but the handleStream
call may cause an
exception. So, the !FileInputStream
leaks when the
handleStream
call raises an IOException
.
Function possibleFix
illustrates a possible fix: call
use
function which executes the given function block on the
stream
and then closes it down correctly whether an
exception is thrown or not. Note that the unconditional call of
use
(and therefore close
call) is acceptable.
Closing a !ByteArrayInputStream
has no effect, but is not
prohibited.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Unknown | false |
KOTLIN | Quality | Normal | Unknown | false |
This checker is a subtype of HANDLE_LEAK
and it finds situations where a database is opened via
android.database.sqlite.SQLiteOpenHelper.getReadableDatabase()
and
android.database.sqlite.SQLiteOpenHelper.getWritableDatabase()
but hasn’t been properly closed.
The detector is under development at the moment.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | true |
CXX | Quality | Normal | Average | true |
GO | Quality | Normal | Average | true |
This checker finds situations where a closed file descriptor is closed again.
void foo() {
FILE *f = fopen("bar", "w");
// ...
(f);
fclose// ...
if (error()) {
(f);
fclose}
}
void dup_to_2(int fd) {
(2); // Now 2 is available.
close(fd); // 2 will be used.
dup}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Normal | High | true |
GO | Quality | Minor | Average | true |
KOTLIN | Quality | Normal | High | true |
This checker finds situations where a file descriptor, file handle or a socket descriptor are closed and there is an attempt to read from or write to it.
fun example(fileName: String) {
val buf = ByteArray(1024)
val stream = FileInputStream(fileName)
.read(buf)
stream// ...
.skip(10)
stream.close()
stream.read(buf)
stream}
fun possibleFix(fileName: String) {
val buf = ByteArray(1024)
(fileName).use {
FileInputStream.read(buf)
it// ...
.skip(10)
it.read(buf)
it}
}
Function example
illustrates the defect:
stream
was closed and then there was an attempt to read
from it. Function possibleFix
illustrates a possible fix:
handle streams with use
function call.
This Microsoft Windows-specific checker finds situations where a
Microsoft COM object is explicitly deallocated. Instead, it should be
automatically deallocated using a Release
method.
This checker find situations where number of loop iteration may be infinite because of integer overflow.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Low | false |
CXX | Quality | Major | Low | false |
GO | Quality | Major | Low | false |
void example1() {
unsigned char i;
for (i = 0; i <= 250; i += 10) { // Possible integer overflow, after `i = 250`.
();
bar}
}
void example2(unsigned len) {
int i = 0;
while (len > 0) {
-= 8; // Overflow if `len % 9 != 0`.
len }
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | VeryLow | false |
CXX | Quality | Major | VeryLow | false |
GO | Quality | Major | Unknown | false |
This checker finds situations where number of loop iteration may be more than it was intended. The loop is depended from value of string.
void func(char * pos) {
while (*pos != ' ') {
++;
pos}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | false |
CXX | Quality | Major | Unknown | false |
GO | Quality | Major | Unknown | false |
Checker INFINITE_LOOP.INT_OVERFLOW.ARRAY find situations with integer overflow that may lead to infinite loop execution and related to buffer access.
void with_tainted() {
unsigned char x = 0;
char* buf = getenv("HOME");
while (buf[x] == ' ') {
++x; //svace: emitted INFINITE_LOOP.INT_OVERFLOW.ARRAY
}
}
Here if first 256 bytes of buffer buf
do not contain
space the variable
x
will overflow which lead
to infinite loop.
The checker emits warnings only for tainted buffers (from external sources). For other buffer subtype INFINITE_LOOP.INT_OVERFLOW.ARRAY.STRICT is emitted.
char buf[1000];
void foo() {
unsigned char x = 0;
while (buf[x] == ' ') {
++x;
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Unknown | false |
CXX | Quality | Major | Unknown | false |
GO | Quality | Critical | VeryLow | false |
Checker finds suspicious situations where loop execution is bounded only by array data.
Example:
char buf[10];
void example() {
char x = 0;
while (buf[x] == ' ') {
++x; // BUFFER_OVERFLOW.LOOP
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker finds arithmetic operations with integer overflows when the result of that arithmetic operation is too big or too small to be represented as a value of the operation’s result type.
void print_integer_overflow(void) {
/* Result of the expression is -1073741827 instead of the expected 3221225469 for 32-bit integer type */
("%d\n", (INT_MAX / 2) * 3);
printf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
CSHARP | Quality | Minor | Unknown | true |
This checker finds situations where the value of an arithmetic expression might overflow before the result is widened to a larger data type.
long mult(int x, int y) {
return (x * y);
}
...
= mult(0x7FFFFFFF, 2); /* Expected 0xFFFFFFFE but result is -2. */
z ...
The multiplication above should be performed after widening the arguments:
long mult(int x, int y) {
return ((long)x * (long)y);
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Average | false |
CXX | Quality | Major | Average | false |
KOTLIN | Quality | Normal | Average | true |
The checker finds situations where value from external source is used in arithmetic operation without checking its range. It potentially may lead to integer overflow.
int add_to_str(const char *str) {
int index = atoi(str);
int res = index + 500; // Potential integer overflow.
return res;
}
fun example(number: String): Int {
val num = number.toInt()
return num - 1
}
fun possibleFix(number: String): Int {
.toIntOrNull()?.let {
numbertry {
return Math.subtractExact(it, 1)
} catch (e: ArithmeticException) {
throw e
}
}
throw IllegalStateException()
}
Function example
illustrates the defect: string
number
is cast to integer which is used in arithmetic
subtraction without checking its range. Function
possibleFix
illustrates a possible fix: use
Math
library when working with values which are potentially
from external source. Also it’s recommended to use safe
toIntOrNull
instead toInt
function.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | false |
CXX | Quality | Normal | Average | false |
GO | Quality | Critical | High | false |
The checker finds situations where value from external source is passed to variable with smaller type size.
unsigned i;
("%3x", &i);
scanfunsigned short h = i; // Potential loss of higher bits.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | false |
CXX | Quality | Normal | Average | false |
GO | Quality | Normal | Unknown | false |
The checker finds situations where potential integer overflow is possible for result of bit-manipulation operations.
void foo(unsigned short a);
#define INVERT_BYTES_16(X) ( \
( ((X) & 0xFF00) >> 8 ) \
| ( ((X) & 0x00FF) << 8 ) \
)
void bar(unsigned short b) {
(INVERT_BYTES_16(b) + 1);
foo}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | false |
CXX | Quality | Normal | Average | false |
GO | Quality | Normal | VeryLow | false |
The checker detects cases where the subtraction is performed on an unsigned value which can be 0 (or small enough), and, therefore the subtraction result may underflow, and it is passed to a function as its sensitive unsigned integer data argument.
void example(char* dst, char* str) {
size_t len = strlen(str);
(dst, str, len - 1);
memcpy}
This detector finds situations where an integer variable is compared with some constant value and the result of arithmetic operations on this integer variable may overflow. The comparison identify the bounds of the possible value of this variable.
fun example(n: Int) = if (n > 100000) n * 1000000 / 8 else 0
fun possibleFix(n: Int) = if (n > 100000) (n as Long) * 1000000 / 8 else 0
Function example
illustrates the defect: n
is compared with 10^5 and after that n
is multiplied by
10^6. The result of the multiplication is greater than the maximum value
an instance of Int
can have. Function
possibleFix
illustrates a possible fix: cast n
to Long
before multiplication.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Undefined | Low | false |
CXX | Quality | Undefined | Low | false |
GO | Quality | Undefined | Low | false |
The checker looks for situations where sensitive data may occur in logs or be visible.
char *password = getpass();
(password); printf
For the example above specification for getpass must be added:
char *getpass() {
char *ret;
(&ret);
sf_overwrite(ret);
sf_password_setreturn ret;
}
The checker may detect sensitive data by name of used variables
(passwd
, pwd
, privkey
, etc.).
char *pwd = "123";
(pwd); // Leak. log
Option SENSITIVE_NAME_REGEX allows changing regex for name.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Undefined | Low | false |
CXX | Quality | Undefined | Low | false |
GO | Quality | Undefined | Low | false |
Checker finds situations where hardcoded password is passed to functions manipulating with passwords.
= []byte("0123456789abcdef0123456789abcdef")
var pass
() {
func main,_ := aes.NewCipher(pass) // Using of hardcoded password.
block}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Security | Critical | Unknown | false |
CSHARP | Security | Minor | Unknown | false |
This checker reports various cases of insecure usages of shell commands execution or dynamic libraries loading when attacker is able to influence the environment of the running application.
const char *command = "some_command";
(command); system
In the example above if the attacker is able to modify the PATH
environment variable where some_command
is searched he may
set it in such way that allows to run his own malicious application
instead of the intended one. The problem can be fixed as follows:
const char *command = "/path/to/some_command"; // Specify absolute path to command.
(command); system
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Critical | Unknown | false |
CXX | Quality | Critical | Unknown | false |
KOTLIN | Quality | Critical | Unknown | false |
CSHARP | Quality | Major | High | false |
This checker finds code that may result in two or more threads are waiting for each other, holding locks needed for the other to resume execution.
pthread_mutex_t *a, *b;
void thread1() {
(a);
pthread_mutex_lock(b);
pthread_mutex_lock// ...
}
void thread2() {
(b);
pthread_mutex_lock(a);
pthread_mutex_lock// ...
}
class DeadlockTest {
private Object lock;
public synchronized void direct() {
synchronized(lock) {
// ...
}
}
public void reverse() {
synchronized(lock) {
synchronized(this) {
// ...
}
}
}
}
class DeadlockExample() {
var l: Any
lateinit
@Synchronized
fun direct(): Unit {
(l) {
synchronized// ...
}
}
fun reverse(): Unit {
(l) {
synchronized(this) {
synchronized// ...
}
}
}
/*
fun reverseCorrect(): Unit {
synchronized(this) {
synchronized(l) {
// ...
}
}
}
*/
}
Functions direct
and reverse
illustrate the
defect: if there are two thread working with the same instance of
DeadlockExample
class, and one thread executing
direct
function acquires this
lock and tries
to acquire l
lock, while another thread executing
reverse
function acquires l
lock and tries to
acquire this
lock, then both threads will wait indefinitely
for one of them to release the lock. Possible fix: use
reverseCorrect
function instead of
reverse
.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | High | true |
CXX | Quality | Major | High | true |
The checker DOUBLE_LOCK finds situations where a lock is acquired twice in succession by the same thread (without getting released).
void proc(pthread_mutex_t* mut, int x) {
(mut);
pthread_mutex_lockif (x)
(mut);
pthread_mutex_unlock
(mut); // DOUBLE_LOCK: pthread_mutex_unlock() might not have been called.
pthread_mutex_lock// ...
(mut);
pthread_mutex_unlock}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | High | true |
CXX | Quality | Major | High | true |
GO | Quality | Minor | High | true |
This checker finds situations where a thread acquires a lock during function execution and leaves it locked on some paths to the exit from the function, but the function also unlocks it on some other paths.
void test(pthread_mutex_t* mut, int flag) {
(mut);
pthread_mutex_lockif (flag) {
return; // `mut` isn't unlocked on this path.
}
(mut);
pthread_mutex_unlock}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker finds situations where a local variable allocated on stack is used as a synchronization primitive. Since each thread has its own stack such locks can’t be used for inter-thread synchronization.
void foo() {
pthread_mutex_t m;
(&m); // Locking stack variable.
pthread_mutex_lock}
class AutoLock {
public:
(pthread_mutex_t *_m) {
AutoLock= _m;
m }
() {}
AutoLock
~AutoLock() {
(m);
pthread_mutex_unlock}
void lock() {
(m);
pthread_mutex_lock}
private:
pthread_mutex_t *m;
};
pthread_mutex_t m_glob; // It should be used.
void bar() {
;
AutoLock l.lock(); // Variable `l` is on stack. `l.m` is also on stack.
l}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker finds situations where a blocking function is called inside a critical section. As a result, all threads have to wait for the blocking function to return, not just the thread that called it.
int proc(pthread_mutex_t* mut, pthread_mutex_t* socket) {
(mut); // Lock.
pthread_mutex_lockint res = accept(socket); // This function might take a lot of time.
(mut);
pthread_mutex_unlockreturn res;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Unknown | true |
This checker finds cases of non-atomic usage of non-constant shared data where critical section is not sufficient to protect a variable.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | false |
KOTLIN | Quality | Major | Unknown | false |
This detector collects statistics in a single file of variables reads and writes inside and outside critical sections. Based on this statistical data it detects possible usages of variables outside critical sections that may lead to data races.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
KOTLIN | Quality | Major | Unknown | true |
This detector collects statistics for each access to the field.
Statistics store whether access to the field was under lock and under
what lock in particular. By handling the statistics the detector decides
if some accesses to field may cause race condition. Constructors and
equals
, hashCode
and clone
methods are excluded from consideration.
The warning will be emitted by this detector if:
NO_LOCK.STAT.EX.THRESHOLD is equal to 80% by default. NO_LOCK.STAT.EX.TWO_OF_THREE is false by default.
Note that NO_LOCK.STAT.EX.TWO_OF_THREE is true for all examples below.
public class NoLockStatExample {
private int a;
private Object l = new Object();
public Example() {
= 0; // Ignore this access to field `a` in constructor.
a }
public synchronized void bar() {
++; // Access to field `a` under `this` lock.
a}
public void foo(int b) {
synchronized(l) {
+= b; // First access to field `a` under `l` lock.
a += 2; // Second access to field `a` under `l` lock.
a }
}
/*
public void barCorrect() {
synchronized(l) {
a++; // Access to field `a` under `this` lock.
}
}
*/
}
Function bar
illustrates the defect: field
a
is accessed under this
lock, while in most
cases (2 of 3) this field is accessed under l
lock in
foo
function. This situation may cause race condition.
Possible fix: use barCorrect
function instead of
bar
, field a
is accessed under l
lock in barCorrect
function.
class NoLockStatExample {
private var a: Int = 0
var l: Any
lateinit
@Synchronized
fun bar() {
++ // Access to `a` under `this` lock.
a}
fun foo(b: Int) {
(l) {
synchronized+= b // Access to `a` under `l` lock.
a += 2 // Access to `a` under `l` lock.
a }
}
/*
fun barCorrect() {
synchronized(l) {
a++
}
}
*/
}
Function bar
illustrates the defect: property
a
is accessed under this
lock, while in most
cases (2 of 3) this property is accessed under l
lock in
foo
function. This situation may cause race condition.
Possible fix: use barCorrect
function instead of
bar
, property a
is accessed under
l
lock in barCorrect
function.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Suppressed | Minor | Unknown | false |
Warning of this type is emitted where a call to function
mkstemp
is not preceded by a call to umask
,
which is necessary to restrict access rights to the newly created file
to its creator.
For most libc implementation function mkstemp
creates
file with correct permissions and this warning is not needed.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Suppressed | Minor | Unknown | false |
Even if function umask
was called before
mkstemp
, as checked by RACE.NO_UMASK, it’s possible that access
rights set by umask
are too permissive (for example, 777).
This warning is emitted if that is the case.
For most libc implementation function mkstemp
creates
file with correct permissions and this warning is not needed.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | false |
This checker finds possible cases of using inconsistent data while executing asynchronous-unsafe functions from signal handlers. According to Section 7.14.1.1 of the C Rationale [ISO/IEC 2003]:
When a signal occurs, the normal flow of control of a program is interrupted. If a signal occurs that is being trapped by a signal handler, that handler is invoked. When it is finished, execution continues at the point at which the signal occurred. This arrangement can cause problems if the signal handler invokes a library function that was being executed at the time of the signal.
The warning is emitted if signal handler calls any asynchronous-unsafe function. It is based on the SIG30-C rule from the CERT Secure Coding Standard.
char* info;
void handler(int signum) {
(info); // Free is an asynchronous unsafe function,
free// so the warning will be emitted here.
= NULL;
info }
int main(void) {
(SIGINT, handler); // Register `handler` as a handler.
signal// Skipped.
return 0;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | false |
This checker finds a rare case of race condition where a handler tries to reinstall a signal handler from itself. On systems that deinstall signal handler after signal delivery (SysV and Windows behavior) another signal can happen before the reinstallation code is run. This will lead to default signal handler call. On systems where signal handlers need to be explicitly deinstalled (BSD and Linux behavior), reinstallation of same signal handler doesn’t make sense.
The warning is emitted if a signal handler calls function
signal
for re-registering same handler again. It is based
on the SIG34-C
rule from the CERT Secure Coding Standard.
void handler(int signum) {
(signum, handler); // Call to `signal` from within `handler` to register.
signal// `handler` again; the warning is emitted here.
}
int main(void) {
(SIGINT, handler); // Register `handler` as a handler.
signal// Skipped.
return 0;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | false |
This checker detects race conditions that can be caused by accessing
or modifying shared objects in signal handlers. The only way to
guarantee that data is read in consistent state and remains in
consistent state after modification is to read and write only variables
of type volatile sig_atomic_t
inside of signal
handlers.
The warning is emitted if signal handler accesses or modifies global
data with the type other than volatile sig_atomic_t
. It is
based on the SIG31-C
rule from the CERT Secure Coding Standard.
volatile sig_atomic_t iflag = 0;
int kflag = 0;
void int_handler(int signum) {
= 2; // All ok, `iflag` is `volatile sig_atomic_t`.
iflag }
void kill_handler(int signum) {
= 3; // The warning is emitted: modifying shared object `kflag` in a signal handler.
kflag }
int main(void) {
(SIGINT, int_handler); // Handler is registered.
signal(SIGKILL, kill_handler); // Handler is registered.
signal// Skipped.
return 0;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | false |
This checker finds situations where a longjmp
function
is called from a signal handler, which may cause inconsistent data use.
Such calls can lead to problems similar to those discussed in SIGHANDLER.ASYNC_UNSAFE.
The warning is emitted if the signal handler function calls the
longjump
function. It is based on a SIG32-C
rule from the CERT Secure Coding Standard.
static jmp_buf env;
void handler(int signum) {
(env, 1); // Call to `longjump` from a signal handler.
longjmp}
int main(void) {
(SIGINT, handler); // Handler is registered.
signal// Skipped.
return 0;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | false |
This checker finds situations where undefined behavior may result
from calling function raise
from a signal handler.
According to C99, Section 7.14.1.1 [ISO/IEC 9899:1999]: If the signal
occurs as the result of calling the abort or raise function, the signal
handler shall not call the raise function.
The warning is emitted if function raise
is called from
a signal handler, but only if a signal with that handler is explicitly
raised using functions raise
or abort
somewhere in the program. It is based on the SIG33-C
rule from the CERT Secure Coding Standard.
void log_msg(int signum) {
// Skipped.
}
void handler(int signum) {
(SIGUSR1); // Handler for SIGINT recusively calls `raise`.
raise}
int main(void) {
(SIGUSR1, log_msg); // Handler is registered.
signal(SIGINT, handler); // Handler is registered.
signal// Skipped.
(SIGINT); // `raise` is explicitly called for SIGINT.
raise// Other code.
return 0;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | false |
This checker detects cases of undefined behavior caused by returning control from certain signal handlers that should instead terminate the program. According to Section 7.14.1.1 of the C standard, returning from SIGSEGV, SIGILL, or SIGFPE signal handlers leads to undefined behavior: If and when the function returns, if the value of sig is SIGFPE, SIGILL, SIGSEGV, or any other implementation-defined value corresponding to a computational exception, the behavior is undefined; otherwise, the program will resume execution at the point it was interrupted.
The warning is emitted if signal handler for SIGFPE, SIGILL or
SIGSEGV can return without calling the abort
function. It
is based on the SIG35-C
rule from the CERT Secure Coding Standard.
volatile sig_atomic_t flag;
void handler(int signum) {
= 1; // No call of `abort`.
flag }
int main(void) {
(SIGILL, handler); // Handler is registered.
signal// Skipped.
return 0;
}
Those warnings are developed special for C++ code.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Average | true |
This checker finds constructors (incl. copy constructor) that fail to initialize some of their class’s fields.
class MyData {
public:
();
MyDataint kind;
char ch;
int sum;
};
::MyData() : ch('q') {
MyDatathis->kind = 7; // Warning: field `sum` is not initialized.
}
Code detected by this checker doesn’t necessarily lead to reading of uninitialized memory, since fields that were not initialized in the constructor might be initialized later or never accessed without additional initialization.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker finds classes in which the destructor explicitly deallocates memory using a pointer, but the copy constructor doesn’t initialize this pointer or doesn’t exist. Not handling such pointer in copy constructors may lead to either deallocation via an uninitialized pointer, or to double deallocation of the same memory.
class MyData {
char* str;
int size;
public:
() {
MyData= (char*) malloc(10);
str = 10;
size }
(const MyData& a) {
MyData= a.size; // `str` is not handled.
size }
~MyData() {
(str); // Deallocate memory pointed to by `str`.
free}
};
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | true |
This checker finds classes where the destructor deallocates memory using a pointer, but the assignment operator doesn’t initialize this pointer or doesn’t exist. This checker is similar to UNINIT_HEAP.CCTOR, but checks assignment operators instead of copy constructors.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
Check for undefined behavior related to the order of initialization
of base classes such as calling a member function when base is not
initialized yet, dynamic_cast
of this
when
base is not initialized yet and calling of typeid()
with
this
as argument when base is not initialized yet.
In the example below f()
is a member function of class
B
. Constructor of class B
has base initializer
A(int a)
and uses return value of method f()
as an argument. The call f()
would be evaluated before
A
initialization is complete which is undefined
behavior.
class A {
public:
(int a);
A};
class B : public A {
public:
int f();
() : A(f()) {}
B};
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | VeryHigh | true |
Warnings of this type are emitted for situations where memory is
deallocated in a way that is incompatible with how it was allocated. For
example, usage of C functions malloc
/free
must
not be mixed with C++ operators
new
/delete
.
char* buf = (char*) malloc(strlen(str) + 12);
//...
delete buf; // HEAP_INCOMPATIBLE.FREE is emitted here. The bug can be fixed
// by using function `free` for deallocation instead of `delete`.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
A subtype of HEAP_INCOMPATIBLE.FREE. Detects
situations where memory was allocated using array allocation operator
new[]
, but deallocated using operator delete
(instead of the correct delete[]
). Using incorrect
deallocation method can lead to undefined behavior.
char* buf = new char[SIZE_MAX];
// ...
delete buf; // HEAP_INCOMPATIBLE.ARRAY warning is emitted here. The bug can be
// fixed by using `delete[]`.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker finds classes with a constructor that allocates memory
using a function (operator) that is incompatible with the function used
by the destructor for deallocation. Examples of incompatible function
pairs: new
/free
,
malloc
/delete
,
new[]
/delete
,
new
/delete[]
.
class C {
int* buf;
public:
() {
C= new int[10];
buf }
~C() {
(buf); // Here svace fires the warning.
free}
};
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | true |
This checker finds classes with a constructor that explicitly allocates memory, but which isn’t deallocated in the destructor.
class C1 {
int* buf;
public:
() { buf = new int[10]; }
C1~C1();
};
::~C1() { // Warning: missing `delete[]` for `buf`.
C1}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | true |
This warning is emitted when an internal string buffer (returned by c_str()) of an STL string escapes its scope.
int func() {
const char* p;
{
std::string s("Hello");
= s.c_str();
p } // Scope of `s` ends, destructor is invoked.
// Memory referenced by `p` is no longer valid here.
return *p; // Emit DEAD_STRING_REF.
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | true |
Finds implementations of operator=
that may misbehave in
case of self-assignments. Only methods containing dangerous operations
would be reported (which means no warning will be emitted for just
assigning simple types or when using copy-and-swap idiom).
class MyData {
public:
& operator=(const MyData& a);
MyDataprivate:
char data[4];
};
& MyData::operator=(const MyData& a) {
MyData(this->data, a.data, 4); // Warning: `a` wasn't checked for equality to `this`, while code produces UB if `a` equals `*this`.
memcpyreturn *this;
}
The problem can be fixed as follows:
& MyData::operator=(const MyData& a) {
MyDataif (&a == this)
return *this;
(this->data, a.data, 4); // No warning.
memcpyreturn *this;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | High | true |
Finds situations where the assignment operator defies syntactical correctness for equality in C++.
class MyData {
public:
void operator=(const MyData& a);
private:
int kind;
};
void MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPES.MINOR (hidden by default)
this->kind = a.kind;
} // Return type is void, meaning chaining equalities like `a = b = c` or calling methods like `(a = b).myMethod()` won't work.
class MyData {
public:
const MyData& operator=(const MyData& a);
private:
int kind;
};
const MyData& MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPES.MINOR (hidden by default)
this->kind = a.kind;
return *this; // Return type is const, meaning calling non-const methods like `(a = b).myMethod()` won't work.
}
class MyData {
public:
int operator=(const MyData& a);
& operator=(int a);
MyDataprivate:
int kind;
};
int MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPES
this->kind = a.kind;
return kind; // Returns different type, meaning chaining equalities like `a = b = c` may not work or call different `operator=` (one that takes int while expecting one that takes object).
}
class MyData {
public:
& operator=(const MyData& a);
MyDataprivate:
int kind;
};
const MyData& MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS
this->kind = a.kind;
return a; // Returns other object, meaning chaining equalities like `a = b = c` or calling methods like `(a = b).myMethod()` may lead to unexpected results (making theoretically possible that `a != b` or calling method on `b` instead of `a`).
}
class MyData {
public:
& operator=(const MyData& a);
MyDataprivate:
int kind;
};
const MyData& MyData::operator=(const MyData& a) {
this->kind = a.kind;
return *this; // Correct, no warning.
}
class MyData {
public:
& operator=(const MyData& a);
MyData& operator=(int a);
MyDataprivate:
int kind;
};
const MyData& MyData::operator=(const MyData& a) {
return *this = a.kind; // Correct, assuming another assignment operator will return a reference to `this`.
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Undefined | Unknown | true |
A class has dynamically allocated data members but does not define a copy constructor or an assignment operator. In this case, whenever an object is copied by value, dynamically allocated members will be destroyed as soon as at least one of the copies is deleted, and other copies would maintain and possibly dereference a stale pointer.
In the code below, the following sequence of events leads to a dereference of a dead pointer:
b
and thus free memory pointed by
both b.p
and a.p
with explicitly defined
destructor;*(a.p)
and dereference a pointer to a
destructed object.class C {
int *p;
public:
() { p = new int; }
C~C() { delete p; }
void setVal(int x) { *p = x; }
int getVal() const { return *p; }
};
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
Checks for exception throwing in copy constructor/assignment operator which can lead to resource leaks or undefined behavior.
In the following example if exception std::bad_alloc
is
thrown then some of the Bundle::KeyInfo
class fields such
as impl_->name_
are initialized while some such as
impl_->own_
aren’t. This leads to the assigned object
being partially initialized and having an indeterminate state.
::KeyInfo& Bundle::KeyInfo::operator = (const Bundle::KeyInfo& k) {
Bundleif (this != &k) {
if (impl_->handle_ && impl_->own_)
(const_cast<bundle_keyval_t*>(impl_->handle_));
bundle_keyval_freeimpl_->handle_ = bundle_keyval_dup(k.impl_->handle_);
impl_->name_ = k.impl_->name_;
if (impl_->handle_ == nullptr)
throw std::bad_alloc();
impl_->own_ = true;
}
return *this;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Average | true |
This checker finds situations where STL iterators are used after being invalidated.
using namespace std;
int last(vector<int>& v) {
<int>::iterator it = v.begin();
vectorwhile (it != v.end())
++it;
return (*it);
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | true |
This checker finds situations where STL iterators are used for a different container.
using namespace std;
bool same(vector<int>& v1, vector<int>& v2) {
<int>::iterator it = v1.begin();
vector<int>::iterator it2 = v2.begin();
vectorreturn it == it2;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | High | true |
The checker finds situations where an object is used after it was moved. Moved-from objects are usually left in unspecified state. Usage of such objects leads to unspecified behavior.
std::string a = "Hello world";
std::string b = std::move(a); // Moved value from `a`. `a` is in unspecified state.
std::cout << a[0]; // Usage of `a`. USE_AFTER_MOVE emitted.
Moved-from object can be reinitialized and used again.
std::vector<int> a = {1, 2, 3, 4};
std::vector<int> b(std::move(a)); // Move from `a`.
.clear(); // `a` is reinitialized.
astd::cout << a.size(); // USE_AFTER_MOVE is NOT emitted.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Major | Unknown | true |
The result of a function call may differ depending on the current culture. Specify culture explicitly to choose the desired behavior.
float theirVersion = 0f;
if (parts.Length > 1)
= float.Parse(parts[1]); theirVersion
float theirVersion = 0f;
if (parts.Length > 1)
= float.Parse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture); theirVersion
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Major | Unknown | true |
The variable was checked for compatibility with given type (which means that it may be incompatible with it by contract) and then cast to it without check.
Here &&
is used instead of ||
, so
the cast will always fail.
if (!(task.Result is ResultResponse) &&
!(((ResultResponse)task.Result).Output is OutputRows))
{
throw new DriverInternalError("Expected rows " + task.Result);
}
Here ||
is used instead of &&
, so
the cast will fail if request["Values"]
is
List<string>
, but request["Names"]
is
not.
if (!(request["Names"] is List<string> || request["Values"] is List<string>))
return FailureResult();
RemoveRequestParamsNotForStorage(request);
<string> _names = (List<string>)request["Names"]; List
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
User input is used as code (e.g. compiled or evaluated) or as path to code.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | Unknown | true |
An assignment operator (=
) is used inside of condition
expression. It is probably a typo for equality operator
(==
).
Although using =
inside of condition is not necessarily
a bug, this warning is useful for immediate bug fixing during
coding.
if (a = b || a == c)
{
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | Unknown | true |
User input is concatenated to database connection string, which
allows the attacker to override connection parameters.
DbConnectionStringBuilder
should be used to create the
connection string.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
User input is used as the redirect URI in an HTTP response. It may be used to redirect the user to a malicious website.
Here a user can be redirected to http://malicious.com
by
following a http://trusted.com/something?site=malicious.com
URL sent by the attackers.
public class RedirectTest : ApiController
{
public HttpResponseMessage GetSomething(string site)
{
var response = Request.CreateResponse<object>(HttpStatusCode.Moved, null);
var uri = new Uri(site);
.Headers.Location = uri;
responsereturn response;
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The result of as
operator is dereferenced without
check.
var auctioneer = chr.Map.GetObject(auctioneerId) as NPC;
.Instance.AuctionHello(chr, auctioneer); AuctionMgr
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The result of conditional access operator (?.
) is
dereferenced without check.
= window?.Size;
Size2D windowSize = new Rectangle(0, 0, windowSize.Width, windowSize.Height); Rectangle ret
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The catch
clause is empty.
Although it’s not an error, it’s considered an antipattern.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The interface is empty.
Although it’s not an error, it’s considered an antipattern.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | false |
CSHARP | Quality | Undefined | Unknown | false |
Floating-point numbers are compared for precise equality.
Floating point mathematics is not exact, so instead of comparing
floating-point values for precise equality, they should be compared with
precision. E.g. Math.Abs(a - b) < 0.0001
instead of
a == b
.
public static Boolean operator ==(ComplexNumber c1, ComplexNumber c2)
{
bool bEqual = false;
if ((c1._realPart == c2._realPart) &&
(c1._imaginaryPart == c2._imaginaryPart))
= true;
bEqual return bEqual;
}
The warning has .CMP_EQ subtype if the code uses <=
or >=
operator.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
Local variable or method parameter name “hides” other variable or class member with the same name.
Although this is not a bug itself, it may confuse the developer.
public partial class AddAccountWizardForm : Form
{
...
private Step m_CurrStep = Step.Screen1;
...
private bool ValidateFields(Step m_CurrStep)
{
...
}
...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
A number of different methods share the same implementation.
public static object GetCharArrayValue(_Array arr) {
return arr.NativeType.GetValue(arr._memHolder, arr, 0, false);
}
public static object GetWCharArrayValue(_Array arr) {
return arr.NativeType.GetValue(arr._memHolder, arr, 0, false);
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
Switch statement doesn’t handle all possible argument values.
public enum TraceEventKind {
,
FrameEnter,
FrameExit,
ThreadExit,
TracePoint,
Exception
ExceptionUnwind}
/* ... */
switch (kind) {
case Debugging.TraceEventKind.FrameEnter: traceEvent = "call"; break;
case Debugging.TraceEventKind.TracePoint: traceEvent = "line"; break;
case Debugging.TraceEventKind.Exception:
= "exception";
traceEvent object pyException = PythonExceptions.ToPython((Exception)payload);
object pyType = ((IPythonObject)pyException).PythonType;
= PythonTuple.MakeTuple(pyType, pyException, null);
args break;
case Debugging.TraceEventKind.FrameExit:
= "return";
traceEvent = payload;
args break;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
Static field is used before its initialization.
public static readonly WeakReference WeakMissingConstant = new WeakReference(
);
StrongMissingConstantprivate static readonly object StrongMissingConstant = new object();
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
At least one argument of ReferenceEquals
call is not of
a reference type.
public struct Polynomial<Variable, Expression>
{
/* ... */
}
/* ... */
<BoxedVariable<Variable>, BoxedExpression> pol;
Polynomial/* ... */
.Assume(!object.ReferenceEquals(pol, null)); Contract
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The loop contains unconditional break
or
return
, so it will always have only one iteration.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
Common constants are defined explicitly, instead of using library constants.
In the following code sample 6.28318531
should be
changed to 2 * Math.PI
.
if (Math.Abs(InitialAngle - LastAngle) > 6.28318531) {
// ...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The exception is created, but throw
operator is
missing.
try
{
= GetMxRecords(domain);
mxRecords }
catch
{
new Exception("Can't connect to DNS server.");
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The loop condition may be changed only in another thread, and the
field that should be changed doesn’t have volatile
modifier, so the field access may be optimized out.
In the following code sample the loop doesn’t modify fields used in
its condition, so the DoneCount
may be retrieved only once
because of optimizations, making the loop infinite. The field needs a
volatile
modifier to prevent such optimization.
class DictThreadGlobalState {
public int DoneCount;
public List<Thread> Threads = new List<Thread>();
/* ... */
}
/* ... */
while (globalState.DoneCount != globalState.Threads.Count) {
// Wait for threads to get back to start point.
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | Unknown | true |
The comparison contains both integer and floating point values.
public void test(int i)
{
if (i > 10.5)
return;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The object implementing IDisposable
interface is
returned inside of using
block. Returned object will be
disposed.
public static MemoryStream SerializeToStream(object o)
{
using (var stream = new MemoryStream())
{
.Serialize(stream, o);
Formatterreturn stream;
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The method always returns the same value.
public bool Matches(byte[] bytes)
{
if (CheckValid())
return false;
for (var i = 0; i < m_MaskBytes.Length; i++)
{
var bte = bytes[i];
if (BanMgr.Matches(m_MaskBytes[i], bte))
return false;
}
return false;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | Unknown | true |
A variable or field is assigned to itself.
class C {
string x;
void f() {
= x;
x }
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Major | Unknown | true |
C#-only subtype of SIMILAR_BRANCHES where branches differ only by comments.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Major | Unknown | true |
C#-only subtype of SIMILAR_BRANCHES
where an if
-else
chain or a
switch
statement contains more than one pair of identical
branches.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | Unknown | true |
C#-only subtype of SIMILAR_BRANCHES
where a case
section has the same body as the
default
section.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
String concatenation in loop is inefficient, because it creates a new
string at each iteration. StringBuilder
or
string.Join
should be used instead.
var pluginStates = string.Format(
"plugin states: ({0} in total)", this.otherStates.Length);
foreach (var state in this.otherStates)
{
var str = state.ToString();
+= Environment.NewLine + str;
pluginStates }
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The format string uses more format items than supplied or does not use some supplied arguments.
Here the format string contains two format items, but only one argument was provided (and it really should be the third format item).
string.Format(
"[AGENT INVENTORY]: Error in SendInventoryAsync() for {0} with folder ID {1}. Exception ", e));
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Normal | Unknown | true |
C#-specific subtype of UNREACHABLE_CODE.EXCEPTION for
situations where a return
statement is unreachable because
the previous operation always throws an exception. Some of such warnings
are useless because the exception is desired behavior and the
return
is required by the compiler.
public static BluetoothOppServer StartServer(string FilePath)
{
if (BluetoothAdapter.IsBluetoothEnabled && Globals.IsInitialize)
{
= new BluetoothOppServer();
_instance int ret = _impl.StartServer(FilePath);
if (ret != (int)BluetoothError.None)
.ThrowBluetoothException(ret);
BluetoothErrorFactoryreturn _instance;
}
else
.ThrowBluetoothException((int)BluetoothError.NotEnabled);
BluetoothErrorFactoryreturn null;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Normal | Unknown | true |
C#-specific Subtype of UNREACHABLE_CODE for situations where a
throw
statement or expression is unreachable.
= typeof(T);
Type type if (null == type)
throw new ArgumentNullException("type");
Here platform.IsValid()
ensures that
platform
is a valid enumeration value, and
switch
handles all possible enumeration values, so
default
is unreachable. However, the default
case should not be removed to detect possible future errors if a new
platform is added and IsValid
is updated but
switch
is not.
if (!platform.IsValid())
= Platform.AnyCpu;
platform
switch (platform)
{
case Platform.Arm64:
= (Machine)0xAA64; //Machine.Arm64; https://github.com/dotnet/roslyn/issues/25185
machine break;
case Platform.Arm:
= Machine.ArmThumb2;
machine break;
case Platform.X64:
= Machine.Amd64;
machine break;
case Platform.Itanium:
= Machine.IA64;
machine break;
case Platform.X86:
= Machine.I386;
machine break;
case Platform.AnyCpu:
case Platform.AnyCpu32BitPreferred:
= Machine.Unknown;
machine break;
default:
throw ExceptionUtilities.UnexpectedValue(platform);
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Major | Unknown | true |
C#-specific subtype of UNREACHABLE_CODE for situations where a
code fragment is unreachable because the condition is a
false
literal.
string data;
if (false)
= null;
data else
= "foo"; data
This tag is not a warning subtype, but a prefix of warning message. It means that the unreachable operation is not visible in the source code, because it was added by the compiler. The expression in the warning text is the original source code expression which was transformed.
Here the compiler has transformed the
resources ?? GetResources(v)
into
resources != null ? resources : GetResources(v)
, and the
resources
expression in the true branch is unreachable,
because resources != null
is always false.
<string, object> resources = null;
Dictionaryif (v != null)
{
= resources ?? GetResources(v); // UNREACHABLE_CODE [IMPLICIT] Execution cannot reach code starting from resources statement.
resources }
C# subtypes may be applied together in the following order: .EXCEPTION.{RETURN, DEFENCE_CODE, EXPLICIT}.TEST
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Security | Critical | Unknown | true |
This checker detects serialization of fields which can be used for code or data injection, such as delegates or unmanaged references.
In this code sample the event’s delegate field is serialized, so changing it in the serialized object can lead to calling some other event handler.
[Serializable]
class WrapEvent
{
public event EventHandler OnRun;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
The expression value may have value type, but is compared with
null
. A value type cannot contain null
value.
Here aliasSymbols
is
ImmutableArray<IAliasSymbol>
, which is a value type.
It should be compared with default
.
var aliasSymbols = await GetAliasSymbolsAsync(document, semanticModel, nonAliasReferences, cancellationToken).ConfigureAwait(false);
if (aliasSymbols == null)
{
return ImmutableArray<ReferenceLocation>.Empty;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
Virtual method of object is called inside its constructor. It may lead to inconsistent state, because the virtual method may be redefined in the child and may use some child-specific data that is not initialized yet.
In the following code sample virtual method Commit
is
called inside the constructor of InvariantSubroutine
class.
public InvariantSubroutine(
<
MethodCache, Parameter, Type, Method, Field, Property,
Local, Attribute, Assembly> methodCache,
Event, Type associatedType)
Subroutine inherited: base(methodCache)
{
.Requires(methodCache != null);
Contractthis.AddSuccessor(this.Entry, "entry", this.Exit);
this.AddBaseInvariant(this.Entry, this.Exit, inherited);
this.Commit();
}
Subtype .PROPERTY means that a virtual property is used in constructor.
Subtype .POTENTIAL means that the method has no known overrides.
Subtype .OVERRIDE means that the method overrides a method from the base class.
A warning can have any combination of these subtypes in the following order: .PROPERTY.OVERRIDE.POTENTIAL.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
After some value is retrieved by as
operator, the
original value is checked for null
instead of the new
one.
var right = other as PredicateNullness;
if (other != null) // Should be `if (right != null)`.
{
if (this.value == right.value)
{
return this;
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CSHARP | Quality | Normal | Unknown | true |
Static field is accessed under lock, but the lock argument is not static. The lock may be entered by multiple threads simultaneously.
private static int _Index;
private readonly Dictionary<string, int> _counters;
public int GetTickIndex(string name) {
lock (_counters) {
if (!_counters.TryGetValue(name, out index)) {
= _Index++;
index .Add(name, index);
_counters}
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Security | Critical | Unknown | true |
User-provided data are used to form an XPath query without neutralization of special elements. It can be used by an attacker to execute arbitrary XPath query.
In this code sample parameter state
is concatenated to
XPath query.
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["state"] != null)
FindSalesPerson(Request.QueryString["state"]);
}
private void FindSalesPerson(string state)
{
= new XmlDocument();
XmlDocument xDoc .LoadXml(xml);
xDoc= xDoc
XmlNodeList list .SelectNodes("//salesperson[state='" + state + "']");
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
Interprocedural detector for NUnit framework based tests, that checks if at least one field of every checked object have been also checked. So, it is required to check not only a reference (ex. by type or null-value), but also fields of the object.
In this code sample at least one field of variable
account
requires a check because the object itself was
checked (twice).
[Test] // Indicates that it is a test method
public void MISSING_ASSERTION_RETURN_VALUE()
{
= new Account();
Account account // CSCC-WARN{{TCCHECK.NUNIT.MISSING_ASSERTION.RETURN_VALUE
// At least one field of object account should be also checked
// if object itself was checked}}
.IsInstanceOf<Account>(account, "Should return Account instance"); // <---
Assert.IsNotNull(account, "Failed to create Account instance");
Assert.Dispose();
account}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
Interprocedural detector for NUnit framework based tests, that checks
if every return value or parameter of method from user-defined list have
been checked with assertion. List of functions is declared in svace
config directory with a JSON file
white-list-tccheck-nunit.json
. The syntax of the file is
like follows:
{
"classname" : "function_classname",
"function" : "function_name",
"checkReturn" : true,
"checkParameters" : [0,2]
}
"classname"
: the string representing a name of class.
It is necessary to specify the fully qualified name of class with its
namespace. The value of parameter will be concatenated with a
"function"
parameter ("classname"."function"
)
and compared with the name of every called method in every test."function"
: the string representing a name of method.
It is necessary to specify the short name without containing type and
namespace. The value of parameter will be concatenated with a
"classname"
parameter ("classname"."function"
)
and compared with the name of every called method in every test."checkReturn"
: the parameter indicates if a return
value of function must be checked with an assertion. Possible values:
true
, false
. The parameter is optional,
false
is the default value."checkParameters"
: an array of integers, that determine
which parameters of the method must be checked with an assertion. The
field "checkParameters"
is optional, no method parameters
will be checked if it is omitted. The first parameter index of regular
method equals to zero. Parameter this
in an extension
method has index equal to -1.In the code sample the return value of method
Account.CreateAccount
must be checked, because it is
specified in the white-list.
[Test] // Indicates that it is a test method
public void MISSING_ASSERTION_USER_DEFINED()
{
// CSCC-WARN{{TCCHECK.NUNIT.MISSING_ASSERTION.USER_DEFINED
// Return value of CreateAccount should be checked because it is in the white-list}}
= Account.CreateAccount(); // <---
Account account string userName = "CSAPI_USER";
.UserName = userName;
account.True(account.UserName.Length > 0, "Failed to add UserName");
Assert.True(account.UserName.CompareTo(userName) == 0, "Failed to add UserName");
Assert}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
Interprocedural detector for NUnit framework that checks whether at least one assertion exists in each test method.
In the example below all lines with assertions are commented out, so test method has no assertions. It is suggested to uncomment or add necessary checks or delete useless test.
[Test] // Indicates that it is a test method
// CSCC-WARN{{TCCHECK.NUNIT.NO_ASSERTION
// Method NO_ASSERTION marked as a test with attribute Test has no assertion
// for validation}}
public void NO_ASSERTION() // <---
{
= Account.CreateAccount();
Account account string userName = "CSAPI_USER_ID";
.UserName = userName;
account
var id = account.AccountId;
// Assert.IsInstanceOf<int>(id, "Failed to get valid AccountId);
// Assert.True(account.AccountId > 0, "Failed to get valid AccountId");
// Assert.True(account.AccountId == dbid,
// "Failed to get valid AccountId. Expected: "+dbid+ " .Actual: "+account.AccountId);
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Minor | Unknown | true |
Interprocedural detector for NUnit framework that detects faulty assertions always evaluating to be true or false. This can happen if a program reaches an assertion and there is nothing left to check, e.g. when all alternatives were already sorted out by the previous code.
In the example below the length of the variable display
is checked to be greater than zero, but its value was assigned in the
same method before and equals to constant string
"MY_DISPLAY"
, so assertions always has true
value. Perhaps the value of account.DisplayName.Length
should be checked instead.
[Test] // Indicates that it is a test method
public void USELESS_ASSERTION()
{
= Account.CreateAccount();
Account account .IsNotNull(account, "Failed to create Account instance");
Assertstring display = "MY_DISPLAY";
.DisplayName = display;
account// CSCC-WARN{{TCCHECK.NUNIT.USELESS_ASSERTION
// Assertion is useless, because checked condition is always met}}
.True(display.Length > 0, "Failed to add DisplayName"); // <---
Assert.True(account.DisplayName.CompareTo(display) == 0, "Failed to add DisplayName");
Assert}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
A thread static field is declared with an initializer. The initialization will happen only in one thread.
[ThreadStatic]
public static List<object> _trace = new List<object>();
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CSHARP | Quality | Critical | High | true |
[ThreadStatic]
attribute is used on a non-static field
and has no effect.
[ThreadStatic]
private bool _traceListenerSuspended;
Those checkers are developed for searching error specific for Go language.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Undefined | Unknown | true |
In Golang most uninitialized errors were eliminated by language design, but it is still possible for global variables. This checker finds situation where global variable is used without any initialization.
var gvar1 map[string]string
var gvar2 map[string]string
func init() {
= make(map[string]string)
gvar1 // `gvar2` is not initialized.
}
func use(name string, value string) {
[name] = value
gvar1[name] = value // Error.
gvar2}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Undefined | Unknown | true |
It searches for using of loop variable in goroutine. Value of this variable may be changed by outer loop before this goroutine’s execution which lead to race condition.
var synchr sync.WaitGroup
for i := 0; i < 100; i++ {
.Add(1)
synchr
go func() {
defer synchr.Done()
.Printf("i = %d\n", i)
fmt}()
}
.Wait() synchr
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Critical | VeryHigh | true |
Interfaces are not pointers in Go even though they may look like pointers, and an interface variable will be “nil” only when its type and value fields are “nil”. This can lead to unexpected behavior when checking an interface variable for “nil”.
The checker detects situations where a pointer, which might be “nil”, is converted to a value of interface type. The checker does not analyze caller code.
func foo(x int) Interface {
var possibleNil *Struct
if (x > 5) {
= &Struct{Field: 5}
possibleNil }
return possibleNil
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Normal | Unknown | true |
The warning is reported when typed nil
value may be
returned from the function and return type is interface.
func createTypedNilStruct() Interface {
var typedNil *Struct
= nil
typedNil return typedNil // Warning.
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Major | High | true |
The analysis propagates information on whether recover before panic on each execution path or not was.
If it was not then there is making the warning NO_RECOVER_FOR_PANIC.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Normal | Unknown | true |
The warning is reported when value is extracted from the map using tainted key without result checking.
For Go language when value = map[key]
is used instead of
value, ok = map[key]
, key
is tainted, and
value
was not checked.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Undefined | Low | true |
Same as TAINTED.UNCHECKED_TUPLE.RET, but the sink is in callee.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Normal | Unknown | true |
The warning is reported for Go language code in the form of
value, ok = i.(T)
when ok
value is
ignored.
, err = function()
valuereturn value // `err` is never checked.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Normal | Unknown | true |
Same as UNCHECKED_TUPLE.CAST, but the sink is in callee.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Normal | Unknown | true |
The warning is reported for Go language code in the form of
value, ok = <-channel
when ok
value is
ignored.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Normal | Unknown | true |
The checker detects situations where tuple result from functions with error part is used without checking for error.
func parse(path string) (uint, error) {
:= read(path)
length if length < 0 {
return 0, fmt.Errorf("error")
}
return length, nil
}
func example(para string, out *int) uint {
, _ := parse(para)
nreturn n // Error, variable `n` is used without checking the error.
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Normal | Unknown | true |
Same as UNCHECKED_TUPLE.RET, but the sink is in callee.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Major | High | true |
This checker finds infinite loop in goroutines.
func waiter(text string) {
for {
.Sleep(time.Second)
time.Println(text)
fmt} // No exit condition.
}
func main() {
go waiter("Hello")
go waiter("World")
.Sleep(50 * time.Second)
time}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
GO | Quality | Undefined | Unknown | true |
The checker finds situation where type-switch does not have default branch.
switch dm := m.(type) {
case DynamicAny:
= dm.Message
m case *DynamicAny:
if dm == nil {
return nil, proto.ErrNil
}
= dm.Message
m }
// No default.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Average | true |
CXX | Quality | Major | Unknown | true |
This checker finds situations where a format string specifies more arguments than the number of arguments actually passed to a format output function. This leads to illegal access to stack memory.
void test(int x) {
("count: %d/%d\n", x);
printf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | true |
This checker finds situations where a format string specifies fewer arguments than the number of arguments actually passed to a format output function.
void fmt_str(int x, int y, int z) {
("count: %d/%d\n", x, y, z);
printf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | false |
This checker finds situations where a format string specificator’s type does not match the type of the corresponding actual argument passed to a format input or output function.
void f(int x) {
("%s\n", x);
printf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Suppressed | Critical | VeryHigh | true |
This warning is emitted when pointer to a local variable is returned from a function. Memory referenced by such pointers is invalid, and using it may lead to inconsistent behavior.
More general cases of usage of memory out of scope is also reported by RETURN_LOCAL_ADDR checker.
char *write_temp_file_and_return_its_name() {
char tempname[] = "/tmp/dir/XXXXXX";
int fd = mkstemp(tempname);
if (fd != -1) {
// ...
} else {
return NULL;
}
return tempname;
}
In the example above the contents of allocated on stack character
array tempname
is filled by calling a mkstemp
function. This local array address is then returned from the function
but that memory can’t be used after return since it is already
reclaimed.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Average | true |
This checker finds functions in the source code that are declared as having a non-void return value, but don’t return any value. This checker only detects such function that don’t unconditionally terminate execution of the program.
void boo();
int foo(void) {
(); // NO_RETURN_VALUE will be emitted here.
boo}
int foo2(int cond) {
if (cond)
(1);
exitelse
(0);
exit
// NO_RETURN_VALUE will NOT be emitted here, since the function always
// terminates the program.
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This warning is emitted when function pointer known to be pointing to
some particular function(s) is compared to NULL
. Such
comparison is always false.
int fun() {
return 1;
}
typedef int (*PF)();
void proc() {
= &fun;
PF addr
if (addr == 0) // Condition is always false, emit PROC_ADDR_NULL_CHECK.
return;
();
addr}
Sometimes such comparison is intended. To suppress this warning,
change 0 to a constant with NULL
value:
int fun() {
return 1;
}
typedef int (*PF)();
void proc() {
= &fun;
PF addr
int (*null_fp)(char const *) = 0;
if (addr == null_fp) // PROC_ADDR_NULL_CHECK is not emitted.
return;
();
addr}
(Svace still emits PROC_ADDR_NULL_PTR_CHECK in this case.)
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | High | false |
This checker finds situations where a structure of size greater than 16 bytes is passed as a function parameter by value.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | High | true |
This checker finds situations where a structure of size greater than 128 bytes is passed as a function parameter by value. Passing structures of such size by value can slow down a program significantly.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Low | true |
This checker finds situations where the total size of memory that might be simultaneously allocated on stack exceeds the allowed bound (8 MB by default on Linux).
void bigmem() {
char arr7mb[7 * 1024 * 1024];
// ...
}
void test() {
char arr2mb[2 * 1024 * 1024];
// ...
();
bigmem// ...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Average | false |
This checker finds situations where the size of a variable or an array exceeds 100 KB.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | true |
This checker finds situations where the size of a variable or an array exceeds 1 MB.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Average | true |
CXX | Quality | Major | Average | true |
GO | Quality | Major | Average | true |
CSHARP | Quality | Major | Average | true |
This checker reports a suspicious expression where the result of operation is always a constant regardless of the value of its variable operands.
int func(unsigned char c) {
if (c > 1024) { // Always false since unsigned char type has possible value
// in the range of [0; 255].
return -1;
}
}
Subtypes of this warning INVARIANT_RESULT.OP_ASSIGN and INVARIANT_RESULT.OP_ZERO indicate the use of a composite logic assignment operator and logic operator with zero operands respectively. Such patterns are separated to contain possible false positives when constant macros are used.
In the following example, bitwise AND is applied with zero.
unsigned flags = 5;
if (flags & 0) {
// ...
}
In the following example, a logical operator !
appears
to have been substituted for a bitwise operator ~
.
int supposedToBeBitwiseComplementOfFLAGS = !FLAGS;
In the following example, parentheses are missed.
!var & FLAGS
In the following example, the right-hand side of an |=
expression is of a wide type than the left-hand side and has high-order
bits set that will not affect the left-hand side.
|= 0x10000; short_variable
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Undefined | Unknown | true |
This checker finds code where the second operand of a logical operator has no impact on the result.
void ex_1(unsigned a) {
if (a < 7 && a < 10) {
// ...
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Major | Average | true |
This checker finds situations where a value is first used by passing it to a function as an argument, and then is checked for potentially being negative afterwards, meaning that such value may indicate an error code.
int foo(int fd) {
int status = dup(fd);
if (fd != -1) {
return status;
}
return -1;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker finds suspicious ‘lesser than’ and ‘greater than’ comparisons between a pointer and a null pointer.
void foo(void *p) {
// ...
if (p >= NULL) // Probably, `p != NULL` was intended.
return;
// ...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This Microsoft Windows-specific checker finds situations when a non-BSTR value is incorrectly compared with a BSTR value.
bool compare(BSTR s1, wchar_t *s2) {
if (s1 == s2) // Incorrect comparison.
return true;
return false;
}
Fixed code:
bool compare(BSTR s1, wchar_t *s2) {
if (wcscmp(s1, s2) == 0)
return true;
return false;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker finds unsafe mix of integer values and boolean constants in comparison operations.
In the AVFS library the return value of the virt_islocal
function is 1
in case of a successful check, 0
in case of a negative check and -1
in case of an error.
But the author of the code below assumed that its return value may be
interpreted as a boolean value and checked only the negative scenario.
In case of the virt_islocal
function returning
-1
on error the true branch of the if expression is
taken.
if (virt_islocal(fname) != false) {
// ...
} else {
// ...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | true |
CXX | Quality | Normal | Average | true |
GO | Quality | Undefined | Unknown | true |
KOTLIN | Quality | Normal | Average | true |
CSHARP | Quality | Normal | Average | true |
This checker finds source code that can’t be executed because control flow path to the code from the rest of the program is unfeasible.
void foo(int i);
void unreachable(int cond) {
if (cond) {
("error!\n");
printf(1);
exit} else {
(0);
exit}
(15); // UNREACHABLE_CODE
foo}
The program is terminated on all paths leading to the call of
foo
function. The function call will never be executed.
fun example(a: Int) {
when {
> 10 -> print("a > 10")
a <= 10 -> print("a <= 10")
a else -> print("unreachable")
}
}
fun possibleFix(a: Int) {
when {
> 10 -> print("a > 10")
a <= 10 -> print("a <= 10")
a }
}
else
branch in when
expression is
unreachable because first and second branches cover all possible values
of a
. Just remove redundant branch to fix this defect.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Minor | Unknown | true |
CXX | Quality | Normal | Unknown | true |
Subtype of UNREACHABLE_CODE for switch with enum variable. All possible values are enumerated by case labels. The warning is emitted for default label. Note: it is a good practice to always write default label.
enum E {
,
aa1,
aa2
aa3};
void example(enum E x) {
int a = 1;
switch(x) {
case aa1: a = 2; break;
case aa2: a = 4; break;
case aa3: a = 5; break;
default: a = 22; break; // UNREACHABLE_CODE.ENUM
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Normal | Unknown | true |
Subtype of UNREACHABLE_CODE for situations where case label of switch is unreachable.
void example(enum E x) {
if (x == aa2)
return;
int a;
switch(x) {
case aa1: a = 2; break;
case aa2: a = 4; break; // UNREACHABLE_CODE.SWITCH
case aa3: a = 4; break;
default: a = 22; break; // UNREACHABLE_CODE.ENUM
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Minor | Unknown | true |
CXX | Quality | Minor | Unknown | true |
Subtype of UNREACHABLE_CODE for situations where default label of switch is unreachable. Situations with enum variables are not related to this type. UNREACHABLE_CODE.ENUM is emitted for them.
void example1(int x) {
if (x < 0 || x > 4) {
return;
}
int a = 1;
switch (x) {
case 0: a = 2; break;
case 1: a = 4; break;
case 2: a = 5; break;
case 3: a = 3; break;
case 4: a = 12; break;
default: a = 22; break; // UNREACHABLE_CODE.DEFAULT
}
}
In follow code snipped programmer may assume that default label include cases where variable x is equal to aa3.
enum E {
,
aa1,
aa2
aa3};
void example2(int z) {
enum E x = aa1;
if (z == 3)
= aa2;
x
int a;
switch (x) {
case aa1: a = 2; break;
case aa2: a = 4; break;
default: a = 22; break; // UNREACHABLE_CODE.DEFAULT
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Unknown | true |
CXX | Quality | Normal | Unknown | true |
CSHARP | Quality | Major | Average | true |
Subtype of UNREACHABLE_CODE for situations where a code fragment is unreachable because the previous operation always throws an exception.
private FileInfo CompileResx(Configuration solutionConfiguration) {
// For performance reasons, compilation of resx files is done in
// batch using the ResGen task in ManagedProjectBase.
throw new InvalidOperationException();
}
public FileInfo Compile(Configuration solutionConfiguration) {
= null;
FileInfo compiledResourceFile switch (InputFile.Extension.ToLower(CultureInfo.InvariantCulture)) {
case ".resx":
= CompileResx(solutionConfiguration);
compiledResourceFile break;
...
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | false |
CSHARP | Quality | Minor | Average | false |
Subtype of UNREACHABLE_CODE for situations where there is no path to a code fragment.
Here a programmer forgot to add a default
label before
throw
statement, so the throw
statement
belongs to case 4
section but is located after
break
.
switch (ByteCapacity)
{
case 1: Stream.WriteByte((byte)CurrentValue); break;
case 2: Stream.WriteStruct((ushort)CurrentValue); break;
case 4: Stream.WriteStruct((uint)CurrentValue); break;
throw(new InvalidOperationException());
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Average | true |
Some C functions, such as getc
, fgetc
and
getchar, return either (non-character) value EOF or a character code.
This checker detects incorrect use of such functions, where their return
value is not compared to EOF before being converted from int to char
(note that comparing to EOF after converting to type char is incorrect,
since integer value of EOF is not a char).
char buf[256];
FILE* fp;
int c, i = 0;
void test() {
while (i < 256) {
= fgetc(fp);
c
if (c == 'q')
break;
[i++] = c; // Unsafe type conversion, not checked for EOF.
buf}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This C-specific portability checker detects situations of using
char
type (without explicit signed
or
unsigned
type modifier) variable in conditional
expressions. Different target platforms have different default
signedness of the char
type and using variables of this
type without explicit signedness type modifier may lead to unexpected
changes in program behaviour on platforms with different defaults.
While the following loop iterates four times as expected when
compiled for a platform with signed char
type, it becomes
infinite when compiled for a platform with unsigned char
type.
for (char depth = 3; depth >= 0 ; --depth) {
// ...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | VeryHigh | true |
This warning is emitted for invocations of functions that open security vulnerabilities, irrespective of their method of use.
A subtype of this warning PROC_USE.VULNERABLE.TEMP indicates the use of vulnerable library functions for working with temporary files.
void vln_use() {
char buf[10000];
(buf); // No matter what is the size if `buf`, `gets` can overflow it.
gets}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | true |
This checker reports warnings for the use of weak pseudo-random
generator functions including rand()
,
random()
, erand48()
, drand48()
,
mrand48()
, and lrand48()
.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | false |
The checker TOCTTOU_SEQUENCE (Time-of-check-to-time-of-use)
finds situations where a call accessing file attributes (such as
stat()
, access()
and readlink()
)
is followed by a call using the same file (such as fopen()
,
opendir()
). This can potentially lead to a kind of race
condition, where between the check of file attributes and use of the
file, the file is changed.
void test(const char* fname) {
if (access(fname, W_OK) == 0)
(fname, O_WRONLY);
open}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | VeryHigh | true |
This checker finds situation where a call to chroot()
is
made, that isn’t followed by a call to chdir("/")
. Function
chroot()
limits application’s access to the file system to
within a given directory (“sandbox”). However, if current directory is
outside the sandbox, the application can still access files outside the
sandbox. Calling chdir("/")
makes sure it doesn’t
happen.
This checker can handle source code that implements calls to
chdir
and chroot
using wrapper functions, or
code that calls chdir
conditionally on the return value of
chroot
.
int chroot_wrapper() {
return chroot("/var/jail");
}
int chdir_wrapper() {
return chdir("/");
}
void a(int b, int c) {
();
chroot_wrapperif (b > c)
(); // CHROOT_JAIL is emitted here, since `chdir`
chdir_wrapper// doesn't follow `chroot` unconditionally.
("/etc/passwd", "r");
fopen}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Average | true |
CXX | Quality | Major | Average | true |
GO | Quality | Major | High | true |
The checker NEGATIVE_CODE_ERROR finds situations where a value is returned by a library function, that may be negative to indicate an error code, but is used in a situation where the negative values are not expected (for example, in a call to another function, or as an index in buffer access).
void test(char* path, int flags, char* mode, char* buf, int nbytes) {
int fd = open(path, flags, mode);
(fd, buf, nbytes);
read}
The value returned by open
may be a negative error code,
which shouldn’t be passed to function read
.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Major | High | true |
KOTLIN | Quality | Major | VeryHigh | true |
This detector finds situations where a library function returns a value that might indicate an error and this value is not checked after it has been assigned to a local variable.
void example(char *oldpath, char *newpath) {
int x = rename(oldpath, newpath);
}
void possible_fix(char *oldpath, char *newpath) {
if (!rename(oldpath, newpath)) {
// ...
}
}
Function example
illustrates the defect: the return
value of rename
function is assigned to x
and
is not checked. Function possible_fix
illustrates a
possible fix: check the return value of rename
function and
handle a possible error.
fun example(string: String, offset: Int): Reader {
val reader = StringReader(string)
val skipped = reader.skip(offset.toLong())
return reader
}
fun possibleFix(string: String, offset: Int): Reader {
val reader = StringReader(string)
if (reader.skip(offset.toLong()) < 0) {
// ...
}
return reader
}
Function example
illustrates the defect: the return
value of skip
function is assigned to skipped
and is not checked. Function possibleFix
illustrates a
possible fix: check the return value of skip
function and
handle a possible error.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Minor | VeryHigh | true |
CXX | Quality | Minor | VeryHigh | true |
KOTLIN | Quality | Minor | VeryHigh | true |
This detector finds situations where a library function returns a value that might indicate an error and this value is neither checked nor assigned to a local variable.
void example(char *oldpath, char *newpath) {
(oldpath, newpath);
rename}
void possible_fix(char *oldpath, char *newpath) {
if (!rename(oldpath, newpath)) {
// ...
}
}
Function example
illustrates the defect: the return
value of rename
function is not assigned to a local
variable and is not checked. Function possible_fix
illustrates a possible fix: check the return value of
rename
function and handle a possible error.
fun example(string: String, offset: Int): Reader {
val reader = StringReader(string)
.skip(offset.toLong())
readerreturn reader
}
fun possibleFix(string: String, offset: Int): Reader {
val reader = StringReader(string)
if (reader.skip(offset.toLong()) < 0) {
// ...
}
return reader
}
Function example
illustrates the defect: the return
value of skip
function is not assigned to local variable
and is not checked. Function possibleFix
illustrates a
possible fix: check the return value of skip
function and
handle a possible error.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This checker finds situations where a value of locally defined (automatic) variable is accessed, but the variable was never initialized.
struct X {
int fld;
};
void uninit() {
struct X st;
int val;
= st.fld;
val }
Variable st.fld
accessed at the last line was never
initialized.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
KOTLIN | Quality | Normal | Unknown | true |
This detector finds following situations: method of some class overrides the method of its base class which is called from constructor, also, the method of derived class accesses this class field. This causes a NullPointerException to be thrown.
This detector requires the
COLLECT_JAVA_METHOD_OVERRIDERS
setting to be enabled. Run
svace config COLLECT_JAVA_METHOD_OVERRIDERS true
to enable
the option.
class Employee {
private final String name;
public Employee(String name) {
this.name = name;
printInfo();
}
void printInfo() {
System.out.println(name);
}
public String getName() {
return name;
}
}
class RussianEmployee extends Employee {
private final String secondName;
public RussianEmployee(String name, String secondName) {
super(name);
this.secondName = secondName;
}
@Override
void printInfo() {
System.out.println(getName() + " " + secondName);
}
}
Defect description: RussianEmployee
constructor calls
Employee
constructor which calls printInfo
method. RussianEmployee
class overrides
printInfo
method in which uninitialized field
secondName
is accessed. Possible fix: do not call
printInfo
method from constructor of base class.
data class Prop(val len: Int, val isRussian: Boolean)
open class Employee(val name: String) {
{ printInfo() }
init private fun printInfo() = println("Employee: name=$name name_len=${baseProp.len} is_russian=${baseProp.isRussian}")
open val baseProp: Prop = Prop(name.length, false)
}
class RussianEmployee(name: String, val secondName: String) : Employee(name) {
override val baseProp: Prop = Prop(name.length + secondName.length, true)
}
Defect description: RussianEmployee
constructor calls
Employee
constructor which calls printInfo
method. printInfo
method calls getBaseProp
method. RussianEmployee
class overrides
getBaseProp
method in which uninitialized field
secondName
is accessed. Possible fix: do not call
printInfo
method from constructor of base class.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | false |
This checker finds situations where a derived class directly uses dynamic memory, but its base class (with public inheritance) doesn’t have a virtual destructor. Deallocating objects of such classes through a pointer to the base class fails to invoke derived class’ destructor, and so can result in a memory leak.
class Base {
public:
~Base() {} // A non-virtual destructor.
};
// Public inheritance.
class Child : public Base {
int* arr;
public:
() {
Child= new int[10];
arr }
virtual ~Child() {
delete[] arr; // This instruction works with dynamic memory;
// NON_VIRTUAL_DTOR will be emitted at this line.
}
};
void use() {
* b = new Child();
Basedelete b; // A memory leak example: only `Base::~Base` is invoked here.
}
Svace emits a warning even if source code never deletes objects of the derived class via a pointer to base class. Svace shouldn’t detect situations with private or protected inheritance. We plan to include traces with location of base class, inheritance chain, and delete location in future versions of the checker.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Normal | High | true |
KOTLIN | Quality | Minor | Unknown | true |
This checker finds situations where some function throws exceptions which should not be thrown from it because of specified exception list, or because the function is a top-level function, or because the function is a destructor.
If some exception does not correspond to specified exception list of some function then the program will crash.
void foo() throw(int) {
throw "Hello world!"; // Warning is fired.
}
int main() {
try {
();
foo} catch(...) {
// Never reached.
}
}
Exceptions from main function also terminate the program.
int main() {
throw "Hello world!"; // Warning is fired.
}
Throwing an exception out of a destructor is dangerous. If another exception is already propagating the application will terminate.
struct Bad {
~Bad(){
throw 1; // Warning is fired.
}
};
int main() {
try {
;
Bad badthrow 2;
} catch(...){
// Never reached.
}
}
For Java and Kotlin this type of warning is emitted, if exception
class that reaches main method (or parent of this class) is annotated
with @MustBeCaught
annotation. By default,
IllegalArgumentException
is annotated with this
annotation.
@MustBeCaught
annotation belongs to the
ru.isp.svace.sf
package. Exception classes from user code
may be annotated with @MustBeCaught
via
svace spec
tool.
class EmptyListException() : IllegalArgumentException()
fun main(args: Array<String>) {
(args) // Warning is fired.
parseArgs}
private fun parseArgs(args: Array<String>) {
if (args.size == 0) {
throw EmptyListException()
}
// ...
}
If some exception types are declared in throws ...
of
main method, then the warning is not emitted for these types of
exceptions.
public class TrustThrowsInSignature {
public static void main(String[] args) throws ClassNotFoundException, LinkageError, ExceptionInInitializerError{
if (args.length < 2) return;
Class<?> clazz = getClass(args[1]); // Warning is not fired.
System.out.println(clazz);
}
public static Class<?> getClass(String name) throws ClassNotFoundException, LinkageError, ExceptionInInitializerError {
return Class.forName(name);
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | VeryLow | true |
CXX | Quality | Normal | VeryLow | true |
KOTLIN | Quality | Minor | Unknown | true |
This warning is emitted in the same patterns as described above but only if the exception could be thrown from some standard library function or operator.
class Base {virtual void member(){}};
class Derived : Base {};
void test() throw() {
;
Base b// Will crash, because dynamic_cast of reference could throw `std::bad_cast`.
& rd = dynamic_cast<Derived&>(b);
Derived}
public class ConditionalThrow {
public static void main(String[] args) {
int port = 7100;
for (int i = 0; i < args.length; ++i) {
if ("-p".equals(args[i]) && i + 1 < args.length) {
++;
i= Integer.parseInt(args[i]); // Warning is fired for `NumberFormatException`.
port }
}
// ...
}
}
fun main(args: Array<String>) {
val portNumber = args[0].toInt() // Warning is fired for `NumberFormatException`.
("Starting server on port $portNumber...")
println// ...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Minor | Unknown | true |
KOTLIN | Quality | Normal | VeryLow | true |
This type of warning is emitted only for Java and Kotlin for
exception types that doesn’t belong to NO_CATCH and NO_CATCH.LIBRARY
groups (i.e. not annotated with @MustBeCaught
and thrown
from user code).
fun main(args: Array<String>) {
(args) // Warning is fired
parseArgs}
private fun parseArgs(args: Array<String>) {
if (args.size == 0) {
throw RuntimeException("Empty list") // `RuntimeException` is not annotated with `@MustBeCaught`.
}
// ...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Low | false |
In some cases the standard library specification declares that some
exception is possible, but actually it could never be thrown. The
warnings of such cases are NO_CATCH.LIBRARY.PEDANTIC type. For example,
std::string
constructors assumes that there is some maximal
capacity of the string that could be less than the size of addressable
memory. So, if the user asks std::string to create a string with the
size more than this maximal capacity, then std::string
constructor will throw std::length_error
. In most standard
library implementations the maximal capacity is equal to the size of
addressable memory and so there will be std::bad_alloc
exception, not std::length_error
, for too big strings.
std::string s3 ('x', 10); // Theoretically can throw `std::length_error`.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Average | false |
The most used version of operator new
cannot return
NULL
but instead could throw std::bad_alloc
exception in the case when it could not allocate memory. Most programs
don’t catch possible std::bad_alloc
’s because they assume
that there is enough memory. So std::bad_alloc
exception
has own warning type.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Unknown | true |
This checker finds situations where a pointer value is cast to a pointer with base type of different size. This may lead to a buffer overflow or unexpected value when the pointer is dereferenced.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This Microsoft Windows-specific checker finds situations when a non-BSTR value is incorrectly converted to BSTR.
() {
BSTR bstringwchar_t *string = L"string";
return string; /* Incorrect: `wchar_t*` can't be directly casted to BSTR. */
}
Fixed code:
() {
BSTR bstringwchar_t *string = L"string";
return SysAllocString(string.c_str());
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | false |
Check for assignment of a signed value to a variable of a bigger unsigned integral type. While it is not a defect by itself this may unexpectedly lead to a large resulting value if the original signed value is negative.
It may not be obvious that due to sign extension the value of b in
the example below is 0xFFFFFFFFFFFFFFFF
rather than
0xFFFFFFFF
.
void ex_1() {
int32_t a = -1;
uint64_t b = a;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | false |
This checker reports unexpected sign extension during integer promotion when result value has all of its high bits set to 1 and consequently is interpreted as a very large value.
In the example below an expression p[0]
with 1-byte
unsigned type unsigned char
is promoted to larger 4-byte
signed type int
before performing computation of expression
p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)
and then sign-extended to type unsigned long
. In case its
high bit is set the promoted value will have its high bits set as well
and the resulting value after bitwise or
computation will
be interpreted as a very large unsigned value.
unsigned long combine(unsigned char *p) {
return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Suppressed | Major | Unknown | true |
This checker finds situations where the address of some not-array variable is used as array.
void set(int* buf) {
[10] = 0;
buf}
int test1() {
int x = 0;
(&x); // There will be memory access violation.
setreturn x;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Unknown | true |
This checker finds situations where the value returned by operator
new
is compared with NULL
.
void foo() {
char *x = new char[10];
if (x) { // This check is redundant.
//...
}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Major | High | true |
KOTLIN | Quality | Normal | Unknown | true |
This checker finds situations where return value of some function is
not checked, while it’s checked in most places where that function is
called. The warning is emitted according to statistics of function use,
without taking into account function’s body (implementation). Compared
to DEREF_OF_NULL.RET, this checker
relies on function use statistics to a greater extent, and works with
more types of return value checks than just comparison with
NULL
. This checker emits warning if more than
UNCHECKED_FUNC_RES.STAT.THRESHOLD percents of all such function calls
are checked. UNCHECKED_FUNC_RES.STAT.THRESHOLD configuration variable is
equal to 80% by default.
class Stat() {
var isReady = false
var counter = 0
fun inc(): Boolean {
++
counterreturn isReady
}
// ...
}
fun checkedA(s: Stat) {
if (s.inc()) {
("checked in A")
println}
}
fun checkedB(s: Stat) {
if (s.inc()) {
("checked in B")
println}
}
fun unchecked(s: Stat) {
.inc()
s}
The UNCHECKED_FUNC_RES.STAT warning will be emitted for
inc
call into unchecked
function. Note that
UNCHECKED_FUNC_RES.STAT.THRESHOLD is set to 65% for this example.
Possible fix: check the return value of inc
call into
unchecked
function.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | High | true |
This checker finds situations where standard library function
open
is invoked with flag O_CREAT, but without passing it
the third parameter.
int open(const char* path, int oflag, ... );
Here if oflag
is O_CREAT
, it’s necessary to
specify the 3rd argument to set correct file access rights.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | High | true |
This checker finds situations where va_end
is missing
from functions that work with variable argument lists.
int no_va(int x, ...) {
va_list va;
(va, x);
va_startif (va_arg(va, int) == x)
return -1; // `va_end` call is missing.
(va);
va_end}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | VeryHigh | true |
This checker finds situations where va_start
is missing
from functions that work with variable argument lists.
void test(int x, ...) {
va_list va;
if (x != 0)
(va, x);
va_startwhile (x > 0) {
// ...
--;
x}
(va);
va_end}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Unknown | true |
This warning type is reported for expressions with side-effects in
assert
macro expressions. Since assertions are usually
disabled in release mode code may behave differently in debug and
release mode if assertions have side effect, such as variable
update.
assert(VLNAME(windowBits_IDAT)[--i].name == range_lo);
(windowBits_IDAT)[i].value = wb > 8 ? 9 : 8; VLNAME
If macro NDEBUG is set then the assert expression becomes a no-op and
variable i
isn’t decremented as expected.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Average | true |
CXX | Quality | Major | Unknown | true |
KOTLIN | Quality | Major | Unknown | true |
CSHARP | Quality | Major | Unknown | true |
This checker locates code snippets that were copied and pasted. However, because of incomplete change, they unintentionally left some portions of the copy unchanged.
int square(int x) {
return x * x;
}
int bad_copy_paste_example(int a, int b, int x, int y) {
int result = 0;
if (b > 0) {
= square(a) + square(x); // Defect: value `a` might be `b`.
result }
if (a > 0) {
= square(a) + square(x); // Original code.
result }
return result;
}
data class ColorData(val rgb: IntArray)
fun badCopyPasteExample(d: ColorData, flg1: Int, flg2: Int): Int {
var ret = 0
if (flg1 > 1 && d.rgb[0] == 255 && d.rgb[1] == 255 && d.rgb[2] == 255) {
+= flg2 // Defect: value `flg2` might be `flg1`.
ret }
if (flg2 > 1 && d.rgb[0] == 0 && d.rgb[1] == 0 && d.rgb[2] == 0) {
+= flg2 // Original code.
ret }
return ret
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Major | Unknown | true |
CSHARP | Quality | Major | Unknown | true |
This checker reports a warning when both true and false branches of a conditional operator are identical.
void foo1(bool c){
int i;
if (c)
= 1; // Same code.
i else
= 1; // Same code.
i }
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Major | Unknown | true |
CSHARP | Quality | Major | Unknown | true |
This checker finds many cases where the indentation structure of the code does not match the syntactic nesting.
void foo(int a, int b) {
if (a > b)
++;
b++; // Doesn't belong to the `if` body but indentation suggests it is.
a}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Major | Unknown | true |
CSHARP | Quality | Major | Unknown | true |
This checker finds possible cases where the arguments to a function are provided in an incorrect order.
In the following example a function with prototype
void *calloc(size_t nmemb, size_t size);
is called with
swapped arguments.
(size, nmemb); calloc
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Unknown | false |
CXX | Quality | Minor | Unknown | false |
GO | Quality | Minor | High | false |
KOTLIN | Quality | Minor | Unknown | false |
This detector finds situations when a result of a function call is assigned to a local variable, but is never used afterwards.
UNUSED_FUNC_RES.MINOR is a subtype of UNUSED_FUNC_RES. This detector reports warnings that are less valuable to the user. The value of a warning is evaluated by a set of heuristic rules.
typedef struct {
int mem;
} storage, something;
* add_something_to_storage(storage*, something*);
storage
void process(storage* stor, something* smt) {
* new_stor = add_something_to_storage(stor, smt);
storage// ...
// Use `stor` instead of `new_stor`.
// ...
return;
}
fun get() = (0..100).random()
fun use(value: Int) = println(value)
fun example() {
val r = get()
// ...
}
fun possibleFix() {
val r = get()
(r)
use}
Function example
illustrates the defect: the return
value of the get
call is assigned to the r
variable, which is not used until the example
function
terminates. Function possibleFix
illustrates a possible
fix: use the return value of the get
call.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | High | true |
CXX | Quality | Minor | High | true |
GO | Quality | Minor | Unknown | true |
KOTLIN | Quality | Minor | High | false |
This detector finds situations when the result of a function call is assigned to a local variable that has never been used before being reassigned.
UNUSED_FUNC_RES.REWRITE.MINOR is a subtype of UNUSED_FUNC_RES.REWRITE. This detector reports warnings that are less valuable to the user. The value of a warning is evaluated by a set of heuristic rules.
fun get() = (0..100).random()
fun use(value: Int) = println(value)
fun example() {
var r = get()
// ...
= get()
r (r)
use}
fun possibleFix() {
var r = get()
(r)
use= get()
r (r)
use}
Function example
illustrates the defect: the return
value of the get
call is assigned to the r
variable, which is not used until the return value of the second
get
call is assigned to r
. Function
possibleFix
illustrates a possible fix: use the return
value of both get
calls.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Minor | Unknown | true |
CXX | Quality | Minor | Average | true |
This checker finds situations when a variable is initially assigned a value but is always reassigned a new value before using the original one.
void func(int *x) {
int value;
= 1; // This value is never used.
value = 2;
value }
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Low | true |
Check for isolated occurrences of sizeof operators that are technically legal in C/C++, yet are often erroneous.
In the following example, param_not_really_an_array
looks syntactically like an array, but is actually a pointer. When you
apply the sizeof operator to the pointer, it yields the size of a
pointer (typically 4 or 8 bytes), and not the expected 10 bytes:
void f(char param_not_really_an_array[10]) {
(param_not_really_an_array, 0, sizeof(param_not_really_an_array));
memset}
In the following example, sizeof is applied to the this
pointer rather than to the *this
object, which yields an
incorrect value for the size of the object:
size_t SomeClass::getObjectSize() const {
return sizeof(this);
}
In the following example, the sizeof operator is applied to the address of s rather than to s itself, which yields a larger value and overwrites adjacent memory locations:
void f() {
short s;
(&s, 0, sizeof(&s));
memset}
In the following example, the sizeof operator is applied to the pointer arithmetic expression buf ??? 3. The expression has type char* and is likely 4 or 8 bytes in size, rather than sizeof applied to buf, and then subtracting 3 from the result, which yields the desired value of 97:
void f() {
char buf[100];
[0] = 'x';
buf[1] = 'y';
buf[2] = 'z';
buf(buf + 3, 0, sizeof(buf - 3));
memset}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Critical | Low | true |
Check for combinations of pointers and sizeof expressions that appear to be mismatched.
For pointers with base type char
warning subtype
SIZEOF_POINTER_TYPE.CHAR is reported. Since it is common to
work with memory intended for storage of bigger data structures through
pointers to the smallest possible char
type such reports
tend to be less reliable.
In the following example, only 4 or 8 bytes of the 100-byte object buf are cleared:
struct buffer {
char b[100];
};
void f() {
struct buffer buf;
(&buf, 0, sizeof(&buf)); // Defect: should have been `sizeof(buf)`.
memset}
In the following example, only 4 or 8 bytes are allocated for a 100-byte object:
struct buffer {
char b[100];
};
void f() {
struct buffer *p = (struct buffer *)malloc(sizeof(struct buffer *));
/* Defect: should be `sizeof(struct buffer)`. */
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | VeryHigh | true |
This warning type is reported when a wide string is passed to a function working with regular character strings.
int test() {
wchar_t b[] = L"qwertyu";
return strlen(b);
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Average | true |
Check for defects in overriding virtual functions due to missing
const
modifiers, which result in type signature
mismatches.
class base {
virtual void foo() const { /* ... */ }
};
class child: public base {
// Warning: `child::foo` is probably meant to override `base::foo` but
// type signatures don't match perfectly.
void foo() { /* ... */ }
};
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Average | false |
Check for cases where a delete
is applied to a pointer
to void
.
In the following example, delete
is used on a pointer of
type void
:
void buggy(void *p) {
delete p;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Average | true |
Check for places in the code where the C/C++ language rules for expression evaluation do not determine the order in which side effects happen.
In the following example, it is unclear whether the left or right side of the operator is evaluated first, so the value of x might change:
int g(int x) {
return x + x++;
}
The following example demonstrates side effect ordering problems. The right-hand side of the assignment is evaluated before the assignment itself takes place. However, the side effect order is unspecified because the side effect associated with ++ could happen before or after the side effect associated with the assignment.
int foo() {
int x = 0;
= x++;
x return x;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Normal | Average | true |
GO | Quality | Major | Average | true |
KOTLIN | Quality | Major | Average | true |
Check for instances of statements or expressions that do not accomplish anything, or statements that perform an action that is not the intended action.
In the following example, the left operand of a comma operator has no side effects:
void extra_comma() {
int a, b;
for (a = 0, b = 0; a < 10, b < 10; a++, b++);
// Extra comma, and so `a < 10` is not used.
}
In the following example, only the first pointer is deleted:
void incomplete_delete() {
int *p, *q;
delete p, q;
// The pointer `q` is not deleted.
}
In the following example, dereference of the pointer is useless:
void no_effect_deref() {
int *p;
*p++;
// `*p` is useless.
}
In the following example, the boolean test is useless:
void no_effect_test() {
int a, b;
== b;
a // Test has no effect, and is
// likely intended to be the assignment `a = b`.
}
In the following example, an assignment is likely intended:
int a, b;
void bool_switch() {
switch (a == b) { // Boolean switch.
case 1:
}
}
In the following example, there’s an unsigned comparison against 0:
void unsigned_compare() {
unsigned int a;
if (a < 0) // `a` is unsigned, and so the comparison is never true.
++;
a}
In the following example, there are self assignments:
int a;
void self_assign(struct foo *ptr) {
= a;
a ->x = ptr->x;
ptr}
void array_null() {
unsigned int a[3];
unsigned int b[1];
unsigned int c[2];
if (*a == 0)
[0];
aif (b == 0) // The entire array b is compared to 0.
[1];
bif (c[1] == 0)
[1];
c}
In the following example, suspicious arguments are passed to the
memset function. A size argument of 0 can indicate that the size and
fill arguments are switched. A fill value outside the range of -1 to 255
will likely lead to truncation. A fill value of 0
is likely
intended to be 0:
void bad_memset() {
int *p;
(p, '0', l); // Fill value is `0`, and 0 is more likely.
memset(p, l, 0); // Length is 0, and so likely that `l` and 0 reversed
memset(p, 0xabcd, l); /* Fill is truncated, and so memory
memset will not contain the 0xabcd pattern. */
}
In the following example, the result of the ==
operator
is useless. Most likely, there should have been the assignment
leftBound == defaultBound
here.
fun testInsteadAssign(defaultBound: Int = 1) {
var leftBound: Int = 1
if (leftBound > defaultBound)
== defaultBound
leftBound // ...
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Undefined | Unknown | true |
This checker warns if it finds that parentheses are possibly missing around an assignment in condition. This code is not only very hard to read but also may contain errors due to operator precedence.
if ((x = f() != y)) {
...
}
In the example above the x
variable is assigned the
value of the conditional expression f() != y
which may not
be what the programmer intended.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Normal | Average | true |
CXX | Quality | Normal | Unknown | true |
GO | Quality | Minor | Unknown | true |
KOTLIN | Quality | Normal | Unknown | true |
This checker detects always-false expressions which are used in conditions that could change the control flow of a program.
Similar always-true expressions are reported by
REDUNDANT_COMPARISON
checker.
if (p != NULL) {
= strdup(p);
q if (p == NULL) {
// Error: allocation failed.
// ...
}
}
In the example above the author of the code first checks that the
value of pointer p
is not NULL
, so it can be
copied using strdup
function but then makes a typo checking
that the result of strdup
isn’t NULL
using the
same variable p
instead of q
. This typo leads
to the comparison always being false.
fun example(a: Any) {
if (a is Int) {
// Handle a as Int value.
} else if (a is Float) {
// Handle a as Float value.
} else if (a is Int) {
// Handle a as Long value.
}
}
fun possibleFix(a: Any) {
when (a) {
is Int -> { /* Handle a as Int value. */ }
is Float -> { /* Handle a as Float value. */ }
is Long -> { /* Handle a as Long value. */ }
}
}
Function example
illustrates the defect: the result of
the second a is Int
expression is always false. Function
possibleFix
illustrates a possible fix: replace the second
a is Int
expression by the a is Long
expression. Also, it’s recommended to use when
expression
instead of large if???else expression. Kotlin compiler produces warnings
about duplicate labels in when
expressions.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Major | Unknown | true |
CSHARP | Quality | Critical | Average | true |
Check for instances where an extraneous semicolon alters the logic of the code.
In the following example, an if statement is followed by an extra semicolon, which results in do_something_conditionally() being called unconditionally:
if (condition);
(); do_something_conditionally
In the following example, a while statement is followed by an extra semicolon. The following block is executed only once, unconditionally, which might result in a premature return from the function:
while (condition);
{
if (other_condition)
return;
/* Advance the loop. */
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Major | Average | true |
CSHARP | Quality | Major | Average | true |
Check for instances of loops that never terminate due to control variables which are not properly updated.
In the following example, a wrong variable is updated
for (i = 0; i < n; ++j) {
// ...
}
In the following example, a wrong variable is updated:
while (i > 0) {
[i] = i;
a--j;
}
In the following example, a wrong variable is updated:
for (;;) {
++y;
if (x > 5)
break;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Low | true |
CXX | Quality | Major | Low | true |
GO | Quality | Major | High | true |
Check for instances of divisions where the divisor (denominator) is zero.
int x = 0;
int z = y / x;
= 1;
x if (y) {
= 0;
x }
= y / x; z
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | High | true |
CXX | Quality | Major | High | true |
GO | Quality | Major | Low | true |
KOTLIN | Quality | Major | High | true |
Path sensitive version of the DIVISION_BY_ZERO. It checks if a path exists where some variable is assigned zero, and then this variable is used as a divisor.
fun example(cond: Boolean, value: Int) {
val d: Int = if (cond) 10 else 0
(value / d)
print}
fun possibleFix(cond: Boolean, value: Int) {
val d: Int = if (cond) 10 else 0
if (d != 0) {
(value / d)
print}
}
Function example
illustrates the defect: variable
d
is maybe assigned zero, but this variable is used as a
divisor also. Function possibleFix
illustrates a
possibleFix
: check d
value before using it as
a divisor.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | true |
CSHARP | Quality | Major | Unknown | true |
This checker finds code with an unexpected loss of arithmetic precision due to converting result of integral division into a floating point number.
float div(int a, int b) {
return (a / b);
}
void foo() {
("%0.2f\n", div(10, 4)); /* Expected 2.50 but result is 2.00 */
printf}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
KOTLIN | Quality | Normal | Unknown | true |
CSHARP | Quality | Major | Average | true |
This checker finds situations where a method is overridden and the overriding method should call the superclass. The checker relies on function for which all overriders should call the superclass.
import android.app.Activity
class Child : Activity() {
override fun onDestroy() {
super.onPause()
}
}
class CorrectChild : Activity() {
override fun onDestroy() {
super.onDestroy()
}
}
The NO_BASE_CALL.LIB warning will be emitted for function
Child.onDestroy
, because it calls unsuitable
Activity.onPause
superclass method. Possible fix: call
proper onDestroy
superclass method as shown in
CorrectChild.onDestroy
method.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
KOTLIN | Quality | Normal | Unknown | true |
CSHARP | Quality | Normal | Unknown | true |
This checker finds situations where a method is overridden and the overriding method should call the superclass method. This checker emits warning if more than CALL_SUPER.STAT.THRESHOLD percents of all overriders call the superclass method. CALL_SUPER.STAT.THRESHOLD configuration variable is equal to 80% by default.
open class Base {
open fun foo(msg: String) = println(msg)
}
class DerivedA : Base() {
override fun foo(msg: String) = super.foo("from A: $msg")
}
class DerivedB : Base() {
override fun foo(msg: String) = super.foo("from B: $msg")
}
class DerivedC : Base() {
override fun foo(msg: String) = println("derived C: $msg")
}
The NO_BASE_CALL.STAT warning will be emitted for function
foo
overridden into DerivedC
class. Note that
CALL_SUPER.STAT.THRESHOLD is set to 65% for this example. Possible fix:
call super method in DerivedC.foo
method.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Average | true |
The checker finds possible fork bombs: situations when
fork()
call creates two processes and both these processes
may execute the same fork()
again.
for (int i = 0; i < NPROC; i++) {
int pid = fork(); // FORK_BOMB emitted.
if (pid < 0) {
("fork():");
perror} else if (pid == 0) {
(...); // May fail: the cycle will continue in two processes.
execve} else {
[i] = pid;
child}
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Average | true |
The checker finds situations when two processes created by one fork() may both execute another fork(). Such code is unlikely to create exponential amount of processes, as in fork bomb, but this situation is probably unwanted.
int pid = fork();
if (pid == 0) {
(...); // May fail.
execve}
// Second fork will create 4 parallel processes. This is probably unwanted.
int pid2 = fork(); // FORK_BOMB.MINOR emitted.
if (pid2 == 0) {
(...);
execve(127);
_exit}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | VeryLow | true |
The checker finds situations when two processes created by
fork()
and separated by condition like
if (pid == 0)
will meet and execute the same code.
int pid = fork();
if (pid < 0) {
("fork():");
perror} else if (pid == 0) { // Child.
(...); // May fail: execution will continue in two processes.
execve} else { // Parent.
[i] = pid; // FORK_PROCESSES_MEET emitted.
child}
The checker has .EXCEPTION variant (FORK_PROCESSES_MEET.EXCEPTION). In this case processes meet after one or both of them threw an exception.
std::string a = "Hello";
int pid = fork();
if (pid == 0) { // Child.
.at(10) = 'u'; // FORK_PROCESSES_MEET.EXCEPTION emitted.
a(127);
_exit} else { // parent
.at(7) = 'b'; // May throw second exception.
a}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | true |
This checker finds situations when an enum-type value is used in places where a condition expression is required. The condition expression will be evaluated to false only if the value of an enum variable is zero. Otherwise, the condition expression will be evaluated to true. A developer may use an enum variable instead of a condition expression intentionally. However, such code pattern makes code less clear, and it sometimes leads to unexpected defects.
enum key {
= 0, right = 1, left = 2
down };
int keyPressed(enum key x) {
if (x) {
return 1;
}
return 0;
}
The developer may intend this behaviour but in this case it is better to write the same code in a more clear way:
int keyPressed(enum key x) {
if (x != down) {
return 1;
}
return 0;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
CXX | Quality | Minor | Unknown | true |
This checker finds situations when an enum-type value is used in places where a condition expression is required just as ENUM_TO_BOOLEAN checker but the enum-type does not have an element with zero value. In this case the probability of a defect is higher since there is no enumeration constant that could be evaluated as false value.
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
JAVA | Quality | Major | Unknown | true |
CXX | Quality | Major | Unknown | true |
This checker finds possible missing break statements in switch-case
statements. Sometimes a break statement is missing on purpose; in such
cases the checker can be silenced by using comments containing any of
the words combination: fall through
,
fallthrough
, falls through
or
no-break
in the related case block; to silence all warnings
reported for the whole switch statement place the same comment before
the switch statement itself.
int ex_1(int val) {
int i = 0;
switch (val) {
case 0:
= 2;
i // This case probably missing a break statement here.
case 1:
= 3;
i break;
default:
= -1;
i break;
}
return i;
}
Language | Situation | Reliability | Severity | Enabled |
---|---|---|---|---|
KOTLIN | Quality | Normal | Average | true |
This detector finds situations where an empty range is created.
fun example() {
for (s in 'a'..'Z') {
// ...
}
}
fun possibleFix() {
for (s in 'A'..'z') {
// ...
}
}
Function example
illustrates the defect: the
'a'..'Z'
expression creates an empty range because its left
bound a
is greater than the right bound Z
.
Lowercase latin characters have greater values in the character table
than the uppercase latin characters. Function possibleFix
illustrates a possible fix: use 'A'..'z'
expression to
create the interval.
In addition to using Svace engine for analysis of Java source code, it can be extended using SpotBugs analysis tool, which is automatically invoked by Svace analysis tool by default. Filtering of warning types imported from SpotBugs can be configured using svace warning command, in the same way as for Svace warnings. The following SpotBugs checkers are supported (their descriptions can be found on the SpotBugs bug descriptions page):
FB.BC_IMPOSSIBLE_CAST
FB.BC_IMPOSSIBLE_INSTANCEOF
FB.ES_COMPARING_PARAMETER_STRING_WITH_EQ
FB.ES_COMPARING_STRINGS_WITH_EQ
FB.ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD
FB.ML_SYNC_ON_UPDATED_FIELD
FB.UL_UNRELEASED_LOCK_EXCEPTION_PATH
FB.CN_IDIOM
FB.DB_DUPLICATE_BRANCHES
FB.DL_SYNCHRONIZATION_ON_BOOLEAN
FB.DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE
FB.DM_EXIT
FB.DMI_INVOKING_HASHCODE_ON_ARRAY
FB.DMI_INVOKING_TOSTRING_ON_ARRAY
FB.DMI_RANDOM_USED_ONLY_ONCE
FB.DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED
FB.EC_ARRAY_AND_NONARRAY
FB.EC_BAD_ARRAY_COMPARE
FB.EC_UNRELATED_CLASS_AND_INTERFACE
FB.EC_UNRELATED_TYPES
FB.EQ_ALWAYS_FALSE
FB.EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC
FB.EQ_SELF_USE_OBJECT
FB.ESYNC_EMPTY_SYNC
FB.FE_FLOATING_POINT_EQUALITY
FB.FI_EXPLICIT_INVOCATION
FB.GC_UNRELATED_TYPES
FB.ICAST_IDIV_CAST_TO_DOUBLE
FB.ICAST_INTEGER_MULTIPLY_CAST_TO_LONG
FB.IL_INFINITE_LOOP
FB.IL_INFINITE_RECURSIVE_LOOP
FB.INT_BAD_COMPARISON_WITH_SIGNED_BYTE
FB.INT_BAD_REM_BY_1
FB.IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN
FB.JLM_JSR166_UTILCONCURRENT_MONITORENTER
FB.LI_LAZY_INIT_STATIC
FB.LI_LAZY_INIT_UPDATE_STATIC
FB.MF_CLASS_MASKS_FIELD
FB.MWN_MISMATCHED_NOTIFY
FB.MWN_MISMATCHED_WAIT
FB.NN_NAKED_NOTIFY
FB.NP_BOOLEAN_RETURN_NULL
FB.NP_SYNC_AND_NULL_CHECK_FIELD
FB.NP_UNWRITTEN_FIELD
FB.NS_DANGEROUS_NON_SHORT_CIRCUIT
FB.RC_REF_COMPARISON
FB.RC_REF_COMPARISON_BAD_PRACTICE
FB.RE_POSSIBLE_UNINTENDED_PATTERN
FB.REC_CATCH_EXCEPTION
FB.RPC_REPEATED_CONDITIONAL_TEST
FB.RV_DONT_JUST_NULL_CHECK_READLINE
FB.RV_EXCEPTION_NOT_THROWN
FB.RV_RETURN_VALUE_IGNORED
FB.RV_RETURN_VALUE_IGNORED_BAD_PRACTICE
FB.SA_FIELD_SELF_COMPARISON
FB.SA_LOCAL_SELF_COMPARISON
FB.SA_LOCAL_SELF_COMPUTATION
FB.SBSC_USE_STRINGBUFFER_CONCATENATION
FB.SC_START_IN_CTOR
FB.SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH
FB.SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW
FB.SIC_INNER_SHOULD_BE_STATIC
FB.STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE
FB.STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE
FB.STI_INTERRUPTED_ON_UNKNOWNTHREAD
FB.SWL_SLEEP_WITH_LOCK_HELD
FB.UCF_USELESS_CONTROL_FLOW_NEXT_LINE
FB.UG_SYNC_SET_UNSYNC_GET
FB.UR_UNINIT_READ
FB.UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR
FB.UW_UNCOND_WAIT
FB.WA_AWAIT_NOT_IN_LOOP
FB.WA_NOT_IN_LOOP
FB.BC_BAD_CAST_TO_CONCRETE_COLLECTION
FB.BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS
FB.BC_UNCONFIRMED_CAST
FB.BC_VACUOUS_INSTANCEOF
FB.BIT_IOR
FB.CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE
FB.DE_MIGHT_IGNORE
FB.DLS_DEAD_LOCAL_STORE
FB.DLS_DEAD_LOCAL_STORE_IN_RETURN
FB.DLS_DEAD_LOCAL_STORE_OF_NULL
FB.DM_BOOLEAN_CTOR
FB.DM_GC
FB.DM_NEXTINT_VIA_NEXTDOUBLE
FB.DM_NUMBER_CTOR
FB.DM_STRING_CTOR
FB.DM_STRING_VOID_CTOR
FB.DMI_HARDCODED_ABSOLUTE_FILENAME
FB.DMI_USELESS_SUBSTRING
FB.DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION
FB.DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED
FB.EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS
FB.EQ_COMPARETO_USE_OBJECT_EQUALS
FB.EQ_DOESNT_OVERRIDE_EQUALS
FB.EQ_GETCLASS_AND_CLASS_CONSTANT
FB.EQ_UNUSUAL
FB.FI_EMPTY
FB.FI_FINALIZER_NULLS_FIELDS
FB.FI_PUBLIC_SHOULD_BE_PROTECTED
FB.FI_USELESS
FB.HE_EQUALS_USE_HASHCODE
FB.IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD
FB.ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL
FB.ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND
FB.ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT
FB.IM_AVERAGE_COMPUTATION_COULD_OVERFLOW
FB.IM_BAD_CHECK_FOR_ODD
FB.IMSE_DONT_CATCH_IMSE
FB.INT_VACUOUS_BIT_OPERATION
FB.NP_CLONE_COULD_RETURN_NULL
FB.NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT
FB.NP_TOSTRING_COULD_RETURN_NULL
FB.RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES
FB.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
FB.RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE
FB.SA_FIELD_DOUBLE_ASSIGNMENT
FB.SA_FIELD_SELF_ASSIGNMENT
FB.SE_BAD_FIELD
FB.SE_BAD_FIELD_STORE
FB.SE_COMPARATOR_SHOULD_BE_SERIALIZABLE
FB.SE_NO_SERIALVERSIONID
FB.SF_SWITCH_FALLTHROUGH
FB.SS_SHOULD_BE_STATIC
FB.ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD
FB.UCF_USELESS_CONTROL_FLOW
FB.UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS
FB.UPM_UNCALLED_PRIVATE_METHOD
FB.URF_UNREAD_FIELD
FB.UUF_UNUSED_FIELD
FB.UWF_NULL_FIELD
FB.UWF_UNWRITTEN_FIELD
FB.WMI_WRONG_MAP_ITERATOR