Svace 3.3.2 User Guide

Svace is an automatic defect detection tool for source code written in C, C++, Java, Go, Kotlin and C# languages. The detection algorithms are based on static analysis. This software is copyrighted by Ivannikov Institute for System Programming of the Russian Academy of Sciences (ISP RAS).

Svace is a continuously evolving product based on long term research. It uses a number of unique state-of-the-art analysis technologies and adopts the front-end of several open compilers to support the latest language standards. Svace provides ease of integration with any build systems and allows to accurately capture the exact code being built and the details of its compilation. Svace analysis engine covers all program execution paths and considers function calls in particular. The analysis is fully parallel and scalable up to projects with tens of millions lines of code. Several third-party analyzers are integrated into Svace to widen the scope of detected issues. Analysis results are represented through detailed reports, where the traces provided for each reported issue support code navigation.

Outline

This User Guide includes the following main sections:

Introduction

Typically, analyzing source code with Svace involves four stages:

Svace includes internal compilers to generate intermediate representation for analysis. For C/C++ Svace uses a modified version of Clang to produce the LLVM intermediate representation (bitcode) files. For Java, javac compiler from OpenJDK is used to produce bytecode. For C#, Roslyn is used.

The analysis tool uses the intermediate files and other data (including source code) produced and stored at the build phase to generate analysis result files (in various formats including plain text, XML and JSON).

History of previous analysis runs allows one to indicate (for a new run) which issues were new to this run and which issues detected on this run were previously marked as incorrectly detected (false positives).

Dependencies and installation

Svace is released in 3 separate distributions:

The analyzer has no external dependencies and may be used right after unpacking.

Using Svace

This section describes how to perform analysis through command line interface, explaining the process of analysis in detail.

Initializing project folder

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.

Building source code

Svace provides the svace build tool to wrap a normal building command. This tool intercepts invocations of compilers supported by Svace (C/C++: GCC, ARMCC, Clang, icc, MSVC and multiple others; Java: OpenJDK, Oracle JDK and ECJ (including their usage via API)) and transparently runs Svace internal compilers along with the original compiler command.

Building in Tizen and Samsung Build System

Running the build 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

Performing analysis

Use svace analyze command line tool.

Secondary analysis engines

A Svace distribution can additionally include Clang Static Analyzer and SpotBugs. Also, several lightweight defect detectors are implemented in Svace javac compiler. By default, all those tools are run before the main analysis, using intermediate data produced during the build phase as input. Defect reports produced by those tools are included into the final analysis results.

Performance tuning

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 by 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 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 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.

Working with history storage

When the svace history tool is used after an analysis, analysis results are added to history storage. History storage for a project is located in its Svace directory. A history storage contains the list of known warnings found in the project, and snapshots of the relevant source code.

History storage allows to recognize some issues detected on a new analysis run as instances of previously detected issues (even if the source code and line numbers changed between the analyses). If an issue was marked as a ‘false positive’ after some analysis run, it will remain marked so in new analysis results.

Viewing the results

Analysis results may be viewed through a web-browser using a builtin server (see svace server) or a standalone Svacer history server, or opened with an editor.

Each issue detected by Svace is associated with the following information:

The analysis results are presented as a tree. The top level lists warnings by type, the second level lists the warnings themselves.

Each warning record shows location in the source code and warning message that may include names of the relevant expressions. For each warning, subnodes in the tree may indicate multiple source locations playing different roles in the warning (for example, for DEREF_AFTER_NULL, one location shows where the pointer is dereferenced, and the other location shows where it was compared to NULL). Together they form a warning trace that usually helps to understand the program flow from the defect occurrence to its manifestation point, or other useful information related to the warning.

When using a web-server warning status can be set by selecting appropriate value from the drop-down list, and is indicated by the color of the issue. There are the following options:

Command line tools

The command line tools can be accessed through a wrapper tool svace located in svace/bin/ (it’s recommended to add this directory to PATH, so that svace becomes accessible as a command). Command line options for svace are as follows:

usage: svace <command> [args]
       svace --version [--quiet | --verbose]
       svace [--help]

This is the command-line interface for Svace static analysis tool.
Type 'svace help <command>' to get help on a specific command.

optional arguments:
  --help                Show this help message and exit.
  -V, --version         Show version information and exit. If used together
                        with '--quiet', only version number in X.Y.Z format is
                        printed. If used together with '--verbose', source
                        code revision is printed too.
  -v, --verbose         Increase output verbosity.
  -q, --quiet           Reduce output verbosity.

commands:
  main:
    analyze             Run Svace static analyzer to search for defects in
                        source code, given the data gathered during 'svace
                        build'.
    build               Capture invocations of supported compilers performed
                        by a given command and generate intermediate data for
                        a subsequent analysis.
    config              Manage general configuration settings.
    init                Create a Svace project directory.
    warning             Control which warning types are enabled during
                        analysis.
  
  experimental:
    admin               Manage object pool in a project directory.
    clean               Remove old annotations.
    context-spec        Manage context in which particular function will be
                        analyzed.
    export-build        Export a build object into an external directory.
    history             Interact with a warning database.
    merge-build         Merge multiple build objects into a single build
                        object.
    nhistory            Interact with a new warning database.
    server              Configure or run Svace server.
    show                Show results in a Svace server (localhost:8060).
    spec                Manage user-provided specifications for third-party
                        library functions.
  
  miscellaneous:
    compare-details     Compare details for 2 svres files.
    compare-svres       Compare warnings from two .svres files ignoring
                        checker names and generate comparison stats.
    convert-svres       Convert analysis results files into newer or older
                        format versions.
    csa-stat            Display Clang Static Analyzer checker statistics
                        summary.
    diff-svres          Show diff for 2 svres files.
    filter-svres        Extract the issues enabled in given warning settings
                        file from a given .svres.
    ignore-test         Check svace.ignore filter.
    import-sqlite       Convert svace sqlite format db to svres.
    match-svres         Match two svres files and show identifiers of the
                        matched warnings from the first svres file.
    merge-svres         Merge multiple .svres files with analysis results for
                        different projects into a single file.
    mti                 Calculate message template id from warning messages
                        taken from svres files.
    mti-csharp          Calculate message template id from warning messages
                        taken from svres files.
    quality-report      Print report about warnings quality for reviewed svres
                        files(s).
    review-rebase       Rebase warning review from one svres file to other.
    split-svres         Split a multi-project .svres file into separate files
                        with analysis results for individual projects.
    suppress            Run stand-alone suppression tool.
    svres2json          Converter from Svace results format to JSON format and
                        vice versa.
    svres2sarif         Converter from Svace results format to Sarif 2.1
                        format.
    warning-selector    Pick particular warnings and write them to an svres
                        file.
  
  for debug and development:
    cgoc                Tool to compare call graph logs.
    cgop                Tool to calculate sub-call graph order.

svace init

usage: svace init [options] [DIR]

Create an empty Svace project directory or reinitialize an existing one. By
default, this command creates a '.svace-dir' subdirectory in DIR (but see
--bare). If DIR is not specified, the value of SVACE_DIR environment variable
(or, if unset, the current directory) is used instead. If the project
directory already exists, this command can reinitialize its permissions
according to the options without changing the stored data. Note that only
permissions of the directory itself can be changed. Permissions of
subdirectories should be changed manually if desired.

optional arguments:
  --bare       Initialize the specified directory as a Svace project directory
               instead of creating '.svace-dir' subdirectory in it.
  --shared     Make the project directory group-writable and set the set-
               group-id bit on it if needed. This allows users belonging to
               the current user's primary group to share this project
               directory.
  -q, --quiet  Print only error messages (to stderr).
  --help       Show this help message and exit.

svace build

This tool launches a specified custom build command, intercepts invocations of supported compilers and uses Svace internal compilers to produce intermediate representation of the same source code that is being built by the given command.

Command line options are as follows:

usage: svace build [options] <build-command>

Run <build-command> and capture all invocations of supported compilers during
its execution. Create intermediate representation files using information from
the intercepted compiler invocations.

<build-command> can include options and arguments. To capture multiple
commands, wrap the commands in a single script.

Typical usage:
 svace build make -j4 all

 svace build gradle --no-daemon build

positional arguments:
  build-command

optional arguments:
  -h, --help            show this help message and exit
  --svace-dir SVACE_DIR
                        Specify a Svace directory, overriding current
                        directory and SVACE_DIR environment variable.
  --init                Initialize the selected Svace project directory before
                        doing anything else. This is a shortcut for avoiding
                        manual 'svace init' if all you need is its default
                        behavior.
  --clear-build-dir     Remove redundant contents of the build directory after
                        build.
  --enable-language LANG, --disable-language LANG
                        Enable or disable compilation interception for the
                        specified programming language. Supported languages
                        are: all, cxx, go, kotlin. By default, all supported
                        languages are enabled.
  -L LANGS, --languages LANGS
                        Enable compilation interception for languages in the
                        specified comma-separated list and disable it for
                        other languages. The special value 'all' enables all
                        supported languages (which is the default).
  --debug               Enable verbose logging useful for debugging and making
                        bug reports.
  --enable-spotbugs, --disable-spotbugs
                        Run SpotBugs checkers for intercepted Java
                        compilations during the build phase. By default, this
                        feature is disabled.
  --disable-java-agent  Don't inject Java agent into intercepted JVM
                        processes. If this option is used, compilations
                        performed via compiler APIs (e.g., javac API) are not
                        intercepted. Since Svace Java agent requires Java 7 or
                        newer and causes build failure if it's injected into
                        an older JVM, this option can be useful if build
                        process involves older Java and compiler API
                        interception is not needed.
  --spotbugs-memory SPOTBUGS_MEMORY
                        Specify maximum size of Java heap (in MB) that
                        SpotBugs may use (default: 2048).
  --kotlinc-memory KOTLINC_MEMORY
                        Specify maximum size of Java heap (in MB) that Kotlin
                        compiler may use (default: 2048).
  --captured-nothing-status STATUS
                        If the build command completed successfully and no
                        serious Svace failures were detected, but no
                        compilations were successfully captured, exit with the
                        specified status (255 by default).
  --low-ready-units-ratio PERCENTAGE
                        If the ratio of ready units to all units (as shown
                        with --verbose) is less than the specified integer
                        percentage for any programming language with at least
                        one intercepted compilation, print a warning and exit
                        with the status specified with --low-ready-units-
                        status option (but still generate all data necessary
                        for analysis). This option doesn't apply if no units
                        were captured at all (see --captured-nothing-status).
                        The default value is 0 (i.e. no capture ratio is
                        considered low).
  --low-ready-units-status STATUS
                        Set the exit status for --low-ready-units-ratio option
                        (254 by default).
  -v, --verbose         Report more detailed statistics after build.

experimental options:
  -t TARGET, --target TARGET
                        Specify the target platform that Clang should use for
                        bitcode generation. Valid values are: auto, aarch64,
                        arm, hexagon, mips, mips64, powerpc, powerpc64,
                        riscv32, riscv64, sparc, sparc64, x64, x86. Values
                        other than 'auto' override automatic target detection.
                        This option is intended for debugging only.
  --enable-ptrace       Use ptrace-based interception for statically-linked
                        executables. By default, Svace uses LD_PRELOAD-based
                        interception, which works only for dynamically linked
                        executables. But sometimes compiler toolchains are
                        statically linked, or there are other statically
                        linked processes that break LD_PRELOAD-based
                        interception (for example, by removing LD_PRELOAD
                        environment variable). This option enables a hybrid
                        mode: Svace will start in LD_PRELOAD mode, but will
                        switch to ptrace-based mode each time it detects a
                        launch of a statically linked executable, and will
                        switch back if that executable runs a dynamically
                        linked executable.
  --enable-ptrace-all   Use ptrace-based interception for all executables and
                        disable LD_PRELOAD-based interception. See --enable-
                        ptrace for more details.
  --prepend-preload-lib
                        By default, the Svace library for function call
                        interception is appended to LD_PRELOAD environment
                        variable, ensuring that necessary environment
                        variables are propagated across the process tree even
                        if other LD_PRELOAD-libraries are used in the course
                        of the build process. However, if such libraries
                        modify arguments of created processes in a way that
                        makes them unrecognizable to the Svace code that
                        detects "interesting" processes, the Svace library
                        should be prepended instead, ensuring that it sees the
                        same arguments that were passed by the application. In
                        particular, this is needed for Scratchbox environment.
  --disable-comp        Disable all compiler addons
  --disable-misc        Disable all non-compiler addons
  --clang-opts CLANG_OPTS
                        Add specified options to the Clang and Clang Static
                        Analyzer run by Svace. The options should be separated
                        by ';'.
  --spotbugs-opts SPOTBUGS_OPTS
                        Add specified options to the SpotBugs run by Svace.
                        The options should be separated by ';'.
  --hash-ar-content     Calculate archive hash based only on names of files in
                        the archive, excluding timestamp, UID, GID and
                        content. Use in case of undeterministic mode of ar or
                        ranlib and in case of prebuilt archives with prebuilt
                        object files (that may differ because of another
                        platform etc.). DO NOT USE in case of rebuilding the
                        same archive more than once in the same build (e.g.
                        debug and release mode).
  --enable-huge-source-workaround
                        Clang can't handle translation units larger than 2 GB
                        after preprocessing because it uses signed 32-bit
                        integer type internally for identifying source
                        locations. Sometimes such huge TUs occur in practice
                        because the same autogenerated header file is included
                        multiple times without guards. In this case source
                        locations are allocated for each inclusion, which may
                        cause an overflow resulting in a hang or crash. This
                        option makes it so source locations are allocated only
                        once per header file, which may help avoid the
                        overflow in this case. This option is incompatible
                        with '--enable-csa-deps'.
  --chroot-interception-failure-mode {fail,warn,ignore}
                        Specify how to react if Svace fails to set up
                        interception upon encountering a transition into a
                        chroot jail. If set to 'fail', terminate the affected
                        process immediately and report a fatal error.
                        Otherwise, disable interception for the affected
                        process and its descendants and, if set to 'warn',
                        also report a warning when 'svace build' completes.
                        This may be useful in cases when chroot() is used for
                        purposes other than running compilations in a chroot
                        jail, making interception failure irrelevant. The
                        default value is 'fail'.

deprecated options (may be removed in a future release):
  --clear-bitcode-dir   An alias for --clear-build-dir.

Environment variables:
  SVACE_DIR             Overrides the default value for --svace-dir
  SVACE_ENABLE_CSA_STAT
                        Overrides the default value for --enable-csa-stat.
                        Must be set to 'yes' or 'no'.

svace config

Usage: svace config [OPTIONS] [<key> [<value>]]
       svace config [OPTIONS] --unset <key>

Manage configuration settings. In the first variant (without --unset), if 
only <key> is specified, get current value assigned to that key (--parents 
must be specified to take system-wide and user-specific configuration into 
account). If both <key> and <value> are specified, set <key> to <value>. For 
boolean keys, possible values are 'true' and 'false'. Some keys that control 
internal (development) settings are 'hidden' and don't show by default, but 
can be listed with --show-hidden.

Common config options (shared by 'warning' and 'config' tools):
  --global           : Work with user-specific configuration
                       ~/.svace/<config-file>, rather than the local
                       configuration $SVACE_DIR/<config-file>
  --system           : Work with system-wide configuration
                       /path/to/distro/config/<config-file>, rather than the
                       local configuration $SVACE_DIR/<config-file>
  -f, --file FILE    : Use specified config file instead of the one implied by
                       options --global, --system, or current svace dir.
                       If FILE is '-', read from stdin and write to stdout
                       (implies --quiet).
  --unset            : Remove the entry for specified key from config file (so
                       that the default value or value specified in parent
                       config files will be used instead).
  --unset-all        : Clear the config file.
  -l, --list         : List the entries specified in config file(s).
  -a, --all          : List entries for all keys, including those not specified
                       in config files. Don't list hidden keys, unless 
                       --show-hidden is specified. Implies --parents.
  --show-hidden      : When listing keys with --all or --list, show hidden keys
                       as well.
  -i, --info         : For each listed key, print a short description.
  -p, --parents      : When reading from config file, take into account the
                       settings specified in parent files. By default, apply
                       system-wide, user-specific and then Svace directory
                       or explicitly specified settings to form the list of
                       modified settings and their values.
                       If --global is specified, apply only system-wide and
                       user-specific settings; if --system is specified,
                       apply only system-wide settings.
  --svace-dir PATH   : Specify a Svace directory, overriding current directory
                       and $SVACE_DIR environment variable.
  -q, --quiet        : Only print error messages (to stderr) and specifically
                       requested output (to stdout).
  --get-val          : Only print the value of specified key (without 'key = ').

svace warning

Usage: svace warning [OPTIONS] [<warn-type> [<state>]]
       svace warning [OPTIONS] --unset <warn-type>

Manage warning settings (which warning types are enabled/disabled). In the
first variant (without --unset), if only <warn-type> (key) is specified, get
current state (value) of that warning type (--parents must be specified to
take system-wide and user-specific configuration into account). If both
<warn-type> and <state> are specified, set <warn-type> to <state>. Possible
values for <state> are 'true', 'false' and 'hidden' (disables a warning type
and hides it from output of '--all').

Special warning name 'all' can be used to enable/disable all warning types
at once, except for warning types hidden by default.
Use special warning name 'hidden' to enable/disable warning types hidden by
default.

Common config options (shared by 'warning' and 'config' tools):
  --global           : Work with user-specific configuration
                       ~/.svace/<config-file>, rather than the local
                       configuration $SVACE_DIR/<config-file>
  --system           : Work with system-wide configuration
                       /path/to/distro/config/<config-file>, rather than the
                       local configuration $SVACE_DIR/<config-file>
  -f, --file FILE    : Use specified config file instead of the one implied by
                       options --global, --system, or current svace dir.
                       If FILE is '-', read from stdin and write to stdout
                       (implies --quiet).
  --unset            : Remove the entry for specified key from config file (so
                       that the default value or value specified in parent
                       config files will be used instead).
  --unset-all        : Clear the config file.
  -l, --list         : List the entries specified in config file(s).
  -a, --all          : List entries for all keys, including those not specified
                       in config files. Don't list hidden keys, unless 
                       --show-hidden is specified. Implies --parents.
  --show-hidden      : When listing keys with --all or --list, show hidden keys
                       as well.
  -i, --info         : For each listed key, print a short description.
  -p, --parents      : When reading from config file, take into account the
                       settings specified in parent files. By default, apply
                       system-wide, user-specific and then Svace directory
                       or explicitly specified settings to form the list of
                       modified settings and their values.
                       If --global is specified, apply only system-wide and
                       user-specific settings; if --system is specified,
                       apply only system-wide settings.
  --svace-dir PATH   : Specify a Svace directory, overriding current directory
                       and $SVACE_DIR environment variable.
  -q, --quiet        : Only print error messages (to stderr) and specifically
                       requested output (to stdout).
  --get-val          : Only print the value of specified key (without 'key = ').

Warning tool specific options:
  --preset NAME      : Work with specific preset of options

Warning type properties (displayed when option --info is given;
see User Guide for more details):
  Situation  : What kind of situation is indicated by the issue.
               Possible values:
                 Quality
                 Security
                 CodingStyle
                 Suppressed
                 Duplicate
                 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
                 CSHARP
                 GO
                 NONE
  Detection tools: Where the checker for the issue is implemented.
               Possible values:
                 SvEng
                 SvaceCppSpecific
                 CSA
                 SpotBugs
                 Goa
                 TagsJavac
                 Roslyn
                 Kotlinc
  Group      : A group which this warning type belongs to.

Examples:
# Get the state of warning type DEREF_AFTER_NULL, taking parent configuration
# into account:
    svace warning -p DEREF_AFTER_NULL

# Disable DEREF_AFTER_NULL for all analysis invocations by current user:
    svace warning --global DEREF_AFTER_NULL false

# Disable DEREF_AFTER_NULL for java language:
    svace warning JAVA.DEREF_AFTER_NULL false

# Disable all warning for language CSHARP:
    svace warning CSHARP false

# Disable autofixes for all warnings (but do not change warnings themselfs):
    svace warning AUTOFIX false

# List all critical warnings:
    svace warning CRITICAL

# List all warnings with autofixes:
    svace warning AUTOFIX

# List all warnings of buffer overflow group:
    svace warning TAINTED_SECTION
 or 
    svace warning TAINTED

# List all warnings by wildcard (only * is allowed):
    svace warning DEREF_*_NULL*

# List 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

svace analyze

This is the main analysis tool. It takes as input the data produced by svace build and generates analysis result files. Command line options are as follows:

usage: svace analyze [options]

Analyze the selected project directory with Svace.
By default, a project directory based on the current working directory 
is selected, and analysis is performed for the last build object produced by
'svace build', which is specified in file <project-dir>/bitcode/build-object.
Use '--svace-dir' to specify a different project directory and '--build'
to specify a different build object.

optional arguments:
  -h, --help            show this help message and exit
  --svace-dir DIR       Specify a Svace directory, overriding current
                        directory and SVACE_DIR environment variable.
  -b HASH, --build HASH
                        A build object to be analyzed. By default, the last
                        build object produced by 'svace build' is used.
  -n NAME, --name NAME  Associate given name with the analyzed project (used
                        for naming files with results and some other files).
                        By default, the name of the project directory is used.
  --memory NUM_MB       Specify maximum amount of RAM (in MB) that Svace may
                        use for analysis. More precisely, this option controls
                        the maximum size of JVM heap. If set to 'auto' (the
                        default), Svace attempts to detect the maximum usable
                        JVM heap size at the time of analysis start and uses
                        the value close to the detected maximum.
  --enable-language LANG, --disable-language LANG
                        Enable or disable analysis for the specified
                        programming language. Supported languages are: all,
                        cxx, go, kotlin. Enabling a language also enables
                        working with the relevant tools (e.g., enabling C/C++
                        turns on analysis with Clang Static Analyzer). Default
                        values set by this option are overridden by
                        --import-<tool>, --skip-import-<tool> and
                        --skip-<lang_or_tool>-analysis options. By default,
                        analysis of all supported languages is enabled.
  -L LANGS, --languages LANGS
                        Enable analysis for languages in the specified comma-
                        separated list and disable it for other languages. The
                        special value 'all' enables all supported languages
                        (which is the default).
  --target TARGET       Select the target architecture of C/C++ code to
                        analyze. If it's set to 'all' (the default), each
                        target is analyzed in turn independently of other
                        targets. Valid values are: all, aarch64, arm, hexagon,
                        mips, mips64, powerpc, powerpc64, riscv32, riscv64,
                        sparc, sparc64, x64, x86.
  --preset NAME         Use the requested warnings preset during the analysis.
  --import-csa, --skip-import-csa
                        Import Clang Static Analyzer results generated at the
                        build phase. Has no effect if CSA is run at the
                        analysis phase (see --skip-csa-analysis).
  --import-spotbugs, --skip-import-spotbugs
                        Import analysis results of SpotBugs generated at the
                        build phase. Has no effect if SpotBugs is run at the
                        analysis phase (see --skip-spotbugs-analysis).
  --import-kotlinc, --skip-import-kotlinc
                        Import analysis results of Kotlin compiler generated
                        at the build phase. Has no effect if kotlinc is run at
                        the analysis phase (see --skip-kotlinc-analysis (not
                        implemented yet)).
  --import-goa, --skip-import-goa
                        Import analysis results of Goa generated at the build
                        phase. Has no effect if Goa is run at the analysis
                        phase (see --run-goa-analysis).
  --skip-c-analysis     Don't analyze C/C++ code with Svace engine. Note that
                        Clang Static Analyzer is not affected by this option.
  --skip-sveng-analysis
                        Don't run Svace engine analysis.
  --skip-csa-analysis   Don't analyze C/C++ code with Clang Static Analyzer.
                        You may still see CSA results if it was enabled at the
                        build phase and its results were imported.
  --skip-spotbugs-analysis
                        Don't analyze Java code with SpotBugs. You may still
                        see SpotBugs results if it was enabled at the build
                        phase and its results were imported.
  --run-goa-analysis    Analyze Golang code with Goa.
  --csa-opts OPTS       Add specified options to the Clang Static Analyzer run
                        by Svace. The options should be separated by
                        semicolons (';'). To specify a literal semicolon,
                        prepend a backslash ('\') to it. To specify a sequence
                        of literal backslashes before a literal semicolon,
                        repeat each of the literal backslashes twice.
  --set-config KEY=VALUE
                        Set the value of config option KEY to VALUE for this
                        analysis run only. Takes precedence over config files.
  --set-warn WARN=(true|false)
                        Enable or disable warning type WARN for this analysis
                        run only. Takes precedence over config files.
  -w FILE, --warnings FILE
                        Load warning settings (information about
                        enabled/disabled warning types) from FILE instead of
                        warn-settings.txt from Svace project directory. Note
                        that defaults from system and global settings are
                        still applied as usual.
  -q, --quiet           Show error messages only.
  -v, --verbose         Show detailed information about analysis and its
                        status.
  --log-level LOG_LEVEL
                        Log level. May have values: quiet, brief, default and
                        verbose.
  --version             Show Svace engine version information and exit. If
                        used together with '--quiet', only version number in
                        X.Y.Z format is printed. The Svace engine version is
                        normally the same as the version of the Svace
                        distribution as shown by 'svace --version'.

experimental options:
  -t HASH, --task HASH  A task object to be analyzed.
  --raw-files           Analyze files with intermediate representation in the
                        build directory ($SVACE_DIR/bitcode by default)
                        instead of analyzing a build object. This option may
                        be useful for debugging, but normally shouldn't be
                        used.
  --with-cache          Use cache for analysis intermediate results. Enabling
                        cache may increased analysis speed of modified
                        project. Require additional disk space.
  --analyze-storage-dir SVACE_ANALYZE_STORAGE_DIR
                        Specify a folder for storing analysis data that are
                        shared between analysis. Analysis with cache, fast
                        analysis and incremental analyses use this folder. By
                        default analysis uses folder $SVACE_DIR/analyze-
                        storage.
  --build-dir DIR       Specify a build directory produced by 'svace build' to
                        use for analysis. The default is $SVACE_DIR/bitcode.
                        Valid only together with --raw-files.
  -s DIR, --source DIR  Search for source code in DIR. Project directory is
                        used by default (as specified, even if the actual
                        Svace directory is its subdirectory .svace-dir). Valid
                        only together with --raw-files.
  -o DIR, --output DIR  Save analysis results in DIR. By default, the results
                        are saved in $SVACE_DIR/analyze-res. The analysis
                        result files are $PRJ_NAME.svres and $PRJ_NAME.txt,
                        where $PRJ_NAME is the name of the analyzed project.
                        Valid only together with --raw-files.
  --fix                 Run autofix during analysis.
  --autofix-backend {svace}
                        Autofix backend to use. The default is 'svace'.
  --emit-autofix-input  Emit autofix input as a separate file (for debug
                        purposes).

svace history

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

svace server

usage: svace server [--server-dir DIR] <subcommand> [args]

optional arguments:
  --server-dir DIR  Specify a Svace server directory. By default, the current
                    directory is used.
  --help            Show this help message and exit.

subcommands:
  subcommand
    init            Create a Svace server directory.
    start           Start the history access server.
    single-start    Start the history access server in single-project mode.
    admin           Manage a Svace server.
    show-api-docs   Display documentation for Svace history API.

Analysis with cache

Svace analysis has special mode which allow to reuse analysis results from previous run. It is intended for cases where project is regular analyzed by Svace.

How to use

Build program

svace init
svace build make

Analyze program with special parameter --use-cache. Svace will store results of intermediate operation to analysis cache.

svace analyze --use-cache

Then program may be changed. Program should be rebuilt and analyzed:

make clean
svace build make
svace analyze --use-cache

Svace will used stored results from previous analysis run. If Svace already has results for intermediate operations than it will be used them. Analysis time should be increased.

Notes

Parameter --use-cache must be used every time. If it will be omitted than results from cache won’t be used.

Svace stores results to folder .svace-dir/analyze-storage. Analysis stores all results not only results from previous run. Such mode may be efficiently used for different projects branches. All result will be in cache.

For using analysis cache project must be build in the same folder.

Time increasing depends on project and changes. We expect that second analysis is in average 10 times faster. Storing results to cache may slightly decrease analysis time compared with regular analysis.

Cache may require sufficient disk space. In average, it is comparable with the size of svace folder after build. After every analysis the cache size grows.

Changing default folder

Folder .svace-dir/analyze-storage where cache is placed may be changed by analysis parameter --analyze-storage-dir. For example:

svace analyze --with-cache --analyze-storage-dir CACHE_DIR

The parameter change storage directory not only for analysis with cache but also for fast analysis.

How it is implemented

We have operations which takes some times. It has input data and output data. Output data are fully defined by input data. Operation is fully independent of other parts of analyzer.

For input data we may quickly calculate hash. Output data are relatively small and may be serialized to disk.

Analysis “does not know” that it is run in special mode. All functions are performed the same way as with regular analysis except of dedicated operations. For such operation Svace creates hash for input data. Then Svace checks cache with for hash. If cache is not empty, then operation is skipped and output data are loaded from cache. If cache is empty then operations is performed and output data are stored to cache.

Described scheme is very simple and reliable.

What cases are supported

CSA analysis. It does not have inter module analysis and that’s why every module are analyzed independently. We recommend to use cache for this analysis.

Tags-javac analysis. It is also does not have inter module analysis. It is based on compilation unit (run of javac). We recommend to use it if build script run many compilation units. If only one compilation unit is used then cache will be obsolete after each source file modification.

Spotbugs analysis. It has similar limitation as Tags-javac analysis. We recommend to use it if build script run many compilation units.

Module parsing. Here input is a module (llvm bitcode file, Java/Kotlin bytecode file). Output is a set of loaded functions. The mode allows to skip module parsing. Moreover, Svace uses module parsing twice inside every analysis: for preliminary phase and for main phase. In some cases uses of such parsing may increase even first analysis because modules will be parsed only on preliminary phase. It has drawback — required disk size. Output data here is all procedures of projects. In some cases it may be bigger than whole svace directory. We recommend to use it if large disk are used.

Function analysis in main phase. It is more complicated analysis which depends on many data: function code, call graph, summaries of called functions, results of preliminary phase. If some function is changed then all functions that are above at call graph must be reanalyzed. Svace analysis spends most time for function analysis. Enabling cache for function analysis may significantly increase analysis time. This analysis depends on preliminary phase. But now we do not have way to define that preliminary phase may change function analysis. We recommend to use it if some changes with regular analysis are acceptable.

Customization

There are two ways to enable analysis with cache. First way is to use option PERSISTENT_MODE:

svace config PERSISTENT_MODE true

After setting this option all further analysis will be done with cache until this option will be disabled.

Second way is to use parameter –use-cache:

svace analyze --use-cache

This parameter temporary enable option PERSISTENT_MODE inside svace analyzer. Never configurations file will be changed. This option should be used every time when analysis is run. It is possible to enable/disable particular analysis by follow options:

Above options are enabled by default.

Also, it is possible to disable logging by using option PERSISTENT_MODE.LOGGING.

Details

Cache is stored at .svace-dir/analyze-storage. Currect implementations are based on file system. No databases are used. Every operation stores data to its own directory:

Logs are put to .svace-dir/analyze-res/logs/<proj>-persistent.log

If cache is enabled then Svace print to console:

Persistent mode is enabled. Some results may be got from cache.

Cache is “flat”. It stores all information from different analysis to the same place. It may be used for analysing different branches of some project. Formally it may be used for analysing different projects, but it is very unlikely that cache will be useful in this case.

Known limitations

The mode is based on file hashes. Even minor changes of 1 bit will lead to hash changes. Any changes lead to cache missing.

Project must be build in the same folder. Changing build folder lead to changing hashes.

Modification of C/C header files lead to changes all modules which are using those headers.

Compiler options also changes hashes.

Svace never clean folder .svace-dir/analyze-res. For now, we do not know what clear mode is needed for users.

Svace settings are not taken into account. If you change option which changes analysis then cache will be incorrect. It is responsibility of user to clean the cache. It is implemented so because many settings are not changes results. For some options it will be desirable to use cache with slightly other results instead of spending more time for analysis.

Svace version are not checked. We do not recommend to use different Svace version with the same cache.

Cache may be ineffective for spotbugs and tags-javac analysis. The reason — compilation unit is used for hash creating. Many Java projects have only one compilation unit.

Analysis with cache may produce different results for main function analysis if they depend on preliminary phase. Preliminary phase collects data about global arrays and constants, performs pointer analysis. Now we can`t define what changes may affect analysis of separate function. If preliminary phase is disabled (USE_PRELIMINARY_PHASE is false) then results should be the same.

Cache does not check it integrity.

Remote analysis

It allows to analyze project on remote server.

How to use

We have two machines remove-server and local-machine. On remove server we have to create svace directory in some folder:

mkdir server
cd server
svace init

Then run svace remote server:

svace server single-start

This command print to console:

Remote analysis server started at port 55135

Now we build project on local machine:

svace init
svace build make

If we would like to analyze project on local machine we run command

svace analyze

but now we want to analyze on server. For doing it we have to run follow command:

svace remote --host remove-server analyze

After running this command, Svace passes all needed data to remote server and performs analysis there. Command remote returns control back when analysis on server is finished. Then it puts results into a .svres file on local machine to regular place: .svace-dir/analyze-res/<proj>.svres.

It is possible to review analysis progress via web browser by reference: http://remote-server:8060/status.

Why remote analysis may be needed

Remote analysis may be useful in follow cases:

Data transferring

Data are passed to server by network using port 55135. It may be changed by modifying option SERVER_PORT in file .svace-dir/conf/svace.conf on remote machine. For changing port on local machine parameter port should be used:

svace remote --host remove-server --port 10000 analyze

Data are send efficiently. Sending data takes a little time compared with analysis time. Sending is limited by network throughput.

Transferring is based on file hashes. Only those files that are needed are passed. If remote server already has such file it won`t be sending again. That is why transferring is especially efficient if project regular is built and analyzed on remote server.

Remote analysis server

It may perform several analysis. By default, limit is 3. It is possible to change such behavior by option SIMULTANEOUS_ANALYSIS_LIMIT in file .svace-dir/conf/svace.conf.

If limit is reached client will wait when some analysis will finish.

All analysis data except results (svres-file) are stored on server. Server put data to different folders inside svace-directory and never clear it.

If analysis option ZIP_ANALYZE_RES is enable Svace will compress folder analyze-res to zip file. This option affects remote analysis too and may be used to transferring analysis results to client machine.

Option REJECT_CLIENT_CONFIGS set up a special server config which ignore user setting and warning configs. This option is disabled by default. In this case user can set up configs for settings and warnings on client machine which will be used on server.

Tutorial

This section demonstrates the process of using the tool with an example (analysis of proftpd-1.3.3).

Building source code

Let’s assume that Svace is located in ~/svace/, on an Ubuntu system.

A copy of the proftpd-1.3.3 distributive can be downloaded from the Internet at ftp://ftp.proftpd.org/distrib/source/proftpd-1.3.3.tar.bz2. Extract the archive in ~/projects/proftpd-1.3.3/.

The package can be built using configure/make. To obtain LLVM bitcode files for analysis by svace, svace project folder must be initialized first and then building commands need to be wrapped in svace build command as follows:

cd ~/projects/proftpd-1.3.3
./configure
~/svace/bin/svace init
~/svace/bin/svace build make

If the operation completes successfully, a text like the following will be displayed by build:

Now Svace will preprocess captured data.
Processing source code markup data...
[**********************************************************************] 100%
Assembled build object: [BUILD]6715d4e4c908f8e62a54f174796282c48ce2d3a5
  83 C/C++ units are ready.

As a result, the folder ~/projects/proftpd-1.3.3/.svace-dir/bitcode/ should now contain files with intermediate representation, for example the file

~/projects/proftpd-1.3.3/.svace-dir/bitcode/auth.e7d9dbb866bdbd6c2456bf8f3212d727.bc

Further analysis will only require files within Svace project folder (~/projects/proftpd-1.3.3/.svace-dir), so the other byproducts of the building process can be safely removed.

Performing analysis

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;

Viewing the results

For example, consider the warning NULL_AFTER_DEREF/proftpd-1.3.3/auth.c:1227. Below is the corresponding excerpt from the plain text file ~/projects/proftpd-1.3.3/.svace-dir/analyze-res/proftpd-1.3.3.txt:

* NULL_AFTER_DEREF: Pointer 'login_name' which was dereferenced at line 1222 is compared to NULL value at line 1227.
    null check at auth.c:1227
    dereference at auth.c:1222
    source line: "if ((!login_name || !anon_c) &&"

The relevant part of the source code is as follows:

1222: if (*login_name &&
          auth_alias_only &&
          *auth_alias_only == TRUE)
        *login_name = NULL;

1227: if ((!login_name || !anon_c) &&
          anon_name) {
        *anon_name = NULL;
      }

On line 1222, variable login_name is dereferenced. If login_name is NULL, this will be a null pointer dereference. On line 1227, this pointer is compared to NULL, which suggests that it indeed can be NULL on line 1222. If it can’t, then the check on line 1227 is redundant.

Warning types

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.

Kinds of warning types

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

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

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.

Type of detected situations

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.

Detection tool

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.).

Warning subtype specifiers

Some warnings have common subtypes, that are indicated by adding a suffix after a dot to the warning type. For example, the TAINTED_INT warning type has a suppressed subtype TAINTED_INT.MIGHT.

.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.

.PROC (except C#)

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.

.GLOBAL

This subtype indicates that memory locations involved in these warning reports have global scope.

.MINOR

This subtype indicates that severity of warning may be less than for major type.

.STRICT

This subtype indicates that a warning is reported may require very strict error policy to analyzed source code.

.EX

This subtype indicates that a warning is reported by an extended version of the checker.

.EXCEPTION

This subtype indicates that a warning is reported for an exception path.

.RET

This subtype indicates that a warning is related to a value returned from a function.

.LIB

This subtype indicates that a warning is related with a library function.

.LOOP

This subtype indicates that the issue is reported for a specific loop iteration.

.COND

This subtype indicates that the issue is reported for a specific condition branch.

.TEST (C#)

This subtype indicates that a warning is reported in test code.

.ARGUMENT (C#)

This subtype has the meaning of .PROC in C# null dereference checkers.

Null pointer dereference

DEREF_OF_NULL

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Critical High true
KOTLIN Quality Normal High true
CSHARP Quality Critical High true

This checker finds situations where a pointer is dereferenced, but can only have NULL value, and so the operation of dereference can never be run without causing a runtime error. A special case where the NULL value is explicitly assigned is categorized as DEREF_OF_NULL.CONST.

More generally, checkers in the DEREF_OF_NULL group find situations where a dereferenced pointer can be assigned NULL value on one of the possible execution paths.

Subtype .PROC (except C#) or .ARGUMENT (only C#) may be applied to the warnings of this group.

Example (C/C++)

struct process {
    char* user;
};

void test(struct process* ps, FILE* log) {
    if (!ps)
        fprintf(log, "No information for user: %s", ps->user);
}

Dereference of structure pointer ps, if reachable, can only lead to a null pointer dereference.

Example (Kotlin)

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.

Example (Kotlin)

fun exampleOfCompilationError(s: String?) {
    if (s == null) {
        s!!.length // kotlin compiler report a compilation error here
    }
}

See also

DEREF_OF_NULL.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Critical Average true
GO Quality Critical Average true
KOTLIN Quality Normal Average true

This checker is similar to DEREF_OF_NULL checker, but can find situations where a pointer is assigned to NULL under some condition and then dereferenced under another condition which is not incompatible with the first one.

Example (Kotlin)

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.

DEREF_OF_NULL.CONST

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Critical High true
GO Quality Critical High true
KOTLIN Quality Normal High true

This checker finds situations where a pointer is dereferenced, but can only have NULL value, because it was explicitly assigned a NULL value.

Example

void test() {
    int* ptr = NULL;
    int x;

    // ...

    x = *ptr;
}

Example (Kotlin)

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.

False positives

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.

DEREF_OF_NULL.ASSIGN

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true
GO Quality Major Average true
KOTLIN Quality Normal Average true

This checker finds situations where a pointer is assigned NULL value, and is subsequently dereferenced. In these situations, the dereferenced pointer is not necessarily NULL, but a pointer that was assigned NULL value is necessarily dereferenced.

Example (C/C++)

void proc(int* p, int flag) {
    if (flag == 7)
        p = 0; // NULL value assigned to `p`.

    *p = 7; // If NULL value is assigned to `p`, it's necessarily dereferenced here.
}

Example (Kotlin)

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.

See also

DEREF_OF_NULL.COND

Language Situation Severity Reliability Enabled
JAVA Quality Normal High true
CXX Quality Normal High true
GO Quality Normal High true
KOTLIN Quality Normal High true

This checker detects issues where a NULL pointer value is passed to a function, which dereferences it under some uncontrolled conditions.

Example (C/C++)

#include <stddef.h>

extern int get_data();
extern void consume(int);

void deref_maybe(int *p) {
    int data = get_data();
    consume(data);
    if (data > 1) {
        *p += data;
    }
}

void example() {
    deref_maybe(NULL);
}

void deref_fixed(int *p) {
    int data = get_data();
    consume(data);
    if (p && data > 1) {
        *p += data;
    }
}

void example_fixed() {
    deref_fixed(NULL);
}

Function example illustrates the defect: NULL pointer value is passed to deref_maybe function, which dereferences it, if the value of data returned by get_data function is greater than 1.

Functions deref_fixed and example_fixed illustrate a possible fix.

Example (Kotlin)

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 ?..

False positives

Sometimes, the condition under which the function dereferences the pointer is not satisfiable at the call site where NULL value is assigned to that pointer. The checker emits the warning even if this possibility wasn’t ruled out, which leads to false positives in such cases.

DEREF_OF_NULL.EX.COND

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true

This checker detects issues where a NULL pointer value is passed to a function, which dereferences it under certain conditions.

Example (C/C++)

#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.

DEREF_OF_NULL.STRICT

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown false
CXX Quality Normal Unknown false
KOTLIN Quality Minor Unknown false

This checker finds null pointer dereferences that are less reliable than those found by the DEREF_OF_NULL checker. Additionally, for Kotlin it reports situations where a value with a nullable annotation is dereferenced. Note that a nullable annotation is supported as a language feature (a nullable type) in case of Kotlin.

Example (Kotlin)

fun getLen(s: String?) = s!!.length

fun getLenCorrect(s: String?) = s?.length ?: throw IllegalStateException()

fun getLenOf(s: Any?) = (s as String).length

fun getLenOfCorrect(s: Any?) = (s as? String)?.length ?: throw IllegalStateException()

Functions getLen and getLenOf illustrate the defect: s may have null value and is cast to non-nullable type by !! and as operators respectively.

Functions getLenCorrect and getLenOfCorrect illustrate possible fixes: use null safe ?. and as? operators.

DEREF_OF_NULL.RET.ALLOC

Language Situation Severity Reliability Enabled
CXX Quality Normal High false

This warning finds situations where a pointer returned by a memory allocation function, such as malloc(), is dereferenced without being checked for NULL value.

Example

void test() {
    char* buf = (char*) malloc(10);
    buf[0] = '\0';
}

See also

DEREF_OF_NULL.RET.LIB

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Major Unknown true
KOTLIN Quality Normal Unknown true
CSHARP Quality Major Unknown true

This detector finds situations where a pointer returned by a library function is dereferenced without being checked for NULL value.

Following is the partial list of library functions that are recognized by this detector as being able to return NULL value for reasons not under user’s control:

Examples from C standard library:

Examples from Java libraries:

Example (C/C++)

void example() {
    char *s = getenv("RANDFILE");
    if (s[0] == '\0') {
        // ...
    }
}

void possible_fix() {
    char *s = getenv("RANDFILE");
    if (s != NULL && s[0] == '\0') {
        // ...
    }
}

Function example illustrates the defect: function getenv may return NULL, which will be dereferenced by array access at index 0.

Function possible_fix illustrates a possible fix: check the result of getenv call.

Example (Kotlin)

import java.io.File

fun example(f: File) = f.parentFile.listFiles()

fun possibleFix(f: File) = f.parentFile?.listFiles()

Function example illustrates the defect: property parentFile may return null, which will be dereferenced by listFiles call.

Function possibleFix illustrates a possible fix: use safe call of listFiles function.

DEREF_OF_NULL.RET.LIB.PROC

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Major Unknown true
KOTLIN Quality Normal Unknown true
CSHARP Quality Major Unknown true

This detector is a subtype of DEREF_OF_NULL.RET.LIB detector. DEREF_OF_NULL.RET.LIB.PROC finds situations where a pointer returned by a library function may have NULL value and is dereferenced within a function call.

Example (C/C++)

void example() {
    FILE *f;
    f = fopen("test.txt", "r");
    fclose(f);
}

void possible_fix() {
    FILE *f;
    f = fopen("test.txt", "r");
    if (f != NULL) {
        fclose(f);
    }
}

Function example illustrates the defect: function fopen may return NULL, which will be dereferenced within fclose call.

Function possible_fix illustrates a possible fix: check the result of fopen call.

Example (Kotlin)

import java.io.File
fun handleFiles(arr: Array<File>?) = arr!!.count { f -> f.isFile && !f.isDirectory }

fun exampleProc(f: File) = handleFiles(f.listFiles())

fun handleFilesCorrect(arr: Array<File>?) = arr?.count { f -> f.isFile && !f.isDirectory } ?: 0

fun possibleFixProc(f: File) = handleFilesCorrect(f.listFiles())

Function exampleProc illustrates the defect: call of listFiles function may return null, which will be dereferenced within the call of user defined function handleFiles.

Function possibleFixProc illustrates a possible fix: use safe implementation handleFilesCorrect to iterate over files. Safe call of count is used inside handleFilesCorrect function, also it returns zero if arr is null.

See also

DEREF_OF_NULL.RET.USER

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown false

This is an obsolete detector. Both DEREF_OF_NULL.RET.USER and DEREF_OF_NULL.RET.USER.PROC have been replaced with DEREF_OF_NULL.RET detector.

DEREF_OF_NULL.RET.USER.PROC

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown false
CSHARP Quality Normal Unknown true

This is an obsolete detector. Both DEREF_OF_NULL.RET.USER and DEREF_OF_NULL.RET.USER.PROC have been replaced with DEREF_OF_NULL.RET detector.

DEREF_OF_NULL.RET

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true
CXX Quality Normal Unknown true
GO Quality Normal Unknown true
KOTLIN Quality Normal Unknown true

This detector finds situations where a pointer returned by a user-written procedure that returns NULL on some execution paths, is dereferenced without being checked for NULL value.

Example (C/C++)

#define USE_PREFIX 1
#define MAX_SIZE 1024

char* buf[MAX_SIZE];

char* get_login(const char* url, int flag) {
    if (flag == USE_PREFIX && url[0] != '%' && url[1] != '%')
        return NULL;

    return fill_from_bd(buf, MAX_SIZE);
}

void use(const char* url) {
    char* login = get_login(url, USE_PREFIX);
    int len = strlen(login);
    // ...
}

Example (Kotlin)

data class Wrapper(val value: Int)

class Helper(val wr: Wrapper?) {
    fun getMaybeNull() = wr?.value
}

fun deref(n: Int?) = n!!.times(3)

fun example(h: Helper): Int {
    val num = h.getMaybeNull()
    return deref(num)
}

fun derefCorrect(n: Int?) = n?.times(3)

fun possibleFix(h: Helper): Int? {
    val num = h.getMaybeNull()
    return derefCorrect(num)
}

Function example illustrates the defect: num may be null after getMaybeNull call and it is dereferenced by call of user defined function deref. Function possibleFix illustrates a possible fix: use safe implementation derefCorrect. Safe call of times is used inside derefCorrect function.

DEREF_OF_NULL.RET.STAT

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true

This detector is a statistical version of DEREF_OF_NULL.RET.

It detects issues when a pointer returned by call to a function is dereferenced without a check to NULL, while for a number of other calls to the same function it is dereferenced only after a proper NULL check.

Example (C/C++)

#include <stddef.h>

extern int *get_ptr(int);

void example() {
    int *p1 = get_ptr(1);
    if (p1)
        *p1 = 3;
    int *p2 = get_ptr(2);
    if (p2)
        *p2 = 1;
    int *p3 = get_ptr(3);
    if (p3)
        *p3 = 4;
    int *p4 = get_ptr(4);
    if (p4)
        *p4 = 1;
    int *p5 = get_ptr(5);
    //if (p5)
        *p5 = 5;
    int *p6 = get_ptr(6);
    if (p6)
        *p6 = 9;
    int *p7 = get_ptr(7);
    if (p7)
        *p7 = 2;
    int *p8 = get_ptr(8);
    if (p8)
        *p8 = 6;
}

Function example illustrates the defect: each time the function get_ptr is called, the returned value is checked to NULL before its dereference, except for variable p5.

Possible fix is to uncomment the commented if, which checks pointer p5 before its dereference.

Settings

See also

DEREF_OF_NULL.DYN_CAST

Language Situation Severity Reliability Enabled
CXX Quality Critical High true

This checker finds issues where pointer value is the result of dynamic_cast C++ operator, it might be NULL, and it is dereferenced without an appropriate check.

Example (C/C++)

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 dynamic_cast operator, and it is dereferenced without any check.

Function example_fixed illustrates a possible fix.

False positives

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.

DEREF_OF_NULL.DYN_CAST.PROC

Language Situation Severity Reliability Enabled
CXX Quality Critical Low true

This checker finds issues where a pointer value comes from a call to function, where the pointer is the result of dynamic_cast C++ operator and it might be NULL, and then it is dereferenced after the call without an appropriate check.

Example (C/C++)

class A;
class B;

class A {
public:
    virtual int a() { return 0; }
    B *asB();
};

class B : public A { 
public:
    virtual int a() { return 1; }
    virtual int b() { return 0; }
};

B *A::asB() {
    return dynamic_cast<B *>(this);
}

int example(A *a) {
    B *b = a->asB();
    return b->b();
}

int example_fixed(A *a) {
    B *b = a->asB();
    if (!b)
        return -1;
    return b->b();
}

Function example illustrates the defect: pointer b is the return value of call to A::asB, where it comes from dynamic_cast operator, and it is dereferenced without any check.

Function example_fixed illustrates a possible fix.

False positives

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.

DEREF_AFTER_NULL

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Critical High true
GO Quality Critical High true
KOTLIN Quality Normal High true
CSHARP Quality Critical High true

This checker finds situations where first, a pointer is compared to NULL (which indicates that it could have a NULL value), and then it is dereferenced (unconditionally).

A subtype DEREF_AFTER_NULL.LOOP is emitted when the conditional expression comparing the pointer to NULL is part of a loop.

A (suppressed) .MACRO subtype of this warning is emitted if it is suspected that the warning is a false positive, caused by the conditional expressions being implemented within a macro. Such checks can be too general and not always reflect possible value range of index variables.

Example (C/C++)

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.

Example (Kotlin)

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 $age years old") }
}

Function example illustrates the defect: age is got from arg with null check, but name is got avoiding null check.

Function possibleFix illustrates a possible fix. Note that Kotlin compiler knows that arg is not null into let code block.

False positives

This warning can cause false positives if the conditional expression has a side effect of terminating the program, but that wasn’t noticed by the tool. For example:

if (!str1) {
    my_exit();
}
return strcmp(str1, str2);

If instead of my_exit(), a standard exit(int) is called, no false warnings will be emitted. The same holds if my_exit() transparently calls exit(int) or one of the other functions annotated as terminating.

See also

DEREF_AFTER_NULL.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Critical Average true
GO Quality Critical Average true
KOTLIN Quality Normal Average true

This checker is a version of the DEREF_AFTER_NULL with path sensitivity. Unlike DEREF_AFTER_NULL, it gives warning when the variable after comparing with null can be dereferenced, and there is a combination of the input parameters, what may cause an error.

Example (Kotlin)

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 onwards by passing as first parameter into parse method.

Function possibleFix illustrates a possible fix: change function contract. First argument of possibleFix function is non-nullable.

DEREF_AFTER_NULL.COND

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true

This checker detects issues where a pointer is compared to NULL (which indicates that it could be NULL) and then passed to a function, which dereferences it under some uncontrolled conditions.

Example (C/C++)

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.

False positives

In some cases the checker might miss information that the condition of the dereference within the called function is not feasible with the precondition of the call itself.

DEREF_AFTER_NULL.EX.COND

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true

This checker detects issues where a pointer is compared to NULL (which indicates that it could be NULL) and then passed to a function, which dereferences it under certain conditions.

Example (C/C++)

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.

False positives

In some cases the checker might miss information that the condition of the dereference within the called function is not feasible with the precondition of the call itself.

DEREF_AFTER_NULL.LOOP

Language Situation Severity Reliability Enabled
JAVA Quality Normal Average true
CXX Quality Normal Average true

This checker detects issues where a pointer is compared to NULL (which indicates that it could be NULL) in a loop condition, and then the pointer is dereferenced.

Example (C/C++)

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.

False positives

Sometimes, when a loop uses several conditions which controls its execution, the assumption that the comparison implies NULL value might not be correct for the path, where the warning is reported, due to some additional unexplicit preconditions.

DEREF_AFTER_NULL.MIGHT

Language Situation Severity Reliability Enabled
JAVA Suppressed Major Average true
CXX Suppressed Major Average true

This checker detects issues where a pointer is compared to NULL (which indicates that it could be NULL), and then dereferenced on a path, where the program execution might be terminated, while the analysis is not able to ensure, whether it avoids NULL pointer dereference, or not.

Example (C/C++)

#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.

False positives

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.

NULL_AFTER_DEREF

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true
KOTLIN Quality Normal High true
CSHARP Quality Major High true

The checker NULL_AFTER_DEREF finds situations where first, a pointer is dereferenced, and then it is compared to null (which indicates that it could have a NULL value).

A (suppressed) .MACRO subtype of this warning is emitted if it is suspected that the warning is a false positive, caused by the conditional expressions being implemented within a macro. Such checks can be too general and not always reflect possible value range of index variables.

Example (C/C++)

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.

Example (Kotlin)

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.

Notes

In some cases, the dereference indicated in the warning points to a function that returns the pointer. Such warnings mean that the function that produced the pointer unconditionally dereferences it, and so the returned pointer can’t be NULL. In some cases, the code that checks the returned value for quality to NULL is protective or follows the specification that allows NULL value as possible behavior. In other cases, presence of the check indicates an incorrect assumption about the function.

See also

NULL_AFTER_DEREF.RET

Language Situation Severity Reliability Enabled
JAVA CodingStyle Minor Average false
CXX CodingStyle Minor Average false

The checker is like NULL_AFTER_DEREF, but it finds situations where pointer was dereferenced in a function and then returned. If the caller of that function still compares the returned value to NULL, they believe that it’s possible for the returned value to be NULL, while it’s actually not.

Example

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';
}

COMPARE_LOCAL_ADDR

Language Situation Severity Reliability Enabled
CXX Quality Minor Average true

Since address of a local variable can’t be null comparing it with null value is redundant.

Example

int foo() {
    int x = 0;
    if (&x) // COMPARE_LOCAL_ADDR, maybe it should be `if (x)`.
        return 0;
    return 1;
}

Tainted input

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, ...)

TAINTED_INT

Language Situation Severity Reliability Enabled
JAVA Quality Critical Average true
CXX Quality Critical Average true
GO Quality Critical Average true

The checker TAINTED_INT finds situations where an integer value received from external source (from a file or from the network; such value is called tainted value) is used in an operation where uncontrolled value may cause problems, such as allocation of memory of given size, or reading the given number of bytes from a file.

There is a .MIGHT variant of this warning (TAINTED_INT.MIGHT).

This warning has the following subtypes:

Example

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);
}

TAINTED_INT.MIGHT

Language Situation Severity Reliability Enabled
JAVA Suppressed Major Average true
CXX Suppressed Major Average true
GO Suppressed Major Average true

.MIGHT variant of TAINTED_INT, which finds situtations where tainted value is used in an unsafe operation, where value is tainted only on some execution paths.

Example

void test(char* str, int flag) {
    int count;
    if (flag) {
        count = atoi(str);
    } else {
        count = -1;
    }

    usleep(count);
}

TAINTED_INT.CTYPE

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

A subtype of TAINTED_INT, where a tainted value is used in character type functions from header <ctype.h> (isdigit, isspace, etc.). ISO C requires that ctype functions work for unsigned char values and for EOF. Other values may lead to undefined behavior, but many library implementations also support negative signed char and some other values. This warning is suppressed if tainted integer is known to have values only in interval [-128; 255].

Example

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;
}

TAINTED_INT.LOOP

Language Situation Severity Reliability Enabled
JAVA Quality Critical High true
CXX Quality Critical High true

A subtype of TAINTED_INT, where the tainted value is used as a bound in a loop, and thus may cause the program to hang.

There is a .MIGHT variant of this warning (TAINTED_INT.LOOP.MIGHT).

Example

size_t size;
char* res;

void test() {
    int i;
    char* env = getenv("QQQ");
    size = strtoul(env, NULL, 10);

    for (i = 0; i < size; i++) {
        // ...
    }
}

TAINTED_INT.LOOP.MIGHT

Language Situation Severity Reliability Enabled
JAVA Suppressed Critical Average true
CXX Suppressed Critical Average true

.MIGHT variant of TAINTED_INT.LOOP, which find situations where loop bound is tainted only on some execution paths.

Example

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++) {
        // ...
    }
}

TAINTED_INT.INFINITE_LOOP

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true

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).

Example

void test() {
    int step = getchar();

    for (char i = 0; i < 100; i += step) {
        // ...
    }
}

TAINTED_INT.INFINITE_LOOP.MIGHT

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true

.MIGHT variant of TAINTED_INT.INFINITE_LOOP, which find situations where loop step is tainted only on some execution paths.

Example

void test(bool flag) {
    int step;
    if (flag) {
        step = getchar();
    } else {
        step = 1;
    }

    for (char i = 0; i < 100; i += step) {
        // ...
    }
}

TAINTED_ARRAY_INDEX

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Critical Average true
GO Quality Critical Average true
KOTLIN Quality Critical Average true

The checker TAINTED_ARRAY_INDEX finds situations where an integer value received from external source (from a file or from the network) is used as an index in accessing an array, without ensuring that it’s within bounds. This warning is a subtype of TAINTED_INT.

There is a .MIGHT variant of this warning (TAINTED_ARRAY_INDEX.MIGHT).

Example (C/C++)

void array_index() {
    int buf[256];
    int index = getchar();

    if (index < 256) {
        // Index may still be negative!
        buf[index] = 7;
    }
}

Example (Kotlin)

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.

TAINTED_ARRAY_INDEX.MIGHT

Language Situation Severity Reliability Enabled
CXX Suppressed Major Unknown true

.MIGHT version of TAINTED_ARRAY_INDEX, which checks if out-of-bounds array access could happen on some execution paths.

Example

void array_index(bool flag) {
    int buf[256];
    int index = getchar();

    if (flag && index < 256) {
        // Index may still be negative!
        buf[index] = 7;
    }
}

TAINTED_ARRAY_INDEX.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Critical Average true
GO Quality Critical Average false

Extended version of TAINTED_ARRAY_INDEX, which checks if out-of-bounds array access could happen on some execution paths. Very similar to TAINTED_ARRAY_INDEX.MIGHT, but can find more diffucult cases.

Example

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;
    }
}

TAINTED_INT.PTR

Language Situation Severity Reliability Enabled
JAVA Quality Critical High true
CXX Quality Critical High true

This checker is very similar to TAINTED_ARRAY_INDEX, except that it finds situations where array access with tainted index value happens through a pointer (TAINTED_ARRAY_INDEX is emitted only when the array is identified explicitly by name).

There is a .MIGHT variant of this warning (TAINTED_INT.PTR.MIGHT).

Example

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`.
}

TAINTED_INT.PTR.MIGHT

Language Situation Severity Reliability Enabled
JAVA Suppressed Major High true
CXX Suppressed Major High true

.MIGHT variant of TAINTED_INT.PTR, which checks if out-of-bounds array access could happen on some execution paths.

Example

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`.
    }
}

TAINTED_PTR

Language Situation Severity Reliability Enabled
JAVA Quality Critical VeryHigh true
CXX Quality Critical VeryHigh true
GO Quality Critical VeryHigh true

The checker TAINTED_PTR finds situations where a string received from external source (from a file or from the network) is used in an operation where uncontrolled string (or unbounded size of the string) may cause problems, such as copying to a fixed-size array.

There is a .MIGHT variant of this warning TAINTED_PTR.MIGHT.

Example

char* env;
char buf[100];

void test() {
    env = getenv("VAR3");

    // Copying a string of unbounded size to a fixed-size buffer.
    strcpy(buf, env);
}

TAINTED_PTR.MIGHT

Language Situation Severity Reliability Enabled
JAVA Suppressed Critical High true
CXX Suppressed Critical High true

.MIGHT variant of TAINTED_PTR, which find situtations where string is tainted only on some execution paths.

Example

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);
}

TAINTED.SPRINTF

Language Situation Severity Reliability Enabled
CXX Quality Critical Unknown true

This checker finds uses of library function sprintf() that may overflow the destination buffer, because some values (integer values or strings) printed according to a constant format string are tainted and unchecked.

Example 1

char buf[100];
char dst[16];

void test() {
    fgets(buf, 50, stdin);
    sprintf(dst, "* %s\n", buf);
}

Example 2

size_t size;
char dst[5];

void test() {
    char* env = getenv("QQQ");
    size = strtoul(env, NULL, 10);

    sprintf(dst, "%d", size);
}

TAINTED_PTR.FORMAT_STRING

Language Situation Severity Reliability Enabled
JAVA Quality Critical High true
CXX Quality Critical High true

The checker TAINTED_PTR.FORMAT_STRING finds situations where a string received from external source (from a file or from the network) is used as a format string parameter in functions of printf() family. This vulnerability may be used in a format string attack.

Example

void fmt_str(int s, char* buf, int len, int flags) {
    recv(s, buf, len, flags);
    printf(buf);
}

Buffer overflow

STATIC_OVERFLOW

Language Situation Severity Reliability Enabled
JAVA Quality Major Low true
CXX Quality Critical Low true
GO Quality Critical Low true

The checker STATIC_OVERFLOW finds situations where a fixed-size array is accessed at a constant index outside its range.

Example

void buf_overflow() {
    char buf[1024];
    buf[1024] = 0;
}

DYNAMIC_OVERFLOW

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Critical High true
GO Quality Critical High true

The checker DYNAMIC_OVERFLOW finds situations where a dynamic array is accessed at a constant index outside its range.

Example

void buf_overflow() {
    char *buf = (char *) malloc(1024);
    buf[1024] = 0;
    free(buf);
}

DYNAMIC_OVERFLOW.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major VeryLow true
CXX Quality Critical VeryLow true

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.

Example

void buf_overflow() {
    char *buf = (char *) malloc(1024);
    buf[1024] = 0;
    free(buf);
}

STATIC_OVERFLOW.LOCAL

Language Situation Severity Reliability Enabled
JAVA Quality Major Low true
CXX Quality Critical Low true

This checker finds buffer overflow situations where a buffer is accessed (with potential overflow) in the same procedure where it’s locally defined.

Example

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.

See also

STATIC_OVERFLOW.PROC

Language Situation Severity Reliability Enabled
JAVA Quality Major VeryLow false
CXX Quality Critical VeryLow false

This checker finds buffer overflow situations where a buffer is passed into another procedure before being accessed (with potential overflow).

Example

void access_buf(int* buf) {
    buf[10] = 0;
}

void run() {
    int buf[10];
    access_buf(buf);
}

Since buf is only 10 elements long, the access operation in procedure access_buf overflows the buffer. This situation is detected interprocedurally, since the size of the buffer is not available inside procedure access_buf.

See also

DYNAMIC_OVERFLOW.PROC

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown false
CXX Quality Critical Unknown false
GO Quality Critical Unknown false

This checker finds overflows for heap buffers where a buffer is passed into another procedure before being accessed (with potential overflow).

Example

void access_buf(int* buf)
{
    buf[10]=0;
}

void test() {
    int* buff = new int[10];
    access_buf(buff); // Overflow.
}

STATIC_OVERFLOW.SPRINTF

Language Situation Severity Reliability Enabled
CXX Quality Critical Unknown true

This checker finds situation where a function from sprintf() family is used to write to a buffer of known size, but the constant format string and possible values of the arguments are such that the buffer could overflow as a result of the call.

Example

void static_overflow(char* val) {
    char buf[6];
    sprintf(buf,"%d!", val);
}

The checker will emit a warning for this example, unless val is known to be in the interval [-999, 9999] (up to 4 characters for the value, one for symbol ! and one for NULL terminator at the end of the string, total of up to 6 characters).

STATIC_OVERFLOW.SCANF

Language Situation Severity Reliability Enabled
CXX Quality Critical VeryHigh true

This checker finds situation where a function from scanf() family is used to read data into a buffer of known size, but the constant format string and possible values of the source of the data are such that the buffer could overflow as a result of the call.

Example

void static_overflow(FILE* f) {
    char buf[50];
    fscanf(f, "%s", buf); // `buf` can overflow.
}

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.
}

BUFFER_OVERFLOW.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Critical Average true
GO Quality Critical Average false

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);
}

BUFFER_UNDERFLOW

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Critical Unknown true
GO Quality Critical Unknown false

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;
    }
}

CHECK_AFTER_OVERFLOW

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true

The checker CHECK_AFTER_OVERFLOW finds situations where first, a buffer is accessed with a certain index, and then this index is compared to some value that indicates that the index may lie outside the buffer’s range.

Example

char buf[256];

int test(int i) {
    buf[i] = 0;

    if (i != 256)
        return 1;
    return 0;
}

In this example, the check at the end shows that i is expected to sometimes have a value of 256, in which case the buffer access above will be out of bounds.

A (suppressed) .MACRO subtype of this warning is emitted if it is suspected that the warning is a false positive, caused by the conditional expressions being implemented within a macro. Such checks can be too general and not always reflect possible value range of index variables.

OVERFLOW_AFTER_CHECK

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Critical Average true

The checker OVERFLOW_AFTER_CHECK finds situations where first, a variable is compared to some value, indicating what the possible values of the variable are, and then it’s used to access a buffer in such a way that one of the possible values indicated by the check lies out of bounds.

Example

char buf[256];

void overflow(int i) {
    if (i > 255)
        printf("i > 255");

    buf[i] = 0;
}

OVERFLOW_AFTER_CHECK.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Critical High true
GO Quality Critical High false

The checker is similar to OVERFLOW_AFTER_CHECK, but uses SMT solver and is capable of finding and filtering out more complex defects.

Example

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.
    }
}

OVERFLOW_UNDER_CHECK

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Critical Average true
GO Quality Critical Average true

Issues of this type are detected when the value of an index used to access a buffer is checked with a bound that is less strict than necessary. A bound on an index value can be used to ensure the absence of buffer overflows due to index value being too high or too low, but if the bound is itself too high or too low, a buffer overflow can still occur. For example, if a buffer has 10 elements, index values up to 9 are permitted, but checking that the index is less than 20 allows values between 10 and 19 that would lead to buffer overflow.

Example

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.

OVERFLOW_UNDER_CHECK.LIB

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Critical Unknown true

The checker is similar to OVERFLOW_UNDER_CHECK, but overflow is occurred inside library function call.

Example

void func(int i, char* p) {

    char buf[100];

    if (i < 200) {
        strncpy(buf, p, i); // Error.
    }
}

In this example OVERFLOW_UNDER_CHECK.LIB will be fired instead of OVERFLOW_UNDER_CHECK.

OVERFLOW_UNDER_CHECK.LIB.MEMCPY

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

Special subtype of OVERFLOW_UNDER_CHECK.LIB, where overflow is occurred inside memcpy function call.

Example

void func(char* p, int len) {

    char buf[100];

    if (len > 101)
        return;

    memcpy(buf, p, len); // Error.
}

OVERFLOW_UNDER_CHECK.PROC

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Critical High true
GO Quality Critical High true

The checker is similar to OVERFLOW_UNDER_CHECK, but overflow is occurred inside a function, and check is performed before a function call.

Example

void access(int index) {
    int array[100];
    array[index] = 0;
}

void func(int index) {
    if (index < 200)
        access(index); // Error.
}

BUFFER_SIZE_MISMATCH

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

The checker BUFFER_SIZE_MISMATCH finds situations where a size parameter passed to “safe” versions of standard functions (such as strncpy or memset) is unsafe (out of local buffer bounds).

Example

char dst[10];
char src[11];

void test() {
    strncpy(dst, src, sizeof(src));
}

The last parameter for strncpy() should’ve been sizeof(dst) - 1.

BUFFER_SIZE_MISMATCH.NONTERMINATED

Language Situation Severity Reliability Enabled
CXX Quality Critical Unknown true

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
}

BUFFER_SIZE_MISMATCH.STRICT

Language Situation Severity Reliability Enabled
CXX Quality Normal Average true

The checker BUFFER_SIZE_MISMATCH.STRICT finds situations where a size parameter passed to “safe” versions of standard functions (such as memset_s, sprintf_s) is not constant and may be out of bounds.

Example

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));
}

DYNAMIC_SIZE_MISMATCH.STRICT

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true
CXX Quality Normal Unknown true

The checker DYNAMIC_SIZE_MISMATCH.STRICT finds situations where a size parameter passed to “safe” versions of standard functions (such as memset_s, sprintf_s) is not constant and may be out of bounds. Unlike BUFFER_SIZE_MISMATCH.STRICT it emits warnings for buffer from heap.

Example

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));
}

DYNAMIC_SIZE_MISMATCH

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true

The checker DYNAMIC_SIZE_MISMATCH finds situations where a size parameter passed to “safe” versions of standard functions (such as strncpy or memcpy) is unsafe (out of dynamic buffer bounds).

Example

char *dst;
char src[11];

void test() {
    dst = (char *)malloc(10);
    strncpy(dst, src, sizeof(src));
}

The last parameter for strncpy() should’ve been sizeof(dst) - 1.

ALLOC_SIZE_MISMATCH

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

This checker reports warnings for instances of pointer assigned memory allocations where the pointer’s target type is larger than the block allocated.

Example

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.

ALLOC_SIZE_MISMATCH.NEW

Language Situation Severity Reliability Enabled
CXX Quality Critical Average false

This checker reports warnings for C++ allocations using operator new when direct initialization is used incorrectly instead of the dynamic array size specification.

Example

int *a = new int(10); // Should be `new int[10]`.

This allocates memory for a single integer and initializes it with value 10 instead of allocating memory for 10 integer numbers.

MEMSET_SIZE_MISMATCH

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

This checker reports the same issue as ALLOC_SIZE_MISMATCH but for memset and similar functions instead of allocations.

Language Situation Severity Reliability Enabled
CXX Quality Major VeryHigh true

This checker finds situations where function readlink (from libc) is used incorrectly. Function readlink returns -1 on error, or the number of bytes written in the buffer, but doesn’t write terminating NULL, so that it may return value equal to buffer size.

Example

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.
    }
}

NONTERMINATED_STRING

Language Situation Severity Reliability Enabled
CXX Quality Critical High true

The checker NONTERMINATED_STRING finds situations where “safe” versions of standard functions working with C strings enable creation of nonterminated strings as a result.

Example

char dst[10];
char src[15];

void cp_str() {
    strncpy(dst, src, sizeof(dst));
}

Even though buffer overflow won’t occur directly in this call, it may create a non-terminated string, which may lead to buffer overflow later, when the string is accessed again. For example, if src is a 10-character string, the call will copy all 10 characters in dst, but there is no space left for a null terminator.

See also

TAINTED.NONTERMINATED_STRING

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

This checker finds situations where “safe” versions of standard functions working with C strings enable creation of non-null-terminated strings as a result of using length parameter coming from untrusted source.

char dst[10];

void cp_str() {
    char* src = getenv("qqq");
    strncpy(dst, src, sizeof(dst));
}

NONTERMINATED_STRING.STRICT

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown false

This checker is similar to NONTERMINATED_STRING, but doesn’t assume that destination memory is zero-filled. It is reported when copying a smaller string into a bigger buffer without adding a null terminator. This can lead to unintended addition of a suffix to the copied string or nonterminated string if the destination buffer wasn’t null-terminated.

NONTERMINATED_STRING.STYLE

Language Situation Severity Reliability Enabled
CXX Quality Critical High true

The checker finds patterns with standard C function “memcpy” gets result of function “strlen” for 2nd parameter as size parameter. By using such pattern null terminator of 2nd parameter is not copied which may lead to creation not null terminated string.

Example

void copy_impl(char*dst, char*src) {
    size_t len = strlen(src);
    memcpy(dst, src, len); //dst may became NNTS.
}

Checker reacts on this pattern. For first parameter only simple checkers are run. If detector is not sure that operation is safe then the warning is emitted.

Checker can fire warnings for other functions like “memcpy_s”. Information about parameters is spread inter procedurally. That is why the checker will find an error if severl function-“wrappers” are used:

Example

extern char* global;

void wrapper(char*src, int len) {
    memcpy(global, src, len);
}

void test(char*a) {
    int n = strlen(a);
    wrapper(a, n);//The warning will be emitted here.
}

STRING_OVERFLOW

Language Situation Severity Reliability Enabled
CXX Quality Minor VeryHigh true

This checker finds situations where a call to a string copying function can lead to a buffer overflow if the source string is larger than the destination buffer.

Example

char buf[100];

void test1(char* param) {
    strcpy(buf, param);
}

OVERFLOW_UNDER_CHECK.LEN

Language Situation Severity Reliability Enabled
CXX Quality Critical Average false

The checker finds situations of potential overflow where string length is used to check possible size of buffer.

Example

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).
    }
}

Memory management

FREE_NONHEAP_MEMORY

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

This checker finds situation where a pointer to non-heap memory could be passed to a memory deallocation function.

Example

int buf[5];
int* ptr;

void test(int cond) {
    if (cond)
        ptr = (int*) malloc(10);
    else
        ptr = buf;
    // ...
    free(ptr); // `ptr` could reference non-heap array `buf`.
}

FREE_NONHEAP_MEMORY.EX

Language Situation Severity Reliability Enabled
CXX Quality Major VeryLow true

This path-sensitive version of checker FREE_NONHEAP_MEMORY finds situation in which there is an reachable path passing through the pointer to non-heap memory could be passed to a memory deallocation function.

Example

void free_if(int*p, int k) {
        if(k)
                my_free(p);
}

void test_func(int z) {
    int arr[] = {2, 3};
    free_if(arr, z);// in this line warning will be emitted
}

FREE_NONHEAP_MEMORY.MACRO

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This subtype of checker FREE_NONHEAP_MEMORY finds situation, where problematic code is contained in macro

Example

#define ALLOC(x, Type) \
        Type x[] = {1, 2, 3};

#define FREE(x) \
        my_free(x);

void foo1() {
        int arr[] = {1, 2, 3};
        FREE(arr);
}

void foo2() {
        ALLOC(arr, int);
        FREE(arr);
}

In both usages of FREE macro analyser will emit a warning

FREE_OF_ARITHM

Language Situation Severity Reliability Enabled
CXX Quality Minor VeryLow false

This checker finds situation where a pointer passed to a memory deallocation function was obtained as a result of an arithmetic operation. Though sometimes the correct address of the beginning of a memory allocation block could be reconstructed using arithmetic operations, this is potentially a serious problem.

Example

void foo(int* ptr) {
    int* start = ptr-30;
    free(start);
}

BAD_ALLOC_ARITHMETIC

Language Situation Severity Reliability Enabled
CXX Quality Critical Unknown true

It emits warnings for arithmetic operation on the results of heap allocation functions. This might be a common typo when a closing brace is mistakenly placed before a part of the arithmetic expression for allocation size calculation.

Example

char *mystrcpy(char *str) {
    char *ret = malloc(strlen(str)) + 1; // Typo.
    for (; *str;) *ret++ = *str++;
    char *copy = ret;
    *copy = 0;
    return ret;
}

Suggested code

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;
}

FREE_OF_NULL

Language Situation Severity Reliability Enabled
CXX Quality Minor Average false

This checker finds situations where a NULL pointer is passed to the library function free(). While it is totally okay, the code is useless as in this case no operation is performed, and it might mean a problem with the program logic.

Example 1

void test(int z, char* x) {
    if (z == 7) {
        x = NULL;
        free(x);
    }
}

Example 2

void test(int x, char* z) {
    if (z == NULL) {
        x = 7;
        free(z);
    }
}

See also

DEREF_AFTER_FREE

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

This checker finds situations where a memory location is accessed through a pointer that has just been deallocated.

Example

void test(char* pval, char x) {
    free(pval);
    x = *pval;
}

See also

USE_AFTER_FREE

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

This checker finds situations where the value of a pointer to a memory location that has been deallocated, is accessed.

Example

void after_free(char* pval, char* pval2) {
    free(pval);
    pval2 = pval + 2;
}

Example

char* foo(char* x) {
    free(x);
    return x;
}

See also

USE_AFTER_FREE.REALLOC

Language Situation Severity Reliability Enabled
CXX Quality Critical Low true

Subtype of USE_AFTER_FREE for situations where a pointer is passed to the realloc function that invalidates it but the original pointer value is used afterwards.

Example

void boo() {
    int *newp = (int *) realloc(p, SIZE); // `p` is released.
    p[0] = 1; // Use of `p`.
    free(newp);
}

PASSED_TO_PROC_AFTER_FREE.EX

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

This checker finds situations where a pointer referencing deallocated memory is passed to a function.

Example

void foo(char* p);

void run(char* p) {
    free(p);
    foo(p);
}

Here, pointer p passed to a call of foo references memory deallocated by function free. One way to fix this defect is to set the deallocated pointer to NULL:

free(p);
p = NULL;
foo(p); // No warning.

Example

struct proc {
    char* mem;
    int a;
};

void foo(struct proc* p);

void run(struct proc* p) {
    free(p->mem);
    foo(p); // `p->mem` is referenced by p.
}

See also

DOUBLE_FREE.EX

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

This checker finds situations where a pointer referencing deallocated memory is deallocated again.

Example

char* foo(char* x) {
    free(x);
    free(x);
}

DANGLING_POINTER.STRICT

Language Situation Severity Reliability Enabled
CXX Quality Minor High false

This checker is designed to detect dangling pointers and resources, i.e. such pointers and resources that can be available from the caller context after they have been freed or released inside the callee.

Example

int naive_pop(struct list *l)
{
    if (l == NULL) {
        return 0;
    }
    int *p = l->ptr;
    int ret = *p;
    free(p); // No removing of the freed pointer from the list, it remains there as a
             // dangling pointer that could crash the program on its dereference.
    return ret;
}

Example

typedef struct _St {
    char *p1;
    char *p2;
} St;

void ex(St *s) {
    if (cond1()) {
        free(s->p1);
        s->p1 = NULL; // Correct: the deallocated pointer is cleared.
        return;
    }

    if (cond2()) {
        free(s->p1);  // Potential defect: the deallocated pointer may be used
                      // outside of the function.
        return;
    }
}

DANGLING_POINTER.STAT

Language Situation Severity Reliability Enabled
CXX Quality Normal High true

Statistical version of DANGLING_POINTER. Svace emits this subtype when for some cases externally accessible memory pointing to it is reassigned and for others it isn’t.

INCORRECT_STRLEN

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown true

This checker detects possible mismatch when using function strlen to determine the size of a newly allocated buffer based on the length of an existing string.

Example

char* alloc_same_plus_1(char* str) {
    char* res = (char*) malloc(strlen(str+1)); // Svace emits INCORRECT_STRLEN.
    res[0] = '\0';
    return res;
}

strlen(str + 1) return value that is less than strlen(str). Programmer probably wanted to write (strlen(str) + 1) to allocate 1 byte more than the length of str; also, str + 1 skips NULL terminator if str was empty, leading to undefined behavior.

MEMORY_LEAK

Language Situation Severity Reliability Enabled
CXX Quality Major High true

This checker detects memory leak situations, where memory was allocated, and then all references to that memory were lost.

Example

void mem_leak() {
    char* ptr1 = (char*)malloc(10);
    ptr1 = 0; // Memory is leaked here.
}

MEMORY_LEAK.STRDUP

Language Situation Severity Reliability Enabled
CXX Quality Normal VeryHigh true

It is an subtype of MEMORY_LEAK for cases where memory is allocated by using function for C library strdup. It is separated to subtype because in many cases function strdup is used for allocating small amount of memory and such errors are less critical.

Example

void mem_leak() {
    char* str = strdup("hello");
    str = "qqq"; // Memory allocated by function strdup is leaked here.
}

MEMORY_LEAK.STRUCT

Language Situation Severity Reliability Enabled
CXX Quality Normal High true

It is subtype of MEMORY_LEAK. The warnings are emitted by the same checker as for MEMORY_LEAK but then it is suppressed to subtype if pointer is related to C structures. Precise modeling of code with complex structures is harder for static analysis. Such warning usually hatrue positive rates.

Example

struct S1 {
    int a;
    int* array;
};

void init1(struct S1* s1) {
    s1->array = malloc(10);
}

void test1() {
    struct S1 s1;
    init1(&s1);//MEMORY_LEAK.STRUCT
}

MEMORY_LEAK.STRDUP.STRUCT

Language Situation Severity Reliability Enabled
CXX Quality Normal Low true

It is subtype of MEMORY_LEAK for cases when both filters for .STRUCT and .STRDUP work. This types describes situations where memory is allocated by function strdup and then it is assigned to structure field.

Example

typedef struct {
  char* s;
} str_t;

typedef struct {
  str_t* data;
} data_t;

str_t* f(data_t *p) {
  if (!p) return 0;

  return p->data;
}

void example(str_t *src) {
  void* local;
  str_t *dest = f(local);

  if (dest)
    dest->s = strdup("hello");//MEMORY_LEAK.STRDUP.STRUCT
}

MEMORY_LEAK.EXCEPTION

Language Situation Severity Reliability Enabled
CXX Quality Normal Average true

Exception handling is very convenient feature that usually improves code readability. But in some cases exceptions may introduce another errors. This checker by using MEMORY_LEAK-engine finds situations where memory is leaked due to incorrect exception handling.

Example

class Exc {}

void may_throw(int x) {
    if (x % 3 == 0) {
        throw Exc();
    }
}


int incorrect_exception_handling(int x) {
    int *ptr = 0;
    try {
        ptr = new int; //memory allocation

        may_throw(x);

        delete ptr;//memory deallocation
    } catch (...) {
        return -1;
    }

    return 0;
}

In the example above memory for ptr won’t be deallocated if function may_throw throw an exception. For C++ we recommend to use RAII-idiom or smart-pointers.

MEMORY_LEAK.EX

Language Situation Severity Reliability Enabled
CXX Quality Major Low true

It is another checker (as MEMORY_LEAK) for detecting situations where memory was allocated, and then all references to that memory were lost. Te difference is that this checker uses formulas for describing when memory was allocated. The checker can detect errors in complicated situations where leak occurred only on some path.

Example

void example(int a, int b, int c) {
    char*p = 0;

    if(a>b+10)
        p = malloc(10);//leak if c != 10

    if(c==10 && a>b+c)
        free(p);
}

In the example above in case if variable c is not equal to 10 memory will be allocated and won’t be released. For next code no warning will be emitted.

Example

void contr_example(int a, int b, int c) {
    char*p = 0;

    if(c==10 && a>b+10)
        p = malloc(10);//will be released

    if(c==10 && a>b+c)
        free(p);
}

Here memory is released for all paths where it was allocated.

MEMORY_LEAK.EX.EXCEPTION

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown true

It is subtype of MEMORY_LEAK.EX for situtations where a leak is occurred on exception path. It is similar to MEMORY_LEAK.EXCEPTION but this checker uses MEMORY_LEAK.EX-engine for leak detection.

Example

class Exc {};

int x;

void may_throw() {
    if (x++ % 7 == 0) {
        throw Exc();
    }
}

int example(bool flag) {
    int *ptr = 0;
    try {
        ptr = flag? nullptr : new int; //allocation

        may_throw(); //potential exception

        if (!flag)
            delete ptr;//deallocation
    } catch (...) {
        return -1;
    }

    return 0;
}

BUFFER_OVERLAP

Language Situation Severity Reliability Enabled
CXX Quality Critical Low true

This checker reports cases of using of the same source and destination buffers in function calls where it is prohibited such as memcpy.

HANDLE_LEAK

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true
GO Quality Major High true
KOTLIN Quality Major High true
CSHARP Quality Major High true

This checker finds situations where a file descriptor, file handle or a socket descriptor are lost, because local variables that held their value went out of scope or were re-assigned.

Example (C/C++)

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 descriptor 1, the next allocated descriptor will have value 1. In such cases, even if the returned value is not recorded, the predicted value can still be used to deallocate resources. For these situations, Svace emits warnings of subtype HANDLE_LEAK.STRICT.

Example (Java)

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 example method. If exception goes out of the method scope, HANDLE_LEAK.EXCEPTION will be emitted.

Example (Kotlin)

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.

HANDLE_LEAK.EXCEPTION

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true
CXX Quality Normal Unknown true
KOTLIN Quality Major Unknown true
CSHARP Quality Normal Unknown true

This checker finds situations where a file descriptor, file handle or a socket descriptor are lost because of exception, which went out of the function scope.

Example (Java)

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.

Example (Kotlin)

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.

HANDLE_LEAK.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true
GO Quality Major High true
KOTLIN Quality Major High true

Path sensitive version of HANDLE_LEAK.

Example (Java)

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.

Example (Kotlin)

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.

HANDLE_LEAK.EX.EXCEPTION

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true
CXX Quality Normal Unknown true
KOTLIN Quality Major Unknown true

Path sensitive version of HANDLE_LEAK.EXCEPTION.

Example (Java)

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.

Example (Kotlin)

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.

HANDLE_LEAK.FRUGAL

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown false
KOTLIN Quality Normal Unknown false
CSHARP Quality Normal Unknown true

This checker is a subtype of HANDLE_LEAK and it finds situations where a database is opened via android.database.sqlite.SQLiteOpenHelper.getReadableDatabase() and android.database.sqlite.SQLiteOpenHelper.getWritableDatabase() but hasn’t been properly closed.

The detector is under development at the moment.

DOUBLE_CLOSE

Language Situation Severity Reliability Enabled
JAVA Quality Normal Average true
CXX Quality Normal Average true
GO Quality Normal Average true

This checker finds situations where a closed file descriptor is closed again.

Example

void foo() {
    FILE *f = fopen("bar", "w");
    // ...
    fclose(f);
    // ...
    if (error()) {
        fclose(f);
    }
}

Example

void dup_to_2(int fd) {
    close(2); // Now 2 is available.
    dup(fd); // 2 will be used.
}

DOUBLE_CLOSE.PROC

Language Situation Severity Reliability Enabled
JAVA Quality Normal Average true
CXX Quality Normal Average true
GO Quality Normal Average true

This checker finds situations where a closed file descriptor is closed again, and the closure happens in procedures.

Example

int my_close(int p) {
        if (cond()) {
                close(p);
                return -1;
        }
        return 0;
}

void test(int fd) {
        my_close(fd);
        close(fd);
}

USE_AFTER_RELEASE

Language Situation Severity Reliability Enabled
JAVA Quality Normal High true
CXX Quality Normal High true
GO Quality Normal High true
KOTLIN Quality Normal High true

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.

Example (Kotlin)

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.

PASSED_TO_PROC_AFTER_RELEASE

Language Situation Severity Reliability Enabled
JAVA Quality Minor Average true
CXX Quality Minor Average true

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.

Example

#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.

BAD_FREE.MS_COM

Language Situation Severity Reliability Enabled
CXX Quality Critical Unknown true

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;
}

Infinite loop

This checker find situations where number of loop iteration may be infinite because of integer overflow.

INFINITE_LOOP.INT_OVERFLOW

Language Situation Severity Reliability Enabled
JAVA Quality Major Low false
CXX Quality Major Low false
GO Quality Major Low false
CSHARP Quality Major Low true

Example 1

void example1() {
    unsigned char i;

    for (i = 0; i <= 250; i += 10) { // Possible integer overflow, after `i = 250`.
        bar();
    }
}

Example 2

void example2(unsigned len) {
    int i = 0;

    while (len > 0) {
        len -= 8; // Overflow if `len % 9 != 0`.
    }
}

INFINITE_LOOP.STRING

Language Situation Severity Reliability Enabled
JAVA Quality Major VeryLow false
CXX Quality Major VeryLow false
GO Quality Major VeryLow false

This checker finds situations where number of loop iteration may be more than it was intended. The loop depends on value of string.

Example

void func(char * pos) {
    while (*pos != ' ') {
        pos++;
    }
}

INFINITE_LOOP.INT_OVERFLOW.ARRAY

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown false
CXX Quality Major Unknown false
GO Quality Major Unknown false

Checker INFINITE_LOOP.INT_OVERFLOW.ARRAY find situations with integer overflow that may lead to infinite loop execution and related to buffer access.

Example

void with_tainted() {
    unsigned char x = 0;
    char* buf = getenv("HOME");

    while (buf[x] == ' ') {
        ++x; //svace: emitted INFINITE_LOOP.INT_OVERFLOW.ARRAY
    }
}

Here if first 256 bytes of buffer buf do not contain space the variable x will overflow which lead to infinite loop.

The checker emits warnings only for tainted buffers (from external sources). For other buffer subtype INFINITE_LOOP.INT_OVERFLOW.ARRAY.STRICT is emitted.

Example

char buf[1000];

void foo() {
unsigned char x = 0;

    while (buf[x] == ' ') {
        ++x;
    }
}

BUFFER_OVERFLOW.LOOP

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown false
CXX Quality Critical Unknown false
GO Quality Critical Unknown false

Checker finds suspicious situations where loop execution is bounded only by array data.

Example:

char buf[10];

void example() {
    char x = 0;

    while (buf[x] == ' ') {
        ++x; // BUFFER_OVERFLOW.LOOP
    }
}

Integer overflow

INTEGER_OVERFLOW

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This checker finds arithmetic operations with integer overflows when the result of that arithmetic operation is too big or too small to be represented as a value of the operation’s result type.

Example

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);
}

NO_CAST.INTEGER_OVERFLOW

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true
CSHARP Quality Major Unknown true

This checker finds situations where the value of an arithmetic expression might overflow before the result is widened to a larger data type.

Example

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:

long mult(int x, int y) {
    return ((long)x * (long)y);
}

TAINTED.INT_OVERFLOW

Language Situation Severity Reliability Enabled
JAVA Quality Major Average false
CXX Quality Major Average false
KOTLIN Quality Major Average true

The checker finds situations where value from external source is used in arithmetic operation without checking its range. It potentially may lead to integer overflow.

Example (C/C++)

int add_to_str(const char *str) {
    int index = atoi(str);
    int res = index + 500; // Potential integer overflow.
    return res;
}

Example (Kotlin)

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 safe toIntOrNull instead toInt function.

TAINTED.INT_OVERFLOW.TRUNC

Language Situation Severity Reliability Enabled
JAVA Quality Normal Average false
CXX Quality Normal Average false
GO Quality Normal Average false

The checker finds situations where value from external source is passed to variable with smaller type size.

Example

unsigned i;
scanf("%3x", &i);
unsigned short h = i; // Potential loss of higher bits.

INT_OVERFLOW.TRUNC.UNDER_BITMASK

Language Situation Severity Reliability Enabled
JAVA Quality Normal Average false
CXX Quality Normal Average false
GO Quality Normal Average false

The checker finds situations where potential integer overflow is possible for result of bit-manipulation operations.

Example

void foo(unsigned short a);

#define INVERT_BYTES_16(X)  ( \
            ( ((X) & 0xFF00) >> 8 ) \
        |   ( ((X) & 0x00FF) << 8 ) \
    )

void bar(unsigned short b) {
    foo(INVERT_BYTES_16(b) + 1);
}

INT_OVERFLOW.LIB

Language Situation Severity Reliability Enabled
JAVA Quality Normal Average false
CXX Quality Normal Average false
GO Quality Normal Average false

The checker detects cases where the subtraction is performed on an unsigned value which can be 0 (or small enough), and, therefore the subtraction result may underflow, and it is passed to a function as its sensitive unsigned integer data argument.

Example

void example(char* dst, char* str) {
    size_t len = strlen(str);
    memcpy(dst, str, len - 1);
}

INT_OVERFLOW.BIG

This detector finds situations where an integer variable is compared with some constant value and the result of arithmetic operations on this integer variable may overflow. The comparison identify the bounds of the possible value of this variable.

Example (Kotlin)

fun example(n: Int) = if (n > 100000) n * 1000000 / 8 else 0

fun possibleFix(n: Int) = if (n > 100000) (n as Long) * 1000000 / 8 else 0

Function example illustrates the defect: n is compared with 10^5 and after that n is multiplied by 10^6. The result of the multiplication is greater than the maximum value an instance of Int can have.

Function possibleFix illustrates a possible fix: cast n to Long before multiplication.

Security errors

SENSITIVE_LEAK

Language Situation Severity Reliability Enabled
JAVA Quality Undefined Low false
CXX Quality Undefined Low false
GO Quality Undefined Low false

The checker looks for situations where sensitive data may occur in logs or be visible.

Example

char *password = getpass();
printf(password);

For the example above specification for getpass must be added:

char *getpass() {
    char *ret;
    sf_overwrite(&ret);
    sf_password_set(ret);
    return ret;
}

The checker may detect sensitive data by name of used variables (passwd, pwd, privkey, etc.).

Example

char *pwd = "123";
log(pwd); // Leak.

Option SENSITIVE_NAME_REGEX allows changing regex for name.

HARDCODED_PASSWORD

Language Situation Severity Reliability Enabled
JAVA Quality Undefined Low false
CXX Quality Undefined Low false
GO Quality Undefined Low false

Checker finds situations where hardcoded password is passed to functions manipulating with passwords.

Example

var pass = []byte("0123456789abcdef0123456789abcdef")

func main() {
    block,_ := aes.NewCipher(pass) // Using of hardcoded password.
}

COMMAND_INJECTION

Language Situation Severity Reliability Enabled
CXX Security Critical Unknown false
CSHARP Security Critical Unknown true

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.

Example

const char *command = "some_command";
system(command);

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);

Concurrency

DEADLOCK

Language Situation Severity Reliability Enabled
JAVA Quality Critical Unknown false
CXX Quality Critical Unknown false
KOTLIN Quality Critical Unknown true
CSHARP Quality Critical Unknown false

This checker finds code that may result in two or more threads are waiting for each other, holding locks needed for the other to resume execution.

Example (C/C++)

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);
    // ...
}

Example (Java)

class DeadlockTest {
    private Object lock;

    public synchronized void direct() {
        synchronized(lock) {
            // ...
        }
    }

    public void reverse() {
        synchronized(lock) {
            synchronized(this) {
                // ...
            }
        }
    }
}

Example (Kotlin)

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 thread working with the same instance of DeadlockExample class, and one thread executing direct function acquires this lock and tries to acquire l lock, while another thread executing reverse function acquires l lock and tries to acquire this lock, then both threads will wait indefinitely for one of them to release the lock.

Possible fix: use reverseCorrect function instead of reverse.

DOUBLE_LOCK

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true

The checker DOUBLE_LOCK finds situations where a lock is acquired twice in succession by the same thread (without getting released).

Example

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);
}

NO_UNLOCK

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true
GO Quality Major High true

This checker finds situations where a thread acquires a lock during function execution and leaves it locked on some paths to the exit from the function, but the function also unlocks it on some other paths.

Example

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);
}

NO_UNLOCK.CTOR

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This checker finds situations where a lock is acquired in a class constructor and not released in its destructor.

Example

#include <pthread.h>

class LockGuardBad {
    pthread_mutex_t m;

  public:
    LockGuardBad() { pthread_mutex_lock(&m); } // report NO_UNLOCK.CTOR

    ~LockGuardBad() {}
};

WRONG_LOCK_OBJECT

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true

This checker finds situations where pooled or reusable objects are used for synchronization. Such objects may be locked by outside code.

String literal example

All three methods (localStringLockTest, fieldStringLockTest and internStringTest) use the only 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 example

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 primitive example

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 field example

Public non-final fields are accessible to 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) {
            // ...
        }
    }
}

LOCK_ON_STACK

Language Situation Severity Reliability Enabled
CXX Quality Major High true

This checker finds situations where a local variable allocated on stack is used as a synchronization primitive. Since each thread has its own stack such locks can’t be used for inter-thread synchronization.

Simple example

void foo() {
    pthread_mutex_t m;

    pthread_mutex_lock(&m); // Locking stack variable.
}

More complex example

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.
}

LONG_TIME_IN_LOCK

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

This checker finds situations where a blocking function is called inside a critical section. As a result, all threads have to wait for the blocking function to return, not just the thread that called it.

Example

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;
}

ATOMICITY

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This checker finds cases of non-atomic usage of non-constant shared data where critical section is not sufficient to protect a variable.

NO_LOCK.STAT

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown false
CXX Quality Major Unknown false
KOTLIN Quality Major Unknown false
CSHARP Quality Major Unknown true

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.

NO_LOCK.STAT.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
KOTLIN Quality Major Unknown true

This detector collects statistics for each access to the field. Statistics store whether access to the field was under lock and under what lock in particular. By handling the statistics the detector decides if some accesses to field may cause race condition. Constructors and equals, hashCode and clone methods are excluded from consideration.

The warning will be emitted by this detector if:

NO_LOCK.STAT.EX.THRESHOLD is equal to 80% by default. NO_LOCK.STAT.EX.TWO_OF_THREE is false by default.

Note that NO_LOCK.STAT.EX.TWO_OF_THREE is true for all examples below.

Example (Java)

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 race condition.

Possible fix: use barCorrect function instead of bar, field a is accessed under l lock in barCorrect function.

Example (Kotlin)

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.

NO_LOCK.GUARD

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true

This detector issues a warning if a field is declared with “GuardedBy” annotation but later accessed without locks (such as “synchronized”).

Example (Java)

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
    }
}

NO_CHECK_IN_LOCK

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true

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.

Example (Java)

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);
        }
    }
}

LOCK_INCONSISTENT

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true

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.

Example (Java)

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.

BAD_WAIT_OF_COND

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true

This 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. 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();
        }
    }
}

RACE.NO_UMASK

Language Situation Severity Reliability Enabled
CXX Quality Minor VeryHigh false

Warning of this type is emitted where a call to function mkstemp is not preceded by a call to umask, which is necessary to restrict access rights to the newly created file to its creator.

For most libc implementation function mkstemp creates file with correct permissions and this warning is not needed.

See also

RACE.BAD_UMASK

Language Situation Severity Reliability Enabled
CXX Quality Minor VeryHigh false

Even if function umask was called before mkstemp, as checked by RACE.NO_UMASK, it’s possible that access rights set by umask are too permissive (for example, 777). This warning is emitted if that is the case.

For most libc implementation function mkstemp creates file with correct permissions and this warning is not needed.

See also

Signal handlers

SIGHANDLER.ASYNC_UNSAFE

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown false

This checker finds possible cases of using inconsistent data while executing asynchronous-unsafe functions from signal handlers. According to Section 7.14.1.1 of the C Rationale [ISO/IEC 2003]:

When a signal occurs, the normal flow of control of a program is interrupted. If a signal occurs that is being trapped by a signal handler, that handler is invoked. When it is finished, execution continues at the point at which the signal occurred. This arrangement can cause problems if the signal handler invokes a library function that was being executed at the time of the signal.

The warning is emitted if signal handler calls any asynchronous-unsafe function. It is based on the SIG30-C rule from the CERT Secure Coding Standard.

Example

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;
}

SIGHANDLER.SELF

Language Situation Severity Reliability Enabled
CXX Quality Minor Unknown false

This checker finds a rare case of race condition where a handler tries to reinstall a signal handler from itself. On systems that uninstalls signal handler after signal delivery (SysV and Windows behavior) another signal can happen before the reinstallation code is run. This will lead to default signal handler call. On systems where signal handlers need to be explicitly uninstalled (BSD and Linux behavior), reinstallation of same signal handler doesn’t make sense.

The warning is emitted if a signal handler calls function signal for re-registering same handler again. It is based on the SIG34-C rule from the CERT Secure Coding Standard.

Example

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;
}

SIGHANDLER.ACCESS_SHARED

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown false

This checker detects race conditions that can be caused by accessing or modifying shared objects in signal handlers. The only way to guarantee that data is read in consistent state and remains in consistent state after modification is to read and write only variables of type volatile sig_atomic_t inside of signal handlers.

The warning is emitted if signal handler accesses or modifies global data with the type other than volatile sig_atomic_t. It is based on the SIG31-C rule from the CERT Secure Coding Standard.

Example

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;
}

SIGHANDLER.LONGJUMP

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown false

This checker finds situations where a longjmp function is called from a signal handler, which may cause inconsistent data use. Such calls can lead to problems similar to those discussed in SIGHANDLER.ASYNC_UNSAFE.

The warning is emitted if the signal handler function calls the longjump function. It is based on a SIG32-C rule from the CERT Secure Coding Standard.

Example

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;
}

SIGHANDLER.REC_RAISE

Language Situation Severity Reliability Enabled
CXX Quality Minor Unknown false

This checker finds situations where undefined behavior may result from calling function raise from a signal handler. According to C99, Section 7.14.1.1 [ISO/IEC 9899:1999]: If the signal occurs as the result of calling the abort or raise function, the signal handler shall not call the raise function.

The warning is emitted if function raise is called from a signal handler, but only if a signal with that handler is explicitly raised using functions raise or abort somewhere in the program. It is based on the SIG33-C rule from the CERT Secure Coding Standard.

Example

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;
}

SIGHANDLER.NO_ABORT

Language Situation Severity Reliability Enabled
CXX Quality Minor Unknown false

This checker detects cases of undefined behavior caused by returning control from certain signal handlers that should instead terminate the program. According to Section 7.14.1.1 of the C standard, returning from SIGSEGV, SIGILL, or SIGFPE signal handlers leads to undefined behavior: If and when the function returns, if the value of sig is SIGFPE, SIGILL, SIGSEGV, or any other implementation-defined value corresponding to a computational exception, the behavior is undefined; otherwise, the program will resume execution at the point it was interrupted.

The warning is emitted if signal handler for SIGFPE, SIGILL or SIGSEGV can return without calling the abort function. It is based on the SIG35-C rule from the CERT Secure Coding Standard.

Example

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;
}

Library specific

Those warnings are specific for libraries.

LIB.BAD_LOAD_PATH

Language Situation Severity Reliability Enabled
CXX Quality Undefined Unknown false

This checker finds 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.

Example

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");
    // ...
}

Notes

Invocation of loadlib is an indirect call of dlopen.

Function foo illustrates the defect: the first argument of loadlib starts with /usr/lib.

C++ warning types

Those warnings are developed special for C++ code.

UNINIT.CTOR

Language Situation Severity Reliability Enabled
CXX Quality Major High true

This checker finds constructors (incl. copy constructor) that fail to initialize some of their class’s fields.

Example

class MyData {
public:
    MyData();
    int kind;
    char ch;
    int sum;
};

MyData::MyData() : ch('q') {
    this->kind = 7; // Warning: field `sum` is not initialized.
}

Notes

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.

UNINIT_HEAP.CCTOR

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This checker finds classes in which the destructor explicitly deallocates memory using a pointer, but the copy constructor doesn’t initialize this pointer or doesn’t exist. Not handling such pointer in copy constructors may lead to either deallocation via an uninitialized pointer, or to double deallocation of the same memory.

Example

class MyData {
    char* str;
    int size;
public:
    MyData() {
        str = (char*) malloc(10);
        size = 10;
    }

    MyData(const MyData& a) {
        size = a.size; // `str` is not handled.
    }

    ~MyData() {
        free(str); // Deallocate memory pointed to by `str`.
    }
};

UNINIT_HEAP.ASSIGN_OP

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This checker finds classes where the destructor deallocates memory using a pointer, but the assignment operator doesn’t initialize this pointer or doesn’t exist. This checker is similar to UNINIT_HEAP.CCTOR, but checks assignment operators instead of copy constructors.

METHOD_CALL_BEFORE_BASE_INIT

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

Check for undefined behavior related to the order of initialization of base classes such as calling a member function when base is not initialized yet, dynamic_cast of this when base is not initialized yet and calling of typeid() with this as argument when base is not initialized yet.

Example

In the example below f() is a member function of class B. Constructor of class B has base initializer A(int a) and uses return value of method f() as an argument. The call f() would be evaluated before A initialization is complete which is undefined behavior.

class A {
  public:
    A(int a);
};

class B : public A {
  public:
    int f();
    B() : A(f()) {}
};

HEAP_INCOMPATIBLE.FREE

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

Warnings of this type are emitted for situations where memory is deallocated in a way that is incompatible with how it was allocated. For example, usage of C functions malloc/free must not be mixed with C++ operators new/delete.

char* buf = (char*) malloc(strlen(str) + 12);
//...
delete buf; // HEAP_INCOMPATIBLE.FREE is emitted here. The bug can be fixed
            // by using function `free` for deallocation instead of `delete`.

See also

HEAP_INCOMPATIBLE.ARRAY

Language Situation Severity Reliability Enabled
CXX Quality Major VeryHigh true

A subtype of HEAP_INCOMPATIBLE.FREE. Detects situations where memory was allocated using array allocation operator new[], but deallocated using operator delete (instead of the correct delete[]). Using incorrect deallocation method can lead to undefined behavior.

char* buf = new char[SIZE_MAX];
// ...
delete buf; // HEAP_INCOMPATIBLE.ARRAY warning is emitted here. The bug can be
            // fixed by using `delete[]`.

See also

HEAP_INCOMPATIBLE.CTOR

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This checker finds classes with a constructor that allocates memory using a function (operator) that is incompatible with the function used by the destructor for deallocation. Examples of incompatible function pairs: new/free, malloc/delete, new[]/delete, new/delete[].

Example

class C {
    int* buf;
public:
    C() {
        buf = new int[10];
    }

    ~C() {
        free(buf); // Here svace fires the warning.
    }
};

See also

MEMORY_LEAK.CTOR

Language Situation Severity Reliability Enabled
CXX Quality Normal Average true

This checker finds classes in which constructor allocates some memory, but destructor doesn’t deallocate it.

Example

class C1 {
    int* buf;
    public:
        C1() { buf = new int[10]; }
        ~C1();
    };

    C1::~C1() { // Warning: missing `delete[]` for `buf`.
}

HANDLE_LEAK.CTOR

Language Situation Severity Reliability Enabled
CXX Quality Normal High true

This checker finds classes in which constructor allocates some resource (i.e. file), but destructor doesn’t free it.

Example

class C1 {
    FILE* f;
    public:
        C1() { f = fopen("filename", "r"); }
        ~C1();
    };

    C1::~C1() { // Warning: missing `fclose` for `f`.
}

DEAD_STRING_REF

Language Situation Severity Reliability Enabled
CXX Quality Normal High true

This warning is emitted when an internal string buffer (returned by c_str()) of an STL string escapes its scope.

Example

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.
}

ASSIGN_NO_CHECK_FOR_THIS

Language Situation Severity Reliability Enabled
CXX Quality Normal High true

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).

Example

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;
}

ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPE.MINOR

Language Situation Severity Reliability Enabled
CXX Quality Minor High false

This checker family find situations where the assignment operator defies syntactical correctness for equality in C++.

Present warning subtype is emitted when assignment operator is written in such way, that some logically correct constructions will lead to syntactical error (and therefore just can’t be used). The subtype is hidden by default.

Example 1

class MyData {
public:
    void operator=(const MyData& a);
private:
    int kind;
};

void MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPES.MINOR
    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.

Example 2

class MyData {
public:
    const MyData& operator=(const MyData& a);
private:
    int kind;
};

const MyData& MyData::operator=(const MyData& a) { // Emit ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPES.MINOR
    this->kind = a.kind;
    return *this; // Return type is const,
                  // meaning calling non-const methods like `(a = b).myMethod()` won't work.
}

ASSIGN_NO_REFERENCE_TO_THIS.INCOMPATIBLE_TYPE

Language Situation Severity Reliability Enabled
CXX Quality Minor High true

This checker family find 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 user.

Example 1

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_TYPES
    this->kind = a.kind;
    return this->kind; // Returns different type,
                       // meaning chaining equalities like `a = b = c` won't work.
}

Example 2

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_TYPES
    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)`.
}

ASSIGN_NO_REFERENCE_TO_THIS

Language Situation Severity Reliability Enabled
CXX Quality Minor High true

This checker family find 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 user.

Example 1

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.
}

Example 2

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.
}

Example 3

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`.
}

MISSING_COPY_CTOR_ASSIGN_OP

Language Situation Severity Reliability Enabled
CXX Quality Minor Unknown true

A class has dynamically allocated data members but does not define a copy constructor or an assignment operator. In this case, whenever an object is copied by value, dynamically allocated members will be destroyed as soon as at least one of the copies is deleted, and other copies would maintain and possibly dereference a stale pointer.

Example

In the code below, the following sequence of events leads to a dereference of a dead pointer:

class C {
    int *p;

public:
    C() { p = new int; }
    ~C() { delete p; }
    void setVal(int x) { *p = x; }
    int getVal() const { return *p; }
};

THROW_WHILE_COPY

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

Checks for exception throwing in copy constructor/assignment operator which can lead to resource leaks or undefined behavior.

Example

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;
}

BAD_ITERATOR.INVALID

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown true

This checker finds situations where STL iterators are used after being invalidated.

Example

using namespace std;
int last(vector<int>& v) {
    vector<int>::iterator it = v.begin();
    while (it != v.end())
        ++it;
    return (*it);
}

BAD_ITERATOR.MISMATCHED

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown true

This checker finds situations where STL iterators are used for a different container.

Example

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;
}

USE_AFTER_MOVE

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown true

The checker finds situations where an object is used after it was moved. Moved-from objects are usually left in unspecified state. Usage of such objects leads to unspecified behavior.

Example

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.

Example

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.

C# warning types

API.CSHARP.CULTURE

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The result of a function call may differ depending on the current culture. Specify culture explicitly to choose the desired behavior.

Example

float theirVersion = 0f;
if (parts.Length > 1)
    theirVersion = float.Parse(parts[1]);

Fix (use invariant culture)

float theirVersion = 0f;
if (parts.Length > 1)
    theirVersion = float.Parse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture);

CAST_AFTER_CHECK

Language Situation Severity Reliability Enabled
CSHARP Quality Major Unknown true

The variable was checked for compatibility with given type (which means that it may be incompatible with it by contract) and then cast to it without check.

Example 1

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);
}

Example 2

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"];

CODE_INJECTION

Language Situation Severity Reliability Enabled
CSHARP Security Critical Unknown true

User input is used as code (e.g. compiled or evaluated) or as path to code.

CONDITIONAL_ASSIGN

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

An assignment operator (=) is used inside of condition expression. It is probably a typo for equality operator (==).

Although using = inside of condition is not necessarily a bug, this warning is useful for immediate bug fixing during coding.

Example

if (a = b || a == c)
{
}

CONNSTR_INJECTION

Language Situation Severity Reliability Enabled
CSHARP Security Critical Unknown true

User input is concatenated to database connection string, which allows the attacker to override connection parameters. DbConnectionStringBuilder should be used to create the connection string.

CROSS_SITE_REDIRECT

Language Situation Severity Reliability Enabled
CSHARP Security Critical Unknown true

User input is used as the redirect URI in an HTTP response. It may be used to redirect the user to a malicious website.

Example

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;
    }
}

DEREF_AFTER_AS

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The result of as operator is dereferenced without check.

Example

var auctioneer = chr.Map.GetObject(auctioneerId) as NPC;
AuctionMgr.Instance.AuctionHello(chr, auctioneer);

DEREF_AFTER_CONDITIONAL_ACCESS

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The result of conditional access operator (?.) is dereferenced without check.

Example

Size2D windowSize = window?.Size;
Rectangle ret = new Rectangle(0, 0, windowSize.Width, windowSize.Height);

DUPLICATE_ENUM_MEMBER

Language Situation Severity Reliability Enabled
CSHARP Quality Minor Unknown true

Two enum members are equal (have the same numeric value).

Example

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,
    /* ... */
}

EMPTY_CATCH

Language Situation Severity Reliability Enabled
CSHARP CodingStyle Major High true

The catch clause is empty.

Although it’s not an error, it’s considered an antipattern.

EMPTY_INTERFACE

Language Situation Severity Reliability Enabled
CSHARP CodingStyle Minor High true

The interface is empty.

Although it’s not an error, it’s considered an antipattern.

FLOATING_COMPARE

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown false
CSHARP Quality Major Unknown true

Floating-point numbers are compared for precise equality.

Floating point mathematics is not exact, so instead of comparing floating-point values for precise equality, they should be compared with precision. E.g. Math.Abs(a - b) < 0.0001 instead of a == b.

Example

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 warning has .CMP_EQ subtype if the code uses <= or >= operator.

HIDDEN_MEMBER

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Local variable or method parameter name “hides” other variable or class member with the same name.

Although this is not a bug itself, it may confuse the developer.

Example

public partial class AddAccountWizardForm : Form
{
    ...
    private Step m_CurrStep = Step.Screen1;
    ...
    private bool ValidateFields(Step m_CurrStep)
    {
        ...
    }
    ...
}

IDENTICAL_METHOD_BODY

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

A number of different methods share the same implementation.

Example

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);
}

INCOMPLETE_SWITCH

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Switch statement doesn’t handle all possible argument values.

Example

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;
}

INCORRECT_INIT

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Static field is used before its initialization.

Example

public static readonly WeakReference WeakMissingConstant = new WeakReference(
    StrongMissingConstant);
private static readonly object StrongMissingConstant = new object();

INCORRECT_REFEQUALS

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

At least one argument of ReferenceEquals call is not of a reference type.

Example

public struct Polynomial<Variable, Expression>
{
    /* ... */
}
/* ... */
Polynomial<BoxedVariable<Variable>, BoxedExpression> pol;
/* ... */
Contract.Assume(!object.ReferenceEquals(pol, null));

ITERATED_ONCE

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The loop contains unconditional break or return, so it will always have only one iteration.

LOCK_OBJECT_REASSIGNMENT

Language Situation Severity Reliability Enabled
CSHARP Quality Minor Unknown true

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.

Example

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;
    }
}

MATH_CONSTANTS

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Common constants are defined explicitly, instead of using library constants.

Example

In the following code sample 6.28318531 should be changed to 2 * Math.PI.

if (Math.Abs(InitialAngle - LastAngle) > 6.28318531) {
    // ...
}

MISSING_THROW

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The exception is created, but throw operator is missing.

Example

try
{
    mxRecords = GetMxRecords(domain);
}
catch
{
    new Exception("Can't connect to DNS server.");
}

MISSING_VOLATILE

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The loop condition may be changed only in another thread, and the field that should be changed doesn’t have volatile modifier, so the field access may be optimized out.

Example

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.
    }

REAL_INT_COMP

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The comparison contains both integer and floating point values.

Example

public void test(int i)
{
    if (i > 10.5)
        return;
}

RETURN_USING

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The object implementing IDisposable interface is returned inside of using block. Returned object will be disposed.

Example

public static MemoryStream SerializeToStream(object o)
{
    using (var stream = new MemoryStream())
    {
        Formatter.Serialize(stream, o);
        return stream;
    }
}

SAME_RETURN

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The method always returns the same value.

Example

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;
}

SELF_ASSIGN

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

A variable or field is assigned to itself.

Example

class C {
    string x;
    void f() {
        x = x;
    }
}

SIMILAR_BRANCHES.COMMENTS

Language Situation Severity Reliability Enabled
CSHARP Quality Major Unknown true

C#-only subtype of SIMILAR_BRANCHES where branches differ only by comments.

SIMILAR_BRANCHES.GROUPED

Language Situation Severity Reliability Enabled
CSHARP Quality Major Unknown true

C#-only subtype of SIMILAR_BRANCHES where an if-else chain or a switch statement contains more than one pair of identical branches.

SIMILAR_BRANCHES.WITHDEFAULT

Language Situation Severity Reliability Enabled
CSHARP Quality Major Unknown true

C#-only subtype of SIMILAR_BRANCHES where a case section has the same body as the default section.

STRING_CONCAT

Language Situation Severity Reliability Enabled
CSHARP Performance Normal High true

String concatenation in loop is inefficient, because it creates a new string at each iteration. StringBuilder or string.Join should be used instead.

Example

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;
}

STRING_FORMAT

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The format string uses more format items than supplied or does not use some supplied arguments.

Example

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));

UNREACHABLE_CODE.EXCEPTION.RETURN

Language Situation Severity Reliability Enabled
CSHARP Quality Normal Unknown true

C#-specific subtype of UNREACHABLE_CODE.EXCEPTION for situations where a return statement is unreachable because the previous operation always throws an exception. Some of such warnings are useless because the exception is desired behavior and the return is required by the compiler.

Example (useless warning)

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;
}

UNREACHABLE_CODE.DEFENCE_CODE

Language Situation Severity Reliability Enabled
CSHARP Quality Normal Unknown true

C#-specific Subtype of UNREACHABLE_CODE for situations where a throw statement or expression is unreachable.

Example 1

Type type = typeof(T);
if (null == type)
    throw new ArgumentNullException("type");

Example 2

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);
}

UNREACHABLE_CODE.RET

Language Situation Severity Reliability Enabled
JAVA Quality Normal Low true
CXX Quality Normal Low true
GO Quality Normal Low true

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.

Example

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;
}

UNREACHABLE_CODE.CATCH

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true

Detects useless catch which intercept Java subtypes of class “Exception”.

Example

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.

UNREACHABLE_CODE.EXPLICIT

Language Situation Severity Reliability Enabled
CSHARP Quality Normal Unknown true

C#-specific subtype of UNREACHABLE_CODE for situations where a code fragment is unreachable because the condition is a false literal.

Example

string data;
if (false)
    data = null;
else
    data = "foo";

[IMPLICIT] (C#)

This tag is not a warning subtype, but a prefix of warning message. It means that the unreachable operation is not visible in the source code, because it was added by the compiler. The expression in the warning text is the original source code expression which was transformed.

Example

Here the compiler has transformed the resources ?? GetResources(v) into resources != null ? resources : GetResources(v), and the resources expression in the true branch is unreachable, because resources != null is always false.

Dictionary<string, object> resources = null;
if (v != null)
{
    resources = resources ?? GetResources(v);  // UNREACHABLE_CODE [IMPLICIT] Execution cannot reach
                                               // code starting from resources statement.
}

C# subtypes may be applied together in the following order: .EXCEPTION.{RETURN, DEFENCE_CODE, EXPLICIT}.TEST

UNSAFE_DESERIALIZATION

Language Situation Severity Reliability Enabled
CSHARP Security Minor Unknown true

This checker detects serialization of fields which can be used for code or data injection, such as delegates or unmanaged references.

Example

In this code sample the event’s delegate field is serialized, so changing it in the serialized object can lead to calling some other event handler.

[Serializable]
class WrapEvent
{
    public event EventHandler OnRun;
}

VALUE_NULL_COMPARISON

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

The expression value may have value type, but is compared with null. A value type cannot contain null value.

Example

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;
}

USE_AFTER_DISPOSE

Language Situation Severity Reliability Enabled
CSHARP Quality Minor Unknown true

The checker detects situations when the disposed resource was 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.

Example

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();
            }
        }
    }
}

VIRTUAL_CALL_IN_CONSTRUCTOR

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Virtual method of object is called inside its constructor. It may lead to inconsistent state, because the virtual method may be redefined in the child and may use some child-specific data that is not initialized yet.

Example

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.

WRONG_COMPARISON

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

After some value is retrieved by as operator, the original value is checked for null instead of the new one.

Example

var right = other as PredicateNullness;
if (other != null) // Should be `if (right != null)`.
{
    if (this.value == right.value)
    {
        return this;
    }
}

WRONG_LOCK.STATIC

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CSHARP Quality Major Unknown true

Static field is accessed under lock, but the lock argument is not static. The lock may be entered by multiple threads simultaneously.

Example

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);
        }
    }
}

XPATH_INJECTION

Language Situation Severity Reliability Enabled
CSHARP Security Critical Unknown true

User-provided data are used to form an XPath query without neutralization of special elements. It can be used by an attacker to execute arbitrary XPath query.

Example

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 + "']");
}

TCCHECK.NUNIT.MISSING_ASSERTION.RETURN_VALUE

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Interprocedural detector for NUnit framework based tests, that checks if at least one field of every checked object have been also checked. So, it is required to check not only a reference (ex. by type or null-value), but also fields of the object.

Example

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();
}

TCCHECK.NUNIT.MISSING_ASSERTION.USER_DEFINED

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Interprocedural detector for NUnit framework based tests, that checks if every return value or parameter of method from user-defined list have been checked with assertion. List of functions is declared in svace config directory with a JSON file white-list-tccheck-nunit.json. The syntax of the file is like follows:

{
    "classname" : "function_classname",
    "function" : "function_name",
    "checkReturn" : true,
    "checkParameters" : [0,2]
}

Example

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");
}

TCCHECK.NUNIT.NO_ASSERTION

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Interprocedural detector for NUnit framework that checks whether at least one assertion exists in each test method.

Example

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);
}

TCCHECK.NUNIT.USELESS_ASSERTION

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

Interprocedural detector for NUnit framework that detects faulty assertions always evaluating to be true or false. This can happen if a program reaches an assertion and there is nothing left to check, e.g. when all alternatives were already sorted out by the previous code.

Example

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");
}

THREAD_STATIC_FIELD_INITIALIZATION

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

A thread static field is declared with an initializer. The initialization will happen only in one thread.

Example

[ThreadStatic]
public static List<object> _trace = new List<object>();

THREAD_STATIC_FIELD_NON_STATIC

Language Situation Severity Reliability Enabled
CSHARP Quality Critical High true

[ThreadStatic] attribute is used on a non-static field and has no effect.

Example

[ThreadStatic]
private bool _traceListenerSuspended;

Go warning types

Those checkers are developed for searching error specific for Go language.

DEREF_OF_NULL.GLOBAL

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

In Golang most uninitialized errors were eliminated by language design, but it is still possible for global variables. This checker finds situation where global variable is used without any initialization.

Example

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.
}

LOOPVAR_IN_CLOSURE

Language Situation Severity Reliability Enabled
GO Quality Undefined Unknown true

It searches for using of loop variable in goroutine. Value of this variable may be changed by outer loop before this goroutine’s execution which lead to race condition.

Example

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()

DEREF_OF_NULL.RET.GO_INTERFACE

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

Interfaces are not pointers in Go even though they may look like pointers, and an interface variable will be nil only when its type and value fields are nil. This can lead to unexpected behavior when checking an interface variable for nil.

The checker detects situations where a pointer, which might be nil, is converted to a value of interface type. The checker does not analyze caller code.

Example

func foo(x int) Interface {
    var possibleNil *Struct
    if (x > 5) {
        possibleNil = &Struct{Field: 5}
    }
    return possibleNil
}

DEREF_OF_NULL.RET.GO_INTERFACE.STRICT

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

The warning is reported when typed nil value may be returned from the function and return type is interface.

func createTypedNilStruct() Interface {
    var typedNil *Struct
    typedNil = nil
    return typedNil // Warning.
}

NO_RECOVER_FOR_PANIC

Language Situation Severity Reliability Enabled
GO Quality Major High true

The analysis propagates information on whether recover before panic on each execution path or not was.

If it was not then there is making the warning NO_RECOVER_FOR_PANIC.

Example

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
    }
}

TAINTED.UNCHECKED_TUPLE.RET

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

The warning is reported when value is extracted from the map using tainted key without result checking.

For Go language when value = map[key] is used instead of value, ok = map[key], key is tainted, and value was not checked.

TAINTED.UNCHECKED_TUPLE.RET.PROC

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

Same as TAINTED.UNCHECKED_TUPLE.RET, but the sink is in callee.

UNCHECKED_TUPLE.CAST

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

The warning is reported for Go language code in the form of value, ok = i.(T) when ok value is ignored.

Example

value, err = function()
return value // `err` is never checked.

UNCHECKED_TUPLE.CAST.PROC

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

Same as UNCHECKED_TUPLE.CAST, but the sink is in callee.

UNCHECKED_TUPLE.CHAN

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

The warning is reported for Go language code in the form of value, ok = <-channel when ok value is ignored.

UNCHECKED_TUPLE.RET

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

The checker detects situations where tuple result from functions with error part is used without checking for error.

Example

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.
}

UNCHECKED_TUPLE.RET.PROC

Language Situation Severity Reliability Enabled
GO Quality Normal Unknown true

Same as UNCHECKED_TUPLE.RET, but the sink is in callee.

INFINITE_LOOP.GOROUTINE

Language Situation Severity Reliability Enabled
GO Quality Major Unknown true

This checker finds infinite loop in goroutines.

Example

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)
}

UNSAFE_TYPE_SWITCH

Language Situation Severity Reliability Enabled
GO Quality Undefined Unknown true

The checker finds situation where type-switch does not have default branch.

Example

switch dm := m.(type) {
case DynamicAny:
    m = dm.Message
case *DynamicAny:
    if dm == nil {
        return nil, proto.ErrNil
    }
    m = dm.Message
}
// No default.

UNSAFE_SWITCH

Language Situation Severity Reliability Enabled
GO Quality Undefined Unknown true

This checker finds situation where switch does not have default branch.

Example

switch num {
case 1:
    fmt.Println("Here is 1")
case 2:
    fmt.Println("Here is 2")
}
// No default.

UNSAFE_TYPE_ASSERTION

Language Situation Severity Reliability Enabled
GO Quality Undefined Unknown true

This checker finds situation where the result of the type assertion is used without checking for the correct value.

Example

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)
}

UNSAFE_TYPE_CONVERSION

Language Situation Severity Reliability Enabled
GO Quality Undefined Unknown true

This checker finds situations where may be an integer overflow in type conversion.

Example

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
}

UNUSED_ERROR_RETURN

Language Situation Severity Reliability Enabled
GO Quality Undefined Unknown true

This checker finds situations where the error return is not used.

Example

func withErrReturn(a int, b int) (int, error) {
    return 0, errors.New("text string")
}

func test() {
    a, _ := withErrReturn(0, 0) // UNUSED_ERROR_RETURN
    common.Use(a)
}

Other warning types

FORMAT_STRING.PARAM_LACK

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Major Unknown true

This checker finds situations where a format string specifies more arguments than the number of arguments actually passed to a format output function. This leads to illegal access to stack memory.

Example

void test(int x) {
    printf("count: %d/%d\n", x);
}

See also

FORMAT_STRING.PARAM_EXCESS

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

This checker finds situations where a format string specifies fewer arguments than the number of arguments actually passed to a format output function.

Example

void fmt_str(int x, int y, int z) {
    printf("count: %d/%d\n", x, y, z);
}

See also

FORMAT_STRING.TYPE_MISMATCH

Language Situation Severity Reliability Enabled
CXX Quality Minor Unknown false

This checker finds situations where a format string specificator’s type does not match the type of the corresponding actual argument passed to a format input or output function.

Example

void f(int x) {
    printf("%s\n", x);
}

RETURN_LOCAL_VAR

Language Situation Severity Reliability Enabled
CXX Quality Critical VeryHigh true

This warning is emitted when pointer to a local variable is returned from a function. Memory referenced by such pointers is invalid, and using it may lead to inconsistent behavior.

More general cases of usage of memory out of scope is also reported by RETURN_LOCAL_ADDR checker.

Example

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.

RETURN_LOCAL_ADDR

Language Situation Severity Reliability Enabled
CXX Quality Critical Average true

This warning is emitted when pointer to a local variable is leaked from a function. Memory referenced by such pointers is invalid, and using it may lead to inconsistent behavior.

This checker reports more general usecases than RETURN_LOCAL_VAR does; for example, it fires a warning when a variable is leaked via a function call.

Example

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;
}

NO_RETURN_VALUE

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

This checker finds functions in the source code that are declared as having a non-void return value, but don’t return any value. This checker only detects such function that don’t unconditionally terminate execution of the program.

Example

void boo();

int foo(void) {
    boo(); // NO_RETURN_VALUE will be emitted here.
}
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.
}

PROC_ADDR_NULL_CHECK

Language Situation Severity Reliability Enabled
CXX Quality Minor VeryLow true

This warning is emitted when function pointer known to be pointing to some particular function(s) is compared to NULL. Such comparison is always false.

Example

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();
}

Sometimes such comparison is intended. To suppress this warning, change 0 to a constant with NULL value:

int fun() {
    return 1;
}

typedef int (*PF)();

void proc() {
    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.)

PROC_ADDR_NULL_PTR_CHECK

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This warning is emitted when function pointer known to be pointing to some particular function(s) is compared to another function pointer that is equal to NULL. In many cases such comparison is useless.

Example

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();
}

PROC_PAR_BIG

Language Situation Severity Reliability Enabled
CXX Quality Major High false

This checker finds situations where a structure of size equal to or greater than 128 bytes and less than 512 bytes is passed as a function parameter by value.

See also

PROC_PAR_HUGE

Language Situation Severity Reliability Enabled
CXX Quality Normal High true

This checker finds situations where a structure of size equal to or greater than 512 bytes is passed as a function parameter by value. Passing structures of such size by value can slow down a program significantly.

See also

STACK_EXCEED

Language Situation Severity Reliability Enabled
CXX Quality Minor High true

This checker finds situations where the total size of memory that might be simultaneously allocated on stack exceeds the allowed bound (8 MB by default on Linux).

Example

void bigmem() {
    char arr7mb[7 * 1024 * 1024];
    // ...
}

void test() {
    char arr2mb[2 * 1024 * 1024];
    // ...
    bigmem();
    // ...
}

See also

LOCAL_VAR.BIG

Language Situation Severity Reliability Enabled
CXX Quality Minor Low false

This checker finds situations where the size of a variable or an array exceeds 100 KB.

See also

LOCAL_VAR.HUGE

Language Situation Severity Reliability Enabled
CXX Quality Minor Average true

This checker finds situations where the size of a variable or an array exceeds 1 MB.

See also

INVARIANT_RESULT

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true
GO Quality Major Average true
CSHARP Quality Major Average true

This checker reports a suspicious expression where the result of operation is always a constant regardless of the value of its variable operands.

Example

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.

Example

In the following example, bitwise AND is applied with zero.

unsigned flags = 5;

if (flags & 0) {
    // ...
}

Example

In the following example, a logical operator ! appears to have been substituted for a bitwise operator ~.

int supposedToBeBitwiseComplementOfFLAGS = !FLAGS;

Example

In the following example, parentheses are missed.

!var & FLAGS

Example

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.

short_variable |= 0x10000;

LOGICAL_OP_USELESS_ARG

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This checker finds code where the second operand of a logical operator has no impact on the result.

Example

void ex_1(unsigned a) {
    if (a < 7 && a < 10) {
        // ...
    }
}

CHECK_AFTER_PASS_TO_PROC

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true

This checker finds situations where a value is first used by passing it to a function as an argument, and then is checked for potentially being negative afterwards, meaning that such value may indicate an error code.

Example

int foo(int fd) {
    int status = dup(fd);

    if (fd != -1) {
        return status;
    }

    return -1;
}

PASS_TO_PROC_AFTER_CHECK

Language Situation Severity Reliability Enabled
JAVA Quality Normal High true
CXX Quality Normal High true

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.

Example

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`.
}

BAD_COMPARE

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This checker finds suspicious ‘lesser than’ and ‘greater than’ comparisons between a pointer and a null pointer.

Example

void foo(void *p) {
    // ...
    if (p >= NULL) // Probably, `p != NULL` was intended.
        return;
    // ...
}

BAD_COMPARE.BSTR_TO_OTHER

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This Microsoft Windows-specific checker finds situations when a non-BSTR value is incorrectly compared with a BSTR value.

Example

bool compare(BSTR s1, wchar_t *s2) {
    if (s1 == s2) // Incorrect comparison.
        return true;
    return false;
}

Fixed code:

bool compare(BSTR s1, wchar_t *s2) {
    if (wcscmp(s1, s2) == 0)
        return true;
    return false;
}

BAD_COMPARE.INT_TO_BOOLEAN

Language Situation Severity Reliability Enabled
CXX Quality Undefined Unknown true

This checker finds unsafe mix of integer values and boolean constants in comparison operations.

Example

In the AVFS library the return value of the virt_islocal function is 1 in case of a successful check, 0 in case of a negative check and -1 in case of an error.

But the author of the code below assumed that its return value may be interpreted as a boolean value and checked only the negative scenario. In case of the virt_islocal function returning -1 on error the true branch of the if expression is taken.

if (virt_islocal(fname) != false) {
    // ...
} else {
    // ...
}

UNREACHABLE_CODE

Language Situation Severity Reliability Enabled
JAVA Quality Normal Average true
CXX Quality Normal Average true
GO Quality Normal Average true
KOTLIN Quality Normal Average true
CSHARP Quality Normal Average true

This checker finds source code that can’t be executed because control flow path to the code from the rest of the program is unfeasible.

Example (C/C++)

void foo(int i);

void unreachable(int cond) {
    if (cond) {
        printf("error!\n");
        exit(1);
    } else {
        exit(0);
    }
    foo(15); // UNREACHABLE_CODE
}

The program is terminated on all paths leading to the call of foo function. The function call will never be executed.

Example (Kotlin)

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 first and second branches cover all possible values of a. Just remove redundant branch to fix this defect.

See also

UNREACHABLE_CODE.ENUM

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true
CXX Quality Normal Unknown true

Subtype of UNREACHABLE_CODE for switch with enum variable. All possible values are enumerated by case labels. The warning is emitted for default label. Note: it is a good practice to always write default label.

Example

enum E {
    aa1,
    aa2,
    aa3
};

void example(enum E x) {
    int a = 1;

    switch(x) {
        case aa1: a = 2; break;
        case aa2: a = 4; break;
        case aa3: a = 5; break;
        default: a = 22; break; // UNREACHABLE_CODE.ENUM
    }
}

UNREACHABLE_CODE.SWITCH

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true
CXX Quality Normal Unknown true

Subtype of UNREACHABLE_CODE for situations where case label of switch is unreachable.

Example

void example(enum E x) {
    if (x == aa2)
        return;

    int a;

    switch(x) {
        case aa1: a = 2; break;
        case aa2: a = 4; break; // UNREACHABLE_CODE.SWITCH
        case aa3: a = 4; break;
        default: a = 22; break; // UNREACHABLE_CODE.ENUM
    }
}

UNREACHABLE_CODE.DEFAULT

Language Situation Severity Reliability Enabled
JAVA Quality Minor Unknown false
CXX Quality Minor Unknown false

Subtype of UNREACHABLE_CODE for situations where default label of switch is unreachable. Situations with enum variables are not related to this type. UNREACHABLE_CODE.ENUM is emitted for them.

Example

void example1(int x) {
    if (x < 0 || x > 4) {
        return;
    }

    int a = 1;

    switch (x) {
        case 0: a = 2; break;
        case 1: a = 4; break;
        case 2: a = 5; break;
        case 3: a = 3; break;
        case 4: a = 12; break;
        default: a = 22; break; // UNREACHABLE_CODE.DEFAULT
    }
}

In follow code snipped programmer may assume that default label include cases where variable x is equal to aa3.

Example

enum E {
    aa1,
    aa2,
    aa3
};

void example2(int z) {
    enum E x = aa1;

    if (z == 3)
        x = aa2;

    int a;

    switch (x) {
        case aa1: a = 2; break;
        case aa2: a = 4; break;
        default: a = 22; break; // UNREACHABLE_CODE.DEFAULT
    }
}

UNREACHABLE_CODE.EXCEPTION

Language Situation Severity Reliability Enabled
JAVA Quality Normal Unknown true
CXX Quality Normal Unknown true
CSHARP Quality Normal Unknown true

Subtype of UNREACHABLE_CODE for situations where a code fragment is unreachable because the previous operation always throws an exception.

Example (C#)

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;
...

UNREACHABLE_CODE.NO_PATH

Language Situation Severity Reliability Enabled
CXX Quality Normal Average false
CSHARP Quality Normal Average true

Subtype of UNREACHABLE_CODE for situations where there is no path to a code fragment.

Example (C#)

Here a programmer forgot to add a default label before throw statement, so the throw statement belongs to case 4 section but is located after break.

switch (ByteCapacity)
{
    case 1: Stream.WriteByte((byte)CurrentValue); break;
    case 2: Stream.WriteStruct((ushort)CurrentValue); break;
    case 4: Stream.WriteStruct((uint)CurrentValue); break;
    throw(new InvalidOperationException());
}

INT_TO_CHAR

Language Situation Severity Reliability Enabled
CXX Quality Normal Average true

Some C functions, such as getc, fgetc and getchar, return either (non-character) value EOF or a character code. This checker detects incorrect use of such functions, where their return value is not compared to EOF before being converted from int to char (note that comparing to EOF after converting to type char is incorrect, since integer value of EOF is not a char).

Example

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.
    }
}

UNSPECIFIED_CHAR_IN_COND

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This C-specific portability checker detects situations of using char type (without explicit signed or unsigned type modifier) variable in conditional expressions. Different target platforms have different default signedness of the char type and using variables of this type without explicit signedness type modifier may lead to unexpected changes in program behaviour on platforms with different defaults.

Example

While the following loop iterates four times as expected when compiled for a platform with signed char type, it becomes infinite when compiled for a platform with unsigned char type.

for (char depth = 3; depth >= 0 ; --depth) {
    // ...
}

PROC_USE.VULNERABLE

Language Situation Severity Reliability Enabled
CXX Quality Minor High true

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.

PROC_USE.VULNERABLE.SSCANF indicates the use of function sscanf.

PROC_USE.VULNERABLE.GETENV indicates the use of getenv function, which value can be hooked by a hacker.

PROC_USE.VULNERABLE.SQLITE indicates the use of vulnerable sqlite functions (e.g. sqlite3_exec, sqlite3_get_table).

Example

void vln_use() {
    char buf[10000];
    gets(buf); // No matter what is the size if `buf`, `gets` can overflow it.
}

PROC_USE.RAND

Language Situation Severity Reliability Enabled
CXX Quality Minor VeryHigh true

This checker reports warnings for the use of weak pseudo-random generator functions including rand(), random(), erand48(), drand48(), mrand48(), and lrand48().

TOCTTOU_SEQUENCE

Language Situation Severity Reliability Enabled
CXX Quality Minor Unknown false

The checker TOCTTOU_SEQUENCE (Time-of-check-to-time-of-use) finds situations where a call accessing file attributes (such as stat(), access() and readlink()) is followed by a call using the same file (such as fopen(), opendir()). This can potentially lead to a kind of race condition, where between the check of file attributes and use of the file, the file is changed.

Example

void test(const char* fname) {
    if (access(fname, W_OK) == 0)
        open(fname, O_WRONLY);
}

CHROOT_JAIL

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown true

This checker finds situation where a call to chroot() is made, that isn’t followed by a call to chdir("/"). Function chroot() limits application’s access to the file system to within a given directory (“sandbox”). However, if current directory is outside the sandbox, the application can still access files outside the sandbox. Calling chdir("/") makes sure it doesn’t happen.

This checker can handle source code that implements calls to chdir and chroot using wrapper functions, or code that calls chdir conditionally on the return value of chroot.

Example

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");
}

NEGATIVE_CODE_ERROR

Language Situation Severity Reliability Enabled
JAVA Quality Major Average true
CXX Quality Major Average true
GO Quality Major Average true

The checker NEGATIVE_CODE_ERROR finds situations where a value is returned by a library function, that may be negative to indicate an error code, but is used in a situation where the negative values are not expected (for example, in a call to another function, or as an index in buffer access).

Example

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.

NEGATIVE_CODE_ERROR.EX

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true

Path sensitive version of the NEGATIVE_CODE_ERROR. It checks if a path exists where some variable is assigned negative value, and then is used in a situation where the negative values are not expected.

Example

char buf[100];

void test1(int a, int b) {
        int x = -1;

        if(a)
                x = b;

        buf[x] = 6;// warning will be emitted here
}

UNCHECKED_FUNC_RES.LIB

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true
KOTLIN Quality Normal High true

This detector finds situations where a library function returns a value that might indicate an error and this value is not checked after it has been assigned to a local variable.

Example (C/C++)

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.

Example (Kotlin)

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.

See also

UNCHECKED_FUNC_RES.FREAD

Language Situation Severity Reliability Enabled
CXX Quality Major VeryHigh true

This detector is an analog of UNCHECKED_FUNC_RES.LIB. It detects situations where fread function result is compared with 0, but ferror or feof functions wasn’t called.

Example (C/C++)

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;
}

UNCHECKED_FUNC_RES.LIB.STRICT

Language Situation Severity Reliability Enabled
JAVA Quality Minor VeryHigh true
CXX Quality Minor VeryHigh true
KOTLIN Quality Minor VeryHigh true

This detector finds situations where a library function returns a value that might indicate an error and this value is neither checked nor assigned to a local variable. A .MACRO subtype of this warning is emitted if it is suspected that the warning caused by the expression being implemented within a macro.

Example (C/C++)

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.

Example (Kotlin)

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.

UNINIT.LOCAL_VAR

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

This checker finds situations where a value of locally defined (automatic) variable is accessed, but the variable was never initialized.

Example

struct X {
    int fld;
};

void uninit() {
    struct X st;
    int val;

    val = st.fld;
}

Variable st.fld accessed at the last line was never initialized.

UNINIT.LOCAL_VAR.VOLATILE

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

It is subtype of UNINIT.LOCAL_VAR for cases where uninitalized variable is volatile.

Example

void bar(int a);

void foo() {
    volatile int a;
    int x = a;//emitted UNINIT.LOCAL_VAR.VOLATILE
    bar(x);
}

UNINIT.BASE_CTOR

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
KOTLIN Quality Major Unknown true

This detector finds following situations: method of some class overrides the method of its base class which is called from constructor, also, the method of derived class accesses this class field. This causes a NullPointerException to be thrown.

This detector requires the COLLECT_JAVA_METHOD_OVERRIDERS setting to be enabled. Run svace config COLLECT_JAVA_METHOD_OVERRIDERS true to enable the option.

Example (Java)

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.

Example (Kotlin)

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.

NON_VIRTUAL_DTOR

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown false

This checker finds situations where a derived class directly uses dynamic memory, but its base class (with public inheritance) doesn’t have a virtual destructor. Deallocating objects of such classes through a pointer to the base class fails to invoke derived class’ destructor, and so can result in a memory leak.

Example

class Base {
public:
    ~Base() {} // A non-virtual destructor.
};

// Public inheritance.
class Child : public Base {
    int* arr;
public:
    Child() {
        arr = new int[10];
    }
    virtual ~Child() {
        delete[] arr; // This instruction works with dynamic memory;
                      // NON_VIRTUAL_DTOR will be emitted at this line.
    }
};

void use() {
    Base* b = new Child();
    delete b; // A memory leak example: only `Base::~Base` is invoked here.
}

Notes

Svace emits a warning even if source code never deletes objects of the derived class via a pointer to base class. Svace shouldn’t detect situations with private or protected inheritance. We plan to include traces with location of base class, inheritance chain, and delete location in future versions of the checker.

NO_CATCH

Language Situation Severity Reliability Enabled
JAVA Quality Normal High true
CXX Quality Normal High true
KOTLIN Quality Normal High true

This checker finds situations where some function throws exceptions which should not be thrown from it because of specified exception list, or because the function is a top-level function, or because the function is a destructor.

If some exception does not correspond to specified exception list of some function then the program will crash.

Example (C++)

void foo() throw(int) {
    throw "Hello world!"; // Warning is fired.
}

int main() {
    try {
        foo();
    } catch(...) {
        // Never reached.
    }
}

Exceptions from main function also terminate the program.

Example (C++)

int main() {
    throw "Hello world!"; // Warning is fired.
}

Throwing an exception out of a destructor is dangerous. If another exception is already propagating the application will terminate.

Example (C++)

struct Bad {
    ~Bad(){
        throw 1; // Warning is fired.
    }
};

int main() {
    try {
        Bad bad;
        throw 2;
    } catch(...){
        // Never reached.
    }
}

For Java and Kotlin this type of warning is emitted, if exception class that reaches main method (or parent of this class) is annotated with @MustBeCaught annotation. By default, IllegalArgumentException is annotated with this annotation.

@MustBeCaught annotation belongs to the ru.isp.svace.sf package. Exception classes from user code may be annotated with @MustBeCaught via svace spec tool.

Example (Kotlin)

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()
    }
    // ...
}

If some exception types are declared in throws ... of main method, then the warning is not emitted for these types of exceptions.

Example (Java)

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);
    }
}

NO_CATCH.LIBRARY

Language Situation Severity Reliability Enabled
JAVA Quality Normal VeryLow true
CXX Quality Normal VeryLow true
KOTLIN Quality Normal VeryLow true

This warning is emitted in the same patterns as described above but only if the exception could be thrown from some standard library function or operator.

Example (C++)

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);
}

Example (Java)

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`.
            }
        }
        // ...
    }
}

Example (Kotlin)

fun main(args: Array<String>) {
    val portNumber = args[0].toInt() // Warning is fired for `NumberFormatException`.
    println("Starting server on port $portNumber...")
    // ...
}

NO_CATCH.STRICT

Language Situation Severity Reliability Enabled
JAVA Quality Minor Unknown true
KOTLIN Quality Minor Unknown true

This type of warning is emitted only for Java and Kotlin for exception types that doesn’t belong to NO_CATCH and NO_CATCH.LIBRARY groups (i.e. not annotated with @MustBeCaught and thrown from user code).

Example (Kotlin)

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`.
    }
    // ...
}

NO_CATCH.LIBRARY.PEDANTIC

Language Situation Severity Reliability Enabled
CXX Quality Minor Unknown false

In some cases the standard library specification declares that some exception is possible, but actually it could never be thrown. The warnings of such cases are NO_CATCH.LIBRARY.PEDANTIC type. For example, std::string constructors assumes that there is some maximal capacity of the string that could be less than the size of addressable memory. So, if the user asks std::string to create a string with the size more than this maximal capacity, then std::string constructor will throw std::length_error. In most standard library implementations the maximal capacity is equal to the size of addressable memory and so there will be std::bad_alloc exception, not std::length_error, for too big strings.

Example

std::string s3 ('x', 10); // Theoretically can throw `std::length_error`.

NO_CATCH.BAD_ALLOC

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown false

The most used version of operator new cannot return NULL but instead could throw std::bad_alloc exception in the case when it could not allocate memory. Most programs don’t catch possible std::bad_alloc’s because they assume that there is enough memory. So std::bad_alloc exception has own warning type.

BAD_CAST

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

This checker finds situations where a pointer value is cast to a pointer with base type of different size. This may lead to a buffer overflow or unexpected value when the pointer is dereferenced.

BAD_CAST.BSTR

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This Microsoft Windows-specific checker finds situations when a non-BSTR value is incorrectly converted to BSTR.

Example

BSTR bstring() {
    wchar_t *string = L"string";
    return string; /* Incorrect: `wchar_t*` can't be directly casted to BSTR. */
}

Fixed code:

BSTR bstring() {
    wchar_t *string = L"string";
    return SysAllocString(string.c_str());
}

SIGNED_TO_BIGGER_UNSIGNED

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown false

Check for assignment of a signed value to a variable of a bigger unsigned integral type. While it is not a defect by itself this may unexpectedly lead to a large resulting value if the original signed value is negative.

Example

It may not be obvious that due to sign extension the value of b in the example below is 0xFFFFFFFFFFFFFFFF rather than 0xFFFFFFFF.

void ex_1() {
    int32_t a = -1;
    uint64_t b = a;
}

See also

SIGN_EXTENSION

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown false

This checker reports unexpected sign extension during integer promotion when result value has all of its high bits set to 1 and consequently is interpreted as a very large value.

Example

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));
}

See also

VARIABLE_IS_NOT_ARRAY

Language Situation Severity Reliability Enabled
CXX Quality Major Average true

This checker finds situations where the address of some not-array variable is used as array.

Example

void set(int* buf) {
    buf[10] = 0;
}

int test1() {
    int x = 0;
    set(&x); // There will be memory access violation.
    return x;
}

COMPARE_RESULT_OF_NEW

Language Situation Severity Reliability Enabled
CXX Quality Minor Average true

This checker finds situations where the value returned by operator new is compared with NULL.

Example

void foo() {
    char *x = new char[10];
    if (x) { // This check is redundant.
        //...
    }
}

UNCHECKED_FUNC_RES.STAT

Language Situation Severity Reliability Enabled
JAVA Quality Major High true
CXX Quality Major High true
KOTLIN Quality Normal High true

This checker finds situations where return value of some function is not checked, while it’s checked in most places where that function is called. The warning is emitted according to statistics of function use, without taking into account function’s body (implementation). Compared to DEREF_OF_NULL.RET, this checker relies on function use statistics to a greater extent, and works with more types of return value checks than just comparison with NULL. This checker emits warning if more than UNCHECKED_FUNC_RES.STAT.THRESHOLD percents of all such function calls are checked. UNCHECKED_FUNC_RES.STAT.THRESHOLD configuration variable is equal to 80% by default.

Example (Kotlin)

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.

CLIB.OPEN.MODE

Language Situation Severity Reliability Enabled
CXX Quality Normal Unknown true

This checker finds situations where standard library function open is invoked with flag O_CREAT, but without passing it the third parameter.

int open(const char* path, int oflag, ... );

Here if oflag is O_CREAT, it’s necessary to specify the 3rd argument to set correct file access rights.

NO_VA_END

Language Situation Severity Reliability Enabled
CXX Quality Normal VeryHigh true

This checker finds situations where va_end is missing from functions that work with variable argument lists.

Example

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);
}

See also

NO_VA_START

Language Situation Severity Reliability Enabled
CXX Quality Normal VeryHigh true

This checker finds situations where va_start is missing from functions that work with variable argument lists.

Example

void test(int x, ...) {
    va_list va;
    if (x != 0)
        va_start(va, x);
    while (x > 0) {
        // ...
        x--;
    }
    va_end(va);
}

See also

BAD_ASSERT_EXPRESSION

Language Situation Severity Reliability Enabled
CXX Quality Major Unknown true

This warning type is reported for expressions with side effects in assert macro expressions. Since assertions are usually disabled in release mode code may behave differently in debug and release mode if assertions have side effect, such as variable update.

Example

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.

BAD_COPY_PASTE

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Major Unknown true
KOTLIN Quality Major Average true
CSHARP Quality Major Unknown true

This checker locates code snippets that were copied and pasted. However, because of incomplete change, they unintentionally left some portions of the copy unchanged.

Example (C/C++)

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;
}

Example (Kotlin)

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
}

SIMILAR_BRANCHES

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Major Unknown true
CSHARP Quality Major Unknown true

This checker reports a warning when both true and false branches of a conditional operator are identical.

Example

void foo1(bool c){
    int i;
    if (c)
        i = 1; // Same code.
    else
        i = 1; // Same code.
}

CONFUSING_INDENTATION

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Major Unknown true
CSHARP Quality Major Unknown true

This checker finds many cases where the indentation structure of the code does not match the syntactic nesting.

Example

void foo(int a, int b) {
    if (a > b)
    b++;
    a++; // Doesn't belong to the `if` body but indentation suggests it is.
}

WRONG_ARGUMENTS_ORDER

Language Situation Severity Reliability Enabled
JAVA Quality Major Unknown true
CXX Quality Major Unknown true
CSHARP Quality Major Unknown true

This checker finds possible cases where the arguments to a function are provided in an incorrect order.

Example

In the following example a function with prototype void *calloc(size_t nmemb, size_t size); is called with swapped arguments.

calloc(size, nmemb);

UNUSED_FUNC_RES

Language Situation Severity Reliability Enabled
JAVA Quality Minor Unknown false
CXX Quality Minor Unknown false
GO Quality Minor Unknown false
KOTLIN Quality Normal Unknown false

This detector finds situations when a result of a function call is assigned to a local variable, but is never used afterwards.

UNUSED_FUNC_RES.MINOR is a subtype of UNUSED_FUNC_RES. This detector reports warnings that are less valuable to the user. The value of a warning is evaluated by a set of heuristic rules.

Example (C/C++)

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;
}

Example (Kotlin)

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.

See also

UNUSED_FUNC_RES.REWRITE

Language Situation Severity Reliability Enabled
JAVA Quality Minor High true
CXX Quality Minor High true
GO Quality Minor High true
KOTLIN Quality Normal High false

This detector finds situations when the result of a function call is assigned to a local variable that has never been used before being reassigned.

UNUSED_FUNC_RES.REWRITE.MINOR is a subtype of UNUSED_FUNC_RES.REWRITE. This detector reports warnings that are less valuable to the user. The value of a warning is evaluated by a set of heuristic rules.

Example (Kotlin)

fun get() = (0..100).random()

fun use(value: Int) = println(value)

fun example() {