Svace is an automatic defect detection tool for source code written in C, C++, C#, Go, Java, Kotlin, Python and Scala 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 build monitoring allows to capture the finest details of the build process unavailable to other tools without this feature. In turn this lets the analyzer produce accurate and relevant intermediate representation of the analyzed source code. Finally, the main analysis engine utilize it to perform deep interprocedural and path-sensitive analysis able to uncover complex issues in the source code. Secondary analysis engines are also run on the same intermediate representation to quickly supplement them with a large number of more specific and simpler issues. The analysis results of all tools are combined and presented together 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 are new to this run and which issues detected on this run have been 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
maven
, sbt
,
scalac
, dotty
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 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
Use Aurora-specific Linux distributions of Svace. Note that Aurora SDK prior to version 5.0 and Sailfish SDK build environments are 32-bit.
Place Svace distribution in a folder for which 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.
Run svace build tool inside the
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.
Add a --prepend-preload-lib
option to
svace build
command.
Svace supports building of Go projects on Linux that use Bazel build system or bazelisk wrapper:
Run the following commands before the build (otherwise not all compilations may be intercepted because of caching):
bazel clean --expunge # clean up cache
bazel shutdown # stop Bazel server processes
# or use '--batch' flag of subsequent 'bazel build' command
Add a --go-interception-mode compile
option to
svace build
command.
Add a --spawn_strategy=local
option to
bazel build
. More details here.
To analyze only Go source code, add options
--disable-language all
and
--enable-language go
to svace build
or
svace analyze
commands. In this way only Go compilations
are intercepted/analyzed.
For example, if the build command of your project is
bazel build //...
, you may use svace build
command like this:
svace build --disable-language all --enable-language go --go-interception-mode compile bazel --batch build --spawn_strategy=local //...
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 for subsequent analyses.
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 additionally includes Clang Static Analyzer and SpotBugs. Also, a number of lightweight detectors are implemented in Svace UAST engine for searching of issues patterns common for all supported languages. 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 are 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.
options:
--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 Configure or run Svace server.
warning Control which warning types are enabled during
analysis.
miscellaneous:
admin Manage object pool in a project directory.
compare-svres Compare warnings from two .svres files ignoring
checker names and generate comparison stats.
context-spec Manage context in which particular function will be
analyzed.
export-build Export a build object into an external directory.
filter-svres Extract the issues enabled in given warning settings
file from a given .svres.
history Interact with a warning database.
ignore-test Check svace.ignore filter.
inactive-comments Find warning suppression comments that appear to be
obsolete.
match-svres Match two svres files and show identifiers of the
matched warnings from the first svres file.
merge-build Merge multiple build objects into a single build
object.
merge-svres Merge multiple .svres files with analysis results for
different projects into a single file.
remote Execute command on remote analysis server.
review-rebase Rebase warning review from one svres file to other.
server-config Manage server configuration settings.
show Show results in a Svace server (localhost:8060).
spec Manages specifications for third-party library
functions,including user-provided.
split-svres Split a multi-project .svres file into separate files
with analysis results for individual projects.
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.
experimental:
suppress Run stand-alone suppression tool.
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.
options:
--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
svace build msbuild /t:Rebuild MySolution.sln
positional arguments:
build-command
options:
-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,
csharp, cxx, go, javascript, kotlin, python, scala,
visualbasic. 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: csharp, cxx,
go, javascript, kotlin, python, scala, visualbasic.
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 IR generation for the specified
programming language. Valid values are: all, go,
javascript, kotlin, python. See also '--uast-
languages'.
--uast-languages UAST_LANGS
Enable UAST IR generation for languages in the
specified comma-separated list and disable it for
other languages. Supported languages are: go,
javascript, kotlin, python. 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.
--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.
--enable-mypy, --disable-mypy
Enable or disable Python analysis with mypy static
analyzer. Note that mypy is not bundled with Svace, so
it can be used only if found in your PATH and mypy
version is at least 0.790.
--enable-python-ir-gen, --disable-python-ir-gen
Enable or disable generation of Python IR used for
analysis by Svace. This option is for advanced users
only.
--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.
--javascript DIRECTORY or FILE.js
Specify path to a directory with JavaScript source
files or a single JavaScript file to process for
subsequent analysis. Note: Only UAST analysis is
currently supported for JavaScript.
--debug Enable verbose logging useful for debugging and making
bug reports.
--enable-dxr, --disable-dxr
Build source reference index used for source code
navigation (via history API). By default, it is
enabled.
--enable-csa, --disable-csa
Run Svace checkers implemented in Clang Static
Analyzer during the build phase. By default, this
feature is disabled.
--enable-csa-deps, --disable-csa-deps
Prepare everything needed for running Clang Static
Analyzer during the analysis phase. By default, this
feature is enabled.
--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 8 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.
--enable-link-fixup, --disable-link-fixup
Attempt to fix missing links in source markup data by
heuristically linking similar symbols. This feature is
disabled by default.
--hash-server-memory HASH_SERVER_MEMORY
Specify maximum size of Java heap (in MB) that Svace
hash server may use (default: 2048).
--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 that Svace Kotlin
compiler may use (default: 'auto'). The value of this
option may be an integer, an integer percentage (in
the range from '1%' to '1000%'), or 'auto'. If set to
an integer, Svace will use the specified size (in MB)
for all compilations. Otherwise, Svace will attempt to
mirror the memory usage profile of the intercepted
build by detecting the maximum heap size used by each
compilation and scaling it by the specified percentage
(or an internal factor if 'auto'). If, for some
compilation, the heap size detection fails, Svace
won't set the maximum heap size for its compiler when
processing this compilation (so a JVM default will be
used instead).
--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, arm,
aarch64, x86, x64, hexagon, mips, mipsel, mips64,
mips64el, powerpc, powerpc64, powerpc64le, riscv32,
riscv64, sparc, sparc64. Values other than 'auto'
override automatic target detection. This option is
intended for debugging only.
--disable-bitcode-gen
Don't run Clang to generate bitcode files. Note that
disabling bitcode generation makes it impossible to
run Svace checkers on the resulting build object. It
is useful if you only want to get results from tools
run at the build time (e.g., warnings from CSA or
metrics). Turning on this option disables default
C/C++ metrics generation.
--disable-pch-gen Don't create an internal precompiled header (PCH) when
creation of a PCH by a supported compiler is detected.
Internal PCHs are used by Svace to speed up bitcode
generation for subsequent compilations, but might lead
to incorrect bitcode in some corner cases. In
particular, if an intercepted compiler creates a PCH
but rejects it in subsequent compilations (for
example, due to macro or option mismatch), falling
back to usage of normal headers, Svace might still use
an unsuitable internal PCH due to different mismatch
detection rules in Clang. PCH generation is currently
supported only for Clang and (partially) GCC.
--enable-dxr-collect Run a helper process that aims to reduce load on the
filesystem when building source reference index is
enabled (see --enable-dxr).
--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, --append-preload-lib
Usually, the Svace library for function call
interception should be appended to LD_PRELOAD
environment variable, ensuring that Svace
modifications to the execution context are not
overridden by other LD_PRELOAD-libraries. 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. By default, the Svace library is
prepended if Scratchbox environment is detected, and
is appended otherwise.
--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-path-sens-csa
Disable all path sensitive checkers of Clang Static
Analyzer.
--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 ';'.
--enable-jar-decompilation, --disable-jar-decompilation
Enable or disable decompilation of used JAR libraries.
By default, it is disabled.
--base-dir CSHARP_BASE_DIR
Base directory to use relative warning paths in C#
source code analysis.
--enable-csa-stat Generate '*.json.times' files containing the running
time of CSA checkers (in ms) in the build directory.
--keep-intermediate-source
If --enable-csa-deps is specified, do not remove
partially preprocessed '.source' files from the build
directory after archiving them. This option is
intended for easier debugging.
--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).
--preserve-build-order
Store compilation data in the same order as it's
received by Svace hash server. If the build order is
deterministic, but the build data is different between
runs, this may be useful to ensure that the data is
always processed in the build order by build object
consumers. By default, the build order is not
preserved, and build artifacts are usually sorted by
content hash before storage.
--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'.
--enable-mount-ns-workaround
If the build process enters a mount namespace and then
creates new mount points under non-shared mount
points, those new mount points are not visibile in the
parent mount namespace, preventing top-level Svace
processes from accessing affected paths. This option
enables a workaround. Currently, it works only if the
build process doesn't also enter a PID namespace.
--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.
--enable-goa, --disable-goa
Enable or disable Go AST analysis during svace build.
--enable-go-ir-gen, --disable-go-ir-gen
Enable or disable generation of Go IR used for
analysis by Svace. This option is for advanced users
only.
--enable-go-vendor-gen, --disable-go-vendor-gen
Enable or disable generation of Go IR for dependencies
from vendor/modules.txt used for analysis by Svace.
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 = ').
--plugin-dir : Add directory to search svace-api plugins in.
Use this option multiple times to add multiple directories.
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.
Special warning names that start with 'CWE' can be used to show all warnings
corresponding to given CWE identifier.
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 = ').
--plugin-dir : Add directory to search svace-api plugins in.
Use this option multiple times to add multiple directories.
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
JAVASCRIPT
VISUALBASIC
PLSQL
NONE
INTERNAL
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
# Show all warnings corresponding to CWE190:
svace warning CWE190
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.
options:
-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.
--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).
--enable-language LANG, --disable-language LANG
Enable or disable analysis for the specified
programming language. Valid values are: all, csharp,
cxx, go, javascript, kotlin, python, scala,
visualbasic. 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: csharp, cxx, go, javascript,
kotlin, python, scala, visualbasic. 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, go,
javascript, kotlin, python. 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: go, javascript,
kotlin, python. 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, arm, aarch64, x86,
x64, hexagon, mips, mipsel, mips64, mips64el, powerpc,
powerpc64, powerpc64le, riscv32, riscv64, sparc,
sparc64.
--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.
--build-call-graph-only
In this mode Svace will make call graph for analyze
project. Any analysis will be skipped.
--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).
--analyze-csharp-solutions-separately
Analyze all captured C# solutions separately.
--plugin-dir DIR Add directory to search svace-api plugins in. Use this
option multiple times to add multiple directories.
This tool manages and starts Svace in server mode listening for remote analysis requests or providing web-interface for integrated analysis results history server.
usage: svace server [--server-dir DIR] <subcommand> [args]
options:
--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.
reboot Reboot Svace remote server.
admin Manage a Svace server.
show-api-docs Display documentation for Svace history API.
Main command line tools are listed in the Command line tools chapter. This section contains other command line tools that may be occasionally useful.
Command server-config
is similar to the config
command and is used to
setup configuration parameters for starting a remote analysis server.
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
svres2json [options] FILENAME
svres2json is a converter from Svace results format to JSON format and vice versa.
Options:
FILENAME : path to the file to be converted
-h, -help, --help : display this message
-o, --out OUT_FILENAME : path to the file where converted results will be stored. If this option
is not used, stdout will be used for this purpose
--compact : dump traces in compact format
-b, --backward : turn the backward mode on. In this mode an input file must be in JSON
format
svres2sarif [options] FILENAME
svres2json is a converter from Svace results format to Sarif 2.1 format.
Options:
FILENAME : path to the file to be converted
-h, -help, --help : display this message
-o, --out OUT_FILENAME : path to the file where converted results will be stored. If this option
is not used, stdout will be used for this purpose
usage: svace spec <subcommand> [args]
Manages specifications for third-party library functions,including user-
provided.
options:
--help Show this help message and exit.
subcommands:
subcommand
add Compile specifications from source files and add them to the
specified location.
list Show existing specifications in the specified location.
remove Remove existing specifications from the specified location.
show Show the source file of a specification.
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.
Some warning reports are less useful than others, and because of that
Svace may suppress some warnings based on different conditions.
Information about suppressed warnings is added to the
.svace-dir/analyze-res/suppress
subfolder in the project
folder after the analysis.
Analysis is performed on the whole code used during the build process
including third-party dependencies and system components that aren’t
actually part of the project. To suppress warning reports for these
files add the regular expressions for these files or whole directories
to the .svace-dir/svace.ignore
file in the project folder.
Patterns for known system components, such as standard compiler header
files, are added by default to this file during project folder creation.
Note that these patterns control only the suppression of the warning
reports for the corresponding files; their content is still analyzed to
perform interprocedural analysis of the code that calls functions from
them.
It is sometimes useful to ignore certain warnings for a specific line
or block of code without affecting the entire analysis process. One
popular way to do this is to mark the related source code lines with
comments in a predefined format. The most common example is a simple
NOLINT
comment. But many static analysis and related tools
use their own formats. Besides NOLINT
Svace supports
several popular warning suppression comment formats out of the box:
NOSONAR
from SonarQube, a tool for continuous inspection of
code quality; svacer_review
from Svacer, a standalone
analysis history management server; noqa
used to ignore
PEP8 warnings in Python. All warning reports issued on the source lines
with any of these comments can be suppressed if this feature is
enabled.
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 comments in the Svacer format
svacer_review
to suppress only warning types listed there.
Arguments of all other types are ignored and such comments suppress
reports of any warning types. E.g. the
nolint:unused,deadcode
comment is interpreted the same way
as nolint
and the noqa: E731,E123
comment is
interpreted the same way as noqa
.
By default this functionality is disabled and can be enabled using
the SUPPRESS_BY_COMMENT
configuration option. By default warning
suppression comments in all formats are processed but specific formats
may be disabled separately using the corresponding configuration
option:
SUPPRESS_BY_COMMENT.SONAR
- for
NOSONAR
;SUPPRESS_BY_COMMENT.LINT
- for nolint
and
nolintnextline
;SUPPRESS_BY_COMMENT.SVACER
- for
svacer_review
;SUPPRESS_BY_COMMENT.NOQA
- for noqa
;Additionally, it is possible to process warning suppression in a
custom format by assigning a SUPPRESS_BY_COMMENT.TEMPLATE
configuration option with a suppression format regular expression. If
the comment text in the source code matches this regular expression all
warning reports for the corresponding source code line are
suppressed.
When Svace is used regularly to analyze the same project it is possible to cache intermediate analysis results for the unchanged code parts to save time and resources for subsequent analysis runs. This takes some disk space to store the cache but can reduce analysis time significantly.
To use this feature build the project with svace build
as usual but add the svace analyze
--with-cache
parameter (or enable the
PERSISTENT_MODE
configuration
option). When this parameter is present the analyzer stores into the
project folder the intermediate results and marks their dependencies at
the end of the analysis. To use the cache during the next analysis use
the same parameter --with-cache
in the
svace analyze
command. Svace then checks the dependencies
of the intermediate source code representation files in the new build
object being analyzed, and if they match those from the cache, retrieves
the analysis results from the cache and uses them instead of running it
again. To achieve the best cache utilization it’s advised to reproduce
as many build environment details as possible. Especially, the folder
where the project is built may affect the resulting intermediate files
and it is best to rebuild the project for the next analysis in the same
folder.
By default the cache is stored in the project folder in the
.svace-dir/analyze-storage
subfolder and is expanded with
new entries after each analysis with cache. Its initial size after the
first analysis is comparable to the size of the intermediate source code
representation files of a single build object and grows with each
subsequent analysis with cache of a new build object depending on the
volume of changes in the analyzed source code. When Svace is integrated
into the development process in such a way that a new project folder is
created for each analysis, it is necessary to designate an external
cache storage folder using a svace analyze
--analyze-storage-dir <SVACE_ANALYZE_STORAGE_DIR>
command line parameter, where
<SVACE_ANALYZE_STORAGE_DIR>
is a path to the folder
where the analysis cache should be stored. This option can also be used
to share the cache between different projects that contain common source
code files. If the cache size becomes too large it can be purged by
removing the cache storage folder entirely. It will be recreated during
the next analysis with cache. The cache folder should also be removed
when upgrading the analyzer to the new version.
The same cache is used for all analysis engines but may provide
different impact for each of them for a particular project. There are
several configuration options that control
specific analysis engines cache usage. Using these options the cache can
be disabled selectively for some of them:
PERSISTENT_MODE.CSA
for Clang Static Analyzer,
PERSISTENT_MODE.SPOTBUGS
for SpotBugs,
PERSISTENT_MODE.MODULE_PARSING
and
PERSISTENT_MODE.FUNC_ANALYSIS
for Svace.
Most projects depend in some way on third-party code, e.g. libraries.
Interprocedural analysis may be much more precise when the analyzer has
knowledge of what the libraries’ code does but most often they are built
separately and it’s not available to the analysis. To overcome this
problem it is possible to analyze part of the code, particularly
third-party libraries, and store the intermediate analysis results in
the form of function summaries to be reused during the analysis of the
other parts of the project. Such summaries contain information about the
notable side effects from the calls of the respective functions. In
order to store such summaries build the library(-ies) with svace build
as usual and add the svace analyze
--collect-summary
parameter when running its analysis. The
summaries are stored in the project folder and can be used for
subsequent analysis of the project dependent on the library by using a
svace analyze
--use-summary
parameter when running its analysis.
In many situations it’s more convenient to run svace build
and svace analyze
steps independently
on different systems. For example, a developer’s PC is able to build a
large project but lacks the required resources to perform the analysis.
To support such scenarios in a simpler way it is possible to designate
one computer as a remote analysis server to perform the analyses and
provide it with analysis tasks over the network from other computers.
Remote analysis server supports multiple projects with own analysis
configurations and user authorization.
The server uses a special server folder that contains its
configuration and data. Note that it is different from the Svace project folder
.svace-dir
. A server folder can be initialized by running a
svace server init
command in an empty folder. The server is
initialized with default settings without any users or projects set up
and they have to be added before its usage. To simplify server
initialization special svace server admin create-admin
or
svace server admin create-defaults
commands can be used.
The former command creates an admin
user with
admin
role allowing to manage the whole server. And the
latter command additionally adds a regular user main
with a
role main
that allows to view and modify any project on the
server, and a project group main
with a
default
project. Managing projects, project groups, users,
roles and permissions is described below.
The following commands sequence demonstrates creating a remote analysis server folder and its basic configuration suitable for performing remote analysis:
mkdir server-dir
cd server-dir
svace server init
svace server admin create-defaults
Above commands produce output similar to the following, showing the initialization process:
Creating admin...
Enter user's password or press enter to use autogenerated password (without quotes): "!?qVt_1J'R%X"
Enter password:
Nothing entered: will use auto-generated password from above.
Creating role 'main'...
[INFO] Role created.
Creating user 'main'...
Enter user's password or press enter to use autogenerated password (without quotes): "jv8HC~hm4nf$"
Enter password:
Nothing entered: will use auto-generated password from above.
[INFO] User created.
Adding role 'main' to user 'main'...
[INFO] Role main added for user main
Creating project group 'main'...
[INFO] Project group created.
Creating project 'default'...
[INFO] No database for svace project directory; initializing new one
[INFO] Project directory initialized @server-dir/dirs/default
[INFO] Project created.
Adding modify permissions for role 'main'...
[INFO] Permission modify on SERVER granted for role main
The following server settings can be changed using a svace server-config
command:
SERVER_PORT
- a port used by the server to listen for
remote analysis clients connections (55135 by default).WEB_SERVER_PORT
- a port used by the server to listen
for user connections using a web-browser (8060 by default).SIMULTANEOUS_ANALYSIS_LIMIT
- a number of simultaneous
analyses the server is allowed to run. By default 3 analyses are allowed
to run in parallel and each analysis task takes one slot unless it’s
assigned a specific weight with a svace remote analyze
--weight
parameter. If not enough simultaneous analysis
slots are available the analysis task is delayed until previous analysis
tasks finish.ALLOW_SIMULTANEOUS_ANALYSIS_PER_PATH
– whether parallel
analyses are allowed using the same project folder. If it is set to
‘false’ then only one simultaneous remote analysis per path is allowed.
The ‘true’ value of this option is ignored for server single-start
mode.REJECT_CLIENT_CONFIGS
- when this server configuration
setting is enabled client analysis settings including enabled warnings
configuration don’t have an effect on the remote analysis. By default
the client settings are applied. By enabling this configuration setting
it is possible to enforce remote analysis settings to those predefined
on the server by its administrator.JOIN_CLIENT_CONFIGS
- when this server configuration
setting is enabled client enabled warnings configuration are merged with
the server configuration so that a warning
type is enabled if it is enabled either on the server or on the
client. By default no configuration merging is made. By enabling this
configuration setting it is possible to supplement required remote
analysis settings with the optional client settings.DELETE_ANALYZE_DIR_ALL
- when this server configuration
setting is enabled the results analysis folder is removed after remote
analysis has been finished to preserve server disk space. By default it
is not removed.DELETE_ANALYZE_DIR_SUCCESS
- when this server
configuration setting is enabled the results analysis folder is removed
after remote analysis has been finished successfully. This allows to
preserve server disk space but still diagnose the causes of analysis
errors when remote analysis fails. By default the analysis results
folder is not removed in all cases.CREATE_PROJECT_IF_NOT_EXIST
- when this server
configuration setting is enabled and there is no project with the
requested name it is created. By default an error about wrong project
name is thrown in this case.The following client configuration option affects remote analysis:
ZIP_ANALYZE_RES
- if enabled Svace compresses whole
analysis results folder contents to transfer it to the client; by
default it is disabled and only analysis results are transferred.If some option should be changed for all projects on server - it can
be done by command svace config
with parameter
--system
or --global
.
For example:
svace config --sytem SUPPRESS_BY_COMMENT true
Server config option REJECT_CLIENT_CONFIGS
should be
enabled (command svace server=config
).
Running remote analysis requires specifying a project on the client.
Each project is represented by a project folder contained inside of the
server folder and can have own analysis settings and caches. Remote analysis project is
created automatically when a remote analysis of a previously
non-existing project is requested. In this case it is assigned to the
main
project group created during the server setup if the
user starting the remote analysis has permissions to do so. To
control the group which it belongs to the project can be created
beforehand using a
svace server admin create --project PROJECT --group GROUP
command during server configuration, where PROJECT
represents the name of the project being created and GROUP
- the name of the group which it should belong to. The following server
configuration command
svace server admin create --project-group GROUP
creates a
new project group with the name GROUP
. It is possible to
register an already existing external project folder to be used for the
project instead of creating it in the server folder. To do this use the
--path
option to specify the location of this project
folder DIR
in the project creation command
svace server admin create --project PROJECT --group GROUP --path DIR
.
To show the list of all projects available on the server with their
project folder locations the
svace server admin list --projects
command can be used. To
show the list of available project groups there is the
svace server admin list --project-groups
command. Command
svace server admin project-group GROUP --list-projects
shows the projects that belong to the specified group
GROUP
. Removing a project PROJECT
is done
using the svace server admin remove --project PROJECT
command. Similarly, removing a project group GROUP
is done
using the svace server admin remove --project-group GROUP
command.
In the following example a new project group with the name
foo
is created followed by creation of a new project
bar
and registering an external project baz
,
both in this group:
svace server admin create --project-group foo
svace server admin create --project bar --group foo
svace server admin create --project baz --group foo --path /home/user/baz/.svace-dir
The remote analysis server works with multiple users using a role-based access control model. Running remote analysis requires user authentication with an account having sufficient privileges for the specified project.
On each remote analysis server, an unlimited number of users can be created, each of whom can have an arbitrary set of roles. Different users can have the same role. Each role describes a set of permissions for accessing individual projects, all projects in certain groups, or all projects on the server. If a role has specific permissions for a group of projects, then all users with that role have those permissions for all projects in that group. If a role has specific permissions for the server, then all users with that role have those permissions for all projects on that server. The final user permissions are determined by the sum of permissions from all the roles held by the user.
There are three types of permissions - read
,
modify
, and admin
. Each subsequent type of
permission includes the properties of the previous one. Currently, the
remote analysis server only checks the modify
permission
for the specified user to run an analysis (thus, modify
or
admin
permission for the project is required). Other types
of permissions are used to organize access to the capabilities of the
built-in history server.
Creating a new user is done using the
svace server admin create --user USER
command, where
USER
is the desired username. When creating a user, you
will need to enter their password (or accept the password suggested by
the server). By default, the user does not have any roles. To change a
user’s password, use the
svace server admin update --user USER
command, where
USER
is the name of an existing user for whom you want to
change the password. You can view a list of existing users using the
svace server admin list --users
command. Deleting a user
account is done with the
svace server admin remove --user USER
command, where
USER
is the name of the existing user you want to
delete.
To create a new role, one should use the command
svace server admin create --role ROLE
, where
ROLE
is the desired role name. By default, the new role
doesn’t have any permissions. To grant to a role a specific permission
for a project, you need to execute the
svace server admin grant-permission PERMISSION --role ROLE --project PROJECT
command. In this command, ROLE
is the name of an existing
role to which you want to grant permission, PERMISSION
is
the corresponding permission (read
, modify
or
admin
), and PROJECT
is the name of the
existing project for which the role is granted the permission.
Similarly, to grant to a role a specific permission for an entire
project group, you should use the
svace server admin grant-permission PERMISSION --role ROLE --project-group GROUP
command, where GROUP
is the name of an existing project
group for which the role is granted the permission and the other
parameters are the same as in the previous command. If you don’t specify
any of the --project
or the --project-group
parameters in the
svace server admin grant-permission PERMISSION --role ROLE
command, then the corresponding permission PERMISSION
will
be granted to the role ROLE
server-wide. To view the
current permissions of a role, you can use the command
svace server admin role ROLE --list-permissions
, where
ROLE
is the name of an existing role for which you want to
see permissions. To remove all permissions granted to a role
ROLE
for a project PROJECT
, you should run the
command
svace server admin revoke-permission --role ROLE --project PROJECT
.
Similarly, to remove all permissions granted to a role ROLE
for a project group GROUP
, you should use the command
svace server admin revoke-permission --role ROLE --project-group GROUP
.
The command
svace server admin revoke-permission --role ROLE
removes
server-wide permissions granted to the role ROLE
. You can
display a list of existing roles on the server using the command
svace server admin list --roles
. To delete a role, use the
command svace server admin remove --role ROLE
, where
ROLE
is the name of the existing role you want to
delete.
To assign a user a new role, you need to execute the command
svace server admin user USER --add-role ROLE
, where
USER
is the name of an existing user you want to assign the
role with the name ROLE
, which should be created
previously. Conversely, to remove the role ROLE
from the
user USER
, you should execute the command
svace server admin user USER --remove-role ROLE
with the
corresponding parameters. The command
svace server admin user USER --list-roles
is used to view
the current roles assigned to the user USER
, and the
svace server admin user USER --list-permissions
command is
used to view the actual permissions that the user has on projects,
project groups or the entire server. The command
svace server admin role ROLE --list-users
is used to view
users who have a specific role, where ROLE
is the name of
the role for which you want to determine the list of users who possess
it.
The example commands below create a new user jenkins
and
allow it to run remote analysis using only the bar
project
using a ci_remote_analysis
role:
svace server admin create --user jenkins
svace server admin create --role ci_remote_analysis
svace server admin user jenkins --add-role ci_remote_analysis
svace server admin grant-permission modify --role ci_remote_analysis --project bar
The server is started with the svace server start
command and can be stopped anytime by killing the server process,
e.g. by pressing Ctrl+C
in the terminal where it is has
been started. The following message is displayed when the server has
been started successfully and is ready to process analysis requests from
the clients:
Remote analysis server started at port 55135
Note: the port can be configured using the SERVER_PORT
server
configuration setting.
While running the server provides the possibility to access the
analyzer user guide text, information about the server status, and the
built-in history server (if the corresponding configuration setting is
enabled) using a web browser. To do this, you need to access the
appropriate port of the running server via the HTTP protocol. For
example, if the server is running on a machine with the IP address
192.168.1.10
, to obtain brief information about the status
of active analyses running remotely, you need to open the page
http://192.168.1.10:8060/status
. The port can be configured
using the WEB_SERVER_PORT
server
configuration setting.
To request remote analysis use the following command on the client,
where SERVER_ADDRESS
represents the address of the running
remote analysis server:
svace remote --host SERVER_ADDRESS analyze
When executing this command the client establishes connection with the remote analysis server and transfers all the data needed for analysis. The server then performs the analysis and returns the results to the client, which places them in the same way as if the analysis has been performed locally.
If the remote analysis server was configured to
use a custom port then it can be specified via the
svace remote --port PORT
option, where PORT
represents that port number. User credentials can be
specified using the --login USER
and
--password PASSWORD
options. If these options aren’t
specified the client requests them from the input stream. The project for the remote analysis is
specified with the --path PROJECT
option. If it is not
present then the name of the current working directory is taken as the
project name.
When the remote analysis is started successfully a message containing an analysis task hash like the following is printed on the client’s output stream:
Starting remote analysis task 'c5466046fe87bbc828de45daf2b1c1d9f33396c4' on remote server.
If necessary, this hash can be used to prematurely terminate the
remote analysis. This can be done by the user who initiated the remote
analysis, as well as by a user with admin
rights on the
project with which this analysis was launched. To do this, use the
following command, where TASK_ID
represents the analysis
task:
svace remote kill TASK_ID
The svace remote kill
command can also be used without
arguments, in which case the latest remote analysis launched by the
client is stopped. Analysis task identifiers of the currently executing
remote analyses can also be found on the remote analysis server status
page (see the last paragraph of the Starting and
stopping remote analysis server chapter).
Remote analysis server can also be started in a quick mode, where
user authentication is not required and analysis is only supported using
a single project folder. To start this mode simply initialize the
project folder using the svace init
command
and execute the svace server single-start
command. To
configure server settings you can use the
svace server-config
command as described in the Remote analysis
server configuration settings chapter.
Along with the remote analysis feature the server can provide a
feature for storing and viewing analysis results. The integrated
analysis results history server has a simple interface and limited
capabilities compared to the standalone Svacer
history server. To enable the integrated history server simply set
the ALLOW_WEB_HISTORY_ACCESS
setting to true
in the remote
analysis server configuration settings. After starting the remote
analysis server the integrated history server will be accessible via the
HTTP protocol through a web browser. For example, if the server is
running on a machine with the IP address 192.168.1.10
, the
integrated analysis results history server can be accessed by opening
the page http://192.168.1.10:8060
in a web browser and
following the “Integrated history web interface” link. The port can be
configured using the WEB_SERVER_PORT
server
configuration setting.
The integrated analysis results history server supports the same user and permission
management and project management
as remote analysis server. To access the server features the user has to
authenticate first. In order to view analysis results for specific
project the user needs at least read
permission for that
project, the modify
permission allows to review the
reported warnings by setting their status (one of
Confirmed
, Won't fix
,
False positive
, Unclear
of the default
Undecided
) and/or add a comment to the warning report.
The authenticated user has only access to those projects, that he has any permissions on. The project to work with, its branch and analysis results snapshot can be selected using the drop-down menus in the upper part of the server web-interface. When the needed analysis results snapshot is selected a list of warning types found in this snapshot is displayed on the left part of the page. The number of found warnings of a specific warning type is shown in parentheses after the corresponding warning type name. Left clicking on an item in this list displays all warnings from the selected analysis results snapshot of the corresponding type below. Clicking on an item in the warnings list makes the selected warning active and shows information about this warning below: warning message, location of the defect, trace and its review status along with added user comments. When a new warning report becomes active as well as when you click on the location links of the defect or elements of the trace in the active warning information panel, the source code of the corresponding location is displayed in the central part of the page. The source code panel supports IDE-like navigation: when you click with the left mouse button on an identifier in the source code (for example, a variable or a function name), all occurrences of that identifier are highlighted. Left-clicking on the highlighted identifier displays a context menu for that identifier. Using this menu, you can navigate to the main declaration or definition of the identifier as well as to the locations where it is used in the analyzed source code.
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 is related to using a value in a call as an argument when corresponding function parameter has specific annotation.
A null pointer dereference error occurs when a program attempts to
dereference a memory address or access an object reference that has a
null value (NULL
in C, nullptr
in C++,
null
in Java, etc). Such dereferencing is invalid because
the null pointer (null reference) does not point to any valid memory
location (to any valid object). When a null pointer dereference error
occurs in a program, written in a manual memory management language, it
typically results in a segmentation fault or access violation, causing
the program to terminate abruptly. For programs in automated memory
management languages in this case an exception is usually raised that
can lead to unexpected behavior. Here and below a pointer dereference
means literal pointer dereference for languages where it is applicable
and object reference or another corresponding notion for languages
without pointers.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
Visual Basic .NET | Quality | Critical | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Kotlin | Quality | Normal | High | Yes |
Python | Quality | Normal | High | Yes |
C# | Quality | Critical | High | Yes |
This checker finds situations where a pointer is dereferenced, but can only have a 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 a null value on
one of the possible execution paths.
if (dvfs->get_avail_governor)
return -ENOTSUP;
return dvfs->get_avail_governor(res_name, avail_governor);
In the example above an accidentally inverted condition meant to
return an error status in case the field
dvfs->get_avail_governor
is NULL
but
instead in this case the execution continues and leads to a null pointer
dereference during the call in the return
statement.
fun example(s: String?) {
if (s == null) {
derefAnyway(s)
}
}
fun derefAnyway(s: String?) {
print(s!!.length)
}
fun possibleFix(s: String?) {
print(s?.length)
}
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) {
s!!.length // kotlin compiler report a compilation error here
}
}
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 |
Python | Quality | Normal | Average | Yes |
Related CWEs: CWE476.
This checker is similar to the DEREF_OF_NULL checker but reports cases when a pointer is assigned a null value under a condition and is then dereferenced under another condition which is not mutually exclusive with the first one.
import java.io.File
fun handleCollection(collection: Collection<Any>?) {
for (elem in collection!!) {
// ...
}
}
fun handleCollectionCorrect(collection: Collection<Any>?) {
collection?.forEach { elem ->
// ...
}
}
fun example(f: File) {
val files = f.listFiles()?.asList()
handleCollection(files)
}
fun possibleFix(f: File) {
val files = f.listFiles()?.asList()
handleCollectionCorrect(files)
}
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 |
Python | Quality | Normal | High | Yes |
Related CWEs: CWE476.
This checker finds situations where a pointer is dereferenced, but can only have a null value because it was explicitly assigned a null value.
class Holder(str: String) {
var nullable: Int? = null
}
fun example() {
val h = Holder("Create and forget to init 'nullable'")
h.nullable!!.dec()
}
fun possibleFix() {
val h = Holder("Create and forget to init 'nullable'")
h.nullable?.dec()
}
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 |
Python | Quality | Normal | Average | Yes |
Related CWEs: CWE476.
This checker finds situations where a pointer is assigned a null value and is subsequently dereferenced. In the situations reported by the checker the dereferenced pointer is not necessarily null but the pointer that was assigned a null value is always dereferenced.
void ep_xfer_timeout(void *ptr)
{
ep_xfer_info_t *xfer_info = NULL; // Assigned a null value
int ep_num = 0;
dctl_data_t dctl = {.d32 = 0 };
gintsts_data_t gintsts = {.d32 = 0 };
gintmsk_data_t gintmsk = {.d32 = 0 };
if (ptr)
xfer_info = (ep_xfer_info_t *) ptr;
if (!xfer_info->ep) { // FLAW: Dereferenced a potentially null value
DWC_ERROR("xfer_info->ep = %p\n", xfer_info->ep);
return;
}
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 value is passed as a pointer argument to a function, where it is dereferenced under some conditions depending on external data (i.e. that can’t be guaranteed to always be false when the pointer is null).
static char *
partial_die_full_name (struct partial_die_info *pdi,
struct dwarf2_cu *cu)
{
...
die = follow_die_ref (NULL, &attr, &ref_cu); // FLAW: Null value passed as 1st parameter
...
static struct die_info *
follow_die_ref (struct die_info *src_die, const struct attribute *attr,
struct dwarf2_cu **ref_cu)
{
...
if (!die)
error (_("Dwarf Error: Cannot find DIE at %s referenced from DIE "
"at %s [in module %s]"),
sect_offset_str (sect_off), sect_offset_str (src_die->sect_off), // First parameter is dereferenced here
objfile_name (cu->per_cu->dwarf2_per_objfile->objfile));
...
In the example above a NULL
pointer value is passed to
the follow_die_ref
function as its first parameter
src_die
and this parameter is dereferenced if the value of
an unrelated die
local variable is not equal to
NULL
.
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 {
a?.let { return a.value + 2 }
b?.let { return b.value + 2 }
throw 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 it is logically impossible for the condition under which the pointer is dereferenced in the called function to become true in the context of the call where a null value is assigned to that pointer. The checker emits a warning even if this possibility isn’t ruled out which leads to false positive reports in such cases.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Go | Quality | Major | Average | Yes |
Related CWEs: CWE476.
This checker detects issues where a null value is passed as a pointer argument to a function, where it is dereferenced under some condition and analysis is able to prove that this condition can be true when the pointer is null.
#include <stddef.h>
extern void use(int);
void deref_if(int *p, int x) {
if (x > 1) {
x -= *p;
}
use(x);
}
void example(int x) {
deref_if(NULL, x);
}
void example_fixed(int x) {
deref_if(NULL, x > 1 ? 1 : x);
}
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 potential null
pointer dereferences that are less reliable than those found by the
DEREF_OF_NULL checker. For example, this
warning type is reported in Java code when an object with a
nullable
annotation is referenced without a null check. For
Kotlin it is reported when a nullable type object is referenced using
the non-null assertion operator !!
also without a null
check.
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 |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
Go | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Python | Quality | Normal | Unknown | Yes |
This checker finds situations where a pointer returned by a user-written function that returns a null value on some execution paths, is dereferenced without a check for being null.
If instead of a direct
reference an assert-like function such as Java’s
Objects.requireNonNull
or Kotlin’s not-null assertion
operator !!
is used a DEREF_OF_NULL.RET.ASSERT
warning subtype is reported.
If instead of a direct
reference a potentially null value is used in a call as an argument and
corresponding parameter has a @NotNull
or
@NonNull
annotation then a
DEREF_OF_NULL.RET.ANNOT
warning subtype is reported.
#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; // Null value may be returned
return fill_from_bd(buf, MAX_SIZE);
}
void use(const char* url) {
char* login = get_login(url, USE_PREFIX);
int len = strlen(login); // FLAW: potentially null value is used without a check
// ...
}
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: variable
num
value may be null after a getMaybeNull
call and it is dereferenced by the call of a user defined function
deref
. Function possibleFix
illustrates a
possible fix: use safe implementation derefCorrect
. Safe
call of the times
method is used inside the
derefCorrect
function.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
Visual Basic .NET | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
This checker finds situations where a pointer returned by a library function is dereferenced without being checked for a null value.
Following is a partial list of library functions that are recognized by this checker as being able to return a null value for reasons not under user’s control:
Examples from the C standard library:
fdopen
,fopen
,popen
,getenv
,opendir
,dlopen
,dlsym
,freopen
,tempnam
,tmpfile
,tmpnam
,ptsname
,getutent
,getutid
,getutline
,pututline
,getcwd
,getwd
.Examples from the 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: if the
RANDFILE
environment variable is not set the
getenv
function returns a NULL
value which is
dereferenced by accessing the first character of the returned string
s
.
Function possible_fix
illustrates a possible fix: check
the result of the getenv
call for being NULL
before accessing the first character of the returned string
s
.
import java.io.File
fun example(f: File) = f.parentFile.listFiles()
fun possibleFix(f: File) = f.parentFile?.listFiles()
Function example
illustrates the defect: getter of a
parentFile
property may return null, which is referenced by
a listFiles
call.
Function possibleFix
illustrates a possible fix: use a
safe call of the listFiles
function.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Average | Yes |
C# | Quality | Normal | Average | Yes |
This is a subtype of DEREF_OF_NULL.RET.LIB warning, and it is reported when a pointer returned by a library function may have null value and is dereferenced not explicitly, but within a function call.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | Yes |
C/C++ | Quality | Minor | Unknown | Yes |
Kotlin | Quality | Minor | Unknown | Yes |
This is a subtype of a DEREF_OF_NULL.RET.LIB warning type that is reported when a pointer returned by a library function may have null value, but this might happen in quite untypical usage conditions, so can be considered as an issue of minor probability.
Examples from the C standard library:
localtime
,Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | No |
This C/C++-specific checker finds situations where a pointer returned
by a memory allocation function that returns a NULL
value
in error cases, such as malloc
, is dereferenced without
being checked for a NULL
value.
Note: when the
amount of allocated memory is a small constant, the probability of error
cases becomes much lower and these situations are reported with a
DEREF_OF_NULL.RET.ALLOC.MINOR
warning subtype. The
threshold can be set by a
DEREF_OF_NULL_RET_ALLOC.SMALL_ARGUMENT
configuration option and has a default value of
5000.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Go | Quality | Major | High | Yes |
Kotlin | Quality | Major | High | Yes |
Related CWEs: CWE476.
This checker is a statistical version of DEREF_OF_NULL.RET that also reports when a potentially null pointer returned by a function is dereferenced without checking for being null. A report of this warning type is emitted when a pointer returned by a call to a function is dereferenced without a null check 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
function get_ptr
is called, the returned value is checked
for not being NULL
before its dereference, except for
variable p5
.
Possible fix is to uncomment the commented if
operator,
which checks the value of the p5
variable before its
dereference too.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | High | Yes |
Related CWEs: CWE476.
This checker reports an issue when a pointer value is the result of a
dynamic_cast
C++ operator, it may 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 *b = dynamic_cast<B*>(a);
return b->b();
}
int example_fixed(A *a) {
B *b = dynamic_cast<B*>(a);
if (!b)
return -1;
return b->b();
}
Function example
illustrates the defect: pointer
b
is the result of a 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 Java/Kotlin-specific checker reports an issue when a null value
is used in a call as an argument and corresponding parameter has a
@NotNull
or @NonNull
annotation.
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 as an argument to the method test_helper
call whose
parameter has a NotNull
annotation. In the
else
clause of the same if
statement, where
the value of variable s
is definitely not
null
, using it as an argument of the call to the same
test_helper
method doesn’t cause this warning to be
reported.
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 | Very high | No |
Kotlin | Quality | Minor | Very high | 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)
s = "string";
test_helper(s); // DEREF_OF_NULL.ANNOT.ASSIGN
}
public NotNullAnnot(boolean cond) {
if (cond)
str = "string";
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)
s = (String) o;
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)
s = (String) o;
else
b = false;
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 |
Visual Basic .NET | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | High | Yes |
Go | Quality | Critical | High | Yes |
Kotlin | Quality | Normal | High | Yes |
Python | 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) DEREF_AFTER_NULL.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.
A less reliable DEREF_AFTER_NULL.RET
subtype of this warning type is emitted if the checked for null pointer
value is returned from a function before the dereference.
C# warnings can have one
of these subtypes: .ARGUMENT and .ARGUMENT.INSTANT. There is no DEREF_AFTER_NULL.EX for C# because
DEREF_AFTER_NULL
is already path-sensitive.
int test(char* str1, char* str2, int len) {
if (!str1) {
len = 0;
} else {
len = strlen(str1);
}
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
println("Client '${arg!!.name}' is $age years old")
}
fun possibleFix(arg: Client?) {
val age = arg?.age
age?.let{ println("Client '${arg.name}' is $it years old") }
}
Function example
illustrates the defect: Variable
age
is initialized from the arg
parameter
using a safe call operator, handling the case when the parameter is
null, but afterwards the arg
parameter is again
dereferenced using a not-null assertion operator to get the field
name
value without a null check this time.
Function possibleFix
illustrates a possible fix by
ensuring non-null value of parameter arg
before getting the
value of its filed name
using a scope function
let
.
Sometimes it is reasonable to handle null dereference issues in JVM
languages by catching NullPointerException
. If null
dereference issue takes place inside a try
-block that
handles NullPointerException
(or any parent exception),
then warning is not emitted.
public class CatchNpe {
public static void test(Object x) {
if (x == null) {
System.out.println("NULL");
}
try {
x.toString(); // DEREF_AFTER_NULL is not emitted
} catch (NullPointerException e) {
// ...
}
}
}
Also, you can use configuration variable
NULL_DEREFERENCE.TREAT_THROWS_NPE_AS_PROPER_HANDLING
to
treat throws NullPointerException
(or any parent exception)
in method’s signature as proper null dereference handling. If this
variable is set to true, warnings in those methods won’t be emitted. By
default, variable is set to false and can be changed using svace config tool.
// > svace config NULL_DEREFERENCE.TREAT_THROWS_NPE_AS_PROPER_HANDLING true
public class ThrowsNpe {
public static void test(Object x) throws NullPointerException {
if (x == null) {
System.out.println("NULL");
}
x.toString(); // DEREF_AFTER_NULL is not emitted
}
}
A common cause of this checker false positives is when the execution paths taken when a variable is null contain program termination before its dereference but the analyzer doesn’t have enough data to detect it. For example if calling external functions that abort the program:
If instead of an unknown my_exit
, a standard program
termination function exit
is called, no false warnings is
emitted.
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 |
Python | 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 produces a warning when a variable can be dereferenced after comparison against a null value and there exist a combination of the input parameters and an execution path where both of these events can happen.
import java.text.DateFormat
import java.util.*
fun example(date: String?): Date {
date?.let{ println("parsing: '$date'") }
val df = DateFormat.getDateInstance(DateFormat.LONG, Locale.KOREA)
return df.parse(date)
}
fun possibleFix(date: String): Date {
println("parsing: '$date'")
val 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 onward 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 | No |
C/C++ | Quality | Major | High | No |
This checker detects issues where a pointer or an object reference is compared against a null value (which indicates that it may be null) and then passed to a function as a parameter, where it is conditionally dereferenced.
Note: the checker does not use smt-solver. It uses heuristics to define, where function may dereference passed null pointer.
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;
}
deref_under_some_condition(p);
}
void example_fixed(int *p) {
if (p) {
*p = 123;
}
if (p) {
deref_under_some_condition(p);
}
}
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 lack or miss information that the condition of the dereference within the called function can’t be true in the context where the function is called, in which case the null dereference can actually never happen during this call but the null check still may be useful for calls from the other contexts.
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 or an object reference is compared against a null value (which indicates that it may be null) and then passed to a function as a parameter, where it is conditionally dereferenced.
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;
}
deref_if_x_is_positive(p, x);
}
void example_fixed(int *p, int x) {
if (p) {
*p = 123;
}
if (p) {
deref_if_x_is_positive(p, x);
}
}
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 lack or miss information that the condition of the dereference within the called function can’t be true in the context where the function is called, in which case the null dereference can actually never happen during this call but the null check still may be useful for calls from the other contexts.
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 or an object reference is compared against a null value (which indicates that it may be null) inside of a loop condition and then it is dereferenced after the loop.
struct Item {
int id;
int value;
struct Item *next;
};
int example(Item *start, int id) {
Item *item = start;
while (item && item->id != id) {
item = item->next;
}
return item->value;
}
int example_fixed(Item *start, int id) {
Item *item = start;
while (item && item->id != id) {
item = item->next;
}
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.
Occasionally when loop condition is composed of several subconditions the assumption that the comparison implies a null value might not be correct for the path where the warning is reported due to some additional implicit preconditions.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Suppressed | Major | Average | Yes |
C/C++ | Suppressed | Major | Average | Yes |
Go | Suppressed | Major | Average | Yes |
Related CWEs: CWE476.
This checker detects issues where a pointer or an object reference is compared against a null value (which indicates that it may be null), and then dereferenced on a path where the program execution might be terminated, but the analysis is not able to ensure whether it avoids null 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) {
exit(1);
}
}
void example(int *p) {
if (!p) {
handle_error(313);
}
*p = -1;
}
void example_fixed(int *p) {
if (!p) {
handle_error(313);
} 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 |
Visual Basic .NET | Quality | Major | Average | Yes |
C/C++ | Quality | Major | High | Yes |
Kotlin | Quality | Normal | High | Yes |
Python | Quality | Normal | High | Yes |
C# | Quality | Major | Average | Yes |
The checker finds situations where first, a pointer or an object reference is dereferenced, and then it is compared against a null value (which indicates that it may be null). This means that either the null check is too late and the potential null dereference has already happened or it’s not needed at all since it’s always false.
A (suppressed) NULL_AFTER_DEREF.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 ranges of index variables.
struct bank {
char* name;
char* pool;
};
void test(struct bank* c, char* name) {
strcpy(name, "<Global>");
if (name)
c->name = pstrdup(c->pool, name);
}
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)
date?.let{ println("'$date' has been parsed") } // svace: emitted NULL_AFTER_DEREF
return ret.after(Date(2020, 12, 10))
}
fun possibleFix(date: String?): Boolean {
val ret = date?.let {
val df = DateFormat.getDateInstance(DateFormat.LONG, Locale.KOREA)
df.parse(date).also { println("'$date' has been parsed") }
}
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 location in the warning report points to a function that returns the pointer. Such warnings reflect an observation that the function that produced the pointer unconditionally dereferences it before the return and so the returned pointer can’t be null. This means that the null check in the callee is redundant. In most cases this happens due to incorrect assumptions about the called function return value (e.g. the check is added “just in case”) but at the same time there are various situations when such check can be beneficial and shouldn’t be removed.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Minor | Average | No |
C/C++ | CodingStyle | Minor | Average | No |
The checker finds situations where first, a pointer or an object reference is obtained as a result of a function call, and then is compared against a null value (which indicates that it is considered to possibly be null), but it has already been dereferenced in the function that produced it. This means that the null check in the callee is redundant. In most cases this happens due to incorrect assumptions about the called function return value (e.g. the check is added “just in case”) but at the same time there are various situations when such check can be beneficial and shouldn’t be removed.
char* get_buf(void) {
char* buf = (char*) malloc(100);
buf[0] = 'q';
return buf;
}
void foo(void) {
char* res = get_buf();
if (res) // NULL_AFTER_DEREF.RET: `res` can't be NULL.
res[1] = 'w';
}
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.
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.
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 in C/C++ can’t be null comparing it a with a 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");
size = strtoul(env, NULL, 10);
// Allocating a buffer of unbounded number of bytes.
res = (char*) malloc(size);
}
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) {
count = atoi(str);
} else {
count = -1;
}
usleep(count);
}
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
}
allocSlice(size)
}
func allocSlice(size int)[]string {
var s = make([]string, size)
return s
}
func main() {
sizeArg := flag.String("size", "0", "")
modeArg := flag.String("size", "0", "")
flag.Parse()
size, _ := strconv.Atoi(*sizeArg)
mode, _ := strconv.Atoi(*modeArg)
allocIf(size, mode) //TAINTED_INT.COND
}
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 paths, and it is uses also only on some paths.
package main
import (
"flag"
"strconv"
)
func allocIf(size, mode int) {
if mode > 10 {
return
}
allocSlice(size)
}
func allocSlice(size int)[]string {
var s = make([]string, size)
return s
}
func main() {
sizeArg := flag.String("size", "0", "")
modeArg := flag.String("size", "0", "")
flag.Parse()
size, _ := strconv.Atoi(*sizeArg)
mode, _ := strconv.Atoi(*modeArg)
if mode < 20 {
//now for some pathes size is not controlled by user
size = 10
}
allocIf(size, mode) //TAINTED_INT.MIGHT.COND
}
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() {
scanf("%d", &val);
if (isdigit(val)) // Here the program may crash; TAINTED_INT.CTYPE is emitted.
ch = (char) val;
}
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) {
scanf("%d", &val);
}
if (isdigit(val)) // Here the program may crash if val < 0; TAINTED_INT.CTYPE.MIGHT is emitted.
ch = (char) val;
}
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");
size = strtoul(env, NULL, 10);
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");
size = strtoul(env, NULL, 10);
} else {
size = 0;
}
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).
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) {
step = getchar();
} else {
step = 1;
}
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!
buf[index] = 7;
}
}
fun example(number: String) {
val num: Int = number.toInt()
val x: IntArray = intArrayOf(1, 2, 3)
if (num < 3) {
print(x[num])
}
}
fun possibleFix(number: String) {
val num: Int = number.toInt()
val x: IntArray = intArrayOf(1, 2, 3)
if (num >=0 && num < 3) {
print(x[num])
}
}
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 |
Python | 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.
#include <cstdio>
void array_index(bool flag) {
int buf[256];
int index = getchar();
if (flag && index < 256) {
// Index may still be negative!
buf[index] = 7;
}
}
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 |
Python | Quality | Critical | Average | Yes |
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 difficult 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
buf[index] = 7;
}
}
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
buf[index] = 7;
}
}
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);
base[size] = '\0'; // Accessing an array through pointer `base` by tainted index `size`.
}
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) {
base[size] = '\0'; // Accessing an array through pointer `base` by tainted index `size`.
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Critical | Very high | Yes |
C/C++ | Quality | Critical | Very high | Yes |
Scala | Quality | Critical | Very high | Yes |
Go | Quality | Critical | Very high | Yes |
Python | Quality | Critical | Very high | 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() {
env = getenv("VAR3");
// Copying a string of unbounded size to a fixed-size buffer.
strcpy(buf, env);
}
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) {
env = getenv("VAR3");
} else {
env = "NONE";
}
// Copying a string of unbounded size to a fixed-size buffer.
strcpy(buf, env);
}
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 paths.
const char* env;
char buf[100];
#define COPY_MODE 10
void copyIfNeeded(const char*ptr, int mode) {
if (mode == COPY_MODE)
strcpy(buf, ptr);
}
char* getEnv() {
return getenv("CODE10");
}
void test(int mode) {
env = getEnv();
// Copying a string of unbounded size to a fixed-size buffer.
copyIfNeeded(env, mode);
}
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)
strcpy(buf, ptr);
}
char* getEnv() {
return getenv("CODE10");
}
void test(int mode) {
env = getEnv();
if (mode<0)
env = "-";
copyIfNeeded(env, mode);
}
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.
size_t size;
char dst[5];
void test() {
char* env = getenv("QQQ");
size = strtoul(env, NULL, 10);
sprintf(dst, "%d", size);
}
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.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
Related CWEs: CWE565.
This checker finds situations where value of a web cookie is used in a condition, thus controlling the execution flow of the application. Cookie values can be easily stolen or modified by malicious users, therefore using them to control execution flow makes application vulnerable to cookie spoofing attack.
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
class Example {
public boolean login(
@RequestParam String username,
@RequestParam String password,
@CookieValue(value = COOKIE_NAME, required = false) String cookieValue) {
if (StringUtils.isEmpty(cookieValue)) {
return credentialsLoginFlow(username, password, response);
} else {
return cookieLoginFlow(cookieValue);
}
}
}
In function login
, parameter cookieValue
is
bound to a value of web cookie via @CookieValue
annotation.
This parameter is used to direct user-authorization flow via emptiness
test. Thus, if the user manipulates the value of this cookie, he can
bypass authorization and gain control over application and the data it
uses.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
Related CWEs: CWE918.
This checker finds situations where a web request parameter is used as URL (or a part thereof) for accessing an external resource, thus creating a possibility of server-side request forgery attack.
import java.util.List;
import java.util.ArrayList;
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.web.bind.annotation.RequestParam;
class Example {
List<String> links(@RequestParam String url) throws IOException {
List<String> result = new ArrayList<String>();
Document doc = Jsoup.connect(url).get();
Elements links = doc.select("a");
for (Element link : links) {
result.add(link.absUrl("href"));
}
return result;
}
}
In function links
, parameter url
is bound
to web request parameter via @RequestParam
annotation. This
parameter is used as URL to fetch a remote document, which is parsed and
used to construct the function’s return value. As web request parameters
are controlled by the user, the latter can exploit this function to
import unexpected data into application and use the outcome to his
advantage.
Buffer overflow occurs when a program writes more data to a buffer (a temporary storage area in memory) than it can hold. This can lead to the excess data overwriting adjacent memory locations, potentially corrupting or altering the data stored there.
Attackers can exploit this vulnerability by deliberately crafting input data that exceeds the buffer’s capacity, allowing them to overwrite critical data, manipulate program behavior, or even execute arbitrary code. This can lead to a range of security issues, including crashes, denial of service attacks, and remote code execution.
To mitigate buffer overflow vulnerabilities, developers should use secure coding practices such as input validation and bounds checking.
In memory-safe programming languages, such as Java, the corruption is prevented by built-in memory management and bounds checking. However, if a program tries to access an element in an array, list, or other data structure using an index that is outside the valid range of indices for that data structure, a runtime exception is thrown, which can lead to unexpected changes in program behaviour including denial of service.
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.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Go | Quality | Critical | High | Yes |
Python | 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.
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
fscanf(f, "%d", &n);
// Pointer 'ptr' created using tainted data
char* ptr = malloc(n);
// Buffer access may lead to buffer overflow
ptr[30] = 0;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Very low | Yes |
C/C++ | Quality | Critical | Very low | Yes |
Python | Quality | Critical | Very low | 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(int a) {
char *buf;
if (a > 0)
buf = (char *) malloc(1024);
else
buf = (char *) malloc(512);
if (a > 0)
buf[1024] = 0;
else
buf[511] = 0;
free(buf);
}
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];
buf[index] = 7;
}
void run() {
int i;
if (i >= 10)
access_buf(i);
}
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 | Very low | No |
C/C++ | Quality | Critical | Very low | No |
This checker finds buffer overflow situations where a buffer is passed into another procedure before being accessed (with potential overflow).
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)
{
buf[10]=0;
}
void test() {
int* buff = new int[10];
access_buf(buff); // Overflow.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Very high | 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.
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];
fscanf(f, "%49s", buf); // `buf` can't overflow: bounds specified in format string.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
Visual Basic .NET | Quality | Major | Unknown | Yes |
C/C++ | Quality | Critical | Unknown | No |
C# | Quality | Major | Unknown | Yes |
Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE127, CWE194, CWE195.
For C/C++ this checker detects usages of memcpy
,
strcpy
and similar standard library functions that may
write beyond the end of destination buffer.
For C# this checker detects potential overflows of allocated unmanaged memory buffers on certain execution paths. It reports a warning when a buffer may be accessed (read or written) beyond its right bound. The checker reports a warning if the allocation expression is known and it can’t prove that the access is safe.
void buffer_overflow(char*p, int n) {
int buf[10];
// Last copied byte will be written outside of buf
memcpy(p, buf + 3, sizeof(int) * 7 + 1);
}
In the example above memcpy will write one byte after the end of
array buf
, corrupting data on stack.
using System;
using System.Runtime.InteropServices;
IntPtr hglobal = Marshal.AllocHGlobal(5); // ℹ️〔function System.Runtime.InteropServices.Marshal.AllocHGlobal(int) allocated buffer of size 5〕
int[] buffer = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Marshal.Copy(buffer, 0, hglobal, 1);
Marshal.Copy(buffer, 0, hglobal, 2); // ⚠️〔BUFFER_OVERFLOW Writing 2 elements of type int into buffer hglobal can exceed its size (5 bytes)〕
Marshal.FreeHGlobal(hglobal);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | No |
Python | Quality | Critical | Average | Yes |
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 a buffer overflow when return value of
strlen
, read
or similar functions used in
buffer access offset calculation is subtracted from, resulting in
possible negative offset:
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 only a
\0
character (and thus is of zero length) an access outside
of the 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) {
n = cnt;
} else {
n = sizeof(other_buf)-1;
}
// writing in buf instead of other_buf for which the size was calculated
memcpy(buf, msg, n);
}
In this example amount of copied data n
is calculated
for other_buf
and may be equal to 99 on either of the
branches if cnt
is sufficiently large. This will result in
out of bounds writes to buf
and will 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 an illegal argument
value. Less reliable reports are issued with a BUFFER_OVERFLOW.PROC.STRICT
warning subtype.
void fill(char * p, int len) {
for (int i = 0; i < len; i++) {
// Buffer overflow occurs if len is bigger than size of buffer
p[i] = 'a';
}
}
int overflow() {
char bufferInput[50];
test(bufferInput, 1000); // This call is bad
test(bufferInput, 30); // No warning here
return 0;
}
In this example function fill
may write beyond the end
of the provided array if the value of variable len
is
larger than the actual size of the buffer.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | Yes |
Related CWEs: CWE120, CWE121, CWE122, CWE124, CWE134, CWE20.
This checker detects buffer overflows when using sprintf
and snprintf
function without limiting %s
conversion specification maximum number of characters to be printed:
For sprintf
function checker detects usages of
%s
conversion without a precision modifier, which may lead
to a buffer overflow if the corresponding argument is a sufficiently
long string.
void with_sprintf(char* str) {
char buf[10];
sprintf(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
}
For snprintf
function the checker additionally takes
into account the function argument limiting the size of the output
buffer.
void with_sprintf(char* str) {
char buf[10];
snprintf(buf, 20, "%s", str); // possible overflow if str is longer than 10
snprintf(buf, 10, "%s", str); // everything is fine
}
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 potentially can have arbitrary large value.
void index(int i) {
int buf[21];
buf[i] = 6; // overflow, non-constant index i accesses buffer of constant size
if(i<15) {
buf[i] = 6; // no warning, because value was checked
}
}
void with_strlen(char *s) {
int a = strlen(s);
char buf[100];
buf[a] = 0; // overflow if string is sufficiently large
}
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
strcpy(a, b); // overflow, copying buffer of size 8 to buffer of size 5
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Critical | Unknown | Yes |
Go | Quality | Critical | Unknown | No |
Python | Quality | Critical | Unknown | Yes |
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)
{
len = x++ / 2;
for (j = 0; j < len; j++)
a[i+j] &= 0xfffffffe;
// buffer underflow happens on 1st iteration if (x == 0)
a[i+(j-1)] |= 1;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Python | Quality | Major | Average | Yes |
Related CWEs: CWE119, CWE124, CWE129, CWE394.
The checker CHECK_AFTER_OVERFLOW
finds situations where
first, an array element with a certain index is accessed, and then this
index is compared to a value that indicates that the index may lie
outside the allowed range.
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) CHECK_AFTER_OVERFLOW.MACRO
subtype of this warning type 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 ranges of index variables.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | No |
The checker finds situations were the array length (obtained using
built-in function len()
) is compared to 0
after some element of the same array has been already accessed. Such
situation can potentially lead to an out of range access and an
unexpected run-time panic.
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 {
fmt.Println(x)
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Python | 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 accepted by the
check lies out of bounds.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Critical | High | Yes |
Go | Quality | Critical | High | No |
Python | Quality | Critical | High | Yes |
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++) {
array[i] = q[i]; // `i` may be 10, but the last index is 9.
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Critical | Unknown | No |
This checker detects access to buffer that contradicts earlier
conditions using len()
.
In this example if array can have length 10, which is indicated by
if
condition. However 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.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Critical | Average | Yes |
Go | Quality | Critical | Average | Yes |
Python | 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 an array element is compared 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 large or too small, but if the bound is inconsistent with the array size, a buffer overflow can still occur. For example, if an array 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.
A less reliable warning subtype OVERFLOW_UNDER_CHECK.COND
is reported when the analyzer detects a bound condition on the value of
a variable used for computation of an element access index of an array
of unknown size but not on the resulting value of the index itself.
int buf[10];
if (i < 20)
buf[i] = 3; // Possible buffer overflow.
if (i >= -1)
buf[i] = 5; // Possible buffer overflow.
for (i = 0; i < 100; ++i)
buf[i] = 7; // Possible buffer overflow.
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.
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;
memcpy(buf, p, len); // Error.
}
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];
array[index] = 0;
}
void func(int index) {
if (index < 200)
access(index); // Error.
}
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).
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];
strncpy(buf, src, sizeof(buf)); // buf may be not null-terminated
strncpy(dst, buf, sizeof(dst)); // overflow of buf will happen in this case
}
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) {
memcpy_s(dst, size, src, strlen(src));
}
void test_good(char* src) {
memcpy_s(dst, 10, src, strlen(src));
}
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);
memcpy_s(dst, size, src, strlen(src));
}
void test_good(char* src) {
char* dst = malloc(10);
memcpy_s(dst, 10, src, strlen(src));
}
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).
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;
ptr = (Person *) malloc(sizeof(ptr)); // Mismatched allocation size.
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.
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 | Unknown | Yes |
This checker issues a report when a bitwise initialization function
(memset
, memcpy
or memmove
) is
applied to a type that is not trivially copyable.
(BITINIT_FOR_NON_TRIVIALLY_COPYABLE/example.cpp)
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Very high | Yes |
Related CWEs: CWE119, CWE121, CWE124, CWE170.
This checker finds situations where a standard C library function
readlink
is used in an unsafe way. This function receives a
buffer start address and its size as arguments and returns the number of
bytes placed in the buffer. Since it doesn’t append a terminating null
character to the buffer the callee usually does this to create a string.
Doing so in a straightforward way by assigning a null value to the
buffer’s byte with an index equal to the returned value may lead to an
“off-by-one” buffer overflow in case the readlink
function
returns a value equal to the 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.
buf[len] = 0; // Emitted READLINK_OVERFLOW.
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This C-specific checker finds potential buffer overflows due to
calling POSIX standard functions to receive messages from sockets
recv
and recvfrom
with mismatched parameters
specifying the start address of a buffer, where the message is to be
stored, and its size.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | High | Yes |
The checker NONTERMINATED_STRING
finds situations where
“safe” versions of standard library functions working with C strings may
create non-null-terminated strings as a result. Working with such
strings as with regular ones, e.g. passing them as standard library
functions arguments, can lead to out-of-bounds memory access.
In this example even though buffer overflow won’t occur directly
executing the following code, it may create a non-null-terminated
string, which may lead to buffer overflow later, when the string is
accessed again. For example, if buffer
is a 300-character
string, the strncpy
call copies 256 bytes to the
name
field (which is declared as
char name [256]
), but there is no space left for a null
terminator.
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 isn’t null-terminated.
When string characters are copied using memcpy
or
related functions instead of strcpy
or other string-related
functions a subtype NONTERMINATED_STRING.STRICT.MINOR
is emitted. While such functions can still be used for working with
strings they are more commonly used to process other data that shouldn’t
be null-terminated.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | High | Yes |
The checker finds patterns when the result of a strlen
standard C library function call is used in a memcpy
function call as second parameter representing the number of copied
bytes. Since the strlen
function doesn’t count the null
terminator and the memcpy
function doesn’t add it to the
end of the resulting string, this pattern results in potential creation
of a non-null-terminated string. Using such string as a regular string,
e.g. in calls to standard library string functions, can lead to
out-of-bounds memory access.
In the following example the dst
buffer is filled with
the contents of the prog->String
string but without
adding the null terminator at the end of the copied characters so if
dst
isn’t zero-filled originally the resulting string will
be non-null-terminated:
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 may create non-null-terminated strings as a result of using length parameter coming from untrusted source. Working with such strings as with regular ones, e.g. passing them as standard library functions arguments, can lead to out-of-bounds memory access.
In the following example local buffer keys_path
(with
size 4096 bytes) is filled by a strncpy
function call using
the value of an external environment variable
ADB_VENDOR_KEYS
as source data. If all 4096 characters are
copied then no space remains for a null terminator and when this
variable is later used as a string a buffer overflow may happen:
adb_keys_path = getenv("ADB_VENDOR_KEYS");
if (!adb_keys_path)
return;
strncpy(keys_path, adb_keys_path, sizeof(keys_path));
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very high | 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.
In this example the command
buffer has only space for 99
characters (and a null terminator) but is filled without checking for an
overflow with the help of standard strcpy
and
strcat
functions using file paths that can be much
longer:
char command[100];
EST_String file1, file2;
file1 = make_tmp_filename();
file2 = make_tmp_filename();
...
strcpy(command, "cat ");
strcat(command, file1);
strcat(command, " | sed -f ");
strcat(command, sedfile);
strcat(command, " > ");
strcat(command, file2);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very high | No |
Related CWEs: CWE120, CWE121, CWE122.
This checker is a variant of STRING_OVERFLOW for potential buffer
overflows caused by calling of a string copying function when the the
source string is larger than the destination buffer, and when the source
string is a parameter of a function being analyzed. In this case the
function has (possibly implicit) contract that the source string should
not be larger than the destination buffer and the function caller is
responsible for making sure to fulfil it which can be hard to achieve.
To avoid such situations it’s better to check for valid source string
length inside of the function, e.g. by using the strncpy
string copying function with a string length constraint.
void error_tag(char error_string[], int error_number) {
if (error_number != 0) {
char error_buffer[100];
strcpy(error_buffer, error_string); // FLAW: copying to fixed-size buffer without
// checking source string length
if (error_number > 4) {
strcpy(error_string, "Error ");
} else {
strcpy(error_string, "Warning ");
}
strcat(error_string, error_buffer);
}
}
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;
s->x = 1; // Potential overflow if len < sizeof(struct S).
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Minor | Unknown | No |
This C-specific checker reports potential buffer overflows when
writing strings using binary output functions fwrite
and
write
which don’t check for the null terminator. It is
based on the FIO18-C
rule from the CERT Secure Coding Standard.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Low | No |
This C-specific checker reports non-portable initialization of a
structure field using standard memcpy
function where the
start address of the field is calculated manually using
sizeof
s of the preceding structure fields instead of the
portable offsetof
macro.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Minor | Unknown | No |
Related CWEs: CWE785.
Incorrect usage of the standard C library function
realpath
to get a canonicalized absolute pathname may cause
buffer overflow vulnerability. The POSIX.1-2001 standard version of
realpath
library function is broken by design, since it is
impossible to determine a suitable size for the output buffer. According
to POSIX.1-2001 a buffer of size PATH_MAX
suffices, but
PATH_MAX
need not be a defined constant, and may have to be
obtained using a pathconf
call. And asking
pathconf
does not help, since, on the one hand POSIX warns
that the result of pathconf
may be huge and unsuitable for
mallocing memory, and on the other hand pathconf
may return
-1
to signify that PATH_MAX
is not bounded.
POSIX.1-2008 allows passing a NULL
pointer as a second
argument of realpath
, allowing this design problem to be
avoided. This is the recommended secure way of using
realpath
.
This section contains detectors for various types of defects primarily related to manual management of memory and other resources. In the case of memory, such errors can lead to its corrupted state, which may go unnoticed for a long time and cause unexpected behavior of the program, or even a crash when finally detected by the operating system.
One of the most common examples of such
issues is when an invalid address is passed to a memory deallocation
function such as standard C library free
. Such functions
expect only exactly those addresses that are returned by the respective
memory allocation functions. Even if the address passed as an argument
belongs to the allocated memory block it is still an invalid parameter
value for a memory deallocation function and using it this way leads to
an undefined behaviour that commonly manifests in abnormal program
termination.
In other cases, if the program does not release the resources it has acquired, including memory, file handles, database connections, etc., after their use, it can lead to exhaustion of the resources available to the program or the entire system, which in turn can result in a slowdown of its performance or denial of service. Such issues are commonly called “resource leaks” or “memory leaks” in case of memory.
Another large group of
detectors are related to situations where a program continues to use a
resource (such as memory, file handle, or other) after it has been
released or deallocated. This can lead to undefined behavior, crashes,
or security vulnerabilities. Sometimes the cause of such problems lie in
logical errors in the application, and sometimes in the programmer’s
incorrect understanding of how resource deallocation functions work. For
example, the memory deallocation operator delete
in C++
does not reset the pointer value — it remains the same, but after
executing this operator it cannot be dereferenced; doing so leads to
undefined behavior.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE590.
This checker finds situation where an address of a non-heap memory block is passed to a memory deallocation function, where only addresses to heap memory, returned by memory allocation functions, are allowed. Trying to do so usually results in operating system terminating the program prematurely.
static int stats_file_history_update(struct stats_file *data_file)
{
struct stats_file _history_file, *history_file;
history_file = &_history_file;
...
err = stats_open_temp(temp_file);
if (err < 0) {
stats_free(history_file); // FLAW: freeing of non-heap memory
return err;
}
...
static void stats_free(gpointer user_data)
{
struct stats_file *file = user_data;
if (!file)
return;
...
g_free(file);
}
In the example above the local pointer history_file
is
assigned an address of a local structure variable
_history_file
, allocated on stack. In the subsequent code
an error situation, represented by a negative value returned by a
stats_open_temp
function call, is handled by calling a
stats_free
function with the value of pointer
history_file
as an argument. The stats_free
function in turn calls a g_free
function in the end which
deallocates the memory which address is passed as its parameter. This
way an address of a memory block allocated on stack is passed to the
memory deallocation function which can lead to abnormal program
termination.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Very low | Yes |
This path-sensitive version of checker FREE_NONHEAP_MEMORY finds situations where there is a reachable path from initialization of a pointer with an address of a non-heap memory block to a memory deallocation function call where this pointer value is used. When execution runs along this path this call may result in abnormal program termination since such values are invalid for memory deallocation functions working only with heap addresses.
void cond_free(int *p, int cond) {
if (cond) {
free(p);
}
}
void free_nonheap_ex(int x) {
int local_array[] = {2, 3};
cond_free(local_array, x); // FLAW: potential free of non-heap memory
}
In the example above when function free_nonheap_ex
is
called with a parameter x
value other than 0
,
address of a local array local_array
which is passed as the
first argument of the cond_free
function call is in turn
passed as an argument to the memory deallocation function
free
. Such address on the stack isn’t valid for the
free
function and this call causes undefined behaviour
usually manifesting in abnormal program termination.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very low | No |
This checker finds situation where a pointer passed to a memory deallocation function was obtained as a result of an arithmetic operation. This can result in an invalid value for a memory deallocation function. Though sometimes the allocated memory address is shifted by a fixed offset intentionally and the correct address of its beginning could be reconstructed using reverse arithmetic operations, this is potentially a cause for incorrect values, passing which to memory deallocation functions as arguments may lead to undefined behaviour that commonly manifests in abnormal program termination.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very low | No |
This checker finds suspicious pointer arithmetic on the result of a memory allocation function call. While semantically such code is correct this is a common typo when because of a misplaced parenthesis a value is added or subtracted from the allocated memory address instead of the allocation size argument, as in the example below. Such typo results in both incorrect size of the allocated memory block which later can lead to a buffer overflow, when this memory is used, and also to an invalid address to the allocated memory block start that can’t be passed to the memory deallocation function.
// Allocate memory for the DMA buffers
if((dma_buffer1 = (char *)JMALLOC(DMA_BUFFER_SIZE * 2) + 32) == (char *)0)
{
fprintf( stderr,"Can not allocate memory for DMA buffers\n");
exit(0);
}
In this example because of a misplaced closing parenthesis the
32
constant is incorrectly added to the return value of the
JMALLOC
memory allocation function instead of to the value
of the DMA_BUFFER_SIZE * 2
expression, which represents the
amount of memory to be allocated.
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. Such typo results in both incorrect size of the allocated memory block which later can lead to a buffer overflow, when this memory is used, and also in an invalid address to the allocated memory block start that can’t be passed to the memory deallocation function.
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 standard library function free
. While it is
not an issue in itself, such code is useless as in this case no
operation is performed, and it might mean a problem with the program
logic such as deallocating a wrong pointer, misplacing the deallocation
function call or other.
if((*root)->right == NULL && (*root)->left == NULL){
*root = NULL;
free(*root); // FLAW: free of NULL
}
In this example the code author accidentally placed the assignment of
a NULL
value to the *root
pointer before
calling the free(*root)
function. This mistake leads to not
only calling of the free
function with a NULL
value as an argument which produces no operation but also to not
deallocating the memory that is no longer used, i.e. a memory leak.
cdata = eina_hash_find(item_custom_map, &obj);
if (NULL == cdata) {
eina_hash_del(item_custom_map, &obj, cdata);
free(cdata); // FLAW: useless free of NULL
}
In this example the free
is called with the value of the
cdata
variable only if it has a null value, checked in the
condition of the if
statement. This is a useless call that
produces no operation in this case and is caused by an accidentally
inverted condition of the if
statement intended to check
for the cdata
variable having a non-null value instead.
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 been already deallocated. This is a critical yet hard to detect issue, because after the memory is deallocated, the operating system or memory manager may not immediately reclaim that memory. Instead, it may mark it as free and allow it to be reused for other purposes. When the program dereferences the pointer before that memory is reused, it might still see the old data, leading to seemingly normal behaviour. But during the next program run the memory may be already reused by the time the dereference is performed and the program may read some completely unrelated data or even may be terminated by the operating system for trying to access now prohibited memory block.
modes = malloc(sizeof (RRModePtr));
if (!modes)
{
RRModeDestroy (mode);
FreeResource (mode->mode.id, 0); // FLAW: accessing a field of a structure in already deallocated memory
return NULL;
}
In this example the function RRModeDestroy
is called
with the value of a mode
pointer as its argument. The
function frees the memory which address is passed as its parameter, so
the mode
variable points to the deallocated memory after
the call. But this memory is accessed in the call to the
FreeResource
function next to calculate the value of its
first argument. This results in accessing already deallocated memory
which is undefined behaviour and may not lead to reading the expected
value. To fix such issue the value of the argument should be calculated
and stored in a temporary variable before the deallocation and the value
of this variable should be used as the call argument instead of the
deallocated memory access.
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 used in some way other than dereferencing it. While the analyzer can’t indicate that this value is dereferenced causing an undefined behaviour (see DEREF_AFTER_FREE), storing it or its derivative, which also can’t be dereferenced, may lead to dereference of an invalid pointer much later in the program execution.
Bmp8 * Bmp8::FromCharDumpFile(FILE *fp) {
// create a Bmp8 object
Bmp8 *bmp_obj = new Bmp8(0, 0);
if (bmp_obj == NULL) {
return NULL;
}
if (bmp_obj->LoadFromCharDumpFile(fp) == false) {
delete bmp_obj;
}
return bmp_obj; // FLAW: potential return of an invalid pointer
}
In this example if an error happens during the
bmp_obj->LoadFromCharDumpFile(fp)
method call, which is
indicated by it returning a false
value, then the memory,
where the variable bmp_obj
points to, is deallocated by a
delete
operator. But instead of returning a null value in
this case to propagate an error (as is done in the previous
if
statement of the Bmp8::FromCharDumpFile
method), the now invalid value of this pointer is returned by the
return
statement at the end of the method which will be
eventually dereferenced by the caller, causing undefined behavior.
pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats) {
pa_assert(data);
pa_assert(formats);
if (data->req_formats)
pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
data->req_formats = formats; // FLAW: storing of a potentially invalid address in a structure field
if (data->source) {
/* Trigger format negotiation */
return pa_source_output_new_data_set_source(data, data->source, data->save_source);
}
return TRUE;
}
In this example the function pa_idxset_free
actually
deallocates the memory which address is passed as its first argument,
the value of a formats
parameter of the
pa_source_output_new_data_set_formats
function in this
case. If this call is executed, then the now invalidated value of the
formats
parameter is stored into the
data->req_formats
field and may be dereferenced later
after the return from the function which will cause an undefined
behavior (see DEREF_AFTER_FREE). The
issue is caused by the code author incorrectly calling the
pa_idxset_free
function with the value of a
formats
parameter as its first argument instead of the
value of the data->req_formats
field.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Low | Yes |
Subtype of USE_AFTER_FREE for
situations where a pointer is passed as argument to the standard C
realloc
or another realloc
-like function that
potentially invalidates it but the original pointer value is used
afterwards instead of a new value, returned by the realloc
call. Dereferencing the original pointer value after calling
realloc
can lead to same effects as using a pointer value
after its deallocation (see DEREF_AFTER_FREE).
int reallocation = 0;
if (nrow > array_size) {
printf("Reallocating valid_red_vals for reduced validation\n");
realloc(valid_red_vals, sizeof(double) * nrow);
reallocation = 1;
}
validation_array("Reduced", nrow, vect_out, valid_red_vals, 6, rawtime); // FLAW: using of potentially invalid pointer
In this example the memory block, which variable
valid_red_vals
points to, is expanded by calling a
realloc
function. The issue with this code is that the
result of calling realloc
is not stored and the original
value of the valid_red_vals
variable is used in call of the
validation_array
function. But realloc
might
change the location of the allocated memory block during reallocation
and return its new address, invalidating the old one.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
This checker finds situations where a pointer referencing already deallocated memory is passed to a function call as an argument. While the analyzer can’t point to the exact place where this value is dereferenced, causing an undefined behaviour (see DEREF_AFTER_FREE), it may lead to dereference of an invalid pointer in the called function or later during the program execution.
One special case of false PASSED_TO_PROC_AFTER_FREE.EX
reports is removing of invalid pointers from collections, such as
vectors and arrays. If this is done using a function call, the analyzer
may flag as an issue passing an invalid pointer value to this function
as an argument. While technically this is indeed use of an invalid
pointer value there is no danger in using its value unless it is
dereferenced.
if (lfd++==10) {
free(tmp);
vstringf (_ ("send_pseudo %s: cannot open tmpfile %s: %s"),
name, tmp, strerror (errno)); // FLAW: use of an invalid pointer
vstring ("\r\n");
return 1;
}
In this example the memory, which address is stored in the
tmp
variable, is deallocated by a free
function call. But the contents of this memory is used right after to
write an error message by calling a vstringf
function and
passing the now invalid pointer value tmp
as one of its
arguments. To fix this issue it is enough to move the free
function call after the vstringf
function call.
...
if (old) {
setlocale(LC_CTYPE, old);
free(old);
}
ic->next = next[0];
fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
if (!fs)
goto out_iconv_close_from;
return fs;
out_iconv_close_from:
iconv_close(ic->fromfs);
out_iconv_close_to:
iconv_close(ic->tofs);
out_free:
free(ic->from_code);
free(ic->to_code);
free(ic);
if (old) {
setlocale(LC_CTYPE, old); // FLAW: use of a potentially invalid pointer
free(old);
}
return NULL;
Here if the value of the old
pointer is non-null this
value is first used as argument in the setlocale
function
call and then is deallocated by a free
function call.
Afterwards in case of an error situation, represented by a null return
value of the fuse_fs_new
function call, the control is
transferred to the out_iconv_close_from
label by the
goto
statement. But before the final return
statement of the function there is a block that again checks the value
of the old
pointer for being non-null. Since the
old
variable value hasn’t been changed in the body of the
first if
statement but the memory, which it points to, has
been deallocated there, the condition of the last if
statement is also evaluated as true, the body of the if
statement is executed and the now invalid value of the old
pointer is passed as a second argument to the setlocale
function call (and as an argument of the free
function call
afterwards). The correct way to fix this issue is to assign a
NULL
value to the old
variable after the
memory is freed in the first if
statement body - this way
the second if
statement check is evaluated as false and its
body isn’t executed.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE415, CWE416, CWE672.
This checker finds situations where a heap memory block, that has already been deallocated, is deallocated again. This causes an undefined behavior, which is usually manifested as an abnormal program termination by the operating system. The most common way to safeguard against this situation is to explicitly assign a null value to the pointer, that holds the deallocated memory block address, after it has been deallocated. This way the deallocated memory address can’t be used later for repeated deallocation or for dereference.
void
rk_rules_free(struct rk_rule* rules)
{
struct rk_rule* p;
for (p = rules; p->lhs != NULL; p++) {
free((void *) p->lhs);
free((void *) p->rhs);
free((void *) p->follow);
}
free(rules);
}
In this example in case of an error situation first the function
rk_rules_free
is called with the value of the
rules
pointer as an argument and then the same
rules
pointer value is used as an argument for the
free
memory deallocation function call. But there is
already a free
function call at the end of the
rk_rules_free
function body that deallocates the memory,
which address is passed as its rules
parameter value. This
results in a situation where the same memory block is deallocated twice
- first at the end of the called rk_rules_free
function and
then in its callee.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | High | No |
This checker is designed to detect dangling pointers, i.e. such
pointers that can be accessed from the caller context after they have
been freed inside the callee. While storing such pointers doesn’t cause
an issue by itself, if they are used accidentally this can lead to hard
to diagnose issues (see DEREF_AFTER_FREE). To avoid such issues it
is advised to explicitly assign a NULL
value to the
pointers that are freed to show that they are actually not
initialized.
class UndoList : public std::list<Undo *>
{
public:
UndoList() : m_name(NULL) {};
virtual ~UndoList() { if ( m_name ) { free(m_name); } };
void setOpName( const char * name ) {
if ( m_name ) { free(m_name); } // FLAW: not resetting 'm_name' value can lead to use of a freed memory
if ( name ) { m_name = strdup(name); } };
const char * getOpName() const { return m_name; };
protected:
char * m_name;
};
In this example in the setOpName
method the value of the
m_name
field isn’t set to NULL
after it is
deallocated and at the same time it isn’t always assigned a new value
leaving the m_name
field potentially contain an invalid
memory address. This allows specific sequences of calls of this method
that can lead to dereference of an already deallocated memory, double
free of the same memory block or return of an invalid memory address
from the getOpName
method: for example, first
setOpName
is called with a non-null string
"Spherify"
as argument (this initializes the
m_name
field with a non-null value); then the same
setOpName
method is called with a NULL
value
as argument (this call frees the memory that was allocated during the
first setOpName
method call but not assigns a new value to
the m_name
field since condition of the
if ( name )
statement is evaluated as false; so this field
value remains non-null); the next call of the setOpName
with any argument leads to deallocation of the same memory that has
already been deallocated, i.e. undefined behavior (see DOUBLE_FREE.EX), or the next call of the
getOpName
method returns address of the already deallocated
memory, accessing which also leads to undefined behavior (see DEREF_AFTER_FREE).
A common pattern for cleaning up fields of a structure is shown in
the example above. While in majority of the cases such methods are
called when the structure won’t be reused afterwards and in this case
there isn’t an issue with the code, it is still better to assign the
NULL
values to the deallocated pointer fields to prevent
hard to diagnose issues with the usage of the freed memory (see DEREF_AFTER_FREE) in the remaining cases.
While both null pointer dereference and dereference of a freed pointer
cause undefined behavior, the former usually result in abnormal program
termination right when the dereference occurs and the latter in the
program using corrupted data which may be much harder to detect and
debug.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
Statistical version of DANGLING_POINTER.STRICT to detect pointers that have been freed but not assigned a new value afterwards. Svace emits this subtype when for sufficient number of times in the source code the same pointer is reassigned after deallocation but in several places it isn’t. This can mean that in those several places the reassignment should be done but is accidentally skipped, which leaves the pointer in an invalid state so it can’t be dereferenced afterwards (see DEREF_AFTER_FREE).
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
Related CWEs: CWE131.
This checker detects possible incorrect calculation of a buffer size,
needed to store a string, when due to a misplaced parenthesis of a
strlen
function call, that computes the string length, a
value is added to the string address instead of its return value. In
this case the strlen
function is called with the address of
the original string substring and computes an incorrect value, which in
turn may result in the buffer size which isn’t enough to store all the
characters of the original string. Trying to copy the string into an
allocated buffer of such size can lead to a buffer overflow.
pkname=(char*)malloc(strlen(argv[optind]+4));
skname=(char*)malloc(strlen(argv[optind]+4));
sprintf(pkname,"%s.pk",argv[optind]);
sprintf(skname,"%s.sk",argv[optind]);
In this example the code author intended to calculate sizes of two
buffers pkname
and skname
, enough to store the
contents of the string argv[optind]
along with three
additional characters for their suffixes and one additional character
for null terminator. But instead of calculating the string length as
strlen(argv[optind])
and then add 4
to its
result, he misplaced the parenthesis and called the strlen
function with argv[optind]+4
value as argument. This
expression produces the address of the fifth character of the
argv[optind]
string so the strlen
call
actually computes the length of the substring starting from its fifth
character which is lesser than the original string length by four.
Afterwards the resulting buffer sizes are used as arguments in
malloc
function calls to allocate two buffers on the heap.
At last these buffers are filled using a sprintf
formatted
print function which doesn’t check for correct buffer sizes, and these
calls lead to writing of the memory outside of the allocated block.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | High | Yes |
Related CWEs: CWE401, CWE404, CWE775.
This checker detects general memory leak situations, where memory was allocated, and then all references to that memory are lost. This makes it impossible to return such memory blocks to the operating system or memory manager so that they can be reallocated for other purposes. Even though typically this doesn’t cause immediate problems, over time such lost memory can amount to large quantities, that can lead to program slow down or even crash due to exhaustion of available memory resources. Memory leaks affect user applications to a lesser degree since when the application exits all the allocated by the application memory, even leaked, is automatically reclaimed by the operating system. But various servers, services and other programs, that commonly have long uptimes, can be more susceptible to memory leaks negative effects. To avoid memory leaks a special care should be taken to properly free all the memory, that was allocated by the program, as soon as it’s clear it won’t be used anymore.
newCallbacks = realloc( offman->FreeBoxesUpdateCallback,
sizeof(FreeBoxCallbackProcPtr) * (offman->NumCallbacks + 1));
newPrivates = realloc(offman->devPrivates,
sizeof(DevUnion) * (offman->NumCallbacks + 1));
if(!newCallbacks || !newPrivates)
return FALSE;
In this code snippet there are two memory (re)allocations using a
standard C function realloc
, whose results are stored in
the newCallbacks
and newPrivates
local
variables. The subsequent if
statement checks whether any
of the allocations have failed by comparing the values of these
variables for being null and returning FALSE
value if any
of them is. But when the first allocation succeeds and the second one
fails, the newCallbacks
variable goes out of scope after
return from the function and its value, which is the address of the
memory block, successfully allocated by the first realloc
call, is lost. The issue is fixed by independently checking the results
of the two allocations and if only the second one fails, then deallocate
the result of the first one before return from the function.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Very high | Yes |
Related CWEs: CWE401, CWE404, CWE775.
This is a subtype of MEMORY_LEAK for cases
where the memory is allocated by using the standard C library string
copying function strdup
, but then all references to that
memory are lost. This function first allocates a memory block, enough to
hold all characters of its string argument, then copies all its
characters into that block and returns its address. The allocated memory
block must be freed using a standard free
function, but if
its address is lost, it’s impossible to do so and even though the memory
can’t be used by the application anymore, it is reserved for the
application by the operating system and can’t be reused for other
purposes, eventually leading to memory shortage. Such issues are
separated into its own subtype since in the majority of cases the
strdup
function is used for allocating small blocks of
memory and so such errors are less critical.
static inline void _set_str_with_default(keynode_t* node, const char *def, char *target)
{
const char *key_str = vconf_keynode_get_str(node);
char *str = NULL;
if (key_str)
str = strdup(key_str);
else
str = strdup(def);
if (!str)
_E("failed to strdup, keep old one");
free(target);
target = str; // FLAW: storing allocated memory address into local memory
} // going out of scope at the return from the function
In the above example the result of one of two strdup
function calls (for this example doesn’t actually matter which one) is
stored into the local variable str
. In the last statement
of the function the value of the str
variable is stored
into the target
parameter. However, this does not affect
the original pointer that was passed into the function. The pointer
target
in the function is a local copy of whatever pointer
was passed in as its last argument but when the
target = str;
assignment is executed, it only changes the
local copy of target
, not the original pointer outside of
this function. So when the function returns, the local variable
target
goes out of scope, and any reference to the newly
allocated memory is lost. Since there are no other references to that
memory, it cannot be freed, resulting in a memory leak. To avoid this
memory leak, the function should be modified so that it actually updates
the pointer in the caller’s context. One common approach is to pass a
pointer to the string instead of the string itself, changing the last
parameter declaration to char **target
and the assignment
to *target = str;
so that changes made to
*target
within the function affect the original pointer
passed in by the caller.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
This is a subtype of MEMORY_LEAK for cases where the memory is allocated for a structure, but then all references to that memory are lost. Since the allocated memory address is lost, it’s impossible to deallocate it anymore, so even though the memory can’t be used by the application anymore, it is reserved for the application by the operating system and can’t be reused for other purposes, eventually leading to memory shortage. Such cases are classified as a separate subtype because some code patterns, specific to low-level work with allocated for structures memory, are difficult to analyze and can lead to an increased number of unclear and false reports.
pPicture->pSourcePict = (SourcePictPtr) malloc(sizeof(PictLinearGradient));
if (!pPicture->pSourcePict) {
*error = BadAlloc;
free(pPicture);
return 0;
}
pPicture->pSourcePict->linear.type = SourcePictTypeLinear;
pPicture->pSourcePict->linear.p1 = *p1;
pPicture->pSourcePict->linear.p2 = *p2;
initGradient(pPicture->pSourcePict, nStops, stops, colors, error);
if (*error) {
free(pPicture);
return 0; // FLAW: allocated memory, which address is stored in field
} // 'pPicture->pSourcePict', is leaked
In this example the memory for structure
PictLinearGradient
is allocated and stored in the
pPicture->pSourcePict
field. If this allocation is
successful, the structure fields are initialized and its address is used
as the first argument in the initGradient
function call.
This function fills other fields of the structure, but if it encounters
an error during its execution, it assigns a non-zero error code to an
integer variable, which address is passed as its last
(int *
pointer) parameter error
and returns
without deallocating the memory, which address passed as its first
parameter. After the initGradient
call the value of that
variable *error
is checked and if it’s non-zero, the
memory, which address is stored in the pPicture
variable,
is deallocated by calling a free
function, but the memory,
which address is stored in its pPicture->pSourcePict
field, is not deallocated. This leads to the only reference to that
memory being lost and impossibility to deallocate it. In order to avoid
this memory leak, it is necessary to deallocate the allocated memory by
calling a free(pPicture->pSourcePict)
before the
free(pPicture)
call.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Low | Yes |
Related CWEs: CWE401, CWE404, CWE775.
This is a subtype of MEMORY_LEAK.STRDUP for cases where a
memory block is allocated using the standard C library string copying
function strdup
, its address is stored in a structure
field, but then all references to that memory are lost. Since the
allocated memory address is lost, it’s impossible to deallocate it
anymore, so even though the memory can’t be used by the application
anymore, it is reserved for the application by the operating system and
can’t be reused for other purposes, eventually leading to memory
shortage. Such cases are classified as a separate subtype because some
code patterns, specific to low-level work with allocated for structures
memory, are difficult to analyze and can lead to an increased number of
unclear and false reports and in the majority of cases the
strdup
function is used for allocating small blocks of
memory and so such errors are less critical.
static void
gst_oss4_source_init (GstOss4Source * osssrc, GstOss4SourceClass * g_class)
{
const gchar *device;
device = g_getenv ("AUDIODEV");
if (device == NULL)
device = DEFAULT_DEVICE;
osssrc->fd = -1;
osssrc->device = g_strdup (device);
osssrc->device_name = g_strdup (DEFAULT_DEVICE_NAME);
osssrc->device_name = NULL; // FLAW: structure field 'device_name', containing
} // just allocated memory address, is overwritten
In the above example at the end of the
gst_oss4_source_init
function the value of the
osssrc->device_name
field is first initialized with the
result of a g_strdup
function call, which is a popular
open-source library GLib’s version of the C standard string copying
function strdup
, and then right in the next statement it is
reassigned a NULL
value. This overwrites the address of the
newly allocated for the copied string memory, returned by the
g_strdup
call, and so this address becomes lost since it
isn’t stored anywhere else. This leads to a memory leak. It looks like the code author
accidentally added initialization of the same structure field a second
time, overwriting the first one.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Average | Yes |
Similar to the MEMORY_LEAK checker,
checker MEMORY_LEAK.EXCEPTION
finds situations where memory is leaked but due to incorrect
exception handling. This can happen when the code, that should
deallocate the memory, is skipped due to taking an exception path.
Common and efficient way to avoid such issues is to use a RAII (Resource
Acquisition Is Initialization) idiom or smart pointers that both provide
strong exception safety guarantees, ensuring that memory is released
even if an exception occurs.
Less reliable cases when the allocation or exception handling contain
complex conditions are reported with a MEMORY_LEAK.EX.EXCEPTION
subtype.
SQLBackend *backend = new SQLBackend();
if (backend->open(PolicyStoragePath)) {
throw runtime::Exception("Policy storage I/O error");
}
In the example above the memory for a SQLBackend
object
is allocated and stored in the backend
local variable but
if the method open
returns a non-zero value indicating an
error, then an exception with type runtime::Exception
is
thrown, the execution leaves the scope, where the backend
variable can be accessed, and its value becomes lost. Since it contains
the allocated memory address, that should be deallocated, a memory leak
occurs. To solve this issue it is enough to add a
delete backend
statement before the throw
statement.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE401, CWE404, CWE775.
This checker detects situations where memory was allocated and then all references to that memory were lost leading to inability to release it back to the system. Compared to the MEMORY_LEAK checker this one is able to detect errors in more complicated situations where leak occurs only on specific paths.
char *pa_getcwd(void) {
size_t l = 128;
for (;;) {
char *p = pa_xmalloc(l);
if (getcwd(p, l))
return p;
if (errno != ERANGE) // FLAW: memory is leaked when 'errno' value is not ERANGE
return NULL;
pa_xfree(p);
l *= 2;
}
}
In the example above the function pa_getcwd
starts by
allocating memory using pa_xmalloc(l)
(which is a wrapper
for the standard C library memory allocation function
malloc
but with added checks), where l
is
initially set to 128
and stores the allocated memory
address into the p
local variable. This memory is intended
to hold the current working directory string. The function then calls
standard POSIX function getcwd(p, l)
to write the path to
the current working directory into the provided buffer p
.
If this call succeeds (returns a non-null pointer), the allocated memory
pointer p
is returned. But if getcwd
call
fails, it checks global errno
variable for an error code:
if errno
is ERANGE
, it indicates that the
buffer was too small, so the function frees the allocated memory with
pa_xfree(p)
and doubles the size of l
for the
next iteration. The memory leak happens if getcwd
fails for
a reason other than needing a larger buffer (e.g., if the current
directory does not exist or there are permission issues). In this case
errno
is set to a value other than ERANGE
and
the function returns NULL
without freeing the previously
allocated memory pointed to by p
. Since there is no
mechanism to free this memory before returning, it becomes inaccessible,
leading to a memory leak.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Low | No |
This checker is a less reliable subtype of a memory leak checker MEMORY_LEAK.EX for cases where the allocated memory may be left not deallocated conditionally on some path, usually for error handling. When all references to allocated memory are lost it leads to inability to release it back to the system and eventually to a potential memory shortage in the system.
switch (argc) {
case 2:
argv_list = 1;
case 1:
usb_controller = strdup(env_get("dfu_usb_con"));
interface = strdup(env_get("dfu_interface"));
devstring = strdup(env_get("dfu_device"));
if (!usb_controller || !interface || !devstring) {
puts("DFU: default device environment is not set.\n");
ret = CMD_RET_USAGE;
goto bad_args;
}
break;
case 5:
argv_list = 4;
case 4:
usb_controller = argv[1];
interface = argv[2];
devstring = argv[3];
break;
default:
return CMD_RET_USAGE;
}
The code snippet above uses standard C library function
strdup
to allocate memory for usb_controller
,
interface
, and devstring
local variables. This
function duplicates a string and allocates enough memory to hold it,
returning a pointer to the new memory. However, this memory must be
freed later to avoid leaks. The switch statement checks the value of
argc
function parameter. If argc
is
2
, it sets argv_list
to 1
but
does not break, which means it falls through to case 1
.
This behavior is intentional, but it’s important to ensure that all
paths that allocate memory are also responsible for freeing it.
done:
dfu_free_entities();
if (dfu_reset)
run_command("reset", 0);
g_dnl_clear_detach();
bad_args:
if (argc == 1) {
free(usb_controller);
free(interface);
free(devstring);
}
#endif
return ret;
This code snippet shows the end of the same function. In the section
with the bad_args
label, memory is only freed if
argc
is equal to 1
. If argc
is
2
and the code jumps to bad_args
, it will not
free the allocated memory for usb_controller
,
interface
, or devstring
. On return from the
function values of these local variables become inaccessible, which
leads to a memory leak.
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
. The standard C library function memcpy
is designed to copy a specified number of bytes from a source address to
a destination address and it performs this operation without any checks
for overlapping memory regions. The C standard states that providing
memcpy
with overlapping source and destination buffers
leads to undefined behaviour. For example, using a straightforward
byte-by-byte copy memcpy
implementation if the destination
starts before the source (e.g., copying from a higher memory address to
a lower one), the bytes in the destination may be overwritten before
they are copied from the source. This leads to data corruption. To avoid
such issues when copying overlapping memory regions use a slightly less
efficient standard memmove
function, which is specifically
designed to handle overlapping memory regions correctly.
if (keep_offset > 0) {
memcpy(data->buff_out, data->buff_out + sizeof(data->buff_out) - keep_offset, keep_offset);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
Visual Basic .NET | 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 handle, a socket descriptor, a database connection or another shared resource is lost because all local variables that held its reference went out of scope or were re-assigned. This leads to inability to release such resource back to the system and eventually to a possible shortage of these resources, i.e. a resource leak.
void func1() {
FILE* f = fopen("qqq.c", "w");
}
void func2() {
FILE* f = fopen("qqq.c", "w");
f = 0;
}
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 output descriptor 1
, the next allocated descriptor
will have value 1
again. 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))) {
output.writeObject("test");
} catch (IOException ignored) { }
}
public static void possibleFix(File f) {
try (FileOutputStream fs = new FileOutputStream(f); ObjectOutputStream output = new ObjectOutputStream(fs)) {
output.writeObject("test");
} 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
the 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()
stream.read(bytes)
}
fun possibleFix(bytes: ByteArray) {
File("data.txt").inputStream().use { it.read(bytes) }
}
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:
.FRUGAL
means that the leaked resource is a
Windows Forms form..EXCEPTION
means that the resource is leaked
because of an exception..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..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 |
Visual Basic .NET | 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, a file handle, a socket descriptor or another shared resource is lost because due to an exception execution left a function where all its references were stored. This leads to inability to release such resource back to the system and eventually to a possible shortage of these resources.
import java.io.*;
public class HandleLeakTest {
public static void example(File f) throws IOException {
try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(f))) {
output.writeObject("test");
}
}
public static void possibleFix(File f) throws IOException {
try (FileOutputStream fs = new FileOutputStream(f); ObjectOutputStream output = new ObjectOutputStream(fs)) {
output.writeObject("test");
}
}
}
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))
zip.putNextEntry(ZipEntry("putNextEntry may throw exception"))
zip.close()
} catch (ex: Exception) {
throw IllegalStateException(ex.message)
}
}
fun possibleFix(f: File, com: String) {
ZipOutputStream(FileOutputStream(f)).use { zip ->
zip.putNextEntry(ZipEntry("putNextEntry may throw exception"))
}
}
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 |
This checker finds situations where a file descriptor, a file handle, a socket descriptor or another shared resource is lost because all local variables that held their values went out of scope or were re-assigned. This leads to inability to release such resource back to the system and eventually to a possible shortage of these resources. Compared to the HANDLE_LEAK checker this one is able to detect errors in more complicated situations where leak occurs only on specific paths.
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) {
stream = new FileInputStream(source); // FileInputStream acquired
} else {
stream = new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
}
readAndPrint(stream);
if (!isFile) {
stream.close();
}
// leaked when the function terminates
}
public static void possibleIncorrectFix(String source, boolean isFile) throws IOException {
final InputStream stream;
if (isFile) {
stream = new FileInputStream(source); // FileInputStream acquired
} else {
stream = new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
}
readAndPrint(stream); // leaked after IOException is thrown
if (isFile) {
stream.close();
}
}
}
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)
stream.read(chunk)
// do some stuff
}
fun example(source: String, isFile: Boolean) {
val stream = if (isFile) File(source).inputStream() else source.byteInputStream() // FileInputStream is acquired
handleStream(stream)
if (!isFile) {
stream.close()
}
// leaked when the function terminates
}
fun possibleIncorrectFix(source: String, isFile: Boolean) {
val stream = if (isFile) File(source).inputStream() else source.byteInputStream() // FileInputStream is acquired
handleStream(stream) // leaked after IOException is thrown
if (isFile) {
stream.close()
}
}
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 |
Similar to the HANDLE_LEAK.EX checker,
checker HANDLE_LEAK.EX.EXCEPTION
finds situations where a
shared resource is leaked but due to incorrect exception handling. It is
able to detect errors in complicated situations where leak occurs only
on specific paths.
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) {
stream = new FileInputStream(source); // FileInputStream is acquired
} else {
stream = new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
}
printAndRead(stream); // leaked after IOException is thrown
if (isFile) {
stream.close();
}
}
public static void possibleFix(String source, boolean isFile) {
InputStream stream = null;
try {
if (isFile) {
stream = new FileInputStream(source); // FileInputStream is acquired
} else {
stream = new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
}
// do some stuff
} catch (IOException e) {
// handle exception
} finally {
if (stream != null && isFile) {
try {
stream.close(); // FileInputStream is closed
} 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)
stream.read(chunk)
// do some stuff
}
fun example(source: String, isFile: Boolean) {
val stream = if (isFile) File(source).inputStream() else source.byteInputStream() // FileInputStream is acquired
handleStream(stream) // leaked after IOException is thrown
if (isFile) {
stream.close()
}
}
fun possibleFix(source: String, isFile: Boolean) {
val stream = if (isFile) File(source).inputStream() else source.byteInputStream() // FileInputStream is acquired
stream.use { handleStream(it) } // FileInputStream is closed
}
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 Java-specific checker is a subtype of HANDLE_LEAK that reports incorrect using of
classes which implement a java.io.Closeable
interface. The
analyzer ensures that after creation of an instance of such class its
close
method is called and reports a warning otherwise. If
the issue occurs due to incorrect exception handling a subtype
HANDLE_LEAK.CLOSEABLE.EXCEPTION
is emitted.
import java.io.Closeable;
import java.io.IOException;
class MyClass implements Closeable {
@Override
public void close() throws IOException {
}
}
class Example {
public void noclose() {
MyClass s = new MyClass();//no close
}
public void withclose() {
MyClass s = new MyClass();//no leak
s.close();
}
}
In the example above Svace will emit warning for method
noclose
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This C-specific checker reports potential memory leaks caused by
misusing the SQLite library APIs working with prepared statements. Each
creation of a prepared statement object using
sqlite3_prepare_v2
function should be accompanied by a
corresponding sqlite3_finalize
call to delete the prepared
statement. Failing to do so may lead to eventual shortage of the dynamic
memory available to the application.
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.
Note that checker for Go finds double close on channels but does not on files or other resources.
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.
Note that checker for Go finds double close on channels but does not on files or other resources.
int my_close(int p) {
if (cond()) {
close(p);
return -1;
}
return 0;
}
void test(int fd) {
my_close(fd);
close(fd);
}
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)
stream.read(buf)
// ...
stream.skip(10)
stream.close()
stream.read(buf)
}
fun possibleFix(fileName: String) {
val buf = ByteArray(1024)
FileInputStream(fileName).use {
it.read(buf)
// ...
it.skip(10)
it.read(buf)
}
}
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) {
fclose(*fd);
return 2;
}
return 0;
}
void example() {
FILE *fd;
openAndPrepare(&fd);
char buf[100];
fread(buf, 10, 10, fd);
}
void possibleFix() {
FILE *fd;
if (openAndPrepare(&fd)) {
return;
}
char buf[100];
fread(buf, 10, 10, fd);
}
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>
DEFINE_GUID( 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);
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:
HRESULT QueryInterface(REFIID riid, void** ppv);
ULONG AddRef();
ULONG Release();
long Add(long Op1, long Op2) { return Op1 + Op2; }
long Sub(long Op1, long Op2) { return Op1 - Op2; }
private:
DWORD m_lRef;
public:
Arithm() : m_lRef(0);
};
HRESULT Arithm::QueryInterface(REFIID riid, void** ppv) {
switch(riid) {
case IID_IUnknown:
case IID_IArithm;
*ppv = this;
AddRef();
return ( S_OK ) ;
default:
return ( E_NOINTERFACE );
}
}
ULONG Math::Release() {
InterlockedDecrement( &m_lRef );
if (m_lRef == 0) {
delete this;
return 0;
} else {
return m_lRef;
}
}
ULONG Math::AddRef() {
InterlockedIncrement( &m_lRef );
return m_lRef;
}
long summarize(IUnknown *pUnknwn, long a, long b) {
IArithm *pArithm = NULL;
HRESULT hr = pUnknwn->QueryInterface(IID_IArithm, (void **)&pArithm);
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 overflows or other reasons.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
C# | Quality | Major | High | Yes |
Related CWEs: CWE835.
Check for instances of loops that never terminate due to control variables which are not properly updated.
In the following example, a wrong variable is updated:
In the following example, a wrong variable is updated:
In the following example, a wrong variable is updated:
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Very high | Yes |
C# | Quality | Minor | Very high | Yes |
Check for methods that unconditionally call themselves.
In the following example, method
CodeBuilder.AppendFormat
always calls itself, which leads
to stack overflow.
public class CodeBuilder
{
public CodeBuilder AppendFormat(string format, object[] args)
{
return AppendFormat(format, args);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Very low | No |
C/C++ | Quality | Major | Very low | No |
Go | Quality | Major | Very low | No |
This checker finds situations where the number of a loop iterations may be more than it was intended because the termination condition depends on the value of a string.
In the following example if there are no space characters in the
pos
string parameter the loop will not be terminated
normally.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | No |
Visual Basic .NET | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Low | No |
Go | Quality | Major | Low | No |
C# | Quality | Major | Average | Yes |
Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.
This checker finds situations when loop termination condition may become infeasible due to an overflow or underflow of the index variable.
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) {
len -= 8; // Overflow if `len % 9 != 0`.
}
}
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.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Critical | Unknown | Yes |
Go | Quality | Critical | Unknown | Yes |
Checker finds suspicious situations where loop execution is bounded only by array data.
Example:
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
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 */
printf("%d\n", (INT_MAX / 2) * 3);
}
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 |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
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
size += 2; // ⚠️〔INTEGER_OVERFLOW.EXPLICIT An overflow in the arithmetic expression size += 2 of type ushort will occur: result will be 1 instead of 65537〕
}
An overflow in the arithmetic expression size += 2
of
type ushort will occur on line 4: result will be 1 instead of 65537.
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)
{
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〕
IntPtr hglobal = Marshal.AllocHGlobal(size);
}
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++;
}
IntPtr hglobal = Marshal.AllocHGlobal(size);
}
To prevent silent memory corruption it is required to use checked expression or statement if overflow may happen
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Average | Yes |
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);
}
...
z = mult(0x7FFFFFFF, 2); /* Expected 0xFFFFFFFE but result is -2. */
...
The multiplication above should be performed after widening the arguments:
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.
The evaluation of the constant expression above should be performed in 64 bits:
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 {
number.toIntOrNull()?.let {
try {
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.
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. 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) {
foo(INVERT_BYTES_32(x) + 1);
}
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) {
simple_add(param);
} else {
simple_add(param + 200);
}
}
void func(boolean boo) {
byte var = 120;
byte result;
// Variable 'result' will contain distorted data in any case
if (boo) {
result = var + 200;
} else {
result = var << 10;
}
}
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);
memcpy(dst, str, len - 1);
}
void possible_fix(char *dst, const char *str) {
size_t len = strlen(str);
if (len >= 0) {
memcpy(dst, str, len - 1);
}
}
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 | Very low | No |
C/C++ | Quality | Normal | Very low | No |
Go | Quality | Normal | Very low | 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) {
str[i] = '*';
}
}
void possible_fix(char *str) {
unsigned len = strlen(str);
unsigned i;
if (len < 2)
return;
for (i = 0; i < len - 2; ++i) {
str[i] = '*';
}
}
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) {
str[len1] = '*';
}
}
void possible_fix(char *str) {
unsigned len = strlen(str);
if (len > 0 && len < 10) {
unsigned len1 = len - 1;
str[len1] = '*';
}
}
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);
dummy(len - 1);
}
void possible_fix(const char *str) {
unsigned len = strlen(str);
if (len != 0) {
dummy(len - 1);
}
}
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 |
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;
stub(y);
}
}
void possible_fix(uint32_t x) {
if (x <= 100000) {
uint64_t y = (uint64_t)x * 50000;
stub(y);
}
}
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;
stub(y);
}
void possible_fix(void) {
uint64_t x = 100000;
uint64_t y = x * 50000;
stub(y);
}
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 | Very low | 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
b := int16(a + 100)
}
func no_overflow(a int8) {
// There is no integer overflow
b := int16(a) + 100
}
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.
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
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This C-specific portability checker finds assignments of a value of a
signed type int
to a variable of a bigger unsigned type
size_t
. This is a common source of various issues when
porting 32-bit code to a 64-bit platform. Since on common 32-bit
platforms signed int
and unsigned size_t
types
have same width converting between them is performed differently than on
most 64-bit platforms where size_t
is usually wider.
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.
For the example above specification for getpass
must be
added:
The checker may detect sensitive data by name of used variables
(passwd
, pwd
, privkey
, etc.).
Configuration option SENSITIVE_NAME_REGEX
allows
changing regular expression for name. Configuration option
LEAK_FUNC_NAME_REGEXP
allows setting additional regular
expression for function names that are considered leaks.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Minor | Very high | Yes |
C# | Security | Minor | Very high | Yes |
Related CWEs: CWE209, CWE532, CWE535.
Database connection string is exposed in user-visible output.
m_log.Info("[REGION DB]: MySql - connecting: " + Util.GetDisplayConnectionString(m_connectionString));
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Minor | Very high | Yes |
C# | Security | Minor | Very high | Yes |
System information (like host name, OS version, stack trace etc.) is written into publicly-visible output. It can help the attacker to gain information about the application and the environment where it is executed.
Note: exception message is also considered publicly-visible output because they are often displayed.
public partial class ExploitDebug : System.Web.UI.Page
{
protected void btnGo_Click(object sender, EventArgs e)
{
StringBuilder strBuilder = new StringBuilder();
strBuilder.AppendFormat("Current Dir: {0}\n", Environment.CurrentDirectory);
strBuilder.AppendFormat("UserName: {0}\n", Environment.UserName);
strBuilder.AppendFormat("Machine Name: {0}\n", Environment.MachineName);
strBuilder.AppendFormat("OS Version: {0}\n", Environment.OSVersion);
throw new Exception(strBuilder.ToString());
}
}
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.
var pass = []byte("0123456789abcdef0123456789abcdef")
func main() {
block,_ := aes.NewCipher(pass) // Using of hardcoded password.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Very high | Yes |
C/C++ | Security | Critical | Unknown | No |
C# | Security | Critical | Very high | 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.
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.
system(command);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Critical | Unknown | No |
Related CWEs: CWE676.
The checker reports a warning for a pattern when C standard library
function strncmp
for string comparison uses the result of
calling string length calculation function strlen
as the
length parameter directly. In this way only the prefix of the string is
compared because comparison stops at the null-terminator so it and the
remaining characters are not compared. It may be the source of a
vulnerability when checking the validity of sensitive data, such as
passwords. For such cases the length parameter should be calculated as
result of the expression strlen(arg) + 1
to account for the
end of the string.
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 using of an insecure in many contexts HTTP
GET
method instead of the POST
method.
void foo() {
CURL* curl = curl_easy_init();
if (curl) {
CURLcode res;
// ...
res = curl_easy_perform(curl);
}
}
Function foo
illustrates the defect: libcurl library
function curl_easy_init
creates a new session with a
GET
method by default. The operation will be processed with
the default method after calling curl_easy_perform
.
void bar() {
CURL* curl = curl_easy_init();
if (curl) {
CURLcode res;
// ...
curl_easy_setopt(curl, CURLOPT_POST, 0L);
res = curl_easy_perform(curl);
}
}
Function bar
illustrates the defect: libcurl library
function curl_east_setopt
with the option
CURLOPT_POST
and third parameter equal to zero resets the
request type back to the GET
method.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Minor | Unknown | No |
Setting inappropriate parameter values in a libcurl library function
curl_easy_setopt
call may lead to security issues.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Critical | Unknown | No |
C/C++ | Quality | Critical | Unknown | No |
Kotlin | Quality | Critical | Unknown | Yes |
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() {
pthread_mutex_lock(a);
pthread_mutex_lock(b);
// ...
}
void thread2() {
pthread_mutex_lock(b);
pthread_mutex_lock(a);
// ...
}
class DeadlockTest {
private Object lock;
public synchronized void direct() {
synchronized(lock) {
// ...
}
}
public void reverse() {
synchronized(lock) {
synchronized(this) {
// ...
}
}
}
}
class DeadlockExample() {
lateinit var l: Any
@Synchronized
fun direct(): Unit {
synchronized(l) {
// ...
}
}
fun reverse(): Unit {
synchronized(l) {
synchronized(this) {
// ...
}
}
}
/*
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() {
wg.Add(1)
atomic.AddInt32(&x, 1)
wg.Done()
}()
}
wg.Wait()
}
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) {
pthread_mutex_lock(mut);
if (x)
pthread_mutex_unlock(mut);
pthread_mutex_lock(mut); // DOUBLE_LOCK: pthread_mutex_unlock() might not have been called.
// ...
pthread_mutex_unlock(mut);
}
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) {
pthread_mutex_lock(mut);
if (flag) {
return; // `mut` isn't unlocked on this path.
}
pthread_mutex_unlock(mut);
}
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() {
pthread_mutex_lock(&m);
}
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:
LockGuardBad() { pthread_mutex_lock(&m); } // report NO_UNLOCK.CTOR
~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 can be locked by external code, which may lead to unexpected blocking or deadlock (the first three examples below). Also, if the object used for synchronization can be modified by external code, and if this happens during the execution of a critical section, then the next thread attempting to enter the critical section will not be blocked as expected (the fourth example below).
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.
class AutoLock {
public:
AutoLock(pthread_mutex_t *_m) {
m = _m;
}
AutoLock() {}
~AutoLock() {
pthread_mutex_unlock(m);
}
void lock() {
pthread_mutex_lock(m);
}
private:
pthread_mutex_t *m;
};
pthread_mutex_t m_glob; // It should be used.
void bar() {
AutoLock l;
l.lock(); // Variable `l` is on stack. `l.m` is also on stack.
}
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) {
pthread_mutex_lock(mut); // Lock.
int res = accept(socket); // This function might take a lot of time.
pthread_mutex_unlock(mut);
return 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 |
---|---|---|---|---|
Visual Basic .NET | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Unknown | No |
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() {
a = 0; // Ignore this access to field `a` in constructor.
}
public synchronized void bar() {
a++; // Access to field `a` under `this` lock.
}
public void foo(int b) {
synchronized(l) {
a += b; // First access to field `a` under `l` lock.
a += 2; // Second access to field `a` under `l` lock.
}
}
/*
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
lateinit var l: Any
@Synchronized
fun bar() {
a++ // Access to `a` under `this` lock.
}
fun foo(b: Int) {
synchronized(l) {
a += b // Access to `a` under `l` lock.
a += 2 // Access to `a` under `l` lock.
}
}
/*
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) {
data = false; // Ok
}
}
public void foo2() {
data = true; // NO_LOCK.GUARD here
}
}
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) {
x = xx;
}
}
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.
fCritialSection = true;
}
fSharedObj = new ClassA(x);
}
}
}
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) {
aRef = new A();
}
}
}
return aRef;
}
private volatile A aVolRef;
public A getA() {
A localRef = aVolRef;
if (localRef == null) {
synchronized (this) {
localRef = aVolRef;
if (localRef == null) {
aVolRef = localRef = new A();
}
}
}
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 {
fis = new FileInputStream(new File("testFile"));
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)
fis.close();
e.printStackTrace();
}
}
}
} else {
break;
}
}
fis.close();
} catch (Throwable th) {
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
th.printStackTrace();
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very high | 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 implementations function mkstemp
creates
file with correct permissions and this warning is not needed.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very high | 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 |
JavaScript | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Go | Quality | Major | Average | Yes |
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. This may be the result of a typo or an application logic error.
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. Subtype
INVARIANT_RESULT.MACRO
is reported when neither INVARIANT_RESULT.OP_ASSIGN
nor
INVARIANT_RESULT.OP_ZERO
should be issued and the
expression is a part of a macro expansion.
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.
In the following example, a logical operator !
appears
to have been substituted for a bitwise operator ~
.
In the following example, parentheses are missed.
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.
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.
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.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Average | Yes |
Visual Basic .NET | Quality | Normal | Average | Yes |
C/C++ | Quality | Normal | Average | Yes |
Go | Quality | Normal | Average | Yes |
Kotlin | Quality | Normal | Average | Yes |
Python | 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. This is commonly caused by logic errors in the related code or nearby.
In the following example the initial value of the
fVerifyTD
variable is FALSE
and it can only be
set to TRUE
in the true branch of the first if
statement. Because of the incorrectly used
else if (fVerifyTD)
statement (instead of the intended
if (fVerifyTD)
) this check is only performed in the false
branch of the first if
statement (instead of being
performed always) where the value of the fVerifyTD
variable
can only have the initial FALSE
value and so the assignment
of the fTrustTD
variable in that if
statement
body is unreachable.
BOOL fVerifyTD = FALSE;
// If this is an exported type with a mdTokenNil class token, then then
// exported type did not give a typedefID hint. We won't be able to trust the typedef
// here.
if ((FoundExportedType != mdTokenNil) && (FoundCl == mdTokenNil))
{
fVerifyTD = TRUE;
fTrustTD = FALSE;
}
// verify that FoundCl is a valid token for pFoundModule, because
// it may be just the hint saved in an ExportedType in another scope
else if (fVerifyTD)
{
fTrustTD = pFoundModule->GetMDImport()->IsValidToken(FoundCl);
}
fun example(a: Int) {
when {
a > 10 -> print("a > 10")
a <= 10 -> print("a <= 10")
else -> print("unreachable")
}
}
fun possibleFix(a: Int) {
when {
a > 10 -> print("a > 10")
a <= 10 -> print("a <= 10")
}
}
else
branch in when
expression is
unreachable because the first and the second branches cover all possible
values of a
. The redundant branch should be removed to fix
this defect.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Subtype of UNREACHABLE_CODE for statements that follow a call of a function that terminates program execution.
In the following example function
report_instruction_timeout
calls a panic
function that never returns so the assignment of a new value to the
start_time
variable can’t be ever executed:
while (1) {
cpu_relax();
status = GET_MSEG_HANDLE_STATUS(h);
if (status != CCHSTATUS_ACTIVE)
break;
if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) {
report_instruction_timeout(h); // Calls 'panic' which doesn't return
start_time = get_cycles(); // so this assignment is unreachable
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Visual Basic .NET | Quality | Normal | Low | 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 preceding operation always throws an exception.
In the following code the compiledResourceFile
variable
in the case ".resx"
block is never assigned because of the
exception in the CompileResx
function:
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) {
FileInfo compiledResourceFile = null;
switch (InputFile.Extension.ToLower(CultureInfo.InvariantCulture)) {
case ".resx":
compiledResourceFile = CompileResx(solutionConfiguration);
break;
...
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 a switch statement is
unreachable. Related situations with enum variables used as conditions
are reported with the UNREACHABLE_CODE.ENUM warning type.
While this is common to have such unreachable statements when
default
cases are strictly enforced by a coding policy or
added to just be on the safe side, occasionally such reports may show an
issue with the program logic.
In this example the default
case is unreachable because
the if
statement enclosing the switch
statement restricts the possible values of the format
variable to those handled by other cases:
if ((format == CTRL_RAW || format == CTRL_USER || format == CTRL_MGMT)
&& size >= 8) {
...
switch (format) {
case CTRL_RAW:
title = "RAW Open";
break;
case CTRL_USER:
title = "USER Open";
break;
case CTRL_MGMT:
title = "MGMT Open";
break;
default: // This case label is unreachable
title = "Control Open";
break;
}
print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN,
title, comm, details);
In the following example function
llhttp__internal__c_test_flags_2
can only return values
0
or 1
:
int llhttp__internal__c_test_flags_2(
llhttp__internal_t* state,
const unsigned char* p,
const unsigned char* endp) {
return (state->flags & 256) == 256;
}
But its result is used as if it can return other values too:
switch (llhttp__internal__c_test_flags_2(state, p, endp)) {
case 0:
goto s_n_llhttp__internal__n_error_11;
case 1:
goto s_n_llhttp__internal__n_invoke_test_flags_3;
default:
goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
Subtype of UNREACHABLE_CODE for
switch
statement with an enum variable as a condition
expression when that switch
statement contains case labels
for each of the possible enumeration type values. The warning is emitted
for the default
label which is unreachable in such
situation. While technically the code statements under this label are
indeed unreachable the use of a default
label may be
enforced by a coding policy.
Note that for C/C++ an enumeration variable is just an integer variable that is able to hold any value from the enumeration type domain so it can still hold an integer value outside of that domain but relying on this makes a program harder to understand and support but can be useful to filter out unsupported values.
In this example the enumeration is declared as containing four elements:
enum dss_rotation {
dss_rotation_0_degree = 0,
dss_rotation_90_degree = 1,
dss_rotation_180_degree = 2,
dss_rotation_270_degree = 3,
};
The following switch statement handles all of them (the code for the
first three is removed for brevity) in unique ways and also has a
default
label with own code that is unreachable under
normal program execution:
switch (rotation) {
case dss_rotation_90_degree:
...
case dss_rotation_180_degree:
...
case dss_rotation_270_degree:
...
case dss_rotation_0_degree:
if (!mirroring) {
*cropped_offset = (line_length * ps) *
crop->top + (crop->left / vr_ps) * ps;
} else {
*cropped_offset = (line_length * ps) *
crop->top + (crop->left / vr_ps) * ps +
(line_length * (crop->height - 1) * ps);
}
break;
default:
*cropped_offset = (line_length * ps * crop->top) /
vr_ps + (crop->left * ps) / vr_ps +
((crop->width / vr_ps) - 1) * ps;
break;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C/C++ | Quality | Normal | Unknown | Yes |
Subtype of UNREACHABLE_CODE for
situations where a case label of a switch
statement is
unreachable.
In the following example the label case DCA_DOLBY
is
unreachable because the DCA_CHANNEL_MASK
mask zeros all
bits except the lower six so the resulting value lies in the range from
0
to 63
which 101
doesn’t belong
to.
#define DCA_MONO 0
#define DCA_CHANNEL 1
#define DCA_STEREO 2
#define DCA_STEREO_SUMDIFF 3
#define DCA_STEREO_TOTAL 4
#define DCA_DOLBY 101
#define DCA_CHANNEL_MASK 0x3F
...
switch (flags & DCA_CHANNEL_MASK) {
case DCA_STEREO:
case DCA_STEREO_SUMDIFF:
case DCA_STEREO_TOTAL:
case DCA_DOLBY:
chans = 2;
if (tpos) {
tpos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
tpos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
}
break;
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 |
Python | Quality | Normal | Average | No |
Subtype of UNREACHABLE_CODE for situations, where a code is unreachable due to a condition that depends on global variables. This is common for debugging code locked behind a check of a global variable value, but occasionally such reports may show an issue with the program logic.
int wgn_verbose = FALSE; // Global variable that is never reassigned
...
if (wgn_verbose) // The following code is essentially unreachable unless the
{ // value of 'wgn_verbose' is changed and the program is recompiled
int i;
for (i=0; i < margin; i++)
cout << " ";
cout << "stopped samples: " << node.samples() << " impurity: "
<< node.get_impurity() << endl;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | No |
Related CWEs: CWE561.
C/C++-specific subtype of UNREACHABLE_CODE for continue
statements in loops whose body is known to be executed only once, such
as do-while
loops with constant false conditions. Contrary
to the probable intention to restart the loop execution from the
beginning the execution of the loop will be terminated. If this is the
intended behaviour it is better to use a break
statement
instead to reflect it more clearly.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Very high | Yes |
C/C++ | Quality | Normal | Average | No |
C# | Quality | Normal | Very high | Yes |
Related CWEs: CWE561.
Subtype of UNREACHABLE_CODE for situations where there is no execution path, even an unfeasible one, to a code fragment.
Common cases for harmless unreachable statements needed for syntactic
correctness, such as return statements or C/C++ va_end
statements are reported by UNREACHABLE_CODE.NO_PATH.RETURN
and UNREACHABLE_CODE.NO_PATH.VARARG
subtypes respectively.
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 {
z := ret(a)
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 |
Python | 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.
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 |
---|---|---|---|---|
Java | Quality | Undefined | Unknown | No |
Go | Quality | Undefined | Unknown | No |
Kotlin | Quality | Undefined | Unknown | No |
Subtype of REDUNDANT_COMPARISON for situations, when there is a presence/absence check of a key in a map, but the given key is always present/absent.
import java.util.*;
public class Example {
public void presentAfterRemove(Map<Object, String> box, Object key) {
box.put(key, "value");
if (box.containsKey(key)) { // svace: emitted REDUNDANT_COMPARISON.MAP
// ...
}
}
public void absentAfterRemove(Map<Object, String> box, Object key) {
box.remove(key);
if (box.containsKey(key)) { // svace: emitted REDUNDANT_COMPARISON.MAP
// ...
}
}
public void absentAfterClear(Map<Object, String> box, Object key) {
box.clear();
if (box.containsKey(key)) { // svace: emitted REDUNDANT_COMPARISON.MAP
// ...
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
Subtype of REDUNDANT_COMPARISON when there is a test for equality of two variables, but their types do not have overlap in subtypes.
import java.util.*;
public class Example {
public void equalsTest(List<String> names) {
String name = names.get(0);
if ("<unknown>".equals(names)) { // svace: emitted REDUNDANT_COMPARISON.EQUALS
// ...
}
name = names.get(1);
if ("<unknown".equals(name)) { // svace: not_emitted REDUNDANT_COMPARISON.EQUALS
// ...
}
}
}
This checker also works with JUnit asserts and reference equals.
import junit.framework.Assert;
import java.util.*;
public class Example1 {
interface CanFly {}
interface CanSwim {}
interface CanWalk {}
class Duck1 implements CanFly {}
class Duck2 implements CanSwim {}
class Duck3 implements CanWalk, CanFly {}
public void assertEqualsTest(CanFly first, CanSwim second, CanWalk third) {
// ...
Assert.assertEquals(first, second); // svace: emitted REDUNDANT_COMPARISON.EQUALS
// ...
// we can compare these objects because they can be of type Duck3
Assert.assertEquals(first, third); // svace: not_emitted REDUNDANT_COMPARISON.EQUALS
// ...
}
}
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.
Variable st.fld
accessed at the last line was never
initialized.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Very low | 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;
val.k = 3;//Structure val was not fully initilialized
memcpy(dst, &val, sizeof(T));//copied not initilized sturcture - UNINIT.STRUCT
}
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) {
init { printInfo() }
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;
read(fd, &s, sizeof(struct S));
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 |
Related CERTs: CERT-SIG30-C.
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.
char* info;
void handler(int signum) {
free(info); // Free is an asynchronous unsafe function,
// so the warning will be emitted here.
info = NULL;
}
int main(void) {
signal(SIGINT, handler); // Register `handler` as a handler.
// Skipped.
return 0;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Related CERTs: CERT-SIG34-C.
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.
void handler(int signum) {
signal(signum, handler); // Call to `signal` from within `handler` to register.
// `handler` again; the warning is emitted here.
}
int main(void) {
signal(SIGINT, handler); // Register `handler` as a handler.
// Skipped.
return 0;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
Related CERTs: CERT-SIG31-C.
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
.
volatile sig_atomic_t iflag = 0;
int kflag = 0;
void int_handler(int signum) {
iflag = 2; // All ok, `iflag` is `volatile sig_atomic_t`.
}
void kill_handler(int signum) {
kflag = 3; // The warning is emitted: modifying shared object `kflag` in a signal handler.
}
int main(void) {
signal(SIGINT, int_handler); // Handler is registered.
signal(SIGKILL, kill_handler); // Handler is registered.
// 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) {
longjmp(env, 1); // Call to `longjump` from a signal handler.
}
int main(void) {
signal(SIGINT, handler); // Handler is registered.
// 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) {
raise(SIGUSR1); // Handler for SIGINT recusively calls `raise`.
}
int main(void) {
signal(SIGUSR1, log_msg); // Handler is registered.
signal(SIGINT, handler); // Handler is registered.
// Skipped.
raise(SIGINT); // `raise` is explicitly called for SIGINT.
// Other code.
return 0;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Related CERTs: CERT-SIG35-C.
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.
volatile sig_atomic_t flag;
void handler(int signum) {
flag = 1; // No call of `abort`.
}
int main(void) {
signal(SIGILL, handler); // Handler is registered.
// 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) {
dll_handle_t handle = dlopen(path, RTLD_LAZY);
if (!handle)
return -errno;
return 0;
}
void foo() {
dll_handle_t handle = loadlib("/usr/lib/lib.so");
// ...
}
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 call of a library function is syntactically correct but semantically the values of the parameters don’t match their meaning, potentially leading to unexpected errors. For example, the C language libraries commonly use the same type (usually integer) for different entities (sockets, file descriptors, offsets, etc) which can be accidentally interchanged. The checker tracks the origins of the values and reports issues even when the incorrectly used values are explicitly converted to the library function parameter type.
void func() {
int fd = open("some_file.txt", SOME_FLAGS);
// Bind function is using for file descriptor
bind(fd, ADDR, ADDR_LEN);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Related CWEs: CWE61.
This Linux-specific checker finds situations where a path of a file
being opened is not checked for containing a symbolic link beforehand.
This potentially allows a malicious user to trick application to work
with a substituted file instead of the right one. Such check can be
performed using a stat
system call.
void func(char *path, char *data) {
// Opened file path is not checked for containing a symbolic link
int fp = open(path, SOME_FLAGS);
if (fp) {
write(data, data, strlen(data));
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Related CWEs: CWE253.
This checker finds cases where the return value of a library function
is processed incorrectly. Return value of library functions usually mark
whether the call is successful or not and if it is checked in a wrong
way it may lead to missing of proper error handling. For example, the
standard C library formatted output function printf
returns
the number of characters printed or a negative value in case of an error
and so comparing this value for equality with zero is incorrect.
void func() {
// The check for zero return value below is incorrect.
// This value should be checked either being negative (in case of an output error) or
// compared with the length of "string\n" to make sure that all characters are written.
if (fprintf(stdout, "%s\n", "string") == 0) {
int do_smth;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Related CERTs: CERT-FIO24-C.
This checker detects cases of double file opening which may lead to a race condition and unexpected program behaviour. A warning is emitted if a file is opened and isn’t closed before another attempt for the same file opening.
FILE* foo(const char* fp) {
return fopen(fp, "a");
}
void bar() {
char filepath[] = "path/to/file";
open(filepath, O_RDONLY);
// ...
FILE* output = foo(filepath);
// ...
}
In this example the bar
function illustrates the issue:
it first opens file using the open
system call and then the
same file is opened again in the foo
function using the
fopen
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. In this case the permissions on the
newly created file may be set randomly.
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++ | Security | Minor | Unknown | No |
Related CWEs: CWE732.
This C-specific checker reports a warning when too broad permissions
are set while creating or modifying file objects using standard library
functions. This may allow an untrusted user to modify application data
in an arbitrary way. Usually, an owner is given maximum permissions,
group has the same rights or less, and others have the same rights as
the group or less but less than the owner. Traditionally, permissions
0755
are used for directories and executable files and
0644
are used for regular files. These permissions prevent
modification of the file for everyone except the file owner but permit
reading for all. For private or secure storage, it is a good idea to
restrict file access for all but the owner with setting
0700
or 0600
permissions. Some software
requires restriction of group access (with 0705
and
0604
permissions). Additionally this checker reports
warnings for a common error when a decimal mode permission constant like
705
is used instead of an octal value like
0705
which results in unexpected permissions.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Average | Yes |
The Linux-specific checker finds possible fork bombs: situations when
fork
system call creates two processes and both these
processes may execute the same fork
again. This leads to
uncontrolled creation of new processes until the system isn’t able to
create them.
for (int i = 0; i < NPROC; i++) {
int pid = fork(); // FORK_BOMB emitted.
if (pid < 0) {
perror("fork():");
} else if (pid == 0) {
execve(...); // May fail: the cycle will continue in two processes.
} else {
child[i] = pid;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Average | Yes |
The Linux-specific checker finds situations when two processes
created by one fork
system call 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) {
execve(...); // May fail.
}
// Second fork will create 4 parallel processes. This is probably unwanted.
int pid2 = fork(); // FORK_BOMB.MINOR emitted.
if (pid2 == 0) {
execve(...);
_exit(127);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Average | Yes |
The Linux-specific checker finds situations when two processes
created by fork
system call and separated by a condition
like if (pid == 0)
will meet and execute the same code in
both the parent and the child process. This is commonly caused by an
application logic error.
int pid = fork();
if (pid < 0) {
perror("fork():");
} else if (pid == 0) { // Child.
execve(...); // May fail: execution will continue in two processes.
} else { // Parent.
child[i] = pid; // FORK_PROCESSES_MEET emitted.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very low | 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
a.at(10) = 'u'; // FORK_PROCESSES_MEET.EXCEPTION emitted.
_exit(127);
} else { // parent
a.at(7) = 'b'; // May throw second exception.
}
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:
MyData();
int kind;
char ch;
int sum;
};
MyData::MyData() : ch('q') {
this->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 | Minor | Average | Yes |
This checker finds classes in which the destructor explicitly deallocates memory using a pointer, but explicit copy constructor is not provided. Using implicitly declared copy constructor in this case willead to double deallocation through the same pointer.
class MyData {
char* str;
int size;
public:
MyData() {
str = (char*) malloc(10);
size = 10;
}
// copy constructor is not provided nor deleted
~MyData() {
free(str); // Deallocate memory pointed to by `str`.
}
};
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.
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 | Very high | 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];
chars[0] = '\0';
return 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() {
buf = new int[10];
}
~C() {
free(buf); // Here svace fires the warning.
}
};
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:
C1() { buf = new int[10]; }
~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:
C1() { f = fopen("filename", "r"); }
~C1();
};
C1::~C1() { // Warning: missing `fclose` for `f`.
}
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 consecutively 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:
C1() { buf = new int[10]; }
~C1() { delete[] buf; }
C1& operator=(C1& other) {
this->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");
p = s.c_str();
} // 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 |
---|---|---|---|---|
Java | Quality | Minor | Average | Yes |
C/C++ | Quality | Minor | Average | Yes |
Kotlin | Quality | Minor | Average | Yes |
This checker finds situations where the value returned by operator
new
is compared with NULL
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | No |
Related CWEs: CWE758.
Check for cases where a C++ delete
operator is applied
to a pointer to void
. This is undefined behaviour and even
though is usually correctly handled by the deallocators results in not
calling the underlying object’s destructors which may lead to issues
with application logic or resources leaks. If the underlying base
pointer type is known to not have a destructor a warning subtype DELETE_VOID.STRICT
is
reported.
In the following example, delete
is used on a pointer of
type void
:
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:
MyData& operator=(const MyData& a);
private:
char data[4];
};
MyData& MyData::operator=(const MyData& a) {
memcpy(this->data, a.data, 4); // Warning: `a` wasn't checked for equality to `this`,
// while code produces UB if `a` equals `*this`.
return *this;
}
The problem can be fixed as follows:
MyData& MyData::operator=(const MyData& a) {
if (&a == this)
return *this;
memcpy(this->data, a.data, 4); // No warning.
return *this;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | CodingStyle | 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).
class MyData {
public:
void operator=(const MyData& a);
private:
int kind;
};
void MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPE.MINOR
this->kind = a.kind;
} // Return type is void, meaning chaining equalities like `a = b = c`
// or calling methods like `(a = b).myMethod()` won't work.
class MyData {
public:
const MyData& operator=(const MyData& a);
private:
int kind;
};
const MyData& MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPE.MINOR
this->kind = a.kind;
return *this; // Return type is const,
// meaning calling non-const methods like `(a = b).myMethod()` won't work.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | CodingStyle | Minor | High | Yes |
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 may either lead to syntactical error or invoke different assignment operator, which is likely to be unexpected by the user.
class MyData {
public:
int operator=(const MyData& a);
MyData& operator=(int a);
private:
int kind;
};
int MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPE
this->kind = a.kind;
return this->kind; // Returns different type,
// meaning chaining equalities like `a = b = c` won't work.
}
class MyData {
public:
int operator=(const MyData& a);
MyData& operator=(int a);
private:
int kind;
};
MyData& MyData::operator=(int a) {
this->kind = a;
return *this;
}
int MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPE
this->kind = a.kind;
return this->kind; // Returns different type, meaning chaining equalities like `a = b = c`
// will invoke `operator=(int)` rather than `operator=(MyData)`.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | High | Yes |
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 may invoke class methods on other object, which is likely to be unexpected by the user.
class MyData {
public:
MyData& operator=(const MyData& a);
private:
int kind;
};
void MyData::permutate() {
this->kind++;
}
MyData& MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS
this->kind = a.kind;
return a; // Returns other object, meaning calling methods like `(a = b).permutate()`
// will have b permutated instead of a.
}
class MyData {
public:
MyData& operator=(const MyData& a);
private:
int kind;
};
const MyData& MyData::operator=(const MyData& a) {
this->kind = a.kind;
return *this; // Correct, no warning.
}
class MyData {
public:
MyData& operator=(const MyData& a);
MyData& operator=(int a);
private:
int kind;
};
const MyData& MyData::operator=(const MyData& a) {
return *this = a.kind; // Correct, assuming another assignment operator
// will return a reference to `this`.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | Yes |
A class has dynamically allocated data members but does not define a copy constructor or an assignment operator. In this case, whenever an object is copied by value, dynamically allocated members will be destroyed as soon as at least one of the copies is deleted, and other copies would maintain and possibly dereference a stale pointer.
In the code below, the following sequence of events leads to a dereference of a dead pointer:
b
is destroyed and thus memory pointed by
both b.p
and a.p
is freed by explicitly
defined destructor;*(a.p)
leads to dereference of a pointer to a
destructed object.class C {
int *p;
public:
C() { p = new int; }
~C() { delete p; }
void setVal(int x) { *p = x; }
int getVal() const { return *p; }
};
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
Related CWEs: CWE1079, CWE772.
This checker finds situations when there exists a base class without a virtual destructor and a deallocation of pointer to this class appears. If an object of the derived class is deallocated by a pointer to base class than undefined behaviour is invoked which usually manifests in a memory leak.
While it is difficult to definitely tell if it is possible for an object of the derived class to be passed by the particular pointer, such class hierarchy might be unhealthy and require attention.
class Base {
public:
Base() {}
~Base() {} // non-virtual destructor of base class
};
class Derived : public Base { // possible inheritor
public:
int* a;
Derived() { a = new int[4]; }
~Derived() { delete[] a; }
};
int main() {
Base* b = new Derived();
delete b; // object deallocation by pointer to base class
return 0;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Check for defects in overriding C++ virtual functions due to missing
const
modifiers, which result in type signature mismatches.
This can lead to calls of incorrect methods from the base class instead
of the derived.
class base {
virtual void foo() const { /* ... */ }
};
class child: public base {
// Warning: `child::foo` is probably meant to override `base::foo` but
// type signatures don't match perfectly.
void foo() { /* ... */ }
};
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
Checks for exception throwing in copy constructor/assignment operator which can lead to resource leaks or undefined behavior.
In the following example if exception std::bad_alloc
is
thrown then some of the Bundle::KeyInfo
class fields such
as impl_->name_
are initialized while some such as
impl_->own_
aren’t. This leads to the assigned object
being partially initialized and having an indeterminate state.
Bundle::KeyInfo& Bundle::KeyInfo::operator = (const Bundle::KeyInfo& k) {
if (this != &k) {
if (impl_->handle_ && impl_->own_)
bundle_keyval_free(const_cast<bundle_keyval_t*>(impl_->handle_));
impl_->handle_ = bundle_keyval_dup(k.impl_->handle_);
impl_->name_ = k.impl_->name_;
if (impl_->handle_ == nullptr)
throw std::bad_alloc();
impl_->own_ = true;
}
return *this;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This checker reports situations when an exception is caught by value in a catch clause.
When an exception of a derived class is thrown and then caught by value in a catch clause as an object of a base class, the exception object is sliced and information may be lost.
To fix this issue, catch exceptions by reference or by pointer.
struct ExceptionBase {
virtual const char *getDescription() const { return ""; }
};
struct MyException : public ExceptionBase {
const char *description;
MyException(const char *descr) : description(descr) {}
const char *getDescription() const { return description; }
};
void f() {
try {
throw MyException("Somebody is doing something nasty!");
} catch (ExceptionBase e) {
// Description provided when throwing the exception is lost
// To fix the problem, declare 'e' like this:
// ExceptionBase &e
const char *descr = e.getDescription();
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
Related CWEs: CWE119.
This checker finds situations where STL iterators are used after being invalidated.
using namespace std;
int last(vector<int>& v) {
vector<int>::iterator it = v.begin();
while (it != v.end())
++it;
return (*it);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
This checker finds situations where STL iterators are used for a different container.
using namespace std;
bool same(vector<int>& v1, vector<int>& v2) {
vector<int>::iterator it = v1.begin();
vector<int>::iterator it2 = v2.begin();
return it == it2;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
The checker finds situations where an object is used after it was moved. Moved-from objects are usually left in unspecified state. Usage of such objects leads to unspecified behavior.
std::string a = "Hello world";
std::string b = std::move(a); // Moved value from `a`. `a` is in unspecified state.
std::cout << a[0]; // Usage of `a`. USE_AFTER_MOVE emitted.
Moved-from object can be reinitialized and used again.
std::vector<int> a = {1, 2, 3, 4};
std::vector<int> b(std::move(a)); // Move from `a`.
a.clear(); // `a` is reinitialized.
std::cout << a.size(); // USE_AFTER_MOVE is NOT emitted.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Average | Yes |
C# | Quality | Minor | Average | Yes |
This rule analyzes method definitions that take a
CancellationToken
, then analyzes all methods invoked in its
body. If any of the method invocations can either accept a
CancellationToken
, or have an overload that takes a
CancellationToken
(given that the types of arguments passed
converge with the types of overload parameters), then the rule suggests
using that option instead to ensure that the cancellation notification
gets propagated to all operations that can listen to it.
The rule will suggest forwarding the c
parameter from
MyMethod
to the MyMethodWithDefault
invocation, because the method defines an optional token parameter:
using System.Threading;
public static class MyTestClass
{
public static void MyMethodWithDefault(CancellationToken ct = default)
{
}
public static void MyMethod(CancellationToken c)
{
MyMethodWithDefault();
}
}
The rule will suggest forwarding the c
parameter from
MyMethod
to the MyMethodWithOverload
invocation, because the method has an overload that takes a
CancellationToken
parameter:
using System.Threading;
public static class MyTestClass
{
public static void MyMethodWithDefault(CancellationToken ct = default)
{
}
public static void MyMethod(CancellationToken c)
{
MyMethodWithDefault();
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Very high | Yes |
C# | Quality | Minor | Very high | Yes |
The result of a function call may differ depending on the current
culture. Specify the culture explicitly to choose the desired behavior.
It’s recommended to use invariant culture for machine-readable output
and to specify CurrentCulture
explicitly for user-visible
output.
float theirVersion = 0f;
if (parts.Length > 1)
theirVersion = float.Parse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Very high | Yes |
C# | Quality | Minor | Very high | Yes |
The result of a ToString()
function call on
floating-point values may differ depending on the current culture. For
example, different decimal separators (.
, ,
)
may be used. Specify the culture explicitly to choose the desired
behavior. It’s recommended to use invariant culture for machine-readable
output and to specify CurrentCulture
explicitly for
user-visible output.
float height;
if (!float.TryParse(kvp.Value, out height) || height < 0 || height > 10)
{
string rawHeight = kvp.Value.Replace(",", ".");
if (!float.TryParse(rawHeight, out height) || height < 0 || height > 10)
height = 1.771488f;
}
av.Data["Value"] = height.ToString();
float height;
if (!float.TryParse(kvp.Value, out height) || height < 0 || height > 10)
{
string rawHeight = kvp.Value.Replace(",", "."); // this may also be removed when the code that generates `kvp.Value` is also fixed to use invariant culture
if (!float.TryParse(rawHeight, out height) || height < 0 || height > 10)
height = 1.771488f;
}
av.Data["Value"] = height.ToString(CultureInfo.InvariantCulture); // use invariant culture to generate consistent string representation
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Unknown | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Critical | Unknown | Yes |
Related CWEs: CWE290, CWE291, CWE293, CWE350, CWE510.
The result of Dns.GetHostByAddress
is used for
security-critical action. DNS names can be easily spoofed or misreported
if an attacker controls the DNS server.
var hostEntry = Dns.GetHostByAddress("10.10.3.134");
if (hostEntry.HostName != "isp-134arm3.intra.ispras.ru")
return;
/* do something */
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Major | High | Yes |
C# | Quality | Major | High | Yes |
The variable was checked for compatibility with given type (which means that it may be incompatible with it by contract) and then cast to it without check.
Here &&
is used instead of ||
, so
the cast will always fail.
if (!(task.Result is ResultResponse) &&
!(((ResultResponse)task.Result).Output is OutputRows))
{
throw new DriverInternalError("Expected rows " + task.Result);
}
Here ||
is used instead of &&
, so
the cast will fail if request["Values"]
is
List<string>
, but request["Names"]
is
not.
if (!(request["Names"] is List<string> || request["Values"] is List<string>))
return FailureResult();
RemoveRequestParamsNotForStorage(request);
List<string> _names = (List<string>)request["Names"];
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Minor | Unknown | Yes |
This checker reports a warning when precedence of the right part
operator is higher than precedence of the left part coalescing operator.
Due to the precedence of ??
is lower than the precedence
of other operators, the user is encouraged to use parentheses to make
the code clearer.
class Program
{
static void Main(string[] args)
{
int? a;
int b = 7, c = 42;
// ...
if (a?.Equals(6) ?? (c <= 69) && b == 6) // This expression is evaluated as a?.Equals(6) ?? ((c <= 69) && b == 6) because of operator precedence
{
// ...
}
// ...
if ((a?.Equals(6) ?? c <= 69) && b == 6) // OK
{
// ...
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Unknown | Yes |
C# | Security | Critical | Unknown | Yes |
Related CWEs: CWE94.
User input is used as code (e.g. compiled or evaluated) or as path to code.
// https://codeql.github.com/codeql-query-help/csharp/cs-code-injection/
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Text;
using System.IO;
using System.Reflection;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Http;
public class CodeInjectionHandler
{
public void ProcessRequest(HttpContext ctx)
{
// Code for calculating tax is provided as unvalidated user input
string taxFormula = ctx.Request.Query["tax_formula"]; // ⚠️〔CODE_INJECTION User input ctx.Request.Query is compiled as code (passed as parameter source to method System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSource(System.CodeDom.Compiler.CompilerParameters, string)) in expression icc.CompileAssemblyFromSource(cp, sourceCode.ToString())〕 // ℹ️〔ctx.Request.Query is the origin of the taint〕 // ℹ️〔ctx.Request.Query is tainted〕 // ℹ️〔taxFormula is tainted〕
// Used to create C#
StringBuilder sourceCode = new StringBuilder("");
sourceCode.Append("public class TaxCalc {\n");
sourceCode.Append("\tpublic int CalculateTax(int value){\n");
sourceCode.Append("\t\treturn " + taxFormula + "; \n"); // ℹ️〔"\t\treturn " + taxFormula is tainted〕 // ℹ️〔"\t\treturn " + taxFormula + "; \n" is tainted〕 // ℹ️〔sourceCode is tainted〕
sourceCode.Append("\t}\n");
sourceCode.Append("}\n");
// BAD: This compiles the sourceCode, containing unvalidated user input
CSharpCodeProvider c = new CSharpCodeProvider();
ICodeCompiler icc = c.CreateCompiler();
CompilerParameters cp = new CompilerParameters();
CompilerResults cr = icc.CompileAssemblyFromSource(cp, sourceCode.ToString()); // ℹ️〔sourceCode.ToString() is tainted〕 // ℹ️〔sourceCode.ToString() is passed into sink in expression icc.CompileAssemblyFromSource(cp, sourceCode.ToString())〕
// Compiled input is loaded, and an instance of the class is constructed
System.Reflection.Assembly a = cr.CompiledAssembly;
object taxCalc = a.CreateInstance("TaxCalc");
// Unsafe code is executed
Type taxCalcType = taxCalc.GetType();
MethodInfo mi = taxCalcType.GetMethod("CalculateTax");
int value = int.Parse(ctx.Request.Query["value"]);
int s = (int)mi.Invoke(taxCalc, new object[] { value });
// Result is returned to the user
using var sw = new StreamWriter(ctx.Response.Body);
sw.Write("Tax value is: " + s);
}
}
A possible fix is to parse user input and construct the formula from a predefined set of allowed parts, rather than create it directly from user input.
An alternative fix is to validate the user input before using it as code, but the warning will still be emitted (and it would become false positive).
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Minor | Unknown | Yes |
Related CWEs: CWE481.
An assignment operator (=
) is used inside of condition
expression. It is probably a typo for equality operator
(==
).
Although using =
inside of condition is not necessarily
a bug, this warning is useful for immediate bug fixing during
coding.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Unknown | Yes |
C# | Security | Critical | Unknown | Yes |
Related CWEs: CWE15.
User input is concatenated to database connection string, which
allows the attacker to override connection parameters.
DbConnectionStringBuilder
should be used to create the
connection string.
using System.Web;
class Controller
{
public void ProcessRequest(HttpRequest request)
{
using var connection = new Microsoft.Data.Sqlite.SqliteConnection("Server=(local);Integrated Security=SSPI;Initial Catalog=AdventureWorks;Password=" + request.Params["password"]); // ⚠️〔CONNSTR_INJECTION User input request.Params is used as database connection string (parameter connectionString of method Microsoft.Data.Sqlite.SqliteConnection.SqliteConnection(string)) in expression new Microsoft.Data.Sqlite.SqliteConnection("Server=(local);Integrated Security=SSPI;Initial Catalog=AdventureWorks;Password=" + request.Params["password"])〕 // ℹ️〔request.Params is the origin of the taint〕 // ℹ️〔request.Params is tainted〕 // ℹ️〔"Server=(local);Integrated Security=SSPI;Initial Catalog=AdventureWorks;Password=" + request.Params["password"] is tainted〕 // ℹ️〔"Server=(local);Integrated Security=SSPI;Initial Catalog=AdventureWorks;Password=" + request.Params["password"] is passed into sink in expression new Microsoft.Data.Sqlite.SqliteConnection("Server=(local);Integrated Security=SSPI;Initial Catalog=AdventureWorks;Password=" + request.Params["password"])〕
}
}
using System.Web;
class Controller
{
public void ProcessRequestGood(HttpRequest request)
{
var csBuilder = new Microsoft.Data.Sqlite.SqliteConnectionStringBuilder("Server=(local);Integrated Security=SSPI;Initial Catalog=AdventureWorks");
csBuilder.Password = request.Params["password"];
using var connection = new Microsoft.Data.Sqlite.SqliteConnection(csBuilder.ConnectionString);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Unknown | Yes |
C# | Security | Critical | Unknown | Yes |
Related CWEs: CWE601.
User input is used as the redirect URI in an HTTP response. It may be used to redirect the user to a malicious website.
Here a user can be redirected to http://malicious.com
by
following a http://trusted.com/something?site=malicious.com
URL sent by the attackers.
public class RedirectTest : ApiController
{
public HttpResponseMessage GetSomething(string site)
{
var response = Request.CreateResponse<object>(HttpStatusCode.Moved, null);
var uri = new Uri(site);
response.Headers.Location = uri;
return response;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Major | High | Yes |
C# | Quality | Major | High | Yes |
The result of as
operator is dereferenced without
check.
A warning can have any combination
of these subtypes in the following order:
.ARGUMENT.INSTANT
.
var auctioneer = chr.Map.GetObject(auctioneerId) as NPC;
AuctionMgr.Instance.AuctionHello(chr, auctioneer);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Major | Very high | Yes |
C# | Quality | Major | Very high | Yes |
The result of conditional access operator (?.
) is
dereferenced without check.
A warning can have
any combination of these subtypes in the following order:
.ARGUMENT.INSTANT
.
Size2D windowSize = window?.Size;
Rectangle ret = new Rectangle(0, 0, windowSize.Width, windowSize.Height);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Average | Yes |
C# | Quality | Normal | Average | Yes |
It is a union of two checkers. 1. Without [STATISTICAL]
prefix in the warning message: null value, which is returned from a
function, is dereferenced.
internal static class DependencyService
{
static void Initialize()
{
if (s_initialized)
{
return;
}
Assembly[] assemblies = Device.GetAssemblies();
if (Tizen.NUI.Binding.Internals.Registrar.ExtraAssemblies != null)
{
assemblies = assemblies.Union(Tizen.NUI.Binding.Internals.Registrar.ExtraAssemblies).ToArray();
}
Initialize(assemblies);
}
}
internal static class Device
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static Assembly[] GetAssemblies()
{
return null;
}
}
[STATISTICAL]
prefix in the warning message: the
result of a function, which may return a null value and is usually
(CSCC.DEREF_OF_NULL__RET__STAT_BIAS
+ checked usages count
< CSCC.DEREF_OF_NULL__RET__STAT_THRESHOLD
% * total
usages count) used with a null check, is dereferenced without
check.using System;
class App
{
public object canReturnNull(bool condition)
{
if (condition) return null;
return new object();
}
void test1(bool condition)
{
Console.WriteLine(canReturnNull(condition).ToString()); // DEREF_OF_NULL.RET.USER.PROC [STATISTICAL] Value canReturnNull(condition), which is result of method invocation with possible null return value, is dereferenced in method call canReturnNull(condition).ToString()
}
void forstat(bool c1, bool c2, bool c3, bool c4)
{
Console.WriteLine(canReturnNull(c1).ToString()); // DEREF_OF_NULL.RET.USER.PROC.STATISTICAL Value canReturnNull(c1), which is result of method invocation with possible null return value, is dereferenced in method call canReturnNull(c1).ToString()
Console.WriteLine(canReturnNull(c2).ToString()); // DEREF_OF_NULL.RET.USER.PROC.STATISTICAL Value canReturnNull(c2), which is result of method invocation with possible null return value, is dereferenced in method call canReturnNull(c2).ToString()
Console.WriteLine(canReturnNull(c3).ToString()); // DEREF_OF_NULL.RET.USER.PROC.STATISTICAL Value canReturnNull(c3), which is result of method invocation with possible null return value, is dereferenced in method call canReturnNull(c3).ToString()
object obj = canReturnNull(c4);
if (obj != null)
{
Console.WriteLine(canReturnNull(c4).ToString());
}
}
}
Note: .PROC
has no meaning in C# – it is a part of a
single suffix .RET.USER.PROC
. It doesn’t mean that the
value is passed into a function which dereferences it.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | No |
C# | Quality | Minor | Unknown | No |
Two enum
members are equal (have the same numeric
value).
In this example CantDoThatRightNow
has the same value
173 as TooManySockets
, but should be different.
public enum SpellFailedReason : byte
{
/* ... */
NotOnGround = 171,
CustomError = 172,
TooManySockets = 173,
CantDoThatRightNow = 173,
InvalidGlyph = 175,
UniqueGlyph = 176,
/* ... */
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | No |
C# | CodingStyle | Minor | Unknown | No |
The interface is empty.
Although it’s not an error, it’s considered an antipattern.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | No |
C/C++ | Quality | Major | Unknown | No |
C# | Quality | Normal | Unknown | No |
Floating-point numbers are compared for exact equality.
Floating point mathematics is not precise, so instead of comparing
floating-point values for exact equality, they should be compared with
precision. E.g. Math.Abs(a - b) < 0.0001
instead of
a == b
.
public static Boolean operator ==(ComplexNumber c1, ComplexNumber c2)
{
bool bEqual = false;
if ((c1._realPart == c2._realPart) &&
(c1._imaginaryPart == c2._imaginaryPart))
bEqual = true;
return bEqual;
}
The issue is reported with a FLOATING_COMPARE.CMP_EQ
warning subtype if the code uses <=
or
>=
operator.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
Related CWEs: CWE482.
Using of comparison operator ==
where assignment
operator =
is expected.
if ((isZero == (zeroOrOne == 0)) == true) // Should be (isZero = (zeroOrOne == 0))〕
if ((isZero == (zeroOrOne > 0)) == true) // Should be (isZero = (zeroOrOne > 0))
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Major | Average | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Major | Average | Yes |
Related CWEs: CWE798.
A hardcoded constant user name is used.
In the following test constant username "TBD"
is used to
configure a SSH session:
string sshUser = "TBD";
string sshPassword = "TBD";
_sshClient = new Renci.SshNet.SshClient(
sshHost, sshPort, sshUser, sshPassword);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Minor | Unknown | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Minor | Unknown | Yes |
Related CWEs: CWE330, CWE336, CWE338.
The result of Random.Next()
is passed into
security-critical function, such as encryption key generation or
filename generation.
In this example the result of Random.Next()
is passed
into SetPasswd
method:
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | No |
C# | Quality | Minor | Unknown | No |
A number of different methods share the same implementation.
public static object GetCharArrayValue(_Array arr) {
return arr.NativeType.GetValue(arr._memHolder, arr, 0, false);
}
public static object GetWCharArrayValue(_Array arr) {
return arr.NativeType.GetValue(arr._memHolder, arr, 0, false);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Minor | Very high | Yes |
Switch statement doesn’t handle all possible argument values.
public enum TraceEventKind {
FrameEnter,
FrameExit,
ThreadExit,
TracePoint,
Exception,
ExceptionUnwind
}
/* ... */
switch (kind) {
case Debugging.TraceEventKind.FrameEnter: traceEvent = "call"; break;
case Debugging.TraceEventKind.TracePoint: traceEvent = "line"; break;
case Debugging.TraceEventKind.Exception:
traceEvent = "exception";
object pyException = PythonExceptions.ToPython((Exception)payload);
object pyType = ((IPythonObject)pyException).PythonType;
args = PythonTuple.MakeTuple(pyType, pyException, null);
break;
case Debugging.TraceEventKind.FrameExit:
traceEvent = "return";
args = payload;
break;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | Yes |
Static field is used before its initialization.
public static readonly WeakReference WeakMissingConstant = new WeakReference(
StrongMissingConstant);
private static readonly object StrongMissingConstant = new object();
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | No |
The loop contains unconditional break
or
return
, so it will always have only one iteration.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | High | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Critical | High | Yes |
Related CWEs: CWE321, CWE329, CWE798.
A hardcoded constant encryption key is used.
In this code hardcoded encryption key Key
and
initialization vector IV
are used to set up AES
encryption:
byte[] Key = { 0x33, 0x45, 0x67 };
byte[] IV = { 0x01, 0x05, 0x04 };
using (Aes aesAlg = Aes.Create()) {
aesAlg.Key = Key;
aesAlg.IV = IV;
/* ... */
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Unknown | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Critical | Unknown | Yes |
Related CWEs: CWE90.
User-provided data are used to form a LDAP query without neutralization of special elements. It can be used by an attacker to execute arbitrary LDAP query.
In the following code sample a user-provided string
fullName
is passed into method
GetUserManagerInfoByFullName
:
[WebMethod]
public static UserManagerInfo GetUserManagerInfoByFullName(
string fullName) {
return DirectoryServices
.GetUserManagerInfoByFullName(fullName);
}
where it is passed into method GetUserByFullName
:
which uses it to form a LDAP query
directorySearch.Filter
by string concatenation:
var directorySearch = new DirectorySearcher(new DirectoryEntry("LDAP://" +
_ldapconn.UserSearchBase));
directorySearch.Filter = "(&(objectClass=user)(cn=" + userName + "))";
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
This checker detects counterintuitive logical not operations with nullable bool.
class TestObject
{
public bool IsVisited = false;
}
var visited = new List<TestObject>();
public void bad(TestObject obj)
{
if (!obj?.IsVisited == true) // LIFTED_LOGICAL_NOT
return;
visited.Add(obj);
}
In the example function is assumed to return unless
obj?.IsVisited
is true. However, if obj
is
null, condition expression is false so that null value is added to list
visited
which may result in error.
class TestObject
{
public bool IsVisited = false;
}
var visited = new List<TestObject>();
public void good(TestObject obj)
{
if (obj?.IsVisited != true)
return;
visited.Add(obj);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Major | Average | Yes |
C# | Quality | Major | Average | Yes |
The checker detects cases when object used in lock statement or as an
argument of Monitor.Enter()
gets reassigned inside of the
critical section. Such reassignments must be avoided because they can
lead to synchronization violation and following race condition. Changed
value of lock object allows another thread to enter critical section
simultaneously.
In this code, object m_scene
is used in lock as lock
object and changed in assignment m_scene = null
:
public virtual void RemoveRegion(Scene scene)
{
/*...*/
lock (m_scene)
{
m_Enabled = false;
RemoveHandlers();
m_scene = null;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Normal | Very low | Yes |
C# | Security | Normal | Very low | Yes |
Related CWEs: CWE117.
User-provided data are written to a log. It can lead to injection of fake log messages if the input contains a line break.
StreamReader content = new StreamReader(resp.GetResponseStream());
m_log.ErrorFormat("[Concierge] response from {0} content: {1}", bs.Uri, content.ReadToEnd());
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Minor | Unknown | Yes |
This checker locates code snippets where the user may
have incorrectly prioritized the logical operators
&&
and ||
. Due to the precedence
of &&
being higher than the precedence
of ||
, the user is encouraged to use parentheses to make
the code clearer.
class Program
{
// ...
bool ReturnBool(bool a, bool b, bool c)
{
if (a.Equals(42) || (a.GetType().IsEquivalentTo(typeof(bool))) && c.Equals(17)) { do_something(); }
// This expression is evaluated as a.Equals(42) || ((a.GetType().IsEquivalentTo(typeof(bool))) && c.Equals(17)) because of operator precedence
// Possible fix: (a.Equals(42) || (a.GetType().IsEquivalentTo(typeof(bool)))) && c.Equals(17)
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | No |
C# | Quality | Minor | Unknown | No |
Common constants are defined explicitly, instead of using library constants.
In the following code sample 6.28318531
should be
changed to 2 * Math.PI
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Major | Average | Yes |
C# | Security | Major | Average | Yes |
Related CWEs: CWE256, CWE261, CWE311, CWE313, CWE314, CWE315, CWE319, CWE523.
Sensitive user data (for example, passwords) are output into logs, displayed on a webpage or are transmitted over the network.
In this code, the result of getPassword
function is
displayed on a web page through labelPassword
control:
labelPassword.Text = @"Security Question Challenge Successfully Completed!
<br/>Your password is: " + getPassword(txtEmail.Text);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Minor | Unknown | No |
C# | Security | Critical | Unknown | Yes |
Related CWEs: CWE759.
This checker detects usage of one-way hash without a salt. Hashing data without a salt makes hashed data vulnerable to dictionary attacks such as rainbow tables, containing pre-computed hashes for various inputs.
In this code sample the salt is null
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | No |
C# | Quality | Normal | Unknown | No |
The exception is created, but throw
operator is
missing.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | No |
C# | Quality | Normal | Unknown | No |
The loop condition may be changed only in another thread, and the
field that should be changed doesn’t have volatile
modifier, so the field access may be optimized out.
In the following code sample the loop doesn’t modify fields used in
its condition, so the DoneCount
may be retrieved only once
because of optimizations, making the loop infinite. The field needs a
volatile
modifier to prevent such optimization.
class DictThreadGlobalState {
public int DoneCount;
public List<Thread> Threads = new List<Thread>();
/* ... */
}
/* ... */
while (globalState.DoneCount != globalState.Threads.Count) {
// Wait for threads to get back to start point.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | High | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Critical | High | Yes |
A hardcoded constant password is used.
In the following test constant password "TBD"
is used to
configure an SSH session:
string sshPassword = "TBD";
_sshClient = new Renci.SshNet.SshClient(
sshHost, sshPort, sshUser, sshPassword);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Minor | Unknown | No |
Related CWEs: CWE22, CWE23, CWE36.
A path is used to open a file or directory, but its full path wasn’t
checked for belonging to certain directory. It makes possible for the
attacker to escape that directory by specifying elements like
..
or /
.
In this code sample path x
is used to create a file, but
function Check
checked x
instead of
Path.GetFullPath(x)
, so an attacker can specify path like
/Users/foo/../../Library/LaunchDaemons/daemon.plist
to
create file outside user’s folder.
private static bool Check(string x) => x.StartsWith("/Users/foo");
public static FileStream OpenFile(string x) {
if (!Check(x))
return;
return File.Open(x, FileMode.Create);
}
This checker doesn’t check whether the path is actually user-supplied. It is checked by RESOURCE_INJECTION.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
Parentheses are probably missing in a pattern matching expression,
because not
has higher precedence than or
.
if (node.ReturnedValue is not ILocalReferenceOperation or IParameterReferenceOperation or IMemberReferenceOperation) // bug: `not` is applied only to ILocalReferenceOperation
return;
if (node.ReturnedValue is not (ILocalReferenceOperation or IParameterReferenceOperation or IMemberReferenceOperation)) // fix: add parentheses around (ILocalReferenceOperation or IParameterReferenceOperation or IMemberReferenceOperation)
return;
if (node.ReturnedValue is (not ILocalReferenceOperation) or IParameterReferenceOperation or IMemberReferenceOperation) // suppress: add parentheses around (not ILocalReferenceOperation) to emphasize the programmer's intent
return;
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Major | Unknown | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Major | Unknown | Yes |
Related CWEs: CWE114.
User-provided data are used to locate a dynamic library. It can be used by an attacker to load a modified library containing malicious code.
In this code libName
takes user input and is passed into
native dlopen
function which loads a dynamic library on
Unix systems:
[DllImport("libdl.so", EntryPoint = "dlopen")]
protected static extern IntPtr GetModuleHandle(string filename, int flags);
[WebMethod]
public void Load(string libName) {
IntPtr moduleHandle = GetModuleHandle(libName, 2);
}
In this code textBox1
takes user input, and its text
value is passed into native SetDllDirectory
function which
changes library search path on Windows:
public TextBox textBox1;
[DllImport("Kernel32.dll", EntryPoint = "SetDllDirectory")]
protected static extern bool SetDir(string path);
[DllImport("Kernel32.dll")]
protected static extern IntPtr LoadLibrary(string filename);
public void Load() {
SetDir(textBox1.Text);
LoadLibrary("AppPlugin.dll");
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
This checker detects declarations of array fields with given values that the programmer intended to be immutable, but they are in fact mutable.
In this example field ConstantString_000Escape_Chars
can
not be reassigned, because it is readonly, but the array element values
can be reassigned using expressions like
ConstantString_000Escape_Chars[i] = value
:
public static readonly char[] ConstantString_000Escape_Chars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', 'B', 'E', 'F' };
ImmutableArray
should be used to make the elements
immutable:
public static readonly ImmutableArray<char> ConstantString_000Escape_Chars = ImmutableArray.CreateRange(new[] { '0', '1', '2', '3', '4', '5', '6', '7', 'B', 'E', 'F' });
public static readonly ImmutableArray<char> ConstantString_000Escape_Chars = new[] { '0', '1', '2', '3', '4', '5', '6', '7', 'B', 'E', 'F' }.ToImmutableArray();
public static readonly ImmutableArray<char> ConstantString_000Escape_Chars = ['0', '1', '2', '3', '4', '5', '6', '7', 'B', 'E', 'F']; // .NET 8.0 or higher
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | No |
C# | Quality | Minor | Unknown | No |
The comparison contains both integer and floating point values.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Low | Yes |
C# | Security | Critical | Low | Yes |
Related CWEs: CWE80, CWE81, CWE83.
User-provided data are used as a part of a web page. It can lead to
cross-site scripting if the entered data contain JavaScript code in a
<script></script>
tag.
public partial class ReflectedXSS : System.Web.UI.Page
{
private IDbProvider du = Settings.CurrentDbProvider;
protected void Page_Load(object sender, EventArgs e)
{
if (Request["city"] != null)
LoadCity(Request["city"]);
}
void LoadCity (String city)
{
DataSet ds = du.GetOffice(city);
lblOutput.Text = "Here are the details for our " + city + " Office"; // REFLECTED_XSS
dtlView.DataSource = ds.Tables[0];
dtlView.DataBind();
}
void FixedLoadCity (String city)
{
DataSet ds = du.GetOffice(city);
lblOutput.Text = "Here are the details for our " + Server.HtmlEncode(city) + " Office"; // HtmlEncode transforms malicious input
dtlView.DataSource = ds.Tables[0];
dtlView.DataBind();
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Average | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Critical | Average | Yes |
Related CWEs: CWE23, CWE36, CWE99.
User-provided data are used as a system resource identifier (file path, host name, port number). An attacker can use it, for example, to read or modify arbitrary files, including server configs.
In this code, filename
is read from HTTP GET request
string and passed into ResponseFile
method:
string filename = Request.QueryString["filename"];
if(filename != null)
{
try
{
ResponseFile(Request, Response, filename,
MapPath("~/Downloads/" + filename), 100);
/* ... */
}
/* ... */
}
which responds with full contents of the file:
public static bool ResponseFile(
HttpRequest _Request,
HttpResponse _Response,
string _fileName,
string _fullPath,
long _speed)
{
try
{
FileStream myFile = new FileStream(
_fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
BinaryReader br = new BinaryReader(myFile);
try
{
/* ... */
long fileLength = myFile.Length;
long startBytes = 0;
int pack = 10240; //10K bytes
/* ... */
br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
int maxCount = (int)
Math.Floor((double)((fileLength - startBytes) / pack)) + 1;
for (int i = 0; i < maxCount; i++)
{
if (_Response.IsClientConnected)
{
_Response.BinaryWrite(br.ReadBytes(pack));
}
else
{
i = maxCount;
}
}
}
/* ... */
}
}
It allows to specify path like "../Web.config"
to get
the application configuration file contents.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
The object implementing IDisposable
interface is
returned inside of using
block. Returned object will be
disposed.
public static MemoryStream SerializeToStream(object o)
{
using (var stream = new MemoryStream())
{
Formatter.Serialize(stream, o);
return stream;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | No |
C# | Quality | Normal | Unknown | No |
The method always returns the same value.
public bool Matches(byte[] bytes)
{
if (CheckValid())
return false;
for (var i = 0; i < m_MaskBytes.Length; i++)
{
var bte = bytes[i];
if (BanMgr.Matches(m_MaskBytes[i], bte))
return false;
}
return false;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | Yes |
Related CWEs: CWE398.
A variable or field is assigned to itself.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Unknown | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Critical | Unknown | Yes |
Related CWEs: CWE15.
Modification of Windows registry and other system settings with user data.
In this code values of txtPreferredDNS
and
txtAlternateDNS
text boxes are passed into
SetDNS
method:
string primaryDNS = txtPreferredDNS.Text.Trim();
string secondaryDNS = txtAlternateDNS.Text.Trim();
if (Helpers.IsValidIPAddress(primaryDNS) &&
Helpers.IsValidIPAddress(secondaryDNS))
result = adapter.SetDNS(primaryDNS, secondaryDNS);
which uses ManagementBaseObject
to update system
settings with user-supplied data:
public uint SetDNS(string primary, string secondary) {
using (ManagementBaseObject parameters =
adapter.GetMethodParameters("SetDNSServerSearchOrder")) {
if (primary == null || secondary == null)
parameters["DNSServerSearchOrder"] = null;
else
parameters["DNSServerSearchOrder"] =
new string[] { primary, secondary };
using (ManagementBaseObject result =
adapter.InvokeMethod("SetDNSServerSearchOrder", parameters, null))
return (uint)result["ReturnValue"];
}
}
In the following code sample user-provided values are passed to
SetValue
method that updates the value of Windows registry
key:
[WebMethod]
public void f(string keyName, string valueName, string value) {
Registry.SetValue(keyName, valueName, value);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Major | Very low | Yes |
C# | Security | Major | Very low | Yes |
Related CWEs: CWE80, CWE81, CWE83.
Data from a database or a file are used as a part of a web page. It
can lead to cross-site scripting if the data may contain JavaScript code
in a <script></script>
tag.
using (SqlDataReader dr = command.ExecuteReader())
{
data = dr.GetString(1);
/* POTENTIAL FLAW: Display of data in web page after using Replace() to remove script tags, which will still allow XSS with strings like <scr<script>ipt> (CWE 182: Collapse of Data into Unsafe Value) */
resp.Write("<br>Bad(): data = " + data.Replace("(<script>)", ""));
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | High | Yes |
C/C++ | Security | Critical | Unknown | No |
C# | Security | Critical | High | Yes |
Related CWEs: CWE89.
User-provided data are used to form an SQL query without neutralization of special elements. It can be used by an attacker to execute arbitrary SQL query.
In the following code sample a user-provided string
Request.Cookies["customerNumber"].Value
is passed into
GetCustomerEmail
method:
string customerNumber = Request.Cookies["customerNumber"].Value;
string email = du.GetCustomerEmail(customerNumber);
which uses it to form an SQL query sql
by string
concatenation:
string sql = "select email from CustomerLogin where customerNumber = "
+ customerNumber;
SqliteCommand command = new SqliteCommand(sql, connection);
output = command.ExecuteScalar().ToString();
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | No |
C#-only subtype of SIMILAR_BRANCHES where branches differ only by comments.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | No |
C#-only subtype of SIMILAR_BRANCHES
where an if
-else
chain or a
switch
statement contains more than one pair of identical
branches.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | No |
C#-only subtype of SIMILAR_BRANCHES
where a case
section has the same body as the
default
section.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Performance | Normal | Unknown | No |
C# | Performance | Normal | Unknown | No |
String concatenation in loop is inefficient, because it creates a new
string at each iteration. StringBuilder
or
string.Join
should be used instead.
var pluginStates = string.Format(
"plugin states: ({0} in total)", this.otherStates.Length);
foreach (var state in this.otherStates)
{
var str = state.ToString();
pluginStates += Environment.NewLine + str;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | No |
C# | Quality | Normal | Unknown | No |
The format string uses more format items than supplied or does not use some supplied arguments.
Here the format string contains two format items, but only one argument was provided (and it really should be the third format item).
string.Format(
"[AGENT INVENTORY]: Error in SendInventoryAsync() for {0} with folder ID {1}. Exception ", e));
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Low | Yes |
C# | Quality | Minor | Low | Yes |
C#-specific subtype of UNREACHABLE_CODE.EXCEPTION for
situations where a return
statement is unreachable because
the previous operation always throws an exception. Some of such warnings
are useless because the exception is desired behavior and the
return
is required by the compiler.
public static BluetoothOppServer StartServer(string FilePath)
{
if (BluetoothAdapter.IsBluetoothEnabled && Globals.IsInitialize)
{
_instance = new BluetoothOppServer();
int ret = _impl.StartServer(FilePath);
if (ret != (int)BluetoothError.None)
BluetoothErrorFactory.ThrowBluetoothException(ret);
return _instance;
}
else
BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NotEnabled);
return null;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | High | Yes |
C# | Quality | Minor | High | Yes |
C#-specific subtype of UNREACHABLE_CODE for situations where a
throw
statement or expression is unreachable.
Here platform.IsValid()
ensures that
platform
is a valid enumeration value, and
switch
handles all possible enumeration values, so
default
is unreachable. However, the default
case should not be removed to detect possible future errors if a new
platform is added and IsValid
is updated but
switch
is not.
if (!platform.IsValid())
platform = Platform.AnyCpu;
switch (platform)
{
case Platform.Arm64:
machine = (Machine)0xAA64; //Machine.Arm64; https://github.com/dotnet/roslyn/issues/25185
break;
case Platform.Arm:
machine = Machine.ArmThumb2;
break;
case Platform.X64:
machine = Machine.Amd64;
break;
case Platform.Itanium:
machine = Machine.IA64;
break;
case Platform.X86:
machine = Machine.I386;
break;
case Platform.AnyCpu:
case Platform.AnyCpu32BitPreferred:
machine = Machine.Unknown;
break;
default:
throw ExceptionUtilities.UnexpectedValue(platform);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
C#-specific subtype of UNREACHABLE_CODE.EXCEPTION for
situations where a throw
statement or expression is
unreachable because the previous operation always throws an
exception.
Here the throw
statement on line 3 is unreachable,
because function CreateInvalidDataException
always throws
an exception instead of just creating and returning it.
if (closureOrdinal >= closureCount)
{
throw CreateInvalidDataException(compressedLambdaMap, blobReader.Offset);
}
private static InvalidDataException CreateInvalidDataException(ImmutableArray<byte> data, int offset)
{
const int maxReportedLength = 1024;
int start = Math.Max(0, offset - maxReportedLength / 2);
int end = Math.Min(data.Length, offset + maxReportedLength / 2);
byte[] left = new byte[offset - start];
data.CopyTo(start, left, 0, left.Length);
byte[] right = new byte[end - offset];
data.CopyTo(offset, right, 0, right.Length);
throw new InvalidDataException(string.Format(CodeAnalysisResources.InvalidDataAtOffset,
offset, (start != 0) ? "..." : "", BitConverter.ToString(left), BitConverter.ToString(right), (end != data.Length) ? "..." : ""));
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
C#-specific subtype of UNREACHABLE_CODE for situations where a
code block is unreachable because the condition is explicitly specified
as false
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Low | No |
C/C++ | Quality | Normal | Low | No |
Go | Quality | Normal | Low | No |
Python | Quality | Normal | Low | No |
Related CWEs: CWE561.
Subtype of UNREACHABLE_CODE for cases where condition uses result of some function. Usually caller code should not rely on implementation details. Called function may be changed later. That’s why in many cases it is not necessary to fix code for such warnings.
static int func(int a) {
return 0;
}
int foo(int a, int b) {
int z = func(a)==0? -1 : b; //UNREACHABLE_CODE.RET
return z;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Detects useless catch which intercept Java subtypes of class “Exception”.
abstract class SomeClass {
static class MyException extends Exception {
}
static class SubException extends MyException {
}
public abstract void couldEmitDerived() throws SubException;
public void example() {
try {
couldEmitDerived();
} catch (SubException e) {
System.err.println("SubException");
} catch (MyException e) { //useless
System.err.println("MyException");
}
}
}
Here function “couldEmitDerived” can throw only derived type “SubException”. So catching of base type “MyException” is useless.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Security | Critical | Unknown | No |
This checker detects serialization of fields which can be used for code or data injection, such as delegates or unmanaged references.
In this code sample the event’s delegate field is serialized, so changing it in the serialized object can lead to calling some other event handler.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Minor | Unknown | Yes |
C# | Security | Minor | Unknown | Yes |
Related CWEs: CWE470.
Checker detects if untrusted input is used as a name of a type to create an object or as a method name to invoke it using reflection.
public void bad()
{
using (StreamReader sr = new StreamReader("data.txt")) // user data
{
var data = sr.ReadLine();
var container = Activator.CreateInstance(null, data); // UNSAFE_REFLECTION: data is used to create an object
}
}
In the example data
, which is result of the reading from
untrusted file, is used to instantiate object using reflection.
public void good()
{
using (StreamReader sr = new StreamReader("data.txt")) // user data
{
var data = sr.ReadLine();
switch (data)
{
case "SafeType":
var container = new SafeType();
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Minor | Unknown | Yes |
C# | Security | Minor | Unknown | Yes |
Related CWEs: CWE789.
Checker detects if untrusted data may affect the size of the data structures, which may lead to memory leak.
In the example data
, which is the result of the reading
from an untrusted file, is parsed as integer value and may be used by
the user to control the size of the array without any restrictions.
void bad() {
StreamReader sr = new StreamReader("data.txt"); // User's data
string data = sr.ReadLine();
int arraySize = int.Parse(data);
var array = new int[arraySize]; // UNCONTROLLED_MEM_ALLOC
}
void good() {
const int MaxSizeOfArray = 100; // A constant that limits the maximum size of the array
StreamReader sr = new StreamReader("data.txt"); // User's data
string data = sr.ReadLine();
int arraySize = int.Parse(data);
if (arraySize > MaxSizeOfArray || arraySize < 0) return; // Array size control
var array = new int[arraySize];
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Minor | Unknown | Yes |
C# | Security | Minor | Unknown | Yes |
Checker detects if untrusted data may affect number of loop iterations or delay time.
public void bad()
{
using (StreamReader sr = new StreamReader("data.txt")) // user data
{
var data = sr.ReadLine();
var loopLimit = int.Parse(data)
for (int i = 0; i < loopLimit; i++) // UNCONTROLLED_RESOURCE_CONSUMPTION
{
..
}
}
}
In the example data
, which is result of the reading from
untrusted file, is parsed as integer value and may be used by user to
control number of loop iteration without any restrictions.
public void good()
{
using (StreamReader sr = new StreamReader("data.txt")) // user data
{
var data = sr.ReadLine();
var loopLimit = int.Parse(data)
if (loopLimit > MaxNumberOfIterations || loopLimit < 0)
return;
for (int i = 0; i < loopLimit; i++)
{
..
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Average | Yes |
C# | Quality | Normal | Average | Yes |
A disposed resource is used. Method Dispose()
is mostly
used to release unmanaged resource, such as memory, file descriptor,
etc. Usage of disposed resource will throw exception, ex.
ObjectDisposedException
, or may cause another runtime
error.
class Test
{
public abstract class TestClass : IDisposable
{
public abstract void Abort();
public void Dispose() { }
public void Use() { }
}
public abstract class TestClass1 : TestClass
{
public override void Abort()
{
Dispose();
}
}
public abstract class TestClass2 : TestClass
{
internal TestClass elem;
public void func()
{
if (elem != null)
{
elem.Abort();
elem.Use();
}
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
The result of non-void method, which has no side effects, is unused.
In this example the call of OrderByDescending
is
useless, because it returns an ordered enumerable, which is ignored, but
does not modify the existing collection.
List<BSPhysObject> orderedPrims = new List<BSPhysObject>(PhysObjects.Values);
orderedPrims.OrderByDescending(p => p.CollisionScore);
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | No |
The expression value may have value type, but is compared with
null
. A value type cannot contain null
value.
Here aliasSymbols
is
ImmutableArray<IAliasSymbol>
, which is a value type.
It should be compared with default
.
var aliasSymbols = await GetAliasSymbolsAsync(document, semanticModel,
nonAliasReferences, cancellationToken).ConfigureAwait(false);
if (aliasSymbols == null)
{
return ImmutableArray<ReferenceLocation>.Empty;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | No |
C# | Quality | Minor | Unknown | No |
Virtual method of object is called inside its constructor. It may lead to inconsistent state, because the virtual method may be redefined in the child and may use some child-specific data that is not initialized yet.
In the following code sample virtual method Commit
is
called inside the constructor of InvariantSubroutine
class.
public InvariantSubroutine(
MethodCache<
Local, Parameter, Type, Method, Field, Property,
Event, Attribute, Assembly> methodCache,
Subroutine inherited, Type associatedType)
: base(methodCache)
{
Contract.Requires(methodCache != null);
this.AddSuccessor(this.Entry, "entry", this.Exit);
this.AddBaseInvariant(this.Entry, this.Exit, inherited);
this.Commit();
}
Subtype .PROPERTY
means that a virtual property is used in constructor.
Subtype .POTENTIAL
means that the method has no known overrides.
Subtype .OVERRIDE
means that the method overrides a method from the base class.
A
warning can have any combination of these subtypes in the following
order: .PROPERTY.OVERRIDE.POTENTIAL
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Normal | Unknown | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Normal | Unknown | Yes |
This checker detects usage of constant salt in encryption schemes. Constant or short salt makes the data vulnerable to brute force-based attacks.
In this code sample the salt is constant.
string salt = "a45hf7yke59vn24923f";
var pwhash = HashAlgorithm.Create("MD5").ComputeHash(
Encoding.UTF8.GetBytes(password + salt));
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Major | High | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Major | High | Yes |
Obsolete cryptographic algorithms (MD5, SHA1, DES, Triple DES, RC2, RIPEMD160, Rijndael, DSA) are used for encryption.
In the following code sample SHA1 algorithm is used, which is vulnerable. SHA256 or SHA512 should have been used instead.
if (encrypted == 0) {
salt = new byte[0];
} else {
salt = new byte[16];
new RNGCryptoServiceProvider().GetBytes(salt);
byte[] key = Encoding.ASCII.GetBytes(password);
SHA1 sha1 = SHA1.Create();
sha1.TransformBlock(salt, 0, salt.Length, salt, 0);
sha1.TransformFinalBlock(key, 0, key.Length);
key = new byte[16];
Array.Copy(sha1.Hash, 0, key, 0, key.Length);
ICryptoTransform rc4 = RC4.Create().CreateEncryptor(key, null);
rc4.TransformBlock(buffer, 8, buffer.Length - 8, buffer, 8);
rc4.Dispose();
sha1.Clear();
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Major | Unknown | Yes |
C/C++ | Security | Minor | Unknown | No |
C# | Security | Major | Unknown | Yes |
Related CWEs: CWE326.
This checker detects usage of encryption schemes not strong enough for the level of protection required.
In this code sample the salt size is insufficient.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C# | Quality | Normal | Unknown | No |
After some value is retrieved by as
operator, the
original value is checked for null
instead of the new
one.
var right = other as PredicateNullness;
if (other != null) // Should be `if (right != null)`.
{
if (this.value == right.value)
{
return this;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
Static field is accessed under lock, but the lock argument is not static. The lock may be entered by multiple threads simultaneously.
private static int _Index;
private readonly Dictionary<string, int> _counters;
public int GetTickIndex(string name) {
lock (_counters) {
if (!_counters.TryGetValue(name, out index)) {
index = _Index++;
_counters.Add(name, index);
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Unknown | Yes |
C# | Security | Critical | Unknown | Yes |
Related CWEs: CWE643.
User-provided data are used to form an XPath query without neutralization of special elements. It can be used by an attacker to execute arbitrary XPath query.
In this code sample parameter state
is concatenated to
XPath query.
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["state"] != null)
FindSalesPerson(Request.QueryString["state"]);
}
private void FindSalesPerson(string state)
{
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xml);
XmlNodeList list = xDoc
.SelectNodes("//salesperson[state='" + state + "']");
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Critical | Unknown | No |
C# | Security | Critical | Unknown | No |
XSS protection is disabled in web.config
for .NET
Framework.
<system.web>
<!-- this disables header checking -->
<httpRuntime requestValidationMode="2.0" enableHeaderChecking="false"/>
</system.web>
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
A thread static field is declared with an initializer. The initialization will happen only in one thread.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
[ThreadStatic]
attribute is used on a non-static field
and has no effect.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | High | Yes |
C# | Quality | Minor | High | Yes |
Use of this
as a synchronization primitive. It may cause
program freeze and even a deadlock, because the same object can be used
for synchronization by another code. Warning is emitted only if analyzer
has found the use for synchronization an object of the type having at
least one method that uses this
as synchronization
primitive.
public class B// < class declaraton >
{
public void fb()
{
lock (this) // < instance lock >
{
}
}
}
public class A
{
public void fa()
{
B b = new B();
lock (b) // <THIS_LOCK_OBJECT Type B contains locks on instance (this) in its functions. It is unsafe to use variable of this type for locking. >
{
}
}
}
The freeze may occur if created in the method fa
object
b
will be passed in another method running in a separate
thread that calls method B.fb()
.
public class B
{
private object _lockObject = new object();
public void fb()
{
lock (_lockObject)
{
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Major | Unknown | Yes |
C# | Quality | Major | Unknown | Yes |
Checker detects invocations of Monitor.Exit(x)
with
parameter x
if there was no corresponding invocation of
Monitor.Enter(x)
earlier in control flow.
class A
{
int x;
private object lockX = new object();
private object lockY = new object();
public void Bad()
{
Monitor.Enter(lockX);
x++;
Monitor.Exit(lockY); // <UNLOCK_NOT_LOCKED Monitor.Exit(lockY) is called outside critical section: without Monitor.Enter(lockY) or after successful Monitor.Exit(lockY) >
}
}
The code in the example will throw synchronization exception, because Monitor.Exit() was called from an unsynchronized block of code.
class A
{
int x;
private object lockX = new object();
private object lockY = new object();
public void Bad()
{
Monitor.Enter(lockX);
x++;
Monitor.Exit(lockX); // lockX
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
Reports warning if lock object satisfies one of the following conditions:
* it is an interned string (string constants are interned by design, see https://learn.microsoft.com/en-us/dotnet/api/system.string.intern?view=net-6.0)
* it has value type
* it has type that is equal to System.Type
public class A
{
private object getSync() => "sync";
public void foo()
{
lock (getSync()) // <WRONG_TYPE_LOCK String literal "sync" is used for locking >
{
// code
}
}
}
String literal "sync"
returned as a result of invocation
of getSync()
is used as synchronization primitive. It may
cause an unexpected program freeze if another thread will use string
constant "sync"
for synchronization.
public class A
{
private object syncObject = new object();
private object getSync() => syncObject;
public void foo()
{
lock (getSync())
{
// code
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
Related CWEs: CWE764.
Checker detects invocations of Monitor.Enter(x)
with
parameter x
if there already was an invocation of
Monitor.Enter(x)
earlier in control flow or it is inside
the lock(x)
statement. Locks are reentrant in C#, but
having multiple sequential locks with the same sync-object often points
to an error in the code.
class A
{
int x;
private object lockObj = new object();
public void Bad()
{
lock (lockObj)
{
Monitor.Enter(lockObj); // <MULTIPLE_LOCK Several sequential locks with the same object (lockObj) are performed. Make sure all of them are closed properly >
// code
}
}
}
class A
{
int x;
private object lockObj = new object();
public void Good()
{
lock (lockObj)
{
// code
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Major | Unknown | Yes |
C# | Quality | Major | Unknown | Yes |
Related CWEs: CWE369.
Checker uses taint analysis. It detects if user input (which may have zero value) is used as a divisor in expression.
using System;
class A {
public void Bad()
{
var divisor = Int32.Parse(Console.ReadLine()); // <DIVISION_BY_ZERO.INPUT Value Console.ReadLine(), provided by user, is used in division expression 42 / divisor, which may lead to division by zero > // < Console.ReadLine() is the origin of the taint > // < Int32.Parse(Console.ReadLine()) is tainted > // < divisor is tainted >
var res = 42 / divisor; // < divisor is passed into sink in expression 42 / divisor >
}
}
In the example program will throw
System.DivideByZeroException
if user input will be
0
.
using System;
class A {
public void Good()
{
var divisor = Int32.Parse(Console.ReadLine());
if (divisor != 0)
{
var res = 42 / divisor;
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
The checker detects fields that should have readonly
attribute, but don’t have it. It is considered that a field should be
readonly
if that field is initialized only in constructors
and cannot be modified externally.
using System.Collections.Generic;
public class A
{
private static List<int> lst = new(); // <FORGOTTEN_READONLY Field A.lst should be readonly > // < Initialization of A.lst field > // < Field A.lst is not accessible from external code because it is private or internal >
public void Test()
{
lst.Add(56);
}
}
In the example above it would be better to add readonly
modifier to the field lst
, because it is created once and
isn’t changed in the program. It may also have positive impact on
program performance.
using System.Collections.Generic;
public class A
{
private static readonly List<int> lst = new();
public void Test()
{
lst.Add(56);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
It’s a subtype of FORGOTTEN_READONLY for fields that are
accessible from outside (public
or protected
fields of public
classes; internal
is treated
as public
if the containing assembly has an
InternalsVisibleTo
attribute).
This subtype is useful when analyzing complete programs and useless for libraries.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
This checker finds unused fields. A field is considered unused when there are no uses of that field (or it is used only for self re-initialization) and it is not accessible from outside.
internal class Comp // < Field Comp.Counter is not accessible from external code because class Comp is private or internal >
{
public int Counter = 0; // <UNUSED_FIELD Field Comp.Counter is unused >// < Initialization of Comp.Counter field >
public void SetCounter()
{
Counter = (Counter + 1) * 2;
}
}
In the example field Counter
is used only for self
re-initialization. Removing of the field Counter
will save
memory and allows to evade useless calculations.
Remove unused field.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
It’s a subtype of UNUSED_FIELD for fields
that are accessible from outside (public
or
protected
fields of public
classes;
internal
is treated as public
if the
containing assembly has an InternalsVisibleTo
attribute).
This subtype is useful when analyzing complete programs and useless for libraries.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
Related CWEs: CWE561.
A function is unused and is not accessible from outside.
Remove unused function.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
This checker finds unused types. It is considered that a type is unused when there are no uses of that type and it is not accessible from outside.
class AnonymousTypeLookup // <UNUSED_TYPE Type AnonymousTypeLookup is unused >
{
public static Dictionary<string, int> Lookup;
static AnonymousTypeLookup()
{
Lookup = new Dictionary<string, int>();
}
}
Remove unused type.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE398.
Warn about empty code block ({}
).
public class A
{
private static void Bad()
{
//using (var writer = new IndentTextWriter(TierFile))
{ // <CODE_QUALITY.EMPTY_BLOCK Empty block has no effect >
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE398.
Warn about empty case
-block.
public class A
{
public void Bad(int x)
{
switch (x)
{
case 0: // <CODE_QUALITY.EMPTY_CASE Empty case has no effect >
break;
// code. switch has no default section
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE398.
Warn about empty else
-block.
public class A
{
public void Bad(int x)
{
if (x == 1)
{
x++;
}
else
{ // <CODE_QUALITY.EMPTY_ELSE Empty body of else statement has no effect >
// Console.WriteLine(x);
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE390.
Warn about empty finally
block.
public class A
{
public void Bad(int x)
{
try
{
x++;
// code
}
finally
{ // <CODE_QUALITY.EMPTY_FINALLY Empty body of finally statement has no effect >
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE398.
Warn about empty for
loop.
public class A
{
public void Bad(List<int> list)
{
foreach (var elem in list)
{ // <CODE_QUALITY.EMPTY_FOR Empty body of for statement has no effect >
// Console.WriteLine(elem);
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE398.
Warn about empty function (except lambda).
public class A
{
public static void Bad(int x)
{ // <CODE_QUALITY.EMPTY_FUNCTION Empty function Bad has no effect >
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Warn about empty if
-block.
public class A
{
public void Bad(int x)
{
if (x == 0)
{ // <CODE_QUALITY.EMPTY_IF Empty body of if statement has no effect >
// Console.WriteLine("x == 0");
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE398.
Warn about empty while
loop.
public class A
{
public void Bad(int x)
{
while (x > 0)
{ // <CODE_QUALITY.EMPTY_WHILE Empty body of while statement has no effect >
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE398.
Warn about redundant semicolon.
public class A
{
public void Bad(int x)
{
var y = x + 1;; // <CODE_QUALITY.SEMICOLON Empty statement has no effect >
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Normal | Average | Yes |
C# | Quality | Normal | Average | Yes |
Related CWEs: CWE193.
Checker detects cases when an array index is 1 off from the corresponding array length.
class A
{
void Bad(byte[] bytes)
{
for (int i = 1; i < bytes.Length; i++) // <OFF_BY_ONE The iteration number in loop with condition i < bytes.Length is off by one >
Console.WriteLine($"byte {i} is {bytes[i - 1]}");
}
}
In the example the last element of the array bytes
will
be ignored by an error. It is necessary to use
i <= bytes.length
instead.
class A
{
void Bad(byte[] bytes)
{
for (int i = 1; i <= bytes.Length; i++) // Use <= instead of <
Console.WriteLine($"byte {i} is {bytes[i - 1]}");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Security | Minor | Unknown | Yes |
C# | Security | Minor | Unknown | Yes |
Related CWEs: CWE134.
Checker detects cases where unchecked user input is used as format string by certain functions, which may cause denial of service if the string is not of a proper format.
using System;
class C {
public void f(int i) {
string s = Console.ReadLine();
s = string.Format(s, i);
Console.WriteLine(s);
}
}
In the example user input s
is used as a format string
in String.Format
. Some values provided by user will cause
an exception. It is advised to either use program-generated format
strings or safer functions instead.
using System;
class C {
public void f(int i) {
string s = "%d";
s = string.Format(s, i);
Console.WriteLine(s);
}
public void f2(int i) {
Console.WriteLine(i.toString());
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
Related CWEs: CWE129.
Checker detects if untrusted input is used as an array index and might reference a position outside the array.
using System;
class C {
public void f(int data) {
int[] array = new int[5];
if (data >= 0)
Console.WriteLine(array[data]);
}
}
In the example it is checked if index is greater or equal to zero but not whether it is less than the array size.
using System;
class C {
public void f(int data) {
int[] array = new int[5];
if (data >= 0 && data < 5)
Console.WriteLine(array[data]);
}
}
Those checkers are developed for searching error specific for Go language.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
In Golang most uninitialized errors were eliminated by language design, but it is still possible for global variables. This checker finds situation where global variable is used without any initialization.
var gvar1 map[string]string
var gvar2 map[string]string
func init() {
gvar1 = make(map[string]string)
// `gvar2` is not initialized.
}
func use(name string, value string) {
gvar1[name] = value
gvar2[name] = value // Error.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Average | No |
In Golang if the key is not in the map, the returned value will be nil. This checker finds situations where extracted value from the map is dereferencing without it nil checking.
import "fmt"
type Data struct {
a int
}
type table struct {
data map[int]*Data
}
func foo(id int, tbl table) {
if idx, ok := tbl.data[id]; !ok {
var flag = idx.a //svace: emitted DEREF_OF_NULL.MAP.UNCHECKED_TUPLE
fmt.Println(flag, ok)
}
}
In the example ok
flag is false in condition branch, so
idx
has nil value, and it leads to null-pointer dereference
error.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
It searches for using of loop variable in goroutine. Value of this variable may be changed by outer loop before this goroutine’s execution which lead to race condition.
var synchr sync.WaitGroup
for i := 0; i < 100; i++ {
synchr.Add(1)
go func() {
defer synchr.Done()
fmt.Printf("i = %d\n", i)
}()
}
synchr.Wait()
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
Interfaces are not pointers in Go even though they may look like
pointers, and an interface variable will be nil
only when
its type and value fields are nil
. This can lead to
unexpected behavior when checking an interface variable for
nil
.
The checker detects situations where a pointer, which might be
nil
, is converted to a value of interface type, and then it
is compared with nil. Such comparison doesn’t protect from nil pointer
dereference. Svace does not check that the pointer will be dereferenced
after comparison to nil.
import "fmt"
type Interface interface {
f()
}
type Struct struct {
Field int
}
func (s *Struct) f() {
fmt.Println("Field: %v\n", (*s).Field)
}
func foo(x int) Interface {
var possibleNil *Struct
if (x > 5) {
possibleNil = &Struct{Field: 5}
}
return possibleNil
}
func bar(x int) {
var b Interface
b = createPossibleNil(x)
if b != nil { //Svace emits warning here for comparison
//b still has nil value and can't be dereferenced if x <= 5
b.f()
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
The warning is reported when typed nil
value may be
returned from the function and return type is interface.
func createTypedNilStruct() Interface {
var typedNil *Struct
typedNil = nil
return typedNil // Warning.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Major | High | Yes |
Uncontrolled calling of panic
may lead to unexpected
program crashes if not handled properly. This checker reports cases when
an main
or init
functions call
panic
but do not call recover
in the deferred
functions. In this situation the program will crash.
func rec() {
recover()
}
func AbortIfError(e error) {
if e != nil {}
panic("panic!!")
}
}
func init() {
f, err := os.Open("foo.txt")
AbortIfError(err) // NO_RECOVER_FOR_PANIC
// ...
}
func BuildRunCommand() Command {
// ...
flags, err := types.BuildRunCommandFlagSet(...)
AbortIfError(err)
// ...
}
func main() {
defer rec()
command = BuildRunCommand() // no error
// ...
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Major | High | No |
Uncontrolled calling of panic
may lead to unexpected
program crashes if not handled properly. This checker reports cases when
an exported function calls panic
but does not call
recover
in the deferred functions. In this situation the
handling of a panic is passed (maybe unexpectedly) to the caller of the
exported function and if it is not done there the program may crash
unpredictably.
func f2() {
recover()
}
func SafePanic(i int) {
defer f2()
if i == 2 {
panic("panic")
}
}
func UnsafePanic(i int) {
if i == 3 {
panic("panic!!") // NO_RECOVER_FOR_PANIC.STRICT
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
The warning is reported when value is extracted from the map using tainted key without result checking.
For Go language when value = map[key]
is used instead of
value, ok = map[key]
, key
is tainted, and
value
was not checked.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
Same as TAINTED.UNCHECKED_TUPLE.RET, but the sink is in the callee.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | No |
The warning is reported for Go language code in the form of
value, ok = i.(T)
when ok
value is
ignored.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
Same as UNCHECKED_TUPLE.CAST, but the sink is in callee.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
The warning is reported for Go language code in the form of
value, ok = <-channel
when ok
value is
ignored.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
The checker detects situations where tuple result from functions with error part is used without checking for error or checking has no effect.
func parse(path string) (uint, error) {
length := read(path)
if length < 0 {
return 0, fmt.Errorf("error")
}
return length, nil
}
func example(para string, out *int) uint {
n, _ := parse(para)
return n // Error, variable `n` is used without checking the error.
}
func useless_check(para string) uint {
n, err := parse(para)
if err != nil {
fmt.Printf("error")
}
// Variable `n` is used while variable `err` has an error.
// Probably missing return statement in `if` branch.
return n
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Normal | Unknown | Yes |
Same as UNCHECKED_TUPLE.RET, but the sink is in callee.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Major | Unknown | Yes |
This checker finds infinite loop in goroutines.
func waiter(text string) {
for {
time.Sleep(time.Second)
fmt.Println(text)
} // No exit condition.
}
func main() {
go waiter("Hello")
go waiter("World")
time.Sleep(50 * time.Second)
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Minor | Unknown | Yes |
The checker finds situation where type-switch does not have default branch.
switch dm := m.(type) {
case DynamicAny:
m = dm.Message
case *DynamicAny:
if dm == nil {
return nil, proto.ErrNil
}
m = dm.Message
}
// No default.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Minor | Unknown | Yes |
This checker finds situation where switch does not have default branch.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Minor | Unknown | Yes |
This checker finds situation where the result of the type assertion is used without checking for the correct value.
type MyObj struct {
val string
mk *MyObj
}
func test(para interface{}) {
fmt.Println("val=%v\n", para.(*MyObj)) // UNSAFE_TYPE_ASSERTION
var mk = &MyObj{"AA", para.(*MyObj)} // UNSAFE_TYPE_ASSERTION
fmt.Println(mk)
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Minor | Unknown | Yes |
This checker finds situations where may be an integer overflow in type conversion.
func unsafeTypeConversion(arr []byte) error {
copyArr := arr
total := len(copyArr)
for {
if int(copyArr[0])+1 > total {
return fmt.Errorf("invalid input arr")
}
total -= int(copyArr[0] + 1) // UNSAFE_TYPE_CONVERSION
if total == 0 {
break
}
copyArr = copyArr[copyArr[0]+1:]
}
return nil
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Minor | Unknown | Yes |
This checker finds situations where the error return is not used.
func withErrReturn(a int, b int) (int, error) {
return 0, errors.New("text string")
}
func test() {
withErrReturn(0, 0) // UNUSED_ERROR_RETURN
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Minor | Unknown | Yes |
This checker finds situations where the error return is ignored via _.
func withErrReturn(a int, b int) (int, error) {
return 0, errors.New("text string")
}
func test() {
a, _ := withErrReturn(0, 0) // UNUSED_ERROR_RETURN.STRICT
common.Use(a)
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Python | Quality | Major | Unknown | Yes |
In Python, the statement x,
means tuple with single
element x
. The comma may be added accidentally, making a
tuple from a variable. PEP
8 recommends using (x,)
syntax for tuples. This means
redundant parentheses like (x)
should be avoided. The
Python interpreter also forces this representation:
>>> 5,
(5,)
>>> (5)
5
def multiply(x, by):
return x * by
def square_root(x):
return x**0.5
x = [1, 3, 7],
x_squared = [square_root(multiply(x_i, 3)) for x_i in x]
After assignment, x
will be a tuple
([1, 3, 7],)
, not a list [1, 3, 7]
. This fact
will entail an error, because during first iteration the input of the
function square_root
will be a list
[1, 3, 7, 1, 3, 7, 1, 3, 7]
, not a number
1
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Python | Quality | Major | Unknown | Yes |
Default value for the function argument is created only once, when the interpreter processes function definition. The object created as a default value will be used every time the function is called.
First call of the function append
will return
[5]
, but the second call of that function will return
[5, 5]
.
This group presents various warning types reporting about issues that are specific to the testing systems, e.g. when a test doesn’t contain any checks at all.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
If the result of a function is an object, it should be validated properly. This means that not only the reference itself but internal fields of the object must be verified via assertions. This JUnit 4 checker finds cases when an object has been checked but none of its fields have been.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
Specific function return values and parameters must always be checked
via assertions. This JUnit 4 checker finds cases when there are no
required assertions for user-defined list of methods. List of functions
that should be checked can be setup using a JSON file located in the
Svace distribution config
folder and named
white-list-junit.json
. See TCCHECK.NUNIT.MISSING_ASSERTION.USER_DEFINED
description for its syntax.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
This checker finds JUnit 4 test cases that don’t have an assertion in the body at all.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
This checker detects potentially faulty JUnit 4 assertion conditions which are always evaluated to be only true or only false. This might happen when by the time the execution reaches an assertion there is nothing left to check. For example, when all bad outcomes were already sorted out by conditional expressions which led to returns from the function. In this case the assertion doesn’t check anything and is useless.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | High | Yes |
C# | Quality | Minor | High | Yes |
Interprocedural detector for NUnit framework based tests, that checks if at least one field of every checked object have been also checked. So, it is required to check not only a reference (ex. by type or null-value), but also fields of the object.
In this code sample at least one field of variable
account
requires a check because the object itself was
checked (twice).
[Test] // Indicates that it is a test method
public void MISSING_ASSERTION_RETURN_VALUE()
{
Account account = new Account();
// CSCC-WARN{{TCCHECK.NUNIT.MISSING_ASSERTION.RETURN_VALUE
// At least one field of object account should be also checked
// if object itself was checked}}
Assert.IsInstanceOf<Account>(account, "Should return Account instance"); // <---
Assert.IsNotNull(account, "Failed to create Account instance");
account.Dispose();
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
Interprocedural detector for NUnit framework based tests, that checks
if every return value or parameter of method from user-defined list have
been checked with assertion. List of functions is declared in svace
config directory with a JSON file
white-list-tccheck-nunit.json
. The syntax of the file is
the following:
{
"classname" : "function_classname",
"function" : "function_name",
"checkReturn" : true,
"checkParameters" : [0,2]
}
"classname"
: the string representing a name of
class. It is necessary to specify the fully qualified name of class with
its namespace. The value of parameter will be concatenated with a
"function"
parameter ("classname"."function"
)
and compared with the name of every called method in every
test.
"function"
: the string representing a name of
method. It is necessary to specify the short name without containing
type and namespace. The value of parameter will be concatenated with a
"classname"
parameter ("classname"."function"
)
and compared with the name of every called method in every
test.
"checkReturn"
: the parameter indicates if a return
value of function must be checked with an assertion. Possible values:
true
, false
. The parameter is optional,
false
is the default value.
"checkParameters"
: an array of integers, that
determine which parameters of the method must be checked with an
assertion. The field "checkParameters"
is optional, no
method parameters will be checked if it is omitted. The first parameter
index of regular method equals to zero. Parameter this
in
an extension method has index equal to -1.
In the code sample the return value of method
Account.CreateAccount
must be checked, because it is
specified in the white-list.
[Test] // Indicates that it is a test method
public void MISSING_ASSERTION_USER_DEFINED()
{
// CSCC-WARN{{TCCHECK.NUNIT.MISSING_ASSERTION.USER_DEFINED
// Return value of CreateAccount should be checked because it is in the white-list}}
Account account = Account.CreateAccount(); // <---
string userName = "CSAPI_USER";
account.UserName = userName;
Assert.True(account.UserName.Length > 0, "Failed to add UserName");
Assert.True(account.UserName.CompareTo(userName) == 0, "Failed to add UserName");
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | Very high | Yes |
C# | Quality | Minor | Very high | Yes |
Interprocedural detector for NUnit framework that checks whether at least one assertion exists in each test method.
In the example below all lines with assertions are commented out, so test method has no assertions. It is suggested to uncomment or add necessary checks or delete useless test.
[Test] // Indicates that it is a test method
// CSCC-WARN{{TCCHECK.NUNIT.NO_ASSERTION
// Method NO_ASSERTION marked as a test with attribute Test has no assertion
// for validation}}
public void NO_ASSERTION() // <---
{
Account account = Account.CreateAccount();
string userName = "CSAPI_USER_ID";
account.UserName = userName;
var id = account.AccountId;
// Assert.IsInstanceOf<int>(id, "Failed to get valid AccountId);
// Assert.True(account.AccountId > 0, "Failed to get valid AccountId");
// Assert.True(account.AccountId == dbid,
// "Failed to get valid AccountId. Expected: "+dbid+ " .Actual: "+account.AccountId);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Visual Basic .NET | Quality | Minor | High | Yes |
C# | Quality | Minor | High | Yes |
Interprocedural detector for NUnit framework that detects faulty assertions always evaluating to be true or false. This can happen if a program reaches an assertion and there is nothing left to check, e.g. when all alternatives were already sorted out by the previous code.
In the example below the length of the variable display
is checked to be greater than zero, but its value was assigned in the
same method before and equals to constant string
"MY_DISPLAY"
, so assertions always has true
value. Perhaps the value of account.DisplayName.Length
should be checked instead.
[Test] // Indicates that it is a test method
public void USELESS_ASSERTION()
{
Account account = Account.CreateAccount();
Assert.IsNotNull(account, "Failed to create Account instance");
string display = "MY_DISPLAY";
account.DisplayName = display;
// CSCC-WARN{{TCCHECK.NUNIT.USELESS_ASSERTION
// Assertion is useless, because checked condition is always met}}
Assert.True(display.Length > 0, "Failed to add DisplayName"); // <---
Assert.True(account.DisplayName.CompareTo(display) == 0, "Failed to add DisplayName");
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Scala | Quality | Major | Unknown | Yes |
This checker finds situations where a format string contains more
format specifiers than the number of arguments actually passed to the
format output function. For C/C++ this leads to illegal access to stack
memory, for Java an exception
java.util.MissingFormatArgumentException
is thrown.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
This checker finds situations where a format string contains fewer format specifiers than the number of arguments actually passed to the format output function. This probably means that some data was intended but actually forgotten to be printed.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | No |
This detector finds situations where functions that work with format
strings (printf
and scanf
like functions) are
passed a non-literal format string. It can lead to format string
vulnerability when by adding or removing format specifiers a malicious
user manipulates the format string in a way that can lead to various
security issues, such as reading or writing arbitrary memory locations,
leaking sensitive information, or executing arbitrary code.
void incorect(char* buf, const char *format, int a) {
// Variable 'format' is not string literal
sprintf(buf, format, a);
}
void corect(char* buf, int a) {
const char *format = "some_format_string";
// Variable 'format' now is string literal
sprintf(buf, format, a);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Very high | Yes |
Related CWEs: CWE562.
This warning is emitted when address of a local variable is returned from a function. Local variables have a lifetime only inside the block where they are defined so after the return from the function their addresses become invalidated, and using them may lead to inconsistent behavior, various memory issues or even sensitive data leaks.
More general cases of using memory out of scope is also reported by RETURN_LOCAL_ADDR checker.
When the function returns a local variable address conditionally and
the analyzer isn’t able to prove that such condition is unfeasible given
the function contract a RETURN_LOCAL_VAR.MIGHT
warning type report is emitted.
char *write_temp_file_and_return_its_name() {
char tempname[] = "/tmp/dir/XXXXXX";
int fd = mkstemp(tempname);
if (fd != -1) {
// ...
} else {
return NULL;
}
return tempname;
}
In the example above the contents of allocated on stack character
array tempname
is filled by calling a mkstemp
function. This local array address is then returned from the function
but that memory can’t be used after return since it is already reclaimed
and may be reused for local variables in other functions stack
frames.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE562.
This warning is emitted when address of a local variable escapes from a function in some way. Local variables have a lifetime only inside the block where they are defined so after the return from the function their addresses become invalidated, and using them may lead to inconsistent behavior, various memory issues or even sensitive data leaks.
This checker reports more general cases than RETURN_LOCAL_VAR does; for example, it reports an issue when a local variable address escapes via a function call where it may be stored in global memory.
void bar(char *p);
int* foo(char b) {
char *p = &b;
if (b) {
char buf[10];
p = &buf[0];
}
if (!b) {
bar(p); // memory allocated on stack may be dangerously used
}
return 0;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE758.
This checker finds functions that are declared as having a non-void return type but don’t return any value. Only functions that don’t unconditionally terminate execution of the program are reported.
int foo2(int cond) {
if (cond)
exit(1);
else
exit(0);
// NO_RETURN_VALUE will NOT be emitted here, since the function always
// terminates the program.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
A function name is compared against some value. This is usually caused by accidentally using the function address instead of the result of its call and can result in subtle application logic issues.
In the following example from the Dart
SDK because of accidentally forgotten parentheses the address of the
function GetLastError
itself is compared against
NO_ERROR
instead of the result of its call.
int SocketBase::GetType(intptr_t fd) {
Handle* handle = reinterpret_cast<Handle*>(fd);
switch (GetFileType(handle->handle())) {
case FILE_TYPE_CHAR:
return File::kTerminal;
case FILE_TYPE_PIPE:
return File::kPipe;
case FILE_TYPE_DISK:
return File::kFile;
default:
return GetLastError == NO_ERROR ? File::kOther : -1; // FLAW: address compare instead of a call
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very low | Yes |
Related CWEs: CWE253, CWE597, CWE628, CWE754, CWE783.
This warning type is reported when a function pointer known to contain address of some particular function(s) is compared against a null value. Such comparison is always false and may be the result of accidentally forgotten parentheses. In this case compilers don’t issue a warning but comparison of a function address instead of the result of its call can lead to subtle application logic issues.
int fun() {
return 1;
}
typedef int (*PF)();
void proc() {
PF addr = &fun;
if (addr == 0) // Condition is always false, emit PROC_ADDR_NULL_CHECK.
return;
addr();
}
Occasionally such comparison is intended. To better reflect this intention in the code and suppress this warning report, change an explicit null value with a constant variable of a function pointer type that has a null value:
int fun() {
return 1;
}
typedef int (*PF)();
void proc() {
PF addr = &fun;
int (*null_fp)() = 0;
if (addr == null_fp) // PROC_ADDR_NULL_CHECK is not emitted.
return;
addr();
}
(Svace still emits PROC_ADDR_NULL_PTR_CHECK in this case.)
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
This warning subtype of PROC_ADDR_NULL_CHECK is reported when a function pointer known to contain address of some particular function(s) is compared with another function pointer that has a null value. Such comparison is always false and may be the result of application logic issue.
int fun() {
return 1;
}
typedef int (*PF)();
void proc() {
PF addr = &fun;
int (*null_fp)() = 0;
if (addr == null_fp) // Condition is false, emit PROC_ADDR_NULL_PTR_CHECK
return;
addr();
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
A pointer variable is compared with a zero literal.
Such comparisons contradict to some coding standards and may indicate
an error: a user may intend to compare a pointed object but not a
pointer. NULL
macro or nullptr
literal should
be used instead of a raw 0
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | High | Yes |
Related CWEs: CWE398.
This checker finds situations where a structure of a large size equal to or greater than 512 bytes is passed as a function argument by value.
When a structure is passed by value in a function call, a copy of the entire structure is created on the stack. If the structure is large, this can consume significant memory and processing time. The larger the structure, the more overhead is incurred during the copying process.
The related threshold value can be changed through
PROC_PAR_HUGE.THRESHOLD
configuration option.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | High | No |
Related CWEs: CWE398.
This checker finds situations where a structure of a large size equal to or greater than 128 bytes, but less that 512 bytes (exceeding the latter threshold value targets PROC_PAR_HUGE warning) is passed as a function argument by value (these threshold values are used by default).
When a structure is passed by value in a function call, a copy of the entire structure is created on the stack. If the structure is large, this can consume significant memory and processing time. The larger the structure, the more overhead is incurred during the copying process.
The related thresholds values can be changed through
PROC_PAR_BIG.THRESHOLD
and
PROC_PAR_HUGE.THRESHOLD
configuration options.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | High | Yes |
This checker finds situations where the total size of memory that might be simultaneously allocated on stack exceeds the allowed threshold. The threshold value is specified in pointer sizes and is equal to 1048576 by default.
void bigmem() {
char arr7mb[7 * 1024 * 1024];
// ...
}
void test() {
char arr2mb[2 * 1024 * 1024];
// ...
bigmem();
// ...
}
The related threshold value can be changed through
STACK_EXCEED.LIMIT
configuration
option.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Low | No |
This checker finds situations where the size of a local variable (or an array) exceeds 10000 bytes, but less than 30000 bytes (exceeding the latter threshold value targets LOCAL_VAR.HUGE) warning (these threshold values are used by default).
The related threshold values can be changed through
LOCAL_VAR.BIG.LIMIT
and LOCAL_VAR.HUGE.LIMIT
configuration options.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Average | Yes |
This checker finds situations where the size of a local variable (or an array) exceeds 30000 bytes (this value is used by default).
The related threshold value can be set through
LOCAL_VAR.HUGE.LIMIT
configuration
option.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE119, CWE129, CWE394.
This checker finds situations where a value is first used by passing it to a function as an argument, and then is checked for potentially being negative afterwards, meaning that such value may indicate an error code.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | High | Yes |
C/C++ | Quality | Normal | High | Yes |
Related CWEs: CWE129, CWE394, CWE606.
Path-sensitive checker which detects using a value, that is probably negative because it was compared to a negative value, as a buffer index or must-be-positive argument of a function.
void proc(int z) {
int buf[10];
buf[z] = 2; // `z` should be non-negative here.
}
void f(int a, int b, int c) {
int fd = socket(a, b, c);
int r = fd < 0 ? -errno : 0; // `fd` is compared to zero.
if (r > 0) {
return;
}
proc(fd); // Error, `fd` is passed to a function `proc`.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
Related CWEs: CWE253, CWE573, CWE754.
This checker finds suspicious ‘lesser than’ and ‘greater than’ comparisons between a pointer and a null pointer.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
This Microsoft Windows-specific checker finds situations when a non-BSTR value is incorrectly compared with a BSTR value.
bool compare(BSTR s1, wchar_t *s2) {
if (s1 == s2) // Incorrect comparison.
return true;
return false;
}
Fixed code:
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
This checker finds unsafe mix of integer values and boolean constants in comparison operations.
In the AVFS library the return value of the virt_islocal
function is 1
in case of a successful check, 0
in case of a negative check and -1
in case of an error.
But the author of the code below assumed that its return value may be
interpreted as a boolean value and checked only the negative scenario.
In case of the virt_islocal
function returning
-1
on error the true branch of the if expression is
taken.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | Yes |
Introduced in Java 14 records provide a compact way to declare
classes that are simple and transparent carriers for immutable data.
With the default implementation of the equals
method two
records are equal only when all the attributes of the instances are
equal irrespective of whether the attributes are primitive or of
reference types. If an array is used as an attribute of a record, then
the comparison of this attribute is performed by reference, resulting in
two records being considered different if the array references
themselves are not equal even if all their elements are equal.
Typically, such attributes need to be compared by value, and for this it
is necessary to override the equals
method. This checker
issues a warning for records that have array attributes but use the
default implementation of the equals
method.
import java.util.Arrays;
record Person1(String[] names, int age) {} // ERROR: Field 'names' will be compared by reference
record Person2(String[] names, int age) { // OK
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person2 person)) return false;
return age == person.age && Arrays.equals(names, person.names);
}
// ...
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Kotlin | Quality | Minor | Unknown | Yes |
For each data class in Kotlin, the compiler automatically generates
additional member functions including an equals
method to
compare two data class instances. If an array is used as a data class
member, then the default comparison of this member is performed by
reference, resulting in two data classes instances being considered
different if the array references themselves are not equal even if all
their elements are equal. Typically, such members need to be compared by
value, and for this it is necessary to provide an explicit
equals
method. This checker issues a warning for data
classes that have array members but use the default implementation of
the equals
method.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Average | Yes |
Related CWEs: CWE197.
Some C functions, such as getc
, fgetc
and
getchar
, return either (non-character) value
EOF
or a character code. This checker detects incorrect use
of such functions, where their return value is not compared to the
EOF
value before being converted from int
to
char
(note that comparing to EOF
after
converting to type char
is incorrect, since integer value
of EOF
can’t be represented by type char
).
char buf[256];
FILE* fp;
int c, i = 0;
void test() {
while (i < 256) {
c = fgetc(fp);
if (c == 'q')
break;
buf[i++] = c; // Unsafe type conversion, not checked for EOF.
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Portability | Major | Unknown | No |
Related CWEs: CWE190, CWE191, CWE197.
This C-specific portability checker detects situations of using
char
type (without explicit signed
or
unsigned
type modifier) variable in conditional
expressions. Different target platforms have different default
signedness of the char
type and using variables of this
type without explicit signedness type modifier may lead to unexpected
changes in program behaviour on platforms with different defaults.
While the following loop iterates four times as expected when
compiled for a platform with signed char
type, it becomes
infinite when compiled for a platform with unsigned char
type.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | High | Yes |
C/C++ | Quality | Minor | High | Yes |
Scala | Quality | Minor | High | Yes |
Related CWEs: CWE242.
This warning is emitted for invocations of functions that open security vulnerabilities, irrespective of their method of use.
There are several subtypes:
PROC_USE.VULNERABLE.TEMP
indicates the use of vulnerable library functions for working with
temporary files that are known to have predictable temporary file names
generation algorithms and other issues that can be exploited by
malicious actors.
PROC_USE.VULNERABLE.SSCANF
indicates the use of function sscanf
.
PROC_USE.VULNERABLE.GETENV
indicates the use of a getenv
function, which return values
can be altered by a malicious user before executing a vulnerable
process.
PROC_USE.VULNERABLE.SQLITE
indicates the use of legacy or unsafe SQLite functions (e.g. sqlite3_exec and sqlite3_get_table).
PROC_USE.VULNERABLE.PCSTRING
indicates the use of obsolete functions from the Samsung
PCString
library.
PROC_USE.VULNERABLE.EXTEND
indicates the use of specific functions that can be considered unsafe in
some contexts.
void vln_use() {
char buf[10000];
gets(buf); // No matter what is the size if `buf`, `gets` can overflow it.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
This checker reports the uses of a Tizen-specific
vconf_get_str
function that should be carefully
reviewed.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Very high | Yes |
Related CWEs: CWE330.
This checker reports warnings for the use of weak pseudo-random
generator functions including rand()
,
random()
, erand48()
, drand48()
,
mrand48()
, and lrand48()
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Related CWEs: CWE367.
The checker TOCTTOU_SEQUENCE
(Time-of-check-to-time-of-use)
finds situations where a call accessing file attributes (such as
stat()
, access()
and readlink()
)
is followed by a call using the same file (such as fopen()
,
opendir()
). This can potentially lead to a kind of race
condition, where the file is changed between the check of file
attributes and the use of the file.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Unknown | Yes |
Related CWEs: CWE243.
This checker finds situation where a call to chroot()
is
made, that isn’t followed by a call to chdir("/")
. Function
chroot()
limits application’s access to the file system to
within a given directory (“sandbox”). However, if current directory is
outside the sandbox, the application can still access files outside the
sandbox. Calling chdir("/")
makes sure it doesn’t
happen.
This checker can handle source code that implements calls to
chdir
and chroot
using wrapper functions, or
code that calls chdir
conditionally on the return value of
chroot
.
int chroot_wrapper() {
return chroot("/var/jail");
}
int chdir_wrapper() {
return chdir("/");
}
void a(int b, int c) {
chroot_wrapper();
if (b > c)
chdir_wrapper(); // CHROOT_JAIL is emitted here, since `chdir`
// doesn't follow `chroot` unconditionally.
fopen("/etc/passwd", "r");
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Average | Yes |
Scala | Quality | Major | Average | Yes |
Go | Quality | Major | Average | Yes |
Related CWEs: CWE129, CWE394, CWE606.
The checker NEGATIVE_CODE_ERROR
finds situations where a
value is returned by a library function, that may be negative to
indicate an error code, but is used in a situation where the negative
values are not expected (for example, in a call to another function, or
as an index in array access).
void test(char* path, int flags, char* mode, char* buf, int nbytes) {
int fd = open(path, flags, mode);
read(fd, buf, nbytes);
}
The value returned by open
may be a negative error code,
which shouldn’t be passed to function read
.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Low | No |
C/C++ | Quality | Normal | Low | No |
Scala | Quality | Normal | Low | No |
It is subtype of warning NEGATIVE_CODE_ERROR for situations, where negative value is converted to unsigned type.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Scala | Quality | Major | High | Yes |
Related CWEs: CWE124, CWE127, CWE129, CWE194, CWE195, CWE394, CWE606.
Path sensitive version of the NEGATIVE_CODE_ERROR. It checks if a path exists where some variable is assigned a negative value, and then is used in a situation where the negative values are not expected.
char buf[100];
void test1(int a, int b) {
int x = -1;
if(a)
x = b;
buf[x] = 6;// warning will be emitted here
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | High | Yes |
C/C++ | Quality | Major | High | Yes |
Scala | Quality | Major | High | Yes |
Kotlin | Quality | Normal | High | Yes |
This detector finds situations where a library function returns a
value that might indicate an error and this value is not checked after
it has been assigned to a local variable. A UNCHECKED_FUNC_RES.LIB.MACRO
subtype of this warning is emitted if the analyzer suspects that the
warning is caused by an expression being implemented within a macro or
automatically generated code.
void example(char *oldpath, char *newpath) {
int x = rename(oldpath, newpath);
}
void possible_fix(char *oldpath, char *newpath) {
if (!rename(oldpath, newpath)) {
// ...
}
}
Function example
illustrates the defect: the return
value of rename
function is assigned to x
and
is not checked.
Function possible_fix
illustrates a possible fix: check
the return value of rename
function and handle a possible
error.
fun example(string: String, offset: Int): Reader {
val reader = StringReader(string)
val skipped = reader.skip(offset.toLong())
return reader
}
fun possibleFix(string: String, offset: Int): Reader {
val reader = StringReader(string)
if (reader.skip(offset.toLong()) < 0) {
// ...
}
return reader
}
Function example
illustrates the defect: the return
value of skip
function is assigned to skipped
and is not checked.
Function possibleFix
illustrates a possible fix: check
the return value of skip
function and handle a possible
error.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Very high | No |
C/C++ | Quality | Minor | Very high | No |
Scala | Quality | Minor | Very high | No |
It is a subtype of UNCHECKED_FUNC_RES.LIB for functions whose return values are often not checked.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
C/C++ | Quality | Normal | Unknown | No |
Scala | Quality | Normal | Unknown | No |
A subtype of UNCHECKED_FUNC_RES.LIB for
non-library functions. If configuration
option USE_UNCHECKED_FUNC_RES
is disabled then these
warnings are instead emitted as UNCHECKED_FUNC_RES.LIB.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Very high | Yes |
Related CWEs: CWE252, CWE253, CWE754.
This checker reports situations where fread
function
result is compared to zero, but neither ferror
nor
feof
functions were called. Zero resulting value is
returned by fread
in both error situation and when
end-of-file reached without reading any items. Not distinguishing these
situations may lead to missing handling of error situations.
int foo(FILE *pFile, long lSize, char* buffer)
{
int res = fread(buffer, 1, lSize, pFile);
if (!res) // warning will be emitted here
{
return 1;
}
return 0;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Very high | Yes |
C/C++ | Quality | Minor | Very high | Yes |
Scala | Quality | Minor | Very high | Yes |
Kotlin | Quality | Minor | Very high | Yes |
This detector finds situations where a library function returns a
value that might indicate an error and this value is neither checked nor
assigned to a local variable. A UNCHECKED_FUNC_RES.LIB.STRICT.MACRO
subtype of this warning is emitted if the analyzer suspects that the
warning is caused by an expression being implemented within a macro or
automatically generated code.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
C/C++ | Quality | Normal | Unknown | No |
Scala | Quality | Normal | Unknown | No |
A subtype of UNCHECKED_FUNC_RES.LIB.STRICT for non-library functions.
void example(char *oldpath, char *newpath) {
rename(oldpath, newpath);
}
void possible_fix(char *oldpath, char *newpath) {
if (!rename(oldpath, newpath)) {
// ...
}
}
Function example
illustrates the defect: the return
value of rename
function is not assigned to a local
variable and is not checked.
Function possible_fix
illustrates a possible fix: check
the return value of rename
function and handle a possible
error.
fun example(string: String, offset: Int): Reader {
val reader = StringReader(string)
reader.skip(offset.toLong())
return reader
}
fun possibleFix(string: String, offset: Int): Reader {
val reader = StringReader(string)
if (reader.skip(offset.toLong()) < 0) {
// ...
}
return reader
}
Function example
illustrates the defect: the return
value of skip
function is not assigned to local variable
and is not checked.
Function possibleFix
illustrates a possible fix: check
the return value of skip
function and handle a possible
error.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Minor | Unknown | No |
This C-specific checker finds cases where a string is read from the
input stream with a standard C library fgets()
function
without checking if all the characters of the input line were actually
read. If the input string size exceeds the buffer size provided, reading
stops when one less than the buffer size number of characters are read.
This situation needs to be handled to prevent input data corruption when
processing only partial input. This checker is based on the FIO-20C
rule from the CERT Secure Coding Standard.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Minor | Unknown | No |
This C-specific portability checker detects unsafely implemented
removals of open files. In particular, calling the standard
remove
function on an open file causes
implementation-defined behavior. In portable code, opened file should
not be removed by calling this function. If removing an open file is
truly necessary for security reasons, the code should fall back to
platform-specific solutions that have well-defined behavior, such as
POSIX unlink
. This checker is based on the FIO08-C
rule from the CERT Secure Coding Standard.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | High | Yes |
C/C++ | Quality | Normal | High | Yes |
Kotlin | Quality | Normal | High | Yes |
Related CWEs: CWE248.
This checker finds situations where an exception is thrown from a function but it’s not caught in a proper way.
For C++ an issue is reported in the following cases:
if the thrown exception violates the function exception
specification. If the function is non-throwing or an exception type
isn’t listed in its declarations’s throw
exception
specification and an exception of this type is thrown then the program
is terminated abnormally;
the function is a top-level function. In this case the program is terminated abnormally and is not guaranteed to unwind the stack calling the destructors in process. This may lead to leaking of resources not cleaned up by the OS on program exit or other unpredictable issues;
the function is a destructor. If another exception is already propagating the application will terminate.
For Java and Kotlin this type of warning is emitted if the class (or
one of the parents of this class) of a thrown exception, that reaches
the main
method, is annotated with a
@MustBeCaught
annotation. By default, the analyzer
considers the java.lang.IllegalArgumentException
to be
annotated with this annotation. Other exception classes or interfaces
can be annotated with the help of user specifications, see
svace spec
tool help and svace_spec_manual.pdf
file in the docs
folder of the analyzer distribution.
Exception types explicitly declared in the throws ...
clause of the main
method declaration don’t trigger a
warning.
In the example below the thrown exception type doesn’t match the function exception specification:
void foo() throw(int) {
throw "Hello world!"; // Warning is fired.
}
int main() {
try {
foo();
} catch(...) {
// Never reached.
}
}
In the following example the uncaught exception from the
main
function terminates the program.
In the following example the exception from the destructor terminates the program.
struct Bad {
~Bad(){
throw 1; // Warning is fired.
}
};
int main() {
try {
Bad bad;
throw 2;
} catch(...){
// Never reached.
}
}
In the following example the EmptyListException
exception isn’t caught in the main
method.
class EmptyListException() : IllegalArgumentException()
fun main(args: Array<String>) {
parseArgs(args) // Warning is fired.
}
private fun parseArgs(args: Array<String>) {
if (args.size == 0) {
throw EmptyListException()
}
// ...
}
In the following example no issues are reported because all exception
classes are listed in the main
function’s declaration
throws
clause.
public class TrustThrowsInSignature {
public static void main(String[] args)
throws ClassNotFoundException, LinkageError, ExceptionInInitializerError{
if (args.length < 2) return;
Class<?> clazz = getClass(args[1]); // Warning is not fired.
System.out.println(clazz);
}
public static Class<?> getClass(String name)
throws ClassNotFoundException, LinkageError, ExceptionInInitializerError {
return Class.forName(name);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Very low | Yes |
C/C++ | Quality | Normal | Very low | Yes |
Kotlin | Quality | Normal | Very low | Yes |
Related CWEs: CWE248.
This warning is emitted in the same cases as described in NO_CATCH but only if the exception is thrown from a standard library function or compiler generated code.
class Base {virtual void member(){}};
class Derived : Base {};
void test() throw() {
Base b;
// Will crash, because dynamic_cast of reference could throw `std::bad_cast`.
Derived& rd = dynamic_cast<Derived&>(b);
}
public class ConditionalThrow {
public static void main(String[] args) {
int port = 7100;
for (int i = 0; i < args.length; ++i) {
if ("-p".equals(args[i]) && i + 1 < args.length) {
i++;
port = Integer.parseInt(args[i]); // Warning is fired for `NumberFormatException`.
}
}
// ...
}
}
fun main(args: Array<String>) {
val portNumber = args[0].toInt() // Warning is fired for `NumberFormatException`.
println("Starting server on port $portNumber...")
// ...
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | Yes |
Kotlin | Quality | Minor | Unknown | Yes |
This type of warning is emitted only for Java and Kotlin for
exception types that doesn’t belong to NO_CATCH
and NO_CATCH.LIBRARY groups (i.e. not
annotated with @MustBeCaught
and thrown from user
code).
fun main(args: Array<String>) {
parseArgs(args) // Warning is fired
}
private fun parseArgs(args: Array<String>) {
if (args.size == 0) {
throw RuntimeException("Empty list") // `RuntimeException` is not annotated with `@MustBeCaught`.
}
// ...
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
In some cases the standard library specification declares that some
exception is possible, but actually it could never be thrown. The
warnings of such cases are NO_CATCH.LIBRARY.PEDANTIC
type.
For example, C++ std::string
constructors assume that there
is some maximal capacity of the string that could be less than the size
of the addressable memory. So, if the user asks std::string
to create a string with the size more than this maximal capacity, then
std::string
constructor will throw
std::length_error
. In most standard library implementations
the maximal capacity is equal to the size of the addressable memory and
so there will be std::bad_alloc
exception, not
std::length_error
, for too big strings.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
This C-specific checker finds situations where a pointer value is cast to a pointer with base type of different size. This may lead to a buffer overflow or unexpected value when this pointer is dereferenced.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
This Microsoft Windows-specific checker finds situations when a non-BSTR value is incorrectly converted to BSTR.
BSTR bstring() {
wchar_t *string = L"string";
return string; /* Incorrect: `wchar_t*` can't be directly casted to BSTR. */
}
Fixed code:
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
This checker finds situations where the address of some non-array variable is used as an array.
void set(int* buf) {
buf[10] = 0;
}
int test1() {
int x = 0;
set(&x); // There will be memory access violation.
return x;
}
Subtype VARIABLE_IS_NOT_ARRAY.UNION
additionally reports similar warnings if a member of a union that is
smaller in size than the largest member is accessed as array.
union U {
int a;
unsigned char c[sizeof(int) * 4];
};
void test(union U *t) {
int *a = &t->a;
a++;
*a = 10;
}
While this code works correctly, it is advised to explicitly add
union member int a_as_array[4]
to access the union data as
an array of int
s.
Subtype VARIABLE_IS_NOT_ARRAY.MIGHT
additionally reports all calls where a pointer to a non-array variable
is passed to some unknown function, which could not be analyzed to
determine if it uses the argument as an array.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | No |
This checker is similar to VARIABLE_IS_NOT_ARRAY, but reports calls of functions that use variables addresses, passed to them as parameters, as arrays.
void fill(char *p, int len) {
for (int i = 0; i < len - 1; i++) {
p[i] = 'a';
}
p[i] = 0;
}
void use() {
int k = 5;
fill(&k, sizeof(int) + 1); // Warning emitted, k is not an array of several ints
fill(&k, sizeof(int)); // No warning, size matches
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
The checker detects situations where an address of a non-array
variable is used like an array. Unlike other
VARIABLE_IS_NOT_ARRAY
issues,
VARIABLE_IS_NOT_ARRAY.VA_ARG
is reported only if the
addressed variable is declared as the last parameter before the variadic
argument list declaration of the function, where the warning is
reported.
In this case an array-like access to the address of the last declared parameter before a variadic argument list declaration can be used as a hack to access the rest of the passed arguments, however it should be considered as quite a spurious and non-portable hack.
#include <stdarg.h>
void demo(int first, ...) {
va_list argptr;
int next = first;
va_start(argptr, first);
int count = 0;
while (next != 0) {
next = va_arg(argptr, int);
count++;
}
va_end(argptr);
int *arr = &first;
for (int i = 0; i < count; i++) {
arr[i] = 0; // spurious hack-like access
}
}
In this example arr
variable is used for a hack in an
indexing operator, but points to an int
-type parameter
first
declared as the last parameter before a variadic
argument list declaration.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | High | Yes |
C/C++ | Quality | Normal | High | Yes |
Scala | Quality | Normal | High | Yes |
Kotlin | Quality | Normal | High | Yes |
This checker finds situations where return value of some function is
not checked, while it’s checked in most places where that function is
called. This usually means forgotten error handling. The warning is
emitted based on the function use statistic, without taking into account
the function implementation. Compared to DEREF_OF_NULL.RET, this checker relies on
the function use statistic exclusively, and works with more types of
return value checks than just comparison with a null value. This checker
emits warning if more than
UNCHECKED_FUNC_RES.STAT.THRESHOLD
percents of all such
function calls are checked. The
UNCHECKED_FUNC_RES.STAT.THRESHOLD
configuration option is
equal to 80% by default and can be changed using svace config tool.
sf_no_check_return
specfunction allows to exclude
specific functions from gathering statistic for this checker.class Stat() {
var isReady = false
var counter = 0
fun inc(): Boolean {
counter++
return isReady
}
// ...
}
fun checkedA(s: Stat) {
if (s.inc()) {
println("checked in A")
}
}
fun checkedB(s: Stat) {
if (s.inc()) {
println("checked in B")
}
}
fun unchecked(s: Stat) {
s.inc()
}
The UNCHECKED_FUNC_RES.STAT
warning will be emitted for
inc
call into unchecked
function. Note that
UNCHECKED_FUNC_RES.STAT.THRESHOLD
is set to 65% for this
example.
Possible fix: check the return value of inc
call into
unchecked
function.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Very high | Yes |
Related CWEs: CWE573.
This checker finds situations where va_end
macro is
missing from C/C++ functions that work with variable argument lists.
int no_va(int x, ...) {
va_list va;
va_start(va, x);
if (va_arg(va, int) == x)
return -1; // `va_end` call is missing.
va_end(va);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Normal | Very high | Yes |
This checker finds situations where va_start
macro is
missing from C/C++ functions that work with variable argument lists.
void test(int x, ...) {
va_list va;
if (x != 0)
va_start(va, x);
while (x > 0) {
// ...
x--;
}
va_end(va);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
This C/C++-specific warning type is reported for expressions with
side effects in the assert
macro parameter. Since
assertions are usually disabled in release mode and in this case
assert
macro generates no code the program may unexpectedly
behave differently in debug and release modes.
assert(VLNAME(windowBits_IDAT)[--i].name == range_lo);
VLNAME(windowBits_IDAT)[i].value = wb > 8 ? 9 : 8;
If macro NDEBUG
is set then the assert expression
becomes a no-op and variable i
isn’t decremented as
expected.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Average | Yes |
JavaScript | Quality | Major | Average | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Go | Quality | Major | Average | Yes |
Kotlin | Quality | Major | Average | Yes |
Python | Quality | Major | Average | Yes |
C# | Quality | Normal | Average | Yes |
Related CWEs: CWE398.
This checker locates code snippets that were copied and pasted. However, because of an incomplete change, some portions of the copy are unintentionally left unchanged.
int square(int x) {
return x * x;
}
int bad_copy_paste_example(int a, int b, int x, int y) {
int result = 0;
if (b > 0) {
result = square(a) + square(x); // Defect: value `a` might be `b`.
}
if (a > 0) {
result = square(a) + square(x); // Original code.
}
return result;
}
data class ColorData(val rgb: IntArray)
fun badCopyPasteExample(d: ColorData, flg1: Int, flg2: Int): Int {
var ret = 0
if (flg1 > 1 && d.rgb[0] == 255 && d.rgb[1] == 255 && d.rgb[2] == 255) {
ret += flg2 // Defect: value `flg2` might be `flg1`.
}
if (flg2 > 1 && d.rgb[0] == 0 && d.rgb[1] == 0 && d.rgb[2] == 0) {
ret += flg2 // Original code.
}
return ret
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
JavaScript | Quality | Major | Unknown | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Go | Quality | Major | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
Python | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Unknown | No |
This checker reports a warning when both true and false branches of a conditional operator are identical. Such useless code expressions are commonly caused by a typo, forgotten update of a copy-pasted code or an error in the application logic.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
JavaScript | Quality | Major | Unknown | Yes |
Go | Quality | Major | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
Subtype of SIMILAR_BRANCHES that is emitted for identical cases of a switch statement.
class Example {
public static String similarBranches(int x) {
switch (x) {
case 0: {
return "Zero";
}
case 1: {
return "One"; // SIMILAR_BRANCHES.SWITCH
}
case -1: {
return "One"; // SIMILAR_BRANCHES.SWITCH
}
default: {
return null;
}
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
This checker reports the same issues as SIMILAR_BRANCHES but unlike the base type it additionally assumes that every function returns the same value for same arguments. This allows to catch more issues but results in false reports if the assumption proves to be wrong.
int func(int a, char* str);
void foo(int y) {
if (func(1, "aaa")) {
bar("aaaa");
} else if (func(1, "aaa")) { //warning is emitted
bar("bbbb");
}
}
void assign_op_call(Attr attrs, bool flag) {
//regular SIMILAR_BRANCHES does not emit a warning here because it is not clear that
//Attr::operator= has the same behavior for the same arguments.
if (flag) //warning
attrs[1] = "aaa";
else
attrs[1] = "aaa";
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | CodingStyle | Normal | Unknown | No |
This checker is specific to JS code and finds usages of for-in loop where the loop body suggests that a for-of loop could be used instead.
function bad() {
let a = [1, 2, 3]
let sum = 0
for (let i in a) // Can be replaced with `for ... of`
sum += a[i]
}
function ok() {
let a = [1, 2, 3]
let sum = 0
for (let x of a)
sum += x
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
C# | Quality | Minor | Unknown | Yes |
Related CWEs: CWE483.
This checker finds many cases where the indentation structure of the code does not match the syntactic nesting.
void foo(int a, int b) {
if (a > b)
b++;
a++; // Doesn't belong to the `if` body but indentation suggests it is.
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
JavaScript | Quality | Major | Unknown | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Go | Quality | Major | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
Python | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
This checker finds possible cases where the arguments to a function are provided in an incorrect order.
In the following example a function with prototype
void *calloc(size_t nmemb, size_t size);
is called with
swapped arguments.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Security | Minor | Unknown | No |
This checker finds potential issues related to updating a loop index variable multiple times in the loop body. While by itself this is not a problem due to increased control flow complexity occasionally such code may contain hard to notice issues.
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 |
Kotlin | Quality | Normal | Unknown | No |
Python | Quality | Minor | Unknown | No |
This detector finds situations when a result of a function call is assigned to a local variable, but is never used afterwards. Such situation is commonly caused by a typo or a fault in the program logic.
UNUSED_FUNC_RES.MINOR
is
a subtype of UNUSED_FUNC_RES that reports
warnings that are more likely to be intentional or not cause any
issues.
typedef struct {
int mem;
} storage, something;
storage* add_something_to_storage(storage*, something*);
void process(storage* stor, something* smt) {
storage* new_stor = add_something_to_storage(stor, smt);
// ...
// Use `stor` instead of `new_stor`.
// ...
return;
}
fun get() = (0..100).random()
fun use(value: Int) = println(value)
fun example() {
val r = get()
// ...
}
fun possibleFix() {
val r = get()
use(r)
}
Function example
illustrates the defect: the return
value of the get
call is assigned to the r
variable, which is not used until the example
function
terminates.
Function possibleFix
illustrates a possible fix: use the
return value of the get
call.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | High | Yes |
C/C++ | Quality | Minor | High | Yes |
Scala | Quality | Minor | High | Yes |
Go | Quality | Minor | High | Yes |
Kotlin | Quality | Normal | High | No |
Python | Quality | Minor | High | Yes |
This detector finds situations when the result of a function call is assigned to a local variable but is never used before the variable reassignment.
UNUSED_FUNC_RES.REWRITE.MINOR
is a subtype of UNUSED_FUNC_RES.REWRITE that reports
warnings that are more likely to be intentional or not cause any
issues.
fun get() = (0..100).random()
fun use(value: Int) = println(value)
fun example() {
var r = get()
// ...
r = get()
use(r)
}
fun possibleFix() {
var r = get()
use(r)
r = get()
use(r)
}
Function example
illustrates the defect: the return
value of the get
call is assigned to the r
variable, which is not used until the return value of the second
get
call is also assigned to r
.
Function possibleFix
illustrates a possible fix: use the
return value of both get
calls.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
It is a subtype of UNUSED_FUNC_RES for junit tests. The warning is emitted if result of some function is not checked in junit tests.
import junit.framework.TestCase;
class My {
int a;
int b;
public My(int a, int b) {
this.a = a;
this.b = b;
}
int add() {
return a + b;
}
}
public class MyTest extends TestCase {
public void testAdd1() {
My my = new My(1, 2);
assertEquals(3, my.add());//result is checked - all ok
}
public void testAdd2() {
My my = new My(10, 20);
int sum = my.add();//result is not used - warning
//nothing
}
public void testAdd3() {
My my = new My(10, 20);
my.add();//result is not used too - warning
//nothing
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Average | Yes |
Visual Basic .NET | Quality | Minor | Unknown | Yes |
C/C++ | Quality | Minor | Average | Yes |
Python | Quality | Minor | Average | Yes |
C# | Quality | Minor | Unknown | Yes |
This checker finds situations when a variable is initially assigned a value but is always reassigned a new value before using the original one.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Average | Yes |
C/C++ | Quality | Minor | Average | Yes |
Python | Quality | Minor | Average | No |
Related CWEs: CWE563.
This checker finds situations when a function parameter is assigned a new value that is never used afterwards.
void test(int rrx, int x) {
int x_ = 0;
if (x > rrx) {
x_ = x;
} else {
x = 5; // UNUSED_VALUE.PARAM_ASSIGN
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | High | Yes |
C/C++ | Quality | Minor | High | Yes |
Python | Quality | Minor | High | No |
This checker finds situations when a function parameter is assigned
with an unused NULL
value.
#define ERROR_NONE 1
#define INVALID_PARAMETER 2
typedef struct _List List;
struct _List {
int data;
List *next;
};
int free_list(List *list) {
if (!list)
return INVALID_PARAMETER;
list = NULL; // UNUSED_VALUE.PARAM_ASSIGN.NULL
return ERROR_NONE;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Related CWEs: CWE563.
This checker finds situations when uninitialized local variable is not used by function.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Related CWEs: CWE563.
This checker finds situations when a variable is assigned non-constant value that will be overwritten.
int use(int a);
void no_warning() {
int x;
x = 5;//no warning, such cases are less interesting.
x = 10;
use(x);
}
void warning(int z) {
int x;
x = z;//warning, z - won't be used.
x = 10;
use(data);
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
This checker finds situation when a non-value returning function returns a non-void value.
bar() {
return 1;
}
void example(void) {
return bar(); // UNUSED_RETURN_VALUE
} // Function declared void
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
This checker finds situations when a function parameter is not used in the function body and is just reassigned on all or some paths. This may mean that it is accidentally used instead of a (similarly named) local variable.
Note: no warning is emitted for cases where parameter is unused but is not assigned a new value at all.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
Check if address of a local variable is stored in a global one and this global variable is not re-assigned before leaving the local variable’s scope. Accessing such variable destroyed on exit from their scope using its address leads to undefined behaviour.
For other cases when address of a local variable may leave its scope see ADDR_OF_LOCAL_ESCAPES_SCOPE.
typedef struct {
int x, y
} point_t;
point_t *current_point = 0;
extern void process_point(const point_t *point);
void process_current_point() {
process_point(current_point);
}
void good() {
point_t one = {1, 1};
current_point = &one;
process_current_point();
current_point = 0; // It's fine, the address is not leaked
}
void bad() {
point_t zero = {0, 0};
current_point = &zero; // Alarm: address of zero is leaked via current_point
process_current_point();
}
void ugly() {
process_current_point();
}
void director() {
good();
bad();
ugly(); // We access a local variable from function 'bad()' that is no longer alive here
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
Check if address of a local variable is somehow stored in an outer scope and is accessible when this variable is no longer alive.
This checker finds various rarer patterns other than those detected by DO_NOT_ASSIGN_ADDR_OF_LOCAL_TO_GLOBAL.
struct Something {
int x, y;
};
class Storage {
Something *something;
public:
Storage() : something(nullptr) {}
void unsafeSave(Something *smth) {
something = smth;
}
void use() {
if (something)
something->x += something->y;
}
};
void storeSomething(bool flag) {
Storage storage;
if (flag) {
Something smth = {2, 3};
storage.unsafeSave(&smth); // Alarm: address of smth is leaked!
}
storage.use(); // Address of smth is dereferenced here, though smth is no longer alive
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Check for isolated occurrences of sizeof
operators that
are technically legal in C/C++, yet are often erroneous.
In the following example, param_not_really_an_array
looks syntactically like an array, but is actually a pointer. When you
apply the sizeof operator to the pointer, it yields the size of a
pointer (typically 4 or 8 bytes), and not the expected 10 bytes:
void f(char param_not_really_an_array[10]) {
memset(param_not_really_an_array, 0, sizeof(param_not_really_an_array));
}
In the following example, sizeof
is applied to the
pointer this
rather than to the object *this
,
which yields an incorrect value for the size of the object:
In the following example, the sizeof
operator is applied
to the address of s
rather than to s
itself,
which yields a larger value and overwrites adjacent memory
locations:
In the following example, the sizeof
operator is applied
to the pointer arithmetic expression buf - 3
. The
expression has type char *
and is likely 4 or 8 bytes in
size, rather than sizeof
applied to buf
, and
then subtracting 3 from the result, which yields the desired value of
97:
void f() {
char buf[100];
buf[0] = 'x';
buf[1] = 'y';
buf[2] = 'z';
memset(buf + 3, 0, sizeof(buf - 3));
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Average | Yes |
Related CWEs: CWE467, CWE468, CWE783.
Check for combinations of pointers and sizeof
expressions that appear to be mismatched.
When a pointer base type and sizeof
expression parameter
type are both pointers but with mismatched base types a subtype SIZEOF_POINTER_TYPE.STRICT
is reported. This may still be a typo or a result of an application
logic error but won’t be an issue for almost any target unless pointers
to these different types have different sizes.
In the following example, only 4 or 8 bytes of the 100-byte object
buf
are cleared:
struct buffer {
char b[100];
};
void f() {
struct buffer buf;
memset(&buf, 0, sizeof(&buf)); // Defect: should have been `sizeof(buf)`.
}
In the following example, only 4 or 8 bytes are allocated for a 100-byte object:
struct buffer {
char b[100];
};
void f() {
struct buffer *p = (struct buffer *)malloc(sizeof(struct buffer *));
/* Defect: should be `sizeof(struct buffer)`. */
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Low | Yes |
Check for combinations of char
pointers and
sizeof
expressions that appear to be mismatched. Compared
to SIZEOF_POINTER_TYPE this warning
subtype is less reliable since some complicated address manipulations
using the most generic char
type may look mismatched but
still be intended.
char *m_alloc(int len, char *c) {
c = (char *)malloc(len * sizeof(c)); // should be "sizeof(*c)"
return c;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Critical | Unknown | Yes |
Related CWEs: CWE121.
This warning type is reported when a wide string is passed to a function working with regular character strings.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE758.
Check for places in the code where the C/C++ language rules for expression evaluation do not determine the order in which side effects happen.
In the following example, it is unclear whether the left or right
side of the operator is evaluated first, so the value of x
might change:
The following example demonstrates side effect ordering problems. The
right-hand side of the assignment is evaluated before the assignment
itself takes place. However, the side effect order is unspecified
because the side effect associated with ++
could happen
before or after the side effect associated with the assignment.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Go | Quality | Major | Average | Yes |
Kotlin | Quality | Normal | Average | Yes |
Related CWEs: CWE197, CWE398, CWE401, CWE456, CWE480, CWE482, CWE570, CWE665.
Check for instances of statements or expressions that do not accomplish anything, or statements that perform an action that is not the intended action.
Note that when an erroneous construct is a part of a macro expansion
NO_EFFECT.MACRO
warning
subtype is reported instead.
In the following example, the left operand of a comma operator has no side effects:
void extra_comma() {
int a, b;
for (a = 0, b = 0; a < 10, b < 10; a++, b++);
// Extra comma, and so `a < 10` is not used.
}
In the following example, only the first pointer is deleted:
In the following example, dereference of the pointer is useless:
In the following example, the boolean test is useless:
void no_effect_test() {
int a, b;
a == b;
// Test has no effect, and is
// likely intended to be the assignment `a = b`.
}
In the following example, an assignment is likely intended:
In the following example, there’s an unsigned comparison against zero:
void unsigned_compare() {
unsigned int a;
if (a < 0) // `a` is unsigned, and so the comparison is never true.
a++;
}
void array_null() {
unsigned int a[3];
unsigned int b[1];
unsigned int c[2];
if (*a == 0)
a[0];
if (b == 0) // The entire array b is compared to 0.
b[1];
if (c[1] == 0)
c[1];
}
In the following example, suspicious arguments are passed to the
memset
function. A size argument of 0 can indicate that the
size and fill arguments are switched. A fill value outside the range of
-1 to 255 will likely lead to truncation. A fill value of 0
is likely intended to be 0:
void bad_memset() {
int *p;
memset(p, '0', l); // Fill value is `0`, and 0 is more likely.
memset(p, l, 0); // Length is 0, and so likely that `l` and 0 reversed
memset(p, 0xabcd, l); /* Fill is truncated, and so memory
will not contain the 0xabcd pattern. */
}
In the following example, the result of the ==
operator
is useless. Most likely, there should have been an assignment
leftBound = defaultBound
.
fun testInsteadAssign(defaultBound: Int = 1) {
var leftBound: Int = 1
if (leftBound > defaultBound)
leftBound == defaultBound
// ...
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | Yes |
Related CWEs: CWE197, CWE398, CWE401, CWE456, CWE480, CWE482, CWE570, CWE665.
Detects a variable which is assigned to itself in a statement or a declaration.
When it is a local variable NO_EFFECT.SELF.LOCAL is reported instead.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Average | No |
Related CWEs: CWE480.
Detects expressions where a local variable is assigned to itself.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Go | Quality | Minor | Unknown | No |
The checker reports function calls that have no effect: the called function has no side effects and the result of a call to it is unused. These calls are either useless and can be removed, or the called function is used inappropriately and its return value should be used.
The call to getNextId
function has no effect and useless
in this example:
func getNextId(id int) int {
if id < 0 || id >= 256 {
return -1
}
return id + 1
}
func processId(id int) int {
getNextId(id) //NO_EFFECT.CALL
return id
}
A possible fix is to use the return value of the call to
getNextId
function:
The call to check
function has no effect and useless in
this example:
A possible fix is to implement the missed behavior in function
check
:
The checker does not report calls to functions with trivial bodies:
in the code below the calls to f1
, f1
,
f3
are not reported.
func f1() {}
func f2(a int, b bool) int {
return 2
}
func f3(a int, b bool) int {
return a
}
func test(a int, b bool) {
f1()
f2(a, b)
f3(a, b)
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | Yes |
This checker warns if it finds that parentheses are possibly missing around an assignment in condition. This code is not only very hard to read but also may contain errors due to operator precedence.
In the example above the x
variable is assigned the
value of the conditional expression f() != y
which may not
be what the programmer intended.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
Check for instances where an extraneous semicolon alters the logic of the code.
In the following example, an if
statement is followed by
an extra semicolon, which results in
do_something_conditionally()
being called
unconditionally:
In the following example, a while
statement is followed
by an extra semicolon. The following block is executed only once,
unconditionally, which might result in a premature return from the
function:
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | Yes |
Visual Basic .NET | Quality | Major | High | Yes |
C/C++ | Quality | Major | Low | Yes |
Scala | Quality | Major | Low | Yes |
Go | Quality | Major | Low | Yes |
C# | Quality | Major | High | Yes |
Related CWEs: CWE369.
Check for instances of divisions where the divisor (denominator) is zero.
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 |
Python | Quality | Major | High | Yes |
Related CWEs: CWE369.
Path sensitive version of the DIVISION_BY_ZERO. It checks if a feasible path exists where some variable is assigned zero, and then this variable is used as a divisor.
fun example(cond: Boolean, value: Int) {
val d: Int = if (cond) 10 else 0
print(value / d)
}
fun possibleFix(cond: Boolean, value: Int) {
val d: Int = if (cond) 10 else 0
if (d != 0) {
print(value / d)
}
}
Function example
illustrates the defect: variable
d
is potentially assigned a zero value, but this variable
is used as a divisor. Function possibleFix
illustrates a
possibleFix
: check d
value before using it as
a divisor.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | High | Yes |
C/C++ | Quality | Minor | High | Yes |
Go | Quality | Minor | High | Yes |
Kotlin | Quality | Minor | High | Yes |
Python | Quality | Minor | High | Yes |
A variant of DIVISION_BY_ZERO.EX which is issued if a variable of a floating-point type whose value can be zero is used as denominator in division operation. The program behaviour in this case is properly defined in many cases but, though it may not lead to abnormal exit, it will likely lead to the unexpected results.
fun example1(cond: Boolean, value: Int) {
val divisor: Double = if (cond) 10.0 else 0.0
print(value / divisor)
}
fun example2(cond: Boolean, value: Double) {
val divisor: Int = if (cond) 10 else 0
print(value / divisor)
}
The defect is exemplified in function example1
. Variable
divisor
of type Double
is assigned zero value
under condition that parameter cond
is false
and then used as denominator in division operation. Note that the same
defect can be detected for integer-typed denominator as well if
numerator is of a floating-point type, as shown in function
example2
. This is due to implicit cast applied to the
integer-type denominator prior to division operation in this case.
Possible fix is to put division operation under condition which excludes zero value of denominator.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Low | Yes |
C/C++ | Quality | Major | Low | Yes |
Scala | Quality | Major | Low | Yes |
Go | Quality | Major | Low | Yes |
Python | Quality | Major | Low | Yes |
Report instances of divisions where divisor is checked (compared with some value) and after this check it can be zero.
int contrTest(int n, int a) {
int z = 1;
if (n < 0 || a > 1) {
z++;
} else {
z += z/a;
}
return z;
}
In this case variable a
is checked, in else
block possible values of variable a
is between minus
infinity and 1. Zero is included in this interval.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | Yes |
C# | Quality | Normal | Unknown | No |
Related CWEs: CWE197.
This checker finds code with an unexpected loss of arithmetic precision due to converting result of integral division into a floating point number.
float div(int a, int b) {
return (a / b);
}
void foo() {
printf("%0.2f\n", div(10, 4)); /* Expected 2.50 but result is 2.00 */
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Visual Basic .NET | Quality | Normal | Unknown | Yes |
Scala | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Unknown | Yes |
This checker finds situations where a method is overridden and the
overriding method must call the respective method from the superclass
but it doesn’t. The analyzer uses a builtin list of such methods whose
overriding methods must call the superclass method (such as
Object.clone
in Java, for example). Custom methods can be
added using an @OverriderMustCallSuper
Java/Kotlin
annotation. Refer to the Overridden methods
chapter of the
Svace Specification Manual document for more
details.
import android.app.Activity
class Child : Activity() {
override fun onDestroy() {
super.onPause()
}
}
class CorrectChild : Activity() {
override fun onDestroy() {
super.onDestroy()
}
}
The NO_BASE_CALL.LIB
warning will be emitted for
function Child.onDestroy
, because it calls an incorrect
Activity.onPause
superclass method.
Possible fix: call proper onDestroy
superclass method as
shown in the CorrectChild.onDestroy
method.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Visual Basic .NET | Quality | Normal | Average | Yes |
Scala | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Major | Unknown | Yes |
C# | Quality | Normal | Average | Yes |
This checker finds situations where a method is overridden and the
overriding method should call the respective method from the superclass
but it doesn’t. This checker emits a warning if a method overrides a
methods from the superclass and doesn’t call it but more than
CALL_SUPER.STAT.THRESHOLD
percent of all overriders call
the superclass method. The CALL_SUPER.STAT.THRESHOLD
configuration variable is equal to 80% by default and can be changed
using svace config tool.
open class Base {
open fun foo(msg: String) = println(msg)
}
class DerivedA : Base() {
override fun foo(msg: String) = super.foo("from A: $msg")
}
class DerivedB : Base() {
override fun foo(msg: String) = super.foo("from B: $msg")
}
class DerivedC : Base() {
override fun foo(msg: String) = println("derived C: $msg")
}
The NO_BASE_CALL.STAT
warning will be emitted for
function foo
overridden into DerivedC
class.
Note that configuration option CALL_SUPER.STAT.THRESHOLD
is
set to 65% for this example.
Possible fix: call super method in DerivedC.foo
method.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | Yes |
This C/C++-specific checker finds situations when an enum-type value is used in places where a condition expression is required. The condition expression will be evaluated to false only if the value of an enum variable is zero. Otherwise, the condition expression will be evaluated to true. A developer may use an enum variable instead of a condition expression intentionally. However, such code pattern makes code less clear, and it sometimes leads to unexpected defects.
enum key {
down = 0, right = 1, left = 2
};
int keyPressed(enum key x) {
if (x) {
return 1;
}
return 0;
}
The developer may intend this behaviour but in this case it is better to write the same code in a more clear way:
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Minor | Unknown | Yes |
This checker finds situations when an enum-type value is used in places where a condition expression is required just as ENUM_TO_BOOLEAN checker but the enum-type does not have an element with zero value. In this case the probability of a defect is higher since there is no enumeration constant that could be evaluated as false value.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | Yes |
C/C++ | Quality | Major | Unknown | Yes |
Related CWEs: CWE484.
This checker finds possible missing break statements in switch-case
statements. Sometimes a break
statement is missing on
purpose; in such cases the checker can be silenced by using comments
containing any of the word combination: fall through
,
fallthrough
, falls through
or
no-break
in the related case block; to silence all warnings
reported for the whole switch statement place the same comment before
the switch statement itself.
int ex_1(int val) {
int i = 0;
switch (val) {
case 0:
i = 2;
// This case probably missing a break statement here.
case 1:
i = 3;
break;
default:
i = -1;
break;
}
return i;
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Kotlin | Quality | Normal | Average | Yes |
This Kotlin-specific detector finds situations where an empty range is created.
fun example() {
for (s in 'a'..'Z') {
// ...
}
}
fun possibleFix() {
for (s in 'A'..'z') {
// ...
}
}
Function example
illustrates the defect: the
'a'..'Z'
expression creates an empty range because its left
bound a
is greater than the right bound Z
since lowercase Latin characters have greater values in the character
table than the uppercase Latin characters.
Function possibleFix
illustrates a possible fix: use
'A'..'z'
expression to create the range.
Function example
illustrates the defect: the variable
x
may not be properly aligned.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | High | No |
Visual Basic .NET | Quality | Normal | Unknown | No |
Go | Quality | Normal | High | No |
Kotlin | Quality | Normal | High | No |
C# | Quality | Normal | Unknown | No |
Local variable or method parameter name “hides” other variable or class member with the same name.
Although this is not a bug itself, it may confuse the developer.
public partial class AddAccountWizardForm : Form
{
...
private Step m_CurrStep = Step.Screen1;
...
private bool ValidateFields(Step m_CurrStep)
{
...
}
...
}
class Example {
private final int someInt;
...
public void doSmth1(int someInt) { // SHADOWED_NAME
...
}
public void doSmth() {
...
int someInt = 123; // SHADOWED_NAME
...
}
private class NestedClass {
private final int someInt; // SHADOWED_NAME
...
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Major | Unknown | No |
Visual Basic .NET | CodingStyle | Minor | Very high | Yes |
Kotlin | CodingStyle | Major | Unknown | No |
C# | CodingStyle | Minor | Very high | Yes |
Related CWEs: CWE396.
Reports warning if exception type in catch block is too broad. It is better to handle specific cases separately.
For Java and Kotlin reported types are:
java.lang.Throwable
, java.lang.Exception
.
class A {
void Bad() {
try {
} catch (System.Exception e) { // <CATCH.GENERIC_EXCEPTION The catch clause is too broad, because it catches any exception type >
}
try {
} catch { // <CATCH.GENERIC_EXCEPTION The catch clause is too broad, because it catches any exception type >
}
}
}
class Example {
public Integer bad(String s) {
try {
return Integer.parseInt(s);
} catch (Exception ignored) { // CATCH.GENERIC_EXCEPTION
return null;
}
}
public Integer possibleFix(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException ignored) { // OK
return null;
}
}
}
Prefer handling specific exceptions instead of all possible.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Major | Unknown | No |
Visual Basic .NET | CodingStyle | Minor | Very high | Yes |
Kotlin | CodingStyle | Major | Unknown | No |
C# | CodingStyle | Minor | Very high | Yes |
Related CWEs: CWE395.
Reports warning if catch (NullReferenceException)
/
catch (NullPointerException)
is used, because it is better
to compare with null instead of catching null dereference.
class A {
void Bad() {
try {
// code
} catch (System.NullReferenceException e) { // <CATCH.NULL_POINTER_EXCEPTION Catching NullReferenceException may hide errors >
}
}
}
class Example {
public String bad(String s, int pos) {
try {
return s.substring(1);
} catch (NullPointerException ignored) { // CATCH.NULL_POINTER_EXCEPTION
return "";
}
}
public String possibleFix(String s, int pos) {
if (s == null) {
return "";
}
return s.substring(pos);
}
}
Replace catch (NullReferenceException)
/
catch (NullPointerException)
with null checking of
potentially nullable variable.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | No |
JavaScript | Quality | Normal | Unknown | No |
Visual Basic .NET | Quality | Minor | High | Yes |
Kotlin | Quality | Normal | Unknown | No |
Python | Quality | Normal | Unknown | Yes |
C# | Quality | Minor | High | Yes |
Related CWEs: CWE390.
Warn about empty catch block.
public class A
{
public void Bad(int x)
{
try
{
// code
}
catch (NotImplementedException)
{ // <CATCH.NO_BODY Empty body of catch statement has no effect >
}
}
}
class Example {
public void bad() {
try {
...
} catch (RuntimeException e) { // CATCH.NO_BODY
}
}
public void possibleFix1() {
try {
...
} catch (RuntimeException e) { // OK
throw new IllegalStateException(e);
}
}
public void possibleFix2() {
try {
...
} catch (RuntimeException ignored) { // OK
}
}
}
Handle the exception in catch block or name exception parameter as
ignored
/skip
/etc.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Major | Unknown | No |
Visual Basic .NET | CodingStyle | Minor | Very high | Yes |
Kotlin | CodingStyle | Major | Unknown | No |
C# | CodingStyle | Minor | Very high | Yes |
Related CWEs: CWE397.
Reports warning if a generic exception is thrown.
For Java and Kotlin reported types are:
java.lang.Throwable
, java.lang.Exception
.
class A {
void Bad() {
bool error;
try {
// code
if (error)
throw new Exception(); // <THROW.GENERIC_EXCEPTION Throwing System.Exception forces the handler to catch any exceptions >
}
}
}
class Example {
public void bad1(String s) throws Exception {
if (s == null) throw new Exception("s can't be null");
...
}
public void bad2(String s) throws Throwable {
if (s == null) throw new Throwable("s can't be null");
...
}
public void possibleFix(String s) {
if (s == null) throw new IllegalArgumentException("s can't be null");
...
}
}
Create specific exception type for the error or use one of the predefined exception types from standard library.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
Reports warning if the synchronized
block is empty.
class Example {
private final Object lock;
...
public void bad() {
synchronized(lock) { // EMPTY_SYNC
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Major | Unknown | No |
JavaScript | CodingStyle | Major | Unknown | No |
Visual Basic .NET | CodingStyle | Minor | Unknown | Yes |
Go | CodingStyle | Major | Unknown | No |
Kotlin | CodingStyle | Major | Unknown | No |
C# | CodingStyle | Minor | Unknown | Yes |
Related CWEs: CWE478.
Reports warning if the switch statement doesn’t cover all cases and doesn’t have default branch.
For C#, the checker may emit INCOMPLETE_SWITCH instead, depending on the number of missing switch cases.
public class A
{
public void Bad(int x)
{
switch (x) // < Switch entry >
{
case 0:
x++;
break;
case 1:
x--;
break;
} // <DEFAULT_CASE_MISSING Missing default case in switch (x) construction >
}
}
In the example all values of variable x
except for the
0
and 1
were not handled. It may cause an
error if these values should be considered or it will be necessary to
handle new possible values in future.
public class A
{
public void Bad(int x)
{
switch (x)
{
case 0:
x++;
break;
case 1:
x--;
break;
default:
throw new NotImplementedException();
}
}
}
class Example {
public String test_withError(int kilos) {
switch(kilos) { // DEFAULT_CASE_MISSING
case 1:
return "K";
case 2:
return "M";
case 3:
return "G";
case 4:
return "T";
}
return null;
}
public String test_withoutError(int kilos) {
switch(kilos) { // OK
case 1:
return "K";
case 2:
return "M";
case 3:
return "G";
case 4:
return "T";
default:
throw new UnsupportedOperationException("Unexpected size 10^" + kilos + " to get suffix");
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
Visual Basic .NET | Quality | Normal | Unknown | No |
C# | Quality | Normal | Unknown | No |
In case of C#, reports warning if at least one argument of
ReferenceEquals
call is not of a reference type.
In case of Java, checks whether objects which should be compared
using equals
method, are compared by reference instead.
public struct Polynomial<Variable, Expression>
{
/* ... */
}
/* ... */
Polynomial<BoxedVariable<Variable>, BoxedExpression> pol;
/* ... */
Contract.Assume(!object.ReferenceEquals(pol, null));
import java.util.Objects;
class Example {
public boolean bad(String fst, String snd) {
return fst == snd;
}
public boolean possibleFix(String fst, String snd) {
return Objects.equals(fst, snd);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Major | Unknown | No |
JavaScript | Quality | Major | Unknown | No |
Kotlin | Quality | Major | Unknown | No |
Python | Quality | Major | Unknown | Yes |
Related CWEs: CWE584.
Reports warning if there is a return statement inside a finally block. This may lead to exception being discarded.
class Example {
public boolean returnInFinally(String s) {
boolean success = false;
try {
success = doSomething(s);
} finally {
return success; // RETURN_IN_FINALLY: NumberFormatException will be discarded
}
}
public boolean doSomething(String s) {
Integer.parseInt(s);
return true;
}
}
def foo(file_name):
file = None
try: # try-1
file = open(file_name, 'r')
return file.read()
except Exception:
print('Error')
raise
finally:
try: # try-2
file.close()
except Exception:
print('Error')
return None # RETURN_IN_FINALLY: If there's an exception in try-1, it will be discarded
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | Yes |
Python | Quality | Normal | Unknown | Yes |
Reports warning when in JavaScript or Python code a check for NaN
(Not-a-Number) value is done with comparison operators (==
,
!=
…) and not with special function isnan
.
function foo(n1) {
if (n1 == NaN) { // Use isNaN(n1)
...
}
if (n1 === NaN) { // Use isNaN(n1)
...
}
if (n1 != NaN) { // Use !isNaN(n1)
...
}
if (n1 !== NaN) { // Use !isNaN(n1)
...
}
if (isNaN(n1)) { // Correct check for Not-a-Number
...
}
}
foo(2);
foo(NaN);
import math
from math import nan
n = float('nan')
if n == float('nan'): # use math.isnan(n)
...
if n == nan: # use math.isnan(n)
...
if n != nan: # use math.isnan(n)
...
if math.isnan(n): # correct check for NaN
...
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | Yes |
Reports warning when result of typeof
operator is
compared to wrong (usually mistyped) string literal.
function foo(baz) {
if (typeof foo === "strnig") { // mistyping in "strNIg" literal
;
}
if (typeof foo == "undefimed") { // mistyping in "undefiMed" literal
;
}
if (typeof baz != "nunber") { // mistyping in "nuNber" literal
;
}
if (typeof baz !== "fucntion") { // mistyping in "fuCNtion" literal
;
}
name = "function"
if (typeof foo == name) { // correct
console.log('"foo" has type "function"')
}
if (typeof foo === typeof baz) { // correct
;
} else {
console.log('tyoe of "foo" doesn\'t equal to type of "baz');
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | Yes |
Reports warning when in JavaScript code a getter doesn’t have return statement. Every getter is expected to return a value.
// incorrect code examples:
p = {
get name(){
// no returns.
}
};
Object.defineProperty(p, "age", {
get: function (){
// no returns.
}
});
class P{
get name(){
// no returns.
}
}
// correct code examples:
pc = {
get name(){
return "nicholas";
}
};
Object.defineProperty(pc, "age", {
get: function (){
return 18;
}
});
class Pc{
get name(){
return "nicholas";
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | Yes |
Reports warning when in JavaScript code a setter does have return statement. Setters cannot return values.
// incorrect code examples:
var foo = {
set a(value) {
this.val = value;
return value;
}
};
class Foo {
set a(value) {
this.val = value * 2;
return this.val;
}
}
const Bar = class {
static set a(value) {
if (value < 0) {
this.val = 0;
return 0;
}
this.val = value;
}
};
Object.defineProperty(foo, "bar", {
set(value) {
if (value < 0) {
return false;
}
this.val = value;
}
});
// correct code examples:
var foo = {
set a(value) {
this.val = value;
}
};
class Foo {
set a(value) {
this.val = value * 2;
}
}
const Bar = class {
static set a(value) {
if (value < 0) {
this.val = 0;
return;
}
this.val = value;
}
};
Object.defineProperty(foo, "bar", {
set(value) {
if (value < 0) {
throw new Error("Negative value.");
}
this.val = value;
}
});
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | No |
Reports warning when in JavaScript code returning of a value in the constructor of a class is performed. Usually it’s mistake resulting from unfamiliarity with the language or a copy-paste error.
// incorrect code examples:
class A {
constructor(a) { // svace: emitted CONSTRUCTOR_WITH_RETURN
this.a = a;
return a;
}
}
class B {
constructor(f) { // svace: emitted CONSTRUCTOR_WITH_RETURN
if (!f) {
return 'falsy';
}
}
}
// correct code examples:
class C {
constructor(c) { // svace: not_emitted CONSTRUCTOR_WITH_RETURN
this.c = c;
}
}
class D {
constructor(f) { // svace: not_emitted CONSTRUCTOR_WITH_RETURN
if (!f) {
return; // Flow control.
}
f();
}
}
class E {
constructor() { // svace: not_emitted CONSTRUCTOR_WITH_RETURN
return;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | Yes |
Checker finds when an empty pattern is used in destructuring.
// incorrect code examples:
var {} = foo;
var [] = foo;
var {a: {}} = foo;
var {a: []} = foo;
function foo({}) {}
function bar([]) {}
function baz({a: {}}) {}
function qux({a: []}) {}
// correct code examples:
var {a = {}} = foo;
var {a = []} = foo;
function foo({a = {}}) {}
function bar({a = []}) {}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | No |
Array has several methods for filtering, mapping, and folding. If return statement in a callback of those methods is not present, it is probably a mistake. If returned results are not needed, consider using .forEach instead.
function bad() {
let a = [1, 2, 3];
a = a.map(function (x) {
x * 2;
});
console.log(a); // Outputs [undefined, undefined, undefined]
}
function ok() {
let a = [1, 2, 3];
a = a.map(function (x) {
return x * 2;
});
console.log(a); // Outputs [2, 4, 6]
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | No |
Array.forEach has a callback which should not return a value. Any value returned by callback of this method won’t be used, so having a return statement in it is probably a mistake.
function bad() {
let a = [1, 2, 3];
a.forEach(function (x) {
return x * 2;
});
console.log(a); // Outputs [1, 2, 3]
}
function ok() {
let a = [1, 2, 3];
a = a.map(function (x) {
return x * 2;
});
console.log(a); // Outputs [2, 4, 6]
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
JavaScript | Quality | Normal | Unknown | Yes |
Checker finds whether an exception parameter of catch-block is being reassigned. Since caught exception can be referred only through that parameter, assigning value to it may lead to exception being lost.
try {
// code
} catch (e) {
e = 10; // svace: emitted CATCH.EXCEPTION_ASSIGN
}
try {
// code
} catch (e) {
var foo = 10; // svace: not_emitted CATCH.EXCEPTION_ASSIGN
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
C# | Quality | Minor | Low | Yes |
Related CWEs: CWE783.
Ternary operator ?
has the lowest precedence (except
assignment operator). It may cause error when the result of a ternary
operator has to be used as the right operand of another operator with
higher precedence.
In C# it has a subtype TERNARY_OPERATOR_PRECEDENCE.PARENTHESIZED for the case when the whole conditional expression is enclosed in parentheses, which don’t specify the operator precedence in the condition, but still are likely to express the developer’s intent.
import java.lang.management.ManagementFactory;
public class TernaryOp {
static public void foo_1() {
String name = ManagementFactory.getRuntimeMXBean().getName();
String text = "process name " + name == null ? "" : name; // svace: emitted TERNARY_OPERATOR_PRECEDENCE
System.out.println(text);
}
static public void foo_2() {
String name = ManagementFactory.getRuntimeMXBean().getName();
String text = "process name " + (name == null ? "" : name); // svace: not_emitted TERNARY_OPERATOR_PRECEDENCE
System.out.println(text);
}
static public void foo_3() {
int i = 2;
int j = 3;
String name = ManagementFactory.getRuntimeMXBean().getName();
String text = "process name " + (i > j ? "" : name == null ? "" : name); // svace: not_emitted TERNARY_OPERATOR_PRECEDENCE
System.out.println(text);
}
static public void foo_4() {
int i = 2;
int j = 3;
String name = ManagementFactory.getRuntimeMXBean().getName();
String text = "process name " + (i > j && j > 0 && i < 3 ? "" : name == null ? "" : name); // svace: not_emitted TERNARY_OPERATOR_PRECEDENCE
System.out.println(text);
}
static public void foo_5() {
int i = 2;
int j = 3;
String name = ManagementFactory.getRuntimeMXBean().getName();
String text = "process name " + (i + 2 > j && name != null ? name : ""); // svace: not_emitted TERNARY_OPERATOR_PRECEDENCE
System.out.println(text);
}
static public void foo_6() {
int i = 2;
int j = 3;
String text = "The result is " + (i + 2 > j ? 77 : 44); // svace: emitted TERNARY_OPERATOR_PRECEDENCE
System.out.println(text);
}
static public void foo_7() {
int i = 2;
int j = 3;
String name = ManagementFactory.getRuntimeMXBean().getName();
String text = i < j ? ("process name " + name == null ? "" : name) : ""; // svace: emitted TERNARY_OPERATOR_PRECEDENCE
System.out.println(text);
}
}
In the above example in the code:
String text = "process name " + name == null ? "" : name;
the check for null
always true
as
+
has higher priority then ?
. To fix the error
the code has to be changed to:
String text = "process name " + (name == null ? "" : name);
class TernaryOp
{
static public void Foo_1_Warn(string errorMsg)
{
string errorStr = "Error" + errorMsg == null ? "" : @": {errorMsg}"; // TERNARY_OPERATOR_PRECEDENCE
Console.WriteLine(errorStr);
}
static public void Foo_1_Fix(string errorMsg)
{
string errorStr = "Error" + (errorMsg == null ? "" : @": {errorMsg}");
Console.WriteLine(errorStr);
}
static public void Foo_2_Warn(int a, int b)
{
int result = a + b < 0 ? 100 : 0; // TERNARY_OPERATOR_PRECEDENCE
Console.WriteLine(result);
}
static public void Foo_2_Fix(int a, int b)
{
int result = a + (b < 0 ? 0 : b);
Console.WriteLine(result);
}
static public void Foo_3_NoWarn(int i, int j)
{
int result = i > j && j > 0 && i < 100 ? 111 : 222;
Console.WriteLine(result);
}
static public void Foo_4_NoWarn(int i, int j)
{
int result = i > j ? 111 : 222;
Console.WriteLine(result);
}
}
In Foo_1_Warn
method the check for null
is
always true
, because the result of concatenation of
variable and non-empty string constant is compared with
null
. But the developer meant to compare
errorMsg
with null
. To fix this error, ternary
operator should be parenthesized. It is shown in Foo_1_Fix
method. There is the same semantic error in Foo_2_Warn
method, but in this case condition may be false
. Also fixed
by parenthesizing the ternary operator in Foo_2_Fix
method.
In Foo_3_NoWarn
and Foo_4_NoWarn
the
conditions cannot be misinterpreted, so checker does not provide
diagnoctics.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | Yes |
In Java, conversion from int
to float
or
from long
to float
/double
may
result in loss of precision. When using ternary operator with values of
these types, such conversion will be made implicitly.
This checker finds situations when value in some branch of a ternary operator is implicitly converted to a floating point type which may lead to a loss of precision.
class Example {
void testBad(boolean cond) {
// WARN: Integer is implicitly converted to Float with loss of precision
Number x = cond ? Integer.valueOf(100500900) : Float.valueOf(1.2f);
System.out.println(x); // "1.00500896E8" is printed, not "100500900"
System.out.println(x.getClass()); // "java.lang.Float" is printed
}
void testOk(boolean cond) {
// OK: Explicit conversion to float
Number x = cond ? (float) Integer.valueOf(100500900) : Float.valueOf(1.2f);
// OK: Explicit conversion to float by calling floatValue()
Number y = cond ? Integer.valueOf(100500900).floatValue() : Float.valueOf(1.2f);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Calling java.util.Collection::toArray
without arguments
returns array of type Object[]
. Checker finds situations
whether result of this call is cast to other array type which can lead
to ClassCastException
.
class Example {
String[] testBad(List<String> list) {
return (String[]) a.toArray(); // ERROR: ClassCastException
}
String[] testOk(List<String> list) {
return a.toArray(new String[0]); // OK
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Normal | Unknown | No |
Kotlin | CodingStyle | Normal | Unknown | No |
Checker finds situations when a bitwise and/or operator is used for operands of boolean type. Bitwise operators usually can be replaced by logical (short-circuit) ones to avoid unnecessary computations.
class Example {
boolean testBad() {
// WARN: getSecondBool is always evaluated
if (getFirstBool() | getSecondBool()) {
// ...
}
}
boolean testOk() {
// OK: getSecondBool is not evaluated, if getFirstBool is true
if (getFirstBool() || getSecondBool()) {
// ...
}
}
boolean getFirstBool() {
return true;
}
boolean getSecondBool() {
// ...
// Long computation
// ...
return false;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds situations when method of a collection is called with itself as an argument.
import java.util.*;
class Example {
public void test(List<Object> objs) {
objs.add(objs); // ERROR: May cause StackOverflowError, if 'hashCode()' is called on objs
objs.addAll(objs); // ERROR: Adding a collection to itself
objs.containsAll(objs); // ERROR: Always returns true
objs.removeAll(objs); // ERROR: Similar to calling 'clear()'
objs.retainAll(objs); // ERROR: Does nothing
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds situation when collection is modified during iteration over it. This can lead to undefined behaviour or an exception being thrown.
import java.util.*;
class Example {
void testBad() {
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
numbers.forEach(number -> {
if (number % 2 == 0) {
numbers.remove(number); // ERROR: ConcurrentModificationException is thrown
}
});
// ...
}
void testOk() {
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> copy = new ArrayList<>(numbers);
numbers.forEach(number -> {
if (number % 2 == 0) {
copy.remove(number); // OK: Copy is modified, not an original list
}
});
numbers = copy;
// ...
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Most of the methods of generic collections in Java were introduced
before generics, so they usually accept Object
as a
parameter.
Some examples are:
Collection.remove(Object o)
Collection.contains(Object o)
List.indexOf(Object o)
Map.containsKey(Object key)
Map.containsValue(Object value)
Map.get(Object key)
Map.remove(Object key)
Invoking this method with the argument of wrong type may lead to unexpected results. This checker finds situations when such method is invoked with an argument of a type which is not a subtype of items in collection.
import java.util.*;
class Example {
public boolean testBad(Collection<Integer> collection, String elementToFind) {
return collection.contains(elementToFind); // ERROR: Always returns false
}
public boolean testOk(Collection<Integer> collection, Integer elementToFind) {
return collection.contains(elementToFind); // OK
}
public boolean testOkSubtype(Collection<Number> collection, Integer elementToFind) {
return collection.contains(elementToFind); // OK: Integer is subtype of Number
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Case-insensitivity regular expression flag (like (?i)
or
Pattern.CASE_INSENSITIVE
) only affects ASCII letters. When
using this flag with Unicode letters, special Unicode flags (like
(?u)
, (?U)
, Pattern.UNICODE_CASE
or Pattern.UNICODE_CHARACTER_CLASS
) must be used.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR: SÖMETHING doesn't match the regex below
Pattern.compile("sömething", Pattern.CASE_INSENSITIVE);
// OK
Pattern.compile("sömething", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
Pattern.compile("sömething", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CHARACTER_CLASS);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Anchors (like $
or ^
) in regular
expressions have higher precedence than alternative operator
(|
). When using them both in single regular expression it
might be confusing, so it is better to put each alternative or a whole
expression in a group.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR: "^a|bc|d$" is equivalent to "(^a)|bc|(d$)", not "^(a|bc|d)$"
Pattern.compile("^a|bc|d$");
// OK
Pattern.compile("^(a|bc|d)$");
Pattern.compile("(^a)|bc|(d$)");
Pattern.compile("^(?:a|bc|d)$");
Pattern.compile("(?:^a)|bc|(?:d$)");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Empty groups (both capturing and non-capturing) should not be used in regular expressions.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR: Matches "foo", not "foo()"
Pattern.compile("foo()");
// OK
Pattern.compile("foo\\(\\)"); // svace: not_emitted REGEX.EMPTY_GROUP
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
When using Unicode grapheme clusters in characters classes of regular expressions, match is performed on single code points, not on a whole clusters, which may lead to unexpected results. Alternatives should be considered instead of character classes.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR
String str1 = "helló".replaceAll("[áéóíú]", "O");
System.out.println(str1); // Prints "hOllOO", not "hellO"
// OK
String str2 = "helló".replaceAll("á|é|ó|í|ú", "O");
System.out.println(str2); // Prints "hellO"
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Multiple space characters in regular expression should be replaced with a single character and a quantifier for better readability.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR
Pattern.compile("a b");
// OK
Pattern.compile("a {4}b");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether some alternative of regular expression is redundant, because other alternative already covers it.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR: "mp4" is present twice
Pattern.compile("mov|mp4|webm|mkv|avi|mp4");
// ERROR: "[a-e]" already contains "c"
Pattern.compile("[a-e]|c");
// ERROR: "a" is a subset of ".+"
Pattern.compile(".+|a");
// OK
Pattern.compile("mov|mp4|webm|mkv|avi");
Pattern.compile("[a-e]|z");
Pattern.compile("a+|b");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether some character class of a regular expression contains only a single character.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR
Pattern.compile("a[b]c");
// OK
Pattern.compile("abc");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Non-capturing group that does not contain alternatives and have no quantifiers applied to it is redundant and can be replaced with a nested regular expression.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR: Equivalent to "abcdef"
Pattern.compile("(?:abc)def");
// OK
Pattern.compile("abcdef");
Pattern.compile("(?:abc)?def");
Pattern.compile("(?:abc){0,1}def");
Pattern.compile("(?:abc|ghi)def");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds repetitions in character classes of regular expressions.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR: Characters a-z are present twice
Pattern.compile("[A-za-z]");
// OK
Pattern.compile("[A-Za-z]");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Single-character alternatives in regular expressions can be replaced by character classes for a better performance.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR
Pattern.compile("a|b|c|d");
// OK
Pattern.compile("[abcd]");
Pattern.compile("a|bb|cc|d");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether regular expression contains unbalanced parentheses/brackets/braces.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR
Pattern.compile("(ab");
// OK: Balanced
Pattern.compile("(ab)");
// OK: Escaped
Pattern.compile("\\(ab");
// OK: Matched literally
Pattern.compile("(ab", Pattern.LITERAL);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
When using methods of Java standard library classes like
String::replaceAll(String, String)
or
String::split(String)
it must be considered that these
methods accept regular expressions as their parameters, not literal
strings. So special values like '.'
, '|'
and
'\\'
should be escaped to avoid unexpected results.
import java.util.regex.Pattern;
import java.io.File;
public class Example {
public void test() {
// ERROR: Returns ",,,,,,,,,,," instead of expected "3,14 + 2,72"
"3.14 + 2.72".replaceAll(".", ",");
// ERROR: Returns "_a_b_c_|_d_e_f_" instead of expected "abc_def"
"abc|def".replaceAll("|", "_");
// ERROR: Won't compile on Windows (since File.separator="\\" is not a valid regex)
"C:\\Users\\Username\\Documents\\file.txt".replaceAll(File.separator, ":");
// OK
"3.14 + 2.72".replaceAll("\\.", "");
"abc|def".replaceAll("\\|", "_");
"C:\\Users\\Username\\Documents\\file.txt".replaceAll("\\\\", ":");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether anchors in regular expressions are used in inappropriate positions and thus can’t be matched.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR
Pattern.compile("$somePattern^");
// OK
Pattern.compile("^somePattern$");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds regular expressions that use quantifiers
{0}
, {1}
, {0,0}
or
{1,1}
and can be simplified.
import java.util.regex.Pattern;
public class Example {
public void test() {
// ERROR: Can be replaced by "abc"
Pattern.compile("ab{1,1}c");
Pattern.compile("ab{1}c");
// ERROR: Can be replaced by "ac"
Pattern.compile("ab{0,0}c");
Pattern.compile("ab{0}c");
// OK
Pattern.compile("abc");
Pattern.compile("ac");
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether competing JUnit annotations are used on the same test method.
Supported annotations: @Test
,
@RepeatedTest
, @ParameterizedTest
,
@TestFactory
, @TestTemplate
.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestTemplate;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class Example {
@Test // @Test and @TestTemplate must not be used one the same method
@TestTemplate
public void test() {
int actual = Integer.sum(3, 4);
int expected = 7;
assertEquals(expected, actual);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | Yes |
Kotlin | Quality | Minor | Unknown | Yes |
Checkers finds whether AssertJ’s assertion is made before its context was described. Such a context is useless and won’t affect the assertion.
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class Example {
@Test
public void test() {
int actual = 2 + 2;
int expected = 4;
// ERROR: Fail message is set AFTER the assertion
assertThat(actual).isEqualTo(expected).withFailMessage("Fail message");
// OK: Fail message is set BEFORE the assertion
assertThat(actual).withFailMessage("Fail message").isEqualTo(expected);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Minor | Unknown | Yes |
Kotlin | CodingStyle | Minor | Unknown | Yes |
Checker finds whether unexpected exception in JUnit tests is handled by hand, but it can be handled automatically instead.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.fail;
public class Example {
@Test
public void testBad() {
try {
foo(10, 0);
} catch (IllegalArgumentException e) {
// ERROR: JUnit can handle unexpected exception by itself
fail(e.getMessage());
}
}
@Test
public void testOk() {
// OK
foo(10, 0);
}
public static double foo(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Divider cannot be zero");
}
return a / b;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether Mockito’s verify(...)
or AssertJ’s
assertThat(...)
are used without an assertion after them.
verify(...)
and assertThat(...)
do nothing by
themselves, so subsequent assertion must be made.
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class Example {
@Test
public void test() {
// ERROR: assertThat doesn't check anything by itself
assertThat(foo());
// OK
assertThat(foo()).isTrue();
}
private boolean foo() {
return true;
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether inappropriate modifiers (such as
private
or static
) are used for JUnit tests
which leads to tests being ignored.
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class Example {
@Test
private void test1() {
// ERROR: JUnit5 skips private methods
int actual = Integer.sum(3, 4);
int expected = 7;
assertEquals(expected, actual);
}
@Test
static void test2() {
// ERROR: JUnit5 skips static methods
int actual = Integer.sum(3, 4);
int expected = 7;
assertEquals(expected, actual);
}
@Test
int test3() {
// ERROR: JUnit5 skips non-void methods
int actual = Integer.sum(3, 4);
int expected = 7;
assertEquals(expected, actual);
return actual;
}
@Nested
private class MyNestedClass {
// ERROR: JUnit5 skips private nested classes
@Test
void test4() {
int actual = Integer.sum(3, 4);
int expected = 7;
assertEquals(expected, actual);
}
}
@Test
public void test5() {
// OK
int actual = Integer.sum(3, 4);
int expected = 7;
assertEquals(expected, actual);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether several exceptional function calls are made while testing for expected exception. Such a test will pass successfully if any of the functions will throw an exception, while the expected behaviour may be different.
import org.junit.Test;
import java.io.*;
public class Example {
@Test(expected = IOException.class)
public void test() throws IOException {
File f = new File("/tmp/testfile.txt");
// ERROR: Two calls below may throw IOException. Which one is expected?
f.createNewFile();
checkIsDirectory(f);
}
private void checkIsDirectory(File f) throws IOException {
if (!f.isDirectory()) {
throw new IOException("File '" + f.getAbsolutePath() + "'is not a directory");
}
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Normal | Unknown | Yes |
Kotlin | CodingStyle | Normal | Unknown | Yes |
Checker finds whether JUnit assertions could be simplified.
Supported cases:
assertTrue(a.equals(b))
→
assertEquals(a, b)
(and similar case for
assertFalse
)assertTrue(a == b)
→
assertSame(a, b)
/assertEquals(a, b)
(and
similar cases for !=
and assertFalse
)assertTrue(a == null)
→ assertNull(a)
(and
similar cases for !=
and assertFalse
)import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class FromGuide {
@Test
public void testEquals() {
String a = "test";
String b = "te" + "st";
// May be simplified to assertEquals
assertTrue(a.equals(b));
// OK
assertEquals(a, b);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | CodingStyle | Minor | Unknown | Yes |
Kotlin | CodingStyle | Minor | Unknown | Yes |
Checker finds whether actual and expected values are passed to assertions in wrong order. This may lead to confusing error message.
Supported libraries and frameworks: AssertJ, JUnit, TestNG.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class Example {
@Test
public void test() {
int actual = 2 + 2;
// ERROR:
// assertEquals takes expected value as the first argument, not the second.
// So error message will look like "Expected:<4>. Actual:<5>".
assertEquals(actual, 5);
// OK
assertEquals(5, actual);
}
}
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Normal | Unknown | Yes |
Kotlin | Quality | Normal | Unknown | Yes |
Checker finds whether JUnit ExceptedException
is used
with an assertion after an exceptional function call. Such an assertion
is unreachable and test will pass successfully even if assertion
fails.
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.junit.Assert.assertEquals;
public class Example {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testBad() {
thrown.expect(IllegalStateException.class);
exceptionalMethod();
// ERROR:
// Assertion below is never reached, if exception is thrown above
// So the test will pass successfully even if assertion below fails
assertEquals(0, 1);
}
@Test
public void testOk() {
thrown.expect(IllegalStateException.class);
// OK: Assertion is done before call to exceptional method
assertEquals(0, 1);
exceptionalMethod();
}
private void exceptionalMethod() {
throw new IllegalStateException();
}
}
Svace provides special APIs for checker creation. Several checkers
described below are implemented using these APIs for demonstration
purposes. Their sources can be found in the plugins
folder
of the analyzer distribution.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
C/C++ | Quality | Major | Unknown | No |
Related CWEs: CWE266.
Various incorrect or insecure combinations of Linux system calls parameters related to permissions/ownership/etc.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker that reports constant file paths in Linux
open
system call.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker that reports non-constant flags in Linux
open
system call.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker that reports when flags argument in Linux
open
system call doesn’t have any of the read or write
access modes specified.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker that reports when flags argument in Linux
open
system call has both read and write access modes
simultaneously.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker for prohibited symbol names that reports functions, variables or function parameters starting with a triple underscore or containing specific substrings.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker for prohibited string contents that reports constant strings that contain specific substrings.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker that reports standard C function library function
malloc
calls with a zero or negative argument value.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker that reports useless divisions by constant
1
that may be caused by typos or application logic
errors.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker that reports useless modulo operations with a constant
1
argument that may be caused by typos or application logic
errors.
Language | Situation | Severity | Reliability | Enabled |
---|---|---|---|---|
Java | Quality | Minor | Unknown | No |
JavaScript | Quality | Minor | Unknown | No |
C/C++ | Quality | Minor | Unknown | No |
Scala | Quality | Minor | Unknown | No |
Go | Quality | Minor | Unknown | No |
Internal | Quality | Minor | Unknown | No |
PL/SQL | Quality | Minor | Unknown | No |
Kotlin | Quality | Minor | Unknown | No |
Python | Quality | Minor | Unknown | No |
Sample checker that reports formatted output functions calls with non constant format strings which is a common vulnerabilities cause.
In addition to using Svace engine for analysis of Java source code, it can be extended using SpotBugs (previously known as FindBugs) analysis tool, which is automatically invoked by Svace analysis tool by default. Filtering of warning types imported from SpotBugs can be configured using svace warning command, in the same way as for Svace warnings. The following SpotBugs checkers are supported (their descriptions can be found on the SpotBugs bug descriptions page):
FB.AA_ASSERTION_OF_ARGUMENTS
FB.AM_CREATES_EMPTY_JAR_FILE_ENTRY
FB.AM_CREATES_EMPTY_ZIP_FILE_ENTRY
FB.ASE_ASSERTION_WITH_SIDE_EFFECT
FB.ASE_ASSERTION_WITH_SIDE_EFFECT_METHOD
FB.AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION
FB.BAC_BAD_APPLET_CONSTRUCTOR
FB.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
FB.BC_BAD_CAST_TO_CONCRETE_COLLECTION
FB.BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS
FB.BC_IMPOSSIBLE_CAST
FB.BC_IMPOSSIBLE_CAST_PRIMITIVE_ARRAY
FB.BC_IMPOSSIBLE_DOWNCAST
FB.BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY
FB.BC_IMPOSSIBLE_INSTANCEOF
FB.BC_NULL_INSTANCEOF
FB.BC_UNCONFIRMED_CAST
FB.BC_UNCONFIRMED_CAST_OF_RETURN_VALUE
FB.BC_VACUOUS_INSTANCEOF
FB.BIT_ADD_OF_SIGNED_BYTE
FB.BIT_AND
FB.BIT_AND_ZZ
FB.BIT_IOR
FB.BIT_IOR_OF_SIGNED_BYTE
FB.BIT_SIGNED_CHECK
FB.BIT_SIGNED_CHECK_HIGH_BIT
FB.BOA_BADLY_OVERRIDDEN_ADAPTER
FB.BRSA_BAD_RESULTSET_ACCESS
FB.BSHIFT_WRONG_ADD_PRIORITY
FB.BX_BOXING_IMMEDIATELY_UNBOXED
FB.BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION
FB.BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR
FB.BX_UNBOXING_IMMEDIATELY_REBOXED
FB.CAA_COVARIANT_ARRAY_ELEMENT_STORE
FB.CAA_COVARIANT_ARRAY_FIELD
FB.CAA_COVARIANT_ARRAY_LOCAL
FB.CAA_COVARIANT_ARRAY_RETURN
FB.CD_CIRCULAR_DEPENDENCY
FB.CI_CONFUSED_INHERITANCE
FB.CN_IDIOM
FB.CN_IDIOM_NO_SUPER_CALL
FB.CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE
FB.CNT_ROUGH_CONSTANT_VALUE
FB.CO_ABSTRACT_SELF
FB.CO_COMPARETO_INCORRECT_FLOATING
FB.CO_COMPARETO_RESULTS_MIN_VALUE
FB.CO_SELF_NO_OBJECT
FB.CT_CONSTRUCTOR_THROW
FB.DB_DUPLICATE_BRANCHES
FB.DB_DUPLICATE_SWITCH_CLAUSES
FB.DC_DOUBLECHECK
FB.DCN_NULLPOINTER_EXCEPTION
FB.DC_PARTIALLY_CONSTRUCTED
FB.DE_MIGHT_DROP
FB.DE_MIGHT_IGNORE
FB.DLS_DEAD_LOCAL_INCREMENT_IN_RETURN
FB.DLS_DEAD_LOCAL_STORE
FB.DLS_DEAD_LOCAL_STORE_IN_RETURN
FB.DLS_DEAD_LOCAL_STORE_OF_NULL
FB.DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD
FB.DLS_DEAD_STORE_OF_CLASS_LITERAL
FB.DLS_OVERWRITTEN_INCREMENT
FB.DL_SYNCHRONIZATION_ON_BOOLEAN
FB.DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE
FB.DL_SYNCHRONIZATION_ON_INTERNED_STRING
FB.DL_SYNCHRONIZATION_ON_SHARED_CONSTANT
FB.DL_SYNCHRONIZATION_ON_UNSHARED_BOXED_PRIMITIVE
FB.DM_BOOLEAN_CTOR
FB.DM_BOXED_PRIMITIVE_FOR_COMPARE
FB.DM_BOXED_PRIMITIVE_FOR_PARSING
FB.DM_BOXED_PRIMITIVE_TOSTRING
FB.DM_CONVERT_CASE
FB.DM_DEFAULT_ENCODING
FB.DM_EXIT
FB.DM_FP_NUMBER_CTOR
FB.DM_GC
FB.DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION
FB.DMI_ARGUMENTS_WRONG_ORDER
FB.DMI_BAD_MONTH
FB.DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE
FB.DMI_BLOCKING_METHODS_ON_URL
FB.DMI_CALLING_NEXT_FROM_HASNEXT
FB.DMI_COLLECTION_OF_URLS
FB.DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES
FB.DMI_CONSTANT_DB_PASSWORD
FB.DMI_DOH
FB.DMI_EMPTY_DB_PASSWORD
FB.DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS
FB.DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR
FB.DMI_HARDCODED_ABSOLUTE_FILENAME
FB.DMI_INVOKING_HASHCODE_ON_ARRAY
FB.DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY
FB.DMI_INVOKING_TOSTRING_ON_ARRAY
FB.DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT
FB.DMI_NONSERIALIZABLE_OBJECT_WRITTEN
FB.DM_INVALID_MIN_MAX
FB.DMI_RANDOM_USED_ONLY_ONCE
FB.DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS
FB.DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED
FB.DMI_UNSUPPORTED_METHOD
FB.DMI_USELESS_SUBSTRING
FB.DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION
FB.DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD
FB.DMI_VACUOUS_SELF_COLLECTION_CALL
FB.DM_MONITOR_WAIT_ON_CONDITION
FB.DM_NEW_FOR_GETCLASS
FB.DM_NEXTINT_VIA_NEXTDOUBLE
FB.DM_NUMBER_CTOR
FB.DM_RUN_FINALIZERS_ON_EXIT
FB.DM_STRING_CTOR
FB.DM_STRING_TOSTRING
FB.DM_STRING_VOID_CTOR
FB.DM_USELESS_THREAD
FB.DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED
FB.DP_DO_INSIDE_DO_PRIVILEGED
FB.EC_ARRAY_AND_NONARRAY
FB.EC_BAD_ARRAY_COMPARE
FB.EC_INCOMPATIBLE_ARRAY_COMPARE
FB.EC_NULL_ARG
FB.EC_UNRELATED_CLASS_AND_INTERFACE
FB.EC_UNRELATED_INTERFACES
FB.EC_UNRELATED_TYPES
FB.EC_UNRELATED_TYPES_USING_POINTER_EQUALITY
FB.EI_EXPOSE_BUF
FB.EI_EXPOSE_BUF2
FB.EI_EXPOSE_REP
FB.EI_EXPOSE_REP2
FB.EI_EXPOSE_STATIC_BUF2
FB.EI_EXPOSE_STATIC_REP2
FB.ENV_USE_PROPERTY_INSTEAD_OF_ENV
FB.EOS_BAD_END_OF_STREAM_CHECK
FB.EQ_ABSTRACT_SELF
FB.EQ_ALWAYS_FALSE
FB.EQ_ALWAYS_TRUE
FB.EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS
FB.EQ_COMPARETO_USE_OBJECT_EQUALS
FB.EQ_COMPARING_CLASS_NAMES
FB.EQ_DOESNT_OVERRIDE_EQUALS
FB.EQ_DONT_DEFINE_EQUALS_FOR_ENUM
FB.EQ_GETCLASS_AND_CLASS_CONSTANT
FB.EQ_OTHER_NO_OBJECT
FB.EQ_OTHER_USE_OBJECT
FB.EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC
FB.EQ_SELF_NO_OBJECT
FB.EQ_SELF_USE_OBJECT
FB.EQ_UNUSUAL
FB.ES_COMPARING_PARAMETER_STRING_WITH_EQ
FB.ES_COMPARING_STRINGS_WITH_EQ
FB.ESYNC_EMPTY_SYNC
FB.FB_MISSING_EXPECTED_WARNING
FB.FB_UNEXPECTED_WARNING
FB.FE_FLOATING_POINT_EQUALITY
FB.FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER
FB.FI_EMPTY
FB.FI_EXPLICIT_INVOCATION
FB.FI_FINALIZER_NULLS_FIELDS
FB.FI_FINALIZER_ONLY_NULLS_FIELDS
FB.FI_MISSING_SUPER_CALL
FB.FI_NULLIFY_SUPER
FB.FI_PUBLIC_SHOULD_BE_PROTECTED
FB.FI_USELESS
FB.FL_FLOATS_AS_LOOP_COUNTERS
FB.FL_MATH_USING_FLOAT_PRECISION
FB.GC_UNCHECKED_TYPE_IN_GENERIC_CALL
FB.GC_UNRELATED_TYPES
FB.HE_EQUALS_NO_HASHCODE
FB.HE_EQUALS_USE_HASHCODE
FB.HE_HASHCODE_NO_EQUALS
FB.HE_HASHCODE_USE_OBJECT_EQUALS
FB.HE_INHERITS_EQUALS_USE_HASHCODE
FB.HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS
FB.HE_USE_OF_UNHASHABLE_CLASS
FB.HRS_REQUEST_PARAMETER_TO_COOKIE
FB.HRS_REQUEST_PARAMETER_TO_HTTP_HEADER
FB.HSC_HUGE_SHARED_STRING_CONSTANT
FB.IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD
FB.ICAST_BAD_SHIFT_AMOUNT
FB.ICAST_IDIV_CAST_TO_DOUBLE
FB.ICAST_INT_2_LONG_AS_INSTANT
FB.ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL
FB.ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND
FB.ICAST_INTEGER_MULTIPLY_CAST_TO_LONG
FB.ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT
FB.IC_INIT_CIRCULARITY
FB.IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION
FB.IIL_ELEMENTS_GET_LENGTH_IN_LOOP
FB.IIL_PATTERN_COMPILE_IN_LOOP
FB.IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT
FB.IIL_PREPARE_STATEMENT_IN_LOOP
FB.IIO_INEFFICIENT_INDEX_OF
FB.IIO_INEFFICIENT_LAST_INDEX_OF
FB.IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD
FB.IJU_BAD_SUITE_METHOD
FB.IJU_NO_TESTS
FB.IJU_SETUP_NO_SUPER
FB.IJU_SUITE_NOT_STATIC
FB.IJU_TEARDOWN_NO_SUPER
FB.IL_CONTAINER_ADDED_TO_ITSELF
FB.IL_INFINITE_LOOP
FB.IL_INFINITE_RECURSIVE_LOOP
FB.IMA_INEFFICIENT_MEMBER_ACCESS
FB.IM_AVERAGE_COMPUTATION_COULD_OVERFLOW
FB.IM_BAD_CHECK_FOR_ODD
FB.IM_MULTIPLYING_RESULT_OF_IREM
FB.IMSE_DONT_CATCH_IMSE
FB.INT_BAD_COMPARISON_WITH_INT_VALUE
FB.INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE
FB.INT_BAD_COMPARISON_WITH_SIGNED_BYTE
FB.INT_BAD_REM_BY_1
FB.INT_VACUOUS_BIT_OPERATION
FB.INT_VACUOUS_COMPARISON
FB.IO_APPENDING_TO_OBJECT_OUTPUT_STREAM
FB.IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN
FB.IS2_INCONSISTENT_SYNC
FB.ISC_INSTANTIATE_STATIC_CLASS
FB.IS_FIELD_NOT_GUARDED
FB.IS_INCONSISTENT_SYNC
FB.ITA_INEFFICIENT_TO_ARRAY
FB.IT_NO_SUCH_ELEMENT
FB.J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION
FB.JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS
FB.JLM_JSR166_LOCK_MONITORENTER
FB.JLM_JSR166_UTILCONCURRENT_MONITORENTER
FB.JML_JSR166_CALLING_WAIT_RATHER_THAN_AWAIT
FB.JUA_DONT_ASSERT_INSTANCEOF_IN_TESTS
FB.LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE
FB.LI_LAZY_INIT_INSTANCE
FB.LI_LAZY_INIT_STATIC
FB.LI_LAZY_INIT_UPDATE_STATIC
FB.MC_OVERRIDABLE_METHOD_CALL_IN_CLONE
FB.MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR
FB.ME_ENUM_FIELD_SETTER
FB.ME_MUTABLE_ENUM_FIELD
FB.MF_CLASS_MASKS_FIELD
FB.MF_METHOD_MASKS_FIELD
FB.ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD
FB.ML_SYNC_ON_UPDATED_FIELD
FB.MS_CANNOT_BE_FINAL
FB.MS_EXPOSE_BUF
FB.MS_EXPOSE_REP
FB.MS_FINAL_PKGPROTECT
FB.MSF_MUTABLE_SERVLET_FIELD
FB.MS_MUTABLE_ARRAY
FB.MS_MUTABLE_COLLECTION
FB.MS_MUTABLE_COLLECTION_PKGPROTECT
FB.MS_MUTABLE_HASHTABLE
FB.MS_OOI_PKGPROTECT
FB.MS_PKGPROTECT
FB.MS_SHOULD_BE_FINAL
FB.MS_SHOULD_BE_REFACTORED_TO_BE_FINAL
FB.MTIA_SUSPECT_SERVLET_INSTANCE_FIELD
FB.MTIA_SUSPECT_STRUTS_INSTANCE_FIELD
FB.MWN_MISMATCHED_NOTIFY
FB.MWN_MISMATCHED_WAIT
FB.NM_BAD_EQUAL
FB.NM_CLASS_NAMING_CONVENTION
FB.NM_CLASS_NOT_EXCEPTION
FB.NM_CONFUSING
FB.NM_FIELD_NAMING_CONVENTION
FB.NM_FUTURE_KEYWORD_USED_AS_IDENTIFIER
FB.NM_FUTURE_KEYWORD_USED_AS_MEMBER_IDENTIFIER
FB.NM_LCASE_HASHCODE
FB.NM_LCASE_TOSTRING
FB.NM_METHOD_CONSTRUCTOR_CONFUSION
FB.NM_METHOD_NAMING_CONVENTION
FB.NM_SAME_SIMPLE_NAME_AS_INTERFACE
FB.NM_SAME_SIMPLE_NAME_AS_SUPERCLASS
FB.NM_VERY_CONFUSING
FB.NM_VERY_CONFUSING_INTENTIONAL
FB.NM_WRONG_PACKAGE
FB.NM_WRONG_PACKAGE_INTENTIONAL
FB.NN_NAKED_NOTIFY
FB.NO_NOTIFY_NOT_NOTIFYALL
FB.NP_ALWAYS_NULL
FB.NP_ALWAYS_NULL_EXCEPTION
FB.NP_ARGUMENT_MIGHT_BE_NULL
FB.NP_BOOLEAN_RETURN_NULL
FB.NP_CLONE_COULD_RETURN_NULL
FB.NP_CLOSING_NULL
FB.NP_DEREFERENCE_OF_READLINE_VALUE
FB.NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT
FB.NP_GUARANTEED_DEREF
FB.NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH
FB.NP_IMMEDIATE_DEREFERENCE_OF_READLINE
FB.NP_LOAD_OF_KNOWN_NULL_VALUE
FB.NP_METHOD_PARAMETER_RELAXING_ANNOTATION
FB.NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION
FB.NP_METHOD_RETURN_RELAXING_ANNOTATION
FB.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
FB.NP_NONNULL_PARAM_VIOLATION
FB.NP_NONNULL_RETURN_VIOLATION
FB.NP_NULL_INSTANCEOF
FB.NP_NULL_ON_SOME_PATH
FB.NP_NULL_ON_SOME_PATH_EXCEPTION
FB.NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE
FB.NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE
FB.NP_NULL_PARAM_DEREF
FB.NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS
FB.NP_NULL_PARAM_DEREF_NONVIRTUAL
FB.NP_OPTIONAL_RETURN_NULL
FB.NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE
FB.NP_STORE_INTO_NONNULL_FIELD
FB.NP_SYNC_AND_NULL_CHECK_FIELD
FB.NP_TOSTRING_COULD_RETURN_NULL
FB.NP_UNWRITTEN_FIELD
FB.NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD
FB.NS_DANGEROUS_NON_SHORT_CIRCUIT
FB.NS_NON_SHORT_CIRCUIT
FB.OBL_UNSATISFIED_OBLIGATION
FB.OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE
FB.ODR_OPEN_DATABASE_RESOURCE
FB.ODR_OPEN_DATABASE_RESOURCE_EXCEPTION_PATH
FB.OS_OPEN_STREAM
FB.OS_OPEN_STREAM_EXCEPTION_PATH
FB.OVERRIDING_METHODS_MUST_INVOKE_SUPER
FB.PA_PUBLIC_ARRAY_ATTRIBUTE
FB.PA_PUBLIC_MUTABLE_OBJECT_ATTRIBUTE
FB.PA_PUBLIC_PRIMITIVE_ATTRIBUTE
FB.PERM_SUPER_NOT_CALLED_IN_GETPERMISSIONS
FB.PI_DO_NOT_REUSE_PUBLIC_IDENTIFIERS_CLASS_NAMES
FB.PI_DO_NOT_REUSE_PUBLIC_IDENTIFIERS_FIELD_NAMES
FB.PI_DO_NOT_REUSE_PUBLIC_IDENTIFIERS_LOCAL_VARIABLE_NAMES
FB.PI_DO_NOT_REUSE_PUBLIC_IDENTIFIERS_METHOD_NAMES
FB.PS_PUBLIC_SEMAPHORES
FB.PT_ABSOLUTE_PATH_TRAVERSAL
FB.PT_RELATIVE_PATH_TRAVERSAL
FB.PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS
FB.PZLA_PREFER_ZERO_LENGTH_ARRAYS
FB.QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT
FB.QF_QUESTIONABLE_FOR_LOOP
FB.RANGE_ARRAY_INDEX
FB.RANGE_ARRAY_LENGTH
FB.RANGE_ARRAY_OFFSET
FB.RANGE_STRING_INDEX
FB.RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE
FB.RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES
FB.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
FB.RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE
FB.RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE
FB.RC_REF_COMPARISON
FB.RC_REF_COMPARISON_BAD_PRACTICE
FB.RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN
FB.RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION
FB.RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION
FB.REC_CATCH_EXCEPTION
FB.REFLC_REFLECTION_MAY_INCREASE_ACCESSIBILITY_OF_CLASS
FB.REFLF_REFLECTION_MAY_INCREASE_ACCESSIBILITY_OF_FIELD
FB.RE_POSSIBLE_UNINTENDED_PATTERN
FB.RI_REDUNDANT_INTERFACES
FB.RPC_REPEATED_CONDITIONAL_TEST
FB.RR_NOT_CHECKED
FB.RS_READOBJECT_SYNC
FB.RU_INVOKE_RUN
FB.RV_01_TO_INT
FB.RV_ABSOLUTE_VALUE_OF_HASHCODE
FB.RV_ABSOLUTE_VALUE_OF_RANDOM_INT
FB.RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE
FB.RV_CHECK_FOR_POSITIVE_INDEXOF
FB.RV_DONT_JUST_NULL_CHECK_READLINE
FB.RV_EXCEPTION_NOT_THROWN
FB.RV_NEGATING_RESULT_OF_COMPARETO
FB.RV_REM_OF_HASHCODE
FB.RV_REM_OF_RANDOM_INT
FB.RV_RETURN_VALUE_IGNORED
FB.RV_RETURN_VALUE_IGNORED_BAD_PRACTICE
FB.RV_RETURN_VALUE_IGNORED_INFERRED
FB.RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT
FB.RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED
FB.SA_FIELD_DOUBLE_ASSIGNMENT
FB.SA_FIELD_SELF_ASSIGNMENT
FB.SA_FIELD_SELF_COMPARISON
FB.SA_FIELD_SELF_COMPUTATION
FB.SA_LOCAL_DOUBLE_ASSIGNMENT
FB.SA_LOCAL_SELF_ASSIGNMENT
FB.SA_LOCAL_SELF_ASSIGNMENT_INSTEAD_OF_FIELD
FB.SA_LOCAL_SELF_COMPARISON
FB.SA_LOCAL_SELF_COMPUTATION
FB.SBSC_USE_STRINGBUFFER_CONCATENATION
FB.SC_START_IN_CTOR
FB.SE_BAD_FIELD
FB.SE_BAD_FIELD_INNER_CLASS
FB.SE_BAD_FIELD_STORE
FB.SE_COMPARATOR_SHOULD_BE_SERIALIZABLE
FB.SE_INNER_CLASS
FB.SE_METHOD_MUST_BE_PRIVATE
FB.SE_NONFINAL_SERIALVERSIONID
FB.SE_NONLONG_SERIALVERSIONID
FB.SE_NONSTATIC_SERIALVERSIONID
FB.SE_NO_SERIALVERSIONID
FB.SE_NO_SUITABLE_CONSTRUCTOR
FB.SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION
FB.SE_PREVENT_EXT_OBJ_OVERWRITE
FB.SE_PRIVATE_READ_RESOLVE_NOT_INHERITED
FB.SE_READ_RESOLVE_IS_STATIC
FB.SE_READ_RESOLVE_MUST_RETURN_OBJECT
FB.SE_TRANSIENT_FIELD_NOT_RESTORED
FB.SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS
FB.SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH
FB.SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW
FB.SF_SWITCH_FALLTHROUGH
FB.SF_SWITCH_NO_DEFAULT
FB.SIC_INNER_SHOULD_BE_STATIC
FB.SIC_INNER_SHOULD_BE_STATIC_ANON
FB.SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS
FB.SIC_THREADLOCAL_DEADLY_EMBRACE
FB.SI_INSTANCE_BEFORE_FINALS_ASSIGNED
FB.SIO_SUPERFLUOUS_INSTANCEOF
FB.SKIPPED_CLASS_TOO_BIG
FB.SP_SPIN_ON_FIELD
FB.SQL_BAD_PREPARED_STATEMENT_ACCESS
FB.SQL_BAD_RESULTSET_ACCESS
FB.SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE
FB.SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING
FB.SR_NOT_CHECKED
FB.SSD_DO_NOT_USE_INSTANCE_LOCK_ON_SHARED_STATIC_DATA
FB.SS_SHOULD_BE_STATIC
FB.STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE
FB.STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE
FB.STCAL_STATIC_CALENDAR_INSTANCE
FB.STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE
FB.STI_INTERRUPTED_ON_CURRENTTHREAD
FB.STI_INTERRUPTED_ON_UNKNOWNTHREAD
FB.ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD
FB.SWL_SLEEP_WITH_LOCK_HELD
FB.SW_SWING_METHODS_INVOKED_IN_SWING_THREAD
FB.THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION
FB.THROWS_METHOD_THROWS_CLAUSE_THROWABLE
FB.THROWS_METHOD_THROWS_RUNTIMEEXCEPTION
FB.TLW_TWO_LOCK_NOTIFY
FB.TLW_TWO_LOCK_WAIT
FB.TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED
FB.TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS
FB.TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK
FB.TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK
FB.TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK
FB.TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK
FB.TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED
FB.TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED
FB.UCF_USELESS_CONTROL_FLOW
FB.UCF_USELESS_CONTROL_FLOW_NEXT_LINE
FB.UC_USELESS_CONDITION
FB.UC_USELESS_CONDITION_TYPE
FB.UC_USELESS_OBJECT
FB.UC_USELESS_OBJECT_STACK
FB.UC_USELESS_VOID_METHOD
FB.UG_SYNC_SET_UNSYNC_GET
FB.UI_INHERITANCE_UNSAFE_GETRESOURCE
FB.UL_UNRELEASED_LOCK
FB.UL_UNRELEASED_LOCK_EXCEPTION_PATH
FB.UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS
FB.UM_UNNECESSARY_MATH
FB.UPM_UNCALLED_PRIVATE_METHOD
FB.URF_UNREAD_FIELD
FB.URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD
FB.UR_UNINIT_READ
FB.UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR
FB.USC_POTENTIAL_SECURITY_CHECK_BASED_ON_UNTRUSTED_SOURCE
FB.USM_USELESS_ABSTRACT_METHOD
FB.USM_USELESS_SUBCLASS_METHOD
FB.UUF_UNUSED_FIELD
FB.UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD
FB.UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
FB.UWF_NULL_FIELD
FB.UWF_UNWRITTEN_FIELD
FB.UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD
FB.UW_UNCOND_WAIT
FB.VA_FORMAT_STRING_USES_NEWLINE
FB.VA_PRIMITIVE_ARRAY_PASSED_TO_OBJECT_VARARG
FB.VO_VOLATILE_INCREMENT
FB.VO_VOLATILE_REFERENCE_TO_ARRAY
FB.VR_UNRESOLVABLE_REFERENCE
FB.VSC_VULNERABLE_SECURITY_CHECK_METHODS
FB.WA_AWAIT_NOT_IN_LOOP
FB.WA_NOT_IN_LOOP
FB.WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL
FB.WMI_WRONG_MAP_ITERATOR
FB.WS_WRITEOBJECT_SYNC
FB.XFB_XML_FACTORY_BYPASS
FB.XSS_REQUEST_PARAMETER_TO_JSP_WRITER
FB.XSS_REQUEST_PARAMETER_TO_SEND_ERROR
FB.XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER
Additionally, a popular SpotBugs plugin Find Security Bugs is integrated too. The following Find Security Bugs warning types are supported (their descriptions can be found on the Bugs Patterns page):
FB.ANDROID_BROADCAST
FB.ANDROID_EXTERNAL_FILE_ACCESS
FB.ANDROID_GEOLOCATION
FB.ANDROID_WEB_VIEW_JAVASCRIPT
FB.ANDROID_WEB_VIEW_JAVASCRIPT_INTERFACE
FB.ANDROID_WORLD_WRITABLE
FB.AWS_QUERY_INJECTION
FB.BAD_HEXA_CONVERSION
FB.BEAN_PROPERTY_INJECTION
FB.BLOWFISH_KEY_SIZE
FB.CIPHER_INTEGRITY
FB.COMMAND_INJECTION
FB.COOKIE_PERSISTENT
FB.COOKIE_USAGE
FB.CRLF_INJECTION_LOGS
FB.CUSTOM_INJECTION
FB.CUSTOM_MESSAGE_DIGEST
FB.DANGEROUS_PERMISSION_COMBINATION
FB.DEFAULT_HTTP_CLIENT
FB.DESERIALIZATION_GADGET
FB.DES_USAGE
FB.ECB_MODE
FB.EL_INJECTION
FB.ENTITY_LEAK
FB.ENTITY_MASS_ASSIGNMENT
FB.ESAPI_ENCRYPTOR
FB.EXTERNAL_CONFIG_CONTROL
FB.FILE_UPLOAD_FILENAME
FB.FORMAT_STRING_MANIPULATION
FB.GROOVY_SHELL
FB.HARD_CODE_KEY
FB.HARD_CODE_PASSWORD
FB.HAZELCAST_SYMMETRIC_ENCRYPTION
FB.HTTPONLY_COOKIE
FB.HTTP_PARAMETER_POLLUTION
FB.HTTP_RESPONSE_SPLITTING
FB.IMPROPER_UNICODE
FB.INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE
FB.INSECURE_COOKIE
FB.INSECURE_SMTP_SSL
FB.JACKSON_UNSAFE_DESERIALIZATION
FB.JAXRS_ENDPOINT
FB.JAXWS_ENDPOINT
FB.JSP_INCLUDE
FB.JSP_JSTL_OUT
FB.JSP_SPRING_EVAL
FB.JSP_XSLT
FB.LDAP_ANONYMOUS
FB.LDAP_ENTRY_POISONING
FB.LDAP_INJECTION
FB.MALICIOUS_XSLT
FB.MODIFICATION_AFTER_VALIDATION
FB.NORMALIZATION_AFTER_VALIDATION
FB.NULL_CIPHER
FB.OBJECT_DESERIALIZATION
FB.OGNL_INJECTION
FB.OVERLY_PERMISSIVE_FILE_PERMISSION
FB.PADDING_ORACLE
FB.PATH_TRAVERSAL_IN
FB.PATH_TRAVERSAL_OUT
FB.PERMISSIVE_CORS
FB.PLAY_UNVALIDATED_REDIRECT
FB.POTENTIAL_XML_INJECTION
FB.PREDICTABLE_RANDOM
FB.PREDICTABLE_RANDOM_SCALA
FB.REDOS
FB.REQUESTDISPATCHER_FILE_DISCLOSURE
FB.RPC_ENABLED_EXTENSIONS
FB.RSA_KEY_SIZE
FB.RSA_NO_PADDING
FB.SAML_IGNORE_COMMENTS
FB.SCALA_COMMAND_INJECTION
FB.SCALA_PATH_TRAVERSAL_IN
FB.SCALA_PLAY_SSRF
FB.SCALA_SENSITIVE_DATA_EXPOSURE
FB.SCALA_SQL_INJECTION_ANORM
FB.SCALA_SQL_INJECTION_SLICK
FB.SCALA_XSS_MVC_API
FB.SCALA_XSS_TWIRL
FB.SCRIPT_ENGINE_INJECTION
FB.SEAM_LOG_INJECTION
FB.SERVLET_CONTENT_TYPE
FB.SERVLET_HEADER
FB.SERVLET_HEADER_REFERER
FB.SERVLET_HEADER_USER_AGENT
FB.SERVLET_PARAMETER
FB.SERVLET_QUERY_STRING
FB.SERVLET_SERVER_NAME
FB.SERVLET_SESSION_ID
FB.SMTP_HEADER_INJECTION
FB.SPEL_INJECTION
FB.SPRING_CSRF_PROTECTION_DISABLED
FB.SPRING_CSRF_UNRESTRICTED_REQUEST_MAPPING
FB.SPRING_ENDPOINT
FB.SPRING_FILE_DISCLOSURE
FB.SPRING_UNVALIDATED_REDIRECT
FB.SQL_INJECTION
FB.SQL_INJECTION_ANDROID
FB.SQL_INJECTION_HIBERNATE
FB.SQL_INJECTION_JDBC
FB.SQL_INJECTION_JDO
FB.SQL_INJECTION_JPA
FB.SQL_INJECTION_SPRING_JDBC
FB.SQL_INJECTION_TURBINE
FB.SQL_INJECTION_VERTX
FB.SSL_CONTEXT
FB.STATIC_IV
FB.STRUTS1_ENDPOINT
FB.STRUTS2_ENDPOINT
FB.STRUTS_FILE_DISCLOSURE
FB.STRUTS_FORM_VALIDATION
FB.TAPESTRY_ENDPOINT
FB.TDES_USAGE
FB.TEMPLATE_INJECTION_FREEMARKER
FB.TEMPLATE_INJECTION_PEBBLE
FB.TEMPLATE_INJECTION_VELOCITY
FB.TRUST_BOUNDARY_VIOLATION
FB.UNENCRYPTED_SERVER_SOCKET
FB.UNENCRYPTED_SOCKET
FB.UNSAFE_HASH_EQUALS
FB.UNVALIDATED_REDIRECT
FB.URLCONNECTION_SSRF_FD
FB.URL_REWRITING
FB.WEAK_FILENAMEUTILS
FB.WEAK_HOSTNAME_VERIFIER
FB.WEAK_MESSAGE_DIGEST_MD5
FB.WEAK_MESSAGE_DIGEST_SHA1
FB.WEAK_TRUST_MANAGER
FB.WICKET_ENDPOINT
FB.WICKET_XSS1
FB.XML_DECODER
FB.XPATH_INJECTION
FB.XSS_JSP_PRINT
FB.XSS_REQUEST_WRAPPER
FB.XSS_SERVLET
FB.XXE_DOCUMENT
FB.XXE_DTD_TRANSFORM_FACTORY
FB.XXE_SAXPARSER
FB.XXE_SCHEMA_FACTORY
FB.XXE_VALIDATOR
FB.XXE_XMLREADER
FB.XXE_XMLSTREAMREADER
FB.XXE_XPATH
FB.XXE_XSLT_TRANSFORM_FACTORY
Mypy serves a dual purpose as both an extension to the Python language, introducing static typing, and a type checker tool. In its role as an extension, Mypy allows users to specify variable types. As a type checker, it detects incompatible types, facilitating early identification of potential errors and enhancing code reliability. Notably, Mypy exclusively operates with types specified by the user.
In addition to the analysis using Svace engine, Svace may use Mypy to
detect incompatible specified types if Mypy version at least 0.790 is
installed to the system and is available by its name, mypy
.
In this case, Svace will invoke Mypy process during Python analysis and
embed Mypy warnings into the analysis results with MYPY.
prefix. E.g. Mypy warning with error code [return-value]
,
that means that function has incompatible return value type will be
imported into Svace results as MYPY.RETURN_VALUE
.