Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d11f4d1 authored by Lee Shombert's avatar Lee Shombert
Browse files

Report double-quoted string literals in SQL statements

This defines a system property `debug.sqlite.no_double_quoted_strs`
that controls if double-quoted string literals are accepted in SQL
statements.  Set the property to 1 to treat double-quoted string
literals as an error.  Exceptions will be thrown.  Set the property to
0 to accept double-quoted strings in compatibility mode.

The default value is 0, which is the legacy Android behavior.

See the following link for a discussion of the issue:
https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted

A developer can set the property to determine if an application has
syntax errors.

This does not change the default behavior of an Android system.

This change does not fix the bug.  It only provides debugging to help
address the bug.

Tested by setting the property to true and verifying that exceptions
are thrown with the error "no such column".  The tests below are run
with the property not set (the default condition).

Flag: EXEMPT bug-fix
Bug: 364625688
Test: atest
 * FrameworksCoreTests:android.database
 * CtsDatabaseTestCases
Change-Id: Ie4c4337995f85caa9432645b75b3cd13db873e1b
parent 694369c5
Loading
Loading
Loading
Loading
+33 −5
Original line number Diff line number Diff line
@@ -236,15 +236,21 @@ public final class SQLiteDatabase extends SQLiteClosable {
     *
     * {@more} Note that the value of this flag is 0, so it is the default.
     */
    public static final int OPEN_READWRITE = 0x00000000;          // update native code if changing
    // LINT.IfChange
    public static final int OPEN_READWRITE = 0x00000000;
    // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)

    /**
     * Open flag: Flag for {@link #openDatabase} to open the database for reading only.
     * This is the only reliable way to open a database if the disk may be full.
     */
    public static final int OPEN_READONLY = 0x00000001;           // update native code if changing
    // LINT.IfChange
    public static final int OPEN_READONLY = 0x00000001;
    // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)

    private static final int OPEN_READ_MASK = 0x00000001;         // update native code if changing
    // LINT.IfChange
    private static final int OPEN_READ_MASK = 0x00000001;
    // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)

    /**
     * Open flag: Flag for {@link #openDatabase} to open the database without support for
@@ -254,13 +260,31 @@ public final class SQLiteDatabase extends SQLiteClosable {
     * You must be consistent when using this flag to use the setting the database was
     * created with.  If this is set, {@link #setLocale} will do nothing.
     */
    public static final int NO_LOCALIZED_COLLATORS = 0x00000010;  // update native code if changing
    // LINT.IfChange
    public static final int NO_LOCALIZED_COLLATORS = 0x00000010;
    // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)

    /**
     * Open flag: Flag for {@link #openDatabase} to open a database, disallowing double-quoted
     * strings.
     *
     * This causes sqlite to reject SQL statements with double-quoted string literals.  String
     * literals must be enclosed in single quotes; double-quotes are reserved for identifiers like
     * column names.
     * See https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted
     * @hide
     */
    // LINT.IfChange
    public static final int NO_DOUBLE_QUOTED_STRS = 0x00000020;
    // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)

    /**
     * Open flag: Flag for {@link #openDatabase} to create the database file if it does not
     * already exist.
     */
    public static final int CREATE_IF_NECESSARY = 0x10000000;     // update native code if changing
    // LINT.IfChange
    public static final int CREATE_IF_NECESSARY = 0x10000000;
    // LINT.ThenChange(/core/jni/android_database_SQLiteConnection.cpp)

    /**
     * Open flag: Flag for {@link #openDatabase} to open the database file with
@@ -490,6 +514,9 @@ public final class SQLiteDatabase extends SQLiteClosable {
        if (SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()) {
            mConfigurationLocked.openFlags |= ENABLE_LEGACY_COMPATIBILITY_WAL;
        }
        if (SQLiteDebug.NoPreloadHolder.NO_DOUBLE_QUOTED_STRS) {
            mConfigurationLocked.openFlags |= NO_DOUBLE_QUOTED_STRS;
        }
        mConfigurationLocked.journalMode = journalMode;
        mConfigurationLocked.syncMode = syncMode;
    }
@@ -3275,6 +3302,7 @@ public final class SQLiteDatabase extends SQLiteClosable {
            OPEN_READONLY,
            CREATE_IF_NECESSARY,
            NO_LOCALIZED_COLLATORS,
            NO_DOUBLE_QUOTED_STRS,
            ENABLE_WRITE_AHEAD_LOGGING
    })
    @Retention(RetentionPolicy.SOURCE)
+9 −1
Original line number Diff line number Diff line
@@ -66,7 +66,6 @@ public final class SQLiteDebug {
        public static final boolean DEBUG_SQL_TIME =
                Log.isLoggable("SQLiteTime", Log.VERBOSE);


        /**
         * True to enable database performance testing instrumentation.
         */
@@ -83,6 +82,15 @@ public final class SQLiteDebug {
         */
        public static final boolean DEBUG_LOG_DETAILED = Build.IS_DEBUGGABLE
                && SystemProperties.getBoolean("db.log.detailed", false);

        /**
         * Whether to accept double-quoted strings in SQL statements.  Double-quoted strings are a
         * syntax error but are accepted by sqlite in compatibility mode (the default).  If the
         * property is set to true, double-quoted strings will be treated by sqlite as a syntax
         * error.
         */
        public static final boolean NO_DOUBLE_QUOTED_STRS =
                SystemProperties.getBoolean("debug.sqlite.no_double_quoted_strs", false);
    }

    private SQLiteDebug() {
+15 −0
Original line number Diff line number Diff line
@@ -70,11 +70,14 @@ struct SQLiteConnection {
    // Open flags.
    // Must be kept in sync with the constants defined in SQLiteDatabase.java.
    enum {
        // LINT.IfChange
        OPEN_READWRITE          = 0x00000000,
        OPEN_READONLY           = 0x00000001,
        OPEN_READ_MASK          = 0x00000001,
        NO_LOCALIZED_COLLATORS  = 0x00000010,
        NO_DOUBLE_QUOTED_STRS   = 0x00000020,
        CREATE_IF_NECESSARY     = 0x10000000,
        // LINT.ThenChange(/core/java/android/database/sqlite/SQLiteDatabase.java)
    };

    sqlite3* const db;
@@ -156,6 +159,18 @@ static jlong nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFla
        }
    }

    // Disallow double-quoted string literals if the proper flag is set.
    if ((openFlags & SQLiteConnection::NO_DOUBLE_QUOTED_STRS) != 0) {
        void *setting = 0;
        int err = 0;
        if ((err = sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, setting)) != SQLITE_OK) {
            ALOGE("failed to configure SQLITE_DBCONFIG_DQS_DDL: %d", err);
        }
        if ((err = sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, setting)) != SQLITE_OK) {
            ALOGE("failed to configure SQLITE_DBCONFIG_DQS_DML: %d", err);
        }
    }

    // Check that the database is really read/write when that is what we asked for.
    if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) {
        throw_sqlite3_exception(env, db, "Could not open the database in read/write mode.");
+54 −0
Original line number Diff line number Diff line
@@ -538,4 +538,58 @@ public class SQLiteDatabaseTest {

        assertEquals(1, db.mConnection.size());
    }

    // Create and open the database, allowing or disallowing double-quoted strings.
    private void createDatabase(boolean noDoubleQuotedStrs) throws Exception {
        // The open-flags that do not change in this test.
        int flags = SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.OPEN_READWRITE;

        // The flag to be tested.
        int flagUnderTest = SQLiteDatabase.NO_DOUBLE_QUOTED_STRS;

        if (noDoubleQuotedStrs) {
            flags |= flagUnderTest;
        } else {
            flags &= ~flagUnderTest;
        }
        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), null, flags, null);
    }

    /**
     * This test verifies that the NO_DOUBLE_QUOTED_STRS flag works as expected when opening a
     * database.  This does not test that the flag is initialized as expected from the system
     * properties.
     */
    @Test
    public void testNoDoubleQuotedStrings() throws Exception {
        closeAndDeleteDatabase();
        createDatabase(/* noDoubleQuotedStrs */ false);

        mDatabase.beginTransaction();
        try {
            mDatabase.execSQL("CREATE TABLE t1 (t text);");
            // Insert a value in double-quotes.  This is invalid but accepted.
            mDatabase.execSQL("INSERT INTO t1 (t) VALUES (\"foo\")");
        } finally {
            mDatabase.endTransaction();
        }

        closeAndDeleteDatabase();
        createDatabase(/* noDoubleQuotedStrs */ true);

        mDatabase.beginTransaction();
        try {
            mDatabase.execSQL("CREATE TABLE t1 (t text);");
            try {
                // Insert a value in double-quotes.  This is invalid and must throw.
                mDatabase.execSQL("INSERT INTO t1 (t) VALUES (\"foo\")");
                fail("expected an exception");
            } catch (SQLiteException e) {
                assertTrue(e.toString().contains("no such column"));
            }
        } finally {
            mDatabase.endTransaction();
        }
        closeAndDeleteDatabase();
    }
}