Svace 4.0.241206 User Guide

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

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

Outline

This User Guide includes the following main sections:

Introduction

Typically, analyzing source code with Svace involves four stages:

Svace build monitoring allows to capture the finest details of the build process unavailable to other tools without this feature. In turn this lets the analyzer produce accurate and relevant intermediate representation of the analyzed source code. Finally, the main analysis engine utilize it to perform deep interprocedural and path-sensitive analysis able to uncover complex issues in the source code. Secondary analysis engines are also run on the same intermediate representation to quickly supplement them with a large number of more specific and simpler issues. The analysis results of all tools are combined and presented together in various formats including plain text, SARIF, XML and JSON.

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

Dependencies and installation

Svace is released in 3 separate distributions:

By request a distribution for macOS may be provided (x86_64, tested on Mojave and newer; svace build requires disabling System Integrity Protection).

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

Supported languages and compilers

Svace supports the following languages versions (including earlier, unless stated otherwise):

List of supported C/C++ compilers:

List of supported target architectures for GCC/Clang-based compilers, not specified explicitly:

List of known compilers with no plans to support:

Using Svace

This section describes how to perform analysis through command line interface, explaining the process of analysis in detail. The command line interface is provided by the svace program located in the bin subfolder of a Svace distribution. It supports a number of commands (or tools) to perform various tasks. The main tools are listed in the Command line tools section. The full list can be shown by running the svace program without arguments or the svace help tool.

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. It has the .svace-dir name by default and is created in the current working directory.

Building source code

Svace provides the svace build tool to wrap a normal building command. This tool intercepts invocations of compilers supported by Svace and transparently runs Svace internal compilers along with the original compiler command. For example, if the analyzed project uses a Make build system and is built using a make -f Makefile command then to automatically generate intermediate files for analysis of the project run svace build make -f Makefile command.

Building in Tizen and Samsung Build System

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

Building in Scratchbox2 used in Sailfish and Aurora SDKs

Building of Go projects using Bazel

Svace supports building of Go projects on Linux that use Bazel build system or bazelisk wrapper:

Selection of reported warning types

By default, warnings from a balanced list of warning types with high reliability and severity are reported. The svace warning tool can list these and other available warning types and control whether they are enabled or not for subsequent analyses.

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 using the svace config tool:

svace config THREAD_NUMBER 4

In the example above the THREAD_NUMBER configuration option, that controls the number of parallel threads used for analysis, is set to 4. By default, the THREAD_NUMBER option has a special value max that allows analyzer to use as many threads as the number of processor cores. Other possible values are positive numbers to set specific analysis threads number and auto to select threads number depending on the current processor load.

In most cases analysis is the fastest when THREAD_NUMBER is max. At the same time each thread uses additional memory so more threads will use more memory and when the amount of RAM is limited it is better to decrease this option value.

Performing analysis

Use the svace analyze command line tool. The analysis results are available afterwards in the project folder in various formats and can be viewed with a text editor, imported directly into a defect management tool or into a history storage.

Secondary analysis engines

A Svace distribution additionally includes Clang Static Analyzer and SpotBugs. Also, a number of lightweight detectors are implemented in Svace UAST engine for searching of issues patterns common for all supported languages. By default, all those tools are run before the main analysis, using intermediate data produced during the build phase as input. Defect reports produced by those tools are included into the final analysis results.

Working with history storage

In order to control history of source code issues introductions and fixes found during project development the analysis results can be imported into history storage to be shown and processed afterwards. History storage contains the list of warnings ever found in the project, and snapshots of the relevant source code. It also recognizes some issues detected on a new analysis run as instances of previously detected issues (even if the source code and line numbers changed between the analyses). If an issue was marked as a ‘false positive’ after some analysis run, it will remain marked so in new analysis results. The history storage functionality is provided by the internal Svace history storage or by a standalone Svacer history server. The latter is advised as it provides more features and is better supported.

Viewing the results

Analysis results may be viewed through a web-browser using either a builtin server (when using internal Svace history storage, see svace server) or a standalone Svacer history server.

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

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

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

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

Command line tools

The command line tools are accessed through a wrapper tool svace located in the bin subfolder of a Svace distribution (it is recommended to add this folder to the PATH environment variable, so that svace is accessible as a command). Command line options for svace are as follows:

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

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

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

commands:
  main:
    analyze             Run Svace static analyzer to search for defects in
                        source code, given the data gathered during 'svace
                        build'.
    build               Capture invocations of supported compilers performed
                        by a given command and generate intermediate data for
                        a subsequent analysis.
    config              Manage general configuration settings.
    init                Create a Svace project directory.
    server              Configure or run Svace server.
    warning             Control which warning types are enabled during
                        analysis.
  
  miscellaneous:
    admin               Manage object pool in a project directory.
    compare-svres       Compare warnings from two .svres files ignoring
                        checker names and generate comparison stats.
    context-spec        Manage context in which particular function will be
                        analyzed.
    export-build        Export a build object into an external directory.
    filter-svres        Extract the issues enabled in given warning settings
                        file from a given .svres.
    history             Interact with a warning database.
    ignore-test         Check svace.ignore filter.
    inactive-comments   Find warning suppression comments that appear to be
                        obsolete.
    match-svres         Match two svres files and show identifiers of the
                        matched warnings from the first svres file.
    merge-build         Merge multiple build objects into a single build
                        object.
    merge-svres         Merge multiple .svres files with analysis results for
                        different projects into a single file.
    remote              Execute command on remote analysis server.
    review-rebase       Rebase warning review from one svres file to other.
    server-config       Manage server configuration settings.
    show                Show results in a Svace server (localhost:8060).
    spec                Manages specifications for third-party library
                        functions,including user-provided.
    split-svres         Split a multi-project .svres file into separate files
                        with analysis results for individual projects.
    svres2json          Converter from Svace results format to JSON format and
                        vice versa.
    svres2sarif         Converter from Svace results format to Sarif 2.1
                        format.
    warning-selector    Pick particular warnings and write them to an svres
                        file.
  
  experimental:
    suppress            Run stand-alone suppression tool.

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.

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

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 intercepted command using the same options and environment.

Command line options are as follows:

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

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

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

Typical usage:
 svace build make -j4 all

 svace build gradle --no-daemon build

 svace build msbuild /t:Rebuild MySolution.sln

positional arguments:
  build-command

options:
  -h, --help            show this help message and exit
  --svace-dir SVACE_DIR
                        Specify a Svace directory, overriding current
                        directory and SVACE_DIR environment variable.
  --init                Initialize the selected Svace project directory before
                        doing anything else. This is a shortcut for avoiding
                        manual 'svace init' if all you need is its default
                        behavior.
  --clear-build-dir     Remove redundant contents of the build directory after
                        build.
  --enable-language LANG, --disable-language LANG
                        Enable or disable compilation interception for the
                        specified programming language. Valid values are: all,
                        csharp, cxx, go, javascript, kotlin, python, scala,
                        visualbasic. By default, all supported languages are
                        enabled.
  -L LANGS, --languages LANGS
                        Enable compilation interception for languages in the
                        specified comma-separated list and disable it for
                        other languages. Supported languages are: csharp, cxx,
                        go, javascript, kotlin, python, scala, visualbasic.
                        The special value 'all' enables all supported
                        languages (which is the default).
  --enable-uast-language UAST_LANG, --disable-uast-language UAST_LANG
                        Enable or disable UAST IR generation for the specified
                        programming language. Valid values are: all, go,
                        javascript, kotlin, python. See also '--uast-
                        languages'.
  --uast-languages UAST_LANGS
                        Enable UAST IR generation for languages in the
                        specified comma-separated list and disable it for
                        other languages. Supported languages are: go,
                        javascript, kotlin, python. The special value 'all'
                        enables all supported languages. If this option is not
                        set, it defaults to the subset of enabled languages
                        supported by UAST analysis.
  --python DIRECTORY or FILE.py
                        Specify the path to a directory with Python source
                        code or to a single Python source file to process for
                        subsequent analysis.
  --enable-mypy, --disable-mypy
                        Enable or disable Python analysis with mypy static
                        analyzer. Note that mypy is not bundled with Svace, so
                        it can be used only if found in your PATH and mypy
                        version is at least 0.790.
  --enable-python-ir-gen, --disable-python-ir-gen
                        Enable or disable generation of Python IR used for
                        analysis by Svace. This option is for advanced users
                        only.
  --too-high-kotlin-version-mode {fail,warn,ignore}
                        Specify what to do if an intercepted Kotlin
                        compilation uses a language version that is higher
                        than the maximum version supported by Svace. If set to
                        'fail' (the default), report a fatal error and don't
                        produce a build object. Otherwise, attempt to process
                        such compilations as though they were using the
                        maximum Kotlin version supported by Svace, and, if set
                        to 'warn', report a warning.
  --javascript DIRECTORY or FILE.js
                        Specify path to a directory with JavaScript source
                        files or a single JavaScript file to process for
                        subsequent analysis. Note: Only UAST analysis is
                        currently supported for JavaScript.
  --debug               Enable verbose logging useful for debugging and making
                        bug reports.
  --enable-dxr, --disable-dxr
                        Build source reference index used for source code
                        navigation (via history API). By default, it is
                        enabled.
  --enable-csa, --disable-csa
                        Run Svace checkers implemented in Clang Static
                        Analyzer during the build phase. By default, this
                        feature is disabled.
  --enable-csa-deps, --disable-csa-deps
                        Prepare everything needed for running Clang Static
                        Analyzer during the analysis phase. By default, this
                        feature is enabled.
  --enable-spotbugs, --disable-spotbugs
                        Run SpotBugs checkers for intercepted Java
                        compilations during the build phase. By default, this
                        feature is disabled.
  --disable-java-agent  Don't inject Java agent into intercepted JVM
                        processes. If this option is used, compilations
                        performed via compiler APIs (e.g., javac API) are not
                        intercepted. Since Svace Java agent requires Java 8 or
                        newer and causes build failure if it's injected into
                        an older JVM, this option can be useful if build
                        process involves older Java and compiler API
                        interception is not needed.
  --enable-link-fixup, --disable-link-fixup
                        Attempt to fix missing links in source markup data by
                        heuristically linking similar symbols. This feature is
                        disabled by default.
  --hash-server-memory HASH_SERVER_MEMORY
                        Specify maximum size of Java heap (in MB) that Svace
                        hash server may use (default: 2048).
  --spotbugs-memory SPOTBUGS_MEMORY
                        Specify maximum size of Java heap (in MB) that
                        SpotBugs may use (default: 2048).
  --kotlinc-memory KOTLINC_MEMORY
                        Specify maximum size of Java heap that Svace Kotlin
                        compiler may use (default: 'auto'). The value of this
                        option may be an integer, an integer percentage (in
                        the range from '1%' to '1000%'), or 'auto'. If set to
                        an integer, Svace will use the specified size (in MB)
                        for all compilations. Otherwise, Svace will attempt to
                        mirror the memory usage profile of the intercepted
                        build by detecting the maximum heap size used by each
                        compilation and scaling it by the specified percentage
                        (or an internal factor if 'auto'). If, for some
                        compilation, the heap size detection fails, Svace
                        won't set the maximum heap size for its compiler when
                        processing this compilation (so a JVM default will be
                        used instead).
  --captured-nothing-status STATUS
                        If the build command completed successfully and no
                        serious Svace failures were detected, but no
                        compilations were successfully captured, exit with the
                        specified status (255 by default).
  --low-ready-units-ratio PERCENTAGE
                        If the ratio of ready units to all units (as shown
                        with --verbose) is less than the specified integer
                        percentage for any programming language with at least
                        one intercepted compilation, print a warning and exit
                        with the status specified with --low-ready-units-
                        status option (but still generate all data necessary
                        for analysis). This option doesn't apply if no units
                        were captured at all (see --captured-nothing-status).
                        The default value is 0 (i.e. no capture ratio is
                        considered low).
  --low-ready-units-status STATUS
                        Set the exit status for --low-ready-units-ratio option
                        (254 by default).
  -v, --verbose         Report more detailed statistics after build.

experimental options:
  -t TARGET, --target TARGET
                        Specify the target platform that Clang should use for
                        bitcode generation. Valid values are: auto, arm,
                        aarch64, x86, x64, hexagon, mips, mipsel, mips64,
                        mips64el, powerpc, powerpc64, powerpc64le, riscv32,
                        riscv64, sparc, sparc64. Values other than 'auto'
                        override automatic target detection. This option is
                        intended for debugging only.
  --disable-bitcode-gen
                        Don't run Clang to generate bitcode files. Note that
                        disabling bitcode generation makes it impossible to
                        run Svace checkers on the resulting build object. It
                        is useful if you only want to get results from tools
                        run at the build time (e.g., warnings from CSA or
                        metrics). Turning on this option disables default
                        C/C++ metrics generation.
  --disable-pch-gen     Don't create an internal precompiled header (PCH) when
                        creation of a PCH by a supported compiler is detected.
                        Internal PCHs are used by Svace to speed up bitcode
                        generation for subsequent compilations, but might lead
                        to incorrect bitcode in some corner cases. In
                        particular, if an intercepted compiler creates a PCH
                        but rejects it in subsequent compilations (for
                        example, due to macro or option mismatch), falling
                        back to usage of normal headers, Svace might still use
                        an unsuitable internal PCH due to different mismatch
                        detection rules in Clang. PCH generation is currently
                        supported only for Clang and (partially) GCC.
  --enable-dxr-collect  Run a helper process that aims to reduce load on the
                        filesystem when building source reference index is
                        enabled (see --enable-dxr).
  --enable-ptrace, --disable-ptrace
                        Use ptrace-based interception for statically-linked
                        executables. By default, Svace uses LD_PRELOAD-based
                        interception, which works only for dynamically linked
                        executables. But sometimes compiler toolchains are
                        statically linked, or there are other statically
                        linked processes that break LD_PRELOAD-based
                        interception (for example, by removing LD_PRELOAD
                        environment variable). This option enables a hybrid
                        mode: Svace will start in LD_PRELOAD mode, but will
                        switch to ptrace-based mode each time it detects a
                        launch of a statically linked executable, and will
                        switch back if that executable runs a dynamically
                        linked executable.
  --enable-ptrace-all   Use ptrace-based interception for all executables and
                        disable LD_PRELOAD-based interception. See --enable-
                        ptrace for more details.
  --prepend-preload-lib, --append-preload-lib
                        Usually, the Svace library for function call
                        interception should be appended to LD_PRELOAD
                        environment variable, ensuring that Svace
                        modifications to the execution context are not
                        overridden by other LD_PRELOAD-libraries. However, if
                        such libraries modify arguments of created processes
                        in a way that makes them unrecognizable to the Svace
                        code that detects "interesting" processes, the Svace
                        library should be prepended instead, ensuring that it
                        sees the same arguments that were passed by the
                        application. By default, the Svace library is
                        prepended if Scratchbox environment is detected, and
                        is appended otherwise.
  --clean-ld-env-vars   Remove Svace-specific parts of LD_* environment
                        variables before the application's main() entry point
                        is called. This may help if the application complains
                        about them or is confused by unexpected values. This
                        option is meaningful only in LD_PRELOAD mode.
  --disable-path-sens-csa
                        Disable all path sensitive checkers of Clang Static
                        Analyzer.
  --disable-comp        Disable all compiler addons
  --disable-misc        Disable all non-compiler addons
  --clang-opts CLANG_OPTS
                        Add specified options to the Clang and Clang Static
                        Analyzer run by Svace. The options should be separated
                        by ';'.
  --spotbugs-opts SPOTBUGS_OPTS
                        Add specified options to the SpotBugs run by Svace.
                        The options should be separated by ';'.
  --enable-jar-decompilation, --disable-jar-decompilation
                        Enable or disable decompilation of used JAR libraries.
                        By default, it is disabled.
  --base-dir CSHARP_BASE_DIR
                        Base directory to use relative warning paths in C#
                        source code analysis.
  --enable-csa-stat     Generate '*.json.times' files containing the running
                        time of CSA checkers (in ms) in the build directory.
  --keep-intermediate-source
                        If --enable-csa-deps is specified, do not remove
                        partially preprocessed '.source' files from the build
                        directory after archiving them. This option is
                        intended for easier debugging.
  --hash-ar-content     Calculate archive hash based only on names of files in
                        the archive, excluding timestamp, UID, GID and
                        content. Use in case of undeterministic mode of ar or
                        ranlib and in case of prebuilt archives with prebuilt
                        object files (that may differ because of another
                        platform etc.). DO NOT USE in case of rebuilding the
                        same archive more than once in the same build (e.g.
                        debug and release mode).
  --preserve-build-order
                        Store compilation data in the same order as it's
                        received by Svace hash server. If the build order is
                        deterministic, but the build data is different between
                        runs, this may be useful to ensure that the data is
                        always processed in the build order by build object
                        consumers. By default, the build order is not
                        preserved, and build artifacts are usually sorted by
                        content hash before storage.
  --disable-hash-server
                        Disable Svace hash server, making it impossible to use
                        the build data on the analysis phase. This option is
                        for advanced users only.
  --enable-huge-source-workaround
                        Clang can't handle translation units larger than 2 GB
                        after preprocessing because it uses signed 32-bit
                        integer type internally for identifying source
                        locations. Sometimes such huge TUs occur in practice
                        because the same autogenerated header file is included
                        multiple times without guards. In this case source
                        locations are allocated for each inclusion, which may
                        cause an overflow resulting in a hang or crash. This
                        option makes it so source locations are allocated only
                        once per header file, which may help avoid the
                        overflow in this case. This option is incompatible
                        with '--enable-csa-deps'.
  --enable-mount-ns-workaround
                        If the build process enters a mount namespace and then
                        creates new mount points under non-shared mount
                        points, those new mount points are not visibile in the
                        parent mount namespace, preventing top-level Svace
                        processes from accessing affected paths. This option
                        enables a workaround. Currently, it works only if the
                        build process doesn't also enter a PID namespace.
  --chroot-interception-failure-mode {fail,warn,ignore}
                        Specify how to react if Svace fails to set up
                        interception upon encountering a transition into a
                        chroot jail. If set to 'fail', terminate the affected
                        process immediately and report a fatal error.
                        Otherwise, disable interception for the affected
                        process and its descendants and, if set to 'warn',
                        also report a warning when 'svace build' completes.
                        This may be useful in cases when chroot() is used for
                        purposes other than running compilations in a chroot
                        jail, making interception failure irrelevant. The
                        default value is 'fail'.
  --go-interception-mode {go-build,compile}
                        Specify Go interception mode. Supported modes are: go-
                        build, compile. The default is 'go-build' mode. In
                        this mode only compilations performed with 'go build'
                        are supported. The (experimental) 'compile' mode is
                        build-system-agnostic, so it supports build systems
                        which don't use 'go build' too (e.g. Bazel). The
                        'compile' mode implies --enable-ptrace. See --enable-
                        ptrace for more details.
  --enable-goa, --disable-goa
                        Enable or disable Go AST analysis during svace build.
  --enable-go-ir-gen, --disable-go-ir-gen
                        Enable or disable generation of Go IR used for
                        analysis by Svace. This option is for advanced users
                        only.
  --enable-go-vendor-gen, --disable-go-vendor-gen
                        Enable or disable generation of Go IR for dependencies
                        from vendor/modules.txt used for analysis by Svace.

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

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

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.
  --json             : Output in json format.
  -i, --info         : For each listed key, print a short description.
  -p, --parents      : When reading from config file, take into account the
                       settings specified in parent files. By default, apply
                       system-wide, user-specific and then Svace directory
                       or explicitly specified settings to form the list of
                       modified settings and their values.
                       If --global is specified, apply only system-wide and
                       user-specific settings; if --system is specified,
                       apply only system-wide settings.
  --svace-dir PATH   : Specify a Svace directory, overriding current directory
                       and $SVACE_DIR environment variable.
  -q, --quiet        : Only print error messages (to stderr) and specifically
                       requested output (to stdout).
  --get-val          : Only print the value of specified key (without 'key = ').
  --plugin-dir       : Add directory to search svace-api plugins in.
                       Use this option multiple times to add multiple directories.

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.

Special warning names that start with 'CWE' can be used to show all warnings
corresponding to given CWE identifier.

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

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

Warning type properties (displayed when option --info is given;
see User Guide for more details):
  Situation  : What kind of situation is indicated by the issue.
               Possible values:
                 Quality
                 Security
                 CodingStyle
                 Suppressed
                 Duplicate
                 Performance
                 Portability
  Severity   : Potential danger represented by the issue, if it's correctly
               detected. Possible values:
                 Critical
                 Major
                 Normal
                 Minor
                 Undefined
  Reliability: How often the issues of this type are detected correctly
               (in different projects). Possible values:
                 VeryHigh
                 High
                 Average
                 Low
                 VeryLow
                 Unknown
  Language   : For which languages warnings of this type are detected.
               Possible values:
                 CXX
                 JAVA
                 KOTLIN
                 SCALA
                 CSHARP
                 GO
                 PYTHON
                 JAVASCRIPT
                 VISUALBASIC
                 PLSQL
                 NONE
                 INTERNAL
  Detection tools: Where the checker for the issue is implemented.
               Possible values:
                 SvEng
                 SvaceCppSpecific
                 UAST
                 CSA
                 SpotBugs
                 Goa
                 Mypy
                 Roslyn
                 SvaceIR_API
                 Kotlinc
  Group      : A group which this warning type belongs to.

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

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

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

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

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

# List all critical warnings:
    svace warning CRITICAL

# List all warnings with autofixes:
    svace warning AUTOFIX

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

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

# List several categories:
    svace warning DEREF_OF_NULL,DEREF_AFTER_NULL

# Intersect several categories:
    svace warning --and CRITICAL,JAVA

# List all warning settings specified for current user, including a short
# description for each warning type:
    svace warning -lpi --global

# Dump all information about the configuration that would be used during
# analysis:
    svace warning -lap --show-hidden

# Show all warnings corresponding to CWE190:
    svace warning CWE190

svace analyze

This is the tool for running analysis. It takes as input the data produced by the svace build tool and produces analysis results files. Command line options are as follows:

usage: svace analyze [options]

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

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

experimental options:
  -t HASH, --task HASH  A task object to be analyzed.
  --raw-files           Analyze files with intermediate representation in the
                        build directory ($SVACE_DIR/bitcode by default)
                        instead of analyzing a build object. This option may
                        be useful for debugging, but normally shouldn't be
                        used.
  --build-call-graph-only
                        In this mode Svace will make call graph for analyze
                        project. Any analysis will be skipped.
  --with-cache          Use cache for analysis intermediate results. Enabling
                        cache may increased analysis speed of modified
                        project. Require additional disk space.
  --collect-summary     During analysis summary for functions will be stored.
                        They may be using if parameter '--use-summary' is
                        specified. Require additional disk space.
  --use-summary         Analysis will be run in special mode: if function body
                        can not be found then function summary will be taken
                        from cache. For cache filling parameter '--collect-
                        summary' is requiring.
  --analyze-storage-dir SVACE_ANALYZE_STORAGE_DIR
                        Specify a folder for storing analysis data that are
                        shared between analysis. Analysis with cache, fast
                        analysis and incremental analyses use this folder. By
                        default analysis uses folder $SVACE_DIR/analyze-
                        storage.
  --build-dir DIR       Specify a build directory produced by 'svace build' to
                        use for analysis. The default is $SVACE_DIR/bitcode.
                        Valid only together with --raw-files.
  -s DIR, --source DIR  Search for source code in DIR. Project directory is
                        used by default (as specified, even if the actual
                        Svace directory is its subdirectory .svace-dir). Valid
                        only together with --raw-files.
  -o DIR, --output DIR  Save analysis results in DIR. By default, the results
                        are saved in $SVACE_DIR/analyze-res. The analysis
                        result files are $PRJ_NAME.svres and $PRJ_NAME.txt,
                        where $PRJ_NAME is the name of the analyzed project.
                        Valid only together with --raw-files.
  --fix                 Run autofix during analysis.
  --autofix-backend {svace}
                        Autofix backend to use. The default is 'svace'.
  --emit-autofix-input  Emit autofix input as a separate file (for debug
                        purposes).
  --analyze-csharp-solutions-separately
                        Analyze all captured C# solutions separately.
  --plugin-dir DIR      Add directory to search svace-api plugins in. Use this
                        option multiple times to add multiple directories.

svace server

This tool manages and starts Svace in server mode listening for remote analysis requests or providing web-interface for integrated analysis results history server.

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

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

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

Other command line tools

Main command line tools are listed in the Command line tools chapter. This section contains other command line tools that may be occasionally useful.

svace server-config

Command server-config is similar to the config command and is used to setup configuration parameters for starting a remote analysis server.

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 svres2json

svres2json [options] FILENAME

svres2json is a converter from Svace results format to JSON format and vice versa.

Options:
   FILENAME              : path to the file to be converted
  -h, -help, --help      : display this message
  -o, --out OUT_FILENAME : path to the file where converted results will be stored. If this option
                           is not used, stdout will be used for this purpose
  --compact              : dump traces in compact format
  -b, --backward         : turn the backward mode on. In this mode an input file must be in JSON
                           format

svace svres2sarif

svres2sarif [options] FILENAME

svres2sarif is a converter from Svace results format to Sarif 2.1 format.

Options:
   FILENAME              : path to the file to be converted
  -h, -help, --help      : display this message
  -o, --out OUT_FILENAME : path to the file where converted results will be stored. If this option
                           is not used, stdout will be used for this purpose

svace spec

usage: svace spec <subcommand> [args]

Manages specifications for third-party library functions,including user-
provided.

options:
  --help      Show this help message and exit.

subcommands:
  subcommand
    add       Compile specifications from source files and add them to the
              specified location.
    list      Show existing specifications in the specified location.
    remove    Remove existing specifications from the specified location.
    show      Show the source file of a specification.

Tutorial

This section demonstrates the process of using the analyzer 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 and all proftpd build dependency packages are already installed.

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

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

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

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

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

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

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

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

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.

Analysis

Warning suppression

Some warning reports are less useful than others, and because of that Svace may suppress some warnings based on different conditions. Information about suppressed warnings is added to the .svace-dir/analyze-res/suppress subfolder in the project folder after the analysis.

Warning suppression in third-party code

Analysis is performed on the whole code used during the build process including third-party dependencies and system components that aren’t actually part of the project. To suppress warning reports for these files add the regular expressions for these files or whole directories to the .svace-dir/svace.ignore file in the project folder. Patterns for known system components, such as standard compiler header files, are added by default to this file during project folder creation. Note that these patterns control only the suppression of the warning reports for the corresponding files; their content is still analyzed to perform interprocedural analysis of the code that calls functions from them.

Warning suppression with comments

It is sometimes useful to ignore certain warnings for a specific line or block of code without affecting the entire analysis process. One popular way to do this is to mark the related source code lines with comments in a predefined format. The most common example is a simple NOLINT comment. But many static analysis and related tools use their own formats. Besides NOLINT Svace supports several popular warning suppression comment formats out of the box: NOSONAR from SonarQube, a tool for continuous inspection of code quality; svacer_review from Svacer, a standalone analysis history management server; noqa used to ignore PEP8 warnings in Python. All warning reports issued on the source lines with any of these comments can be suppressed if this feature is enabled.

Examples of supported comments:

//NOSONAR

// NOLINT

// NOLINTNEXTLINE

//nolint
//nolint:unused,deadcode

//svacer_review: -
//svacer_review: -INTEGER_OVERFLOW
//svacer_review: -INTEGER_OVERFLOW|-BUFFER_OVERFLOW

# noqa: E731,E123
#noqa

Svace parses arguments of comments in the Svacer format svacer_review to suppress only warning types listed there. Arguments of all other types are ignored and such comments suppress reports of any warning types. E.g. the nolint:unused,deadcode comment is interpreted the same way as nolint and the noqa: E731,E123 comment is interpreted the same way as noqa.

By default this functionality is disabled and can be enabled using the SUPPRESS_BY_COMMENT configuration option. By default warning suppression comments in all formats are processed but specific formats may be disabled separately using the corresponding configuration option:

Additionally, it is possible to process warning suppression in a custom format by assigning a SUPPRESS_BY_COMMENT.TEMPLATE configuration option with a suppression format regular expression. If the comment text in the source code matches this regular expression all warning reports for the corresponding source code line are suppressed.

Analysis with cache

When Svace is used regularly to analyze the same project it is possible to cache intermediate analysis results for the unchanged code parts to save time and resources for subsequent analysis runs. This takes some disk space to store the cache but can reduce analysis time significantly.

To use this feature build the project with svace build as usual but add the svace analyze --with-cache parameter (or enable the PERSISTENT_MODE configuration option). When this parameter is present the analyzer stores into the project folder the intermediate results and marks their dependencies at the end of the analysis. To use the cache during the next analysis use the same parameter --with-cache in the svace analyze command. Svace then checks the dependencies of the intermediate source code representation files in the new build object being analyzed, and if they match those from the cache, retrieves the analysis results from the cache and uses them instead of running it again. To achieve the best cache utilization it’s advised to reproduce as many build environment details as possible. Especially, the folder where the project is built may affect the resulting intermediate files and it is best to rebuild the project for the next analysis in the same folder.

Managing the cache

By default the cache is stored in the project folder in the .svace-dir/analyze-storage subfolder and is expanded with new entries after each analysis with cache. Its initial size after the first analysis is comparable to the size of the intermediate source code representation files of a single build object and grows with each subsequent analysis with cache of a new build object depending on the volume of changes in the analyzed source code. When Svace is integrated into the development process in such a way that a new project folder is created for each analysis, it is necessary to designate an external cache storage folder using a svace analyze --analyze-storage-dir <SVACE_ANALYZE_STORAGE_DIR> command line parameter, where <SVACE_ANALYZE_STORAGE_DIR> is a path to the folder where the analysis cache should be stored. This option can also be used to share the cache between different projects that contain common source code files. If the cache size becomes too large it can be purged by removing the cache storage folder entirely. It will be recreated during the next analysis with cache. The cache folder should also be removed when upgrading the analyzer to the new version.

The same cache is used for all analysis engines but may provide different impact for each of them for a particular project. There are several configuration options that control specific analysis engines cache usage. Using these options the cache can be disabled selectively for some of them: PERSISTENT_MODE.CSA for Clang Static Analyzer, PERSISTENT_MODE.SPOTBUGS for SpotBugs, PERSISTENT_MODE.MODULE_PARSING and PERSISTENT_MODE.FUNC_ANALYSIS for Svace.

Separate analysis of interdependent projects

Most projects depend in some way on third-party code, e.g. libraries. Interprocedural analysis may be much more precise when the analyzer has knowledge of what the libraries’ code does but most often they are built separately and it’s not available to the analysis. To overcome this problem it is possible to analyze part of the code, particularly third-party libraries, and store the intermediate analysis results in the form of function summaries to be reused during the analysis of the other parts of the project. Such summaries contain information about the notable side effects from the calls of the respective functions. In order to store such summaries build the library(-ies) with svace build as usual and add the svace analyze --collect-summary parameter when running its analysis. The summaries are stored in the project folder and can be used for subsequent analysis of the project dependent on the library by using a svace analyze --use-summary parameter when running its analysis.

Remote analysis

In many situations it’s more convenient to run svace build and svace analyze steps independently on different systems. For example, a developer’s PC is able to build a large project but lacks the required resources to perform the analysis. To support such scenarios in a simpler way it is possible to designate one computer as a remote analysis server to perform the analyses and provide it with analysis tasks over the network from other computers. Remote analysis server supports multiple projects with own analysis configurations and user authorization.

Remote analysis server setup

The server uses a special server folder that contains its configuration and data. Note that it is different from the Svace project folder .svace-dir. A server folder can be initialized by running a svace server init command in an empty folder. The server is initialized with default settings without any users or projects set up and they have to be added before its usage. To simplify server initialization special svace server admin create-admin or svace server admin create-defaults commands can be used. The former command creates an admin user with admin role allowing to manage the whole server. And the latter command additionally adds a regular user main with a role main that allows to view and modify any project on the server, and a project group main with a default project. Managing projects, project groups, users, roles and permissions is described below.

The following commands sequence demonstrates creating a remote analysis server folder and its basic configuration suitable for performing remote analysis:

mkdir server-dir
cd server-dir
svace server init
svace server admin create-defaults

Above commands produce output similar to the following, showing the initialization process:

Creating admin...
Enter user's password or press enter to use autogenerated password (without quotes): "!?qVt_1J'R%X"
Enter password:
Nothing entered: will use auto-generated password from above.

Creating role 'main'...
[INFO] Role created.
Creating user 'main'...
Enter user's password or press enter to use autogenerated password (without quotes): "jv8HC~hm4nf$"
Enter password:
Nothing entered: will use auto-generated password from above.
[INFO] User created.
Adding role 'main' to user 'main'...
[INFO] Role main added for user main
Creating project group 'main'...
[INFO] Project group created.
Creating project 'default'...
[INFO] No database for svace project directory; initializing new one
[INFO] Project directory initialized @server-dir/dirs/default
[INFO] Project created.
Adding modify permissions for role 'main'...
[INFO] Permission modify on SERVER granted for role main

Remote analysis server configuration settings

The following server settings can be changed using a svace server-config command:

The following client configuration option affects remote analysis:

Setting up analysis options on server

If some option should be changed for all projects on server - it can be done by command svace config with parameter --system or --global.

For example:

svace config --sytem SUPPRESS_BY_COMMENT true

Server config option REJECT_CLIENT_CONFIGS should be enabled (command svace server=config).

Project management

Running remote analysis requires specifying a project on the client. Each project is represented by a project folder contained inside of the server folder and can have own analysis settings and caches. Remote analysis project is created automatically when a remote analysis of a previously non-existing project is requested. In this case it is assigned to the main project group created during the server setup if the user starting the remote analysis has permissions to do so. To control the group which it belongs to the project can be created beforehand using a svace server admin create --project PROJECT --group GROUP command during server configuration, where PROJECT represents the name of the project being created and GROUP - the name of the group which it should belong to. The following server configuration command svace server admin create --project-group GROUP creates a new project group with the name GROUP. It is possible to register an already existing external project folder to be used for the project instead of creating it in the server folder. To do this use the --path option to specify the location of this project folder DIR in the project creation command svace server admin create --project PROJECT --group GROUP --path DIR. To show the list of all projects available on the server with their project folder locations the svace server admin list --projects command can be used. To show the list of available project groups there is the svace server admin list --project-groups command. Command svace server admin project-group GROUP --list-projects shows the projects that belong to the specified group GROUP. Removing a project PROJECT is done using the svace server admin remove --project PROJECT command. Similarly, removing a project group GROUP is done using the svace server admin remove --project-group GROUP command.

In the following example a new project group with the name foo is created followed by creation of a new project bar and registering an external project baz, both in this group:

svace server admin create --project-group foo
svace server admin create --project bar --group foo
svace server admin create --project baz --group foo --path /home/user/baz/.svace-dir

User and permission management

The remote analysis server works with multiple users using a role-based access control model. Running remote analysis requires user authentication with an account having sufficient privileges for the specified project.

On each remote analysis server, an unlimited number of users can be created, each of whom can have an arbitrary set of roles. Different users can have the same role. Each role describes a set of permissions for accessing individual projects, all projects in certain groups, or all projects on the server. If a role has specific permissions for a group of projects, then all users with that role have those permissions for all projects in that group. If a role has specific permissions for the server, then all users with that role have those permissions for all projects on that server. The final user permissions are determined by the sum of permissions from all the roles held by the user.

There are three types of permissions - read, modify, and admin. Each subsequent type of permission includes the properties of the previous one. Currently, the remote analysis server only checks the modify permission for the specified user to run an analysis (thus, modify or admin permission for the project is required). Other types of permissions are used to organize access to the capabilities of the built-in history server.

Creating a new user is done using the svace server admin create --user USER command, where USER is the desired username. When creating a user, you will need to enter their password (or accept the password suggested by the server). By default, the user does not have any roles. To change a user’s password, use the svace server admin update --user USER command, where USER is the name of an existing user for whom you want to change the password. You can view a list of existing users using the svace server admin list --users command. Deleting a user account is done with the svace server admin remove --user USER command, where USER is the name of the existing user you want to delete.

To create a new role, one should use the command svace server admin create --role ROLE, where ROLE is the desired role name. By default, the new role doesn’t have any permissions. To grant to a role a specific permission for a project, you need to execute the svace server admin grant-permission PERMISSION --role ROLE --project PROJECT command. In this command, ROLE is the name of an existing role to which you want to grant permission, PERMISSION is the corresponding permission (read, modify or admin), and PROJECT is the name of the existing project for which the role is granted the permission. Similarly, to grant to a role a specific permission for an entire project group, you should use the svace server admin grant-permission PERMISSION --role ROLE --project-group GROUP command, where GROUP is the name of an existing project group for which the role is granted the permission and the other parameters are the same as in the previous command. If you don’t specify any of the --project or the --project-group parameters in the svace server admin grant-permission PERMISSION --role ROLE command, then the corresponding permission PERMISSION will be granted to the role ROLE server-wide. To view the current permissions of a role, you can use the command svace server admin role ROLE --list-permissions, where ROLE is the name of an existing role for which you want to see permissions. To remove all permissions granted to a role ROLE for a project PROJECT, you should run the command svace server admin revoke-permission --role ROLE --project PROJECT. Similarly, to remove all permissions granted to a role ROLE for a project group GROUP, you should use the command svace server admin revoke-permission --role ROLE --project-group GROUP. The command svace server admin revoke-permission --role ROLE removes server-wide permissions granted to the role ROLE. You can display a list of existing roles on the server using the command svace server admin list --roles. To delete a role, use the command svace server admin remove --role ROLE, where ROLE is the name of the existing role you want to delete.

To assign a user a new role, you need to execute the command svace server admin user USER --add-role ROLE, where USER is the name of an existing user you want to assign the role with the name ROLE, which should be created previously. Conversely, to remove the role ROLE from the user USER, you should execute the command svace server admin user USER --remove-role ROLE with the corresponding parameters. The command svace server admin user USER --list-roles is used to view the current roles assigned to the user USER, and the svace server admin user USER --list-permissions command is used to view the actual permissions that the user has on projects, project groups or the entire server. The command svace server admin role ROLE --list-users is used to view users who have a specific role, where ROLE is the name of the role for which you want to determine the list of users who possess it.

The example commands below create a new user jenkins and allow it to run remote analysis using only the bar project using a ci_remote_analysis role:

svace server admin create --user jenkins
svace server admin create --role ci_remote_analysis
svace server admin user jenkins --add-role ci_remote_analysis
svace server admin grant-permission modify --role ci_remote_analysis --project bar

Starting and stopping remote analysis server

The server is started with the svace server start command and can be stopped anytime by killing the server process, e.g. by pressing Ctrl+C in the terminal where it is has been started. The following message is displayed when the server has been started successfully and is ready to process analysis requests from the clients:

Remote analysis server started at port 55135

Note: the port can be configured using the SERVER_PORT server configuration setting.

While running the server provides the possibility to access the analyzer user guide text, information about the server status, and the built-in history server (if the corresponding configuration setting is enabled) using a web browser. To do this, you need to access the appropriate port of the running server via the HTTP protocol. For example, if the server is running on a machine with the IP address 192.168.1.10, to obtain brief information about the status of active analyses running remotely, you need to open the page http://192.168.1.10:8060/status. The port can be configured using the WEB_SERVER_PORT server configuration setting.

Starting and stopping remote analysis from a client

To request remote analysis use the following command on the client, where SERVER_ADDRESS represents the address of the running remote analysis server:

svace remote --host SERVER_ADDRESS analyze

When executing this command the client establishes connection with the remote analysis server and transfers all the data needed for analysis. The server then performs the analysis and returns the results to the client, which places them in the same way as if the analysis has been performed locally.

If the remote analysis server was configured to use a custom port then it can be specified via the svace remote --port PORT option, where PORT represents that port number. User credentials can be specified using the --login USER and --password PASSWORD options. If these options aren’t specified the client requests them from the input stream. The project for the remote analysis is specified with the --path PROJECT option. If it is not present then the name of the current working directory is taken as the project name.

When the remote analysis is started successfully a message containing an analysis task hash like the following is printed on the client’s output stream:

Starting remote analysis task 'c5466046fe87bbc828de45daf2b1c1d9f33396c4' on remote server.

If necessary, this hash can be used to prematurely terminate the remote analysis. This can be done by the user who initiated the remote analysis, as well as by a user with admin rights on the project with which this analysis was launched. To do this, use the following command, where TASK_ID represents the analysis task:

svace remote kill TASK_ID

The svace remote kill command can also be used without arguments, in which case the latest remote analysis launched by the client is stopped. Analysis task identifiers of the currently executing remote analyses can also be found on the remote analysis server status page (see the last paragraph of the Starting and stopping remote analysis server chapter).

Starting remote analysis server in quick mode

Remote analysis server can also be started in a quick mode, where user authentication is not required and analysis is only supported using a single project folder. To start this mode simply initialize the project folder using the svace init command and execute the svace server single-start command. To configure server settings you can use the svace server-config command as described in the Remote analysis server configuration settings chapter.

Integrated analysis results history server

Along with the remote analysis feature the server can provide a feature for storing and viewing analysis results. The integrated analysis results history server has a simple interface and limited capabilities compared to the standalone Svacer history server. To enable the integrated history server simply set the ALLOW_WEB_HISTORY_ACCESS setting to true in the remote analysis server configuration settings. After starting the remote analysis server the integrated history server will be accessible via the HTTP protocol through a web browser. For example, if the server is running on a machine with the IP address 192.168.1.10, the integrated analysis results history server can be accessed by opening the page http://192.168.1.10:8060 in a web browser and following the “Integrated history web interface” link. The port can be configured using the WEB_SERVER_PORT server configuration setting.

The integrated analysis results history server supports the same user and permission management and project management as remote analysis server. To access the server features the user has to authenticate first. In order to view analysis results for specific project the user needs at least read permission for that project, the modify permission allows to review the reported warnings by setting their status (one of Confirmed, Won't fix, False positive, Unclear of the default Undecided) and/or add a comment to the warning report.

The authenticated user has only access to those projects, that he has any permissions on. The project to work with, its branch and analysis results snapshot can be selected using the drop-down menus in the upper part of the server web-interface. When the needed analysis results snapshot is selected a list of warning types found in this snapshot is displayed on the left part of the page. The number of found warnings of a specific warning type is shown in parentheses after the corresponding warning type name. Left clicking on an item in this list displays all warnings from the selected analysis results snapshot of the corresponding type below. Clicking on an item in the warnings list makes the selected warning active and shows information about this warning below: warning message, location of the defect, trace and its review status along with added user comments. When a new warning report becomes active as well as when you click on the location links of the defect or elements of the trace in the active warning information panel, the source code of the corresponding location is displayed in the central part of the page. The source code panel supports IDE-like navigation: when you click with the left mouse button on an identifier in the source code (for example, a variable or a function name), all occurrences of that identifier are highlighted. Left-clicking on the highlighted identifier displays a context menu for that identifier. Using this menu, you can navigate to the main declaration or definition of the identifier as well as to the locations where it is used in the analyzed source code.

Warning types

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 MEMORY_LEAK warning type has a suppressed subtype MEMORY_LEAK.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 to 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.

.INSTANT (C#)

This subtype of null dereference checkers indicates that the value is dereferenced in the same expression where it is created.

.ANNOT (Java, Kotlin)

This subtype indicates that the issue is related to using a value in a call as an argument when corresponding function parameter has specific annotation.

Null pointer dereference

A null pointer dereference error occurs when a program attempts to dereference a memory address or access an object reference that has a null value (NULL in C, nullptr in C++, null in Java, etc). Such dereferencing is invalid because the null pointer (null reference) does not point to any valid memory location (to any valid object). When a null pointer dereference error occurs in a program, written in a manual memory management language, it typically results in a segmentation fault or access violation, causing the program to terminate abruptly. For programs in automated memory management languages in this case an exception is usually raised that can lead to unexpected behavior. Here and below a pointer dereference means literal pointer dereference for languages where it is applicable and object reference or another corresponding notion for languages without pointers.

DEREF_OF_NULL

Language Situation Severity Reliability Enabled
Java Quality Major High Yes
Visual Basic .NET Quality Critical High Yes
C/C++ Quality Critical High Yes
Kotlin Quality Normal High Yes
Python Quality Normal High Yes
C# Quality Critical High Yes

Related CWEs: CWE476, CWE690.

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

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

Example (C/C++)

if (dvfs->get_avail_governor)
    return -ENOTSUP;

return dvfs->get_avail_governor(res_name, avail_governor);

In the example above an accidentally inverted condition meant to return an error status in case the field dvfs->get_avail_governor is NULL but instead in this case the execution continues and leads to a null pointer dereference during the call in the return statement.

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 Yes
C/C++ Quality Critical Average Yes
Go Quality Critical Average Yes
Kotlin Quality Normal Average Yes
Python Quality Normal Average Yes

Related CWEs: CWE476.

This checker is similar to the DEREF_OF_NULL checker but reports cases when a pointer is assigned a null value under a condition and is then dereferenced under another condition which is not mutually exclusive with the first one.

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 Yes
C/C++ Quality Critical High Yes
Go Quality Critical High Yes
Kotlin Quality Normal High Yes
Python Quality Normal High Yes

Related CWEs: CWE476.

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

Example (C/C++)

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 Yes
C/C++ Quality Major Average Yes
Go Quality Major Average Yes
Kotlin Quality Normal Average Yes
Python Quality Normal Average Yes

Related CWEs: CWE476.

This checker finds situations where a pointer is assigned a null value and is subsequently dereferenced. In the situations reported by the checker the dereferenced pointer is not necessarily null but the pointer that was assigned a null value is always dereferenced.

Example (C/C++)

void ep_xfer_timeout(void *ptr)
{
    ep_xfer_info_t *xfer_info = NULL; // Assigned a null value
    int ep_num = 0;
    dctl_data_t dctl = {.d32 = 0 };
    gintsts_data_t gintsts = {.d32 = 0 };
    gintmsk_data_t gintmsk = {.d32 = 0 };

    if (ptr)
        xfer_info = (ep_xfer_info_t *) ptr;

    if (!xfer_info->ep) { // FLAW: Dereferenced a potentially null value
        DWC_ERROR("xfer_info->ep = %p\n", xfer_info->ep);
        return;
    }

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 Yes
C/C++ Quality Normal High Yes
Go Quality Normal High Yes
Kotlin Quality Normal High Yes

This checker detects issues where a null value is passed as a pointer argument to a function, where it is dereferenced under some conditions depending on external data (i.e. that can’t be guaranteed to always be false when the pointer is null).

Example (C/C++)

static char *
partial_die_full_name (struct partial_die_info *pdi,
               struct dwarf2_cu *cu)
{
...
      die = follow_die_ref (NULL, &attr, &ref_cu); // FLAW: Null value passed as 1st parameter
...

static struct die_info *
follow_die_ref (struct die_info *src_die, const struct attribute *attr,
        struct dwarf2_cu **ref_cu)
{
...
  if (!die)
    error (_("Dwarf Error: Cannot find DIE at %s referenced from DIE "
       "at %s [in module %s]"),
       sect_offset_str (sect_off), sect_offset_str (src_die->sect_off), // First parameter is dereferenced here
       objfile_name (cu->per_cu->dwarf2_per_objfile->objfile));
...

In the example above a NULL pointer value is passed to the follow_die_ref function as its first parameter src_die and this parameter is dereferenced if the value of an unrelated die local variable is not equal to NULL.

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 it is logically impossible for the condition under which the pointer is dereferenced in the called function to become true in the context of the call where a null value is assigned to that pointer. The checker emits a warning even if this possibility isn’t ruled out which leads to false positive reports in such cases.

DEREF_OF_NULL.EX.COND

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
C/C++ Quality Major Average Yes
Go Quality Major Average Yes

Related CWEs: CWE476.

This checker detects issues where a null value is passed as a pointer argument to a function, where it is dereferenced under some condition and analysis is able to prove that this condition can be true when the pointer is null.

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 No
C/C++ Quality Normal Unknown No
Kotlin Quality Minor Unknown No

Related CWEs: CWE476.

This checker finds potential null pointer dereferences that are less reliable than those found by the DEREF_OF_NULL checker. For example, this warning type is reported in Java code when an object with a nullable annotation is referenced without a null check. For Kotlin it is reported when a nullable type object is referenced using the non-null assertion operator !! also without a null check.

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

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown Yes
C/C++ Quality Normal Unknown Yes
Go Quality Normal Unknown Yes
Kotlin Quality Normal Unknown Yes
Python Quality Normal Unknown No

Related CWEs: CWE476, CWE690.

This checker finds situations where a pointer returned by a user-written function that returns a null value on some execution paths, is dereferenced without a check for being null.

If instead of a direct reference an assert-like function such as Java’s Objects.requireNonNull or Kotlin’s not-null assertion operator !! is used a DEREF_OF_NULL.RET.ASSERT warning subtype is reported.

If instead of a direct reference a potentially null value is used in a call as an argument and corresponding parameter has a @NotNull or @NonNull annotation then a DEREF_OF_NULL.RET.ANNOT warning subtype is reported.

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; // Null value may be returned

    return fill_from_bd(buf, MAX_SIZE);
}

void use(const char* url) {
    char* login = get_login(url, USE_PREFIX);
    int len = strlen(login); // FLAW: potentially null value is used without a check
    // ...
}

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: variable num value may be null after a getMaybeNull call and it is dereferenced by the call of a user defined function deref. Function possibleFix illustrates a possible fix: use safe implementation derefCorrect. Safe call of the times method is used inside the derefCorrect function.

DEREF_OF_NULL.RET.LIB

Language Situation Severity Reliability Enabled
Java Quality Major Unknown Yes
Visual Basic .NET Quality Normal Unknown Yes
C/C++ Quality Major Unknown Yes
Kotlin Quality Normal Unknown Yes
C# Quality Normal Unknown Yes

Related CWEs: CWE476, CWE690.

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

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

Examples from the C standard library:

Examples from the 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: if the RANDFILE environment variable is not set the getenv function returns a NULL value which is dereferenced by accessing the first character of the returned string s.

Function possible_fix illustrates a possible fix: check the result of the getenv call for being NULL before accessing the first character of the returned string s.

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: getter of a parentFile property may return null, which is referenced by a listFiles call.

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

See also

DEREF_OF_NULL.RET.LIB.PROC

Language Situation Severity Reliability Enabled
Visual Basic .NET Quality Normal Average Yes
C# Quality Normal Average Yes

Related CWEs: CWE476, CWE690.

This is a subtype of DEREF_OF_NULL.RET.LIB warning, and it is reported when a pointer returned by a library function may have null value and is dereferenced not explicitly, but within a function call.

See also

DEREF_OF_NULL.RET.LIB.MINOR

Language Situation Severity Reliability Enabled
Java Quality Minor Unknown Yes
C/C++ Quality Minor Unknown Yes
Kotlin Quality Minor Unknown Yes

This is a subtype of a DEREF_OF_NULL.RET.LIB warning type that is reported when a pointer returned by a library function may have null value, but this might happen in quite untypical usage conditions, so can be considered as an issue of minor probability.

Examples from the C standard library:

See also

DEREF_OF_NULL.RET.ALLOC

Language Situation Severity Reliability Enabled
C/C++ Quality Normal High No

Related CWEs: CWE476, CWE690.

This C/C++-specific checker finds situations where a pointer returned by a memory allocation function that returns a NULL value in error cases, such as malloc, is dereferenced without being checked for a NULL value.

Note: when the amount of allocated memory is a small constant, the probability of error cases becomes much lower and these situations are reported with a DEREF_OF_NULL.RET.ALLOC.MINOR warning subtype. The threshold can be set by a DEREF_OF_NULL_RET_ALLOC.SMALL_ARGUMENT configuration option and has a default value of 5000.

Example

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

See also

DEREF_OF_NULL.RET.STAT

Language Situation Severity Reliability Enabled
Java Quality Major High Yes
C/C++ Quality Major High Yes
Go Quality Major High Yes
Kotlin Quality Major High Yes

Related CWEs: CWE476.

This checker is a statistical version of DEREF_OF_NULL.RET that also reports when a potentially null pointer returned by a function is dereferenced without checking for being null. A report of this warning type is emitted when a pointer returned by a call to a function is dereferenced without a null check while for a number of other calls to the same function it is dereferenced only after a proper null check.

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 function get_ptr is called, the returned value is checked for not being NULL before its dereference, except for variable p5.

Possible fix is to uncomment the commented if operator, which checks the value of the p5 variable before its dereference too.

See also

DEREF_OF_NULL.DYN_CAST

Language Situation Severity Reliability Enabled
C/C++ Quality Critical High Yes

Related CWEs: CWE476.

This checker reports an issue when a pointer value is the result of a dynamic_cast C++ operator, it may be null and it is dereferenced without an appropriate check.

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

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown No
Kotlin Quality Normal Unknown No

This Java/Kotlin-specific checker reports an issue when a null value is used in a call as an argument and corresponding parameter has a @NotNull or @NonNull annotation.

Example (Java)

class Example {
    public void test_helper(@NotNull String s) {
    }

    public void test(String s) {
        if (s == null) 
            test_helper(s); // DEREF_OF_NULL.ANNOT
        else
            test_helper(s); // No warning, variable s is not null!
    }
}

Method test illustrates this defect - the value of variable s is checked for being null and then passed as an argument to the method test_helper call whose parameter has a NotNull annotation. In the else clause of the same if statement, where the value of variable s is definitely not null, using it as an argument of the call to the same test_helper method doesn’t cause this warning to be reported.

The issue can be fixed by removing the NotNull annotation on the parameter of test_helper method in this case.

See also

DEREF_OF_NULL.ANNOT.CONST

Language Situation Severity Reliability Enabled
Java Quality Major Average No
Kotlin Quality Normal Low No

This checker detects situations where literal NULL value or a variable initialized by literal NULL value is used as an argument in a function call and thus passed to a parameter which has a NotNull or NonNull annotation.

Example (Java)

class Example {
    public void test_helper(@NotNull String s) {
    }

    public void test1(int i) {
        String s = null;
        test_helper(s); // DEREF_OF_NULL.ANNOT.CONST
    }

    public void test2() {
        test_helper(null); // DEREF_OF_NULL.ANNOT.CONST
    }
}

Methods test1 and test2 exemplify the two possible ways for this defect to occur. In the former case, variable s is initialized by literal NULL value and then passed to the parameter of method test_helper which has a NotNull annotation. In the latter case, literal NULL is passed directly to the same parameter of test_helper method.

Possible fix in both cases is to remove the NotNull annotation on the parameter of test_helper method.

False positives

Due to known issue, this checker can produce false positive findings in Kotlin projects where a call to typeOf function from Kotlin reflection API is used as an argument in a function call and passed to a parameter with a non-nullable type.

DEREF_OF_NULL.ANNOT.STRICT

Language Situation Severity Reliability Enabled
Java Quality Normal Very high No
Kotlin Quality Minor Very high No

This checker finds issues where a value which bears a Nullable annotation is used as an argument in a function call and thus passed to a parameter with NotNull or NonNull annotation.

Example (Java)

class Example {
    @Nullable private String s
    public void test_helper(@NotNull String s) {
    }

    public void test1(@Nullable String s) {
        test_helper(s); // DEREF_OF_NULL.ANNOT.STRICT
    }

    public void test2() {
       test_helper(this.s); // DEREF_OF_NULL.ANNOT.STRICT
    }
}

The defect is shown in methods test1 and test2. In the former, a parameter with Nullable annotation is passed to parameter with NotNull annotation in a call to method test_helper. In the latter, a field declared with Nullable annotation is similarly passed to the same parameter of method test_helper.

Possible fixes include removal of either annotation or both of them, or placing calls to test_helper method under if statements that check that the value passed to the parameter with NotNull annotation is in fact not NULL.

False positives

Due to known issue, false positive warnings of this type can be issued where a value which bears Nullable annotation is used as an argument in a call to another function of specific form. Namely, the return value of this function should have NonNull or NotNull annotation while the parameter receiving the value with Nullable annotation should also have Nullable annotation. Moreover, the function’s code should return the value of that parameter, while it’s free to perform any operations on that parameter prior to that.

DEREF_OF_NULL.ANNOT.COND

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown No
Kotlin Quality Normal Unknown No

This checker finds situations where NULL value is passed to a function and later inside that function, under conditions independent of this value, it is used as an argument in a call to another function, whereby it is passed to a parameter which has NotNull or NonNull annotation.

Example (Java)

class Example {
    public void test_helper(@NotNull String s) {
    }

    public void test(String s1, String s2) {
        if (s1 != null)
            test_helper(s1);
        else
            test_helper(s2);
    }

    public void foo() {
       test(null, null); // DEREF_OF_NULL.ANNOT.COND
    }
}

The defect is illustrated by method foo - NULL value passed in the call to test method to parameter s2 may later be used in the call to test_helper method and thus be passed to a parameter with NotNull annotation, depending on the value passed to parameter s1 of test method.

The issue can be fixed by putting the call to test_helper method under condition that checks that the value passed to that call is not NULL. Alternatively, the NotNull annotation on the parameter of test_helper method can be removed.

DEREF_OF_NULL.ANNOT.ASSIGN

Language Situation Severity Reliability Enabled
Java Quality Major Average No
Kotlin Quality Normal Average No

This checker produces warnings if some variable is first assigned NULL value and later on it is used as an argument in a function call and thus passed to a parameter with NotNull or NonNull annotation. Note that the value of the variable may be changed along some execution paths leading to the call site; the important point is that the paths that leave the NULL value intact must lead to the call site as well.

Example (Java)

class Example {
    private String str;

    public void test_helper(@NotNull String s) {
    }

    public void test(boolean cond) {
        String s = null;
        if (cond)
            s = "string";

        test_helper(s); // DEREF_OF_NULL.ANNOT.ASSIGN
    }

    public NotNullAnnot(boolean cond) {
        if (cond)
           str = "string";

        test_helper(str); // DEREF_OF_NULL.ANNOT.ASSIGN
    }
}

Method test and the constructor of Example class exemplify the defect. In the former case, a local variable s is first assigned a NULL value which may be changed depending on the parameter cond and then is used in a call to method test_helper whereby it is passed to parameter s which has NotNull annotation. In the latter case, uninitialized field str which has default NULL value may be changed depending on the constructor parameter cond and then similarly is passed to the same parameter of method test_helper.

Possible fix is to put the call to test_helper method under if statement checking that the value being passed to it is not NULL. Alternatively, the NotNull annotation on the parameter of test_helper method can be removed.

DEREF_OF_NULL.ANNOT.EX

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
Kotlin Quality Normal Unknown No

This checker detects situations where a value that may be NULL under some condition is used as an argument in a function call and thus passed to a parameter with NotNull or NonNull annotation under another condition which is not mutually exclusive with the first condition.

Example (Java)

class Example {
    private int x;

    public void test_helper(@NotNull String s) {
    }

    public void test1(Object o) {
        String s = null;
        if (o instanceof String)
            s = (String) o;

        if (x > 0)
            test_helper(s); // DEREF_OF_NULL.ANNOT.EX
    }

    public void test2(Object o, boolean b) {
        String s = null;
        if (o instanceof String)
            s = (String) o;
        else
            b = false;

        if (b)
            test_helper(s); // No warning here, as b is false when s is null
    }
}

The defect is shown in methods test1 and test2. In the first case, the value of local variable s is NULL under condition which is totally independent of the condition under which that variable is used in a call to test_helper method and passed to the parameter which has NotNull annotation. Therefore, the warning is emitted in this case. By contrast, in the second case the condition under which the value of s is NULL and condition under which s is passed to the same parameter of test_helper method are mutually exclusive, thus the issue is not detected.

Possible fix in the first case is either to remove the NotNull annotation on the parameter of test_helper method or augment the condition under which test_helper method is called so that it also checks that the value of s at the call site is not NULL.

DEREF_OF_NULL.ANNOT.EX.COND

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
Kotlin Quality Major Unknown No

This checker reports issues where a NULL value is used as an argument in a function call under certain conditions and thus passed to a parameter with NotNull or NonNull annotation.

Example (Java)

class Example {
    public void func(@NotNull String s) {
    }

    void passIfTrue(String str, int f1) {
        if (f1 > 0)
            func(str);
    }

    void test(int f1, int f2) {
        String str = null;
        passIfTrue(str, f1);
    }
}

Method test exemplifies the defect - variable str is assigned NULL value and then used in a call to method passIfTrue whereby, if the other argument passed in the same call is positive, that NULL value is passed to the parameter of method func which bears NotNull annotation.

Possible fixes include removal of NotNull or NonNull annotation on the parameter of func method, extending the condition in passIfTrue method to ensure the argument used in the call to func method is not NULL or placing the call to passIfTrue method under condition that excludes using NULL value as the first argument and a positive value as the second argument of that call.

DEREF_AFTER_NULL

Language Situation Severity Reliability Enabled
Java Quality Major High Yes
Visual Basic .NET Quality Major Average Yes
C/C++ Quality Critical High Yes
Go Quality Critical High Yes
Kotlin Quality Normal High Yes
Python Quality Normal High Yes
C# Quality Major Average Yes

Related CWEs: CWE476, CWE690.

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

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

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

A less reliable DEREF_AFTER_NULL.RET subtype of this warning type is emitted if the checked for null pointer value is returned from a function before the dereference.

C# warnings can have one of these subtypes: .ARGUMENT and .ARGUMENT.INSTANT. There is no DEREF_AFTER_NULL.EX for C# because DEREF_AFTER_NULL is already path-sensitive.

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

Function example illustrates the defect: Variable age is initialized from the arg parameter using a safe call operator, handling the case when the parameter is null, but afterwards the arg parameter is again dereferenced using a not-null assertion operator to get the field name value without a null check this time.

Function possibleFix illustrates a possible fix by ensuring non-null value of parameter arg before getting the value of its filed name using a scope function let.

Example (Java)

Sometimes it is reasonable to handle null dereference issues in JVM languages by catching NullPointerException. If null dereference issue takes place inside a try-block that handles NullPointerException (or any parent exception), then warning is not emitted.

public class CatchNpe {
    public static void test(Object x) {
        if (x == null) {
            System.out.println("NULL");
        }
        try {
            x.toString(); // DEREF_AFTER_NULL is not emitted
        } catch (NullPointerException e) {
            // ...
        }
    }
}

Also, you can use configuration variable NULL_DEREFERENCE.TREAT_THROWS_NPE_AS_PROPER_HANDLING to treat throws NullPointerException (or any parent exception) in method’s signature as proper null dereference handling. If this variable is set to true, warnings in those methods won’t be emitted. By default, variable is set to false and can be changed using svace config tool.

// > svace config NULL_DEREFERENCE.TREAT_THROWS_NPE_AS_PROPER_HANDLING true

public class ThrowsNpe {
    public static void test(Object x) throws NullPointerException {
        if (x == null) {
            System.out.println("NULL");
        }
        x.toString(); // DEREF_AFTER_NULL is not emitted
    }
}

False positives

A common cause of this checker false positives is when the execution paths taken when a variable is null contain program termination before its dereference but the analyzer doesn’t have enough data to detect it. For example if calling external functions that abort the program:

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

If instead of an unknown my_exit, a standard program termination function exit is called, no false warnings is emitted.

See also

DEREF_AFTER_NULL.EX

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
C/C++ Quality Critical Average Yes
Go Quality Critical Average Yes
Kotlin Quality Normal Average Yes
Python Quality Normal Average Yes

Related CWEs: CWE476.

This checker is a version of the DEREF_AFTER_NULL with path sensitivity. Unlike DEREF_AFTER_NULL, it produces a warning when a variable can be dereferenced after comparison against a null value and there exist a combination of the input parameters and an execution path where both of these events can happen.

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

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

DEREF_AFTER_NULL.COND

Language Situation Severity Reliability Enabled
Java Quality Major High No
C/C++ Quality Major High No

This checker detects issues where a pointer or an object reference is compared against a null value (which indicates that it may be null) and then passed to a function as a parameter, where it is conditionally dereferenced.

Note: the checker does not use smt-solver. It uses heuristics to define, where function may dereference passed null pointer.

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 lack or miss information that the condition of the dereference within the called function can’t be true in the context where the function is called, in which case the null dereference can actually never happen during this call but the null check still may be useful for calls from the other contexts.

DEREF_AFTER_NULL.EX.COND

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
C/C++ Quality Major Average Yes
Go Quality Major Average Yes

This checker detects issues where a pointer or an object reference is compared against a null value (which indicates that it may be null) and then passed to a function as a parameter, where it is conditionally dereferenced.

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 lack or miss information that the condition of the dereference within the called function can’t be true in the context where the function is called, in which case the null dereference can actually never happen during this call but the null check still may be useful for calls from the other contexts.

DEREF_AFTER_NULL.LOOP

Language Situation Severity Reliability Enabled
Java Quality Normal Average Yes
C/C++ Quality Normal Average Yes

Related CWEs: CWE476.

This checker detects issues where a pointer or an object reference is compared against a null value (which indicates that it may be null) inside of a loop condition and then it is dereferenced after the loop.

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

Occasionally when loop condition is composed of several subconditions the assumption that the comparison implies a null value might not be correct for the path where the warning is reported due to some additional implicit preconditions.

DEREF_AFTER_NULL.MIGHT

Language Situation Severity Reliability Enabled
Java Suppressed Major Average Yes
C/C++ Suppressed Major Average Yes
Go Suppressed Major Average Yes

Related CWEs: CWE476.

This checker detects issues where a pointer or an object reference is compared against a null value (which indicates that it may be null), and then dereferenced on a path where the program execution might be terminated, but the analysis is not able to ensure whether it avoids null dereference or not.

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.

DEREF_AFTER_NULL.INL

Language Situation Severity Reliability Enabled
Kotlin Suppressed Normal Unknown No

A subtype of DEREF_AFTER_NULL, where a null check happens inside of an inlined code.

Example (Kotlin)

data class Person(val id: Int, val name: String)

fun example(id: Int, persons: List<Person>): String {
    return persons.find { it.id == id }!!.name
}

fun possibleFix(id: Int, persons: List<Person>): String? {
    return persons.find { it.id == id }?.name
}

Function example illustrates the defect: higher-order function find may return null, if there was no entry with the given id, so !! operator will fail with an exception.

NULL_AFTER_DEREF

Language Situation Severity Reliability Enabled
Java Quality Major High Yes
Visual Basic .NET Quality Major Average Yes
C/C++ Quality Major High Yes
Kotlin Quality Normal High Yes
Python Quality Normal High Yes
C# Quality Major Average Yes

Related CWEs: CWE476, CWE690.

The checker finds situations where first, a pointer or an object reference is dereferenced, and then it is compared against a null value (which indicates that it may be null). This means that either the null check is too late and the potential null dereference has already happened or it’s not needed at all since it’s always false.

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

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 location in the warning report points to a function that returns the pointer. Such warnings reflect an observation that the function that produced the pointer unconditionally dereferences it before the return and so the returned pointer can’t be null. This means that the null check in the callee is redundant. In most cases this happens due to incorrect assumptions about the called function return value (e.g. the check is added “just in case”) but at the same time there are various situations when such check can be beneficial and shouldn’t be removed.

See also

NULL_AFTER_DEREF.RET

Language Situation Severity Reliability Enabled
Java CodingStyle Minor Average No
C/C++ CodingStyle Minor Average No

The checker finds situations where first, a pointer or an object reference is obtained as a result of a function call, and then is compared against a null value (which indicates that it is considered to possibly be null), but it has already been dereferenced in the function that produced it. This means that the null check in the callee is redundant. In most cases this happens due to incorrect assumptions about the called function return value (e.g. the check is added “just in case”) but at the same time there are various situations when such check can be beneficial and shouldn’t be removed.

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

NULL_AFTER_DEREF.ASSERT

Language Situation Severity Reliability Enabled
Kotlin Quality Normal High Yes

This is a Kotlin-specific checker similar to NULL_AFTER_DEREF which issues warnings if a variable has the non-null assertion operator (!!) applied to it prior to being compared to null.

Example (Kotlin)

fun test(s: String?): Int? {
    val ss = s!!
    return ss?.length
}

Function test shows the defect - parameter s of nullable type has the not-null assertion operator (!!) applied. If the value of s (as passed by caller) is null, NullPointerException will be thrown and execution won’t proceed to the next line. Thus, the null-check via null-safe member access operator (?.) on the next line is unnecessary.

See also

NULL_AFTER_DEREF.UNSAFE_CAST

Language Situation Severity Reliability Enabled
Kotlin Quality Normal High Yes

This Kotlin-specific checker detects situations where a variable has the unsafe cast operator (as operator with non-nullable target type) applied to it prior to being compared to null.

Example (Kotlin)

fun test(s: String?): Int? {
    val ss = s as String
    return ss?.length
}

Function test exemplifies the defect - parameter s of nullable type has the unsafe cast operator applied. If the value of s (as passed by caller) is null, ClassCastException will be thrown and execution won’t proceed to the next line. Thus, the null-check via null-safe member access operator (?.) on the next line is unnecessary.

See also

COMPARE_LOCAL_ADDR

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Average Yes

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

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 Yes
C/C++ Quality Critical Average Yes
Scala Quality Critical Average Yes
Go Quality Critical Average Yes
Python Quality Critical Average Yes

Related CWEs: CWE121, CWE122, CWE129, CWE194, CWE195, CWE20, CWE400, CWE606, CWE789.

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

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

This warning has the following subtypes:

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 Yes
C/C++ Suppressed Major Average Yes
Scala Suppressed Major Average Yes
Go Suppressed Major Average Yes

Related CWEs: CWE121, CWE122, CWE129, CWE194, CWE195, CWE20, CWE400, CWE606, CWE789.

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

Example

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

    usleep(count);
}

TAINTED_INT.COND

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
C/C++ Quality Major Unknown No
Scala Quality Major Unknown No
Go Quality Major Unknown No

Related CWEs: CWE121, CWE122, CWE129, CWE194, CWE195, CWE20, CWE400, CWE606, CWE789.

.COND variant of TAINTED_INT, which finds situations where tainted value is used in an unsafe operation, where tainted value is passed to function, which uses it only on some execution paths.

Example (Go)

package main

import (
    "flag"
    "strconv"
)


func allocIf(size, mode int)  {
    if mode > 10 {
        return
    }
    allocSlice(size)
}

func allocSlice(size int)[]string {
    var s = make([]string, size)
    return s
}

func main() {
    sizeArg := flag.String("size", "0", "")
    modeArg := flag.String("size", "0", "")
    flag.Parse()
    size, _ := strconv.Atoi(*sizeArg)
    mode, _ := strconv.Atoi(*modeArg)

    allocIf(size, mode) //TAINTED_INT.COND
}

TAINTED_INT.MIGHT.COND

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
C/C++ Quality Major Unknown No
Scala Quality Major Unknown No
Go Quality Major Unknown No

Related CWEs: CWE121, CWE122, CWE129, CWE194, CWE195, CWE20, CWE400, CWE606, CWE789.

It is variant of TAINTED_INT with features of both .COND and .MIGHT types. The detector finds situations where tainted value is used in an unsafe operation, and it is used in an operation where uncontrolled value may cause problems. The value is tainted only on some paths, and it is uses also only on some paths.

Example (Go)

package main

import (
    "flag"
    "strconv"
)


func allocIf(size, mode int)  {
    if mode > 10 {
        return
    }
    allocSlice(size)
}

func allocSlice(size int)[]string {
    var s = make([]string, size)
    return s
}

func main() {
    sizeArg := flag.String("size", "0", "")
    modeArg := flag.String("size", "0", "")
    flag.Parse()
    size, _ := strconv.Atoi(*sizeArg)
    mode, _ := strconv.Atoi(*modeArg)

    if mode < 20 {
        //now for some pathes size is not controlled by user
        size = 10
    }

    allocIf(size, mode) //TAINTED_INT.MIGHT.COND
}

TAINTED_INT.CTYPE

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown Yes

Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE606.

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

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

Language Situation Severity Reliability Enabled
C/C++ Suppressed Major Unknown No

Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE606.

.MIGHT variant of TAINTED_INT.CTYPE, where value is tainted only on some execution paths.

Example

char ch = '0';

void int_type(int val) {
    if (val < 0) {
        scanf("%d", &val);        
    }
 
    if (isdigit(val)) // Here the program may crash if val < 0; TAINTED_INT.CTYPE.MIGHT is emitted.
        ch = (char) val;
}

TAINTED_INT.LOOP

Language Situation Severity Reliability Enabled
Java Quality Critical High Yes
C/C++ Quality Critical High Yes
Scala Quality Critical High Yes

Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE400, CWE606.

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

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

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 Yes
C/C++ Suppressed Critical Average Yes
Scala Suppressed Critical Average Yes

Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE400, CWE606.

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

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 Yes
C/C++ Quality Major Average Yes
Scala Quality Major Average Yes

A subtype of TAINTED_INT, where the tainted value is used as a loop step, and thus may cause the loop to be infinite.

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

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 Yes
C/C++ Quality Major Average Yes
Scala Quality Major Average Yes

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

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 Yes
C/C++ Quality Critical Average Yes
Scala Quality Critical Average Yes
Go Quality Critical Average Yes
Kotlin Quality Critical Average Yes
Python Quality Critical Average Yes

Related CWEs: CWE121, CWE122, CWE124, CWE126, CWE127, CWE129, CWE194, CWE195, CWE20, CWE606.

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

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

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
Java Suppressed Major Unknown Yes
C/C++ Suppressed Major Unknown Yes
Scala Suppressed Major Unknown Yes
Python Suppressed Major Unknown Yes

Related CWEs: CWE121, CWE122, CWE124, CWE127, CWE129, CWE194, CWE195, CWE20, CWE606.

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

Example

#include <cstdio>

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

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

TAINTED_ARRAY_INDEX.EX

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
C/C++ Quality Critical Average Yes
Scala Quality Critical Average Yes
Go Quality Critical Average No
Python Quality Critical Average Yes

Related CWEs: CWE121, CWE122, CWE126, CWE129, CWE606.

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

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 Yes
C/C++ Quality Critical High Yes
Scala Quality Critical High Yes

Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE606.

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

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

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 Yes
C/C++ Suppressed Major High Yes
Scala Suppressed Major High Yes

Related CWEs: CWE121, CWE122, CWE129, CWE20, CWE606.

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

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 Very high Yes
C/C++ Quality Critical Very high Yes
Scala Quality Critical Very high Yes
Go Quality Critical Very high Yes
Python Quality Critical Very high Yes

Related CWEs: CWE120, CWE20.

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 Yes
C/C++ Suppressed Critical High Yes
Scala Suppressed Critical High Yes

Related CWEs: CWE120, CWE20.

.MIGHT variant of TAINTED_PTR, which find situations 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_PTR.COND

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
C/C++ Quality Major Unknown No
Scala Quality Major Unknown No
Go Quality Major Unknown No

Related CWEs: CWE20.

It is .COND subtype of TAINTED_PTR for situations where called function pass tainted value to critical operations only for some paths.

Example

const char* env;
char buf[100];

#define COPY_MODE 10

void copyIfNeeded(const char*ptr, int mode) {
    if (mode == COPY_MODE)
        strcpy(buf, ptr);
}

char* getEnv() {
    return getenv("CODE10");
}

void test(int mode) {
    env = getEnv();

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

TAINTED_PTR.MIGHT.COND

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
C/C++ Quality Major Unknown No
Scala Quality Major Unknown No
Go Quality Major Unknown No

Related CWEs: CWE20.

It is subtype of TAINTED_PTR with both features .MIGHT and .COND. So this checker emits situations where value is tainted only on some paths and it is passed to function, which uses this value in critical operation not on all paths.

Example

const char* env;
char buf[100];

#define COPY_MODE 10

void copyIfNeeded(const char*ptr, int mode) {
    if (mode == COPY_MODE)
        strcpy(buf, ptr);
}

char* getEnv() {
    return getenv("CODE10");
}

void test(int mode) {
    env = getEnv();
    if (mode<0)
        env = "-";

    copyIfNeeded(env, mode);
}

TAINTED.SPRINTF

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Unknown Yes

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

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 Yes
C/C++ Quality Critical High Yes
Scala Quality Critical High Yes
Python Quality Critical High Yes

Related CWEs: CWE120, CWE134, CWE20.

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

Example

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

TAINTED_PTR.COOKIE

Language Situation Severity Reliability Enabled
Java Quality Major Unknown Yes

Related CWEs: CWE565.

This checker finds situations where value of a web cookie is used in a condition, thus controlling the execution flow of the application. Cookie values can be easily stolen or modified by malicious users, therefore using them to control execution flow makes application vulnerable to cookie spoofing attack.

Example

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

class Example {
  public boolean login(
      @RequestParam String username,
      @RequestParam String password,
      @CookieValue(value = COOKIE_NAME, required = false) String cookieValue) {

    if (StringUtils.isEmpty(cookieValue)) {
      return credentialsLoginFlow(username, password, response);
    } else {
      return cookieLoginFlow(cookieValue);
    }
  }
}

In function login, parameter cookieValue is bound to a value of web cookie via @CookieValue annotation. This parameter is used to direct user-authorization flow via emptiness test. Thus, if the user manipulates the value of this cookie, he can bypass authorization and gain control over application and the data it uses.

TAINTED_PTR.COOKIE.INSECURE

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No

A subtype of TAINTED_PTR.COOKIE which indicates that a web cookie carrying sensitive information is passed to web response without marking it as HttpOnly. Web cookies without HttpOnly mark are exposed to client-side scripts and sensitive information they carry can thus be stolen.

Example

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

class Example {
    public static void bad(HttpServletResponse response, String sessionToken) {
        Cookie cookie = new Cookie("token", sessionToken);
        response.addCookie(cookie);
    }

    public static void good(HttpServletResponse response, String sessionToken) {
        Cookie cookie = new Cookie("token", sessionToken);
        cookie.setHttpOnly(true);
        response.addCookie(cookie);
    }
}

In function bad, a web cookie is created with value that may carry sensitive information (due to name of variable sessionToken passed to constructor). This cookie is then added to web response without being marked as HttpOnly, thus making its sensitive value accessible by client-side scripts.

By contrast, in function good the sensitive value of web cookie is secured by calling setHttpOnly method with true parameter prior to adding the cookie to web response. This prevents possible theft of sensitive information by blocking access of client-side scripts to this cookie.

TAINTED_PTR.SSRF

Language Situation Severity Reliability Enabled
Java Quality Major Unknown Yes

Related CWEs: CWE918.

This checker finds situations where a web request parameter is used as URL (or a part thereof) for accessing an external resource, thus creating a possibility of server-side request forgery attack.

Example

import java.util.List;
import java.util.ArrayList;
import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import org.springframework.web.bind.annotation.RequestParam;

class Example {
    List<String> links(@RequestParam String url) throws IOException {
        List<String> result = new ArrayList<String>();
        Document doc = Jsoup.connect(url).get();
        Elements links = doc.select("a");

        for (Element link : links) {
            result.add(link.absUrl("href"));
        }

        return result;
    }
}

In function links, parameter url is bound to web request parameter via @RequestParam annotation. This parameter is used as URL to fetch a remote document, which is parsed and used to construct the function’s return value. As web request parameters are controlled by the user, the latter can exploit this function to import unexpected data into application and use the outcome to his advantage.

TAINTED_PTR.JWT

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No

Related CWEs: CWE347.

This checker detects creation of JSON Web Token (JWT) without proper cryptographic signature, which opens a possibility of various impersonation attacks.

Example

import io.jsonwebtoken.Jwts;

public String bad_encode() {
    return Jwts.builder()
               .setSubject(USER_LOGIN)
               .compact();
}

public String good_encode() {
    return Jwts.builder()
               .setSubject(USER_LOGIN)
               .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
               .compact();
}

Function bad_encode exemplifies construction of JWT without cryptographic signature. Class io.jsonwebtoken.JwtBuilder used in creation of token has several signWith methods which enable the user to properly sign the token being created, as shown in function good_encode.

TAINTED_PTR.SQL_INJECTION

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
C/C++ Quality Major Unknown No
Go Quality Major Unknown No
Python Quality Major Unknown No

Related CWEs: CWE89.

This checker detects situations where a variable whose value is obtained from external source is used in constructing an SQL query without proper validation. That query is later run on a database, thus opening a possibility of SQL injection attack.

Example

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

class Example {
    public void bad(Connection conn) throws SQLException {
        String str = System.getenv("USERNAME");
        Statement st = conn.createStatement();
        st.executeQuery("SELECT body FROM proc WHERE name=" + str);
    }

    public void safe(Connection conn) throws SQLException {
        String str = System.getenv("USERNAME");
        PreparedStatement st = conn.prepareStatement("SELECT body FROM proc WHERE name=?");
        st.setString(1, str);
        st.executeQuery();
    }
}

In function bad, the value of variable str is assigned by reading environment variable USERNAME which can be controlled externally. It is then concatenated with the rest of the query, which is run using Statement object which does no validation on the query, thus making injection of malicios SQL commands possible.

By contrast, in function safe the data read from environment variable USERNAME are inserted into query using setString method of PreparedStatement object. This method validates the resulting query, thus eliminating the possibility of SQL injection attacks.

TAINTED_PTR.LDAP_INJECTION

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
C/C++ Quality Major Unknown No
Go Quality Major Unknown No
Python Quality Major Unknown No

Related CWEs: CWE90.

This checker finds situations where a variable whose value is obtained from external source is used in constructing filter for LDAP query without proper validation. Running this query later creates possibility of LDAP injection attack.

Example

import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.InitialLdapContext;

class BadExample {
    public void bad() throws NamingException {
        String ldapAccountToLookup = System.getenv("ACCTNAME");

        DirContext ctx = new InitialLdapContext(null, null);
        String searchFilter = "(&(objectClass=user)(sAMAccountName=" + ldapAccountToLookup + "))";

        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        ctx.search("dc=ad,dc=my-domain,dc=com", searchFilter, searchControls);
    }
}

In functon bad of class BadExample, the value of variable ldapAccountToLookup is assigned by reading environment variable ACCTNAME which can be controlled externally. It is then concatenated with the rest of filter string and used without any validating in running an LDAP query, thus making injection of malicious LDAP commands possible.

import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.directory.ldap.client.api.search.FilterBuilder;
import org.apache.directory.api.ldap.model.message.SearchScope;

class GoodExample {
    public void good() {
        String ldapAccountToLookup = System.getenv("ACCTNAME");
        String searchFilter = FilterBuilder.and(
                                  FilterBuilder.equal("objectClass", "user"),
                                  FilterBuilder.equal("sAMAccountName", ldapAccountToLookup))
                              .toString();

        LdapNetworkConnection conn = new LdapNetworkConnection();
        conn.search("dc=ad,dc=my-domain,dc=com", searchFilter, SearchScope.SUBTREE);
    }
}

By contrast, in function good of class GoodExample the data read from environment variable ACCTNAME are inserted into filter string using methods of FilterBuilder class which validate the resulting filter. Running LDAP query with such filter will not incur any risk of LDAP injection attacks.

Note that class GoodExample uses Apache LDAP API because JNDI API used by class BadExample lacks functionality for constructing safe filters for LDAP queries. It’s a good practice to always use API that has such functionality when working with LDAP queries.

Buffer overflow

Buffer overflow occurs when a program writes more data to a buffer (a temporary storage area in memory) than it can hold. This can lead to the excess data overwriting adjacent memory locations, potentially corrupting or altering the data stored there.

Attackers can exploit this vulnerability by deliberately crafting input data that exceeds the buffer’s capacity, allowing them to overwrite critical data, manipulate program behavior, or even execute arbitrary code. This can lead to a range of security issues, including crashes, denial of service attacks, and remote code execution.

To mitigate buffer overflow vulnerabilities, developers should use secure coding practices such as input validation and bounds checking.

In memory-safe programming languages, such as Java, the corruption is prevented by built-in memory management and bounds checking. However, if a program tries to access an element in an array, list, or other data structure using an index that is outside the valid range of indices for that data structure, a runtime exception is thrown, which can lead to unexpected changes in program behaviour including denial of service.

STATIC_OVERFLOW

Language Situation Severity Reliability Enabled
Java Quality Major Low Yes
C/C++ Quality Critical Low Yes
Go Quality Critical Low Yes

Related CWEs: CWE119, CWE121, CWE124, CWE127, CWE194, CWE195.

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

Example

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

DYNAMIC_OVERFLOW

Language Situation Severity Reliability Enabled
Java Quality Major High Yes
C/C++ Quality Critical High Yes
Go Quality Critical High Yes
Python Quality Critical High Yes

Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE127, CWE194, CWE195.

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

Example

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

TAINTED_DYNAMIC_OVERFLOW

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
C/C++ Quality Critical Unknown No
Scala Quality Critical Unknown No
Go Quality Critical Unknown No

The detector finds situations where a memory at the pointer was dynamically allocated with tainted size, and after this memory was accessed without checking. This may lead to a buffer overflow.

Example (C)

void func(FILE*f) {
    int n;
    // Variable 'n' is tainted because it given by external source
    fscanf(f, "%d", &n);

    // Pointer 'ptr' created using tainted data
    char* ptr = malloc(n);

    // Buffer access may lead to buffer overflow
    ptr[30] = 0;
}

DYNAMIC_OVERFLOW.EX

Language Situation Severity Reliability Enabled
Java Quality Major Very low Yes
C/C++ Quality Critical Very low Yes
Python Quality Critical Very low Yes

Related CWEs: CWE119, CWE121, CWE122, CWE126.

Path sensitive version of the DYNAMIC_OVERFLOW. It checks if a path exists where dynamic array is accessed at a constant index outside its range.

Example

void buf_overflow(int a) {
    char *buf;
    if (a > 0)
        buf = (char *) malloc(1024);
    else
    buf = (char *) malloc(512);

    if (a > 0)
        buf[1024] = 0;
    else
    buf[511] = 0;

    free(buf);
}

STATIC_OVERFLOW.LOCAL

Language Situation Severity Reliability Enabled
Java Quality Major Low Yes
C/C++ Quality Critical Low Yes

Related CWEs: CWE119, CWE121, CWE124, CWE127, CWE194, CWE195.

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

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 Very low No
C/C++ Quality Critical Very low No

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

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 No
C/C++ Quality Critical Unknown No
Go Quality Critical Unknown No

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

Example

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

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

STATIC_OVERFLOW.SCANF

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Very high Yes

Related CWEs: CWE121.

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

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

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
Visual Basic .NET Quality Major Unknown Yes
C/C++ Quality Critical Unknown No
C# Quality Major Unknown Yes

Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE127, CWE194, CWE195.

For C/C++ this checker detects usages of memcpy, strcpy and similar standard library functions that may write beyond the end of destination buffer.

For C# this checker detects potential overflows of allocated unmanaged memory buffers on certain execution paths. It reports a warning when a buffer may be accessed (read or written) beyond its right bound. The checker reports a warning if the allocation expression is known and it can’t prove that the access is safe.

Example (C/C++)


void buffer_overflow(char*p, int n) {
    int buf[10];

    // Last copied byte will be written outside of buf
    memcpy(p, buf + 3, sizeof(int) * 7 + 1);
}

In the example above memcpy will write one byte after the end of array buf, corrupting data on stack.

Example (C#)

using System;
using System.Runtime.InteropServices;
IntPtr hglobal = Marshal.AllocHGlobal(5); // ℹ️〔function System.Runtime.InteropServices.Marshal.AllocHGlobal(int) allocated buffer of size 5〕

int[] buffer = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Marshal.Copy(buffer, 0, hglobal, 1);
Marshal.Copy(buffer, 0, hglobal, 2); // ⚠️〔BUFFER_OVERFLOW Writing 2 elements of type int into buffer hglobal can exceed its size (5 bytes)〕
Marshal.FreeHGlobal(hglobal);

BUFFER_OVERFLOW.EX

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
C/C++ Quality Critical Average Yes
Go Quality Critical Average No
Python Quality Critical Average Yes

Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125, CWE126.

This checker detects potential overflows of fixed-size arrays on certain execution paths. It reports a warning when an array may be accessed beyond its right bound. Out-of-bounds array access leads to Undefined Behaviour (C/C++), Runtime Exception (Java), or run-time panic (Go).

enum Type {
  TYPE_ONE,
  TYPE_TWO,
  TYPE_THREE,
  TYPE_INVALID
};

enum Type get_type (int data) {
     if (data == 100)
         return TYPE_ONE;

     if (data == 200)
         return TYPE_TWO;

     return TYPE_INVALID;
}


const char* get_name (const char *n[], enum Type type) {
    return n[(int)type];
}

const char* example (int data, int flag) {
  const char* names[3] = {"First", "Second", "Third"};
  enum Type type = flag ? TYPE_THREE : get_type(data);

  // overflow in `get_name` occurs when type is TYPE_INVALID
  return get_name(names, type);
}

BUFFER_OVERFLOW.LEN

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Unknown No

This checker detects a buffer overflow when return value of strlen, read or similar functions used in buffer access offset calculation is subtracted from, resulting in possible negative offset:

void test1(const char* buf) {
    int len = strlen(buf);

    // Access at -1 if buf = { '\0' }
    if(buf[len - 1] == 'a') {
        return;
    }
}

In this example if the string buf contains only a \0 character (and thus is of zero length) an access outside of the buffer will occur.

BUFFER_OVERFLOW.LIB.EX

Language Situation Severity Reliability Enabled
Java Quality Major Low No
C/C++ Quality Critical Low No
Go Quality Critical Low No

Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125.

This checker detects potential overflows of fixed-size arrays on certain execution paths, when using memcpy, strcpy and similar functions.

void overflow(dhcp_msg *msg, int cnt) {
    int n;
    char buf[10];
    char other_buf[100];

    if (cnt < sizeof(other_buf)-1) {
        n = cnt;                   
    } else {
        n = sizeof(other_buf)-1;
    } 

    // writing in buf instead of other_buf for which the size was calculated
    memcpy(buf, msg, n);
}

In this example amount of copied data n is calculated for other_buf and may be equal to 99 on either of the branches if cnt is sufficiently large. This will result in out of bounds writes to buf and will corrupt data on stack.

BUFFER_OVERFLOW.PROC

Language Situation Severity Reliability Enabled
Java Quality Major Low No
C/C++ Quality Critical Low No

Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125.

This checker is similar to BUFFER_OVERFLOW.EX, but detects when overflow happens inside of function because of an illegal argument value. Less reliable reports are issued with a BUFFER_OVERFLOW.PROC.STRICT warning subtype.

void fill(char * p, int len) {
    for (int i = 0; i < len; i++) {
        // Buffer overflow occurs if len is bigger than size of buffer
        p[i] = 'a';
    }
}

int overflow() {
    char bufferInput[50];
    test(bufferInput, 1000); // This call is bad
    test(bufferInput, 30); // No warning here
    return 0;
}

In this example function fill may write beyond the end of the provided array if the value of variable len is larger than the actual size of the buffer.

BUFFER_OVERFLOW.SPRINTF

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Unknown Yes

Related CWEs: CWE120, CWE121, CWE122, CWE124, CWE134, CWE20.

This checker detects buffer overflows when using sprintf and snprintf function without limiting %s conversion specification maximum number of characters to be printed:

For sprintf function checker detects usages of %s conversion without a precision modifier, which may lead to a buffer overflow if the corresponding argument is a sufficiently long string.

void with_sprintf(char* str) {
    char buf[10];

    sprintf(buf, "%s", str); // possible overflow if str is longer than 10
    sprintf(buf, "%.10s", str); // everything is fine
    sprintf(buf, "%10s", str); // possible overflow with str of any length
}

For snprintf function the checker additionally takes into account the function argument limiting the size of the output buffer.

void with_sprintf(char* str) {
    char buf[10];

    snprintf(buf, 20, "%s", str); // possible overflow if str is longer than 10
    snprintf(buf, 10, "%s", str); // everything is fine
}

BUFFER_OVERFLOW.STRICT

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Unknown No

This checker finds situation when constant buffer is accessed using a non-constant index that potentially can have arbitrary large value.

void index(int i) {
    int buf[21];

    
    buf[i] = 6; // overflow, non-constant index i accesses buffer of constant size

    if(i<15) {
        buf[i] = 6; // no warning, because value was checked
    }
}

void with_strlen(char *s) {
    int a = strlen(s);

    char buf[100];
    buf[a] = 0; // overflow if string is sufficiently large
}

BUFFER_OVERFLOW.STRING

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Average No

Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125.

This checker is similar to BUFFER_OVERFLOW.LIB.EX, but can infer buffer sizes when using string literals for initialization.

void overflow() {
    char a[5];
    char b[] = "7777777"; // This buffer has size 7 + 1
    strcpy(a, b); // overflow, copying buffer of size 8 to buffer of size 5
}

BUFFER_UNDERFLOW

Language Situation Severity Reliability Enabled
Java Quality Major Unknown Yes
C/C++ Quality Critical Unknown Yes
Go Quality Critical Unknown No
Python Quality Critical Unknown Yes

Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE125, CWE127, CWE194, CWE195.

This checker detects potential buffer underflows on certain execution paths. It reports a warning when an array may be accessed beyond its left bound (with negative index). Out-of-bounds array access leads to Undefined Behaviour (C/C++), Runtime Exception (Java), or run-time panic (Go).

unsigned a[1024];
void example(int x) {
    int len, i, j;
    for (i = 0; i < 1024; i += 4)
    {
        len = x++ / 2;
        for (j = 0; j < len; j++)
            a[i+j] &= 0xfffffffe;

        // buffer underflow happens on 1st iteration if (x == 0)
        a[i+(j-1)] |= 1;
    }
}

CHECK_AFTER_OVERFLOW

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
C/C++ Quality Major Average Yes
Python Quality Major Average Yes

Related CWEs: CWE119, CWE124, CWE129, CWE394.

The checker CHECK_AFTER_OVERFLOW finds situations where first, an array element with a certain index is accessed, and then this index is compared to a value that indicates that the index may lie outside the allowed range.

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) CHECK_AFTER_OVERFLOW.MACRO subtype of this warning type is emitted if it is suspected that the warning is a false positive, caused by the conditional expressions being implemented within a macro. Such checks can be too general and not always reflect possible value ranges of index variables.

CHECK_AFTER_OVERFLOW.LEN

Language Situation Severity Reliability Enabled
Go Quality Normal Unknown No

The checker finds situations were the array length (obtained using built-in function len()) is compared to 0 after some element of the same array has been already accessed. Such situation can potentially lead to an out of range access and an unexpected run-time panic.

Example (Go)

func foo(arr []byte, x int) {
    var _ byte = arr[x]

    // Assumption that len of array 'arr' may be 0, so expression 'arr[x]' could cause a buffer overflow
    if len(arr) == 0 {
        fmt.Println(x)
    }
}

OVERFLOW_AFTER_CHECK

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
C/C++ Quality Critical Average Yes
Python Quality Critical Average Yes

Related CWEs: CWE119, CWE121, CWE124.

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

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 Yes
C/C++ Quality Critical High Yes
Go Quality Critical High No
Python Quality Critical High Yes

Related CWEs: CWE119, CWE121, CWE124.

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

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

Language Situation Severity Reliability Enabled
Go Quality Critical Unknown No

This checker detects access to buffer that contradicts earlier conditions using len().

func after_check(arr []int) {
    if len(arr)==10 {
        arr[9] = 0
    }

    arr[20] = 1
}

In this example if array can have length 10, which is indicated by if condition. However later buffer is accessed at index 10, which contradicts the check.

OVERFLOW_AFTER_CHECK.VAR

Language Situation Severity Reliability Enabled
Java Quality Minor Unknown No
C/C++ Quality Minor Unknown No

A checker fire a warning if some pointer is access by function under some check and after it without any check. Here we use heuristic that first check may be a buffer length.

Example

void for(char*p, int max, int i) {
    if(i<max)
        p[i] = 0;

    p[i] = 1;//OVERFLOW_AFTER_CHECK.VAR
}

OVERFLOW_UNDER_CHECK

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
C/C++ Quality Critical Average Yes
Go Quality Critical Average Yes
Python Quality Critical Average Yes

Related CWEs: CWE119, CWE121, CWE122, CWE124, CWE194, CWE195.

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

A less reliable warning subtype OVERFLOW_UNDER_CHECK.COND is reported when the analyzer detects a bound condition on the value of a variable used for computation of an element access index of an array of unknown size but not on the resulting value of the index itself.

Example (C/C++)

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 Yes
C/C++ Quality Critical Unknown Yes

Related CWEs: CWE119, CWE121, CWE124.

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

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 reported instead of OVERFLOW_UNDER_CHECK.

OVERFLOW_UNDER_CHECK.LIB.MEMCPY

Language Situation Severity Reliability Enabled
C/C++ Quality Major Average Yes

Related CWEs: CWE119, CWE121, CWE124.

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

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 Yes
C/C++ Quality Critical High Yes
Go Quality Critical High Yes

Related CWEs: CWE119, CWE121, CWE124.

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

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
C/C++ Quality Critical Average Yes

Related CWEs: CWE120, CWE121, CWE122, CWE170, CWE783.

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

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
C/C++ Quality Critical Unknown Yes

Related CWEs: CWE121, CWE122.

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
C/C++ Quality Normal Average Yes

Related CWEs: CWE121, CWE122.

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 Yes
C/C++ Quality Normal Unknown Yes

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

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 Yes
C/C++ Quality Major High Yes

Related CWEs: CWE120, CWE121, CWE122, CWE170.

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

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
C/C++ Quality Critical Average Yes

Related CWEs: CWE120, CWE131, CWE170, CWE783.

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

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
C/C++ Quality Critical Average No

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

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
C/C++ Quality Critical Average Yes

Related CWEs: CWE783.

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

BITINIT_FOR_NON_TRIVIALLY_COPYABLE

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown Yes

This checker issues a report when a bitwise initialization function (memset, memcpy or memmove) is applied to a type that is not trivially copyable.

Example

(BITINIT_FOR_NON_TRIVIALLY_COPYABLE/example.cpp)

Language Situation Severity Reliability Enabled
C/C++ Quality Major Very high Yes

Related CWEs: CWE119, CWE121, CWE124, CWE170.

This checker finds situations where a standard C library function readlink is used in an unsafe way. This function receives a buffer start address and its size as arguments and returns the number of bytes placed in the buffer. Since it doesn’t append a terminating null character to the buffer the callee usually does this to create a string. Doing so in a straightforward way by assigning a null value to the buffer’s byte with an index equal to the returned value may lead to an “off-by-one” buffer overflow in case the readlink function returns a value equal to the buffer size.

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

SEC_RECV_OVERFLOW

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

This C-specific checker finds potential buffer overflows due to calling POSIX standard functions to receive messages from sockets recv and recvfrom with mismatched parameters specifying the start address of a buffer, where the message is to be stored, and its size.

NONTERMINATED_STRING

Language Situation Severity Reliability Enabled
C/C++ Quality Critical High Yes

Related CWEs: CWE120, CWE170.

The checker NONTERMINATED_STRING finds situations where “safe” versions of standard library functions working with C strings may create non-null-terminated strings as a result. Working with such strings as with regular ones, e.g. passing them as standard library functions arguments, can lead to out-of-bounds memory access.

Example (C/C++)

In this example even though buffer overflow won’t occur directly executing the following code, it may create a non-null-terminated string, which may lead to buffer overflow later, when the string is accessed again. For example, if buffer is a 300-character string, the strncpy call copies 256 bytes to the name field (which is declared as char name [256]), but there is no space left for a null terminator.

if (i < psf->cues->cue_count)
    strncpy (psf->cues->cue_points [i].name, buffer, 256) ;
} ;

See also

NONTERMINATED_STRING.STRICT

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

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

When string characters are copied using memcpy or related functions instead of strcpy or other string-related functions a subtype NONTERMINATED_STRING.STRICT.MINOR is emitted. While such functions can still be used for working with strings they are more commonly used to process other data that shouldn’t be null-terminated.

NONTERMINATED_STRING.STYLE

Language Situation Severity Reliability Enabled
C/C++ Quality Critical High Yes

Related CWEs: CWE120, CWE170.

The checker finds patterns when the result of a strlen standard C library function call is used in a memcpy function call as second parameter representing the number of copied bytes. Since the strlen function doesn’t count the null terminator and the memcpy function doesn’t add it to the end of the resulting string, this pattern results in potential creation of a non-null-terminated string. Using such string as a regular string, e.g. in calls to standard library string functions, can lead to out-of-bounds memory access.

Example (C/C++)

In the following example the dst buffer is filled with the contents of the prog->String string but without adding the null terminator at the end of the copied characters so if dst isn’t zero-filled originally the resulting string will be non-null-terminated:

if (prog->String)
   memcpy(dst, prog->String, strlen((char *) prog->String));

TAINTED.NONTERMINATED_STRING

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Average Yes

Related CWEs: CWE120, CWE170.

This checker finds situations where “safe” versions of standard functions working with C strings may create non-null-terminated strings as a result of using length parameter coming from untrusted source. Working with such strings as with regular ones, e.g. passing them as standard library functions arguments, can lead to out-of-bounds memory access.

Example (C/C++)

In the following example local buffer keys_path (with size 4096 bytes) is filled by a strncpy function call using the value of an external environment variable ADB_VENDOR_KEYS as source data. If all 4096 characters are copied then no space remains for a null terminator and when this variable is later used as a string a buffer overflow may happen:

adb_keys_path = getenv("ADB_VENDOR_KEYS");
if (!adb_keys_path)
    return;
strncpy(keys_path, adb_keys_path, sizeof(keys_path));

STRING_OVERFLOW

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Very high Yes

Related CWEs: CWE120, CWE121, CWE122, CWE127.

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

Example (C/C++)

In this example the command buffer has only space for 99 characters (and a null terminator) but is filled without checking for an overflow with the help of standard strcpy and strcat functions using file paths that can be much longer:

char command[100];
EST_String file1, file2;
file1 = make_tmp_filename();
file2 = make_tmp_filename();
...
strcpy(command, "cat ");
strcat(command, file1);
strcat(command, " | sed -f ");
strcat(command, sedfile);
strcat(command, " > ");
strcat(command, file2);

STRING_OVERFLOW.MINOR

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Very high No

Related CWEs: CWE120, CWE121, CWE122.

This checker is a variant of STRING_OVERFLOW for potential buffer overflows caused by calling of a string copying function when the the source string is larger than the destination buffer, and when the source string is a parameter of a function being analyzed. In this case the function has (possibly implicit) contract that the source string should not be larger than the destination buffer and the function caller is responsible for making sure to fulfil it which can be hard to achieve. To avoid such situations it’s better to check for valid source string length inside of the function, e.g. by using the strncpy string copying function with a string length constraint.

Example (C/C++)

void error_tag(char error_string[], int error_number) {

    if (error_number != 0) {
        char error_buffer[100];
        strcpy(error_buffer, error_string); // FLAW: copying to fixed-size buffer without
                                            // checking source string length
        if (error_number > 4) {
            strcpy(error_string, "Error ");
        } else {
            strcpy(error_string, "Warning ");
        }

        strcat(error_string, error_buffer);
    }
}

OVERFLOW_UNDER_CHECK.LEN

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Average No

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

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

CSTRING_FWRITE_OVERFLOW

Language Situation Severity Reliability Enabled
C/C++ Security Minor Unknown No

This C-specific checker reports usecases when a C-style string is written to a file using binary output functions fwrite and write that don’t check for a null terminator. Writing symbols following the terminator is usually not what a user expects. This also will cause problems when reading contents of this file as null-terminated strings.

This checker is based on the FIO18-C rule from the CERT Secure Coding Standard.

Example

(CSTRING_FWRITE_OVERFLOW/example.c)

FIELD_OVERFLOW.STRICT

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Low No

This C-specific checker reports non-portable initialization of a structure field using standard memcpy function where the start address of the field is calculated manually using sizeofs of the preceding structure fields instead of the portable offsetof macro.

FILE_SYSTEM_REALPATH

Language Situation Severity Reliability Enabled
C/C++ Security Minor Unknown No

Related CWEs: CWE785.

Incorrect usage of the standard C library function realpath to get a canonicalized absolute pathname may cause buffer overflow vulnerability. The POSIX.1-2001 standard version of realpath library function is broken by design, since it is impossible to determine a suitable size for the output buffer. According to POSIX.1-2001 a buffer of size PATH_MAX suffices, but PATH_MAX need not be a defined constant, and may have to be obtained using a pathconf call. And asking pathconf does not help, since, on the one hand POSIX warns that the result of pathconf may be huge and unsuitable for mallocing memory, and on the other hand pathconf may return -1 to signify that PATH_MAX is not bounded. POSIX.1-2008 allows passing a NULL pointer as a second argument of realpath, allowing this design problem to be avoided. This is the recommended secure way of using realpath.

Memory and resource management

This section contains detectors for various types of defects primarily related to manual management of memory and other resources. In the case of memory, such errors can lead to its corrupted state, which may go unnoticed for a long time and cause unexpected behavior of the program, or even a crash when finally detected by the operating system.

One of the most common examples of such issues is when an invalid address is passed to a memory deallocation function such as standard C library free. Such functions expect only exactly those addresses that are returned by the respective memory allocation functions. Even if the address passed as an argument belongs to the allocated memory block it is still an invalid parameter value for a memory deallocation function and using it this way leads to an undefined behaviour that commonly manifests in abnormal program termination.

In other cases, if the program does not release the resources it has acquired, including memory, file handles, database connections, etc., after their use, it can lead to exhaustion of the resources available to the program or the entire system, which in turn can result in a slowdown of its performance or denial of service. Such issues are commonly called “resource leaks” or “memory leaks” in case of memory.

Another large group of detectors are related to situations where a program continues to use a resource (such as memory, file handle, or other) after it has been released or deallocated. This can lead to undefined behavior, crashes, or security vulnerabilities. Sometimes the cause of such problems lie in logical errors in the application, and sometimes in the programmer’s incorrect understanding of how resource deallocation functions work. For example, the memory deallocation operator delete in C++ does not reset the pointer value — it remains the same, but after executing this operator it cannot be dereferenced; doing so leads to undefined behavior.

FREE_NONHEAP_MEMORY

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Average Yes

Related CWEs: CWE590.

This checker finds situation where an address of a non-heap memory block is passed to a memory deallocation function, where only addresses to heap memory, returned by memory allocation functions, are allowed. Trying to do so usually results in operating system terminating the program prematurely.

Example (C/C++)

static int stats_file_history_update(struct stats_file *data_file)
{
    struct stats_file _history_file, *history_file;
    
    history_file = &_history_file;
    ...
    err = stats_open_temp(temp_file);
    if (err < 0) {
        stats_free(history_file); // FLAW: freeing of non-heap memory
        return err;
    }
    ...
static void stats_free(gpointer user_data)
{
    struct stats_file *file = user_data;

    if (!file)
        return;
    ...
    g_free(file);
}

In the example above the local pointer history_file is assigned an address of a local structure variable _history_file, allocated on stack. In the subsequent code an error situation, represented by a negative value returned by a stats_open_temp function call, is handled by calling a stats_free function with the value of pointer history_file as an argument. The stats_free function in turn calls a g_free function in the end which deallocates the memory which address is passed as its parameter. This way an address of a memory block allocated on stack is passed to the memory deallocation function which can lead to abnormal program termination.

FREE_NONHEAP_MEMORY.EX

Language Situation Severity Reliability Enabled
C/C++ Quality Major Very low Yes

This path-sensitive version of checker FREE_NONHEAP_MEMORY finds situations where there is a reachable path from initialization of a pointer with an address of a non-heap memory block to a memory deallocation function call where this pointer value is used. When execution runs along this path this call may result in abnormal program termination since such values are invalid for memory deallocation functions working only with heap addresses.

Example (C/C++)

void cond_free(int *p, int cond) {
    if (cond) {
        free(p);
    }
}

void free_nonheap_ex(int x) {
    int local_array[] = {2, 3};
    cond_free(local_array, x); // FLAW: potential free of non-heap memory
}

In the example above when function free_nonheap_ex is called with a parameter x value other than 0, address of a local array local_array which is passed as the first argument of the cond_free function call is in turn passed as an argument to the memory deallocation function free. Such address on the stack isn’t valid for the free function and this call causes undefined behaviour usually manifesting in abnormal program termination.

FREE_OF_ARITHM

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Very low No

This checker finds situation where a pointer passed to a memory deallocation function was obtained as a result of an arithmetic operation. This can result in an invalid value for a memory deallocation function. Though sometimes the allocated memory address is shifted by a fixed offset intentionally and the correct address of its beginning could be reconstructed using reverse arithmetic operations, this is potentially a cause for incorrect values, passing which to memory deallocation functions as arguments may lead to undefined behaviour that commonly manifests in abnormal program termination.

Example (C/C++)

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

See also

ALLOC_ARITHM

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Very low No

This checker finds suspicious pointer arithmetic on the result of a memory allocation function call. While semantically such code is correct this is a common typo when because of a misplaced parenthesis a value is added or subtracted from the allocated memory address instead of the allocation size argument, as in the example below. Such typo results in both incorrect size of the allocated memory block which later can lead to a buffer overflow, when this memory is used, and also to an invalid address to the allocated memory block start that can’t be passed to the memory deallocation function.

Example (C/C++)

// Allocate memory for the DMA buffers
if((dma_buffer1 = (char *)JMALLOC(DMA_BUFFER_SIZE * 2) + 32) == (char *)0)
{
    fprintf( stderr,"Can not allocate memory for DMA buffers\n");
    exit(0);
}

In this example because of a misplaced closing parenthesis the 32 constant is incorrectly added to the return value of the JMALLOC memory allocation function instead of to the value of the DMA_BUFFER_SIZE * 2 expression, which represents the amount of memory to be allocated.

See also

BAD_ALLOC_ARITHMETIC

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Unknown Yes

Related CWEs: CWE119.

It emits warnings for arithmetic operation on the results of heap allocation functions. This might be a common typo when a closing brace is mistakenly placed before a part of the arithmetic expression for allocation size calculation. Such typo results in both incorrect size of the allocated memory block which later can lead to a buffer overflow, when this memory is used, and also in an invalid address to the allocated memory block start that can’t be passed to the memory deallocation function.

Example

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

Suggested fix

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
C/C++ Quality Minor Average No

This checker finds situations where a NULL pointer is passed to the standard library function free. While it is not an issue in itself, such code is useless as in this case no operation is performed, and it might mean a problem with the program logic such as deallocating a wrong pointer, misplacing the deallocation function call or other.

Example 1 (C/C++)

if((*root)->right == NULL && (*root)->left == NULL){
    *root = NULL;
    free(*root); // FLAW: free of NULL
}

In this example the code author accidentally placed the assignment of a NULL value to the *root pointer before calling the free(*root) function. This mistake leads to not only calling of the free function with a NULL value as an argument which produces no operation but also to not deallocating the memory that is no longer used, i.e. a memory leak.

Example 2 (C/C++)

cdata = eina_hash_find(item_custom_map, &obj);
if (NULL == cdata) {
    eina_hash_del(item_custom_map, &obj, cdata);
    free(cdata); // FLAW: useless free of NULL
}

In this example the free is called with the value of the cdata variable only if it has a null value, checked in the condition of the if statement. This is a useless call that produces no operation in this case and is caused by an accidentally inverted condition of the if statement intended to check for the cdata variable having a non-null value instead.

See also

DEREF_AFTER_FREE

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Average Yes

Related CWEs: CWE415, CWE416, CWE672.

This checker finds situations where a memory location is accessed through a pointer that has been already deallocated. This is a critical yet hard to detect issue, because after the memory is deallocated, the operating system or memory manager may not immediately reclaim that memory. Instead, it may mark it as free and allow it to be reused for other purposes. When the program dereferences the pointer before that memory is reused, it might still see the old data, leading to seemingly normal behaviour. But during the next program run the memory may be already reused by the time the dereference is performed and the program may read some completely unrelated data or even may be terminated by the operating system for trying to access now prohibited memory block.

Example (C/C++)

modes = malloc(sizeof (RRModePtr));
if (!modes)
{
    RRModeDestroy (mode);
    FreeResource (mode->mode.id, 0); // FLAW: accessing a field of a structure in already deallocated memory
    return NULL;
}

In this example the function RRModeDestroy is called with the value of a mode pointer as its argument. The function frees the memory which address is passed as its parameter, so the mode variable points to the deallocated memory after the call. But this memory is accessed in the call to the FreeResource function next to calculate the value of its first argument. This results in accessing already deallocated memory which is undefined behaviour and may not lead to reading the expected value. To fix such issue the value of the argument should be calculated and stored in a temporary variable before the deallocation and the value of this variable should be used as the call argument instead of the deallocated memory access.

See also

USE_AFTER_FREE

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Average Yes

Related CWEs: CWE415, CWE416.

This checker finds situations where the value of a pointer to a memory location that has been deallocated, is used in some way other than dereferencing it. While the analyzer can’t indicate that this value is dereferenced causing an undefined behaviour (see DEREF_AFTER_FREE), storing it or its derivative, which also can’t be dereferenced, may lead to dereference of an invalid pointer much later in the program execution.

Example (C/C++)

Bmp8 * Bmp8::FromCharDumpFile(FILE *fp) {
  // create a Bmp8 object
  Bmp8 *bmp_obj = new Bmp8(0, 0);
  if (bmp_obj == NULL) {
    return NULL;
  }

  if (bmp_obj->LoadFromCharDumpFile(fp) == false) {
    delete bmp_obj;
  }

  return bmp_obj; // FLAW: potential return of an invalid pointer
}

In this example if an error happens during the bmp_obj->LoadFromCharDumpFile(fp) method call, which is indicated by it returning a false value, then the memory, where the variable bmp_obj points to, is deallocated by a delete operator. But instead of returning a null value in this case to propagate an error (as is done in the previous if statement of the Bmp8::FromCharDumpFile method), the now invalid value of this pointer is returned by the return statement at the end of the method which will be eventually dereferenced by the caller, causing undefined behavior.

Example (C/C++)

pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats) {
    pa_assert(data);
    pa_assert(formats);

    if (data->req_formats)
        pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);

    data->req_formats = formats; // FLAW: storing of a potentially invalid address in a structure field

    if (data->source) {
        /* Trigger format negotiation */
        return pa_source_output_new_data_set_source(data, data->source, data->save_source);
    }

    return TRUE;
}

In this example the function pa_idxset_free actually deallocates the memory which address is passed as its first argument, the value of a formats parameter of the pa_source_output_new_data_set_formats function in this case. If this call is executed, then the now invalidated value of the formats parameter is stored into the data->req_formats field and may be dereferenced later after the return from the function which will cause an undefined behavior (see DEREF_AFTER_FREE). The issue is caused by the code author incorrectly calling the pa_idxset_free function with the value of a formats parameter as its first argument instead of the value of the data->req_formats field.

See also

USE_AFTER_FREE.REALLOC

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Low Yes

Subtype of USE_AFTER_FREE for situations where a pointer is passed as argument to the standard C realloc or another realloc-like function that potentially invalidates it but the original pointer value is used afterwards instead of a new value, returned by the realloc call. Dereferencing the original pointer value after calling realloc can lead to same effects as using a pointer value after its deallocation (see DEREF_AFTER_FREE).

Example (C/C++)

int reallocation = 0;
if (nrow > array_size) {
  printf("Reallocating valid_red_vals for reduced validation\n");
  realloc(valid_red_vals, sizeof(double) * nrow);
  reallocation = 1;
}
validation_array("Reduced", nrow, vect_out, valid_red_vals, 6, rawtime); // FLAW: using of potentially invalid pointer

In this example the memory block, which variable valid_red_vals points to, is expanded by calling a realloc function. The issue with this code is that the result of calling realloc is not stored and the original value of the valid_red_vals variable is used in call of the validation_array function. But realloc might change the location of the allocated memory block during reallocation and return its new address, invalidating the old one.

PASSED_TO_PROC_AFTER_FREE.EX

Language Situation Severity Reliability Enabled
C/C++ Quality Major Average Yes

Related CWEs: CWE415, CWE416.

This checker finds situations where a pointer referencing already deallocated memory is passed to a function call as an argument. While the analyzer can’t point to the exact place where this value is dereferenced, causing an undefined behaviour (see DEREF_AFTER_FREE), it may lead to dereference of an invalid pointer in the called function or later during the program execution.

One special case of false PASSED_TO_PROC_AFTER_FREE.EX reports is removing of invalid pointers from collections, such as vectors and arrays. If this is done using a function call, the analyzer may flag as an issue passing an invalid pointer value to this function as an argument. While technically this is indeed use of an invalid pointer value there is no danger in using its value unless it is dereferenced.

Example (C/C++)

if (lfd++==10) {
    free(tmp);
    vstringf (_ ("send_pseudo %s: cannot open tmpfile %s: %s"),
             name, tmp, strerror (errno)); // FLAW: use of an invalid pointer
    vstring ("\r\n");
    return 1;
}

In this example the memory, which address is stored in the tmp variable, is deallocated by a free function call. But the contents of this memory is used right after to write an error message by calling a vstringf function and passing the now invalid pointer value tmp as one of its arguments. To fix this issue it is enough to move the free function call after the vstringf function call.

Example (C/C++)

    ...
    if (old) {
        setlocale(LC_CTYPE, old);
        free(old);
    }

    ic->next = next[0];
    fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
    if (!fs)
        goto out_iconv_close_from;

    return fs;

out_iconv_close_from:
    iconv_close(ic->fromfs);
out_iconv_close_to:
    iconv_close(ic->tofs);
out_free:
    free(ic->from_code);
    free(ic->to_code);
    free(ic);
    if (old) {
        setlocale(LC_CTYPE, old); // FLAW: use of a potentially invalid pointer
        free(old);
    }
    return NULL;

Here if the value of the old pointer is non-null this value is first used as argument in the setlocale function call and then is deallocated by a free function call. Afterwards in case of an error situation, represented by a null return value of the fuse_fs_new function call, the control is transferred to the out_iconv_close_from label by the goto statement. But before the final return statement of the function there is a block that again checks the value of the old pointer for being non-null. Since the old variable value hasn’t been changed in the body of the first if statement but the memory, which it points to, has been deallocated there, the condition of the last if statement is also evaluated as true, the body of the if statement is executed and the now invalid value of the old pointer is passed as a second argument to the setlocale function call (and as an argument of the free function call afterwards). The correct way to fix this issue is to assign a NULL value to the old variable after the memory is freed in the first if statement body - this way the second if statement check is evaluated as false and its body isn’t executed.

See also

DOUBLE_FREE.EX

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Average Yes

Related CWEs: CWE415, CWE416, CWE672.

This checker finds situations where a heap memory block, that has already been deallocated, is deallocated again. This causes an undefined behavior, which is usually manifested as an abnormal program termination by the operating system. The most common way to safeguard against this situation is to explicitly assign a null value to the pointer, that holds the deallocated memory block address, after it has been deallocated. This way the deallocated memory address can’t be used later for repeated deallocation or for dereference.

Example (C/C++)

...
 ERROR:
  rules[i].lhs = NULL;
  rk_rules_free(rules);
  free(rules);
  return NULL;
void
rk_rules_free(struct rk_rule* rules)
{
  struct rk_rule* p;

  for (p = rules; p->lhs != NULL; p++) {
    free((void *) p->lhs);
    free((void *) p->rhs);
    free((void *) p->follow);
  }
  
  free(rules);
}

In this example in case of an error situation first the function rk_rules_free is called with the value of the rules pointer as an argument and then the same rules pointer value is used as an argument for the free memory deallocation function call. But there is already a free function call at the end of the rk_rules_free function body that deallocates the memory, which address is passed as its rules parameter value. This results in a situation where the same memory block is deallocated twice - first at the end of the called rk_rules_free function and then in its callee.

DANGLING_POINTER.STRICT

Language Situation Severity Reliability Enabled
C/C++ Quality Minor High No

This checker is designed to detect dangling pointers, i.e. such pointers that can be accessed from the caller context after they have been freed inside the callee. While storing such pointers doesn’t cause an issue by itself, if they are used accidentally this can lead to hard to diagnose issues (see DEREF_AFTER_FREE). To avoid such issues it is advised to explicitly assign a NULL value to the pointers that are freed to show that they are actually not initialized.

Example (C/C++)

class UndoList : public std::list<Undo *>
{
   public:
      UndoList() : m_name(NULL) {};
      virtual ~UndoList() { if ( m_name ) { free(m_name); } };

      void setOpName( const char * name ) { 
         if ( m_name ) { free(m_name); } // FLAW: not resetting 'm_name' value can lead to use of a freed memory
         if ( name )   { m_name = strdup(name); } };
      const char * getOpName() const { return m_name; };

   protected:
      char * m_name;
};

In this example in the setOpName method the value of the m_name field isn’t set to NULL after it is deallocated and at the same time it isn’t always assigned a new value leaving the m_name field potentially contain an invalid memory address. This allows specific sequences of calls of this method that can lead to dereference of an already deallocated memory, double free of the same memory block or return of an invalid memory address from the getOpName method: for example, first setOpName is called with a non-null string "Spherify" as argument (this initializes the m_name field with a non-null value); then the same setOpName method is called with a NULL value as argument (this call frees the memory that was allocated during the first setOpName method call but not assigns a new value to the m_name field since condition of the if ( name ) statement is evaluated as false; so this field value remains non-null); the next call of the setOpName with any argument leads to deallocation of the same memory that has already been deallocated, i.e. undefined behavior (see DOUBLE_FREE.EX), or the next call of the getOpName method returns address of the already deallocated memory, accessing which also leads to undefined behavior (see DEREF_AFTER_FREE).

Example (C/C++)

static void
lzx_huffman_free(struct huffman *hf)
{
    free(hf->bitlen);
    free(hf->tbl);
}

A common pattern for cleaning up fields of a structure is shown in the example above. While in majority of the cases such methods are called when the structure won’t be reused afterwards and in this case there isn’t an issue with the code, it is still better to assign the NULL values to the deallocated pointer fields to prevent hard to diagnose issues with the usage of the freed memory (see DEREF_AFTER_FREE) in the remaining cases. While both null pointer dereference and dereference of a freed pointer cause undefined behavior, the former usually result in abnormal program termination right when the dereference occurs and the latter in the program using corrupted data which may be much harder to detect and debug.

DANGLING_POINTER.STAT

Language Situation Severity Reliability Enabled
C/C++ Quality Normal High Yes

Statistical version of DANGLING_POINTER.STRICT to detect pointers that have been freed but not assigned a new value afterwards. Svace emits this subtype when for sufficient number of times in the source code the same pointer is reassigned after deallocation but in several places it isn’t. This can mean that in those several places the reassignment should be done but is accidentally skipped, which leaves the pointer in an invalid state so it can’t be dereferenced afterwards (see DEREF_AFTER_FREE).

INCORRECT_STRLEN

Language Situation Severity Reliability Enabled
C/C++ Quality Normal Unknown Yes

Related CWEs: CWE131.

This checker detects possible incorrect calculation of a buffer size, needed to store a string, when due to a misplaced parenthesis of a strlen function call, that computes the string length, a value is added to the string address instead of its return value. In this case the strlen function is called with the address of the original string substring and computes an incorrect value, which in turn may result in the buffer size which isn’t enough to store all the characters of the original string. Trying to copy the string into an allocated buffer of such size can lead to a buffer overflow.

Example (C/C++)

pkname=(char*)malloc(strlen(argv[optind]+4));
skname=(char*)malloc(strlen(argv[optind]+4));
sprintf(pkname,"%s.pk",argv[optind]);
sprintf(skname,"%s.sk",argv[optind]);

In this example the code author intended to calculate sizes of two buffers pkname and skname, enough to store the contents of the string argv[optind] along with three additional characters for their suffixes and one additional character for null terminator. But instead of calculating the string length as strlen(argv[optind]) and then add 4 to its result, he misplaced the parenthesis and called the strlen function with argv[optind]+4 value as argument. This expression produces the address of the fifth character of the argv[optind] string so the strlen call actually computes the length of the substring starting from its fifth character which is lesser than the original string length by four. Afterwards the resulting buffer sizes are used as arguments in malloc function calls to allocate two buffers on the heap. At last these buffers are filled using a sprintf formatted print function which doesn’t check for correct buffer sizes, and these calls lead to writing of the memory outside of the allocated block.

MEMORY_LEAK

Language Situation Severity Reliability Enabled
C/C++ Quality Major High Yes

Related CWEs: CWE401, CWE404, CWE775.

This checker detects general memory leak situations, where memory was allocated, and then all references to that memory are lost. This makes it impossible to return such memory blocks to the operating system or memory manager so that they can be reallocated for other purposes. Even though typically this doesn’t cause immediate problems, over time such lost memory can amount to large quantities, that can lead to program slow down or even crash due to exhaustion of available memory resources. Memory leaks affect user applications to a lesser degree since when the application exits all the allocated by the application memory, even leaked, is automatically reclaimed by the operating system. But various servers, services and other programs, that commonly have long uptimes, can be more susceptible to memory leaks negative effects. To avoid memory leaks a special care should be taken to properly free all the memory, that was allocated by the program, as soon as it’s clear it won’t be used anymore.

Example (C/C++)

newCallbacks = realloc( offman->FreeBoxesUpdateCallback,
         sizeof(FreeBoxCallbackProcPtr) * (offman->NumCallbacks + 1));

newPrivates = realloc(offman->devPrivates,
               sizeof(DevUnion) * (offman->NumCallbacks + 1));

if(!newCallbacks || !newPrivates)
    return FALSE;     

In this code snippet there are two memory (re)allocations using a standard C function realloc, whose results are stored in the newCallbacks and newPrivates local variables. The subsequent if statement checks whether any of the allocations have failed by comparing the values of these variables for being null and returning FALSE value if any of them is. But when the first allocation succeeds and the second one fails, the newCallbacks variable goes out of scope after return from the function and its value, which is the address of the memory block, successfully allocated by the first realloc call, is lost. The issue is fixed by independently checking the results of the two allocations and if only the second one fails, then deallocate the result of the first one before return from the function.

MEMORY_LEAK.STRDUP

Language Situation Severity Reliability Enabled
C/C++ Quality Normal Very high Yes

Related CWEs: CWE401, CWE404, CWE775.

This is a subtype of MEMORY_LEAK for cases where the memory is allocated by using the standard C library string copying function strdup, but then all references to that memory are lost. This function first allocates a memory block, enough to hold all characters of its string argument, then copies all its characters into that block and returns its address. The allocated memory block must be freed using a standard free function, but if its address is lost, it’s impossible to do so and even though the memory can’t be used by the application anymore, it is reserved for the application by the operating system and can’t be reused for other purposes, eventually leading to memory shortage. Such issues are separated into its own subtype since in the majority of cases the strdup function is used for allocating small blocks of memory and so such errors are less critical.

Example (C/C++)

static inline void _set_str_with_default(keynode_t* node, const char *def, char *target)
{
    const char *key_str = vconf_keynode_get_str(node);
    char *str = NULL;

    if (key_str)
        str = strdup(key_str);
    else
        str = strdup(def);

    if (!str)
        _E("failed to strdup, keep old one");

    free(target);
    target = str; // FLAW: storing allocated memory address into local memory
}                 // going out of scope at the return from the function                 

In the above example the result of one of two strdup function calls (for this example doesn’t actually matter which one) is stored into the local variable str. In the last statement of the function the value of the str variable is stored into the target parameter. However, this does not affect the original pointer that was passed into the function. The pointer target in the function is a local copy of whatever pointer was passed in as its last argument but when the target = str; assignment is executed, it only changes the local copy of target, not the original pointer outside of this function. So when the function returns, the local variable target goes out of scope, and any reference to the newly allocated memory is lost. Since there are no other references to that memory, it cannot be freed, resulting in a memory leak. To avoid this memory leak, the function should be modified so that it actually updates the pointer in the caller’s context. One common approach is to pass a pointer to the string instead of the string itself, changing the last parameter declaration to char **target and the assignment to *target = str; so that changes made to *target within the function affect the original pointer passed in by the caller.

MEMORY_LEAK.STRUCT

Language Situation Severity Reliability Enabled
C/C++ Quality Normal High Yes

Related CWEs: CWE401, CWE404.

This is a subtype of MEMORY_LEAK for cases where the memory is allocated for a structure, but then all references to that memory are lost. Since the allocated memory address is lost, it’s impossible to deallocate it anymore, so even though the memory can’t be used by the application anymore, it is reserved for the application by the operating system and can’t be reused for other purposes, eventually leading to memory shortage. Such cases are classified as a separate subtype because some code patterns, specific to low-level work with allocated for structures memory, are difficult to analyze and can lead to an increased number of unclear and false reports.

Example (C/C++)

pPicture->pSourcePict = (SourcePictPtr) malloc(sizeof(PictLinearGradient));
if (!pPicture->pSourcePict) {
    *error = BadAlloc;
    free(pPicture);
    return 0;
}

pPicture->pSourcePict->linear.type = SourcePictTypeLinear;
pPicture->pSourcePict->linear.p1 = *p1;
pPicture->pSourcePict->linear.p2 = *p2;

initGradient(pPicture->pSourcePict, nStops, stops, colors, error);
if (*error) {
    free(pPicture);
    return 0; // FLAW: allocated memory, which address is stored in field
}             // 'pPicture->pSourcePict', is leaked

In this example the memory for structure PictLinearGradient is allocated and stored in the pPicture->pSourcePict field. If this allocation is successful, the structure fields are initialized and its address is used as the first argument in the initGradient function call. This function fills other fields of the structure, but if it encounters an error during its execution, it assigns a non-zero error code to an integer variable, which address is passed as its last (int * pointer) parameter error and returns without deallocating the memory, which address passed as its first parameter. After the initGradient call the value of that variable *error is checked and if it’s non-zero, the memory, which address is stored in the pPicture variable, is deallocated by calling a free function, but the memory, which address is stored in its pPicture->pSourcePict field, is not deallocated. This leads to the only reference to that memory being lost and impossibility to deallocate it. In order to avoid this memory leak, it is necessary to deallocate the allocated memory by calling a free(pPicture->pSourcePict) before the free(pPicture) call.

MEMORY_LEAK.STRDUP.STRUCT

Language Situation Severity Reliability Enabled
C/C++ Quality Normal Low Yes

Related CWEs: CWE401, CWE404, CWE775.

This is a subtype of MEMORY_LEAK.STRDUP for cases where a memory block is allocated using the standard C library string copying function strdup, its address is stored in a structure field, but then all references to that memory are lost. Since the allocated memory address is lost, it’s impossible to deallocate it anymore, so even though the memory can’t be used by the application anymore, it is reserved for the application by the operating system and can’t be reused for other purposes, eventually leading to memory shortage. Such cases are classified as a separate subtype because some code patterns, specific to low-level work with allocated for structures memory, are difficult to analyze and can lead to an increased number of unclear and false reports and in the majority of cases the strdup function is used for allocating small blocks of memory and so such errors are less critical.

Example (C/C++)

static void
gst_oss4_source_init (GstOss4Source * osssrc, GstOss4SourceClass * g_class)
{
  const gchar *device;

  device = g_getenv ("AUDIODEV");
  if (device == NULL)
    device = DEFAULT_DEVICE;

  osssrc->fd = -1;
  osssrc->device = g_strdup (device);
  osssrc->device_name = g_strdup (DEFAULT_DEVICE_NAME);
  osssrc->device_name = NULL; // FLAW: structure field 'device_name', containing
}                             // just allocated memory address, is overwritten

In the above example at the end of the gst_oss4_source_init function the value of the osssrc->device_name field is first initialized with the result of a g_strdup function call, which is a popular open-source library GLib’s version of the C standard string copying function strdup, and then right in the next statement it is reassigned a NULL value. This overwrites the address of the newly allocated for the copied string memory, returned by the g_strdup call, and so this address becomes lost since it isn’t stored anywhere else. This leads to a memory leak. It looks like the code author accidentally added initialization of the same structure field a second time, overwriting the first one.

MEMORY_LEAK.EXCEPTION

Language Situation Severity Reliability Enabled
C/C++ Quality Normal Average Yes

Related CWEs: CWE401, CWE775.

Similar to the MEMORY_LEAK checker, checker MEMORY_LEAK.EXCEPTION finds situations where memory is leaked but due to incorrect exception handling. This can happen when the code, that should deallocate the memory, is skipped due to taking an exception path. Common and efficient way to avoid such issues is to use a RAII (Resource Acquisition Is Initialization) idiom or smart pointers that both provide strong exception safety guarantees, ensuring that memory is released even if an exception occurs.

Less reliable cases when the allocation or exception handling contain complex conditions are reported with a MEMORY_LEAK.EX.EXCEPTION subtype.

Example (C/C++)

SQLBackend *backend = new SQLBackend();

if (backend->open(PolicyStoragePath)) {
    throw runtime::Exception("Policy storage I/O error");
}

In the example above the memory for a SQLBackend object is allocated and stored in the backend local variable but if the method open returns a non-zero value indicating an error, then an exception with type runtime::Exception is thrown, the execution leaves the scope, where the backend variable can be accessed, and its value becomes lost. Since it contains the allocated memory address, that should be deallocated, a memory leak occurs. To solve this issue it is enough to add a delete backend statement before the throw statement.

MEMORY_LEAK.EX

Language Situation Severity Reliability Enabled
C/C++ Quality Major Average Yes

Related CWEs: CWE401, CWE404, CWE775.

This checker detects situations where memory was allocated and then all references to that memory were lost leading to inability to release it back to the system. Compared to the MEMORY_LEAK checker this one is able to detect errors in more complicated situations where leak occurs only on specific paths.

Example (С/C++)

char *pa_getcwd(void) {
    size_t l = 128;

    for (;;) {
        char *p = pa_xmalloc(l);
        if (getcwd(p, l))
            return p;

        if (errno != ERANGE) // FLAW: memory is leaked when 'errno' value is not ERANGE
            return NULL;

        pa_xfree(p);
        l *= 2;
    }
}

In the example above the function pa_getcwd starts by allocating memory using pa_xmalloc(l) (which is a wrapper for the standard C library memory allocation function malloc but with added checks), where l is initially set to 128 and stores the allocated memory address into the p local variable. This memory is intended to hold the current working directory string. The function then calls standard POSIX function getcwd(p, l) to write the path to the current working directory into the provided buffer p. If this call succeeds (returns a non-null pointer), the allocated memory pointer p is returned. But if getcwd call fails, it checks global errno variable for an error code: if errno is ERANGE, it indicates that the buffer was too small, so the function frees the allocated memory with pa_xfree(p) and doubles the size of l for the next iteration. The memory leak happens if getcwd fails for a reason other than needing a larger buffer (e.g., if the current directory does not exist or there are permission issues). In this case errno is set to a value other than ERANGE and the function returns NULL without freeing the previously allocated memory pointed to by p. Since there is no mechanism to free this memory before returning, it becomes inaccessible, leading to a memory leak.

MEMORY_LEAK.MIGHT

Language Situation Severity Reliability Enabled
C/C++ Quality Major Low No

This checker is a less reliable subtype of a memory leak checker MEMORY_LEAK.EX for cases where the allocated memory may be left not deallocated conditionally on some path, usually for error handling. When all references to allocated memory are lost it leads to inability to release it back to the system and eventually to a potential memory shortage in the system.

Example

switch (argc) {
case 2:
    argv_list = 1;
case 1:
    usb_controller = strdup(env_get("dfu_usb_con"));
    interface = strdup(env_get("dfu_interface"));
    devstring = strdup(env_get("dfu_device"));
    if (!usb_controller || !interface || !devstring) {
            puts("DFU: default device environment is not set.\n");
        ret = CMD_RET_USAGE;
        goto bad_args;
    }
    break;
case 5:
    argv_list = 4;
case 4:
        usb_controller = argv[1];
    interface = argv[2];
    devstring = argv[3];
    break;
default:
    return CMD_RET_USAGE;
}

The code snippet above uses standard C library function strdup to allocate memory for usb_controller, interface, and devstring local variables. This function duplicates a string and allocates enough memory to hold it, returning a pointer to the new memory. However, this memory must be freed later to avoid leaks. The switch statement checks the value of argc function parameter. If argc is 2, it sets argv_list to 1 but does not break, which means it falls through to case 1. This behavior is intentional, but it’s important to ensure that all paths that allocate memory are also responsible for freeing it.

done:
    dfu_free_entities();

    if (dfu_reset)
        run_command("reset", 0);

    g_dnl_clear_detach();
bad_args:
    if (argc == 1) {
        free(usb_controller);
        free(interface);
        free(devstring);
    }
#endif
    return ret;

This code snippet shows the end of the same function. In the section with the bad_args label, memory is only freed if argc is equal to 1. If argc is 2 and the code jumps to bad_args, it will not free the allocated memory for usb_controller, interface, or devstring. On return from the function values of these local variables become inaccessible, which leads to a memory leak.

BUFFER_OVERLAP

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Low Yes

Related CWEs: CWE121, CWE122, CWE475.

This checker reports cases of using of the same source and destination buffers in function calls where it is prohibited, such as memcpy. The standard C library function memcpy is designed to copy a specified number of bytes from a source address to a destination address and it performs this operation without any checks for overlapping memory regions. The C standard states that providing memcpy with overlapping source and destination buffers leads to undefined behaviour. For example, using a straightforward byte-by-byte copy memcpy implementation if the destination starts before the source (e.g., copying from a higher memory address to a lower one), the bytes in the destination may be overwritten before they are copied from the source. This leads to data corruption. To avoid such issues when copying overlapping memory regions use a slightly less efficient standard memmove function, which is specifically designed to handle overlapping memory regions correctly.

Example (C/C++)

if (keep_offset > 0) {
    memcpy(data->buff_out, data->buff_out + sizeof(data->buff_out) - keep_offset, keep_offset);
}

HANDLE_LEAK

Language Situation Severity Reliability Enabled
Java Quality Major High Yes
Visual Basic .NET Quality Major High Yes
C/C++ Quality Major High Yes
Scala Quality Major High Yes
Go Quality Major High Yes
Kotlin Quality Major High Yes
C# Quality Major High Yes

Related CWEs: CWE404, CWE772, CWE773, CWE775.

This checker finds situations where a file handle, a socket descriptor, a database connection or another shared resource is lost because all local variables that held its reference went out of scope or were re-assigned. This leads to inability to release such resource back to the system and eventually to a possible shortage of these resources, i.e. a resource leak.

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

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

C#

For C# this checker finds situations where a resource (object that implements IDisposable interface) is lost, because local variables that held its value went out of scope or were re-assigned.

It has the following subtypes:

A warning can have the following combination of these subtypes in the specified order: [.FRUGAL][.EXCEPTION][{.HAS_FINALIZER,.SAFEHANDLE}][.TEST].

HANDLE_LEAK.EXCEPTION

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown Yes
Visual Basic .NET Quality Normal Unknown Yes
C/C++ Quality Normal Unknown Yes
Scala Quality Normal Unknown Yes
Kotlin Quality Major Unknown Yes
C# Quality Normal Unknown Yes

Related CWEs: CWE404.

This checker finds situations where a file descriptor, a file handle, a socket descriptor or another shared resource is lost because due to an exception execution left a function where all its references were stored. This leads to inability to release such resource back to the system and eventually to a possible shortage of these resources.

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 Yes
C/C++ Quality Major High Yes
Scala Quality Major High Yes
Go Quality Major High Yes
Kotlin Quality Major High Yes

Related CWEs: CWE404, CWE775.

This checker finds situations where a file descriptor, a file handle, a socket descriptor or another shared resource is lost because all local variables that held their values went out of scope or were re-assigned. This leads to inability to release such resource back to the system and eventually to a possible shortage of these resources. Compared to the HANDLE_LEAK checker this one is able to detect errors in more complicated situations where leak occurs only on specific paths.

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 Yes
C/C++ Quality Normal Unknown Yes
Scala Quality Normal Unknown Yes
Kotlin Quality Major Unknown Yes

Related CWEs: CWE401, CWE775.

Similar to the HANDLE_LEAK.EX checker, checker HANDLE_LEAK.EX.EXCEPTION finds situations where a shared resource is leaked but due to incorrect exception handling. It is able to detect errors in complicated situations where leak occurs only on specific paths.

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

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown No
Scala Quality Normal Unknown No

This Java-specific checker is a subtype of HANDLE_LEAK that reports incorrect using of classes which implement a java.io.Closeable interface. The analyzer ensures that after creation of an instance of such class its close method is called and reports a warning otherwise. If the issue occurs due to incorrect exception handling a subtype HANDLE_LEAK.CLOSEABLE.EXCEPTION is emitted.

Example (Java)

import java.io.Closeable;
import java.io.IOException;

class MyClass implements Closeable {
    @Override
    public void close() throws IOException {
    }
}

class Example {
    public void noclose() {
        MyClass s = new MyClass();//no close
    }

    public void withclose() {
        MyClass s = new MyClass();//no leak
        s.close();
    }
}

In the example above Svace will emit warning for method noclose.

HANDLE_LEAK.SQLITE_STATEMENT

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

This C-specific checker reports potential memory leaks caused by misusing the SQLite library APIs working with prepared statements. Each creation of a prepared statement object using sqlite3_prepare_v2 function should be accompanied by a corresponding sqlite3_finalize call to delete the prepared statement. Failing to do so may lead to eventual shortage of the dynamic memory available to the application.

DOUBLE_CLOSE

Language Situation Severity Reliability Enabled
Java Quality Normal Average Yes
C/C++ Quality Normal Average Yes
Scala Quality Normal Average Yes
Go Quality Normal Average Yes

Related CWEs: CWE415, CWE416, CWE672, CWE675.

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

Note that checker for Go finds double close on channels but does not on files or other resources.

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 Yes
C/C++ Quality Normal Average Yes
Scala Quality Normal Average Yes
Go Quality Normal Average Yes

Related CWEs: CWE415, CWE416, CWE672.

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

Note that checker for Go finds double close on channels but does not on files or other resources.

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 Yes
C/C++ Quality Normal High Yes
Scala Quality Normal High Yes
Go Quality Normal High Yes
Kotlin Quality Normal High Yes

Related CWEs: CWE415, CWE416.

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 Yes
C/C++ Quality Minor Average Yes
Scala Quality Minor Average Yes
Python Quality Minor Average Yes

Related CWEs: CWE415, CWE416.

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
C/C++ Quality Critical Unknown Yes

Related CWEs: CWE416.

This Microsoft Windows-specific checker finds situations where an instance of Microsoft COM interface is explicitly deallocated. Instead, its Release method should be used.

#include <unknwn.h>

DEFINE_GUID( CLSID_Arithm, 0xa888f560, 0x58e4, 0x11d0, 0xa6, 0x8a, 0x0, 0x0, 0x83, 0x7e, 0x31, 0x0);

DEFINE_GUID( IID_IArithm, 0xa888f561, 0x58e4, 0x11d0, 0xa6, 0x8a, 0x0, 0x0, 0x83, 0x7e, 0x31, 0x0);

class IArithm : public IUnknown {
  public:
    virtual long Add(long Op1, long Op2) = 0;
    virtual long Sub(long Op1, long Op2) = 0;
};

class Arithm : public IArithm {
  public:
    HRESULT QueryInterface(REFIID riid, void** ppv);
    ULONG   AddRef();
    ULONG   Release();

    long Add(long Op1, long Op2) { return Op1 + Op2; }
    long Sub(long Op1, long Op2) { return Op1 - Op2; }

  private:
    DWORD m_lRef;

  public:
    Arithm() : m_lRef(0);
};

HRESULT Arithm::QueryInterface(REFIID riid, void** ppv) {
  switch(riid) {
    case IID_IUnknown:
    case IID_IArithm;
      *ppv = this;
      AddRef();
      return ( S_OK ) ;

    default:
      return ( E_NOINTERFACE );
  }
}

ULONG Math::Release() {
  InterlockedDecrement( &m_lRef ); 

  if (m_lRef == 0) {
    delete this;
    return 0;
  } else {
    return m_lRef;
  }
}

ULONG Math::AddRef() {
  InterlockedIncrement( &m_lRef );
  return m_lRef;
}

long summarize(IUnknown *pUnknwn, long a, long b) {
  IArithm *pArithm = NULL;

  HRESULT hr = pUnknwn->QueryInterface(IID_IArithm, (void **)&pArithm);

  if (SUCCEEDED(hr)) {
    long result = pArithm->Add(a, b);
    delete pArithm; // BAD_FREE.MS_COM is reported; use pArithm->Release() here!
    return result;
  }

  return 0;
}

Infinite loop

These checkers find situations where number of loop iteration may be infinite because of integer overflows or other reasons.

INFINITE_LOOP

Language Situation Severity Reliability Enabled
C/C++ Quality Major Average Yes
C# Quality Major High Yes

Related CWEs: CWE835.

Check for instances of loops that never terminate due to control variables which are not properly updated.

Example 1

In the following example, a wrong variable is updated:

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

Example 2

In the following example, a wrong variable is updated:

while (i > 0) {
    a[i] = i;
    --j;
}

Example 3

In the following example, a wrong variable is updated:

for (;;) {
    ++y;
    if (x > 5)
        break;
}

INFINITE_LOOP.INFINITE_RECURSION

Language Situation Severity Reliability Enabled
Visual Basic .NET Quality Minor Very high Yes
C# Quality Minor Very high Yes

Check for methods that unconditionally call themselves.

Example

In the following example, method CodeBuilder.AppendFormat always calls itself, which leads to stack overflow.

public class CodeBuilder
{
    public CodeBuilder AppendFormat(string format, object[] args)
    {
        return AppendFormat(format, args);
    }
}

INFINITE_LOOP.STRING

Language Situation Severity Reliability Enabled
Java Quality Major Very low No
C/C++ Quality Major Very low No
Go Quality Major Very low No

This checker finds situations where the number of a loop iterations may be more than it was intended because the termination condition depends on the value of a string.

Example (C/C++)

In the following example if there are no space characters in the pos string parameter the loop will not be terminated normally.

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

INFINITE_LOOP.INT_OVERFLOW

Language Situation Severity Reliability Enabled
Java Quality Major Low No
Visual Basic .NET Quality Major Average Yes
C/C++ Quality Major Low No
Go Quality Major Low No
C# Quality Major Average Yes

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

This checker finds situations when loop termination condition may become infeasible due to an overflow or underflow of the index variable.

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

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No
C/C++ Quality Major Unknown No
Go Quality Major Unknown No

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

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

Example

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

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

In the example above if the first 256 bytes of buffer buf do not contain a space character, the variable x will eventually be incremented enough times to overflow an unsigned char type resetting its value back to zero which leads to an infinite loop.

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

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 Yes
C/C++ Quality Critical Unknown Yes
Go Quality Critical Unknown Yes

Related CWEs: CWE121, CWE122.

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
Visual Basic .NET Quality Normal Unknown Yes
C/C++ Quality Major Unknown Yes
C# Quality Normal Unknown Yes

Related CWEs: CWE125, CWE190, CWE191.

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

Example (C++)

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

Example (C#)

  void foo(int a) {
            if (a == int.MaxValue) // ℹ️〔 Step 1: Condition 'a == int.MaxValue' taking false branch〕
                return;
            long b = a + 1; // overflow is NOT possible
            long c = a + 2; // ⚠️〔INTEGER_OVERFLOW An overflow in the arithmetic expression a + 2 of type int may occur〕 // ℹ️〔overflow may happen when a + 2 is Int32.MinValue〕 // ℹ️〔value without overflow may be 2147483648〕 // ℹ️〔overflow may happen when a is Int32.MaxValue - 1〕
            long d = 2 + a; // do not report same
  }

In the example the value of the variable a was compared against int.MaxValue, so overflow in the expression a + 1 is not possible. However overflow may happen in the a + 1 if a is Int32.MaxValue - 1. The result will be equal to Int32.MinValue instead of Int32.MaxValue + 1

Fix

  void foo(int a) {
            if (a is (int.MaxValue or (int.MaxValue - 1)) 
                return;
            long b = a + 1; // overflow is NOT possible
            long c = a + 2; // overflow is NOT possible
            long d = 2 + a; // overflow is NOT possible
  }

Additional comparison of the variable a against Int32.MaxValue - 1 prevents overflow in the expression a + 2

INTEGER_OVERFLOW.EXPLICIT

Language Situation Severity Reliability Enabled
Visual Basic .NET Quality Normal Unknown Yes
C# Quality Normal Unknown Yes

Checker detects integer overflow when values of variables are explicitly known at compile time.

Example

    void foo()
    {
        ushort size = ushort.MaxValue;  // size is 65535
        size += 2;                      // ⚠️〔INTEGER_OVERFLOW.EXPLICIT An overflow in the arithmetic expression size += 2 of type ushort will occur: result will be 1 instead of 65537〕
    }

An overflow in the arithmetic expression size += 2 of type ushort will occur on line 4: result will be 1 instead of 65537.

Fix

    void foo()
    {
        ushort size = ushort.MaxValue;  // size is 65535
        unchecked {
            size += 2;
        }
    }

If overflow is expected it is possible to use unchecked expression or statement to suppress warning.

INTEGER_OVERFLOW.CHECKED_REQUIRED

Language Situation Severity Reliability Enabled
C# Quality Minor Unknown Yes

Checker detects if the result of expression that may overflow is used as an argument of Marshal.AllocHGlobal. It is required to use checked expression or statement to emit exception in case of overflow and reduce risk of silent memory damage.

Example

public static void foo(int size)
{
    size++; // ⚠️〔INTEGER_OVERFLOW.CHECKED_REQUIRED An overflow in the arithmetic expression size++ which is used in Marshal.AllocHGlobal(size) may occur. Please use checked arithmetic to throw IntegerOverflowException in case of overflow instead of possible memory damage〕 // ℹ️〔overflow may happen when size++ is Int32.MinValue〕
    IntPtr hglobal = Marshal.AllocHGlobal(size);
}

An overflow in the arithmetic expression size++ (line 3) which is used in Marshal.AllocHGlobal(size) (line 4) may occur when size is Int32.MaxValue

Fix

public static void foo(int size)
{
    checked
    {
        size++;
    }
    IntPtr hglobal = Marshal.AllocHGlobal(size);
}

To prevent silent memory corruption it is required to use checked expression or statement if overflow may happen

NO_CAST.INTEGER_OVERFLOW

Language Situation Severity Reliability Enabled
Visual Basic .NET Quality Normal Average Yes
C/C++ Quality Major Unknown Yes
C# Quality Normal Average Yes

Related CWEs: CWE190, CWE197.

This checker finds situations where an arithmetic expression may contain an overflow and is widened to a larger data type.

It works only on AST structures and does not analyze execution paths.

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

NO_CAST.INTEGER_OVERFLOW.MACRO

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

This checker finds situations where an arithmetic expression inside a macro may contain an overflow and is widened to a larger data type.

It works only on AST structures and does not analyze execution paths.

Example

#define TRANSFORM(x, y, offset) (((x) << (y)) + (offset))

unsigned long long transform(unsigned int x, unsigned int y) {
    return TRANSFORM(x, y, 0);  /* Having x == 1, y == 32, we get 0 instead of 2^32 */
}

The shift above should be performed after widening the arguments:

#define TRANSFORM(x, y, offset, type) (((type)(x) << (y)) + (offset))

unsigned long long transform(unsigned int x, unsigned int y) {
    return TRANSFORM(x, y, 0, unsigned long long);
}

NO_CAST.INHERITED_INTEGER_OVERFLOW

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

This checker finds situations where an arithmetic expression is widened to a larger data type and its subexpression may contain an overflow.

It works only on AST structures and does not analyze execution paths.

Example

long long update(int x) {
    return (x * (0x8000 << 20)); /* always returns 0 */
}

The evaluation of the constant expression above should be performed in 64 bits:

long long update(int x) {
    return (x * (0x8000LL << 20));
}

NO_CAST.INHERITED_INTEGER_OVERFLOW.MACRO

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

This checker finds situations where an arithmetic expression inside a macro is widened to a larger data type and its subexpression may contain an overflow.

It works only on AST structures and does not analyze execution paths.

Example

#define MUL2x20(x) ((x) << 20)
#define COEF 0x8000

long long update(int x) {
    return (x * MUL2x20(COEF)); /* always returns 0 */
}

The evaluation of the constant expression above should be performed in 64 bits:

#define MUL2x20(x) ((x) << 20)
#define COEF 0x8000LL

long long update(int x) {
    return (x * MUL2x20(COEF)); /* always returns 0 */
}

TAINTED.INT_OVERFLOW

Language Situation Severity Reliability Enabled
Java Quality Major Average No
C/C++ Quality Major Average No
Scala Quality Major Average No
Kotlin Quality Major Average Yes

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

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

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

TAINTED.INT_OVERFLOW.TRUNC

Language Situation Severity Reliability Enabled
Java Quality Normal Average No
C/C++ Quality Normal Average No
Scala Quality Normal Average No
Go Quality Normal Average No

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

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

Example

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

INT_OVERFLOW.TRUNC.UNDER_BITMASK.LONG

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown No
C/C++ Quality Normal Unknown No
Go Quality Normal Unknown No

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

The checker detects cases where a potential loss of data bits due an integer truncation is possible, while an assumption about an integer data range comes from bit-manipulation operations. This warning subtype is reported for the integers of 32-bit size or a greater.

Example

void foo(unsigned int a);

#define INVERT_BYTES_32(X)  ( \
        ( ((X) & 0x000000FF) << 24 ) \
        |   ( ((X) & 0x0000FF00) <<  8 ) \
        |   ( ((X) & 0x00FF0000) >>  8 ) \
        |   ( ((X) & 0xFF000000) >> 24 ) \
    )

void bar(unsigned int x) {
    foo(INVERT_BYTES_32(x) + 1);
}

INT_OVERFLOW

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown No
C/C++ Quality Normal Unknown No

Related CWEs: CWE190, CWE191.

This checker finds situations where an arithmetic expression will inevitably overflow. It means that any way of program execution leak to overflow.

Example (C)

void simple_add(int var) {
    int result = var + 500;
}

void func(int boo) {
    int param = INT_MAX - 300;
    // Result of operation 'var + 500' in function simple_add will overflow in any way
    if (boo) {
        simple_add(param);
    } else {
        simple_add(param + 200);
    }
}

Example (Java)

void func(boolean boo) {
    byte var = 120;
    byte result;
    // Variable 'result' will contain distorted data in any case
    if (boo) {
        result = var + 200;
    } else {
        result = var << 10;
    }
}

INT_OVERFLOW.LIB

Language Situation Severity Reliability Enabled
Java Quality Normal Average No
C/C++ Quality Normal Average No
Go Quality Normal Average No

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

The checker detects cases where the subtraction is performed on an unsigned value that can be 0, and therefore, the subtraction result may underflow, while its result is passed to a function as is passed to a library function as a sensitive unsigned integer data argument.

Example (C)

#include <string.h>

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

void possible_fix(char *dst, const char *str) {
    size_t len = strlen(str);
    if (len >= 0) {
        memcpy(dst, str, len - 1);
    }
}

Function example illustrates the defect: len variable is an unsigned variable and its value comes from the return value of strlen function and can be 0, so a subtraction from it without a check may underflow and result to a big unsigned value, which is used as a memcpy argument.

Function possible_fix illustrates a possible fix: adding a proper check of len to guarantee a valid argument value for memcpy.

INT_OVERFLOW.LOOP

Language Situation Severity Reliability Enabled
Java Quality Normal Very low No
C/C++ Quality Normal Very low No
Go Quality Normal Very low No

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

The checker detects cases where the subtraction is performed on an unsigned value that can be 0, and therefore the subtraction result may underflow, while its result is used as a loop bound.

Example (C)

#include <string.h>

void example(char *str) {
    unsigned len = strlen(str);
    unsigned i;

    for (i = 0; i < len - 2; ++i) {
        str[i] = '*';
    }
}

void possible_fix(char *str) {
    unsigned len = strlen(str);
    unsigned i;

    if (len < 2)
        return;

    for (i = 0; i < len - 2; ++i) {
        str[i] = '*';
    }
}

Function example illustrates the defect: len variable is an unsigned variable and its value comes from the return value of strlen function and can be 0, so a subtraction from it without a check may underflow and result to a big unsigned value, which is used as a loop bound.

Function possible_fix illustrates a possible fix: adding a proper check of len to guarantee a valid loop bound.

INT_OVERFLOW.ZERO.WRAP

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown No
C/C++ Quality Normal Unknown No
Go Quality Normal Unknown No

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

The checker detects cases where the subtraction is performed on an unsigned value that can be 0, and therefore the subtraction result may underflow.

Unlike INT_OVERFLOW.LIB and INT_OVERFLOW.LOOP this checker reports dubious subtracting itself, not the critical use of their result, so an underflow might be an intended behavior in certain cases.

Example 1 (C)

#include <string.h>

void example(char *str) {
    unsigned len = strlen(str);
    unsigned len1 = len - 1;
    if (len1 < 10) {
        str[len1] = '*';
    }
}

void possible_fix(char *str) {
    unsigned len = strlen(str);
    if (len > 0 && len < 10) {
        unsigned len1 = len - 1;
        str[len1] = '*';
    }
}

Function example illustrates the defect and function possible_fix illustrates a possible fix.

Note that the reported issue is a possible underflow while expression len - 1 calculation within an unsigned context, when the value of len variable might be 0, but not any use of its result further.

Example 2 (C)

#include <string.h>

void dummy(unsigned arg) {
    (void)arg;
}

void example(const char *str) {
    unsigned len = strlen(str);
    dummy(len - 1);
}

void possible_fix(const char *str) {
    unsigned len = strlen(str);
    if (len != 0) {
        dummy(len - 1);
    }
}

Function example illustrates the defect and function possible_fix illustrates a possible fix.

Note that the reported issue is a possible underflow while expression len - 1 calculation within an unsigned context, when the value of len variable might be 0, but not any use of its result further.

INT_OVERFLOW.AFTER_CHECK

Language Situation Severity Reliability Enabled
Java Quality Normal Low No
C/C++ Quality Normal Low No
Kotlin Quality Normal Low Yes

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

This checker detects issues where an integer variable is compared to some constant value and the result of an arithmetic operation on this variable after that may overflow. The comparison identifies the bounds for the possible values of this variable.

Example (C)

#include <stdint.h>

extern void stub(uint64_t);

void example(uint32_t x) {
    if (x <= 100000) {
        uint32_t y = x * 50000;
        stub(y);
    }   
}

void possible_fix(uint32_t x) {
    if (x <= 100000) {
        uint64_t y = (uint64_t)x * 50000;
        stub(y);
    }   
}

Function example illustrates the defect: x is compared to 105 and x is multiplied by 5*104 after that. The result of the multiplication is greater than the maximum of uint32_t type range, if the value of x reaches the upper bound of the comparison.

Function possible_fix illustrates a possible fix: adding a type cast of x value to uint64_t type before multiplication.

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 105 and n is multiplied by 106 after that. The result of the multiplication is greater than the maximum value an instance of Int can have.

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

INT_OVERFLOW.CONST

Language Situation Severity Reliability Enabled
Java Quality Normal Low No
C/C++ Quality Normal Low No
Go Quality Normal Low No
Kotlin Quality Normal Low No

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

This checker detects issues where a constant value is assigned to an integer variable and the result of an arithmetic operation on this variable after that may overflow.

Example (C)

#include <stdint.h>

extern void stub(uint64_t);

void example(void) {
    uint32_t x = 100000;
    uint32_t y = x * 50000;
    stub(y);
}

void possible_fix(void) {
    uint64_t x = 100000;
    uint64_t y = x * 50000;
    stub(y);
}

Function example illustrates the defect: constant value 105 is assigned to x of type uint32_t and x is multiplied by 5*104 after that. The result of the multiplication is greater than the maximum value of uint32_t type range.

Function possible_fix illustrates a possible fix: changing the type of x variable to uint64_t.

INT_OVERFLOW.NEG_TO_UNSIGNED.UNDER_CHECK

Language Situation Severity Reliability Enabled
Go Quality Normal Very low No

Related CWEs: CWE190, CWE191, CWE194, CWE195, CWE196, CWE197.

This checker detects issues where a signed value is converted to an unsigned after a check that is not sufficient to guarantee that the converted value is non-negative.

Example (Go)

func example(x int32) uint32 {
    if x < 123 {
        return uint32(x)
    }
    return 1
}

func possible_fix(x int32) uint32 {
    if x < 123 {
        if x >= 0 {
            return uint32(x)
        }
    }
    return 1
}

Function example illustrates the defect: variable x is a signed integer and the condition x < 123 does not guarantee that the value of x is positive in the subsequent conversion to an unsigned type.

Function possible_fix demonstrates a possible fix: guarding the conversion with a non-negative check for x.

INT_OVERFLOW.ARITHM

Language Situation Severity Reliability Enabled
Go Quality Minor Unknown No

Related CWEs: CWE190, CWE191, CWE682.

This checker finds situations where the result of an arithmetic expression casts to a bigger type, but the result could overflow before this cast.

Example (Go)

func overflow(a int8) {
    // Potential integer overflow
    b := int16(a + 100)
}

func no_overflow(a int8) {
    // There is no integer overflow
    b := int16(a) + 100
}

SIGNED_LEFT_SHIFT

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

This checker finds for shift left arithmetic operations where the left argument is a negative value. According to the C language standard, this is undefined behavior.

Example

void func() {
    int shiftValue = -100;
    int someOtherValue = shiftValue << 5; // Undefined behavior
}

Security errors

SIGNED_TO_BIGGER_UNSIGNED

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

Related CWEs: CWE195.

Check for assignment of a signed value to a variable of a bigger unsigned integral type. While it is not a defect by itself this may unexpectedly lead to a large resulting value if the original signed value is negative.

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

SEC_DO_NOT_CAST_INT_TO_SIZE_T

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

This C-specific portability checker finds assignments of a value of a signed type int to a variable of a bigger unsigned type size_t. This is a common source of various issues when porting 32-bit code to a 64-bit platform. Since on common 32-bit platforms signed int and unsigned size_t types have same width converting between them is performed differently than on most 64-bit platforms where size_t is usually wider.

SIGN_EXTENSION

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown No

Related CWEs: CWE194.

This checker reports unexpected sign extension during integer promotion when result value has all of its high bits set to 1 and consequently is interpreted as a very large value.

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

SENSITIVE_LEAK

Language Situation Severity Reliability Enabled
Java Quality Major Low No
C/C++ Quality Major Low No
Scala Quality Major Low No
Go Quality Major Low No

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

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.

Configuration option SENSITIVE_NAME_REGEX allows changing regular expression for name. Configuration option LEAK_FUNC_NAME_REGEXP allows setting additional regular expression for function names that are considered leaks.

INFORMATION_EXPOSURE.CONNECTION_STRING

Language Situation Severity Reliability Enabled
Visual Basic .NET Security Minor Very high Yes
C# Security Minor Very high Yes

Related CWEs: CWE209, CWE532, CWE535.

Database connection string is exposed in user-visible output.

Example

m_log.Info("[REGION DB]: MySql - connecting: " + Util.GetDisplayConnectionString(m_connectionString));

INFORMATION_EXPOSURE.SYSTEM_INFO

Language Situation Severity Reliability Enabled
Visual Basic .NET Security Minor Very high Yes
C# Security Minor Very high Yes

Related CWEs: CWE209, CWE526.

System information (like host name, OS version, stack trace etc.) is written into publicly-visible output. It can help the attacker to gain information about the application and the environment where it is executed.

Note: exception message is also considered publicly-visible output because they are often displayed.

Example

public partial class ExploitDebug : System.Web.UI.Page
{
    protected void btnGo_Click(object sender, EventArgs e)
    {
        StringBuilder strBuilder = new StringBuilder();
        
        strBuilder.AppendFormat("Current Dir: {0}\n", Environment.CurrentDirectory);
        strBuilder.AppendFormat("UserName: {0}\n", Environment.UserName);
        strBuilder.AppendFormat("Machine Name: {0}\n", Environment.MachineName);
        strBuilder.AppendFormat("OS Version: {0}\n", Environment.OSVersion);
        
        throw new Exception(strBuilder.ToString());
    }
}

HARDCODED_PASSWORD

Language Situation Severity Reliability Enabled
Java Quality Major Low No
C/C++ Quality Major Low No
Scala Quality Major Low No
Go Quality Major Low No
Python Quality Major Low No

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

Example

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

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

COMMAND_INJECTION

Language Situation Severity Reliability Enabled
Visual Basic .NET Security Critical Very high Yes
C/C++ Security Critical Unknown No
C# Security Critical Very high Yes

Related CWEs: CWE77, CWE78.

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

LIB.INSECURE_STRNCMP

Language Situation Severity Reliability Enabled
C/C++ Security Critical Unknown No

Related CWEs: CWE676.

The checker reports a warning for a pattern when C standard library function strncmp for string comparison uses the result of calling string length calculation function strlen as the length parameter directly. In this way only the prefix of the string is compared because comparison stops at the null-terminator so it and the remaining characters are not compared. It may be the source of a vulnerability when checking the validity of sensitive data, such as passwords. For such cases the length parameter should be calculated as result of the expression strlen(arg) + 1 to account for the end of the string.

Example

int get_id(char*name);

int bad_code(char*name, char*passwd, char* argv[]) {
    if (strncmp(passwd, argv[1], strlen(passwd)) == 0) { 
        return get_id(name);
    } else {
        return -1;
    }
}

int good_code(char*name, char*passwd, char* argv[]) {
    if (strncmp(passwd, argv[1], strlen(passwd) + 1) == 0) {  
        return get_id(name);
    } else {
        return -1;
    }
}

int good_code_too(char*name, char*passwd, char* argv[]) {
    if (strcmp(passwd, argv[1]) == 0) {
        return get_id(name);
    } else {
        return -1;
    }
}

CURL.USE_HTTPGET

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Unknown No

This checker finds using of an insecure in many contexts HTTP GET method instead of the POST method.

Example (C/C++)

void foo() {
    CURL* curl = curl_easy_init();
    if (curl) {
        CURLcode res;
        // ...
        res = curl_easy_perform(curl);
    }
}

Function foo illustrates the defect: libcurl library function curl_easy_init creates a new session with a GET method by default. The operation will be processed with the default method after calling curl_easy_perform.

Example (C/C++)

void bar() {
    CURL* curl = curl_easy_init();
    if (curl) {
        CURLcode res;
        // ...
        curl_easy_setopt(curl, CURLOPT_POST, 0L);
        res = curl_easy_perform(curl);
    }
}

Function bar illustrates the defect: libcurl library function curl_east_setopt with the option CURLOPT_POST and third parameter equal to zero resets the request type back to the GET method.

INSECURE_DATA_CHANNEL

Language Situation Severity Reliability Enabled
C/C++ Security Minor Unknown No

Setting inappropriate parameter values in a libcurl library function curl_easy_setopt call may lead to security issues.

Concurrency

DEADLOCK

Language Situation Severity Reliability Enabled
Java Quality Critical Unknown No
C/C++ Quality Critical Unknown No
Kotlin Quality Critical Unknown Yes

Related CWEs: CWE833.

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

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

Possible fix: use reverseCorrect function instead of reverse.

DEADLOCK.CLASS_LOADING

Language Situation Severity Reliability Enabled
Java Quality Major Unknown No

This checker finds code that might lead to a class loading deadlock. JVM might start loading super and child classes in separate threads. If static initialization block contains a reference to a subclass it might lead to a deadlock.

class SuperClass {
    final static SubClass subClass = new SubClass();
}

class SubClass extends SuperClass { }

The class loader sees the subClass static field and tries to lock SubClass for loading. Another thread may load SubClass which is inherited from SuperClass. It locks SuperClass for loading which leads directly to the deadlock.

UNLOCK.CONC

Language Situation Severity Reliability Enabled
Go Quality Normal Unknown No

This checker finds situations, when a goroutine may not have time to complete its execution, which are caused by using sync.WaitGroup.Add() inside the goroutine and sync.WaitGroup.Wait() after the goroutine’s function initialization.

Example

func foo() {
    var wg sync.WaitGroup
    var x int32 = 0
    for i := 0; i < 100; i++ {
        go func() {
            wg.Add(1)
            atomic.AddInt32(&x, 1)
            wg.Done()
        }()
    }
    wg.Wait()
}

Function foo illustrates the defect: wg.Add(1) inside the goroutine can be executed after the execution of wg.Wait(), which may not wait for the goroutine to finish.

DOUBLE_LOCK

Language Situation Severity Reliability Enabled
Java Quality Major High Yes
C/C++ Quality Major High Yes

Related CWEs: CWE617, CWE764.

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 Yes
C/C++ Quality Major High Yes
Go Quality Major High Yes

Related CWEs: CWE617, CWE667, CWE764.

This checker finds situations where a thread acquires a lock during function execution and leaves it locked on some paths to the exit from the function, but the function also unlocks it on some other paths. This may be caused by failing to release a lock when an erroneous situation happens.

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

Language Situation Severity Reliability Enabled
Java Quality Minor High No
C/C++ Quality Minor High No
Go Quality Minor High No

Related CWEs: CWE617, CWE667, CWE764.

It is subtype of NO_UNLOCK. The checker emits warnings if mutex is locked on some paths and there are no any unlock operation.

Example

#include <pthread.h>

pthread_mutex_t m;

void lock() {
    pthread_mutex_lock(&m);
}

int func(int flag) {
    if(flag>0) {
        lock();

        if(flag)return 0;//NO_UNLOCK.STRICT
    }
    return 1;
}

NO_UNLOCK.CTOR

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown Yes

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

Example

#include <pthread.h>

class LockGuardBad {
    pthread_mutex_t m;

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

    ~LockGuardBad() {}
};

WRONG_LOCK

Language Situation Severity Reliability Enabled
Java Quality Major Unknown Yes
Kotlin Quality Major Unknown Yes

This checker finds situations where pooled or reusable objects are used for synchronization. Such objects can be locked by external code, which may lead to unexpected blocking or deadlock (the first three examples below). Also, if the object used for synchronization can be modified by external code, and if this happens during the execution of a critical section, then the next thread attempting to enter the critical section will not be blocked as expected (the fourth example below).

String literal example

All three methods (localStringLockTest, fieldStringLockTest and internStringTest) use the singleton string object stored in Java String Pool for synchronization.

class StringLiteralExample {
    public void localStringLockTest() {
        String localStringLock = "Some string";
        synchronized (localStringLock) {
            // ...
        }
    }

    private final String fieldStringLock = "Some string";
    public void fieldStringLockTest() {
        synchronized (fieldStringLock) {
            // ...
        }
    }

    private final String internStringLock = new String("Some string").intern();
    public void internStringTest() {
        synchronized (internStringLock) {
            // ...
        }
    }
}

Boolean literal 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 the outside code. It’s recommended to use private final fields for synchronization as possibleFix method shows.

class PublicNonFinalFieldExample {
    public Object publicNonFinalLock = new Object();
    public void publicNonFinalLockTest() {
        synchronized (publicNonFinalLock) {
            // ...
        }
    }

    private final Object privateFinalLock = new Object();
    public void possibleFix() {
        synchronized (privateFinalLock) {
            // ...
        }
    }
}

LOCK_ON_STACK

Language Situation Severity Reliability Enabled
C/C++ Quality Major High Yes

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

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
C/C++ Quality Major Average Yes

Related CWEs: CWE667.

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

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
C/C++ Quality Major Unknown Yes

Related CWEs: CWE662.

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

NO_LOCK.STAT

Language Situation Severity Reliability Enabled
Visual Basic .NET Quality Major Average Yes
C/C++ Quality Major Unknown No
C# Quality Major Average Yes

Related CWEs: CWE366.

This detector collects statistics in a single file of variables reads and writes inside and outside critical sections. Based on this statistical data it detects possible usages of variables outside critical sections that may lead to data races.

NO_LOCK.STAT.EX

Language Situation Severity Reliability Enabled
Java Quality Major Unknown Yes
Kotlin Quality Major Unknown Yes

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

The warning will be emitted by this detector if:

NO_LOCK.STAT.EX.THRESHOLD 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 a 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 Yes

This Java-specific 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 Yes

This detector finds situations when an assignment to a variable occurs in a synchronized block, while the variable’s value is checked before that block.

This may lead to a synchronization error, when many processes simultaneously pass the check and then reach the synchronized block one by one.

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 Yes

This detector finds double-checked locking anti-pattern commonly used to reduce the overhead of acquiring a lock by testing the locking criterion before acquiring the lock.

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 Yes

This Java-specific detector finds situations when synchronized block contains wait method but does not contain an enclosing loop. This may cause the wait to be triggered by a condition that it is not initially intended for (so called ‘spurious wake-up’). The solution is to put the loop under synchronized.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class BadCheckOfWaitCond {
    private void testFunc(boolean cond1, boolean cond2) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(new File("testFile"));
            while (fis.read() > 0) {
                if (cond1) {
                    while (true) {
                        synchronized (this) {
                            try {
                                if (cond2) {
                                    this.wait(); // report BAD_WAIT_OF_COND here
                                } else {
                                    break;
                                }
                            } catch (InterruptedException e) {
                                if (fis != null)
                                    fis.close();
                                e.printStackTrace();
                            }
                        }
                    }
                } else {
                    break;
                }
            }
            fis.close();
        } catch (Throwable th) {
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            th.printStackTrace();
        }
    }
}

RACE.NO_UMASK

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Very high No

Related CWEs: CWE377.

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

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

See also

RACE.BAD_UMASK

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Very high No

Related CWEs: CWE377.

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

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

See also

Unreachable and dead code

INVARIANT_RESULT

Language Situation Severity Reliability Enabled
Java Quality Major Average Yes
JavaScript Quality Major Average Yes
C/C++ Quality Major Average Yes
Go Quality Major Average Yes
Kotlin Quality Major Average Yes
Python Quality Major Average Yes
C# Quality Minor Average Yes

Related CWEs: CWE480, CWE783.

This checker reports a suspicious expression where the result of operation is always a constant regardless of the value of its variable operands. This may be the result of a typo or an application logic error.

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. Subtype INVARIANT_RESULT.MACRO is reported when neither INVARIANT_RESULT.OP_ASSIGN nor INVARIANT_RESULT.OP_ZERO should be issued and the expression is a part of a macro expansion.

Example

In the following example, second operand of bitwise AND is zero which makes the condition always false regardless of the value of the first operand.

unsigned flags = 5;

if (flags & 0) {
    // ...
}

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;

INVARIANT_RESULT.EX

Language Situation Severity Reliability Enabled
Java Quality Minor Unknown No
C/C++ Quality Minor Unknown No
Scala Quality Minor Unknown No
Go Quality Minor Unknown No

This checker is similar to INVARIANT_RESULT, but uses SMT solver to find more complex expressions with constant result.

void invariant(unsigned char ch) {
    unsigned char temp = (0xF0 | (ch << 4));
}

LOGICAL_OP_USELESS_ARG

Language Situation Severity Reliability Enabled
C/C++ Quality Major Unknown Yes

Related CWEs: CWE561.

This checker finds code where the second operand of a logical operator has no impact on the result.

Example

void ex_1(unsigned a) {
    if (a < 7 && a < 10) {
        // ...
    }
}

UNREACHABLE_CODE

Language Situation Severity Reliability Enabled
Java Quality Normal Average Yes
Visual Basic .NET Quality Normal Average Yes
C/C++ Quality Normal Average Yes
Go Quality Normal Average Yes
Kotlin Quality Normal Average Yes
Python Quality Normal Average Yes
C# Quality Normal Average Yes

Related CWEs: CWE561.

This checker finds source code that can’t be executed because control flow path to the code from the rest of the program is unfeasible. This is commonly caused by logic errors in the related code or nearby.

Example (C/C++)

In the following example the initial value of the fVerifyTD variable is FALSE and it can only be set to TRUE in the true branch of the first if statement. Because of the incorrectly used else if (fVerifyTD) statement (instead of the intended if (fVerifyTD)) this check is only performed in the false branch of the first if statement (instead of being performed always) where the value of the fVerifyTD variable can only have the initial FALSE value and so the assignment of the fTrustTD variable in that if statement body is unreachable.

BOOL fVerifyTD = FALSE;
// If this is an exported type with a mdTokenNil class token, then then
// exported type did not give a typedefID hint. We won't be able to trust the typedef
// here.
if ((FoundExportedType != mdTokenNil) && (FoundCl == mdTokenNil))
{
    fVerifyTD = TRUE;
    fTrustTD = FALSE;
}
// verify that FoundCl is a valid token for pFoundModule, because
// it may be just the hint saved in an ExportedType in another scope
else if (fVerifyTD)
{
    fTrustTD = pFoundModule->GetMDImport()->IsValidToken(FoundCl);
}

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 the first and the second branches cover all possible values of a. The redundant branch should be removed to fix this defect.

See also

UNREACHABLE_CODE.TERMINATION

Language Situation Severity Reliability Enabled
Java Quality Minor Unknown No
C/C++ Quality Minor Unknown No

Subtype of UNREACHABLE_CODE for statements that follow a call of a function that terminates program execution.

Example (C/C++)

In the following example function report_instruction_timeout calls a panic function that never returns so the assignment of a new value to the start_time variable can’t be ever executed:

while (1) {
    cpu_relax();
    status = GET_MSEG_HANDLE_STATUS(h);
    if (status != CCHSTATUS_ACTIVE)
        break;
    if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) {
        report_instruction_timeout(h); // Calls 'panic' which doesn't return
        start_time = get_cycles();     // so this assignment is unreachable
    }
}

UNREACHABLE_CODE.EXCEPTION

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown Yes
Visual Basic .NET Quality Normal Low Yes
C/C++ Quality Normal Unknown Yes
C# Quality Normal Low Yes

Subtype of UNREACHABLE_CODE for situations where a code fragment is unreachable because the preceding operation always throws an exception.

Example (C#)

In the following code the compiledResourceFile variable in the case ".resx" block is never assigned because of the exception in the CompileResx function:

private FileInfo CompileResx(Configuration solutionConfiguration) {
    // For performance reasons, compilation of resx files is done in
    // batch using the ResGen task in ManagedProjectBase.
    throw new InvalidOperationException();
}

public FileInfo Compile(Configuration solutionConfiguration) {
    FileInfo compiledResourceFile = null;
    switch (InputFile.Extension.ToLower(CultureInfo.InvariantCulture)) {
        case ".resx":
            compiledResourceFile = CompileResx(solutionConfiguration);
            break;
...

UNREACHABLE_CODE.DEFAULT

Language Situation Severity Reliability Enabled
Java Quality Minor Unknown No
C/C++ Quality Minor Unknown No

Subtype of UNREACHABLE_CODE for situations where default label of a switch statement is unreachable. Related situations with enum variables used as conditions are reported with the UNREACHABLE_CODE.ENUM warning type. While this is common to have such unreachable statements when default cases are strictly enforced by a coding policy or added to just be on the safe side, occasionally such reports may show an issue with the program logic.

Example (C/C++)

In this example the default case is unreachable because the if statement enclosing the switch statement restricts the possible values of the format variable to those handled by other cases:

if ((format == CTRL_RAW || format == CTRL_USER || format == CTRL_MGMT)
                            && size >= 8) {
...
    switch (format) {
    case CTRL_RAW:
        title = "RAW Open";
        break;
    case CTRL_USER:
        title = "USER Open";
        break;
    case CTRL_MGMT:
        title = "MGMT Open";
        break;
    default: // This case label is unreachable
        title = "Control Open"; 
        break;
    }
    print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN,
                    title, comm, details);

Example (C/C++)

In the following example function llhttp__internal__c_test_flags_2 can only return values 0 or 1:

int llhttp__internal__c_test_flags_2(
    llhttp__internal_t* state,
    const unsigned char* p,
    const unsigned char* endp) {
  return (state->flags & 256) == 256;
}

But its result is used as if it can return other values too:

switch (llhttp__internal__c_test_flags_2(state, p, endp)) {
  case 0:
    goto s_n_llhttp__internal__n_error_11;
  case 1:
    goto s_n_llhttp__internal__n_invoke_test_flags_3;
  default:
    goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete;
}

UNREACHABLE_CODE.ENUM

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown Yes
C/C++ Quality Normal Unknown Yes

Subtype of UNREACHABLE_CODE for switch statement with an enum variable as a condition expression when that switch statement contains case labels for each of the possible enumeration type values. The warning is emitted for the default label which is unreachable in such situation. While technically the code statements under this label are indeed unreachable the use of a default label may be enforced by a coding policy.

Note that for C/C++ an enumeration variable is just an integer variable that is able to hold any value from the enumeration type domain so it can still hold an integer value outside of that domain but relying on this makes a program harder to understand and support but can be useful to filter out unsupported values.

Example (C/C++)

In this example the enumeration is declared as containing four elements:

enum dss_rotation {
    dss_rotation_0_degree   = 0,
    dss_rotation_90_degree  = 1,
    dss_rotation_180_degree = 2,
    dss_rotation_270_degree = 3,
};

The following switch statement handles all of them (the code for the first three is removed for brevity) in unique ways and also has a default label with own code that is unreachable under normal program execution:

switch (rotation) {
case dss_rotation_90_degree:
    ...
case dss_rotation_180_degree:
    ...
case dss_rotation_270_degree:
    ...
case dss_rotation_0_degree:
    if (!mirroring) {
        *cropped_offset = (line_length * ps) *
            crop->top + (crop->left / vr_ps) * ps;
    } else {
        *cropped_offset = (line_length * ps) *
            crop->top + (crop->left / vr_ps) * ps +
            (line_length * (crop->height - 1) * ps);
    }
    break;
default:
    *cropped_offset = (line_length * ps * crop->top) /
        vr_ps + (crop->left * ps) / vr_ps +
        ((crop->width / vr_ps) - 1) * ps;
    break;
}

UNREACHABLE_CODE.SWITCH

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown Yes
C/C++ Quality Normal Unknown Yes

Subtype of UNREACHABLE_CODE for situations where a case label of a switch statement is unreachable.

Example (C/C++)

In the following example the label case DCA_DOLBY is unreachable because the DCA_CHANNEL_MASK mask zeros all bits except the lower six so the resulting value lies in the range from 0 to 63 which 101 doesn’t belong to.

#define DCA_MONO 0
#define DCA_CHANNEL 1
#define DCA_STEREO 2
#define DCA_STEREO_SUMDIFF 3
#define DCA_STEREO_TOTAL 4
#define DCA_DOLBY 101
#define DCA_CHANNEL_MASK 0x3F
...
switch (flags & DCA_CHANNEL_MASK) {
  case DCA_STEREO:
  case DCA_STEREO_SUMDIFF:
  case DCA_STEREO_TOTAL:
  case DCA_DOLBY:
    chans = 2;
    if (tpos) {
      tpos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
      tpos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
    }
    break;

UNREACHABLE_CODE.GLOBAL

Language Situation Severity Reliability Enabled
Java Quality Normal Average No
C/C++ Quality Normal Average No
Go Quality Normal Average No
Kotlin Quality Normal Average No
Python Quality Normal Average No

Subtype of UNREACHABLE_CODE for situations, where a code is unreachable due to a condition that depends on global variables. This is common for debugging code locked behind a check of a global variable value, but occasionally such reports may show an issue with the program logic.

Example (C/C++)

int wgn_verbose = FALSE; // Global variable that is never reassigned
...
if (wgn_verbose) // The following code is essentially unreachable unless the
{                // value of 'wgn_verbose' is changed and the program is recompiled
    int i;
    for (i=0; i < margin; i++)
    cout << " ";
    cout << "stopped samples: " << node.samples() << " impurity: "
    << node.get_impurity() << endl;
}

UNREACHABLE_CODE.EXECUTED_ONCE

Language Situation Severity Reliability Enabled
C/C++ Quality Major Average No

Related CWEs: CWE561.

C/C++-specific subtype of UNREACHABLE_CODE for continue statements in loops whose body is known to be executed only once, such as do-while loops with constant false conditions. Contrary to the probable intention to restart the loop execution from the beginning the execution of the loop will be terminated. If this is the intended behaviour it is better to use a break statement instead to reflect it more clearly.

UNREACHABLE_CODE.NO_PATH

Language Situation Severity Reliability Enabled
Visual Basic .NET Quality Normal Very high Yes
C/C++ Quality Normal Average No
C# Quality Normal Very high Yes

Related CWEs: CWE561.

Subtype of UNREACHABLE_CODE for situations where there is no execution path, even an unfeasible one, to a code fragment.

Common cases for harmless unreachable statements needed for syntactic correctness, such as return statements or C/C++ va_end statements are reported by UNREACHABLE_CODE.NO_PATH.RETURN and UNREACHABLE_CODE.NO_PATH.VARARG subtypes respectively.

Example (C#)

Here a programmer forgot to add a default label before the throw statement, so the throw statement belongs to case 4 section but is located after break.

switch (ByteCapacity)
{
    case 1: Stream.WriteByte((byte)CurrentValue); break;
    case 2: Stream.WriteStruct((ushort)CurrentValue); break;
    case 4: Stream.WriteStruct((uint)CurrentValue); break;
    throw(new InvalidOperationException());
}

REDUNDANT_COMPARISON

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown No
C/C++ Quality Normal Unknown No
Go Quality Normal Unknown No
Python Quality Normal Unknown No

This checker detects redundant comparisons.

Example (C/C++)

int example(int a) {
    if (a >= 2) {
            if (a > 0) { //svace: emitted REDUNDANT_COMPARISON
            return 1;
        }
    }
    return 0;
}

In the example above the second if-condition is always true because the first if-condition has already checked that a more than zero.

Example (go)

func ret(a int) int {
    return 10
}

func example(a int) int {
    z := ret(a)

    if z == 10 { //svace: emitted REDUNDANT_COMPARISON
        return z + 3;
    }

    return z + a; /* unreachable code */
}

Function example illustrates the defect: the variable z has a constant value received from function ret, so the if-condition is redundant because z is always 10.

REDUNDANT_COMPARISON.RET

Language Situation Severity Reliability Enabled
Java Quality Minor Unknown No
C/C++ Quality Minor Unknown No
Kotlin Quality Minor Unknown No

Subtype of REDUNDANT_COMPARISON for situations, where a code is redundant due to the condition that depends on the returned value of the function call.

Example (C/C++)

int func() {
    return 0;
}

void example(int a) {
    if (func() < 0) { //svace: emitted REDUNDANT_COMPARISON.RET
        ++a;
    }
}

Function example illustrates the defect: the returned value of func() has a constant value equals to 0 therefore the comparison with 0 is always false.

REDUNDANT_COMPARISON.ALWAYS_FALSE

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown Yes
C/C++ Quality Normal Unknown Yes
Go Quality Normal Unknown Yes
Kotlin Quality Normal Unknown Yes
Python Quality Normal Unknown Yes

This checker detects always-false expressions which are used in conditions that could change the control flow of a program.

Similar always-true expressions are reported by REDUNDANT_COMPARISON checker.

Example (C/C++)

if (p != NULL) {
    q = strdup(p);
    if (p == NULL) {
        // Error: allocation failed.
        // ...
    }
}

In the example above the author of the code first checks that the value of pointer p is not NULL, so it can be copied using strdup function but then makes a typo checking that the result of strdup isn’t NULL using the same variable p instead of q. This typo leads to the comparison always being false.

Example (Kotlin)

fun example(a: Any) {
    if (a is Int) {
        // Handle a as Int value.
    } else if (a is Float) {
        // Handle a as Float value.
    } else if (a is Int) {
        // Handle a as Long value.
    }
}

fun possibleFix(a: Any) {
  when (a) {
    is Int -> { /* Handle a as Int value. */ }
    is Float -> { /* Handle a as Float value. */ }
    is Long -> { /* Handle a as Long value. */ }
  }
}

Function example illustrates the defect: the result of the second a is Int expression is always false.

Function possibleFix illustrates a possible fix: replace the second a is Int expression by the a is Long expression. Also, it’s recommended to use when expression instead of large if-else expression. Kotlin compiler produces warnings about duplicate labels in when expressions.

REDUNDANT_COMPARISON.GLOBAL

Language Situation Severity Reliability Enabled
Java Quality Minor Unknown No
C/C++ Quality Minor Unknown No
Go Quality Minor Unknown No
Kotlin Quality Minor Unknown No

Subtype of REDUNDANT_COMPARISON.ALWAYS_FALSE for situations, where a code is redundant due to condition that depends on global variables.

Example

int flag = 0;

#define mode(a) flag

void f(int* a) {
    int m = mode(1);

    if(m) { //svace: REDUNDANT_COMPARISON.GLOBAL
        *a = 0;
    }
}

REDUNDANT_COMPARISON.MAP

Language Situation Severity Reliability Enabled
Java Quality Undefined Unknown No
Go Quality Undefined Unknown No
Kotlin Quality Undefined Unknown No

Subtype of REDUNDANT_COMPARISON for situations, when there is a presence/absence check of a key in a map, but the given key is always present/absent.

Example (Java)

import java.util.*;

public class Example {
    public void presentAfterRemove(Map<Object, String> box, Object key) {
        box.put(key, "value");
        if (box.containsKey(key)) { // svace: emitted REDUNDANT_COMPARISON.MAP
            // ...
        }
    }

    public void absentAfterRemove(Map<Object, String> box, Object key) {
        box.remove(key);
        if (box.containsKey(key)) { // svace: emitted REDUNDANT_COMPARISON.MAP
            // ...
        }
    }

    public void absentAfterClear(Map<Object, String> box, Object key) {
        box.clear();
        if (box.containsKey(key)) { // svace: emitted REDUNDANT_COMPARISON.MAP
            // ...
        }
    }
}

REDUNDANT_COMPARISON.EQUALS

Language Situation Severity Reliability Enabled
Java Quality Normal Unknown No

Subtype of REDUNDANT_COMPARISON when there is a test for equality of two variables, but their types do not have overlap in subtypes.

Example (Java)

import java.util.*;

public class Example {
    public void equalsTest(List<String> names) {
        String name = names.get(0);
        if ("<unknown>".equals(names)) { // svace: emitted REDUNDANT_COMPARISON.EQUALS
            // ...
        }
        name = names.get(1);
        if ("<unknown".equals(name)) { // svace: not_emitted REDUNDANT_COMPARISON.EQUALS
            // ...
        }
    }
}

This checker also works with JUnit asserts and reference equals.

Example (Java)

import junit.framework.Assert;
import java.util.*;

public class Example1 {
    interface CanFly {}
    interface CanSwim {}
    interface CanWalk {}

    class Duck1 implements CanFly {}
    class Duck2 implements CanSwim {}
    class Duck3 implements CanWalk, CanFly {}

    public void assertEqualsTest(CanFly first, CanSwim second, CanWalk third) {
        // ...
        Assert.assertEquals(first, second); // svace: emitted REDUNDANT_COMPARISON.EQUALS
        // ...
        // we can compare these objects because they can be of type Duck3
        Assert.assertEquals(first, third); // svace: not_emitted REDUNDANT_COMPARISON.EQUALS
        // ...
    }
}

Uninitialized values

UNINIT.LOCAL_VAR

Language Situation Severity Reliability Enabled
C/C++ Quality Major Average Yes

Related CWEs: CWE457.

This checker finds situations where a value of locally defined (automatic) variable is accessed, but the variable was never initialized.

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

Language Situation Severity Reliability Enabled
C/C++ Quality Major Very low No

This checker finds different situations, where a structure of structure field is not initialized before using.

Example

typedef struct {
    int k;
    int p;
    int q;
} T;

void test1(T* dst) {
    T val;

    val.k = 3;//Structure val was not fully initilialized

    memcpy(dst, &val, sizeof(T));//copied not initilized sturcture - UNINIT.STRUCT
}

UNINIT.BASE_CTOR

Language Situation Severity Reliability Enabled
Java Quality Major Unknown Yes
Kotlin Quality Major Unknown Yes

This detector finds following situations: method of some class overrides the method of its base class which is called from constructor, also, the method of derived class accesses this class field. This causes a NullPointerException to be thrown.

This detector requires the COLLECT_JAVA_METHOD_OVERRIDERS setting to be enabled. Run svace config COLLECT_JAVA_METHOD_OVERRIDERS true to enable the option.

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.

UNINIT.LIB

Language Situation Severity Reliability Enabled
C/C++ Quality Critical Unknown No

The checker finds situations when library function fills some buffer but caller code does not check that all values are filled. Buffer may have uninitialized memory.

Example (C/C++)

struct S {
    int a;
    int b;
    char buf[10];
    int c;
};

int func_with_error(int fd) {
    struct S s;

    read(fd, &s, sizeof(struct S));

    int x = s.c; //error, we don't know that structure 'S' was fully filled.
    return x; //x may contain uninitialized values
}

int fix(int fd) {
    struct S s;

    int len = read(fd, &s, sizeof(struct S));

    if (len!=sizeof(struct S))
        return -1;

    int x = s.c;
    return x;
}

Signal handlers

SIGHANDLER.ASYNC_UNSAFE

Language Situation Severity Reliability Enabled
C/C++ Quality Normal Unknown No

Related CERTs: CERT-SIG30-C.

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

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

The warning is emitted if signal handler calls any asynchronous-unsafe function.

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
C/C++ Quality Minor Unknown No

Related CERTs: CERT-SIG34-C.

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

The warning is emitted if a signal handler calls function signal for re-registering same handler again.

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
C/C++ Quality Normal Unknown No

Related CERTs: CERT-SIG31-C.

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

The warning is emitted if signal handler accesses or modifies global data with the type other than volatile sig_atomic_t.

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
C/C++ Quality Normal Unknown No

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

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

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
C/C++ Quality Minor Unknown No

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

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

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
C/C++ Quality Minor Unknown No

Related CERTs: CERT-SIG35-C.

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

The warning is emitted if signal handler for SIGFPE, SIGILL or SIGSEGV can return without calling the abort function.

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
C/C++ Quality Normal Unknown No

This checker detects cases of opening 32-bit dynamic libraries via dlopen from <dlfcn.h>. The warning is emitted if the first argument of dlopen starts with /lib or /usr/lib.

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.

LIB.FUNC_INCOMPATIBLE

Language Situation Severity Reliability Enabled
C/C++ Quality Normal Unknown No

Related CWEs: CWE404.

This checker finds situations where call of a library function is syntactically correct but semantically the values of the parameters don’t match their meaning, potentially leading to unexpected errors. For example, the C language libraries commonly use the same type (usually integer) for different entities (sockets, file descriptors, offsets, etc) which can be accidentally interchanged. The checker tracks the origins of the values and reports issues even when the incorrectly used values are explicitly converted to the library function parameter type.

Example (C)

void func() {
    int fd = open("some_file.txt", SOME_FLAGS);
    // Bind function is using for file descriptor
    bind(fd, ADDR, ADDR_LEN); 
}
Language Situation Severity Reliability Enabled
C/C++ Quality Minor Unknown No

Related CWEs: CWE61.

This Linux-specific checker finds situations where a path of a file being opened is not checked for containing a symbolic link beforehand. This potentially allows a malicious user to trick application to work with a substituted file instead of the right one. Such check can be performed using a stat system call.

Example (C)

void func(char *path, char *data) {
    // Opened file path is not checked for containing a symbolic link
    int fp = open(path, SOME_FLAGS);
    if (fp) {
        write(data, data, strlen(data));
    }
}

LIB.WRONG_CHECK

Language Situation Severity Reliability Enabled
C/C++ Quality Minor Unknown No

Related CWEs: CWE253.

This checker finds cases where the return value of a library function is processed incorrectly. Return value of library