diff --git a/infer/tests/codetoanalyze/java/pulse/AnalysisStops.java b/infer/tests/codetoanalyze/java/pulse/AnalysisStops.java new file mode 100644 index 000000000..93b2c5ef7 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/AnalysisStops.java @@ -0,0 +1,270 @@ +/* + * 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 java.util.Iterator; + +public class AnalysisStops { + + private native Object externalFunc(); + + public void skipPointerDerefMayCauseLocalFalseNegativeBad() { + Object ret = externalFunc(); + ret.toString(); + int i = 1 / 0; + } + + private Object skipPointerDerefPreventsSpecInferenceRetObj() { + Object ret = externalFunc(); + ret.toString(); + return new Object(); + } + + public void skipPointerDerefMayCauseCalleeFalsePositiveOk() { + Object o = skipPointerDerefPreventsSpecInferenceRetObj(); + o.toString(); + } + + private int skipPointerDerefPreventsSpecInferenceRetZero() { + Object ret = externalFunc(); + ret.toString(); + return 0; + } + + public void skipPointerDerefMayCauseCalleeFalseNegativeBad() { + int ret = skipPointerDerefPreventsSpecInferenceRetZero(); + int i = 1 / ret; + } + + private void divideByParam(int i) { + int j = 1 / i; + } + + public void skipPointerDerefMayCauseInterprocFalseNegativeBad() { + int i = skipPointerDerefPreventsSpecInferenceRetZero(); + divideByParam(i); + } + + private String castExternalPreventsSpecInference() { + return (String) externalFunc(); + } + + public void castFailureOnUndefinedObjMayCauseFalseNegativeBad() { + castExternalPreventsSpecInference(); + int i = 1 / 0; + } + + public void callOnCastUndefinedObjMayCauseFalseNegativeBad() { + String s = castExternalPreventsSpecInference(); + s.toString(); + int i = 1 / 0; + } + + private static class MyObj { + Object f; + MyObj rec; + int i; + + public int retOne() { + return 1; + } + + public int retZero() { + return 0; + } + } + + private native MyObj externalFunc2(); + + public void callOnUndefinedObjMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + int i = 1 / ret.retZero(); + } + + public void callOnUndefinedObjMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + int i = 1 / ret.retOne(); + } + + public void fieldWriteOnUndefinedObjMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + ret.f = new Object(); + int i = 1 / 0; + } + + public void fieldWriteOnUndefinedObjMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + ret.f = new Object(); + ret.f.toString(); + } + + public void fieldReadOnUndefinedObjMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + Object o = ret.f; + int i = 1 / 0; + } + + public void fieldReadOnUndefinedObjMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + Object o = ret.f; + o.toString(); + } + + public void recursiveAngelicTypesMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + MyObj rec1 = ret.rec; + MyObj rec2 = rec1.rec; + int i = 1 / 0; + } + + public void recursiveAngelicTypesMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + MyObj rec1 = ret.rec; + rec1.rec.toString(); + } + + public void infiniteMaterializationMayCauseFalseNegativeBad(boolean b) { + MyObj rec = externalFunc2(); + while (b) { + rec = rec.rec; + } + int i = 1 / 0; + } + + public void infiniteMaterializationMayCauseFalsePositiveOk(boolean b) { + MyObj rec = externalFunc2(); + while (b) { + rec = rec.rec; + } + rec.toString(); + } + + public void primitiveFieldOfAngelicObjMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + if (ret.i == 0) { + int i = 1 / 0; + } else { + int i = 1 / 0; + } + } + + public void primitiveFieldOfAngelicObjMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + if (ret.i != 0) { + int i = 1 / ret.i; + } + } + + public void heapFieldOfAngelicObjMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + Object obj = ret.f; + if (obj == ret.f) { + int i = 1 / 0; + } + } + + public void heapFieldOfAngelicObjMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + Object obj = ret.f; + if (obj != ret.f) { + int i = 1 / 0; + } + } + + public void fieldReadAferCastMayCauseFalseNegativeBad(Iterator iter) { + MyObj ret = iter.next(); + Object obj = ret.f; + obj.toString(); + int i = ret.i; + if (i == 7) { + int j = 1 / 0; + } + } + + public void derefParamOk(MyObj obj) { + Object f = obj.f; + f.toString(); + } + + public void fieldReadInCalleeMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + derefParamOk(ret); + } + + public void fieldReadInCalleeMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + ret.f = null; + derefParamOk(ret); + } + + public void fieldReadInCalleeWithAngelicObjFieldMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + derefParamOk(ret.rec); + } + + public void fieldReadInCalleeWithAngelicObjFieldMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + ret.rec.f = null; + derefParamOk(ret.rec); + } + + public void accessPathOnParamOk(MyObj obj) { + MyObj ret = obj.rec; + Object f = ret.f; + f.toString(); + } + + public void accessPathInCalleeMayCauseFalsePositiveOk() { + MyObj ret = externalFunc2(); + accessPathOnParamOk(ret); + } + + public void accessPathInCalleeMayCauseFalseNegativeBad() { + MyObj ret = externalFunc2(); + ret.rec.f = null; + accessPathOnParamOk(ret); + } + + public void skipFunctionInLoopMayCauseFalseNegativeBad() { + Object o = null; + for (int i = 0; i < 10; i++) { + externalFunc(); + } + o.toString(); + } + + // will fail to find error unless spec inference succeeds for all callees + public void specInferenceMayFailAndCauseFalseNegativeBad(boolean b, Iterator iter) { + skipPointerDerefMayCauseLocalFalseNegativeBad(); + skipPointerDerefPreventsSpecInferenceRetObj(); + skipPointerDerefPreventsSpecInferenceRetZero(); + skipPointerDerefMayCauseCalleeFalseNegativeBad(); + skipPointerDerefMayCauseInterprocFalseNegativeBad(); + castFailureOnUndefinedObjMayCauseFalseNegativeBad(); + callOnCastUndefinedObjMayCauseFalseNegativeBad(); + callOnUndefinedObjMayCauseFalseNegativeBad(); + callOnUndefinedObjMayCauseFalsePositiveOk(); + fieldWriteOnUndefinedObjMayCauseFalseNegativeBad(); + fieldWriteOnUndefinedObjMayCauseFalsePositiveOk(); + fieldReadOnUndefinedObjMayCauseFalseNegativeBad(); + fieldReadOnUndefinedObjMayCauseFalsePositiveOk(); + recursiveAngelicTypesMayCauseFalseNegativeBad(); + recursiveAngelicTypesMayCauseFalsePositiveOk(); + infiniteMaterializationMayCauseFalseNegativeBad(b); + infiniteMaterializationMayCauseFalsePositiveOk(b); + primitiveFieldOfAngelicObjMayCauseFalsePositiveOk(); + primitiveFieldOfAngelicObjMayCauseFalseNegativeBad(); + heapFieldOfAngelicObjMayCauseFalsePositiveOk(); + heapFieldOfAngelicObjMayCauseFalseNegativeBad(); + fieldReadAferCastMayCauseFalseNegativeBad(iter); + fieldReadInCalleeMayCauseFalsePositiveOk(); + fieldReadInCalleeWithAngelicObjFieldMayCauseFalsePositiveOk(); + accessPathInCalleeMayCauseFalsePositiveOk(); + int i = 1 / 0; + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/ArrayOutOfBounds.java b/infer/tests/codetoanalyze/java/pulse/ArrayOutOfBounds.java new file mode 100644 index 000000000..88dd3e68b --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/ArrayOutOfBounds.java @@ -0,0 +1,92 @@ +/* + * 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; + +public class ArrayOutOfBounds { + + public int arrayOutOfBoundsBad() { + int[] arr = new int[1]; + return arr[3]; + } + + public int arrayInBoundsOk() { + int[] arr = new int[2]; + return arr[1]; + } + + // tests below this line are turned off until array functionality improves + public void arrayLoopOutOfBoundsOk(int[] arr) { + for (int i = 0; i <= arr.length; i++) { + int j = arr[i]; + } + } + + public void arrayLoopInBoundsOk(int[] arr) { + for (int i = 0; i < arr.length; i++) { + int j = arr[i]; + } + } + + public void buggyIterOk(int[] arr1, int[] arr2) { + for (int i = 0; i < arr1.length; i++) { + arr2[i] = 7; + } + } + + public void switchedArrsOutOfBoundsOk() { + buggyIterOk(new int[11], new int[10]); + } + + public void buggyNestedLoop1Ok(int[] arr1, int[] arr2) { + for (int i = 0; i < arr1.length; i++) { + for (int j = 0; i < arr2.length; j++) { + arr1[i] = arr1[i] + arr2[j]; + } + } + } + + public void nestedOutOfBounds1Ok() { + buggyNestedLoop1Ok(new int[11], new int[10]); + } + + public void buggyNestedLoop2Ok(int[] arr1, int[] arr2) { + for (int i = 0; i < arr1.length; i++) { + for (int j = 0; j < arr2.length; i++) { + arr1[i] = arr1[i] + arr2[j]; + } + } + } + + public void nestedOutOfBounds2Ok() { + buggyNestedLoop2Ok(new int[11], new int[10]); + } + + public void buggyNestedLoop3Ok(int[] arr1, int[] arr2) { + for (int i = 0; i < arr1.length; i++) { + for (int j = 0; j < arr2.length; j++) { + arr1[i] = 2 * arr2[i]; + } + } + } + + public void nestedOutOfBounds3Ok() { + buggyNestedLoop3Ok(new int[11], new int[10]); + } + + public void safeNestedLoopOk(int[] arr1, int[] arr2) { + for (int i = 0; i < arr1.length; i++) { + for (int j = 0; j < arr2.length; j++) { + arr1[i] = arr1[i] + arr2[j]; + } + } + } + + public void nestedInBoundsOk() { + safeNestedLoopOk(new int[11], new int[10]); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/AutoGenerated.java b/infer/tests/codetoanalyze/java/pulse/AutoGenerated.java new file mode 100644 index 000000000..507407682 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/AutoGenerated.java @@ -0,0 +1,18 @@ +/* + * 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; + +/* @generated */ + +public class AutoGenerated { + + void npeBad() { + String s = null; + int n = s.length(); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/Builtins.java b/infer/tests/codetoanalyze/java/pulse/Builtins.java new file mode 100644 index 000000000..f9e8b072c --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/Builtins.java @@ -0,0 +1,37 @@ +/* + * 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 com.facebook.infer.builtins.InferBuiltins; + +public class Builtins { + + void blockErrorOk() { + Object x = null; + InferBuiltins.assume(x != null); + x.toString(); + } + + void doNotBlockErrorBad(Object x) { + Object y = null; + InferBuiltins.assume(x != null); + y.toString(); + } + + void blockErrorIntAssumeOk(Object x) { + Object y = null; + int i = 0; + InferBuiltins.assume(i != 0); + y.toString(); + } + + void causeErrorBad(Object x) { + InferBuiltins.assume(x == null); + x.toString(); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/ClassCastExceptions.java b/infer/tests/codetoanalyze/java/pulse/ClassCastExceptions.java new file mode 100644 index 000000000..88108295e --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/ClassCastExceptions.java @@ -0,0 +1,66 @@ +/* + * 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 java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +class SuperClass {} + +class SubClassA extends SuperClass {} + +class SubClassB extends SuperClass {} + +interface MyInterface { + public int getInt(); +} + +class ImplementationOfInterface implements MyInterface { + + public int getInt() { + return 0; + } +} + +class AnotherImplementationOfInterface implements MyInterface { + public int getInt() { + return 1; + } +} + +public class ClassCastExceptions { + + public void classCastExceptionBad() { + SuperClass a = new SubClassA(); + SubClassB b = (SubClassB) a; + } + + public int classCastExceptionImplementsInterfaceCalleeOk(MyInterface i) { + ImplementationOfInterface impl = (ImplementationOfInterface) i; + return impl.getInt(); + } + + public int classCastExceptionImplementsInterfaceBad() { + return classCastExceptionImplementsInterfaceCalleeOk(new AnotherImplementationOfInterface()); + } + + public String getURL() { + return "http://bla.com"; + } + + public void openHttpURLConnectionOk() throws IOException { + URL url = new URL(getURL()); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.disconnect(); + } + + public void castingArrayOfPrimitiveTypeOk(int[] a) { + int[] b = (int[]) a; + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/CloseableAsResourceExample.java b/infer/tests/codetoanalyze/java/pulse/CloseableAsResourceExample.java new file mode 100644 index 000000000..d1f8db00b --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/CloseableAsResourceExample.java @@ -0,0 +1,248 @@ +/* + * 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 java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; + +class LocalException extends IOException {} + +class SomeResource implements Closeable { + + void doSomething() throws LocalException { + if (!CloseableAsResourceExample.star()) { + throw new LocalException(); + } + } + + public void close() {} + + native void foo(int i); + + static native void bar(SomeResource r); +} + +class Resource implements Closeable { + public Resource() {} + + public void close() {} +} + +class Wrapper implements Closeable { + Resource mR; + + public Wrapper(Resource r) { + mR = r; + } + + public void close() { + mR.close(); + } +} + +class Sub extends Wrapper { + public Sub(Resource r) { + super(r); + } +} + +class ResourceWithException implements Closeable { + + public void close() throws IOException { + if (CloseableAsResourceExample.star()) { + throw new IOException(); + } + } +} + +class ByteArrayOutputStreamWrapper extends ByteArrayOutputStream {} + +class ByteArrayInputStreamWrapper extends ByteArrayInputStream { + + public ByteArrayInputStreamWrapper(byte[] arr) { + super(arr); + } +} + +public class CloseableAsResourceExample { + + static native boolean star(); + + void closingCloseable() { + SomeResource res = new SomeResource(); + res.close(); + } + + void notClosingCloseable() { + SomeResource res = new SomeResource(); + } // should report a resource leak + + void tryWithResource() { + try (SomeResource res = new SomeResource()) { + try { + res.doSomething(); + } catch (LocalException e) { + // do nothing + } + } + } + + void withExceptionBad() throws LocalException { + SomeResource res = new SomeResource(); + res.doSomething(); + res.close(); + } // should report a resource leak + + void closingWrapper() { + Resource r = new Resource(); + Sub s = new Sub(r); + s.close(); + } + + void notClosingWrapper() { + Sub s = new Sub(new Resource()); + s.mR.close(); + } // should report a resource leak + + void noNeedToCloseStringReader() { + StringReader stringReader = new StringReader("paf!"); + } + + void noNeedToCloseByteArrayOutputStream() { + ByteArrayOutputStream stream = new ByteArrayOutputStream(42); + } + + void noCloseByteArrayWrappersOk(byte[] array) { + ByteArrayOutputStreamWrapper stream1 = new ByteArrayOutputStreamWrapper(); + ByteArrayInputStreamWrapper stream2 = new ByteArrayInputStreamWrapper(array); + } + + void noNeedToCloseByteArrayInputStream(byte[] array) { + ByteArrayInputStream stream = new ByteArrayInputStream(array); + } + + void closingWithCloseQuietly() { + SomeResource r = null; + try { + r = new SomeResource(); + r.doSomething(); + } catch (IOException e) { + } finally { + Utils.closeQuietly(r); + } + } + + void failToCloseWithCloseQuietly() { + try { + SomeResource r = new SomeResource(); + r.doSomething(); + Utils.closeQuietly(r); + } catch (IOException e) { + } + } + + void noLeakwithExceptionOnClose() throws IOException { + ResourceWithException res = new ResourceWithException(); + res.close(); + } + + void noLeakWithCloseQuietlyAndExceptionOnClose() { + ResourceWithException res = new ResourceWithException(); + Utils.closeQuietly(res); + } + + static T sourceOfNullWithResourceLeak() { + SomeResource r = new SomeResource(); + return null; + } + + interface MyCloseable extends Closeable {} + + class MyResource implements MyCloseable { + public void close() {} + } + + void leakFoundWhenIndirectlyImplementingCloseable() { + MyResource res = new MyResource(); + } + + void skippedCallClosesResourceOnArgs() { + SomeResource res = new SomeResource(); + SomeResource.bar(res); + } + + void skippedVritualCallDoesNotCloseResourceOnReceiver() { + SomeResource res = new SomeResource(); + res.foo(42); + } + + Map returnsLocalMapContainingResourcesOk() { + HashMap map = new HashMap<>(); + SomeResource res = new SomeResource(); + Integer key = 42; + map.put(key, res); + return map; + } + + void createsLocalMapContainingResourcesOk() { + HashMap map = new HashMap<>(); + SomeResource res = new SomeResource(); + Integer key = 42; + map.put(key, res); + map.clear(); + } + + HashMap resourceMap = new HashMap<>(); + + void fieldMapContainingResourcesOk() { + Integer key = 42; + SomeResource res = new SomeResource(); + resourceMap.put(key, res); + } + + // this case is not supported + void FN_notClearinglocalMapContainingResourcesBad() { + HashMap map = new HashMap<>(); + SomeResource res = new SomeResource(); + Integer key = 42; + map.put(key, res); + } + + public static void closeCloseable(Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } catch (Exception ex) { + } + } + + public void finallyCloseOk(File file, String fileContent) { + if (!file.exists()) { + FileWriter writer = null; + try { + writer = new FileWriter(file); + writer.write(fileContent); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + closeCloseable(writer); + } + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/CursorLeaks.java b/infer/tests/codetoanalyze/java/pulse/CursorLeaks.java new file mode 100644 index 000000000..551e529df --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/CursorLeaks.java @@ -0,0 +1,244 @@ +/* + * 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.app.DownloadManager; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.database.CursorWrapper; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.os.RemoteException; +import android.provider.MediaStore; + +public class CursorLeaks { + + public int cursorClosed(SQLiteDatabase sqLiteDatabase) { + Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null); + try { + return cursor.getCount(); + } finally { + cursor.close(); + } + } + + public Object cursorClosedCheckNull(SQLiteDatabase sqLiteDatabase) { + Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null); + Object value = null; + + try { + if (cursor == null) { + return null; + } + + value = cursor.getString(0); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return value; + } + + public Object cursorClosedCheckNullCheckClosed_FP(SQLiteDatabase sqLiteDatabase) { + Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null); + Object value = null; + + try { + if (cursor == null) { + return null; + } + + value = cursor.getString(0); + } finally { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + } + return value; + } + + public int cursorNotClosed(SQLiteDatabase sqLiteDatabase) { + Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null); + return cursor.getCount(); + } + + Context mContext; + ContentResolver mContentResolver; + + public int getImageCountHelperNotClosed(String customClause) { + String[] projection = {"COUNT(*)"}; + + String selectionClause = selectionClause = customClause; + + Cursor cursor = + mContext.getContentResolver().query(null, projection, selectionClause, null, null); + + if (cursor != null) { + int count = cursor.getInt(0); + // cursor.close(); + return count; + } else { + return 0; + } + } + + public int getImageCountHelperClosed(String customClause) { + String[] projection = {"COUNT(*)"}; + + String selectionClause = selectionClause = customClause; + + Cursor cursor = + mContext.getContentResolver().query(null, projection, selectionClause, null, null); + + if (cursor != null) { + int count = cursor.getInt(0); + cursor.close(); + return count; + } else { + return 0; + } + } + + public int getBucketCountNotClosed() { + Cursor cursor = MediaStore.Images.Media.query(mContentResolver, null, null, null, null, null); + if (cursor == null) { + return 0; + } else { + int count = 0; + while (cursor.moveToNext()) { + count++; + } + return count; + } + } + + public int getBucketCountClosed() { + Cursor cursor = MediaStore.Images.Media.query(mContentResolver, null, null, null, null, null); + if (cursor == null) { + return 0; + } else { + try { + int count = 0; + while (cursor.moveToNext()) { + count++; + } + return count; + } finally { + cursor.close(); + } + } + } + + private void queryUVMLegacyDbNotClosed() { + SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); + builder.setTables(""); + Cursor cursor = builder.query(null, null, "", null, null, null, null); + if (cursor != null) cursor.moveToFirst(); + } + + private void queryUVMLegacyDbClosed() { + SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); + builder.setTables(""); + Cursor cursor = builder.query(null, null, "", null, null, null, null); + if (cursor != null) cursor.close(); + } + + public int completeDownloadClosed(DownloadManager downloadManager) { + DownloadManager.Query query = new DownloadManager.Query(); + Cursor cursor = (Cursor) null; + try { + cursor = downloadManager.query(query); + if (cursor == null) { + return 0; + } else { + return cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); + } + } finally { + if (cursor != null) cursor.close(); + } + } + + public int completeDownloadNotClosed(DownloadManager downloadManager) { + DownloadManager.Query query = new DownloadManager.Query(); + Cursor cursor = null; + try { + cursor = downloadManager.query(query); + if (cursor == null) { + return 0; + } else { + return cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); + } + } finally { + // cursor.close(); + } + } + + private void loadPrefsFromContentProviderClosed() { + ContentProviderClient contentProviderClient = mContentResolver.acquireContentProviderClient(""); + if (contentProviderClient != null) { + Cursor cursor = null; + try { + try { + cursor = contentProviderClient.query(null, null, null, null, null); + } catch (RemoteException ex) { + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + } + + private void loadPrefsFromContentProviderNotClosed() { + ContentProviderClient contentProviderClient = mContentResolver.acquireContentProviderClient(""); + if (contentProviderClient == null) return; + Cursor cursor = null; + try { + try { + cursor = contentProviderClient.query(null, null, null, null, null); + } catch (RemoteException ex) { + } + } finally { + if (cursor != null) { + // cursor.close(); + } + } + } + + class NamedCursor extends CursorWrapper { + private String mName; + + NamedCursor(Cursor cursor, String name) { + super(cursor); + mName = name; + } + } + + public Cursor cursorWrapperReturned(SQLiteDatabase sqLiteDatabase) { + Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null); + return new NamedCursor(cursor, "abc"); + } + + // TODO (#7474990): investigate why is Infer reporting a resource leak here + // public void cursorWrapperClosed(SQLiteDatabase sqLiteDatabase) { + // Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null); + // Cursor c = new NamedCursor(cursor, "abc"); + // c.close(); + // } + + native NamedCursor createWrapper(Cursor cursor); + + public NamedCursor cursorAttachedTheWrapper(SQLiteDatabase sqLiteDatabase) { + Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null); + return createWrapper(cursor); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/CursorNPEs.java b/infer/tests/codetoanalyze/java/pulse/CursorNPEs.java new file mode 100644 index 000000000..46506e806 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/CursorNPEs.java @@ -0,0 +1,85 @@ +/* + * 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.app.DownloadManager; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.os.RemoteException; +import android.provider.MediaStore; + +public class CursorNPEs { + + public int cursorNPEfromQuery(SQLiteDatabase sqLiteDatabase) { + Cursor cursor = sqLiteDatabase.query("events", null, null, null, null, null, null); + try { + return cursor.getCount(); + } finally { + cursor.close(); + } + } + + 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 void cursorFromMediaNPE() { + Cursor cursor = MediaStore.Images.Media.query(mContentResolver, null, null, null, null, null); + cursor.close(); + } + + private void cursorFromSQLiteQueryBuilderNPE() { + SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); + builder.setTables(""); + Cursor cursor = builder.query(null, null, "", null, null, null, null); + cursor.close(); + } + + public int cursorFromDownloadManagerNPE(DownloadManager downloadManager) { + DownloadManager.Query query = new DownloadManager.Query(); + Cursor cursor = null; + try { + cursor = downloadManager.query(query); + return cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); + } finally { + if (cursor != null) cursor.close(); + } + } + + private void cursorFromContentProviderClient() { + ContentProviderClient contentProviderClient = mContentResolver.acquireContentProviderClient(""); + if (contentProviderClient != null) { + Cursor cursor = null; + try { + try { + cursor = contentProviderClient.query(null, null, null, null, null); + cursor.moveToFirst(); + } catch (RemoteException ex) { + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/DivideByZero.java b/infer/tests/codetoanalyze/java/pulse/DivideByZero.java new file mode 100644 index 000000000..46eaa1b36 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/DivideByZero.java @@ -0,0 +1,39 @@ +/* + * 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; + +public class DivideByZero { + + public int divByZeroLocal(String s) { + int denominator = 0; + int nominator = 10; + int result = nominator / denominator; + return result; + } + + public int divideByZeroInterProc(int denominator) { + return 10 / denominator; + } + + // DO NOT MOVE, test relies on line number + public int callDivideByZeroInterProc() { + return divideByZeroInterProc(0); + } + + // divide by zero with static fields + private static int x; + + public void setXToZero() { + x = 0; + } + + public int divideByZeroWithStaticField() { + setXToZero(); + return divideByZeroInterProc(x); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/DoubleExample.java b/infer/tests/codetoanalyze/java/pulse/DoubleExample.java new file mode 100644 index 000000000..e69bd1f19 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/DoubleExample.java @@ -0,0 +1,24 @@ +/* + * 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 javax.annotation.Nullable; + +public class DoubleExample { + + @Nullable Double x; + + private Double testAssignNonNullOk() { + x = 1.0; + return x + 1.0; + } + + private Double testdReadNullableBad() { + return x + 1.0; + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/FilterInputStreamLeaks.java b/infer/tests/codetoanalyze/java/pulse/FilterInputStreamLeaks.java new file mode 100644 index 000000000..da4e8d0d6 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/FilterInputStreamLeaks.java @@ -0,0 +1,261 @@ +/* + * 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 java.io.*; +import java.security.DigestInputStream; +import java.util.zip.CheckedInputStream; +import java.util.zip.DeflaterInputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; +import javax.crypto.CipherInputStream; + +public class FilterInputStreamLeaks { + + // BufferedInputStream tests + + public void bufferedInputStreamNotClosedAfterRead() { + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + BufferedInputStream bis = new BufferedInputStream(fis); + bis.read(); + bis.close(); + } catch (IOException e) { + } + } + + public void bufferedInputStreamClosedAfterReset() throws IOException { + FileInputStream fis; + BufferedInputStream bis = null; + try { + fis = new FileInputStream("file.txt"); + bis = new BufferedInputStream(fis); + bis.reset(); + } catch (IOException e) { + } finally { + if (bis != null) bis.close(); + } + } + + // CheckedInputStream tests + + public void checkedInputStreamNotClosedAfterRead() { + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + CheckedInputStream chis = new CheckedInputStream(fis, null); + chis.read(); + chis.close(); + } catch (IOException e) { + } + } + + public void checkedInputStreamClosedAfterSkip() throws IOException { + FileInputStream fis; + CheckedInputStream chis = null; + try { + fis = new FileInputStream("file.txt"); + chis = new CheckedInputStream(fis, null); + chis.skip(5); + } catch (IOException e) { + } finally { + if (chis != null) chis.close(); + } + } + + // CipherInputStream tests + + public void cipherInputStreamNotClosedAfterSkip() { + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + CipherInputStream cis = new CipherInputStream(fis, null); + cis.skip(8); + cis.close(); + } catch (IOException e) { + } + } + + public void cipherInputStreamClosedAfterRead() throws IOException { + FileInputStream fis; + CipherInputStream cis = null; + try { + fis = new FileInputStream("file.txt"); + cis = new CipherInputStream(fis, null); + cis.read(); + } catch (IOException e) { + } finally { + if (cis != null) cis.close(); + } + } + + // DataInputStream tests + + public void dataInputStreamNotClosedAfterRead() { + byte[] arr = new byte[10]; + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + DataInputStream dis = new DataInputStream(fis); + dis.read(arr); + dis.close(); + } catch (IOException e) { + } + } + + public void dataInputStreamClosedAfterReadBoolean() throws IOException { + FileInputStream fis; + DataInputStream dis = null; + try { + fis = new FileInputStream("file.txt"); + dis = new DataInputStream(fis); + dis.readBoolean(); + } catch (IOException e) { + } finally { + if (dis != null) dis.close(); + } + } + + // DeflaterInputStream tests + + public void deflaterInputStreamNotClosedAfterRead() { + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + DeflaterInputStream dis = new DeflaterInputStream(fis, null); + dis.read(); + dis.close(); + } catch (IOException e) { + } + } + + public void deflaterInputStreamClosedAfterReset() throws IOException { + FileInputStream fis; + DeflaterInputStream dis = null; + try { + fis = new FileInputStream("file.txt"); + dis = new DeflaterInputStream(fis, null); + dis.reset(); + } catch (IOException e) { + } finally { + if (dis != null) dis.close(); + } + } + + // GZipInputStream tests + + public void gzipInputStreamNotClosedAfterRead() { + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + GZIPInputStream gzipInputStream = new GZIPInputStream(fis); + gzipInputStream.read(); + gzipInputStream.close(); + } catch (IOException e) { + } + } + + public void gzipInputStreamClosedAfterRead() throws IOException { + FileInputStream fis = null; + GZIPInputStream gzipInputStream = null; + try { + fis = new FileInputStream("file.txt"); + gzipInputStream = new GZIPInputStream(fis); + gzipInputStream.read(); + } catch (IOException e) { + } finally { + if (gzipInputStream != null) gzipInputStream.close(); + else if (fis != null) fis.close(); + } + } + + // DigestInputStream tests + + public void digestInputStreamNotClosedAfterRead() { + byte[] arr = new byte[10]; + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + DigestInputStream dis = new DigestInputStream(fis, null); + dis.read(arr); + dis.close(); + } catch (IOException e) { + } + } + + public void digestInputStreamClosedAfterRead() throws IOException { + FileInputStream fis; + DigestInputStream dis = null; + try { + fis = new FileInputStream("file.txt"); + dis = new DigestInputStream(fis, null); + dis.read(); + } catch (IOException e) { + } finally { + if (dis != null) dis.close(); + } + } + + // InflaterInputStream tests + + public void inflaterInputStreamNotClosedAfterRead() { + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + InflaterInputStream iis = new InflaterInputStream(fis, null); + iis.read(); + iis.close(); + } catch (IOException e) { + } + } + + public void inflaterInputStreamClosedAfterAvailable() throws IOException { + FileInputStream fis; + InflaterInputStream iis = null; + try { + fis = new FileInputStream("file.txt"); + iis = new InflaterInputStream(fis, null); + iis.available(); + } catch (IOException e) { + } finally { + if (iis != null) iis.close(); + } + } + + // PushbackInputStream tests + + public void pushbackInputStreamNotClosedAfterRead() { + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + PushbackInputStream pms = new PushbackInputStream(fis); + pms.read(); + pms.close(); + } catch (IOException e) { + } + } + + public void pushbackInputStreamClosedAfterReset() throws IOException { + FileInputStream fis; + PushbackInputStream pms = null; + try { + fis = new FileInputStream("file.txt"); + pms = new PushbackInputStream(fis); + pms.reset(); + } catch (IOException e) { + } finally { + if (pms != null) pms.close(); + } + } + + public void twoLevelWrapperNoLeak(File file) throws IOException { + DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); + in.close(); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/FilterOutputStreamLeaks.java b/infer/tests/codetoanalyze/java/pulse/FilterOutputStreamLeaks.java new file mode 100644 index 000000000..6334fbc75 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/FilterOutputStreamLeaks.java @@ -0,0 +1,302 @@ +/* + * 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 java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.security.DigestOutputStream; +import java.util.zip.CheckedOutputStream; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.InflaterOutputStream; +import javax.crypto.CipherOutputStream; + +public class FilterOutputStreamLeaks { + + // FilterOutputStream tests + + public void filterOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + try { + fis = new FileOutputStream("file.txt"); + FilterOutputStream fos = new FilterOutputStream(fis); + fos.write(arr); + fos.close(); + } catch (IOException e) { + } + } + + public void filterOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + FilterOutputStream fos = null; + try { + fis = new FileOutputStream("file.txt"); + fos = new FilterOutputStream(fis); + fos.write(arr); + } catch (IOException e) { + } finally { + if (fos != null) fos.close(); + } + } + + // DataOutputStream tests + + public void dataOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + try { + fis = new FileOutputStream("file.txt"); + DataOutputStream dos = new DataOutputStream(fis); + dos.write(arr); + dos.close(); + } catch (IOException e) { + } + } + + public void dataOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + DataOutputStream dos = null; + try { + fis = new FileOutputStream("file.txt"); + dos = new DataOutputStream(fis); + dos.write(arr); + } catch (IOException e) { + } finally { + if (dos != null) dos.close(); + } + } + + // BufferedOutputStream tests + + public void bufferedOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis = null; + try { + fis = new FileOutputStream("file.txt"); + BufferedOutputStream bos = new BufferedOutputStream(fis); + bos.write(arr); + bos.close(); + } catch (IOException e) { + } + } + + public void bufferedOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + BufferedOutputStream bos = null; + try { + fis = new FileOutputStream("file.txt"); + bos = new BufferedOutputStream(fis); + bos.write(arr); + } catch (IOException e) { + } finally { + if (bos != null) bos.close(); + } + } + + // CheckedOutputStream tests + + public void checkedOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + try { + fis = new FileOutputStream("file.txt"); + CheckedOutputStream chos = new CheckedOutputStream(fis, null); + chos.write(arr); + chos.close(); + } catch (IOException e) { + } + } + + public void checkedOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + CheckedOutputStream chos = null; + try { + fis = new FileOutputStream("file.txt"); + chos = new CheckedOutputStream(fis, null); + chos.write(arr); + } catch (IOException e) { + } finally { + if (chos != null) chos.close(); + } + } + + // CipherOutputStream tests + + public void cipherOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + try { + fis = new FileOutputStream("file.txt"); + CipherOutputStream cos = new CipherOutputStream(fis, null); + cos.write(arr); + cos.close(); + } catch (IOException e) { + } + } + + public void cipherOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + CipherOutputStream cos = null; + try { + fis = new FileOutputStream("file.txt"); + cos = new CipherOutputStream(fis, null); + cos.write(arr); + } catch (IOException e) { + } finally { + if (cos != null) cos.close(); + } + } + + // DeflaterOutputStream tests + + public void deflaterOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + try { + fis = new FileOutputStream("file.txt"); + DeflaterOutputStream dos = new DeflaterOutputStream(fis, null); + dos.write(arr); + dos.close(); + } catch (IOException e) { + } + } + + public void deflaterOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + DeflaterOutputStream dos = null; + try { + fis = new FileOutputStream("file.txt"); + dos = new DeflaterOutputStream(fis, null); + dos.write(arr); + } catch (IOException e) { + } finally { + if (dos != null) dos.close(); + } + } + + // DigestOutputStream tests + + public void digestOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + try { + fis = new FileOutputStream("file.txt"); + DigestOutputStream dos = new DigestOutputStream(fis, null); + dos.write(arr); + dos.close(); + } catch (IOException e) { + } + } + + public void digestOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + DigestOutputStream dos = null; + try { + fis = new FileOutputStream("file.txt"); + dos = new DigestOutputStream(fis, null); + dos.write(arr); + } catch (IOException e) { + } finally { + if (dos != null) dos.close(); + } + } + + // InflaterOutputStream tests + + public void inflaterOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + try { + fis = new FileOutputStream("file.txt"); + InflaterOutputStream ios = new InflaterOutputStream(fis, null); + ios.write(arr); + ios.close(); + } catch (IOException e) { + } + } + + public void inflaterOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + InflaterOutputStream ios = null; + try { + fis = new FileOutputStream("file.txt"); + ios = new InflaterOutputStream(fis, null); + ios.write(arr); + } catch (IOException e) { + } finally { + if (ios != null) ios.close(); + } + } + + // GZipOutputStream tests + + public void gzipOutputStreamNotClosedAfterFlush() { + FileOutputStream fos; + try { + fos = new FileOutputStream("file.txt"); + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(fos); + gzipOutputStream.flush(); + gzipOutputStream.close(); + } catch (IOException e) { + } + } + + public void gzipOutputStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fos = null; + GZIPOutputStream gzipOutputStream = null; + try { + fos = new FileOutputStream("file.txt"); + gzipOutputStream = new GZIPOutputStream(fos); + gzipOutputStream.write(arr); + } catch (IOException e) { + } finally { + if (gzipOutputStream != null) gzipOutputStream.close(); + else if (fos != null) fos.close(); + } + } + + // PrintStream tests + + public void printStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + try { + fis = new FileOutputStream("file.txt"); + InflaterOutputStream printer = new InflaterOutputStream(fis, null); + printer.write(arr); + } catch (IOException e) { + } + } + + public void printStreamClosedAfterWrite() throws IOException { + byte[] arr = {1, 2, 3}; + FileOutputStream fis; + InflaterOutputStream printer = null; + try { + fis = new FileOutputStream("file.txt"); + printer = new InflaterOutputStream(fis, null); + printer.write(arr); + } catch (IOException e) { + } finally { + if (printer != null) printer.close(); + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/GuardedByExample.java b/infer/tests/codetoanalyze/java/pulse/GuardedByExample.java new file mode 100644 index 000000000..694b0358d --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/GuardedByExample.java @@ -0,0 +1,576 @@ +/* + * 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 com.google.common.annotations.VisibleForTesting; +import java.io.Closeable; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.annotation.concurrent.GuardedBy; + +public class GuardedByExample { + + static class AutoCloseableReadWriteUpdateLock implements Closeable { + @Override + public void close() {} + } + + private Object mLock = new Object(); + + private Object mOtherLock = new Object(); + + private AutoCloseableReadWriteUpdateLock mReadWriteLock = new AutoCloseableReadWriteUpdateLock(); + + @GuardedBy("mLock") + private Object f = new Object(); + + @GuardedBy("this") + Object g = new Object(); + + Object mCopyOfG; + + @GuardedBy("SomeLockThatDoesntExist") + Object h = new Object(); + + @GuardedBy("mReadWriteLock") + Object i = new Object(); + + private static Object sLock = new Object(); + + @GuardedBy("sLock") + static Object sFld; + + @GuardedBy("GuardedByExample.class") + static Object sGuardedByClass; + + static { + // don't warn on class initializer + sFld = new Object(); + } + + public GuardedByExample() { + // don't warn on reads or writes of Guarded fields in constructor + f.toString(); + g = new Object(); + } + + void readFBad() { + this.f.toString(); + } + + @SuppressLint("InvalidAccessToGuardedField") + void readFBadButSuppressed() { + this.f.toString(); + } + + @SuppressLint("SomeOtherWarning") + void readFBadButSuppressedOther() { + this.f.toString(); + } + + void writeFBad() { + this.f = new Object(); + } + + void readFBadWrongLock() { + synchronized (mOtherLock) { + this.f.toString(); // f is supposed to be protected by mLock + } + } + + void writeFBadWrongLock() { + synchronized (mOtherLock) { + this.f = new Object(); // f is supposed to be protected by mLock + } + } + + void readFAfterBlockBad() { + synchronized (mLock) { + } + this.f.toString(); + } + + void writeFAfterBlockBad() { + synchronized (mLock) { + } + this.f = new Object(); + } + + @GuardedBy("mOtherLock") + void readFBadWrongAnnotation() { + this.f.toString(); + } + + @GuardedBy("mLock") + void readFOkMethodAnnotated() { + this.f.toString(); + } + + synchronized void synchronizedMethodReadOk() { + this.g.toString(); + } + + synchronized void synchronizedMethodWriteOk() { + this.g = new Object(); + } + + void readFOkSynchronized() { + synchronized (mLock) { + this.f.toString(); + } + } + + void writeFOkSynchronized() { + synchronized (mLock) { + this.f = new Object(); + } + } + + synchronized void synchronizedMethodReadBad() { + this.f.toString(); // f is supposed to be protected by mLock, not this + } + + synchronized void synchronizedMethodWriteBad() { + this.f = new Object(); // f is supposed to be protected by mLock, not this + } + + void reassignCopyOk() { + synchronized (this) { + mCopyOfG = g; // these are ok: access of g guarded by this + } + mCopyOfG = new Object(); // ok; this doesn't change the value of g + } + + void readHBad() { + synchronized (mLock) { // h is not protected by mLock + this.h.toString(); + } + } + + synchronized void readHBadSynchronizedMethodShouldntHelp() { + this.h.toString(); // h is not protected by this + } + + private void privateUnguardedAccess() { + // not protected, but safe if all call sites guard the access to f + this.g.toString(); + } + + public void guardedCallSite1() { + synchronized (this) { + privateUnguardedAccess(); // should not warn; lock is held + } + } + + public synchronized void guardedCallSite2() { + privateUnguardedAccess(); // should not warn; lock is held + } + + private void wrapper() { + privateUnguardedAccess(); // should not warn, just propagate the proof obl + } + + public void guardedCallSite3() { + synchronized (this) { + wrapper(); // should not warn + } + } + + void readWriteLockOk() { + try (AutoCloseableReadWriteUpdateLock lock = mReadWriteLock) { + this.i.toString(); + } + } + + static synchronized void staticSynchronizedOk() { + sGuardedByClass.toString(); + } + + static void synchronizeOnClassOk1() { + synchronized (GuardedByExample.class) { + sGuardedByClass.toString(); // should not warn here + sGuardedByClass = new Object(); // or here + } + } + + void synchronizedOnThisBad() { + sGuardedByClass.toString(); + } + + Object dontReportOnCompilerGenerated() { + return new Object() { + public void accessInAnonClassOk() { + synchronized (mLock) { + f.toString(); + } + } + }; + } + + Object readFromInnerClassOkOuter() { + return new Object() { + public String readFromInnerClassOk() { + synchronized (GuardedByExample.this) { + return g.toString(); + } + } + }; + } + + Object readFromInnerClassBad1Outer() { + return new Object() { + public String readFromInnerClassBad1() { + synchronized (this) { + return g.toString(); // g is guarded by the outer class this, not this$0 + } + } + }; + } + + Object readFromInnerClassBad2Outer() { + return new Object() { + public synchronized String readFromInnerClassBad2() { + return g.toString(); // g is guarded by the outer class this, not this$0 + } + }; + } + + @VisibleForTesting + public void visibleForTestingOk1() { + f.toString(); // should push proof obl to caller + } + + @VisibleForTesting + void visibleForTestingOk2() { + f.toString(); // should push proof obl to caller + } + + synchronized Object returnPtG() { + return g; + } + + // note: this test should raise an error under "by value" GuardedBy semantics, but not under + // "by reference" GuardedBy semantics + void readGFromCopyOk() { + synchronized (this) { + mCopyOfG = g; // these are ok: access of g guarded by this + g.toString(); + } + mCopyOfG.toString(); // should be an error; unprotected access to pt(g) + } + + // another "by reference" vs "by value" test. buggy in "by value", but safe in "by reference" + void usePtG() { + Object ptG = returnPtG(); + ptG.toString(); + } + + Object byRefTrickyBad() { + Object local = null; + synchronized (this) { + local = g; // we have a local pointer... to pt(G) + } + g.toString(); // ...but unsafe access is through g! + return local; + } + + void byRefTrickyOk() { + Object local = null; + synchronized (this) { + local = g; // we have a local pointer... to pt(G) + } + local.toString(); // ...but unsafe access is through g! + } + + @GuardedBy("ui_thread") + Object uiThread1; + + @GuardedBy("ui-thread") + Object uiThread2; + + @GuardedBy("uithread") + Object uiThread3; + + @GuardedBy("something that's clearly not an expression") + Object nonExpression; + + // tests for not reporting false alarms on unrecognized GuardedBy strings + void accessUnrecognizedGuardedByFieldsOk() { + uiThread1 = new Object(); + uiThread1.toString(); + uiThread2 = new Object(); + uiThread2.toString(); + uiThread3 = new Object(); + uiThread3.toString(); + nonExpression = new Object(); + nonExpression.toString(); + } + + // outer class this tests + @GuardedBy("GuardedByExample.this") + Object guardedByOuterThis; + + synchronized void okOuterAccess() { + guardedByOuterThis = null; + } + + // inner class this tests + private class Inner { + @GuardedBy("this") + Object guardedByInnerThis1; + + @GuardedBy("Inner.this") + Object guardedByInnerThis2; + + @GuardedBy("GuardedByExample$Inner.this") + Object guardedByInnerThis3; + + @GuardedBy("Inner.class") + Object guardedByInnerClass1; + + @GuardedBy("GuardedByExample.Inner.class") + Object guardedByInnerClass2; + + @GuardedBy("GuardedByExample$Inner.class") + Object guardedByInnerClass3; + + synchronized void okAccess1() { + guardedByInnerThis1 = null; + } + + synchronized void okAccess2() { + guardedByInnerThis2 = null; + } + + synchronized void okAccess3() { + guardedByInnerThis3 = null; + } + + void okInnerClassGuard1() { + synchronized (Inner.class) { + guardedByInnerClass1 = new Object(); + guardedByInnerClass2 = new Object(); + guardedByInnerClass3 = new Object(); + } + } + + void okInnerClassGuard2() { + synchronized (GuardedByExample.Inner.class) { + guardedByInnerClass1 = new Object(); + guardedByInnerClass2 = new Object(); + guardedByInnerClass3 = new Object(); + } + } + } + + // TODO: report on these cases + /* + public void unguardedCallSiteBad1() { + privateUnguardedAccess(); // should warn; lock is not held + } + + protected void unguardedCallSiteBad2() { + privateUnguardedAccess(); // should warn; lock is not held + } + + void unguardedCallSiteBad3() { + privateUnguardedAccess(); // should warn; lock is not held + } + */ + + int n; + + public void withloop2() { + synchronized (mLock) { + for (int i = 0; i <= n; i++) { + f = 42; + } + } + } + + public void withoutloop2() { + synchronized (mLock) { + f = 42; + } + } + + @GuardedBy("self_reference") + Object self_reference; + + void guardedBySelfReferenceOK() { + synchronized (self_reference) { + this.self_reference.toString(); + } + } + + // TODO: report on this case, or at least a version which writes + /* + void guardedBySelfReferenceBad() { + this.self_reference.toString(); + } + */ + + @GuardedBy("itself") + Object itself_fld; + + void itselfOK() { + synchronized (itself_fld) { + this.itself_fld.toString(); + } + } + + // TODO: report on this case, or at least a version which writes + /* + void itselfBad() { + this.itself_fld.toString(); + } + */ + + ReadWriteLock mRWL; + + @GuardedBy("mRWL") + Integer guardedbymRWL; + + Integer someOtherInt; + + void readLockOK() { + mRWL.readLock().lock(); + someOtherInt = guardedbymRWL; + mRWL.readLock().unlock(); + } + + void writeLockOK() { + mRWL.writeLock().lock(); + guardedbymRWL = 55; + mRWL.writeLock().unlock(); + } + + ReentrantReadWriteLock mRRWL; + + @GuardedBy("mRRWL") + Integer guardedbymRRWL; + + void reentrantReadLockOK() { + mRRWL.readLock().lock(); + someOtherInt = guardedbymRRWL; + mRRWL.readLock().unlock(); + } + + void reentrantWriteLockOK() { + mRRWL.writeLock().lock(); + guardedbymRRWL = 55; + mRRWL.writeLock().unlock(); + } + + // TODO: warn on misuse of read/write locks. + + @GuardedBy("this") + Integer xForSub; + + static class Sub extends GuardedByExample { + + void goodSub1() { + synchronized (this) { + xForSub = 22; + } + } + + synchronized void goodSub2() { + xForSub = 22; + } + + void badSub() { + xForSub = 22; + } + } + + Lock normallock; + + @GuardedBy("normallock") + Integer guardedbynl; + + ReentrantLock reentrantlock; + + @GuardedBy("reentrantlock") + Integer guardedbyrel; + + void goodGuardedByNormalLock() { + normallock.lock(); + guardedbynl = 22; + normallock.unlock(); + } + + void goodTryLockGuardedByNormalLock() { + if (normallock.tryLock()) { + guardedbynl = 22; + normallock.unlock(); + } + } + + void goodTryLockGuardedByReentrantLock() { + if (reentrantlock.tryLock()) { + guardedbyrel = 44; + reentrantlock.unlock(); + } + } + + void badGuardedByNormalLock() { + guardedbynl = 22; + } + + void badGuardedByReentrantLock() { + guardedbyrel = 44; + } + + static class OtherClassWithLock { + ReentrantLock lock; + + @GuardedBy("lock") + Object guardedByLock; + + Object otherClassObject; + + void guardedInSameClassOk() { + lock.lock(); + guardedByLock = new Object(); + lock.unlock(); + } + } + + @GuardedBy("OtherClassWithLock.lock") + Object guardedByLock1; + + @GuardedBy("codetoanalyze.java.infer.GuardedByExample$OtherClassWithLock.lock") + Object guardedByLock2; + + @GuardedBy("OtherClassWithLock.otherClassObject") + Object guardedByLock3; + + OtherClassWithLock otherClass; + + void guardedByTypeSyntaxOk1() { + otherClass.lock.lock(); + guardedByLock1 = true; + guardedByLock2 = true; + otherClass.lock.unlock(); + } + + void guardedByTypeSyntaxOk2() { + synchronized (otherClass.otherClassObject) { + guardedByLock3 = true; + } + } + + void guardedByTypeSyntaxBad() { + guardedByLock1 = true; + guardedByLock2 = true; + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/HashMapExample.java b/infer/tests/codetoanalyze/java/pulse/HashMapExample.java new file mode 100644 index 000000000..27b650086 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/HashMapExample.java @@ -0,0 +1,110 @@ +/* + * 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 java.util.HashMap; + +public class HashMapExample { + + public static void putIntegerTwiceThenGetTwice(HashMap hashMap) { + Integer i32 = new Integer(32); + Integer i52 = new Integer(52); + + hashMap.put(i32, i32); + hashMap.put(i52, i52); + + Integer a = hashMap.get(i32); + Integer b = hashMap.get(i52); + + a.intValue(); + b.intValue(); + } + + public static void containsIntegerTwiceThenGetTwice(HashMap hashMap) { + Integer i32 = new Integer(32); + Integer i52 = new Integer(52); + + if (hashMap.containsKey(i32) && hashMap.containsKey(i52)) { + Integer a = hashMap.get(i32); + Integer b = hashMap.get(i52); + a.intValue(); + b.intValue(); + } + } + + public static int getOneIntegerWithoutCheck() { + HashMap hashMap = new HashMap<>(); + Integer i32 = new Integer(32); + + Integer a = hashMap.get(i32); + + return a.intValue(); + } + + public static void getTwoIntegersWithOneCheck(Integer i, Integer j) { + HashMap hashMap = new HashMap<>(); + + if (hashMap.containsKey(i) && !i.equals(j)) { + Integer a = hashMap.get(i); + Integer b = hashMap.get(j); + + a.intValue(); + b.intValue(); + } + } + + public static Integer getOrCreateInteger(final HashMap map, final int id) { + Integer x = null; + if (map.containsKey(id)) { + x = map.get(id); + } else { + x = new Integer(0); + map.put(id, x); + } + return x; + } + + public static void getOrCreateIntegerThenDeref(final HashMap map) { + Integer x = getOrCreateInteger(map, 42); + // dereference x + x.toString(); + } + + void getAfterRemovingTheKeyBad() { + HashMap map = new HashMap(); + Integer key = 42; + map.put(key, new Object()); + map.remove(key); + map.get(key).toString(); // NPE here + } + + void getAfterRemovingAnotherKeyOk() { + HashMap map = new HashMap(); + Integer key = 42; + map.put(key, new Object()); + map.remove(0); + map.get(key).toString(); + } + + void getAfterClearBad() { + HashMap map = new HashMap(); + Integer key = 42; + map.put(key, new Object()); + map.clear(); + map.get(key).toString(); // NPE here + } + + void getFromKeySetGood_FP(HashMap map) { + for (String key : map.keySet()) { + String s = map.get(key); + if (s.equals("foo")) { + System.out.println("true"); + } + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/IntegerExample.java b/infer/tests/codetoanalyze/java/pulse/IntegerExample.java new file mode 100644 index 000000000..c98aaa027 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/IntegerExample.java @@ -0,0 +1,45 @@ +/* + * 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; + +public class IntegerExample { + + private static void testIntegerEqualsGood() { + Integer a = new Integer(42); + Integer b = new Integer(42); + Integer c = null; + + if (!a.equals(b)) { + c.intValue(); + } + + if (a != 42) { + c.intValue(); + } + } + + private static void testIntegerEqualsBad() { + Integer a = new Integer(42); + Integer b = new Integer(42); + Integer c = null; + + if (a.equals(b)) { + c.intValue(); + } + } + + private static void testIntegerEqualsFN() { + Integer a = new Integer(42); + Integer b = new Integer(42); + Integer c = null; + + if (a == b) { + c.intValue(); + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/InvokeDynamic.java b/infer/tests/codetoanalyze/java/pulse/InvokeDynamic.java new file mode 100644 index 000000000..4c89e1472 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/InvokeDynamic.java @@ -0,0 +1,57 @@ +/* + * 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 java.util.Collections; +import java.util.List; +import java.util.function.Function; + +public class InvokeDynamic { + + void invokeDynamicThenNpeBad(List list) { + Object o = null; + Collections.sort( + list, + (String a, String b) -> { + return b.compareTo(a); + }); + o.toString(); + } + + void npeInLambdaBad(List list) { + Collections.sort( + list, + (String a, String b) -> { + Object o = null; + o.toString(); + return b.compareTo(a); + }); + } + + // we still don't get this one (even with Javalib lambda rewriting) + // because Collections.sort is skipped + void FN_npeViaCaptureBad(List list) { + String s = null; + Collections.sort( + list, + (String a, String b) -> { + return s.compareTo(a); + }); + } + + Integer npeViaSimpleCapture() { + String s = null; + Function f = (s1) -> s.length(); + return f.apply(null); + } + + Integer npeViaSimpleParamPassing() { + Function f = (s) -> s.length(); + return f.apply(null); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/JunitAssertion.java b/infer/tests/codetoanalyze/java/pulse/JunitAssertion.java new file mode 100644 index 000000000..13f66c68d --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/JunitAssertion.java @@ -0,0 +1,27 @@ +/* + * 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 static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +public class JunitAssertion { + class A { + public void f() {} + } + + public void consistentAssertion(A a) { + assertTrue(a != null); + a.f(); + } + + public void inconsistentAssertion(A a) { + assertFalse("Should not happen!", a != null); + a.f(); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/Lists.java b/infer/tests/codetoanalyze/java/pulse/Lists.java new file mode 100644 index 000000000..5d601ac4a --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/Lists.java @@ -0,0 +1,75 @@ +/* + * 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 java.util.List; + +class Lists { + + void emptyRemembersOk(List l) { + boolean empty = l.isEmpty(); + Object o = null; + if (empty != l.isEmpty()) { + o.toString(); + } + } + + void removeInvalidatesNonEmptinessNPE(List l, int i) { + if (!l.isEmpty()) { + l.remove(i); + Object o = null; + if (l.isEmpty()) { + o.toString(); + } + } + } + + void clearCausesEmptinessNPE(List l, int i) { + if (!l.isEmpty()) { + l.clear(); + Object o = null; + if (l.isEmpty()) { + o.toString(); + } + } + } + + // it would be too noisy to report here + void plainGetOk(List l, int i) { + l.get(i).toString(); + } + + Object getElement(List l) { + return l.isEmpty() ? null : l.get(0); + } + + void getElementOk(List l) { + if (l.isEmpty()) { + return; + } + getElement(l).toString(); + } + + void getElementNPE(List l) { + if (!l.isEmpty()) { + return; + } + getElement(l).toString(); + } + + // don't fully understand why we don't get this one; model should allow it + void FN_addInvalidatesEmptinessNPE(List l) { + if (l.isEmpty()) { + l.add(0, new Object()); + Object o = null; + if (!l.isEmpty()) { + o.toString(); + } + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/NeverNullSource.java b/infer/tests/codetoanalyze/java/pulse/NeverNullSource.java new file mode 100644 index 000000000..ee3514226 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/NeverNullSource.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +// _AUTOMATICALLY_GENERATED_ + +package codetoanalyze.java.infer; + +import javax.annotation.Nullable; + +public class NeverNullSource { + + @Nullable T t; + + T get() { + return t == null ? null : t; + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/NopFun.java b/infer/tests/codetoanalyze/java/pulse/NopFun.java new file mode 100644 index 000000000..701235da3 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/NopFun.java @@ -0,0 +1,32 @@ +/* + * 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. + */ +// Command: infer -g --biabduction-only +class T { + static int q; + + static void f() { + if (q == 0) { + q = 1; + } else if (q == 1) { + while (true) ; + } + } + + static void h() { + // Important to have 2 branches, and one of them is (q==1). + if (q == 1) { + } else if (q == 2) { + } + } + + static void go() { + q = 0; + f(); + h(); // warning disappears if the NOP function h() is called here + f(); // should warn of PRECONDITION_NOT_MET here + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/NullPointerExceptions.java b/infer/tests/codetoanalyze/java/pulse/NullPointerExceptions.java new file mode 100644 index 000000000..d7ba08656 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/NullPointerExceptions.java @@ -0,0 +1,637 @@ +/* + * 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 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 h, int position) { + return h.get(position); + } + + Integer NPEvalueOfFromHashmapGood(HashMap 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.sourceOfNullWithResourceLeak(); + 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 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 listBuilder = ImmutableList.builder(); + listBuilder.add(getObject()); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/ReaderLeaks.java b/infer/tests/codetoanalyze/java/pulse/ReaderLeaks.java new file mode 100644 index 000000000..1dc75731a --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/ReaderLeaks.java @@ -0,0 +1,206 @@ +/* + * 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 java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PipedReader; +import java.io.PipedWriter; +import java.io.PushbackReader; +import java.io.Reader; + +public class ReaderLeaks { + + private void ignore(Object o) {} + + // Reader tests + + public void readerNotClosedAfterRead() { + Reader r; + try { + r = new FileReader("testing.txt"); + r.read(); + r.close(); + } catch (IOException e) { + } + } + /* This test seems to be flaky in the CI at the moment. + We guess it's because of timeouts in the analysis. + public void readerClosedOk() throws IOException { + Reader r = null; + try { + r = new FileReader("testing.txt"); + boolean ready = r.ready(); + r.close(); + } catch (IOException e) { + } finally { + if (r != null) r.close(); + } + } + */ + // BufferedReader tests + + public void bufferedReaderNotClosedAfterRead() { + BufferedReader reader; + try { + reader = new BufferedReader(new FileReader("testing.txt")); + ignore(reader.read()); + reader.close(); + } catch (IOException e) { + } + } + + public void bufferedReaderClosed() throws IOException { + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader("testing.txt")); + ignore(reader.read()); + } catch (IOException e) { + } finally { + if (reader != null) reader.close(); + } + } + + public void noNeedToCloseBufferReaderWrapperOk(File file) throws IOException { + try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file))) { + BufferedReader reader = new BufferedReader(inputStreamReader); + ignore(reader.readLine()); + } + } + + // InputStreamReader tests + + public void inputStreamReaderNotClosedAfterRead() { + InputStreamReader reader; + try { + reader = new InputStreamReader(new FileInputStream("testing.txt")); + reader.read(); + reader.close(); + } catch (IOException e) { + } + } + + public void inputStreamReaderClosed() throws IOException { + InputStreamReader reader = null; + try { + reader = new InputStreamReader(new FileInputStream("testing.txt")); + ignore(reader.read()); + } catch (IOException e) { + } finally { + if (reader != null) reader.close(); + } + } + + // FileReader tests + + public void fileReaderNotClosedAfterRead() { + FileReader reader; + try { + reader = new FileReader("testing.txt"); + reader.read(); + reader.close(); + } catch (IOException e) { + } + } + + public void fileReaderClosed() throws IOException { + FileReader reader = null; + try { + reader = new FileReader("testing.txt"); + reader.read(); + } catch (IOException e) { + } finally { + if (reader != null) reader.close(); + } + } + + // PushbackReader tests + + public void pushbackReaderNotClosedAfterRead() { + PushbackReader reader; + try { + reader = new PushbackReader(new InputStreamReader(new FileInputStream("testing.txt"))); + reader.read(); + reader.close(); + } catch (IOException e) { + } + } + + public void pushbackReaderClosed() throws IOException { + PushbackReader reader = null; + try { + reader = new PushbackReader(new InputStreamReader(new FileInputStream("testing.txt"))); + reader.read(); + } catch (IOException e) { + } finally { + if (reader != null) reader.close(); + } + } + + // PipedReader tests + + public void pipedReaderNotClosedAfterConstructedWithWriter() { + PipedReader reader; + PipedWriter writer; + try { + writer = new PipedWriter(); + reader = new PipedReader(writer); + reader.read(); + reader.close(); + } catch (IOException e) { + } + } + + public void pipedReaderNotClosedAfterConnect(PipedWriter writer) { + PipedReader reader; + try { + reader = new PipedReader(); + reader.connect(writer); + reader.read(); + reader.close(); + } catch (IOException e) { + } + } + + public void pipedReaderNotConnected() { + PipedReader reader; + try { + reader = new PipedReader(); + reader.close(); + } catch (IOException e) { + } + } + + public void pipedReaderClosed(PipedWriter writer) throws IOException { + PipedReader reader = null; + try { + reader = new PipedReader(); + reader.connect(writer); + reader.read(); + } catch (IOException e) { + } finally { + if (reader != null) reader.close(); + } + } + + public void pipedReaderFalsePositive() throws IOException { + PipedReader reader; + PipedWriter writer = null; + try { + reader = new PipedReader(writer); + reader.read(); + } catch (IOException e) { + } finally { + if (writer != null) writer.close(); + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/ResourceLeaks.java b/infer/tests/codetoanalyze/java/pulse/ResourceLeaks.java new file mode 100644 index 000000000..d378d9cee --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/ResourceLeaks.java @@ -0,0 +1,950 @@ +/* + * 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.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.json.UTF8StreamJsonParser; +import com.google.common.base.Preconditions; +import com.google.common.io.Closeables; +import java.io.BufferedInputStream; +import java.io.Closeable; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintWriter; +import java.io.RandomAccessFile; +import java.net.HttpURLConnection; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.FileChannel; +import java.util.Scanner; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.zip.Deflater; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.Inflater; +import java.util.zip.ZipFile; +import javax.net.ssl.HttpsURLConnection; + +public class ResourceLeaks { + + // FileOutputStream tests + + public void fileOutputStreamNotClosed() throws IOException { + FileOutputStream fis = new FileOutputStream("file.txt"); + } + + public void fileOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3}; + FileOutputStream fis = null; + try { + fis = new FileOutputStream("file.txt"); + fis.write(arr); + fis.close(); + } catch (IOException e) { + } + } + + public void fileOutputStreamClosed() throws IOException { + FileOutputStream fis = new FileOutputStream("file.txt"); + fis.close(); + } + + public void fileOutputStreamOneLeak() throws IOException { + FileOutputStream fis = new FileOutputStream("file.txt"); + if (fis != null) { + } else { + } + } + + public int fileOutputStreamTwoLeaks1(boolean ok) throws IOException { + FileOutputStream fis = new FileOutputStream("file.txt"); + if (ok) { + fis.write(1); + return 1; + } else { + fis.write(2); + return 2; + } + } + + public void fileOutputStreamTwoLeaks2() throws IOException { + FileOutputStream fis = new FileOutputStream("file.txt"); + if (fis != null) { + } else { + } + fis = new FileOutputStream("x"); + } + + // TwoResources tests + + public static void twoResources() throws IOException { + FileInputStream fis = null; + FileOutputStream fos = null; + try { + fis = new FileInputStream(new File("infile.txt")); + fos = new FileOutputStream(new File("outfile.txt")); + fos.write(fis.read()); + } finally { + if (fis != null) fis.close(); + if (fos != null) fos.close(); + } + } + + public static void twoResourcesHeliosFix() throws IOException { + FileInputStream fis = null; + FileOutputStream fos = null; + try { + fis = new FileInputStream(new File("whatever.txt")); + try { + fos = new FileOutputStream(new File("everwhat.txt")); + fos.write(fis.read()); + } finally { + if (fos != null) fos.close(); + } + } finally { + if (fis != null) fis.close(); + } + } + + public static void twoResourcesCommonFix() throws IOException { + FileInputStream fis = null; + FileOutputStream fos = null; + try { + fis = new FileInputStream(new File("infile.txt")); + fos = new FileOutputStream(new File("outfile.txt")); + fos.write(fis.read()); + } finally { + try { + if (fis != null) fis.close(); + } catch (Exception e) { + } + try { + if (fos != null) fos.close(); + } catch (Exception e) { + } + } + } + + public static void twoResourcesServerSocket() throws IOException { + ServerSocket a = null; + ServerSocket b = null; + try { + a = new ServerSocket(); + b = new ServerSocket(); + } finally { + if (a != null) a.close(); + if (b != null) b.close(); + } + } + + public static void twoResourcesRandomAccessFile() throws IOException { + RandomAccessFile a = null; + RandomAccessFile b = null; + try { + a = new RandomAccessFile("", "rw"); + b = new RandomAccessFile("", "rw"); + } finally { + if (a != null) a.close(); + if (b != null) b.close(); + } + } + + public static void twoResourcesRandomAccessFileCommonFix() throws IOException { + RandomAccessFile a = null; + RandomAccessFile b = null; + try { + a = new RandomAccessFile("", "rw"); + b = new RandomAccessFile("", "rw"); + } finally { + try { + if (a != null) a.close(); + } catch (Exception e) { + } + try { + if (b != null) b.close(); + } catch (Exception e) { + } + } + } + + // NestedResource tests + + // BufferedInputStream does not throw exception, and its close + // closes the FileInputStream as well + public void nestedGood() throws IOException { + BufferedInputStream b = new BufferedInputStream(new FileInputStream("file.txt")); + b.close(); + } + + // GZipInputStream can throw IO Exception + // in which case the new FileInputStream will be dangling + public void nestedBad1() throws IOException { + GZIPInputStream g = new GZIPInputStream(new FileInputStream("file.txt")); + g.close(); + } + + public void nestedBad2() throws IOException { + GZIPOutputStream g = new GZIPOutputStream(new FileOutputStream("file.txt")); + g.close(); + } + + /* Fixed versions of this are below with ObjectInputStream tests */ + public void objectInputStreamClosedNestedBad() throws IOException { + ObjectInputStream oin = null; + try { + oin = new ObjectInputStream(new FileInputStream("file.txt")); + int a = oin.available(); + } catch (IOException e) { + } finally { + if (oin != null) oin.close(); + } + } + + /* Fixed versions of this are below with ObjectInputStream tests */ + public void objectOutputStreamClosedNestedBad() throws IOException { + ObjectOutputStream oin = null; + try { + oin = new ObjectOutputStream(new FileOutputStream("file.txt")); + oin.write(3); + } catch (IOException e) { + } finally { + if (oin != null) oin.close(); + } + } + + // ZipFile tests (Jarfile Tests also test Zipfiles) + + public static void zipFileLeakExceptionalBranch() throws IOException { + ZipFile j = null; + try { + j = new ZipFile(""); + } catch (IOException e) { + FileOutputStream fis = new FileOutputStream("file.txt"); + // The purpose of this is to cause a leak, from when ZipFile constructor throws + } finally { + if (j != null) j.close(); + } + } + + public static void zipFileNoLeak() throws IOException { + ZipFile j = null; + try { + j = new ZipFile(""); + } finally { + if (j != null) j.close(); + } + } + + // JarFile tests + + public boolean jarFileClosed() { + JarFile jarFile = null; + try { + jarFile = new JarFile(""); + } catch (IOException e) { + } finally { + try { + if (jarFile != null) { + jarFile.close(); + } + } catch (IOException e) { + } + } + return false; + } + + public boolean jarFileNotClosed() { + JarFile jarFile = null; + try { + jarFile = new JarFile(""); + } catch (IOException e) { + } + return false; + } + + // FileInputStream tests + + public void fileInputStreamNotClosedAfterRead() { + FileInputStream fis; + try { + fis = new FileInputStream("file.txt"); + fis.read(); + fis.close(); + } catch (IOException e) { + } + } + + public void fileInputStreamClosed() throws IOException { + FileInputStream fis = null; + try { + fis = new FileInputStream("file.txt"); + fis.available(); + } catch (IOException e) { + } finally { + if (fis != null) fis.close(); + } + } + + // PipedInputStream tests + + public void pipedInputStreamNotClosedAfterRead(PipedOutputStream pout) { + PipedInputStream pin; + try { + pin = new PipedInputStream(pout); + int data = pin.read(); + pin.close(); + } catch (IOException e) { + } + } + + public void pipedInputStreamClosed(PipedOutputStream pout) throws IOException { + PipedInputStream pin = null; + try { + pin = new PipedInputStream(pout); + int data = pin.read(); + } catch (IOException e) { + } finally { + pin.close(); + } + } + + // PipedOutputStream tests + + public void pipedOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3, 4, 5}; + PipedOutputStream pout; + try { + pout = new PipedOutputStream(); + pout.write(arr); + pout.close(); + } catch (IOException e) { + } + } + + public void pipedOutputStreamClosed(PipedInputStream pin) throws IOException { + PipedOutputStream pout = null; + try { + pout = new PipedOutputStream(pin); + pout.flush(); + } catch (IOException e) { + } finally { + pout.close(); + } + } + + // ObjectOutputStream tests + + public void objectOutputStreamNotClosedAfterWrite() { + byte[] arr = {1, 2, 3, 4, 5}; + ObjectOutputStream oout; + try { + oout = new ObjectOutputStream(new FileOutputStream("file.txt")); + oout.write(arr); + oout.close(); + } catch (IOException e) { + } + } + + public void objectOutputStreamClosed() throws IOException { + ObjectOutputStream oout = null; + FileOutputStream fis = new FileOutputStream("file.txt"); + try { + oout = new ObjectOutputStream(fis); + oout.flush(); + } catch (IOException e) { + } finally { + fis.close(); + } + } + + // ObjectInputStream tests + + public void objectInputStreamNotClosedAfterRead() { + ObjectInputStream oin; + try { + oin = new ObjectInputStream(new FileInputStream("file.txt")); + oin.read(); + oin.close(); + } catch (IOException e) { + } + } + + public void objectInputStreamClosed() throws IOException { + ObjectInputStream oin = null; + FileInputStream fis = new FileInputStream("file.txt"); + try { + oin = new ObjectInputStream(fis); + int a = oin.available(); + } catch (IOException e) { + } finally { + if (oin != null) { + oin.close(); + } else { + fis.close(); + } + } + } + + public void objectInputStreamClosed2() throws IOException { + ObjectInputStream oin = null; + FileInputStream fis = new FileInputStream("file.txt"); + try { + oin = new ObjectInputStream(fis); + int a = oin.available(); + } catch (IOException e) { + } finally { + fis.close(); + } + } + + // JarInputStream tests + + public static void jarInputStreamNoLeak() throws IOException { + FileInputStream fos = new FileInputStream(""); + try { + JarInputStream g = new JarInputStream(fos); + g.close(); + } catch (IOException e) { + fos.close(); + } + } + + public static void jarInputStreamLeak() throws IOException { + FileInputStream fos = new FileInputStream(""); + try { + JarInputStream g = new JarInputStream(fos); // Testing exceptional condition in constructor + g.close(); + } catch (IOException e) { + // fos.close(); + } + } + + public static void nestedBadJarInputStream(File file) throws IOException { + JarInputStream g = new JarInputStream(new FileInputStream(file)); + g.close(); + } + + // JarOutputStream tests + + public static void jarOutputStreamNoLeak() throws IOException { + FileOutputStream fos = new FileOutputStream(""); + try { + JarOutputStream g = new JarOutputStream(fos); + g.close(); + } catch (IOException e) { + fos.close(); + } + } + + public static void jarOutputStreamLeak() throws IOException { + FileOutputStream fos = new FileOutputStream(""); + try { + JarOutputStream g = new JarOutputStream(fos); // Testing exceptional condition in constructor + g.close(); + } catch (IOException e) { + // fos.close(); + } + } + + public static void nestedBadJarOutputStream() throws IOException { + JarOutputStream g = new JarOutputStream(new FileOutputStream("file.txt")); + g.close(); + } + + // Socket tests + + public void socketNotClosed() { + Socket socket = new Socket(); + } + + public void socketClosed() throws IOException { + Socket socket = new Socket(); + socket.close(); + } + + // Socket InputStream tests + + public int socketInputStreamNotClosed(Socket socket) throws IOException { + InputStream stream = socket.getInputStream(); + return stream.read(); + } + + public void socketInputStreamClosed() throws IOException { + Socket socket = new Socket(); + InputStream stream = socket.getInputStream(); + try { + stream.close(); + } catch (Exception e) { + } + socket.close(); + } + + // Socket OutputStream tests + + public void socketOutputStreamNotClosed(Socket socket) throws IOException { + OutputStream stream = socket.getOutputStream(); + stream.write(10); + } + + public void socketOutputStreamClosed() throws IOException { + Socket socket = new Socket(); + OutputStream stream = socket.getOutputStream(); + try { + stream.close(); + } catch (Exception e) { + } + socket.close(); + } + + // ServerSocket tests + + public void serverSocketNotClosed() throws IOException { + ServerSocket listener = new ServerSocket(9090); + while (true) { + Socket socket = listener.accept(); + try { + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + out.println(""); + } finally { + socket.close(); + } + listener.close(); + } + } + + public void serverSocketClosed() throws IOException { + ServerSocket socket = new ServerSocket(); + socket.close(); + } + + public void serverSocketWithSocketClosed() throws IOException { + ServerSocket listener = new ServerSocket(9090); + try { + while (true) { + Socket socket = listener.accept(); + try { + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + out.println(""); + } finally { + socket.close(); + } + } + } finally { + listener.close(); + } + } + + // HttpURLConnection + + public void openHttpURLConnectionDisconnected() throws IOException { + String content = "TEXT"; + DataOutputStream outputStream = null; + HttpURLConnection connection = null; + URL address = new URL("http://www.facebook.com"); + connection = (HttpURLConnection) address.openConnection(); + try { + outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.writeBytes(content); + outputStream.flush(); + + } finally { + connection.disconnect(); + } + } + + public void openHttpURLConnectionNotDisconnected() throws IOException { + String content = "TEXT"; + DataOutputStream outputStream = null; + HttpURLConnection connection = null; + URL address = new URL("http://www.facebook.com"); + connection = (HttpURLConnection) address.openConnection(); + + outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.writeBytes(content); + } + + public void openHttpsURLConnectionNotDisconnected() throws IOException { + HttpsURLConnection connection = null; + URL address = new URL("https://www.facebook.com"); + connection = (HttpsURLConnection) address.openConnection(); + } + + public void openHttpsURLConnectionDisconnected() throws IOException { + HttpsURLConnection connection = null; + URL address = new URL("https://www.facebook.com"); + connection = (HttpsURLConnection) address.openConnection(); + connection.disconnect(); + } + + public void closedWithCloseables() throws IOException { + FileInputStream fs = new FileInputStream("file.txt"); + try { + fs.read(); + } finally { + Closeables.close(fs, false); + } + } + + public void closedQuietlyWithCloseables() throws IOException { + FileInputStream fs = new FileInputStream("file.txt"); + try { + fs.read(); + } finally { + Closeables.closeQuietly(fs); + } + } + + public void closeNullWithCloseables() throws IOException { + FileInputStream fs = null; + try { + fs = new FileInputStream("file.txt"); + } finally { + Closeables.close(fs, true); + } + } + + public void closeNullQuietlyWithCloseables() throws IOException { + FileInputStream fs = null; + try { + fs = new FileInputStream("file.txt"); + } finally { + Closeables.closeQuietly(fs); + } + } + + private static void myClose(Closeable closeable, boolean swallowIOException) throws IOException { + if (closeable == null) { + return; + } + try { + closeable.close(); + } catch (IOException e) { + if (!swallowIOException) { + throw e; + } + } + } + + public void closeWithCloseablesNestedAlloc() throws IOException { + BufferedInputStream b = null; + try { + b = new BufferedInputStream(new FileInputStream("file.txt")); + } finally { + myClose(b, false); + } + } + + // JsonParser tests + + public void parseFromStringAndNotClose(JsonFactory factory) throws IOException { + UTF8StreamJsonParser parser = null; + try { + parser = (UTF8StreamJsonParser) factory.createParser(new File("[]")); + Object o = parser.readValueAs(Object.class); + ignore(o); + } catch (Exception e) { + } finally { + } + } + + public void parseFromInputStreamAndClose(JsonFactory factory) throws IOException { + JsonParser parser = null; + FileInputStream in = null; + try { + in = new FileInputStream(""); + parser = factory.createParser(in); + Object o = parser.readValueAs(Object.class); + ignore(o); + } catch (Exception e) { + } finally { + if (in != null) in.close(); + } + // parser does not own a resources which is closed externally + } + + public void parseFromInputStreamAndLeak(JsonFactory factory) throws IOException { + JsonParser parser = null; + FileInputStream in = null; + try { + in = new FileInputStream(""); + parser = factory.createParser(in); + Object o = parser.readValueAs(Object.class); + ignore(o); + } catch (Exception e) { + } finally { + if (parser != null) parser.close(); + } + // parser does not own a resource which is leaked + } + + private void ignore(Object o) {} + + // Installation.java examples. Even the fix was a fp for a while + // for several reasons, so this test is just to make sure it remains + // banished forever + + private String readInstallationFileGood(File installation) throws IOException { + RandomAccessFile f = new RandomAccessFile(installation, "r"); + try { + byte[] bytes = new byte[(int) f.length()]; + f.readFully(bytes); + return new String(bytes); + } finally { + f.close(); + } + } + + private String readInstallationFileBad(File installation) throws IOException { + RandomAccessFile f = new RandomAccessFile(installation, "r"); + byte[] bytes = new byte[(int) f.length()]; + f.readFully(bytes); + f.close(); + return new String(bytes); + } + + private int readConfigCloseStream(String mTurnConfigUrl) { + try { + URL url = new URL(mTurnConfigUrl); + URLConnection connection = url.openConnection(); + InputStream stream = connection.getInputStream(); + try { + return stream.read(); + } finally { + stream.close(); + } + } catch (Exception e) { + } + return 0; + } + + private int readConfigNotCloseStream(String mTurnConfigUrl) { + try { + URL url = new URL(mTurnConfigUrl); + URLConnection connection = url.openConnection(); + InputStream stream = connection.getInputStream(); + return stream.read(); + } catch (Exception e) { + } + return 0; + } + + private void readConfigNotClosedOK(String mTurnConfigUrl) { + try { + URL url = new URL(mTurnConfigUrl); + URLConnection connection = url.openConnection(); + ignore(connection); + } catch (Exception e) { + } + } + + // TypedArray + + public void themeObtainTypedArrayAndRecycle(Resources.Theme theme) { + TypedArray array = theme.obtainStyledAttributes(new int[] {}); + ignore(array); + array.recycle(); + } + + public void themeObtainTypedArrayAndLeak(Resources.Theme theme) { + TypedArray array = theme.obtainStyledAttributes(new int[] {}); + ignore(array); + } + + public void activityObtainTypedArrayAndRecycle(Activity activity) { + TypedArray array = activity.obtainStyledAttributes(new int[] {}); + ignore(array); + array.recycle(); + } + + public void activityObtainTypedArrayAndLeak(Activity activity) { + TypedArray array = activity.obtainStyledAttributes(new int[] {}); + ignore(array); + } + + public void contextObtainTypedArrayAndRecycle(Context context) { + TypedArray array = context.obtainStyledAttributes(new int[] {}); + ignore(array); + array.recycle(); + } + + public void contextObtainTypedArrayAndLeak(Context context) { + TypedArray array = context.obtainStyledAttributes(new int[] {}); + ignore(array); + } + + // FileChannel + + void copyFileLeak(File src, File dst) throws IOException { + FileChannel inChannel = new FileInputStream(src).getChannel(); + FileChannel outChannel = new FileOutputStream(dst).getChannel(); + try { + inChannel.transferTo(0, inChannel.size(), outChannel); + } finally { + if (inChannel != null) inChannel.close(); + if (outChannel != null) outChannel.close(); + } + } + + void copyFileClose(File src, File dst) throws IOException { + FileChannel inChannel = new FileInputStream(src).getChannel(); + try { + ignore(inChannel); + } finally { + inChannel.close(); + } + } + + protected long checkNotNullCauseNoLeak(URL mUrl) throws IOException { + URL url = new URL("http://www.facebook.com"); + HttpURLConnection serverConnection = + (HttpURLConnection) Preconditions.checkNotNull(url.openConnection()); + try { + ignore(serverConnection); + } catch (NumberFormatException nfe) { + } finally { + serverConnection.disconnect(); + } + return 0; + } + + void scannerNotClosed() throws IOException { + Scanner scanner = new Scanner(new FileInputStream("file.txt")); + } + + void scannerClosed() throws IOException { + Scanner scanner = new Scanner(new FileInputStream("file.txt")); + scanner.close(); + } + + void processDestroyed() { + Process process = null; + try { + process = Runtime.getRuntime().exec(""); + } catch (IOException e) { + } finally { + process.destroy(); + } + } + + void processForciblyDestroyed() throws IOException { + Process process = null; + try { + process = Runtime.getRuntime().exec(""); + } finally { + ignore(process.destroyForcibly()); + } + } + + class Container { + FileInputStream inputStream; + } + + native Container load(FileInputStream inputStream); + + public Container resourceReturnedIndirectly() { + FileInputStream inputStream; + Container container = null; + try { + inputStream = new FileInputStream("pif.txt"); + container = load(inputStream); + } catch (FileNotFoundException e) { + return null; + } + return container; + } + + native void unknownClose(Closeable c); + + public void resourceClosedBySkippedMethod() { + FileInputStream inputStream = null; + try { + inputStream = new FileInputStream("pif.txt"); + } catch (FileNotFoundException e) { + return; + } finally { + unknownClose(inputStream); + } + } + + public int tryWithResource() { + try (FileInputStream inputStream = new FileInputStream("paf.txt")) { + return inputStream.read(); + } catch (IOException e) { + return 0; + } + } + + public InputStreamReader withCharset(URLConnection urlConnection) { + InputStreamReader reader = null; + try { + reader = new InputStreamReader(urlConnection.getInputStream(), "iso-8859-1"); + } catch (Exception e) { + return null; + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + // do nothing + } + } + } + return reader; + } + + public void withZipFile() throws IOException { + ZipFile f = new ZipFile("hi"); + InputStream s = f.getInputStream(f.getEntry("there")); + if (s != null) s.toString(); + f.close(); + } + + public void deflaterLeak() { + Deflater comp = new Deflater(); + } + + public void deflaternoLeak() { + Deflater comp = new Deflater(); + comp.end(); + } + + public void inflaterLeak() { + Inflater decomp = new Inflater(); + } + + public void inflaterNoLeak() { + Inflater decomp = new Inflater(); + decomp.end(); + } + + void NoResourceLeakWarningAfterCheckState(File f, int x) throws IOException { + InputStream stream = new FileInputStream(f); + Preconditions.checkState(x > 0); + stream.close(); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/ReturnValueIgnored.java b/infer/tests/codetoanalyze/java/pulse/ReturnValueIgnored.java new file mode 100644 index 000000000..8d9b0d013 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/ReturnValueIgnored.java @@ -0,0 +1,19 @@ +/* + * 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; + +public class ReturnValueIgnored { + + private int m() { + return 1; + } + + public void returnValueIgnored() { + m(); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/SkippedSourceFile.java b/infer/tests/codetoanalyze/java/pulse/SkippedSourceFile.java new file mode 100644 index 000000000..8be5faf8d --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/SkippedSourceFile.java @@ -0,0 +1,17 @@ +/* + * 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. + */ + +// _SHOULD_BE_SKIPPED_ + +package codetoanalyze.java.infer; + +public class SkippedSourceFile { + + static Object createdBySkippedFile() { + return null; + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/SomeLibrary.java b/infer/tests/codetoanalyze/java/pulse/SomeLibrary.java new file mode 100644 index 000000000..b30086b15 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/SomeLibrary.java @@ -0,0 +1,19 @@ +/* + * 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; + +public class SomeLibrary { + + class $$Z {} + + T t; + + T get() { + return t == null ? null : t; + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/SuppressLintExample.java b/infer/tests/codetoanalyze/java/pulse/SuppressLintExample.java new file mode 100644 index 000000000..804e88c28 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/SuppressLintExample.java @@ -0,0 +1,49 @@ +/* + * 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 com.facebook.infer.annotation.SuppressLint; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +// @SuppressLint("Suppressing all the warnings in a class is not supported yet") +class SuppressAllWarnigsInTheClass { + + void shouldNotReportNPE() { + Object object = null; + object.toString(); + } + + void shouldNotReportResourceLeak() { + try { + FileInputStream fis = new FileInputStream(new File("whatever.txt")); + } catch (IOException e) { + } + } +} + +public class SuppressLintExample { + + @SuppressLint("null-dereference") + SuppressLintExample() { + Object object = null; + object.toString(); + } + + void shouldReportNPE() { + Object object = null; + object.toString(); + } + + @SuppressLint("null-dereference") + void shouldNotReportNPE() { + Object object = null; + object.toString(); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/T.java b/infer/tests/codetoanalyze/java/pulse/T.java new file mode 100644 index 000000000..294dfa2c1 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/T.java @@ -0,0 +1,14 @@ +/* + * 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; + +public class T { + int x; + + void f() {} +} diff --git a/infer/tests/codetoanalyze/java/pulse/Utils.java b/infer/tests/codetoanalyze/java/pulse/Utils.java new file mode 100644 index 000000000..bb88ae671 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/Utils.java @@ -0,0 +1,22 @@ +/* + * 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 java.io.Closeable; + +public class Utils { + + public static void closeQuietly(Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } catch (Exception ex) { + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/WriterLeaks.java b/infer/tests/codetoanalyze/java/pulse/WriterLeaks.java new file mode 100644 index 000000000..f5e2941f0 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/WriterLeaks.java @@ -0,0 +1,184 @@ +/* + * 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 java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PipedReader; +import java.io.PipedWriter; +import java.io.PrintWriter; +import java.io.Writer; + +public class WriterLeaks { + + // Writer tests + + public void writerNotClosedAfterWrite() { + Writer writer; + try { + writer = new PrintWriter("file.txt"); + writer.write(10); + writer.close(); + } catch (IOException e) { + } + } + + public void writerClosed() throws IOException { + Writer writer = null; + try { + writer = new PrintWriter("file.txt"); + writer.write(10); + } catch (IOException e) { + } finally { + if (writer != null) writer.close(); + } + } + + // PrintWriter tests + + public void printWriterNotClosedAfterAppend() { + PrintWriter writer; + try { + writer = new PrintWriter("file.txt"); + writer = writer.append('0'); + writer.close(); + } catch (IOException e) { + } + } + + public void printWriterClosed() throws IOException { + PrintWriter writer = null; + try { + writer = new PrintWriter("file.txt"); + writer = writer.append(null); + } catch (IOException e) { + } finally { + if (writer != null) writer.close(); + } + } + + // BufferedWriter tests + + public void bufferedWriterNotClosedAfterWrite() { + BufferedWriter writer; + try { + FileWriter fw = new FileWriter("file.txt"); + writer = new BufferedWriter(fw); + writer.write("word"); + writer.close(); + } catch (IOException e) { + } + } + + public void bufferedWriterClosed() throws IOException { + BufferedWriter writer = null; + try { + FileWriter fw = new FileWriter("file.txt"); + writer = new BufferedWriter(fw); + writer.flush(); + } catch (IOException e) { + } finally { + if (writer != null) writer.close(); + } + } + + // OutputStreamWriter tests + + public void outputStreamWriterNotClosedAfterWrite() { + OutputStreamWriter writer; + try { + writer = new OutputStreamWriter(new FileOutputStream("file.txt")); + writer.write("word"); + writer.close(); + } catch (IOException e) { + } + } + + public void outputStreamWriterClosed() throws IOException { + OutputStreamWriter writer = null; + try { + writer = new OutputStreamWriter(new FileOutputStream("file.txt")); + writer.write(10); + } catch (IOException e) { + } finally { + if (writer != null) writer.close(); + } + } + + // FileWriter tests + + public void fileWriterNotClosedAfterWrite() { + FileWriter writer; + try { + writer = new FileWriter("file.txt"); + writer.write("word"); + writer.close(); + } catch (IOException e) { + } + } + + public void fileWriterClosed() throws IOException { + FileWriter writer = null; + try { + writer = new FileWriter("file.txt"); + writer.write(10); + } catch (IOException e) { + } finally { + if (writer != null) writer.close(); + } + } + + // PipedWriter tests + + public void pipedWriterNotClosedAfterConstructedWithReader() { + PipedWriter writer; + PipedReader reader; + try { + reader = new PipedReader(); + writer = new PipedWriter(reader); + writer.write(42); + writer.close(); + } catch (IOException e) { + } + } + + public void pipedWriterNotClosedAfterConnect(PipedReader reader) { + PipedWriter writer; + try { + writer = new PipedWriter(); + writer.connect(reader); + writer.write(42); + writer.close(); + } catch (IOException e) { + } + } + + public void pipedWriterNotConnected() { + PipedWriter writer; + try { + writer = new PipedWriter(); + writer.close(); + } catch (IOException e) { + } + } + + public void pipedWriterClosed(PipedReader reader) throws IOException { + PipedWriter writer = null; + try { + writer = new PipedWriter(); + writer.connect(reader); + writer.write(42); + } catch (IOException e) { + } finally { + if (writer != null) writer.close(); + } + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/issues.exp b/infer/tests/codetoanalyze/java/pulse/issues.exp index c12072be8..e7b6fb65f 100644 --- a/infer/tests/codetoanalyze/java/pulse/issues.exp +++ b/infer/tests/codetoanalyze/java/pulse/issues.exp @@ -1,3 +1,10 @@ +codetoanalyze/java/pulse/AnalysisStops.java, codetoanalyze.java.infer.AnalysisStops.accessPathInCalleeMayCauseFalseNegativeBad():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,when calling `void AnalysisStops.accessPathOnParamOk(AnalysisStops$MyObj)` here,parameter `obj` of void AnalysisStops.accessPathOnParamOk(AnalysisStops$MyObj),assigned,assigned,invalid access occurs here] +codetoanalyze/java/pulse/AnalysisStops.java, codetoanalyze.java.infer.AnalysisStops.fieldReadInCalleeMayCauseFalseNegativeBad():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,when calling `void AnalysisStops.derefParamOk(AnalysisStops$MyObj)` here,parameter `obj` of void AnalysisStops.derefParamOk(AnalysisStops$MyObj),assigned,invalid access occurs here] +codetoanalyze/java/pulse/AnalysisStops.java, codetoanalyze.java.infer.AnalysisStops.fieldReadInCalleeWithAngelicObjFieldMayCauseFalseNegativeBad():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,when calling `void AnalysisStops.derefParamOk(AnalysisStops$MyObj)` here,parameter `obj` of void AnalysisStops.derefParamOk(AnalysisStops$MyObj),assigned,invalid access occurs here] +codetoanalyze/java/pulse/Builtins.java, codetoanalyze.java.infer.Builtins.blockErrorIntAssumeOk(java.lang.Object):void, 4, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Builtins.java, codetoanalyze.java.infer.Builtins.blockErrorOk():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Builtins.java, codetoanalyze.java.infer.Builtins.doNotBlockErrorBad(java.lang.Object):void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [*** LATENT ***,invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Builtins.java, codetoanalyze.java.infer.Builtins.doNotBlockErrorBad(java.lang.Object):void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [*** LATENT ***,invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] codetoanalyze/java/pulse/DefaultInInterface.java, DefaultInInterface$A.defaultCallNPE():void, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DefaultInInterface$I.defaultMethod1()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DefaultInInterface$I.defaultMethod1()`,return from call to `Object DefaultInInterface$I.defaultMethod1()`,assigned,invalid access occurs here] codetoanalyze/java/pulse/DefaultInInterface.java, DefaultInInterface$B.overridenCallNPE():void, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DefaultInInterface$B.defaultMethod2()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DefaultInInterface$B.defaultMethod2()`,return from call to `Object DefaultInInterface$B.defaultMethod2()`,assigned,invalid access occurs here] codetoanalyze/java/pulse/DefaultInInterface.java, DefaultInInterface.uncertainCallMethod1NPE_latent(int):void, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [*** LATENT ***,invalidation part of the trace starts here,when calling `Object DefaultInInterface$I.defaultMethod1()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DefaultInInterface$I.defaultMethod1()`,return from call to `Object DefaultInInterface$I.defaultMethod1()`,assigned,invalid access occurs here] @@ -7,3 +14,46 @@ codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicD codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch.dynamicDispatchShouldNotCauseFalseNegativeEasyBad():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch$Subtype.foo()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch$Subtype.foo()`,return from call to `Object DynamicDispatch$Subtype.foo()`,assigned,invalid access occurs here] codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch.dynamicResolutionWithVariadicMethodBad():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch.variadicMethod(DynamicDispatch$Supertype[])` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch.variadicMethod(DynamicDispatch$Supertype[])`,return from call to `Object DynamicDispatch.variadicMethod(DynamicDispatch$Supertype[])`,assigned,invalid access occurs here] codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch.interfaceShouldNotCauseFalseNegativeEasyBad():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch$Impl.foo()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch$Impl.foo()`,return from call to `Object DynamicDispatch$Impl.foo()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/IntegerExample.java, codetoanalyze.java.infer.IntegerExample.testIntegerEqualsBad():void, 6, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/IntegerExample.java, codetoanalyze.java.infer.IntegerExample.testIntegerEqualsFN():void, 6, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/IntegerExample.java, codetoanalyze.java.infer.IntegerExample.testIntegerEqualsGood():void, 6, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/IntegerExample.java, codetoanalyze.java.infer.IntegerExample.testIntegerEqualsGood():void, 10, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/InvokeDynamic.java, codetoanalyze.java.infer.InvokeDynamic.invokeDynamicThenNpeBad(java.util.List):void, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/InvokeDynamic.java, codetoanalyze.java.infer.InvokeDynamic.lambda$npeInLambdaBad$1(java.lang.String,java.lang.String):int, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Lists.java, codetoanalyze.java.infer.Lists.FN_addInvalidatesEmptinessNPE(java.util.List):void, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Lists.java, codetoanalyze.java.infer.Lists.clearCausesEmptinessNPE(java.util.List,int):void, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Lists.java, codetoanalyze.java.infer.Lists.emptyRemembersOk(java.util.List):void, 4, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Lists.java, codetoanalyze.java.infer.Lists.getElementNPE(java.util.List):void, 4, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object Lists.getElement(List)` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object Lists.getElement(List)`,return from call to `Object Lists.getElement(List)`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Lists.java, codetoanalyze.java.infer.Lists.getElementOk(java.util.List):void, 4, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object Lists.getElement(List)` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object Lists.getElement(List)`,return from call to `Object Lists.getElement(List)`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/Lists.java, codetoanalyze.java.infer.Lists.removeInvalidatesNonEmptinessNPE(java.util.List,int):void, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions$$$Class$Name$With$Dollars.npeWithDollars():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.badCheckShouldCauseNPE():void, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object NullPointerExceptions.getObj()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object NullPointerExceptions.getObj()`,return from call to `Object NullPointerExceptions.getObj()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.derefBoxedGetterAfterCheckShouldNotCauseNPE():void, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Boolean NullPointerExceptions.getBool()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Boolean NullPointerExceptions.getBool()`,return from call to `Boolean NullPointerExceptions.getBool()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.derefGetterAfterCheckShouldNotCauseNPE():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object NullPointerExceptions.getObj()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object NullPointerExceptions.getObj()`,return from call to `Object NullPointerExceptions.getObj()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.derefNonThisGetterAfterCheckShouldNotCauseNPE():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object NullPointerExceptions.getObj()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object NullPointerExceptions.getObj()`,return from call to `Object NullPointerExceptions.getObj()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.derefNull():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object NullPointerExceptions.derefUndefinedCallee()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object NullPointerExceptions.derefUndefinedCallee()`,return from call to `Object NullPointerExceptions.derefUndefinedCallee()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.derefNullableRet(boolean):void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [*** LATENT ***,invalidation part of the trace starts here,when calling `Object NullPointerExceptions.nullableRet(boolean)` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object NullPointerExceptions.nullableRet(boolean)`,return from call to `Object NullPointerExceptions.nullableRet(boolean)`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.dereferenceAfterLoopOnList(codetoanalyze.java.infer.NullPointerExceptions$L):void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [*** LATENT ***,invalidation part of the trace starts here,when calling `Object NullPointerExceptions.returnsNullAfterLoopOnList(NullPointerExceptions$L)` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object NullPointerExceptions.returnsNullAfterLoopOnList(NullPointerExceptions$L)`,return from call to `Object NullPointerExceptions.returnsNullAfterLoopOnList(NullPointerExceptions$L)`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.dereferenceAfterUnlock1(java.util.concurrent.locks.Lock):void, 4, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.dereferenceAfterUnlock2(java.util.concurrent.locks.Lock):void, 6, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerException():int, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerExceptionArrayLength():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerExceptionCallArrayReadMethod():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object NullPointerExceptions.arrayReadShouldNotCauseSymexMemoryError(int)` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object NullPointerExceptions.arrayReadShouldNotCauseSymexMemoryError(int)`,return from call to `Object NullPointerExceptions.arrayReadShouldNotCauseSymexMemoryError(int)`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerExceptionInArrayLengthLoop(java.lang.Object[]):void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerExceptionInterProc():int, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `NullPointerExceptions$A NullPointerExceptions.canReturnNullObject(boolean)` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `NullPointerExceptions$A NullPointerExceptions.canReturnNullObject(boolean)`,return from call to `NullPointerExceptions$A NullPointerExceptions.canReturnNullObject(boolean)`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerExceptionUnlessFrameFails():void, 4, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerExceptionWithArray():int, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerExceptionWithNullArrayParameter():void, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,when calling `void NullPointerExceptions.expectNotNullArrayParameter(NullPointerExceptions$A[])` here,parameter `array` of void NullPointerExceptions.expectNotNullArrayParameter(NullPointerExceptions$A[]),invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.nullPointerExceptionWithNullObjectParameter():void, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,when calling `void NullPointerExceptions.expectNotNullObjectParameter(NullPointerExceptions$A)` here,parameter `a` of void NullPointerExceptions.expectNotNullObjectParameter(NullPointerExceptions$A),invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.otherSinkWithNeverNullSource():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `T SomeLibrary.get()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `T SomeLibrary.get()`,return from call to `T SomeLibrary.get()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.shouldNotReportNPE():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.sinkWithNeverNullSource():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `T NeverNullSource.get()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `T NeverNullSource.get()`,return from call to `T NeverNullSource.get()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.someNPEAfterResourceLeak():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `T CloseableAsResourceExample.sourceOfNullWithResourceLeak()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `T CloseableAsResourceExample.sourceOfNullWithResourceLeak()`,return from call to `T CloseableAsResourceExample.sourceOfNullWithResourceLeak()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.stringConstantEqualsFalseNotNPE_FP():void, 10, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.stringConstantEqualsTrueNotNPE():void, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.stringConstantEqualsTrueNotNPE():void, 12, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/NullPointerExceptions.java, codetoanalyze.java.infer.NullPointerExceptions.stringVarEqualsFalseNPE():void, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/SuppressLintExample.java, codetoanalyze.java.infer.SuppressAllWarnigsInTheClass.shouldNotReportNPE():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/SuppressLintExample.java, codetoanalyze.java.infer.SuppressLintExample.(), 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/SuppressLintExample.java, codetoanalyze.java.infer.SuppressLintExample.shouldNotReportNPE():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] +codetoanalyze/java/pulse/SuppressLintExample.java, codetoanalyze.java.infer.SuppressLintExample.shouldReportNPE():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here]