False Negative: NullPointerException Not Detected in AtomicReference Usage

Summary:

  • What language is this for?
    • java
  • Which rule?
  • Why do you believe it’s a false-negative?
    • I have detailed it below.
  • I’m using
    • SonarQube Enterprise Edition (v9.9.6)
  • How can we reproduce the problem? Give us a self-contained snippet of code (formatted text, no screenshots)
    • I have detailed it below.

Description:
Dear SonarQube Team,

I would like to report a potential false negative case in SonarQube’s static analysis, where a possible NullPointerException (NPE) is not being detected. The issue arises in a scenario involving the use of AtomicReference and nullable objects.

Code Example:

import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.Nullable;

import io.reactivex.functions.Action;

public class ExampleService {

    private final AtomicReference<Cache<String, List<Object>>> cacheReference;

    public ExampleService(ConfigService configService) {
        cacheReference = new AtomicReference<>();
        configService.doOnConfigUpdate(() -> cacheReference.set(configService.createCache()));
    }

    public List<Object> getItems(String key) {
        Cache<String, List<Object>> cache = cacheReference.get(); // can be null
        return cache.getOrReload(key); // NPE could be thrown
    }

    public static class ConfigService {
        public void doOnConfigUpdate(Action createOrUpdateAction) {
            // This method performs the action of safely executing the cache creation or update
            // and subscribes to configuration changes.
        }

        @Nullable
        public Cache<String, List<Object>> createCache() {
            return null;
        }
    }

    public interface Cache<K, V> {
        V getOrReload(K key);
    }
}

Issue Details:
In the above code, the ConfigService.createCache() method is annotated with @Nullable, indicating that it can return null. This null value is then set to the AtomicReference<Cache<String, List<Object>>> cacheReference. When getItems(String key) is called, cacheReference.get() can return null, leading to a potential NPE when cache.getOrReload(key) is invoked.

Despite this clear path to a NullPointerException, SonarQube does not flag this as an issue. This oversight could lead to runtime exceptions that are not caught during the development phase.

Request:
Before requesting an enhancement, I would like to inquire if there is a way to configure the current version of SonarQube (v9.9.6) to detect this NPE scenario. If there is a configuration or rule adjustment that can address this, I would appreciate your guidance on implementing it.

If no such configuration is available, I kindly request that the SonarQube team consider improving detection capabilities for potential NPEs involving AtomicReference and nullable objects. Enhancing detection in these scenarios would greatly assist developers in identifying and resolving critical issues early in the development process.

Thank you for your attention to this matter. :bowing_man:

Hello @coodercl , thanks a lot for reporting this issue.

As I see it, the problem does not lie on @Nullable, as in fact it’s the default behaviour for Java. The issue happens with all access to subelements that could be null. Currently the rule is only checking the variable mentioned, but not it’s members.

It happens the same with this code :

        List<Integer> items = List.of(1, 2, null);
        var b = items.get(3).intValue() * 2; // NPE could be thrown
         ....
            private void getObject() {
        MyObject obj = new MyObject();
        obj.value = null;
        System.out.println(obj.value.intValue() * 2); // NPE could be thrown
    }
    class MyObject {
        public Integer value;
    }     

There are 2 rules that cover some of these issues S2259 and S6555. The latter is not enabled by default at “Sonar Way” quality profile ( you can extend it and enable the rule, and assign this QP to the project)

if we have this code :

public class ExampleService {
    private final AtomicInteger cacheReference;
    public ExampleService() {
        cacheReference = new AtomicInteger();
        cacheReference.set(createCache());
        System.out.println("Cache created: " + getCache());
        getItems();
        getObject();
    }
    public Integer getCache() {
        Integer cache = cacheReference.get(); // can be null
        return cache.intValue() * 2; // NPE not catched
    }
    private void getVar() {
        Integer a = null;
        System.out.println(a.intValue() * 2); // S6555 catches this issue
    }
    private void getItems() {
        List<Integer> items = List.of(1, 2, null);
        var b = items.get(3).intValue() * 2; // NPE not catched
        System.out.println(b);
    }
    private void getObject() {
        MyObject obj = new MyObject();
        obj.value = null;
        System.out.println(obj.value.intValue() * 2); // S6555 catches this issue
    }
    class MyObject {
        @Nullable
        public Integer value;
    }
    @Nullable
    public Integer createCache() {
        return null;
    }
}

I have created this Jira ticket in order to work on this case.
https://sonarsource.atlassian.net/browse/SONARJAVA-5337

To give a bit of context, work on improving S2259 is currently stopped in favor of another approach to doing symbolic execution checks, which is superseded by S6555. But at this moment, there’s no clarity on when there will be capacity to work on it.