Resolving potential memory leak vulnerability in QueryInterceptorStatement.
Each time a `bind*` in QueryInterceptorStatement is invoked, `saveArgsToCache` is called to save the new argument being processed into the `bindArgsCache`.
This works as intended for all `bind*` functions except `bindNull()`, due to the fact that the `bindArgsCache` itself is is passed in for the `value` parameter of `saveArgsToCache` instead of a `null` value. This results in the entire cache being placed at the given index within `bindArgsCache` as opposed a single null value.
Bug: 290641662
Test: QueryInterceptorTest.kt
Change-Id: If6c711f836ac1e90aed55bccdd24bcbd9e36bf6f
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
index 2808373..9fb9cc8 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
@@ -51,7 +51,7 @@
var queryAndArgs = CopyOnWriteArrayList<Pair<String, ArrayList<Any?>>>()
@Entity(tableName = "queryInterceptorTestDatabase")
- data class QueryInterceptorEntity(@PrimaryKey val id: String, val description: String)
+ data class QueryInterceptorEntity(@PrimaryKey val id: String, val description: String?)
@Dao
interface QueryInterceptorDao {
@@ -197,6 +197,23 @@
}
@Test
+ fun testNullBindArgumentCompileStatement() {
+ val sql = "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+ "VALUES (?,?)"
+ val statement = mDatabase.openHelper.writableDatabase.compileStatement(sql)
+ statement.bindString(1, "ID")
+ statement.bindNull(2)
+ statement.execute()
+
+ val filteredQueries = queryAndArgs.filter { (query, _) -> query == sql }
+
+ assertThat(filteredQueries).hasSize(1)
+ assertThat(filteredQueries[0].second).hasSize(2)
+ assertThat(filteredQueries[0].second[0]).isEqualTo("ID")
+ assertThat(filteredQueries[0].second[1]).isEqualTo(null)
+ }
+
+ @Test
fun testCallbackCalledOnceAfterCloseAndReOpen() {
val dbBuilder = Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt
index 50cf02c..37eca34 100644
--- a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt
+++ b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt
@@ -67,7 +67,7 @@
}
override fun bindNull(index: Int) {
- saveArgsToCache(index, arrayOf(*bindArgsCache.toTypedArray()))
+ saveArgsToCache(index, null)
delegate.bindNull(index)
}