You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
638 lines
14 KiB
638 lines
14 KiB
3 years ago
|
/*
|
||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*/
|
||
|
|
||
|
package codetoanalyze.java.infer;
|
||
|
|
||
|
import android.annotation.SuppressLint;
|
||
|
import android.content.ContentResolver;
|
||
|
import android.content.Context;
|
||
|
import android.database.Cursor;
|
||
|
import android.database.sqlite.SQLiteDatabase;
|
||
|
import android.text.TextUtils;
|
||
|
import com.facebook.infer.annotation.Assertions;
|
||
|
import com.google.common.base.Optional;
|
||
|
import com.google.common.base.Preconditions;
|
||
|
import com.google.common.collect.ImmutableList;
|
||
|
import java.io.File;
|
||
|
import java.io.FileInputStream;
|
||
|
import java.io.FileOutputStream;
|
||
|
import java.io.IOException;
|
||
|
import java.nio.channels.FileChannel;
|
||
|
import java.nio.channels.FileLock;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.concurrent.locks.Lock;
|
||
|
import javax.annotation.Nonnull;
|
||
|
import javax.annotation.Nullable;
|
||
|
|
||
|
public class NullPointerExceptions {
|
||
|
|
||
|
class A {
|
||
|
int x;
|
||
|
|
||
|
public void method() {}
|
||
|
}
|
||
|
|
||
|
// npe local with field
|
||
|
public int nullPointerException() {
|
||
|
A a = null;
|
||
|
return a.x;
|
||
|
}
|
||
|
|
||
|
public A canReturnNullObject(boolean ok) {
|
||
|
A a = new A();
|
||
|
if (ok) return a;
|
||
|
else return null;
|
||
|
}
|
||
|
|
||
|
public static void expectNotNullObjectParameter(A a) {
|
||
|
a.method();
|
||
|
}
|
||
|
|
||
|
public static void expectNotNullArrayParameter(A[] array) {
|
||
|
array.clone();
|
||
|
}
|
||
|
|
||
|
// npe with branching, interprocedural
|
||
|
public int nullPointerExceptionInterProc() {
|
||
|
A a = canReturnNullObject(false);
|
||
|
return a.x;
|
||
|
}
|
||
|
|
||
|
// npe with exception handling
|
||
|
public int nullPointerExceptionWithExceptionHandling(boolean ok) {
|
||
|
A a = null;
|
||
|
try {
|
||
|
throw new Exception();
|
||
|
} catch (Exception e) {
|
||
|
return a.x;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class B {
|
||
|
A a;
|
||
|
|
||
|
void test() {}
|
||
|
}
|
||
|
|
||
|
public static int nullPointerExceptionWithArray() {
|
||
|
A[] array = new A[] {null};
|
||
|
A t = array[0];
|
||
|
return t.x;
|
||
|
}
|
||
|
|
||
|
// npe with a chain of fields
|
||
|
class C {
|
||
|
B b;
|
||
|
}
|
||
|
|
||
|
public int nullPointerExceptionWithAChainOfFields(C c) {
|
||
|
c.b = new B();
|
||
|
return c.b.a.x;
|
||
|
}
|
||
|
|
||
|
// npe with a null object parameter
|
||
|
public static void nullPointerExceptionWithNullObjectParameter() {
|
||
|
expectNotNullObjectParameter(null);
|
||
|
}
|
||
|
|
||
|
// npe with a null array parameter
|
||
|
public static void nullPointerExceptionWithNullArrayParameter() {
|
||
|
expectNotNullArrayParameter(null);
|
||
|
}
|
||
|
|
||
|
public static void nullPointerExceptionFromFaillingResourceConstructor() throws IOException {
|
||
|
FileInputStream fis = null;
|
||
|
try {
|
||
|
fis = new FileInputStream(new File("whatever.txt"));
|
||
|
} catch (IOException e) {
|
||
|
} finally {
|
||
|
fis.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static void nullPointerExceptionFromFailingFileOutputStreamConstructor()
|
||
|
throws IOException {
|
||
|
FileOutputStream fos = null;
|
||
|
try {
|
||
|
fos = new FileOutputStream(new File("whatever.txt"));
|
||
|
} catch (IOException e) {
|
||
|
} finally {
|
||
|
fos.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int x;
|
||
|
|
||
|
public void nullPointerExceptionFromNotKnowingThatThisIsNotNull() {
|
||
|
if (this == null) {}
|
||
|
this.x = 4;
|
||
|
}
|
||
|
|
||
|
public <T> T id_generics(T o) {
|
||
|
o.toString();
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
public A frame(A x) {
|
||
|
return id_generics(x);
|
||
|
}
|
||
|
|
||
|
public void nullPointerExceptionUnlessFrameFails() {
|
||
|
String s = null;
|
||
|
Object a = frame(new A());
|
||
|
if (a instanceof A) {
|
||
|
s.length();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class D {
|
||
|
int x;
|
||
|
}
|
||
|
|
||
|
public int preconditionCheckStateTest(D d) {
|
||
|
Preconditions.checkState(d != null);
|
||
|
return d.x;
|
||
|
}
|
||
|
|
||
|
public void genericMethodSomewhereCheckingForNull(String s) {
|
||
|
if (s == null) {}
|
||
|
}
|
||
|
|
||
|
public void FP_noNullPointerExceptionAfterSkipFunction() {
|
||
|
String t = new String("Hello!");
|
||
|
String s = t.toString();
|
||
|
genericMethodSomewhereCheckingForNull(s);
|
||
|
s.length();
|
||
|
}
|
||
|
|
||
|
String hashmapNPE(HashMap h, Object o) {
|
||
|
return (h.get(o).toString());
|
||
|
}
|
||
|
|
||
|
String NPEhashmapProtectedByContainsKey(HashMap h, Object o) {
|
||
|
if (h.containsKey(o)) {
|
||
|
return (h.get(o).toString());
|
||
|
}
|
||
|
return "aa";
|
||
|
}
|
||
|
|
||
|
int NPEvalueOfFromHashmapBad(HashMap<Integer, Integer> h, int position) {
|
||
|
return h.get(position);
|
||
|
}
|
||
|
|
||
|
Integer NPEvalueOfFromHashmapGood(HashMap<Integer, Integer> h, int position) {
|
||
|
return h.get(position);
|
||
|
}
|
||
|
|
||
|
void nullPointerExceptionInArrayLengthLoop(Object[] arr) {
|
||
|
for (int i = 0; i < arr.length; i++) {
|
||
|
Object x = null;
|
||
|
x.toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Context mContext;
|
||
|
ContentResolver mContentResolver;
|
||
|
|
||
|
public void cursorFromContentResolverNPE(String customClause) {
|
||
|
String[] projection = {"COUNT(*)"};
|
||
|
String selectionClause = selectionClause = customClause;
|
||
|
Cursor cursor =
|
||
|
mContext.getContentResolver().query(null, projection, selectionClause, null, null);
|
||
|
cursor.close();
|
||
|
}
|
||
|
|
||
|
public int cursorQueryShouldNotReturnNull(SQLiteDatabase sqLiteDatabase) {
|
||
|
Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null);
|
||
|
try {
|
||
|
return cursor.getCount();
|
||
|
} finally {
|
||
|
cursor.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Object[] arr = new Object[1];
|
||
|
|
||
|
Object arrayReadShouldNotCauseSymexMemoryError(int i) {
|
||
|
arr[i].toString();
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
void nullPointerExceptionCallArrayReadMethod() {
|
||
|
arr[0] = new Object();
|
||
|
arrayReadShouldNotCauseSymexMemoryError(0).toString();
|
||
|
}
|
||
|
|
||
|
public void sinkWithNeverNullSource() {
|
||
|
NeverNullSource source = new NeverNullSource();
|
||
|
T t = source.get();
|
||
|
t.f();
|
||
|
}
|
||
|
|
||
|
public void otherSinkWithNeverNullSource() {
|
||
|
SomeLibrary source = new SomeLibrary();
|
||
|
T t = source.get();
|
||
|
t.f();
|
||
|
}
|
||
|
|
||
|
private @Nullable Object mFld;
|
||
|
|
||
|
void nullableFieldNPE() {
|
||
|
mFld.toString();
|
||
|
}
|
||
|
|
||
|
void guardedNullableFieldDeref() {
|
||
|
if (mFld != null) mFld.toString();
|
||
|
}
|
||
|
|
||
|
void allocNullableFieldDeref() {
|
||
|
mFld = new Object();
|
||
|
mFld.toString();
|
||
|
}
|
||
|
|
||
|
void nullableParamNPE(@Nullable Object param) {
|
||
|
param.toString();
|
||
|
}
|
||
|
|
||
|
void guardedNullableParamDeref(@Nullable Object param) {
|
||
|
if (param != null) param.toString();
|
||
|
}
|
||
|
|
||
|
void allocNullableParamDeref(@Nullable Object param) {
|
||
|
param = new Object();
|
||
|
param.toString();
|
||
|
}
|
||
|
|
||
|
native boolean test();
|
||
|
|
||
|
Object getObj() {
|
||
|
if (test()) {
|
||
|
return new Object();
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Boolean getBool() {
|
||
|
if (test()) {
|
||
|
return new Boolean(true);
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void derefGetterAfterCheckShouldNotCauseNPE() {
|
||
|
if (getObj() != null) {
|
||
|
getObj().toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void derefBoxedGetterAfterCheckShouldNotCauseNPE() {
|
||
|
boolean b = getBool() != null && getBool();
|
||
|
}
|
||
|
|
||
|
static void derefNonThisGetterAfterCheckShouldNotCauseNPE() {
|
||
|
NullPointerExceptions c = new NullPointerExceptions();
|
||
|
if (c.getObj() != null) {
|
||
|
c.getObj().toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void badCheckShouldCauseNPE() {
|
||
|
if (getBool() != null) getObj().toString();
|
||
|
}
|
||
|
|
||
|
void nullPointerExceptionArrayLength() {
|
||
|
Object[] arr = null;
|
||
|
int i = arr.length;
|
||
|
}
|
||
|
|
||
|
class $$Class$Name$With$Dollars {
|
||
|
void npeWithDollars() {
|
||
|
String s = null;
|
||
|
int n = s.length();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void nullableNonNullStringAfterTextUtilsIsEmptyCheckShouldNotCauseNPE(@Nullable String str) {
|
||
|
if (!TextUtils.isEmpty(str)) {
|
||
|
str.length();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void someNPEAfterResourceLeak() {
|
||
|
T t = CloseableAsResourceExample.sourceOfNullWithResourceLeakBad();
|
||
|
t.f();
|
||
|
}
|
||
|
|
||
|
private Object mOkObj = new Object();
|
||
|
|
||
|
public void nullableParamReassign1(@Nullable Object o) {
|
||
|
if (o == null) {
|
||
|
o = mOkObj;
|
||
|
}
|
||
|
o.toString();
|
||
|
}
|
||
|
|
||
|
public void nullableParamReassign2(@Nullable Object o, Object okObj) {
|
||
|
if (o == null) {
|
||
|
o = okObj;
|
||
|
}
|
||
|
o.toString();
|
||
|
}
|
||
|
|
||
|
private @Nullable Object mNullableField;
|
||
|
|
||
|
public void nullableFieldReassign1() {
|
||
|
if (mNullableField == null) {
|
||
|
mNullableField = mOkObj;
|
||
|
}
|
||
|
mNullableField.toString();
|
||
|
}
|
||
|
|
||
|
public void nullableFieldReassign2(Object okObj) {
|
||
|
if (mNullableField == null) {
|
||
|
mNullableField = okObj;
|
||
|
}
|
||
|
mNullableField.toString();
|
||
|
}
|
||
|
|
||
|
public void nullableFieldReassign3(Object param) {
|
||
|
mNullableField = param;
|
||
|
mNullableField.toString();
|
||
|
}
|
||
|
|
||
|
public Object nullableGetter() {
|
||
|
return mNullableField;
|
||
|
}
|
||
|
|
||
|
public void derefNullableGetter() {
|
||
|
Object o = nullableGetter();
|
||
|
o.toString();
|
||
|
}
|
||
|
|
||
|
public @Nullable Object nullableRet(boolean b) {
|
||
|
if (b) {
|
||
|
return null;
|
||
|
}
|
||
|
return new Object();
|
||
|
}
|
||
|
|
||
|
public void derefNullableRet(boolean b) {
|
||
|
Object ret = nullableRet(b);
|
||
|
ret.toString();
|
||
|
}
|
||
|
|
||
|
public void derefNullableRetOK(boolean b) {
|
||
|
Object ret = nullableRet(b);
|
||
|
if (ret != null) {
|
||
|
ret.toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public native @Nullable Object undefNullableRet();
|
||
|
|
||
|
public void derefUndefNullableRet() {
|
||
|
Object ret = undefNullableRet();
|
||
|
ret.toString();
|
||
|
}
|
||
|
|
||
|
public void derefUndefNullableRetOK() {
|
||
|
Object ret = undefNullableRet();
|
||
|
if (ret != null) {
|
||
|
ret.toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void assumeUndefNullableIdempotentOk() {
|
||
|
if (undefNullableRet() != null) {
|
||
|
undefNullableRet().toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Object undefNullableWrapper() {
|
||
|
return undefNullableRet();
|
||
|
}
|
||
|
|
||
|
public void derefUndefNullableRetWrapper() {
|
||
|
undefNullableWrapper().toString();
|
||
|
}
|
||
|
|
||
|
private int returnsThreeOnlyIfRetNotNull(Object obj) {
|
||
|
if (obj == null) {
|
||
|
return 2;
|
||
|
}
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
public void testNullablePrecision() {
|
||
|
Object ret = undefNullableRet();
|
||
|
if (returnsThreeOnlyIfRetNotNull(ret) == 3) {
|
||
|
ret.toString(); // shouldn't warn here
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public @Nullable String testSystemGetPropertyArgument() {
|
||
|
String s = System.getProperty(null);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
public void testSystemGetPropertyReturn() {
|
||
|
String s = System.getProperty("");
|
||
|
int n = s.length();
|
||
|
}
|
||
|
|
||
|
Object retUndefined() {
|
||
|
return "".toString(); // toString is a skip function
|
||
|
}
|
||
|
|
||
|
Object derefUndefinedCallee() {
|
||
|
// if retUndefined() is handled incorrectly, we get a symexec_memory_error here
|
||
|
retUndefined().toString();
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
void derefNull() {
|
||
|
// should be NPE, but will not be reported if we handled retUndefined() incorrectly
|
||
|
derefUndefinedCallee().toString();
|
||
|
}
|
||
|
|
||
|
@SuppressLint("NULL_DEREFERENCE")
|
||
|
void shouldNotReportNPE() {
|
||
|
Object o = null;
|
||
|
o.toString();
|
||
|
}
|
||
|
|
||
|
void shouldNotReportOnSkippedSource() {
|
||
|
Object o = SkippedSourceFile.createdBySkippedFile();
|
||
|
o.toString();
|
||
|
}
|
||
|
|
||
|
int nullListFiles(String pathname) {
|
||
|
File dir = new File(pathname);
|
||
|
File[] files = dir.listFiles();
|
||
|
return files.length; // expect possible NullPointerException as files == null is possible
|
||
|
}
|
||
|
|
||
|
native Object unknownFunc();
|
||
|
|
||
|
void nullDerefernceReturnOfSkippedFunctionBad() {
|
||
|
Object object = unknownFunc();
|
||
|
if (object == null) {
|
||
|
object.toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
native @Nonnull Object doesNotReturnNull();
|
||
|
|
||
|
void noNPEWhenCallingSkippedNonnullAnnotatedMethodGood() {
|
||
|
Object object = doesNotReturnNull();
|
||
|
if (object == null) {
|
||
|
object.toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Object callUnknownFunc() {
|
||
|
return unknownFunc();
|
||
|
}
|
||
|
|
||
|
void dontReportOnNullableDirectReassignmentToUnknown(@Nullable Object o) {
|
||
|
o = unknownFunc();
|
||
|
o.toString();
|
||
|
}
|
||
|
|
||
|
void dontReportOnNullableIndirectReassignmentToUnknown(@Nullable Object o) {
|
||
|
o = callUnknownFunc();
|
||
|
o.toString();
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
Object wrapUnknownFuncWithNullable() {
|
||
|
return unknownFunc();
|
||
|
}
|
||
|
|
||
|
void deferenceNullableMethodCallingSkippedMethodBad() {
|
||
|
wrapUnknownFuncWithNullable().toString();
|
||
|
}
|
||
|
|
||
|
String nullTryLock(FileChannel chan) throws IOException {
|
||
|
FileLock lock = chan.tryLock();
|
||
|
return lock.toString(); // expect possible NullPointerException as lock == null is possible
|
||
|
}
|
||
|
|
||
|
String tryLockThrows(FileChannel chan) {
|
||
|
try {
|
||
|
FileLock lock = chan.tryLock();
|
||
|
return (lock != null ? lock.toString() : "");
|
||
|
} catch (IOException e) {
|
||
|
Object o = null;
|
||
|
return o.toString(); // expect NullPointerException as tryLock can throw
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class L {
|
||
|
L next;
|
||
|
}
|
||
|
|
||
|
Object returnsNullAfterLoopOnList(L l) {
|
||
|
while (l != null) {
|
||
|
l = l.next;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
void dereferenceAfterLoopOnList(L l) {
|
||
|
Object obj = returnsNullAfterLoopOnList(l);
|
||
|
obj.toString();
|
||
|
}
|
||
|
|
||
|
void dereferenceAfterUnlock1(Lock l) {
|
||
|
l.unlock();
|
||
|
String s = l.toString();
|
||
|
s = null;
|
||
|
s.toString(); // Expect NPE here
|
||
|
}
|
||
|
|
||
|
void dereferenceAfterUnlock2(Lock l) {
|
||
|
synchronized (l) {
|
||
|
String b = null;
|
||
|
}
|
||
|
String s = l.toString();
|
||
|
s = null;
|
||
|
s.toString(); // Expect NPE here
|
||
|
}
|
||
|
|
||
|
void optionalNPE(Optional<Object> o) {
|
||
|
o.orNull().toString();
|
||
|
}
|
||
|
|
||
|
void stringConstantEqualsTrueNotNPE() {
|
||
|
final String c1 = "Test string!";
|
||
|
final String c2 = "Test string!";
|
||
|
String s = null;
|
||
|
if (c1.equals(c1)) {
|
||
|
s = "safe";
|
||
|
}
|
||
|
s.toString(); // No NPE
|
||
|
s = null;
|
||
|
if (c1.equals(c2)) {
|
||
|
s = "safe";
|
||
|
}
|
||
|
s.toString(); // No NPE
|
||
|
}
|
||
|
|
||
|
void stringConstantEqualsFalseNotNPE_FP() {
|
||
|
// This won't actually cause an NPE, but our current model for String.equals
|
||
|
// returns boolean_undefined for all cases other than String constant
|
||
|
// equality. Consider handling constant inequality in the future.
|
||
|
final String c1 = "Test string 1";
|
||
|
final String c2 = "Test string 2";
|
||
|
String s = null;
|
||
|
if (!c1.equals(c2)) {
|
||
|
s = "safe";
|
||
|
}
|
||
|
s.toString(); // No NPE
|
||
|
}
|
||
|
|
||
|
String getString2() {
|
||
|
return "string 2";
|
||
|
}
|
||
|
|
||
|
void stringVarEqualsFalseNPE() {
|
||
|
final String c1 = "Test string 1";
|
||
|
String c2 = "Test " + getString2();
|
||
|
String s = null;
|
||
|
if (!c1.equals(c2)) {
|
||
|
s.toString(); // NPE
|
||
|
}
|
||
|
}
|
||
|
|
||
|
String assertParameterNotNullableOk(@Nullable Object object) {
|
||
|
return Assertions.assertNotNull(object).toString();
|
||
|
}
|
||
|
|
||
|
interface I {
|
||
|
@Nullable Object mObject = null;
|
||
|
}
|
||
|
|
||
|
class E implements I {
|
||
|
|
||
|
void dereferenceNullableInterfaceFieldBad() {
|
||
|
mObject.toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Object getObject() {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
void addNullToImmutableListBuilderBad() {
|
||
|
ImmutableList.Builder<Object> listBuilder = ImmutableList.builder();
|
||
|
listBuilder.add(getObject());
|
||
|
}
|
||
|
}
|