Svace is an automatic defect detection tool for source code written in C, C++, Java, Go, Kotlin, Python 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 integrated 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, SARIF, 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:
By request a distribution for macOS may be provided (x86_64, tested
on Mojave and newer; svace build
requires disabling System
Integrity Protection).
The analyzer has no external dependencies and may be used right after unpacking.
Svace supports the following languages versions (including earlier, unless stated otherwise):
go build
and go install
or bazel
javac
(including related,
e.g. Axiom JDK and Liberica JDK)gradle
, maven
or
kotlinc
List of supported C/C++ compilers:
List of supported target architectures for GCC/Clang-based compilers, not specified explicitly:
List of known compilers with no plans to support:
This section describes how to perform analysis through command line
interface, explaining the process of analysis in detail. The command
line interface is provided by the svace
program located in
the bin
subfolder of a Svace distribution. It supports a
number of commands (or tools) to perform various tasks. The main tools
are listed in the Command line tools
section. The full list can be shown by running the svace
program without arguments or the svace help
tool.
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. It has the
.svace-dir
name by default and is created in the current
working directory.
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 example, if the analyzed project
uses a Make build system and is built using a
make -f Makefile
command then to automatically generate
intermediate files for analysis of the project run
svace build make -f Makefile
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
sb2
doesn’t rewrite paths, e.g. $HOME
. Incorrectly placed
distribution may lead to build errors similar to these:ERROR: ld.so: object 'libsvace.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
sb2
sandbox (i.e. sb2 svace build ...
instead
of svace build sb2 ...
). Otherwise, Svace components
working outside the sandbox won’t be able to properly handle paths to
files inside the sandbox. Incorrectly run command may lead to the
following build interception error:svace build: warning: Hash server reported some errors.
--prepend-preload-lib
option to
svace build
command.By default, warnings from a balanced list of warning types with high reliability and severity are reported. The svace warning tool can list these and other available warning types and control whether they are enabled or not.
Svace supports configuration options that may affect the analysis behaviour. Default option values are suitable for most scenarios, but it is possible to tune specific parameters. Changing their values is done using the svace config tool:
svace config THREAD_NUMBER 4
In the example above the THREAD_NUMBER
configuration
option, that controls the number of parallel threads used for analysis,
is set to 4. By default, the THREAD_NUMBER
option has a
special value max
that allows analyzer to use as many
threads as the number of processor cores. Other possible values are
positive numbers to set specific analysis threads number and
auto
to select threads number depending on the current
processor load.
In most cases analysis is the fastest when THREAD_NUMBER
is max
. At the same time each thread uses additional memory
so more threads will use more memory and when the amount of RAM is
limited it is better to decrease this option value.
Use the svace analyze command line tool. The analysis results are available afterwards in the project folder in various formats and can be viewed with a text editor, imported directly into a defect management tool or into a history storage.
A Svace distribution can additionally include Clang Static Analyzer and SpotBugs. Also, a number of lightweight defect detectors are implemented in Svace Java/Kotlin/Go compilers. 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.
In order to control history of source code issues introductions and fixes found during project development the analysis results can be imported into history storage to be shown and processed afterwards. History storage contains the list of warnings ever found in the project, and snapshots of the relevant source code. It also recognizes 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. The history storage functionality is provided by the internal Svace history storage or by a standalone Svacer history server. The latter is advised as it provides more features and is better supported.
Analysis results may be viewed through a web-browser using either a builtin server (when using internal Svace history storage, see svace server) or a standalone Svacer history server.
Each issue detected by Svace is associated with the following information:
Undecided
, Confirmed
,
Won't fix
, False positive
or
Unclear
).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 variables, functions,
classes, etc. 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 helps in understanding the program flow from the
defect origin to its manifestation point, or provides other useful
information related to the warning.
When using a web-server warning evaluation 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 the bin
subfolder of a Svace
distribution (it is recommended to add this folder to the
PATH
environment variable, so that svace
is
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.
server-config Manage server configuration settings.
warning Control which warning types are enabled during
analysis.
experimental:
admin Manage object pool in a project directory.
context-spec Manage context in which particular function will be
analyzed.
export-build Export a build object into an external directory.
history Interact with a warning database.
merge-build Merge multiple build objects into a single build
object.
server Configure or run Svace server.
show Show results in a Svace server (localhost:8060).
spec Manages specifications for third-party library
functions,including user-provided.
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 intercepted command using the same options and environment.
Command line options are as follows:
usage: svace build [options] <build-command>
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. Valid values are: all,
cxx, go, kotlin, python, scala. 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. Supported languages are: cxx, go,
kotlin, python, scala. The special value 'all' enables
all supported languages (which is the default).
--python DIRECTORY or FILE.py
Specify the path to a directory with Python source
code or to a single Python source file to process for
subsequent analysis.
--too-high-kotlin-version-mode {fail,warn,ignore}
Specify what to do if an intercepted Kotlin
compilation uses a language version that is higher
than the maximum version supported by Svace. If set to
'fail' (the default), report a fatal error and don't
produce a build object. Otherwise, attempt to process
such compilations as though they were using the
maximum Kotlin version supported by Svace, and, if set
to 'warn', report a warning.
--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, compilations
performed via compiler APIs (e.g., javac API) are not
intercepted. Since Svace Java agent requires Java 7 or
newer and causes build failure if it's injected into
an older JVM, this option can be useful if build
process involves older Java and compiler API
interception is not needed.
--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).
--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).
-v, --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, mips64el, mipsel, powerpc,
powerpc64, powerpc64le, riscv32, riscv64, sparc,
sparc64, x64, x86. Values other than 'auto' override
automatic target detection. This option is intended
for debugging only.
--enable-ptrace, --disable-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.
--clean-ld-env-vars Remove Svace-specific parts of LD_* environment
variables before the application's main() entry point
is called. This may help if the application complains
about them or is confused by unexpected values. This
option is meaningful only in LD_PRELOAD mode.
--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).
--disable-hash-server
Disable Svace hash server, making it impossible to use
the build data on the analysis phase. This option is
for advanced users only.
--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'.
--go-interception-mode {go-build,compile}
Specify Go interception mode. Supported modes are: go-
build, compile. The default is 'go-build' mode. In
this mode only compilations performed with 'go build'
are supported. The (experimental) 'compile' mode is
build-system-agnostic, so it supports build systems
which don't use 'go build' too (e.g. Bazel). The
'compile' mode implies --enable-ptrace. See --enable-
ptrace for more details.
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.
--json : Output in json format.
-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.
--json : Output in json format.
-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
Performance
Portability
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
SCALA
CSHARP
GO
PYTHON
NONE
Detection tools: Where the checker for the issue is implemented.
Possible values:
SvEng
SvaceCppSpecific
UAST
CSA
SpotBugs
Goa
Mypy
Roslyn
SvaceIR_API
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 several categories:
svace warning DEREF_OF_NULL,DEREF_AFTER_NULL
# Intersect several categories:
svace warning --and CRITICAL,JAVA
# 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
Command server-config
is similar to config
command and has the same parameters and possibilities. The only
difference that command server-config
is needed to set up
config for server.
This is the tool for running analysis. It takes as input the data
produced by the svace build
tool
and produces analysis results 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 or in %) 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. If set to a percentage (e.g. '70%'), Svace
uses the specified percentage of total memory in the
analysis environment.
--enable-language LANG, --disable-language LANG
Enable or disable analysis for the specified
programming language. Valid values are: all, cxx, go,
kotlin, python, scala. 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.
Supported languages are: cxx, go, kotlin, python,
scala. The special value 'all' enables all supported
languages (which is the default).
--enable-uast-language UAST_LANG, --disable-uast-language UAST_LANG
Enable or disable UAST analysis for the specified
programming language. Valid values are: all. See also
'--uast-languages'.
--uast-languages UAST_LANGS
Enable UAST analysis for languages in the specified
comma-separated list and disable it for other
languages. Supported languages are: . The special
value 'all' enables all supported languages. If this
option is not set, it defaults to the subset of
enabled languages supported by UAST analysis.
--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, mips64el, mipsel, powerpc, powerpc64,
powerpc64le, riscv32, riscv64, sparc, sparc64, x64,
x86.
--preset NAME Use the requested warnings preset during the analysis.
--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).
--skip-import-goa Don't import analysis results of Goa generated at the
build phase.
--skip-uast-analysis Don't run uast 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.
--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.
--ignore-file FILE Load regular expressions from FILE for suppress
warnings by path considering. These adding along with
systems and local ignore files.
--settings FILE Load Svace analyze settings from FILE instead of
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 (both console and file). May have values:
quiet, brief, default and verbose.
--console-log-level CONSOLE_LOG_LEVEL
Console log level. May have values: quiet, brief,
default and verbose.
--file-log-level FILE_LOG_LEVEL
Log level for files. May have values: quiet, brief,
default and verbose. File-logs with upper level will
be disabled. For example, if file log has default
level then it won't be created for quite and brief
levels.
--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.
--collect-summary During analysis summary for functions will be stored.
They may be using if parameter '--use-summary' is
specified. Require additional disk space.
--use-summary Analysis will be run in special mode: if function body
can not be found then function summary will be taken
from cache. For cache filling parameter '--collect-
summary' is requiring.
--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.
--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).
--plugin-dir DIR Add directory to search svace-api plugins in. Use this
option multiple times to add multiple directories.
deprecated options (may be removed in a future release):
--import-kotlinc, --skip-import-kotlinc
Analysis inside of Kotlin compiler was replaced with
UAST analysis. This option does nothing now. Consider
using some of UAST options to disable AST checkers.
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.
This section demonstrates the process of using the analyzer with an example (analysis of proftpd-1.3.3).
Let’s assume that Svace is located in ~/svace/
, on an
Ubuntu system and all proftpd build dependency packages are already
installed.
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.
Svace supports analysis for go projects which is using Bazel build system (only for Linux distributions).
In order to analyze go project which is using Bazel build system it is necessary to consider the following:
Before analysis run the following commands (otherwise not all compilations will be intercepted):
bazel clean --expunge # clean up cache
bazel shutdown # stop Bazel server processes or use '--batch' flag
svace build
have to contain the following flag:
--go-interception-mode compile
, without it, the
corresponding compile commands will not be intercepted.
bazel build
have to contain
--spawn_strategy=local
flag. See
https://bazel.build/docs/user-manual#spawn-strategy.
To analyze only go source code, additionally add the
--disable-language all
, --enable-language go
flags, due to which only go compilations will be intercepted/analyzed.
It can be used with both svace build
and
svace analyze
.
For example, the build command of your project is
bazel build //...
, then svace build
will look
like this:
svace build --disable-language all --enable-language go --go-interception-mode compile bazel --batch build --spawn_strategy=local //...
svace build
will also work when using bazelisk.
For example, let’s analyze bazel-ethereum:
Before analysis run:
cd bazel-go-ethereum
bazel clean --expunge
bazel shutdown
Run svace build
:
svace init
svace build --go-interception-mode compile bazelisk build --spawn_strategy=local --define version="local" //...
Run svace analyze
:
svace analyze --disable-language all --enable-language go // C/C++ compilations were also intercepted
Not all found warnings are shown to user. Svace may suppress some warnings based on different conditions.
Information about suppressed warnings is added to folder
.svace-dir/analyze-res/suppress
.
Svace supports many C/C++ compilers and extensions. But in some cases
our compiler can’t compile some specific code. If the problem is in
function body then the compiler marks this function as compiler with
errors and continue parsing a module. The code of such function likely
won’t reflect actual function semantic. As a result Svace analyzer may
produce false positives for this function. Some detectors are very
sensitive to such errors. For example, UNINIT.LOCAL_VAR
emits a warning if local variable is used without initializing. If
initilized code was not compiled this detector will produce many false
positives about using this variable.
Svace have two options for suppression warnings in such functions:
SUPPRESS_WARNINGS_IN_NOT_COMPILED_FUNCTION
and
SUPPRESS_SENSITIVE_WARNINGS_IN_NOT_COMPILED_FUNCTION
. Those
options can be set up via svace config
.
If option SUPPRESS_WARNINGS_IN_NOT_COMPILED_FUNCTION
is
enabled then all warnings in functions, which were compiled with errors,
will be suppressed. If option
SUPPRESS_SENSITIVE_WARNINGS_IN_NOT_COMPILED_FUNCTION
is
enabled (and SUPPRESS_WARNINGS_IN_NOT_COMPILED_FUNCTION
is
disabled) then only checkers, that are sensitive to such errors, will be
suppressed.
It is common practice for static analysis marks that some code does
not have any errors. One way is to add comment NOLINT
to
code. A tool for continious inspection of code quality SonarQube has
comment NOSONAR
for suppression warnings. History server
svacer
reacts on comments svacer_review
. In
python code comment noqa
(NO Quality Assurance) is used to
ignore PEP8 warnings.
Svace supports all above keywords in comments. If Svace emits a warning on a source line with these comments - it will be suppressed.
Examples of supported comments:
//NOSONAR
// NOLINT
// NOLINTNEXTLINE
//nolint
//nolint:unused,deadcode
//svacer_review: -
//svacer_review: -INTEGER_OVERFLOW
//svacer_review: -INTEGER_OVERFLOW|-BUFFER_OVERFLOW
# noqa: E731,E123
#noqa
Svace parses arguments of a comment svacer_review
.
Arguments of all other types are ignored. Comment
nolint:unused,deadcode
means the same as
nolint
. Comment noqa: E731,E123
means the same
as noqa
.
This functionality is controlled by option
SUPPRESS_BY_COMMENT
(disabled by default). Different types
of keywords may be disabled by follow options:
It is possible to add custom keyword by using option
SUPPRESS_BY_COMMENT.TEMPLATE
. The options get regular
expression for keyword in comments.
Note: for suppression Svace identifies comments by only parsing line, where warning is emitted. Now any whole-file parsing is not used here.
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 --with-cache
.
Svace will store results of intermediate operation to analysis
cache.
svace analyze --with-cache
Then program may be changed. Program should be rebuilt and analyzed:
make clean
svace build make
svace analyze --with-cache
Svace will used stored results from previous analysis run. If Svace already has results for intermediate operations than it will be used them. Analysis time should be increased.
Parameter --with-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 efficiently 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 of 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, then 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 recommend to use cache for this analysis.
Spotbugs analysis. It also does not have inter module analysis. It is based on compilation unit (run of javac). We recommend 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.
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 reanalyzed. Svace analysis spends most time for function analysis. Enabling cache for function analysis may significantly increase analysis time. This analysis depends on 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 further analysis will be done with cache until this option will be disabled.
Second way is to use parameter –with-cache:
svace analyze --with-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 analysisPERSISTENT_MODE.SPOTBUGS
— for spotbugs analysisPERSISTENT_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 databases are used. Every
operation stores data to its own directory:
.svace-dir/analyze-storage/csa
— CSA 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 analysisLogs 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 taken 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 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 depend on 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.
Main Svace analysis engine uses summary based analysis. Such analysis traverse functions according call graph starting from leaves. Called functions are analyzed first. For analysing call instruction Svace uses summary for called function.
In many cases a project contains main part and library part. The code for library part are not changed very often. For those cases it may be useful to analyze library code only once and remember summaries for functions from library. After it only main part can be analyzed without library code. In many cases it can significally increase analysis speed.
Svace has special analysis modes for supporting such scenarios.
For example our build command contains two build scripts:
Analysis process consist from follow commands:
svace init
svace build lib.sh && main.sh
svace analyze
For collection summaries library code should be built and analyzed
with parameter --collect-summary
:
svace init
svace build lib.sh
svace analyze --collect-summary
Svace will analyze library code and put summaries to folder
.svace-dir/analyze-storage/annots
.
Now for using those summaries it is required to build user code and
analyze with parameter --use-summary
:
svace init
svace build main.sh
svace analyze --use-summary
Svace uses names for identifying calls. It is not precise in many cases. During regular analysis information from linkage may be used.
If library code calls functions from user code (by pointers) then Svace won’t analyzed such calls.
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 doing it we have to run follow command:
svace remote --host remove-server analyze
After running this command, Svace passes all needed data to remote
server and performs analysis there. Command remote
returns
control back when analysis on server is finished. Then it puts results
into a .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:
Server may be tuned by changing options in file
.svace-dir/conf/svace.conf
on remote machine. Server reads
this file at starting. For applying settings from this file server
restart is required.
Available options:
SERVER_PORT
- allows to change used port.SIMULTANEOUS_ANALYSIS_LIMIT
- change number of
simultaneously run analysis on server. It is 3 by default. It is
possible to change weight of analysis by using parameter
--weight
: svace remote --weight analyze
. In
this case analysis will be started on the server only if there are more
free slots than analysis weight. If weight is lager than
SIMULTANEOUS_ANALYSIS_LIMIT
, then the analysis will be
never started.ZIP_ANALYZE_RES
- if enabled Svace will compress folder
analyze-res to zip file. This option affects remote analysis too and may
be used to transferring analysis results to client machine.REJECT_CLIENT_CONFIGS
- set up a special server config
which ignore user setting and warning configs. This option is disabled
by default. In this case user can set up configs for settings and
warnings on client machine which will be used on server.JOIN_CLIENT_CONFIGS
- set up a special server config
which enable a warning if it is enabled either on server or on client.
This option is disabled by default.DELETE_ANALYZE_DIR_ALL
- working directory on server
will be removed after analysis.DELETE_ANALYZE_DIR_SUCCESS
- working directory on
server will be removed after analysis if analysis was successful. For
analyses, which were not analyzed due to errors, working directory won’t
be removed.CREATE_PROJECT_IF_NOT_EXIST
- if project with given nam
does not exist on remote server, then it will be created in group
main
if current user has right for modifying this
group.Data are passed to server by network using port 55135. It may be
changed by modifying option SERVER_PORT 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 a little 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 may perform several analysis. By default,
limit is 3. It is possible to change such behavior by option
SIMULTANEOUS_ANALYSIS_LIMIT
. 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. An option ZIP_ANALYZE_RES
enables Svace will
compress folder analyze-res
to zip file. This option
affects remote analysis too and may be used to transferring analysis
results to client machine.
In some cases it is required to stop analysis on remote server. Svace
has special commands for it svace remote kill
. The command
has argument - hash of analysis task.
After starting remote analysis by command
svace remote analyze
you can find this task by 3 ways:
http://localhost:8060/status
.remote analyze
prints this hash before sending
command to remote server. For example:Starting remote analysis task '389eb8d4735c84774a96be9661b14b6b9b30f759' on remote server.
.svace-dir/analyze-res/used-task-object
, which contains
task hash.Example of killing remote analysis:
svace remote kill 389eb8d4735c84774a96be9661b14b6b9b30f759
The commands prints that kill command was sent to server. It does not return error if task with such hash is not run on server. Remote server will kill correspondent svace analysis by OS-specific methods. Svace remote server does not have its own ways to stop specified analysis.
Clients of killed analysis will got message about it: “Analysis failed: Remote error: Analysis were killed by client”.
It is possible to run kill
command without
arguments:
svace remote kill
In this case Svace will read file
.svace-dir/analyze-res/used-task-object
and send kill
command with hash from this file. The later means that latest started
analysis job will be killed.
After starting Svace collect some metrics and output them in
prometheus format (https://prometheus.io). Those
metrics can be found by address
http:<host>:8060/metrics
.
Follow metrics are supported: - analysed_projects - number of
analyzed projects with status. Follow statuses are possible: - Success:
project was successfully analyzed. - Failed: project analysed was
finished by some error. - Cancelled: project analysis was killed. -
Queued: project is in queue. - InProgress: project is currently being
analyzed. The metric has labels: status and user. - tasks_killed -
number of killed tasks. - tasks_killed_requests - number of requests for
task killing. - queue_size - size of analysis queue. -
task_creation_time - time of task creation. - task_start_time - time of
task starting. - task_end_time - time of task end. - analysis_time -
time of analysis. - analysis_time_by_user - time of analysis by user.
The metric calculate the same value as
analysis_time_by_user
but split it by users. It has label
user
. - queued_time - time of task, which it spend in
analysis queue before analysis start. - queued_time_by_user - the same
as queued_time
but with information about user. It has
label user
.
Svace has integrated history server, which allows to review results after analysis. We recommend to use this server only for convenient watching result after analysis. The server is not intended for long term usage as history server. For purpose of reviewring warnings we recommend to use history server svacer.
Remote analysis server is implemented as a part of history server.
The server has mode for fast running: single-start
. For
running it, svace directory should be created by commands
init
:
svace init
After it the server can be run by command:
svace server single-start
The server prints follow lines to console:
You are starting Svace history server in single-project mode. In this mode server accepts any credentials and gives unlimited access to one selected project to anyone who can access your computer via network.
Remote analysis server started at port 55135
Web server started at port 8060
Press Ctrl+C to interrupt the process
Results of web server can be accessed by address
localhost:8060
.
Remote analysis server is also started and use default port
55135
. The ports can be changes by options
WEB_SERVER_PORT
and SERVER_PORT
by utility
svace server-config
.
Main server mode has several directories, where results may be stored. For initializing directory for server follow command should be used:
svace server init
Command svace server admin
allows to manage svace
server.
For fast settings the server command
svace server admin create-defaults
.
The server can be run by command:
svace server start
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 MEMORY_LEAK warning type has a suppressed subtype MEMORY_LEAK.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 to 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.
This subtype of null dereference checkers indicates that the value is dereferenced in the same expression where it is created.
This subtype indicates that the issue relates to passing a value to a function parameter with specific annotation.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Kotlin | Quality | Normal | High | Yes |
C# | Quality | Critical | High | Yes |
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 synthetic, because it’s quite difficult to
create Kotlin code where Svace will report a 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | Yes |
Kotlin | Quality | Normal | Average | Yes |
Related CWEs: CWE476.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Go | Quality | Critical | High | Yes |
Kotlin | Quality | Normal | High | Yes |
Related CWEs: CWE476.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Go | Quality | Major | Average | Yes |
Kotlin | Quality | Normal | Average | Yes |
Related CWEs: CWE476.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | High | Yes |
C/C++ | Quality | Normal | High | Yes |
Go | Quality | Normal | High | Yes |
Kotlin | Quality | Normal | High | Yes |
This checker detects issues where a NULL
pointer value
is passed to a function, which dereferences it under some uncontrolled
conditions.
#include <stddef.h>
extern int get_data();
extern void consume(int);
void deref_maybe(int *p) {
int data = get_data();
(data);
consumeif (data > 1) {
*p += data;
}
}
void example() {
(NULL);
deref_maybe}
void deref_fixed(int *p) {
int data = get_data();
(data);
consumeif (p && data > 1) {
*p += data;
}
}
void example_fixed() {
(NULL);
deref_fixed}
Function example
illustrates the defect:
NULL
pointer value is passed to deref_maybe
function, which dereferences it, if the value of data
returned by get_data
function is greater than 1.
Functions deref_fixed
and example_fixed
illustrate a possible fix.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE476.
This checker detects issues where a NULL
pointer value
is passed to a function, which dereferences it under certain
conditions.
#include <stddef.h>
extern void use(int);
void deref_if(int *p, int x) {
if (x > 1) {
-= *p;
x }
(x);
use}
void example(int x) {
(NULL, x);
deref_if}
void example_fixed(int x) {
(NULL, x > 1 ? 1 : x);
deref_if}
Function example
illustrates the defect:
NULL
pointer value is passed to deref_if
function, which dereferences it, if the passed value of x
is greater than 1.
Function example_fixed
illustrates a possible fix.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
C/C++ | Quality | Normal | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Related CWEs: CWE476.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | No |
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(8192);
[0] = '\0';
buf}
Note: the detector suppresses warnings for cases with small amount of
memory is allocated. The limit can be changed by option
DEREF_OF_NULL_RET_ALLOC.SMALL_ARGUMENT
. Svace emits
suppressed warnings with type
DEREF_OF_NULL.RET.ALLOC.MINOR
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Average | No |
Related CWEs: CWE690.
It is subtype of DEREF_OF_NULL.RET.ALLOC
for cases where
small amount of memory is allocated. The limit can be changed by option
DEREF_OF_NULL_RET_ALLOC.SMALL_ARGUMENT
.
void test() {
char* buf = (char*) malloc(50);
[0] = '\0';
buf}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Average | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
Go | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
C/C++ | Quality | Normal | Unknown | No |
Go | Quality | Normal | Unknown | No |
Kotlin | Quality | Normal | Unknown | Yes |
It is a subtype of checker DEREF_OF_NULL.RET
. It has the
same logic. The only difference that the checker emits warning for cases
where pointer is passed to special assert
-function, which
throw an exception if pointer is null. For example
Objects.requireNonNull
or kotlin’s operator
!!
.
class Example {
String returnIfTrue(boolean x) {
if (x)
return "ok";
else
return null;
}
void testWithDereference(boolean x) {
String s = returnIfTrue(x);
.toString(); //regular emitted DEREF_OF_NULL.RET
s}
void testWithAssert(boolean x) {
String s = returnIfTrue(x);
.requireNonNull(s, "null"); //DEREF_OF_NULL.RET.ASSERT
Objects}
}
class ArrayWrapper {
private var array = IntArray(10)
public operator fun get(index: Int): Int? {
return if (index < array.size) array[index] else null
}
}
fun foo(arr: ArrayWrapper, idx: Int) {
val someElem: Int = arr[idx]!! //DEREF_OF_NULL.RET.ASSERT
//...
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Related CWEs: CWE476.
This detector is a statistical version of DEREF_OF_NULL.RET.
It detects issues when a pointer returned by call to a function is
dereferenced without a check to NULL
, while for a number of
other calls to the same function it is dereferenced only after a proper
NULL
check.
#include <stddef.h>
extern int *get_ptr(int);
void example() {
int *p1 = get_ptr(1);
if (p1)
*p1 = 3;
int *p2 = get_ptr(2);
if (p2)
*p2 = 1;
int *p3 = get_ptr(3);
if (p3)
*p3 = 4;
int *p4 = get_ptr(4);
if (p4)
*p4 = 1;
int *p5 = get_ptr(5);
//if (p5)
*p5 = 5;
int *p6 = get_ptr(6);
if (p6)
*p6 = 9;
int *p7 = get_ptr(7);
if (p7)
*p7 = 2;
int *p8 = get_ptr(8);
if (p8)
*p8 = 6;
}
Function example
illustrates the defect: each time the
function get_ptr
is called, the returned value is checked
to NULL
before its dereference, except for variable
p5
.
Possible fix is to uncomment the commented if
, which
checks pointer p5
before its dereference.
STAT.EXAMPLES_NUMBER
GROUP_RESULTS_OF_STAT_CHECKERS
GROUP_RESULTS_OF_DEREF_OF_NULL.RET.STAT
DEREF_OF_NULL.RET.STAT.THRESHOLD
DEREF_OF_NULL.RET.STAT.MAX_FAILS_IN_FILE
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | High | No |
Kotlin | Quality | Normal | High | No |
This is a subtype of DEREF_OF_NULL.RET that indicates that a
possibly NULL
value returned by some function is used as an
argument in a call to another function and thus passed to a parameter
which has a NotNull
or NonNull
annotation.
class Example {
public void test_helper(@NotNull String s) {
}
public void test(int i) {
String s = getString(i);
test_helper(s); // DEREF_OF_NULL.RET.ANNOT
}
private String getString(int i) {
String s = i % 2 == 0 ? "even" : null;
return s;
}
}
The defect is demonstrated by method test
- a call to
method getString
may return NULL
, depending on
the value of its integer argument. The returned value is stored in
variable s
which is then passed to the parameter of method
test_helper
that has a NotNull
annotation.
A possible fix is to place the call to test_helper
method under an if
statement that checks that the value of
variable s
is not NULL
or remove the
NotNull
annotation on the parameter of
test_helper
method.
Due to known issue, false positive warnings of this type can be
issued where a possibly NULL
value returned from some
function is used as an argument in a call to another function of
specific form. Namely, the return value of this function should have
NonNull
or NotNull
annotation while the
parameter receiving the value with Nullable
annotation
should also have Nullable
annotation. Moreover, the
function’s code should return the value of that parameter, while it’s
free to perform any operations on that parameter prior to that.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | High | Yes |
Related CWEs: CWE476.
This checker finds issues where pointer value is the result of
dynamic_cast
C++ operator, it might be NULL
,
and it is dereferenced without an appropriate check.
class A {
public:
virtual int a() { return 0; }
};
class B : public A {
public:
virtual int a() { return 1; }
virtual int b() { return 0; }
};
int example(A *a) {
*b = dynamic_cast<B*>(a);
B return b->b();
}
int example_fixed(A *a) {
*b = dynamic_cast<B*>(a);
B if (!b)
return -1;
return b->b();
}
Function example
illustrates the defect: pointer
b
is the result of dynamic_cast
operator, and
it is dereferenced without any check.
Function example_fixed
illustrates a possible fix.
Sometimes, a complex program logic might guarantee that
dynamic_cast
can be used safely without any additional
checks, while the analysis is not able to prove it.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
Kotlin | Quality | Normal | Unknown | No |
This checker issues warnings where some expression is used as an
argument in a function call and thus passed to a parameter which has a
NotNull
or NonNull
annotation but the value of
that expression can only be NULL
at the call site.
class Example {
public void test_helper(@NotNull String s) {
}
public void test(String s) {
if (s == null)
test_helper(s); // DEREF_OF_NULL.ANNOT
else
test_helper(s); // No warning, variable s is not null!
}
}
Method test
illustrates this defect - the value of
variable s
is checked for being NULL
and then
passed to a parameter of method test_helper
which has
NotNull
annotation. In the else
clause of the
same if
statement, where the value of variable
s
is definitely not NULL
, using it as the
argument of the call to the same test_helper
method doesn’t
cause this warning to be issued.
The issue can be fixed by removing the NotNull
annotation on the parameter of test_helper
method in this
case.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | No |
Kotlin | Quality | Normal | Low | No |
This checker detects situations where literal NULL
value
or a variable initialized by literal NULL
value is used as
an argument in a function call and thus passed to a parameter which has
a NotNull
or NonNull
annotation.
class Example {
public void test_helper(@NotNull String s) {
}
public void test1(int i) {
String s = null;
test_helper(s); // DEREF_OF_NULL.ANNOT.CONST
}
public void test2() {
test_helper(null); // DEREF_OF_NULL.ANNOT.CONST
}
}
Methods test1
and test2
exemplify the two
possible ways for this defect to occur. In the former case, variable
s
is initialized by literal NULL
value and
then passed to the parameter of method test_helper
which
has a NotNull
annotation. In the latter case, literal
NULL
is passed directly to the same parameter of
test_helper
method.
Possible fix in both cases is to remove the NotNull
annotation on the parameter of test_helper
method.
Due to known issue, this checker can produce false positive findings in Kotlin projects where a call to typeOf function from Kotlin reflection API is used as an argument in a function call and passed to a parameter with a non-nullable type.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | VeryHigh | No |
Kotlin | Quality | Minor | VeryHigh | No |
This checker finds issues where a value which bears a
Nullable
annotation is used as an argument in a function
call and thus passed to a parameter with NotNull
or
NonNull
annotation.
class Example {
@Nullable private String s
public void test_helper(@NotNull String s) {
}
public void test1(@Nullable String s) {
test_helper(s); // DEREF_OF_NULL.ANNOT.STRICT
}
public void test2() {
test_helper(this.s); // DEREF_OF_NULL.ANNOT.STRICT
}
}
The defect is shown in methods test1
and
test2
. In the former, a parameter with
Nullable
annotation is passed to parameter with
NotNull
annotation in a call to method
test_helper
. In the latter, a field declared with
Nullable
annotation is similarly passed to the same
parameter of method test_helper
.
Possible fixes include removal of either annotation or both of them,
or placing calls to test_helper
method under
if
statements that check that the value passed to the
parameter with NotNull
annotation is in fact not
NULL
.
Due to known issue, false positive warnings of this type can be
issued where a value which bears Nullable
annotation is
used as an argument in a call to another function of specific form.
Namely, the return value of this function should have
NonNull
or NotNull
annotation while the
parameter receiving the value with Nullable
annotation
should also have Nullable
annotation. Moreover, the
function’s code should return the value of that parameter, while it’s
free to perform any operations on that parameter prior to that.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
Kotlin | Quality | Normal | Unknown | No |
This checker finds situations where NULL
value is passed
to a function and later inside that function, under conditions
independent of this value, it is used as an argument in a call to
another function, whereby it is passed to a parameter which has
NotNull
or NonNull
annotation.
class Example {
public void test_helper(@NotNull String s) {
}
public void test(String s1, String s2) {
if (s1 != null)
test_helper(s1);
else
test_helper(s2);
}
public void foo() {
test(null, null); // DEREF_OF_NULL.ANNOT.COND
}
}
The defect is illustrated by method foo
-
NULL
value passed in the call to test
method
to parameter s2
may later be used in the call to
test_helper
method and thus be passed to a parameter with
NotNull
annotation, depending on the value passed to
parameter s1
of test
method.
The issue can be fixed by putting the call to
test_helper
method under condition that checks that the
value passed to that call is not NULL
. Alternatively, the
NotNull
annotation on the parameter of
test_helper
method can be removed.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | No |
Kotlin | Quality | Normal | Average | No |
This checker produces warnings if some variable is first assigned
NULL
value and later on it is used as an argument in a
function call and thus passed to a parameter with NotNull
or NonNull
annotation. Note that the value of the variable
may be changed along some execution paths leading to the call site; the
important point is that the paths that leave the NULL
value
intact must lead to the call site as well.
class Example {
private String str;
public void test_helper(@NotNull String s) {
}
public void test(boolean cond) {
String s = null;
if (cond)
= "string";
s
test_helper(s); // DEREF_OF_NULL.ANNOT.ASSIGN
}
public NotNullAnnot(boolean cond) {
if (cond)
= "string";
str
test_helper(str); // DEREF_OF_NULL.ANNOT.ASSIGN
}
}
Method test
and the constructor of Example class
exemplify the defect. In the former case, a local variable
s
is first assigned a NULL
value which may be
changed depending on the parameter cond
and then is used in
a call to method test_helper
whereby it is passed to
parameter s
which has NotNull
annotation. In
the latter case, uninitialized field str
which has default
NULL
value may be changed depending on the constructor
parameter cond
and then similarly is passed to the same
parameter of method test_helper
.
Possible fix is to put the call to test_helper
method
under if
statement checking that the value being passed to
it is not NULL
. Alternatively, the NotNull
annotation on the parameter of test_helper
method can be
removed.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
Kotlin | Quality | Normal | Unknown | No |
This checker detects situations where a value that may be
NULL
under some condition is used as an argument in a
function call and thus passed to a parameter with NotNull
or NonNull
annotation under another condition which is not
mutually exclusive with the first condition.
class Example {
private int x;
public void test_helper(@NotNull String s) {
}
public void test1(Object o) {
String s = null;
if (o instanceof String)
= (String) o;
s
if (x > 0)
test_helper(s); // DEREF_OF_NULL.ANNOT.EX
}
public void test2(Object o, boolean b) {
String s = null;
if (o instanceof String)
= (String) o;
s else
= false;
b
if (b)
test_helper(s); // No warning here, as b is false when s is null
}
}
The defect is shown in methods test1
and
test2
. In the first case, the value of local variable
s
is NULL
under condition which is totally
independent of the condition under which that variable is used in a call
to test_helper
method and passed to the parameter which has
NotNull
annotation. Therefore, the warning is emitted in
this case. By contrast, in the second case the condition under which the
value of s
is NULL
and condition under which
s
is passed to the same parameter of
test_helper
method are mutually exclusive, thus the issue
is not detected.
Possible fix in the first case is either to remove the
NotNull
annotation on the parameter of
test_helper
method or augment the condition under which
test_helper
method is called so that it also checks that
the value of s
at the call site is not
NULL
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
Kotlin | Quality | Major | Unknown | No |
This checker reports issues where a NULL
value is used
as an argument in a function call under certain conditions and thus
passed to a parameter with NotNull
or NonNull
annotation.
class Example {
public void func(@NotNull String s) {
}
void passIfTrue(String str, int f1) {
if (f1 > 0)
func(str);
}
void test(int f1, int f2) {
String str = null;
passIfTrue(str, f1);
}
}
Method test
exemplifies the defect - variable
str
is assigned NULL
value and then used in a
call to method passIfTrue
whereby, if the other argument
passed in the same call is positive, that NULL
value is
passed to the parameter of method func
which bears
NotNull
annotation.
Possible fixes include removal of NotNull
or
NonNull
annotation on the parameter of func
method, extending the condition in passIfTrue
method to
ensure the argument used in the call to func
method is not
NULL
or placing the call to passIfTrue
method
under condition that excludes using NULL
value as the first
argument and a positive value as the second argument of that call.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Go | Quality | Critical | High | Yes |
Kotlin | Quality | Normal | High | Yes |
C# | Quality | Major | Average | Yes |
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.
C# warnings can have any combination of these subtypes in the
following order: .ARGUMENT.INSTANT
. There is no (SVACE-WARN
DEREF_AFTER_NULL.EX) for C# because DEREF_AFTER_NULL
is
already path-sensitive.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | Yes |
Kotlin | Quality | Normal | Average | Yes |
Related CWEs: CWE476.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
This checker detects issues where a pointer is compared to
NULL
(which indicates that it could be NULL
)
and then passed to a function, which dereferences it under some
uncontrolled conditions.
extern int get_some_data();
void deref_under_some_condition(int *p) {
if (get_some_data() > 0) {
*p = -1;
}
}
void example(int *p) {
if (p) {
*p = 123;
}
(p);
deref_under_some_condition}
void example_fixed(int *p) {
if (p) {
*p = 123;
}
if (p) {
(p);
deref_under_some_condition}
}
Function example
illustrates the defect: variable
p
is compared to NULL
and then passed to
function deref_under_some_condition
which could dereference
it under a condition, that depends on some uncontrolled integer value
(returned by function get_some_data
).
Function example_fixed
illustrates a possible fix.
In some cases the checker might miss information that the condition of the dereference within the called function is not feasible with the precondition of the call itself.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Go | Quality | Major | Average | Yes |
This checker detects issues where a pointer is compared to
NULL
(which indicates that it could be NULL
)
and then passed to a function, which dereferences it under certain
conditions.
void deref_if_x_is_positive(int *p, int x) {
if (x > 0) {
*p = x;
}
}
void example(int *p, int x) {
if (p) {
*p = 123;
}
(p, x);
deref_if_x_is_positive}
void example_fixed(int *p, int x) {
if (p) {
*p = 123;
}
if (p) {
(p, x);
deref_if_x_is_positive}
}
Function example
illustrates the defect: pointer
p
is compared to NULL
and then passed to
function deref_if_x_is_positive
which could dereference it,
if condition x > 0
is true.
Function example_fixed
illustrates a possible fix.
In some cases the checker might miss information that the condition of the dereference within the called function is not feasible with the precondition of the call itself.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | Yes |
C/C++ | Quality | Normal | Average | Yes |
Related CWEs: CWE476.
This checker detects issues where a pointer is compared to
NULL
(which indicates that it could be NULL
)
in a loop condition, and then the pointer is dereferenced.
struct Item {
int id;
int value;
struct Item *next;
};
int example(Item *start, int id) {
*item = start;
Item while (item && item->id != id) {
= item->next;
item }
return item->value;
}
int example_fixed(Item *start, int id) {
*item = start;
Item while (item && item->id != id) {
= item->next;
item }
if (!item)
return 0;
return item->value;
}
Function example
illustrates the defect: variable
item
is compared to NULL
in a
while
loop condition and then dereferenced after the
loop.
Function example_fixed
illustrates a possible fix.
Sometimes, when a loop uses several conditions which controls its execution, the assumption that the comparison implies NULL value might not be correct for the path, where the warning is reported, due to some additional unexplicit preconditions.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Suppressed | Major | Average | Yes |
C/C++ | Suppressed | Major | Average | Yes |
Related CWEs: CWE476.
This checker detects issues where a pointer is compared to
NULL
(which indicates that it could be NULL
),
and then dereferenced on a path, where the program execution might be
terminated, while the analysis is not able to ensure, whether it avoids
NULL
pointer dereference, or not.
#include <stdlib.h>
extern int get_error_level(int error_id);
void handle_error(int error_id) {
if (get_error_level(error_id) > 1) {
(1);
exit}
}
void example(int *p) {
if (!p) {
(313);
handle_error}
*p = -1;
}
void example_fixed(int *p) {
if (!p) {
(313);
handle_error} else {
*p = -1;
}
}
Function example
illustrates the defect: variable
p
is compared to NULL
and then dereferenced,
while on a path, where it is null function handle_error
is
called, which might terminate the program execution.
Function example_fixed
illustrates a possible fix.
Sometimes, the program logic guarantees that the called function will
always terminate the program execution, when the pointer is
NULL
, but the analysis is not able to prove it.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Kotlin | Suppressed | Normal | Unknown | No |
A subtype of DEREF_AFTER_NULL, where a null check happens inside of an inlined code.
data class Person(val id: Int, val name: String)
fun example(id: Int, persons: List<Person>): String {
return persons.find { it.id == id }!!.name
}
fun possibleFix(id: Int, persons: List<Person>): String? {
return persons.find { it.id == id }?.name
}
Function example
illustrates the defect: higher-order
function find
may return null, if there was no entry with
the given id
, so !!
operator will fail with an
exception.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Kotlin | Quality | Normal | High | Yes |
C# | Quality | Major | Average | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Minor | Average | No |
C/C++ | CodingStyle | Minor | Average | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Kotlin | Quality | Normal | High | Yes |
This is a Kotlin-specific checker similar to NULL_AFTER_DEREF which issues warnings if a variable has the non-null assertion operator (!!) applied to it prior to being compared to null.
fun test(s: String?): Int? {
val ss = s!!
return ss?.length
}
Function test
shows the defect - parameter
s
of nullable type has the not-null assertion operator (!!)
applied. If the value of s
(as passed by caller) is null,
NullPointerException will be thrown and execution won’t proceed to the
next line. Thus, the null-check via null-safe member access operator
(?.) on the next line is unnecessary.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Kotlin | Quality | Normal | High | Yes |
This Kotlin-specific checker detects situations where a variable has the unsafe cast operator (as operator with non-nullable target type) applied to it prior to being compared to null.
fun test(s: String?): Int? {
val ss = s as String
return ss?.length
}
Function test
exemplifies the defect - parameter
s
of nullable type has the unsafe cast operator applied. If
the value of s
(as passed by caller) is null,
ClassCastException will be thrown and execution won’t proceed to the
next line. Thus, the null-check via null-safe member access operator
(?.) on the next line is unnecessary.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Average | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Critical | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Scala | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | Yes |
Python | Quality | Critical | Average | Yes |
Related CWEs: CWE121, CWE122, CWE129, CWE194, CWE195, CWE20, CWE400, CWE606, CWE789.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Suppressed | Major | Average | Yes |
C/C++ | Suppressed | Major | Average | Yes |
Scala | Suppressed | Major | Average | Yes |
Go | Suppressed | Major | Average | Yes |
Related CWEs: CWE121, CWE122, CWE129, CWE194, CWE195, CWE20, CWE400, CWE606, CWE789.
.MIGHT variant of TAINTED_INT, which finds situations where tainted value is used in an unsafe operation, where value is tainted only on some execution paths.
void test(char* str, int flag) {
int count;
if (flag) {
= atoi(str);
count } else {
= -1;
count }
(count);
usleep}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Major | Unknown | No |
Scala | Quality | Major | Unknown | No |
Go | Quality | Major | Unknown | No |
Related CWEs: CWE121, CWE122, CWE129, CWE194, CWE195, CWE20, CWE400, CWE606, CWE789.
.COND variant of TAINTED_INT, which finds situations where tainted value is used in an unsafe operation, where tainted value is passed to function, which uses it only on some execution paths.
package main
import (
"flag"
"strconv"
)
func allocIf(size, mode int) {
if mode > 10 {
return
}
(size)
allocSlice}
func allocSlice(size int)[]string {
var s = make([]string, size)
return s
}
func main() {
:= flag.String("size", "0", "")
sizeArg := flag.String("size", "0", "")
modeArg .Parse()
flag, _ := strconv.Atoi(*sizeArg)
size, _ := strconv.Atoi(*modeArg)
mode
(size, mode) //TAINTED_INT.COND
allocIf}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Major | Unknown | No |
Scala | Quality | Major | Unknown | No |
Go | Quality | Major | Unknown | No |
Related CWEs: CWE121, CWE122, CWE129, CWE194, CWE195, CWE20, CWE400, CWE606, CWE789.
It is variant of TAINTED_INT with features of both .COND and .MIGHT types. The detector finds situations where tainted value is used in an unsafe operation, and it is used in an operation where uncontrolled value may cause problems. The value is tainted only on some pathes, and it is uses also only on some pathes.
package main
import (
"flag"
"strconv"
)
func allocIf(size, mode int) {
if mode > 10 {
return
}
(size)
allocSlice}
func allocSlice(size int)[]string {
var s = make([]string, size)
return s
}
func main() {
:= flag.String("size", "0", "")
sizeArg := flag.String("size", "0", "")
modeArg .Parse()
flag, _ := strconv.Atoi(*sizeArg)
size, _ := strconv.Atoi(*modeArg)
mode
if mode < 20 {
//now for some pathes size is not controlled by user
= 10
size }
(size, mode) //TAINTED_INT.MIGHT.COND
allocIf}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE606.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Suppressed | Major | Unknown | No |
Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE606.
.MIGHT variant of TAINTED_INT.CTYPE, where value is tainted only on some execution paths.
char ch = '0';
void int_type(int val) {
if (val < 0) {
("%d", &val);
scanf}
if (isdigit(val)) // Here the program may crash if val < 0; TAINTED_INT.CTYPE.MIGHT is emitted.
= (char) val;
ch }
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Critical | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Scala | Quality | Critical | High | Yes |
Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE400, CWE606.
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.
There is a .MIGHT variant of this warning (TAINTED_INT.LOOP.MIGHT).
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Suppressed | Critical | Average | Yes |
C/C++ | Suppressed | Critical | Average | Yes |
Scala | Suppressed | Critical | Average | Yes |
Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE400, CWE606.
.MIGHT variant of TAINTED_INT.LOOP, which find situations where loop bound is tainted only on some execution paths.
size_t size;
char* res;
void test(bool flag) {
int i;
if (flag) {
char* env = getenv("QQQ");
= strtoul(env, NULL, 10);
size } else {
= 0;
size }
for (i = 0; i < size; i++) {
// ...
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Scala | Quality | Major | Average | Yes |
A subtype of TAINTED_INT, where the tainted value is used as a loop step, and thus may cause the loop to be infinite.
There is a .MIGHT variant of this warning (TAINTED_INT.INFINITE_LOOP.MIGHT).
void test() {
int step = getchar();
for (char i = 0; i < 100; i += step) {
// ...
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Scala | Quality | Major | Average | Yes |
.MIGHT variant of TAINTED_INT.INFINITE_LOOP, which find situations where loop step is tainted only on some execution paths.
void test(bool flag) {
int step;
if (flag) {
= getchar();
step } else {
= 1;
step }
for (char i = 0; i < 100; i += step) {
// ...
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Scala | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | Yes |
Kotlin | Quality | Critical | Average | Yes |
Python | Quality | Critical | Average | Yes |
Related CWEs: CWE121, CWE122, CWE124, CWE126, CWE127, CWE129, CWE194, CWE195, CWE20, CWE606.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Suppressed | Major | Unknown | Yes |
C/C++ | Suppressed | Major | Unknown | Yes |
Scala | Suppressed | Major | Unknown | Yes |
Related CWEs: CWE121, CWE122, CWE124, CWE127, CWE129, CWE194, CWE195, CWE20, CWE606.
.MIGHT version of TAINTED_ARRAY_INDEX, which checks if out-of-bounds array access could happen on some execution paths.
void array_index(bool flag) {
int buf[256];
int index = getchar();
if (flag && index < 256) {
// Index may still be negative!
[index] = 7;
buf}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Scala | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | No |
Related CWEs: CWE121, CWE122, CWE126, CWE129, CWE606.
Extended version of TAINTED_ARRAY_INDEX, which checks if out-of-bounds array access could happen on some execution paths. Very similar to TAINTED_ARRAY_INDEX.MIGHT, but can find more diffucult cases.
int bounded_getchar(int bound) {
int res = getchar();
return res > bound ? bound : res;
}
void array_index_error(int bound) {
int buf[256];
// If bound > 256, index may be equal to 256
int index = bounded_getchar(bound > 256 ? 256 : bound);
if (index >= 0) {
// Index may still be equal to 256
[index] = 7;
buf}
}
void possible_fix(int bound) {
int buf[256];
// Index is not greater than 255
int index = bounded_getchar(bound > 255 ? 255 : bound);
if (index >= 0) {
// No error here
[index] = 7;
buf}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Critical | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Scala | Quality | Critical | High | Yes |
Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE606.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Suppressed | Major | High | Yes |
C/C++ | Suppressed | Major | High | Yes |
Scala | Suppressed | Major | High | Yes |
Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE606.
.MIGHT variant of TAINTED_INT.PTR, which checks if out-of-bounds array access could happen on some execution paths.
void set_base(char* base, bool flag) { // Here 'base' an unknown pointer that probably points to an array.
char* env = getenv("QQQ");
size_t size = strtoul(env, NULL, 10);
if (flag) {
[size] = '\0'; // Accessing an array through pointer `base` by tainted index `size`.
base}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Critical | VeryHigh | Yes |
C/C++ | Quality | Critical | VeryHigh | Yes |
Scala | Quality | Critical | VeryHigh | Yes |
Go | Quality | Critical | VeryHigh | Yes |
Python | Quality | Critical | VeryHigh | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Suppressed | Critical | High | Yes |
C/C++ | Suppressed | Critical | High | Yes |
Scala | Suppressed | Critical | High | Yes |
.MIGHT variant of TAINTED_PTR, which find situations where string is tainted only on some execution paths.
char* env;
char buf[100];
void test(bool flag) {
if (flag) {
= getenv("VAR3");
env } else {
= "NONE";
env }
// Copying a string of unbounded size to a fixed-size buffer.
(buf, env);
strcpy}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Major | Unknown | No |
Scala | Quality | Major | Unknown | No |
Go | Quality | Major | Unknown | No |
Related CWEs: CWE20.
It is .COND subtype of TAINTED_PTR
for situations where called function pass tainted value to critical
operations only for some pathes.
const char* env;
char buf[100];
#define COPY_MODE 10
void copyIfNeeded(const char*ptr, int mode) {
if (mode == COPY_MODE)
(buf, ptr);
strcpy}
char* getEnv() {
return getenv("CODE10");
}
void test(int mode) {
= getEnv();
env
// Copying a string of unbounded size to a fixed-size buffer.
(env, mode);
copyIfNeeded}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Major | Unknown | No |
Scala | Quality | Major | Unknown | No |
Go | Quality | Major | Unknown | No |
Related CWEs: CWE20.
It is subtype of TAINTED_PTR
with both features .MIGHT and .COND. So this checker
emits situations where value is tainted only on some paths and it is
passed to function, which uses this value in critical operation not on
all paths.
const char* env;
char buf[100];
#define COPY_MODE 10
void copyIfNeeded(const char*ptr, int mode) {
if (mode == COPY_MODE)
(buf, ptr);
strcpy}
char* getEnv() {
return getenv("CODE10");
}
void test(int mode) {
= getEnv();
env if (mode<0)
= "-";
env
(env, mode);
copyIfNeeded}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Critical | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Scala | Quality | Critical | High | Yes |
Python | Quality | Critical | High | Yes |
Related CWEs: CWE120, CWE134, CWE20.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | Yes |
C/C++ | Quality | Critical | Low | Yes |
Go | Quality | Critical | Low | Yes |
Related CWEs: CWE119, CWE121, CWE124, CWE127, CWE194, CWE195.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Go | Quality | Critical | High | Yes |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE127, CWE194, CWE195.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Critical | Unknown | No |
Scala | Quality | Critical | Unknown | No |
Go | Quality | Critical | Unknown | No |
The detector finds situations where a memory at the pointer was dynamically allocated with tainted size, and after this memory was accessed without checking. This may lead to a buffer overflow.
void func(FILE*f) {
int n;
// Variable 'n' is tainted because it given by external source
(f, "%d", &n);
fscanf
// Pointer 'ptr' created using tainted data
char* ptr = malloc(n);
// Buffer access may lead to buffer overflow
[30] = 0;
ptr}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | VeryLow | Yes |
C/C++ | Quality | Critical | VeryLow | Yes |
Related CWEs: CWE119, CWE121, CWE122, CWE126.
Path sensitive version of the DYNAMIC_OVERFLOW. It checks if a path exists where 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | Yes |
C/C++ | Quality | Critical | Low | Yes |
Related CWEs: CWE119, CWE121, CWE124, CWE127, CWE194, CWE195.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | VeryLow | No |
C/C++ | Quality | Critical | VeryLow | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Critical | Unknown | No |
Go | Quality | Critical | Unknown | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | VeryHigh | Yes |
Related CWEs: CWE121.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Critical | Unknown | No |
C# | Quality | Major | Unknown | Yes |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE127, CWE194, CWE195.
This checker detcts usages of memcpy
,
strcpy
and similar functions that may write beyond the end
of destination buffer.
void buffer_overflow(char*p, int n) {
int buf[10];
// Last copied byte will be written outside of buf
(p, buf + 3, sizeof(int) * 7 + 1);
memcpy}
In this example memcpy will write one byte after the end of array
buf
, corrupting data on stack.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | No |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125, CWE126.
This checker detects potential overflows of fixed-size arrays on certain execution paths. It reports a warning when an array may be accessed beyond its right bound. Out-of-bounds array access leads to Undefined Behaviour (C/C++), Runtime Exception (Java), or run-time panic (Go).
enum Type {
,
TYPE_ONE,
TYPE_TWO,
TYPE_THREE
TYPE_INVALID};
enum Type get_type (int data) {
if (data == 100)
return TYPE_ONE;
if (data == 200)
return TYPE_TWO;
return TYPE_INVALID;
}
const char* get_name (const char *n[], enum Type type) {
return n[(int)type];
}
const char* example (int data, int flag) {
const char* names[3] = {"First", "Second", "Third"};
enum Type type = flag ? TYPE_THREE : get_type(data);
// overflow in `get_name` occurs when type is TYPE_INVALID
return get_name(names, type);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | No |
This checker detects buffer overflow when return value of
strlen
, read
and similar functions is
subtracted from, resulting in possible negative index:
void test1(const char* buf) {
int len = strlen(buf);
// Access at -1 if buf = { '\0' }
if(buf[len - 1] == 'a') {
return;
}
}
In this example if the string buf
contains onlt
character \0
(and thus is length zero) an access outside of
buffer will occur.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | No |
C/C++ | Quality | Critical | Low | No |
Go | Quality | Critical | Low | No |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125.
This checker detects potential overflows of fixed-size arrays on
certain execution paths, when using memcpy
,
strcpy
and similar functions.
void overflow(dhcp_msg *msg, int cnt) {
int n;
char buf[10];
char other_buf[100];
if (cnt < sizeof(other_buf)-1) {
= cnt;
n } else {
= sizeof(other_buf)-1;
n }
// writing in buf instead of other_buf for which the size was calculated
(buf, msg, n);
memcpy}
In this example amount of copied data n
is calculated
for other_buf
and may be equal to 99 either of branches if
cnt
is sufficiently large. This will result in out of
bounds writes to buf
and corrupt data on stack.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | No |
C/C++ | Quality | Critical | Low | No |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125.
This checker is similar to BUFFER_OVERFLOW.EX, but detects when overflow happens inside of function because of bad argument value.
void fill(char * p, int len) {
for (int i = 0; i < len; i++) {
// Buffer overflow occurs if len is bigger than size of buffer
[i] = 'a';
p}
}
int overflow() {
char bufferInput[50];
(bufferInput, 1000); // This call is bad
test(bufferInput, 30); // No warning here
testreturn 0;
}
In this example fill
function may write beyond the end
of supplied array if len
is larger than actual size of
buffer.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | No |
Related CWEs: CWE120, CWE121, CWE122, CWE124, CWE134, CWE20.
This checker detects buffer overflows when using sprintf
and snprintf
function without limiting %s
width:
For sprintf
function cheker detects usages of
%s
parameter without maximum length, which may lead to
buffer overflow is corresponding argument is a sufficiently large
string.
void with_sprintf(char* str) {
char buf[10];
(buf, "%s", str); // possible overflow if str is longer than 10
sprintf(buf, "%.10s", str); // everything is fine
sprintf(buf, "%10s", str); // possible overflow with str of any length
sprintf}
For snprintf
checker takes into account argument
limiting the size of output buffer.
void with_sprintf(char* str) {
char buf[10];
(buf, 20, "%s", str); // possible overflow if str is longer than 10
snprintf(buf, 10, "%s", str); // everything is fine
snprintf}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
This checker finds situation when constant buffer is accessed using a non-constant index that potentialy can have arbitrary large value.
void index(int i) {
int buf[21];
[i] = 6; // overflow, non-constant index i accesses buffer of constant size
buf
if(i<15) {
[i] = 6; // no warning, because value was checked
buf}
}
void with_strlen(char *s) {
int a = strlen(s);
char buf[100];
[a] = 0; // overflow if string is sufficiently large
buf}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | No |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125.
This checker is similar to BUFFER_OVERFLOW.LIB.EX, but can infer buffer sizes when using string literals for initialization.
void overflow() {
char a[5];
char b[] = "7777777"; // This buffer has size 7 + 1
(a, b); // overflow, copying buffer of size 8 to buffer of size 5
strcpy}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Critical | Unknown | Yes |
Go | Quality | Critical | Unknown | No |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125, CWE127, CWE194, CWE195.
This checker detects potential buffer underflows on certain execution paths. It reports a warning when an array may be accessed beyond its left bound (with negative index). Out-of-bounds array access leads to Undefined Behaviour (C/C++), Runtime Exception (Java), or run-time panic (Go).
unsigned a[1024];
void example(int x) {
int len, i, j;
for (i = 0; i < 1024; i += 4)
{
= x++ / 2;
len for (j = 0; j < len; j++)
[i+j] &= 0xfffffffe;
a
// buffer underflow happens on 1st iteration if (x == 0)
[i+(j-1)] |= 1;
a}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE119, CWE124, CWE129, CWE394.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | No |
The checker finds situations were the length (finding with build-in
function len()
) of the buffer is compared to 0 after this
buffer has been accessed by any index. These situations can potentially
leak to buffer overflows.
func foo(arr []byte, x int) {
var _ byte = arr[x]
// Assumption that len of array 'arr' may be 0, so expression 'arr[x]' could cause a buffer overflow
if len(arr) == 0 {
.Println(x)
fmt}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE119, CWE121, CWE124.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Go | Quality | Critical | High | No |
Related CWEs: CWE119, CWE121, CWE124.
The checker is similar to OVERFLOW_AFTER_CHECK, but uses SMT solver and is capable of finding and filtering out more complex defects.
struct A {
int x[10];
int y;
};
struct A array[10];
void func(struct A *q) {
int i;
for (i = 0; i <= 10; i++) {
[i] = q[i]; // `i` may be 10, but the last index is 9.
array}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Critical | Unknown | No |
This checker detects access to buffer that contradicts earlier
conditions using len()
.
func after_check(arr []int) {
if len(arr)==10 {
[9] = 0
arr}
[20] = 1
arr}
In this example if array can have length 10, which is indicated by
if
condition. Hovewer later buffer is accessed at index 10,
which contradicts the check.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
A checker fire a warning if some pointer is access by function under some check and after it without any check. Here we use heuristic that first check may be a buffer length.
void for(char*p, int max, int i) {
if(i<max)
[i] = 0;
p
[i] = 1;//OVERFLOW_AFTER_CHECK.VAR
p}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | Yes |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE194, CWE195.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Critical | Unknown | Yes |
Related CWEs: CWE119, CWE121, CWE124.
The checker is similar to OVERFLOW_UNDER_CHECK, but overflow is occurred inside library function call.
void func(int i, char* p) {
char buf[100];
if (i < 200) {
(buf, p, i); // Error.
strncpy}
}
In this example OVERFLOW_UNDER_CHECK.LIB
will be
reported instead of OVERFLOW_UNDER_CHECK
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE119, CWE121, CWE124.
Special subtype of OVERFLOW_UNDER_CHECK.LIB, where
overflow is occurred inside memcpy
function call.
void func(char* p, int len) {
char buf[100];
if (len > 101)
return;
(buf, p, len); // Error.
memcpy}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Go | Quality | Critical | High | Yes |
Related CWEs: CWE119, CWE121, CWE124.
The checker is similar to OVERFLOW_UNDER_CHECK, but overflow is occurred inside a function, and check is performed before a function call.
void access(int index) {
int array[100];
[index] = 0;
array}
void func(int index) {
if (index < 200)
(index); // Error.
access}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE120, CWE121, CWE122, CWE170, CWE783.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | Yes |
Similar to BUFFER_SIZE_MISMATCH, but this type of warning is emitted for the particular case when possibly nonterminated string is passed as an argument to a standard function that requires this argument to be null-terminated.
void example(char *src) {
char buf[100], dst[4000];
(buf, src, sizeof(buf)); // buf may be not null-terminated
strncpy(dst, buf, sizeof(dst)); // overflow of buf will happen in this case
strncpy}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Average | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Related CWEs: CWE120, CWE121, CWE122, CWE170.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE120, CWE131, CWE170, CWE783.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE783.
This checker reports the same issue as ALLOC_SIZE_MISMATCH but for
memset
and similar functions instead of allocations.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | VeryHigh | Yes |
Related CWEs: CWE119, CWE121, CWE124, CWE170.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | High | Yes |
The checker NONTERMINATED_STRING
finds situations where
“safe” versions of standard functions working with C strings enable
creation of non-null-terminated 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-null-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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | High | Yes |
The checker finds patterns with standard C function
memcpy
gets result of function strlen
for
second parameter as size parameter. By using such pattern null
terminator of second parameter is not copied which may lead to creation
not null terminated string.
void copy_impl(char*dst, char*src) {
size_t len = strlen(src);
(dst, src, len); //dst may became NNTS.
memcpy}
Checker reacts on this pattern. For first parameter only simple checkers are run. If detector is not sure that operation is safe then the warning is emitted.
Checker can report warnings for other functions like
memcpy_s
. Information about parameters is spread inter
procedurally. That is why the checker will find an error if several
wrapper functions are used:
extern char* global;
void wrapper(char*src, int len) {
(global, src, len);
memcpy}
void test(char*a) {
int n = strlen(a);
(a, n);//The warning will be emitted here.
wrapper}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | VeryHigh | Yes |
Related CWEs: CWE120, CWE121, CWE122, CWE127.
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.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | VeryHigh | No |
Related CWEs: CWE120, CWE121, CWE122.
This checker is a variant of STRING_OVERFLOW, which indicates that the source string is a parameter of a function being analyzed. This checker blindly reports a warning because it has no information on the actual length of the source string. This checker suggests to use ‘strncpy’ always instead of ‘strcpy’ to avoid possible buffer overflows.
void test5(char* param) {
char localBuf[100];
(localBuf, param);//svace: emitted STRING_OVERFLOW.MINOR
strcpy}
char buf[100];
void test1(char* param) {
(buf, param);
strcpy}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE590.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | VeryLow | Yes |
This path-sensitive version of checker FREE_NONHEAP_MEMORY finds situation in which there is a reachable path passing through the pointer to non-heap memory could be passed to a memory deallocation function.
void free_if(int*p, int k) {
if(k)
(p);
my_free}
void test_func(int z) {
int arr[] = {2, 3};
(arr, z);// in this line warning will be emitted
free_if}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
This subtype of checker FREE_NONHEAP_MEMORY finds situation, where problematic code is contained in macro.
#define ALLOC(x, Type) \
Type x[] = {1, 2, 3};
#define FREE(x) \
my_free(x);
void foo1() {
int arr[] = {1, 2, 3};
(arr);
FREE}
void foo2() {
(arr, int);
ALLOC(arr);
FREE}
In both usages of the macro FREE
analyzer will emit a
warning.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | VeryLow | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | VeryLow | No |
This checker finds suspitions pointer shifting for result of allocated memory on heap.
char* allocate(int size) {
char *p = (char*)malloc(size)+4; //ALLOC_ARITHM
return p;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | Yes |
Related CWEs: CWE119.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Average | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE415, CWE416, CWE672.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Low | Yes |
Subtype of USE_AFTER_FREE for situations where a pointer is passed to the reallocation 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE415, CWE416, CWE672.
This checker finds situations where a pointer referencing deallocated memory is deallocated again.
char* foo(char* x) {
(x);
free(x);
free}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | High | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
Statistical version of DANGLING_POINTER.STRICT. Svace emits this subtype when for some cases externally accessible memory pointing to it is reassigned and for others it isn’t.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
Related CWEs: CWE131.
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)
returns a 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | High | Yes |
Related CWEs: CWE401, CWE404, CWE775.
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 }
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | VeryHigh | Yes |
Related CWEs: CWE401, CWE404, CWE775.
It is an subtype of MEMORY_LEAK or MEMORY_LEAK.EX for cases where memory is
allocated by using function for C library strdup
. It is
separated to subtype because in many cases function strdup
is used for allocating small amount of memory and such errors are less
critical.
void mem_leak() {
char* str = strdup("hello");
= "qqq"; // Memory allocated by function strdup is leaked here.
str }
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
It is subtype of MEMORY_LEAK. The warnings are emitted by the same checker as for MEMORY_LEAK but then it is suppressed to subtype if pointer is related to C structures. Due to complexity of precise analysis of code with structures this warning type has a lower true positive rate.
struct S1 {
int a;
int* array;
};
void init1(struct S1* s1) {
->array = malloc(10);
s1}
void test1() {
struct S1 s1;
(&s1);//MEMORY_LEAK.STRUCT
init1}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Low | Yes |
Related CWEs: CWE401, CWE404, CWE775.
It is subtype of MEMORY_LEAK for cases
when both filters for MEMORY_LEAK.STRUCT and MEMORY_LEAK.STRDUP work. This types
describes situations where memory is allocated by function
strdup
and then it is assigned to a structure field.
typedef struct {
char* s;
} str_t;
typedef struct {
* data;
str_t} data_t;
* f(data_t *p) {
str_tif (!p) return 0;
return p->data;
}
void example(str_t *src) {
void* local;
*dest = f(local);
str_t
if (dest)
->s = strdup("hello");//MEMORY_LEAK.STRDUP.STRUCT
dest}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Average | Yes |
Exception handling is very convenient feature that usually improves code readability. But in some cases exceptions may introduce another errors. This checker by using MEMORY_LEAK engine finds situations where memory is leaked due to incorrect exception handling.
class Exc {}
void may_throw(int x) {
if (x % 3 == 0) {
throw Exc();
}
}
int incorrect_exception_handling(int x) {
int *ptr = 0;
try {
= new int; //memory allocation
ptr
(x);
may_throw
delete ptr;//memory deallocation
} catch (...) {
return -1;
}
return 0;
}
In the example above memory for ptr
won’t be deallocated
if function may_throw
throws an exception. To avoid this
issue it is better to use RAII-idiom or smart-pointers.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE401, CWE404, CWE775.
It is another checker (as MEMORY_LEAK) for detecting situations where memory was allocated, and then all references to that memory were lost. The difference is that this checker uses formulas for describing when memory was allocated. The checker can detect errors in complicated situations where leak occurred only on some path.
void example(int a, int b, int c) {
char*p = 0;
if(a>b+10)
= malloc(10);//leak if c != 10
p
if(c==10 && a>b+c)
(p);
free}
In the example above in case if variable c
is not equal
to 10
memory will be allocated and won’t be released. For
next code no warning will be emitted.
void contr_example(int a, int b, int c) {
char*p = 0;
if(c==10 && a>b+10)
= malloc(10);//will be released
p
if(c==10 && a>b+c)
(p);
free}
Here memory is released for all paths where it was allocated.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Low | No |
It is subtype of MEMORY_LEAK.EX for cases where memory may be deallocated by complicated conditions.
Note: MEMORY_LEAK.EX is emitted if function does not free memory at all on analyzed paths. Svace checks memory leaks for follow cases:
For every such case leak algorithm is run and if there are no any
memory releasing than Svace will emit MEMORY_LEAK.EX. So in example of
MEMORY_LEAK.EX despite that free is called on some path - Svace still
does not emit MEMORY_LEAK.MIGHT because leak algorithm is run for path,
where condition if(c==10 && a>b+c)
is not
true.
void example(int a, int b, int c) {
void *data = (char*)malloc(100);
if(a) {
(data);
free}
if (b) {
(data);
free}
//leak if !a && !b
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
It is subtype of MEMORY_LEAK.EX for situtations where a leak is occurred on exception path. It is similar to MEMORY_LEAK.EXCEPTION but this checker uses MEMORY_LEAK.EX engine for leak detection.
class Exc {};
int x;
void may_throw() {
if (x++ % 7 == 0) {
throw Exc();
}
}
int example(bool flag) {
int *ptr = 0;
try {
= flag? nullptr : new int; //allocation
ptr
(); //potential exception
may_throw
if (!flag)
delete ptr;//deallocation
} catch (...) {
return -1;
}
return 0;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Low | Yes |
Related CWEs: CWE121, CWE122, CWE475.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Scala | Quality | Major | High | Yes |
Go | Quality | Major | High | Yes |
Kotlin | Quality | Major | High | Yes |
C# | Quality | Major | High | Yes |
Related CWEs: CWE404, CWE772, CWE773, CWE775.
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.
For C# this checker finds situations where a resource (object that
implements IDisposable
interface) is lost, because local
variables that held its value went out of scope or were re-assigned.
It has the following subtypes: - Subtype .FRUGAL
means
that the leaked resource is a Windows Forms form. - Subtype
.EXCEPTION
means that the resource is leaked because of an
exception. - Subtype .HAS_FINALIZER
means that the resource
class has a finalizer which performs cleanup when the object is garbage
collected. Note that it will happen in unknown time after losing the
last reference to the resource, and the .NET runtime doesn’t call
finalizers on application exit. - Subtype .SAFEHANDLE
means
that the resource class is a type derived from
System.Runtime.InteropServices.SafeHandle
.
A warning can have the following combination of these subtypes in the
specified order:
[.FRUGAL][.EXCEPTION][{.HAS_FINALIZER,.SAFEHANDLE}][.TEST]
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
Scala | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
Related CWEs: CWE404.
This checker finds situations where a file descriptor, file handle or a socket descriptor are lost because of an 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Scala | Quality | Major | High | Yes |
Go | Quality | Major | High | Yes |
Kotlin | Quality | Major | High | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
Scala | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
Scala | Quality | Normal | Unknown | No |
This checker is a subtype of HANDLE_LEAK.
It reacts on using classes, which implements interface
java.io.IOException
. Svace will recheck that after creation
an instance of such class a method close
wil be called.
import java.io.Closeable;
import java.io.IOException;
class MyClass implements Closeable {
@Override
public void close() throws IOException {
}
}
class Example {
public void noclose() {
= new MyClass();//no close
MyClass s }
public void withclose() {
= new MyClass();//no leak
MyClass s .close();
s}
}
In the example above Svace will emit warning for method
noclose
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Hidden |
Scala | Quality | Normal | Unknown | Hidden |
Kotlin | Quality | Normal | Unknown | Hidden |
C# | Quality | Minor | Unknown | Yes |
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.
For C# it finds situations where a Windows Forms form is created but not disposed.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | Yes |
C/C++ | Quality | Normal | Average | Yes |
Scala | Quality | Normal | Average | Yes |
Go | Quality | Normal | Average | Yes |
Related CWEs: CWE415, CWE416, CWE672, CWE675.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | Yes |
C/C++ | Quality | Normal | Average | Yes |
Scala | Quality | Normal | Average | Yes |
Go | Quality | Normal | Average | Yes |
Related CWEs: CWE415, CWE416, CWE672.
This checker finds situations where a closed file descriptor is closed again, and the close happens in procedures.
int my_close(int p) {
if (cond()) {
(p);
closereturn -1;
}
return 0;
}
void test(int fd) {
(fd);
my_close(fd);
close}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | High | Yes |
C/C++ | Quality | Normal | High | Yes |
Scala | Quality | Normal | High | Yes |
Go | Quality | Normal | High | Yes |
Kotlin | Quality | Normal | High | Yes |
This checker finds situations where a file descriptor, file handle or a socket descriptor is 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.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Average | Yes |
C/C++ | Quality | Minor | Average | Yes |
Scala | Quality | Minor | Average | Yes |
Python | Quality | Minor | Average | Yes |
This checker finds situations where a file descriptor, file handle or a socket descriptor is closed and then passed as an argument to a function.
#include<unistd.h>
#include<stdio.h>
int prepare(FILE *fd);
int openAndPrepare(FILE **fd) {
*fd = fopen("fileName", "r");
if (*fd == NULL) {
return 1;
}
int err = prepare(*fd);
if (err) {
(*fd);
fclosereturn 2;
}
return 0;
}
void example() {
FILE *fd;
(&fd);
openAndPreparechar buf[100];
(buf, 10, 10, fd);
fread}
void possibleFix() {
FILE *fd;
if (openAndPrepare(&fd)) {
return;
}
char buf[100];
(buf, 10, 10, fd);
fread}
Function example
illustrates the defect: fd
can be closed after the openAndPrepare
function is called,
then it is passed as the first parameter to the fwrite
function. Function possibleFix
illustrates a possible fix:
check the return value of the openAndPrepare
function
call.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | Yes |
Related CWEs: CWE416.
This Microsoft Windows-specific checker finds situations where an
instance of Microsoft COM interface is explicitly deallocated. Instead,
its Release
method should be used.
#include <unknwn.h>
( CLSID_Arithm, 0xa888f560, 0x58e4, 0x11d0, 0xa6, 0x8a, 0x0, 0x0, 0x83, 0x7e, 0x31, 0x0);
DEFINE_GUID
( IID_IArithm, 0xa888f561, 0x58e4, 0x11d0, 0xa6, 0x8a, 0x0, 0x0, 0x83, 0x7e, 0x31, 0x0);
DEFINE_GUID
class IArithm : public IUnknown {
public:
virtual long Add(long Op1, long Op2) = 0;
virtual long Sub(long Op1, long Op2) = 0;
};
class Arithm : public IArithm {
public:
(REFIID riid, void** ppv);
HRESULT QueryInterface();
ULONG AddRef();
ULONG Release
long Add(long Op1, long Op2) { return Op1 + Op2; }
long Sub(long Op1, long Op2) { return Op1 - Op2; }
private:
m_lRef;
DWORD
public:
() : m_lRef(0);
Arithm};
::QueryInterface(REFIID riid, void** ppv) {
HRESULT Arithmswitch(riid) {
case IID_IUnknown:
case IID_IArithm;
*ppv = this;
();
AddRefreturn ( S_OK ) ;
default:
return ( E_NOINTERFACE );
}
}
::Release() {
ULONG Math( &m_lRef );
InterlockedDecrement
if (m_lRef == 0) {
delete this;
return 0;
} else {
return m_lRef;
}
}
::AddRef() {
ULONG Math( &m_lRef );
InterlockedIncrementreturn m_lRef;
}
long summarize(IUnknown *pUnknwn, long a, long b) {
*pArithm = NULL;
IArithm
= pUnknwn->QueryInterface(IID_IArithm, (void **)&pArithm);
HRESULT hr
if (SUCCEEDED(hr)) {
long result = pArithm->Add(a, b);
delete pArithm; // BAD_FREE.MS_COM is reported; use pArithm->Release() here!
return result;
}
return 0;
}
These checkers find situations where number of loop iteration may be infinite because of integer overflow.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | No |
C/C++ | Quality | Major | Low | No |
Go | Quality | Major | Low | No |
C# | Quality | Major | Average | Yes |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | VeryLow | No |
C/C++ | Quality | Major | VeryLow | No |
Go | Quality | Major | VeryLow | No |
This checker finds situations where number of loop iteration may be more than it was intended. The loop depends on value of string.
void func(char * pos) {
while (*pos != ' ') {
++;
pos}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Major | Unknown | No |
Go | Quality | Major | Unknown | No |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
Checker INFINITE_LOOP.INT_OVERFLOW.ARRAY
finds
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
}
}
In the example above if the first 256 bytes of buffer
buf
do not contain a space character, the variable
x
will eventually be incremented enough times to overflow
an unsigned char
type resetting its value back to zero
which leads to an infinite loop.
The checker emits warnings only for tainted buffers (from external
sources). For other buffer types
INFINITE_LOOP.INT_OVERFLOW.ARRAY.STRICT
is emitted.
char buf[1000];
void foo() {
unsigned char x = 0;
while (buf[x] == ' ') {
++x;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
C/C++ | Quality | Critical | Unknown | No |
Go | Quality | Critical | Unknown | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
Related CWEs: CWE125, CWE190, CWE191.
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}
void foo(int a) {
if (a == int.MaxValue) // ℹ️〔 Step 1: Condition 'a == int.MaxValue' taking false branch〕
return;
long b = a + 1; // overflow is NOT possible
long c = a + 2; // ⚠️〔INTEGER_OVERFLOW An overflow in the arithmetic expression a + 2 of type int may occur〕 // ℹ️〔overflow may happen when a + 2 is Int32.MinValue〕 // ℹ️〔value without overflow may be 2147483648〕 // ℹ️〔overflow may happen when a is Int32.MaxValue - 1〕
long d = 2 + a; // do not report same
}
In the example the value of the variable a
was compared
against int.MaxValue
, so overflow in the expression
a + 1
is not possible. However overflow may happen in the
a + 1
if a
is Int32.MaxValue - 1
.
The result will be equal to Int32.MinValue
instead of
Int32.MaxValue + 1
void foo(int a) {
if (a is (int.MaxValue or (int.MaxValue - 1))
return;
long b = a + 1; // overflow is NOT possible
long c = a + 2; // overflow is NOT possible
long d = 2 + a; // overflow is NOT possible
}
Additional comparison of the variable a
against
Int32.MaxValue - 1
prevents overflow in the expression
a + 2
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | Yes |
Checker detects integer overflow when values of variables are explicitly known at compile time.
void foo()
{
ushort size = ushort.MaxValue; // size is 65535
+= 2; // ⚠️〔INTEGER_OVERFLOW.EXPLICIT An overflow in the arithmetic expression size += 2 of type ushort will occur: result will be 1 instead of 65537〕
size }
An overflow in the arithmetic expression size += 2
of
type ushort will occur on line 4: result will be 1 instead of 65537.
void foo()
{
ushort size = ushort.MaxValue; // size is 65535
unchecked {
+= 2;
size }
}
If overflow is expected it is possible to use unchecked
expression or statement to suppress warning.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Minor | Unknown | Yes |
Checker detects if the result of expression that may overflow is used
as an argument of Marshal.AllocHGlobal
. It is required to
use checked
expression or statement to emit exception in
case of overflow and reduce risk of silent memory damage.
public static void foo(int size)
{
++; // ⚠️〔INTEGER_OVERFLOW.CHECKED_REQUIRED An overflow in the arithmetic expression size++ which is used in Marshal.AllocHGlobal(size) may occur. Please use checked arithmetic to throw IntegerOverflowException in case of overflow instead of possible memory damage〕 // ℹ️〔overflow may happen when size++ is Int32.MinValue〕
size= Marshal.AllocHGlobal(size);
IntPtr hglobal }
An overflow in the arithmetic expression size++
(line 3)
which is used in Marshal.AllocHGlobal(size)
(line 4) may
occur when size is Int32.MaxValue
public static void foo(int size)
{
checked
{
++;
size}
= Marshal.AllocHGlobal(size);
IntPtr hglobal }
To prevent silent memory corruption it is required to use checked expression or statement if overflow may happen
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Average | Yes |
This checker finds situations where an arithmetic expression may contain an overflow and is widened to a larger data type.
It works only on AST structures and does not analyze execution paths.
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 * y);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This checker finds situations where an arithmetic expression inside a macro may contain an overflow and is widened to a larger data type.
It works only on AST structures and does not analyze execution paths.
#define TRANSFORM(x, y, offset) (((x) << (y)) + (offset))
unsigned long long transform(unsigned int x, unsigned int y) {
return TRANSFORM(x, y, 0); /* Having x == 1, y == 32, we get 0 instead of 2^32 */
}
The shift above should be performed after widening the arguments:
#define TRANSFORM(x, y, offset, type) (((type)(x) << (y)) + (offset))
unsigned long long transform(unsigned int x, unsigned int y) {
return TRANSFORM(x, y, 0, unsigned long long);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This checker finds situations where an arithmetic expression is widened to a larger data type and its subexpression may contain an overflow.
It works only on AST structures and does not analyze execution paths.
long long update(int x) {
return (x * (0x8000 << 20)); /* always returns 0 */
}
The evaluation of the constant expression above should be performed in 64 bits:
long long update(int x) {
return (x * (0x8000LL << 20));
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This checker finds situations where an arithmetic expression inside a macro is widened to a larger data type and its subexpression may contain an overflow.
It works only on AST structures and does not analyze execution paths.
#define MUL2x20(x) ((x) << 20)
#define COEF 0x8000
long long update(int x) {
return (x * MUL2x20(COEF)); /* always returns 0 */
}
The evaluation of the constant expression above should be performed in 64 bits:
#define MUL2x20(x) ((x) << 20)
#define COEF 0x8000LL
long long update(int x) {
return (x * MUL2x20(COEF)); /* always returns 0 */
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | No |
C/C++ | Quality | Major | Average | No |
Scala | Quality | Major | Average | No |
Kotlin | Quality | Major | Average | Yes |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
The checker finds situations where value from external source is used in arithmetic operation without checking its range. It potentially may lead to an 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 safer
toIntOrNull
function instead of toInt
function.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | No |
C/C++ | Quality | Normal | Average | No |
Scala | Quality | Normal | Average | No |
Go | Quality | Normal | Average | No |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | Hidden |
C/C++ | Quality | Normal | Average | Hidden |
Go | Quality | Normal | Average | Hidden |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
The checker detects cases where a potential loss of data bits due an integer truncation is possible, while an assumption about an integer data range comes from 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
C/C++ | Quality | Normal | Unknown | No |
Go | Quality | Normal | Unknown | No |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
The checker detects cases where a potential loss of data bits due an integer truncation is possible, while an assumption about an integer data range comes from bit-manipulation operations. It is similar to INT_OVERFLOW.TRUNC.UNDER_BITMASK, while this warning subtype is reported for the integers of 32-bit size or a greater.
void foo(unsigned int a);
#define INVERT_BYTES_32(X) ( \
( ((X) & 0x000000FF) << 24 ) \
| ( ((X) & 0x0000FF00) << 8 ) \
| ( ((X) & 0x00FF0000) >> 8 ) \
| ( ((X) & 0xFF000000) >> 24 ) \
)
void bar(unsigned int x) {
(INVERT_BYTES_32(x) + 1);
foo}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
C/C++ | Quality | Normal | Unknown | No |
This checker finds situations where an arithmetic expression will inevitably overflow. It means that any way of program execution leak to overflow.
void simple_add(int var) {
int result = var + 500;
}
void func(int boo) {
int param = INT_MAX - 300;
// Result of operation 'var + 500' in function simple_add will overflow in any way
if (boo) {
(param);
simple_add} else {
(param + 200);
simple_add}
}
void func(boolean boo) {
byte var = 120;
byte result;
// Variable 'result' will contain distorted data in any case
if (boo) {
= var + 200;
result } else {
= var << 10;
result }
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | No |
C/C++ | Quality | Normal | Average | No |
Go | Quality | Normal | Average | No |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
The checker detects cases where the subtraction is performed on an unsigned value that can be 0, and therefore, the subtraction result may underflow, while its result is passed to a function as is passed to a library function as a sensitive unsigned integer data argument.
#include <string.h>
void example(char *dst, const char *str) {
size_t len = strlen(str);
(dst, str, len - 1);
memcpy}
void possible_fix(char *dst, const char *str) {
size_t len = strlen(str);
if (len >= 0) {
(dst, str, len - 1);
memcpy}
}
Function example
illustrates the defect:
len
variable is an unsigned variable and its value comes
from the return value of strlen
function and can be 0, so a
subtraction from it without a check may underflow and result to a big
unsigned value, which is used as a memcpy
argument.
Function possible_fix
illustrates a possible fix: adding
a proper check of len
to guarantee a valid argument value
for memcpy
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | VeryLow | No |
C/C++ | Quality | Normal | VeryLow | No |
Go | Quality | Normal | VeryLow | No |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
The checker detects cases where the subtraction is performed on an unsigned value that can be 0, and therefore the subtraction result may underflow, while its result is used as a loop bound.
#include <string.h>
void example(char *str) {
unsigned len = strlen(str);
unsigned i;
for (i = 0; i < len - 2; ++i) {
[i] = '*';
str}
}
void possible_fix(char *str) {
unsigned len = strlen(str);
unsigned i;
if (len < 2)
return;
for (i = 0; i < len - 2; ++i) {
[i] = '*';
str}
}
Function example
illustrates the defect:
len
variable is an unsigned variable and its value comes
from the return value of strlen
function and can be 0, so a
subtraction from it without a check may underflow and result to a big
unsigned value, which is used as a loop bound.
Function possible_fix
illustrates a possible fix: adding
a proper check of len
to guarantee a valid loop bound.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
C/C++ | Quality | Normal | Unknown | No |
Go | Quality | Normal | Unknown | No |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
The checker detects cases where the subtraction is performed on an unsigned value that can be 0, and therefore the subtraction result may underflow.
Unlike INT_OVERFLOW.LIB and INT_OVERFLOW.LOOP this checker reports dubious subtracting itself, not the critical use of their result, so an underflow might be an intended behavior in certain cases.
#include <string.h>
void example(char *str) {
unsigned len = strlen(str);
unsigned len1 = len - 1;
if (len1 < 10) {
[len1] = '*';
str}
}
void possible_fix(char *str) {
unsigned len = strlen(str);
if (len > 0 && len < 10) {
unsigned len1 = len - 1;
[len1] = '*';
str}
}
Function example
illustrates the defect and function
possible_fix
illustrates a possible fix.
Note that the reported issue is a possible underflow while expression
len - 1
calculation within an unsigned context, when the
value of len
variable might be 0
, but not any
use of its result further.
#include <string.h>
void dummy(unsigned arg) {
(void)arg;
}
void example(const char *str) {
unsigned len = strlen(str);
(len - 1);
dummy}
void possible_fix(const char *str) {
unsigned len = strlen(str);
if (len != 0) {
(len - 1);
dummy}
}
Function example
illustrates the defect and function
possible_fix
illustrates a possible fix.
Note that the reported issue is a possible underflow while expression
len - 1
calculation within an unsigned context, when the
value of len
variable might be 0
, but not any
use of its result further.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Low | No |
C/C++ | Quality | Normal | Low | No |
Go | Quality | Normal | Low | Hidden |
Kotlin | Quality | Normal | Low | Yes |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
This checker detects issues where an integer variable is compared to some constant value and the result of an arithmetic operation on this variable after that may overflow. The comparison identifies the bounds for the possible values of this variable.
#include <stdint.h>
extern void stub(uint64_t);
void example(uint32_t x) {
if (x <= 100000) {
uint32_t y = x * 50000;
(y);
stub}
}
void possible_fix(uint32_t x) {
if (x <= 100000) {
uint64_t y = (uint64_t)x * 50000;
(y);
stub}
}
Function example
illustrates the defect: x
is compared to 10
5
and
x
is multiplied by
5
*10
4
after that. The
result of the multiplication is greater than the maximum of
uint32_t
type range, if the value of x
reaches
the upper bound of the comparison.
Function possible_fix
illustrates a possible fix: adding
a type cast of x
value to uint64_t
type before
multiplication.
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
n
is multiplied by 10
6
after that. 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Low | No |
C/C++ | Quality | Normal | Low | No |
Go | Quality | Normal | Low | No |
Kotlin | Quality | Normal | Low | No |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
This checker detects issues where a constant value is assigned to an integer variable and the result of an arithmetic operation on this variable after that may overflow.
#include <stdint.h>
extern void stub(uint64_t);
void example(void) {
uint32_t x = 100000;
uint32_t y = x * 50000;
(y);
stub}
void possible_fix(void) {
uint64_t x = 100000;
uint64_t y = x * 50000;
(y);
stub}
Function example
illustrates the defect: constant value
10
5
is assigned to x
of type uint32_t
and x
is multiplied by
5
*10
4
after that. The
result of the multiplication is greater than the maximum value of
uint32_t
type range.
Function possible_fix
illustrates a possible fix:
changing the type of x
variable to
uint64_t
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | VeryLow | No |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
This checker detects issues where a signed value is converted to an unsigned after a check that is not sufficient to guarantee that the converted value is non-negative.
func example(x int32) uint32 {
if x < 123 {
return uint32(x)
}
return 1
}
func possible_fix(x int32) uint32 {
if x < 123 {
if x >= 0 {
return uint32(x)
}
}
return 1
}
Function example
illustrates the defect: variable
x
is a signed integer and the condition
x < 123
does not guarantee that the value of
x
is positive in the subsequent conversion to an unsigned
type.
Function possible_fix
demonstrates a possible fix:
guarding the conversion with a non-negative check for
x
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Minor | Unknown | No |
Related CWEs: CWE190, CWE191, CWE682.
This checker finds situations where the result of an arithmetic expression casts to a bigger type, but the result could overflow before this cast.
func overflow(a int8) {
// Potential integer overflow
:= int16(a + 100)
b }
func no_overflow(a int8) {
// There is no integer overflow
:= int16(a) + 100
b }
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This checker finds for shift left arithmetic operations where the left argument is a negative value. According to the C language standard, this is undefined behavior.
void func() {
int shiftValue = -100;
int someOtherValue = shiftValue << 5; // Undefined behavior
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
Related CWEs: CWE195.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
Related CWEs: CWE194.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | No |
C/C++ | Quality | Major | Low | No |
Scala | Quality | Major | Low | No |
Go | Quality | Major | Low | No |
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
Configuration option SENSITIVE_NAME_REGEX
allows
changing regular expression for name.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | No |
C/C++ | Quality | Major | Low | No |
Scala | Quality | Major | Low | No |
Go | Quality | Major | Low | No |
Python | Quality | Major | Low | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Critical | Unknown | No |
C# | Security | Critical | VeryHigh | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Critical | Unknown | No |
C/C++ | Quality | Critical | Unknown | No |
Kotlin | Quality | Critical | Unknown | Yes |
C# | Quality | Major | Unknown | Hidden |
Related CWEs: CWE833.
This checker finds code that may result in two or more threads 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 threads 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
This checker finds code that might lead to a class loading deadlock. JVM might start loading super and child classes in separate threads. If static initialization block contains a reference to a subclass it might lead to a deadlock.
class SuperClass {
final static SubClass subClass = new SubClass();
}
class SubClass extends SuperClass { }
The class loader sees the subClass
static field and
tries to lock SubClass
for loading. Another thread may load
SubClass
which is inherited from SuperClass
.
It locks SuperClass
for loading which leads directly to the
deadlock.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | No |
This checker finds situations, when a goroutine may not have time to
complete its execution, which are caused by using
sync.WaitGroup.Add()
inside the goroutine and
sync.WaitGroup.Wait()
after the goroutine’s function
initialization.
func foo() {
var wg sync.WaitGroup
var x int32 = 0
for i := 0; i < 100; i++ {
go func() {
.Add(1)
wg.AddInt32(&x, 1)
atomic.Done()
wg}()
}
.Wait()
wg}
Function foo
illustrates the defect:
wg.Add(1)
inside the goroutine can be executed after the
execution of wg.Wait()
, which may not wait for the
goroutine to finish.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Go | Quality | Major | High | Yes |
Related CWEs: CWE617, CWE667, CWE764.
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. This may be caused by failing to release a lock when an erroneous situation happens.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | High | No |
C/C++ | Quality | Minor | High | No |
Go | Quality | Minor | High | No |
Related CWEs: CWE617, CWE667, CWE764.
It is subtype of NO_UNLOCK. The checker emits warnings if mutex is locked on some paths and there are no any unlock operation.
#include <pthread.h>
;
pthread_mutex_t m
void lock() {
(&m);
pthread_mutex_lock}
int func(int flag) {
if(flag>0) {
();
lock
if(flag)return 0;//NO_UNLOCK.STRICT
}
return 1;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
This checker finds situations where a lock is acquired in a class constructor and not released in its destructor.
#include <pthread.h>
class LockGuardBad {
pthread_mutex_t m;
public:
() { pthread_mutex_lock(&m); } // report NO_UNLOCK.CTOR
LockGuardBad
~LockGuardBad() {}
};
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
This checker finds situations where pooled or reusable objects are used for synchronization. Such objects may be locked by outside code.
All three methods (localStringLockTest
,
fieldStringLockTest
and internStringTest
) use
the singleton string object stored in Java String Pool for
synchronization.
class StringLiteralExample {
public void localStringLockTest() {
String localStringLock = "Some string";
synchronized (localStringLock) {
// ...
}
}
private final String fieldStringLock = "Some string";
public void fieldStringLockTest() {
synchronized (fieldStringLock) {
// ...
}
}
private final String internStringLock = new String("Some string").intern();
public void internStringTest() {
synchronized (internStringLock) {
// ...
}
}
}
Boolean literal values share the unique instances of the
Boolean
class.
class BooleanLiteralExample {
private final Boolean booleanLock = Boolean.TRUE;
public void booleanLockTest() {
synchronized (booleanLock) {
// ...
}
}
}
Boxed types may reuse the instance for some values.
PossibleFix
method illustrates the possible fix: create a
unique instance of such type object.
class BoxedPrimitiveExample {
private int index = 0;
private final Integer boxedPrimitiveLock = index;
public void boxedPrimitiveTest() {
synchronized (boxedPrimitiveLock) {
++;
index// ...
}
}
private final Integer uniqueLock = new Integer(index);
public void possibleFix() {
synchronized (uniqueLock) {
++;
index// ...
}
}
}
Public non-final fields are accessible to the outside code. It’s
recommended to use private final fields for synchronization as
possibleFix
method shows.
class PublicNonFinalFieldExample {
public Object publicNonFinalLock = new Object();
public void publicNonFinalLockTest() {
synchronized (publicNonFinalLock) {
// ...
}
}
private final Object privateFinalLock = new Object();
public void possibleFix() {
synchronized (privateFinalLock) {
// ...
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | High | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE667.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
Related CWEs: CWE662.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Hidden |
C/C++ | Quality | Major | Unknown | No |
Kotlin | Quality | Major | Unknown | Hidden |
C# | Quality | Major | Average | Yes |
Related CWEs: CWE366.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
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
percentage of accesses to a field are under the same lock;NO_LOCK.STAT.EX.TWO_OF_THREE
configuration option is
true
.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 a 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
This Java-specific detector issues a warning if a field is declared
with GuardedBy
annotation but later accessed without locks
(such as synchronized
).
import com.android.internal.annotations.GuardedBy;
public class NoLockGuard001 {
@GuardedBy("lock")
public boolean data;
private Object lock = new Object();
public void foo1() {
synchronized (lock) {
= false; // Ok
data }
}
public void foo2() {
= true; // NO_LOCK.GUARD here
data }
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
This detector finds situations when an assignment to a variable
occurs in a synchronized
block, while the variable’s value
is checked before that block.
This may lead to a synchronization error, when many processes simultaneously pass the check and then reach the synchronized block one by one.
public class NoCheckInLock {
class ClassA {
private int x;
ClassA(int xx) {
= xx;
x }
}
class ClassB {
private Object fLock = new Object();
public ClassA fSharedObj;
public boolean fCritialSection = false;
public void access(int x) {
// Compare : Checking Value 'fCritialSection'.
if (fCritialSection) {
return;
}
// Synchronized
synchronized (fLock) {
// NO_CHECK_IN_LOCK : Field 'fCritialSection' has been compared
// without locking and is assigned under lock.
= true;
fCritialSection }
= new ClassA(x);
fSharedObj }
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
This detector finds double-checked locking anti-pattern commonly used to reduce the overhead of acquiring a lock by testing the locking criterion before acquiring the lock.
class DoubleCheckedLockingExample {
private static class A { }
private A aRef;
public A brokenIdiom() {
if (aRef == null) {
synchronized (this) {
if (aRef == null) {
= new A();
aRef }
}
}
return aRef;
}
private volatile A aVolRef;
public A getA() {
= aVolRef;
A localRef if (localRef == null) {
synchronized (this) {
= aVolRef;
localRef if (localRef == null) {
= localRef = new A();
aVolRef }
}
}
return localRef;
}
}
Function brokenIdiom
illustrates the defect. Function
possibleFix
illustrates a possible fix: use
volatile
keyword in the field declaration.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
This Java-specific detector finds situations when
synchronized
block contains wait
method but
does not contain an enclosing loop. This may cause the wait
to be triggered by a condition that it is not initially intended for (so
called ‘spurious wake-up’). The solution is to put the loop under
synchronized
.
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class BadCheckOfWaitCond {
private void testFunc(boolean cond1, boolean cond2) {
FileInputStream fis = null;
try {
= new FileInputStream(new File("testFile"));
fis while (fis.read() > 0) {
if (cond1) {
while (true) {
synchronized (this) {
try {
if (cond2) {
this.wait(); // report BAD_WAIT_OF_COND here
} else {
break;
}
} catch (InterruptedException e) {
if (fis != null)
.close();
fis.printStackTrace();
e}
}
}
} else {
break;
}
}
.close();
fis} catch (Throwable th) {
try {
if (fis != null)
.close();
fis} catch (IOException e) {
.printStackTrace();
e}
.printStackTrace();
th}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | VeryHigh | No |
Related CWEs: CWE377.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | VeryHigh | No |
Related CWEs: CWE377.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Go | Quality | Major | Average | No |
Kotlin | Quality | Major | Average | Yes |
Python | Quality | Major | Average | Yes |
C# | Quality | Minor | Average | Yes |
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, second operand of bitwise AND is zero which makes the condition always false regardless of the value of the first operand.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
This checker is similar to INVARIANT_RESULT, but uses smt solver to find more complex expressions with constant result.
void invariant(unsigned char ch) {
unsigned char temp = (0xF0 | (ch << 4));
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
Related CWEs: CWE561.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | Yes |
C/C++ | Quality | Normal | Average | Yes |
Go | Quality | Normal | Average | Yes |
Kotlin | Quality | Normal | Average | Yes |
C# | Quality | Normal | Average | Yes |
Related CWEs: CWE561.
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.
For code related to macros .MACRO
subtype is
emitted.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Subtype of UNREACHABLE_CODE for situations where function, that terminated a program, was called.
int call_num = 0;
void func(int flag) {
(0);
exit+;//svace: emitted UNREACHABLE_CODE.TERMINATION
call_num}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
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 the following code snippet the programmer may incorrectly assume
that default
case will be selected when 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | No |
C/C++ | Quality | Normal | Average | No |
Go | Quality | Normal | Average | No |
Kotlin | Quality | Normal | Average | No |
Subtype of UNREACHABLE_CODE for situations, where a code is unreachable due to condition that depends on global variables.
int flag = 0;
#define mode(a) flag
void f(int* a) {
int m = mode(1);
if(m) {
if(a) { //UNREACHABLE_CODE.GLOBAL
*a = 0;
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Low | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Average | No |
C# | Quality | Normal | VeryHigh | Yes |
Related CWEs: CWE561.
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
the 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
C/C++ | Quality | Normal | Unknown | No |
Go | Quality | Normal | Unknown | No |
This checker detects redundant comparisons.
int example(int a) {
if (a >= 2) {
if (a > 0) { //svace: emitted REDUNDANT_COMPARISON
return 1;
}
}
return 0;
}
In the example above the second if-condition is always true because
the first if-condition has already checked that a
more than
zero.
func ret(a int) int {
return 10
}
func example(a int) int {
:= ret(a)
z
if z == 10 { //svace: emitted REDUNDANT_COMPARISON
return z + 3;
}
return z + a; /* unreachable code */
}
Function example
illustrates the defect: the variable
z
has a constant value received from function
ret
, so the if-condition is redundant because
z
is always 10
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Subtype of REDUNDANT_COMPARISON for situations, where a code is redundant due to the condition that depends on the returned value of the function call.
int func() {
return 0;
}
void example(int a) {
if (func() < 0) { //svace: emitted REDUNDANT_COMPARISON.RET
++a;
}
}
Function example
illustrates the defect: the returned
value of func()
has a constant value equals to
0
therefore the comparison with 0
is always
false.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
Go | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Subtype of REDUNDANT_COMPARISON.ALWAYS_FALSE for situations, where a code is redundant due to condition that depends on global variables.
int flag = 0;
#define mode(a) flag
void f(int* a) {
int m = mode(1);
if(m) { //svace: REDUNDANT_COMPARISON.GLOBAL
*a = 0;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE457.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | VeryLow | No |
This checker finds different situations, where a structure of structure field is not initialized before using.
typedef struct {
int k;
int p;
int q;
} T;
void test1(T* dst) {
;
T val
.k = 3;//Structure val was not fully initilialized
val
(dst, &val, sizeof(T));//copied not initilized sturcture - UNINIT.STRUCT
memcpy}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | No |
The checker finds situations when library function fills some buffer but caller code does not check that all values are filled. Buffer may have uninitialized memory.
struct S {
int a;
int b;
char buf[10];
int c;
};
int func_with_error(int fd) {
struct S s;
(fd, &s, sizeof(struct S));
read
int x = s.c; //error, we don't know that structure 'S' was fully filled.
return x; //x may contain uninitialized values
}
int fix(int fd) {
struct S s;
int len = read(fd, &s, sizeof(struct S));
if (len!=sizeof(struct S))
return -1;
int x = s.c;
return x;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
This checker finds a rare case of race condition where a handler tries to reinstall a signal handler from itself. On systems that uninstalls 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 uninstalled (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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
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 specific for libraries.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
This checker detects cases of opening 32-bit dynamic libraries via
dlopen
from <dlfcn.h>
. The warning is
emitted if the first argument of dlopen
starts with
/lib
or /usr/lib
.
typedef void* dll_handle_t;
int loadlib(const char* path) {
= dlopen(path, RTLD_LAZY);
dll_handle_t handle if (!handle)
return -errno;
return 0;
}
void foo() {
= loadlib("/usr/lib/lib.so");
dll_handle_t handle // ...
}
Invocation of loadlib
is an indirect call of
dlopen
.
Function foo
illustrates the defect: the first argument
of loadlib
starts with /usr/lib
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
Related CWEs: CWE404.
This checker finds situations where using of a library function is
incompatible with the passed parameter. These situations specific for
C/C++ languages, since different objects (sockets, file descriptors, or
I/O streams) are using the same type (usually int). It means that type
of variable not contains purpose of this variable. For example, it is
possible to use lseek()
function for an argument of type
int, which is actually associated not with a file, but with a socket.
Such cases of working with library functions are incorrect. The checker
can work with I/O streams, file descriptors, sockets and pointers for
working with dynamic memory.
void func() {
int fd = open("some_file.txt", SOME_FLAGS);
// Bind function is using for file descriptor
(fd, ADDR, ADDR_LEN);
bind}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Related CWEs: CWE61.
This checker finds situations where a file is not checked for a
symbolic link before opening. This should be checked with
S_ISLNK
macro.
void func(char *path, char *data) {
// Variable 'fp' is not checking for a symbolic link
int fp = open(path, SOME_FLAGS);
if (fp) {
(data, data, strlen(data));
write}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Related CWEs: CWE253.
This detector finds cases where the return value of a library
function is handled incorrectly. These situations contain cases where
the return value is compared for a wrong expression. For example, the
printf
function returns the number of characters printed,
and comparing this value to a number greater or less than what you want
to print would be incorrect.
void func() {
// Compare with 0 is incorrect. It must be EOF constant (in error case) or length of "string"
if (fprintf(stdout, "%s\n", "string") == 0) {
int do_smth;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
This checker detects cases of double file opening. The warning is emitted if file was opened and was not closed before the new file opening. File opening maybe under conditions, inside operators and loops.
FILE* foo(const char* fp) {
return fopen(fp, "a");
}
void bar() {
char filepath[] = "path/to/file";
(filepath, O_RDONLY);
open// ...
FILE* output = foo(filepath);
// ...
}
Invocation of foo
function is an indirect file
opening.
Function foo
illustrates the defect: foo
opens file, after the file was opened again by open
function.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
This checker finds situations where POSIX system call
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 third argument to set the correct file access rights.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Average | Yes |
The Linux-specific 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Average | Yes |
The Linux-specific 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Average | Yes |
The Linux-specific 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}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | VeryLow | Yes |
Same as FORK_PROCESSES_MEET, but in this case the processes meet after at least one of them throws an exception.
std::string a = "Hello";
int pid = fork();
if (pid == 0) { // Child.
// May throw an exception
.at(10) = 'u'; // FORK_PROCESSES_MEET.EXCEPTION emitted.
a(127);
_exit} else { // parent
.at(7) = 'b'; // May throw second exception.
a}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Critical | Unknown | No |
Related CWEs: CWE676.
The checker is emitted for pattern when c library function
strncmp
uses result of strlen
as length
parameter. The problem that such using checks only prefix of string
because null-terminator is not checked. It may be source of
vulnarability when using for compariso passwords. Correct pattern should
use strlen(arg) + 1
.
int get_id(char*name);
int bad_code(char*name, char*passwd, char* argv[]) {
if (strncmp(passwd, argv[1], strlen(passwd)) == 0) {
return get_id(name);
} else {
return -1;
}
}
int good_code(char*name, char*passwd, char* argv[]) {
if (strncmp(passwd, argv[1], strlen(passwd) + 1) == 0) {
return get_id(name);
} else {
return -1;
}
}
int good_code_too(char*name, char*passwd, char* argv[]) {
if (strcmp(passwd, argv[1]) == 0) {
return get_id(name);
} else {
return -1;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
This checker finds insecure HTTP Get method using instead of safe POST method.
void foo() {
* curl = curl_easy_init();
CURLif (curl) {
;
CURLcode res// ...
= curl_easy_perform(curl);
res }
}
Function foo
illustrates the defect:
curl_easy_init
creates new session with GET method by
default. Operation will be processed after calling
curl_easy_perform
.
void bar() {
* curl = curl_easy_init();
CURLif (curl) {
;
CURLcode res// ...
(curl, CURLOPT_POST, 0L);
curl_easy_setopt= curl_easy_perform(curl);
res }
}
Function bar
illustrates the defect:
curl_east_setopt
with option CURLOPT_POST
and
parameter equal to zero sets GET method. Operation will be processed
after calling curl_easy_perform
.
Those warnings are specific to issues in C++ code.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | High | Yes |
Related CWEs: CWE457.
This checker finds constructors (including 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | VeryHigh | Yes |
Related CWEs: CWE404, CWE459, CWE762.
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[]`.
char* GetExtraCode() {
char* chars = new char[16];
[0] = '\0';
charsreturn chars;
}
void test_bad() {
/* Here using 'unique_ptr<char>' instead of 'unique_ptr<char[]>' makes the
* specialization to use 'std::default_delete' instead of 'std::default_delete<T[]>'
* which actually calls 'delete' instead of 'delete[]'. */
std::unique_ptr<char> warmup_script(GetExtraCode()); //error HEAP_INCOMPATIBLE.ARRAY
}
void test_good() {
std::unique_ptr<char[]> warmup_script(GetExtraCode()); //no error
}
In the example above class std::unique_ptr<char>
will call delete
at its destructor. So, function
test_bad
contains an error. For fixing it, template
specialization with array type
std::unique_ptr<char[]>
should be used, as in
function test_good
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
Related CWEs: CWE404.
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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
This checker finds classes in which some method (usually constructor) allocates memory, but destructor doesn’t deallocate it.
class C1 {
int* buf;
public:
() { buf = new int[10]; }
C1~C1() {} // Warning: missing `delete[]` for `buf`.
};
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
Related CWEs: CWE401, CWE404, CWE775.
This checker finds classes in which some method (usually constructor) acquires resource, but destructor doesn’t release it.
class C1 {
FILE* f;
public:
() { f = fopen("filename", "r"); }
C1~C1();
};
::~C1() { // Warning: missing `fclose` for `f`.
C1}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
Related CWEs: CWE401.
This checker finds classes in which there exist two methods that being called consequtively may cause a memory leak. It works the same way as regular MEMORY_LEAK does, except it checks sequences of two methods instead of single functions.
class C1 {
int* buf;
public:
() { buf = new int[10]; }
C1~C1() { delete[] buf; }
& operator=(C1& other) {
C1this->buf = other.buf; // Warning: `this->buf` allocated in constructor will leak after assignment.
}
};
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Low | Yes |
Related CWEs: CWE401.
The same as above checker but uses MEMORY_LEAK.EX instead of regular MEMORY_LEAK.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
Related CWEs: CWE401.
The same as above checker but uses both MEMORY_LEAK.EXCEPTION and MEMORY_LEAK.EX.EXCEPTION.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
Related CWEs: CWE775.
The same idea as MEMORY_LEAK.PAIR but uses HANDLE_LEAK instead.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
Related CWEs: CWE775.
The same idea as MEMORY_LEAK.EX.PAIR but uses HANDLE_LEAK.EX.PAIR instead.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
Related CWEs: CWE775.
The same idea as MEMORY_LEAK.EXCEPTION.PAIR but uses HANDLE_LEAK.EX and HANDLE_LEAK.EXCEPTION instead.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
Related CWEs: CWE416.
This warning is emitted when an internal string buffer (returned by
method 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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
Finds implementations of assignment 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`,
memcpy// while code produces UB if `a` equals `*this`.
return *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 | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | High | No |
This checker family finds situations where the assignment operator defies syntactical correctness for equality in C++.
Present warning subtype is emitted when assignment operator is written in such way, that some logically correct constructions will lead to syntactical error (and therefore just can’t be used). The subtype is hidden by default.