New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
debug symbols trigger false positive RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE on java11 #1931
Comments
Oddly enough 07d001a looks at line numbers that are present regardless of debug symbols, so could be that the exact culprit is somewhere else |
Generated code semantically looks like following (except that try-catch blocks are set in extra table and there are gotos between blocks) final Stream<Object> stream = Stream.of(); // note: try-with-resources can enter body with null resource, that's not an exceptional case
try {
stream.hashCode(); // user code
} catch (Throwable e1) {
try {
if (stream != null) { // javac generated code
stream.close()
}
} catch (Throwable e2) {
e1.addSuppressed(e2)
}
} finally { // not quite, this doesn't get executed after catch block as catch block handles close() already
if (stream != null) { // javac generated code
stream.close()
}
} but actually finally block gets inlined to become final Stream<Object> stream = Stream.of(); // note: try-with-resources can enter body with null resource, that's not an exceptional case
try {
stream.hashCode(); // user code
if (stream != null) { // javac generated code, also try block ends before this line
stream.close()
}
} catch (Throwable e1) {
try {
if (stream != null) { // javac generated code
stream.close()
}
} catch (Throwable e2) {
e1.addSuppressed(e2)
}
} so inspection makes sense, and maybe javac could potentially eliminate the null check. But it doesn't explain why debug info presence triggers the inspection |
Another clue is that it also matters whether resource used is an interface or a class (invokeinterface vs invokevirtual) public class A {
private int foo(AutoCloseable x) throws Exception {
try (var obj = x) { // RCN with debug symbols
return obj.hashCode();
}
}
private int bar(B x) throws Exception {
try (var obj = x) { // no RCN regardless of debug symbols
return obj.hashCode();
}
}
private int baz(C x) throws Exception {
try (var obj = x) { // RCN with debug symbols
return obj.hashCode();
}
}
private static abstract class B implements AutoCloseable {
}
private static interface C extends AutoCloseable {
}
} |
So in the original bytecode
having variable debug info seems to be "helping" analysis connect dereferencing with null check |
will try adding invokeinterface block along with invokevirtual in https://github.com/spotbugs/spotbugs/blob/master/spotbugs/src/main/java/edu/umd/cs/findbugs/detect/FindNullDeref.java#L1907 might be the charm |
… presence of debug symbols Resolves spotbugs#1931 by improving on spotbugs#1575 Probably also spotbugs#868, spotbugs#1338, spotbugs#1694 Not entirely sure about what exactly is happening but since 4.4.0 on javac11+ (openjdk) findbugs was reporting redundant null check if all of following holds - code in try block always dereferences the object - try-with-resources is applied to an object via interface reference - debug symbols are present (LocalVariableTable, via javac -g) javac11+ generates bytecode along the lines of ``` try-body if (obj != null) obj.close() on_any_exceptions_in_try_body: if (obj != null) obj.close() ``` This does always close obj if it is not null Javac could've deduced that if try-body always throws on `obj==null` then null check isn't needed but isn't obliged to do so, for simplicity for example. Fix by treating invokeinterface same way as invokevirtual in FindNullDeref Adding a minimal test for interface case. Note that test suite is compiled with default gradle settings that have debug symbols enabled.
… presence of debug symbols (#1932) * Fix RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE in try-with-resources in presence of debug symbols Resolves #1931 by improving on #1575 Probably also #868, #1338, #1694 Not entirely sure about what exactly is happening but since 4.4.0 on javac11+ (openjdk) findbugs was reporting redundant null check if all of following holds - code in try block always dereferences the object - try-with-resources is applied to an object via interface reference - debug symbols are present (LocalVariableTable, via javac -g) javac11+ generates bytecode along the lines of ``` try-body if (obj != null) obj.close() on_any_exceptions_in_try_body: if (obj != null) obj.close() ``` This does always close obj if it is not null Javac could've deduced that if try-body always throws on `obj==null` then null check isn't needed but isn't obliged to do so, for simplicity for example. Fix by treating invokeinterface same way as invokevirtual in FindNullDeref Adding a minimal test for interface case. Note that test suite is compiled with default gradle settings that have debug symbols enabled. * Add changelog entry * Update Issue600Test assertions to catch the issue with interfaces * fix indentation
Starting 4.4.0 and up to at least 4.5.3 there's a regression described below.
Especially annoying as is block log4j CVE mitigation upgrade to 4.5.3
with debug symbols
without debug symbols
Output of
javap -l -p -c -v
related to methodfoo
the difference is that debug version has this block
and triggers false positive, while non-debug version doesn't have this block and works
related to
#868
#1338
#1694
likely introduced in 07d001a #1575
The text was updated successfully, but these errors were encountered: