Question

How can I build a string that can pass null check but will throw null pointer exception when call str.length?

I met a quite strange case in a program with jdk1.8, that I got a null pointer exception in String.length (which is in StringBuilder.append()) method,

the exact position of the null pointer exception is here in String of jdk,

public int length() {
        return value.length;  //line 623
    }

and it's called in AbstractStringBuilder class :

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();  //here line 447
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

I can't understand how this can happen, as there is a null check before str.length() called.

And maybe due to the same reason, all the way I tried to print the string content crashed, so I can't get what string content caused this.

Now, what I want to know is what can cause this, can I build a string content to reproduce this? Thanks for any ideas.

the stacktrace

java.lang.NullPointerException
at java.lang.String.length(String.java:623)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:447)
at java.lang.StringBuilder.append(StringBuilder.java:141)
at ....toString(....java:14)  //a toString method of some class
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at java.util.AbstractCollection.toString(AbstractCollection.java:462)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at com

I got this in both these two enviroments:

java version "1.8.0_102" Java(TM) SE Runtime Environment (build 1.8.0_102-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

and

openjdk version "1.8.0_345" OpenJDK Runtime Environment (Temurin)(build 1.8.0_345-b01) OpenJDK 64-Bit Server VM (Temurin)(build 25.345-b01, mixed mode)

 2  88  2
1 Jan 1970

Solution

 3

I was able to reproduce this error in a pretty convoluted way, which I doubt could happen by mistake. The String class has a package-protected constructor that takes a char[] and a boolean (that isn't actually used). This constructor is meant for internal use, and just naively stores the char[] without performing any validation on it.

You can use reflection to invoke it, and produce the described NullPointerExcpetion:

// Use reflection to invoke "new String((char[]) null, true)";
Constructor<String> ctor =
    String.class.getDeclaredConstructor(char[].class, boolean.class);
ctor.setAccessible(true);
String s = ctor.newInstance(null, true);

StringBuilder sb = new StringBuilder();
sb.append(s); // Null pointer exception when calling String.length
2024-07-25
Mureinik