Skip to content

MASTG-DEMO-0037: App Leaking Information about Unclosed SQL Cursor via StrictMode

Content in BETA

This content is in beta and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Download MASTG-DEMO-0037 APK Open MASTG-DEMO-0037 Folder Build MASTG-DEMO-0037 APK

Sample

The snippet below shows sample code that enables a StrictMode policy to detect leaked (i.e., unclosed) SQLite objects. When such an object is detected, a log message is emitted to the system log.

The snippet then opens an SQL Cursor which remains unclosed, to trigger the policy.

MastgTest.kt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package org.owasp.mastestapp

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.os.StrictMode


class MastgTest (private val context: Context){

    fun mastgTest(): String {
        enableStrictMode()
        triggerSqliteCursorLeak()

        System.gc() // Force garbage collection to trigger leak detection

        return "SUCCESS!!\n\nSQL Cursor leaked."
    }

    private fun enableStrictMode() {
        StrictMode.setVmPolicy(
            StrictMode.VmPolicy.Builder()
                .detectLeakedClosableObjects() // Detect leaked/unclosed SQLite objects
                .penaltyLog()                 // Log violations
                .build()
        )
    }

    private fun triggerSqliteCursorLeak() {
        val db: SQLiteDatabase = context.openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null)
        db.execSQL("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")
        db.execSQL("INSERT INTO users (name) VALUES ('Alice'), ('Bob')")

        // Create cursor, and intentionally do not close it
        val cursor = db.rawQuery("SELECT * FROM users", null)
    }
}

Steps

  1. Install the app on your device.
  2. Open the app and let it execute.
  3. Execute run.sh which uses Monitoring System Logs to show the system logs created by StrictMode.
run.sh
1
adb logcat -s StrictMode -d > output.txt

Observation

The system log outputs all detected StrictMode policy violations.

output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
03-26 11:33:23.415  2340  2407 D StrictMode: StrictMode policy violation: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.os.StrictMode$AndroidCloseGuardReporter.report(StrictMode.java:1994)
03-26 11:33:23.415  2340  2407 D StrictMode:    at dalvik.system.CloseGuard.warnIfOpen(CloseGuard.java:336)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.database.AbstractCursor.finalize(AbstractCursor.java:531)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.database.sqlite.SQLiteCursor.finalize(SQLiteCursor.java:291)
03-26 11:33:23.415  2340  2407 D StrictMode:    at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:339)
03-26 11:33:23.415  2340  2407 D StrictMode:    at java.lang.Daemons$FinalizerDaemon.processReference(Daemons.java:324)
03-26 11:33:23.415  2340  2407 D StrictMode:    at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300)
03-26 11:33:23.415  2340  2407 D StrictMode:    at java.lang.Daemons$Daemon.run(Daemons.java:145)
03-26 11:33:23.415  2340  2407 D StrictMode:    at java.lang.Thread.run(Thread.java:1012)
03-26 11:33:23.415  2340  2407 D StrictMode: Caused by: java.lang.Throwable: Explicit termination method 'AbstractCursor.close' not called
03-26 11:33:23.415  2340  2407 D StrictMode:    at dalvik.system.CloseGuard.openWithCallSite(CloseGuard.java:288)
03-26 11:33:23.415  2340  2407 D StrictMode:    at dalvik.system.CloseGuard.open(CloseGuard.java:257)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.database.AbstractCursor.<init>(AbstractCursor.java:227)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.database.AbstractWindowedCursor.<init>(AbstractWindowedCursor.java:39)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.database.sqlite.SQLiteCursor.<init>(SQLiteCursor.java:98)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:52)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1714)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1653)
03-26 11:33:23.415  2340  2407 D StrictMode:    at org.owasp.mastestapp.MastgTest.triggerSqliteCursorLeak(MastgTest.kt:35)
03-26 11:33:23.415  2340  2407 D StrictMode:    at org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:12)
03-26 11:33:23.415  2340  2407 D StrictMode:    at org.owasp.mastestapp.MainActivityKt$MyScreenContent$1$1$1.invoke(MainActivity.kt:117)
03-26 11:33:23.415  2340  2407 D StrictMode:    at org.owasp.mastestapp.MainActivityKt$MyScreenContent$1$1$1.invoke(MainActivity.kt:115)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.foundation.ClickablePointerInputNode$pointerInput$3.invoke-k-4lQ0M(Clickable.kt:987)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.foundation.ClickablePointerInputNode$pointerInput$3.invoke(Clickable.kt:981)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)
03-26 11:33:23.415  2340  2407 D StrictMode:    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
03-26 11:33:23.415  2340  2407 D StrictMode:    at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:177)
03-26 11:33:23.415  2340  2407 D StrictMode:    at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
03-26 11:33:23.415  2340  2407 D StrictMode:    at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474)
03-26 11:33:23.415  2340  2407 D StrictMode:    at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508)
03-26 11:33:23.415  2340  2407 D StrictMode:    at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497)
03-26 11:33:23.415  2340  2407 D StrictMode:    at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:368)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.kt:665)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.dispatchPointerEvent(SuspendingPointerInputFilter.kt:544)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:566)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.foundation.AbstractClickablePointerInputNode.onPointerEvent-H0pRuoY(Clickable.kt:947)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.foundation.AbstractClickableNode.onPointerEvent-H0pRuoY(Clickable.kt:795)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:317)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:303)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:185)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:104)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:113)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.platform.AndroidComposeView.sendMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1576)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.platform.AndroidComposeView.handleMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1527)
03-26 11:33:23.415  2340  2407 D StrictMode:    at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:1466)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-26 11:33:23.415  2340  2407 D StrictMode:    at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:490)
03-26 11:33:23.415  2340  2407 D StrictMode:    at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1904)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.app.Activity.dispatchTouchEvent(Activity.java:4377)
03-26 11:33:23.415  2340  2407 D StrictMode:    at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:448)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.View.dispatchPointerEvent(View.java:15919)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:7021)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:6815)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6229)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6286)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6252)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:6417)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6260)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:6474)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6233)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6286)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6252)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6260)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6233)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:9211)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:9162)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:9131)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:9337)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:267)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.os.MessageQueue.nativePollOnce(Native Method)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.os.MessageQueue.next(MessageQueue.java:335)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.os.Looper.loopOnce(Looper.java:162)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.os.Looper.loop(Looper.java:294)
03-26 11:33:23.415  2340  2407 D StrictMode:    at android.app.ActivityThread.main(ActivityThread.java:8177)
03-26 11:33:23.415  2340  2407 D StrictMode:    at java.lang.reflect.Method.invoke(Native Method)
03-26 11:33:23.415  2340  2407 D StrictMode:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
03-26 11:33:23.415  2340  2407 D StrictMode:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

Evaluation

The test fails because StrictMode is enabled, as we can see from the system log output which shows that there is a location (MastgTest.kt:35) where an SQL cursor is not closed properly.

Note: The reported cursor not being closed is a different issue outside the scope of this demo.