mozglue/android/SQLiteBridge.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include <stdlib.h>
     6 #include <stdio.h>
     7 #include <jni.h>
     8 #include <android/log.h>
     9 #include "dlfcn.h"
    10 #include "APKOpen.h"
    11 #include "ElfLoader.h"
    12 #include "SQLiteBridge.h"
    14 #ifdef DEBUG
    15 #define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
    16 #else
    17 #define LOG(x...)
    18 #endif
    20 #define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name;
    22 SQLITE_WRAPPER_INT(sqlite3_open)
    23 SQLITE_WRAPPER_INT(sqlite3_errmsg)
    24 SQLITE_WRAPPER_INT(sqlite3_prepare_v2)
    25 SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count)
    26 SQLITE_WRAPPER_INT(sqlite3_bind_text)
    27 SQLITE_WRAPPER_INT(sqlite3_step)
    28 SQLITE_WRAPPER_INT(sqlite3_column_count)
    29 SQLITE_WRAPPER_INT(sqlite3_finalize)
    30 SQLITE_WRAPPER_INT(sqlite3_close)
    31 SQLITE_WRAPPER_INT(sqlite3_column_name)
    32 SQLITE_WRAPPER_INT(sqlite3_column_type)
    33 SQLITE_WRAPPER_INT(sqlite3_column_blob)
    34 SQLITE_WRAPPER_INT(sqlite3_column_bytes)
    35 SQLITE_WRAPPER_INT(sqlite3_column_text)
    36 SQLITE_WRAPPER_INT(sqlite3_changes)
    37 SQLITE_WRAPPER_INT(sqlite3_last_insert_rowid)
    39 void setup_sqlite_functions(void *sqlite_handle)
    40 {
    41 #define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(sqlite_handle, #name)
    42   GETFUNC(sqlite3_open);
    43   GETFUNC(sqlite3_errmsg);
    44   GETFUNC(sqlite3_prepare_v2);
    45   GETFUNC(sqlite3_bind_parameter_count);
    46   GETFUNC(sqlite3_bind_text);
    47   GETFUNC(sqlite3_step);
    48   GETFUNC(sqlite3_column_count);
    49   GETFUNC(sqlite3_finalize);
    50   GETFUNC(sqlite3_close);
    51   GETFUNC(sqlite3_column_name);
    52   GETFUNC(sqlite3_column_type);
    53   GETFUNC(sqlite3_column_blob);
    54   GETFUNC(sqlite3_column_bytes);
    55   GETFUNC(sqlite3_column_text);
    56   GETFUNC(sqlite3_changes);
    57   GETFUNC(sqlite3_last_insert_rowid);
    58 #undef GETFUNC
    59 }
    61 static bool initialized = false;
    62 static jclass stringClass;
    63 static jclass objectClass;
    64 static jclass byteBufferClass;
    65 static jclass cursorClass;
    66 static jmethodID jByteBufferAllocateDirect;
    67 static jmethodID jCursorConstructor;
    68 static jmethodID jCursorAddRow;
    70 static jobject sqliteInternalCall(JNIEnv* jenv, sqlite3 *db, jstring jQuery,
    71                                   jobjectArray jParams, jlongArray jQueryRes);
    73 static void throwSqliteException(JNIEnv* jenv, const char* aFormat, ...)
    74 {
    75     va_list ap;
    76     va_start(ap, aFormat);
    77     char* msg = nullptr;
    78     vasprintf(&msg, aFormat, ap);
    79     LOG("Error in SQLiteBridge: %s\n", msg);
    80     JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", msg);
    81     free(msg);
    82     va_end(ap);
    83 }
    85 static void
    86 JNI_Setup(JNIEnv* jenv)
    87 {
    88     if (initialized) return;
    90     jclass lObjectClass       = jenv->FindClass("java/lang/Object");
    91     jclass lStringClass       = jenv->FindClass("java/lang/String");
    92     jclass lByteBufferClass   = jenv->FindClass("java/nio/ByteBuffer");
    93     jclass lCursorClass       = jenv->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor");
    95     if (lStringClass == nullptr
    96         || lObjectClass == nullptr
    97         || lByteBufferClass == nullptr
    98         || lCursorClass == nullptr) {
    99         throwSqliteException(jenv, "FindClass error");
   100         return;
   101     }
   103     // Those are only local references. Make them global so they work
   104     // across calls and threads.
   105     objectClass = (jclass)jenv->NewGlobalRef(lObjectClass);
   106     stringClass = (jclass)jenv->NewGlobalRef(lStringClass);
   107     byteBufferClass = (jclass)jenv->NewGlobalRef(lByteBufferClass);
   108     cursorClass = (jclass)jenv->NewGlobalRef(lCursorClass);
   110     if (stringClass == nullptr || objectClass == nullptr
   111         || byteBufferClass == nullptr
   112         || cursorClass == nullptr) {
   113         throwSqliteException(jenv, "NewGlobalRef error");
   114         return;
   115     }
   117     // public static ByteBuffer allocateDirect(int capacity)
   118     jByteBufferAllocateDirect =
   119         jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
   120     // new MatrixBlobCursor(String [])
   121     jCursorConstructor =
   122         jenv->GetMethodID(cursorClass, "<init>", "([Ljava/lang/String;)V");
   123     // public void addRow (Object[] columnValues)
   124     jCursorAddRow =
   125         jenv->GetMethodID(cursorClass, "addRow", "([Ljava/lang/Object;)V");
   127     if (jByteBufferAllocateDirect == nullptr
   128         || jCursorConstructor == nullptr
   129         || jCursorAddRow == nullptr) {
   130         throwSqliteException(jenv, "GetMethodId error");
   131         return;
   132     }
   134     initialized = true;
   135 }
   137 extern "C" NS_EXPORT jobject JNICALL
   138 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
   139                                                       jstring jDb,
   140                                                       jstring jQuery,
   141                                                       jobjectArray jParams,
   142                                                       jlongArray jQueryRes)
   143 {
   144     JNI_Setup(jenv);
   146     int rc;
   147     jobject jCursor = nullptr;
   148     const char* dbPath;
   149     sqlite3 *db;
   151     dbPath = jenv->GetStringUTFChars(jDb, nullptr);
   152     rc = f_sqlite3_open(dbPath, &db);
   153     jenv->ReleaseStringUTFChars(jDb, dbPath);
   154     if (rc != SQLITE_OK) {
   155         throwSqliteException(jenv,
   156             "Can't open database: %s", f_sqlite3_errmsg(db));
   157         f_sqlite3_close(db); // close db even if open failed
   158         return nullptr;
   159     }
   160     jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
   161     f_sqlite3_close(db);
   162     return jCursor;
   163 }
   165 extern "C" NS_EXPORT jobject JNICALL
   166 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCallWithDb(JNIEnv* jenv, jclass,
   167                                                             jlong jDb,
   168                                                             jstring jQuery,
   169                                                             jobjectArray jParams,
   170                                                             jlongArray jQueryRes)
   171 {
   172     JNI_Setup(jenv);
   174     jobject jCursor = nullptr;
   175     sqlite3 *db = (sqlite3*)jDb;
   176     jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
   177     return jCursor;
   178 }
   180 extern "C" NS_EXPORT jlong JNICALL
   181 Java_org_mozilla_gecko_sqlite_SQLiteBridge_openDatabase(JNIEnv* jenv, jclass,
   182                                                         jstring jDb)
   183 {
   184     JNI_Setup(jenv);
   186     int rc;
   187     const char* dbPath;
   188     sqlite3 *db;
   190     dbPath = jenv->GetStringUTFChars(jDb, nullptr);
   191     rc = f_sqlite3_open(dbPath, &db);
   192     jenv->ReleaseStringUTFChars(jDb, dbPath);
   193     if (rc != SQLITE_OK) {
   194         throwSqliteException(jenv,
   195             "Can't open database: %s", f_sqlite3_errmsg(db));
   196         f_sqlite3_close(db); // close db even if open failed
   197         return 0;
   198     }
   199     return (jlong)db;
   200 }
   202 extern "C" NS_EXPORT void JNICALL
   203 Java_org_mozilla_gecko_sqlite_SQLiteBridge_closeDatabase(JNIEnv* jenv, jclass,
   204                                                         jlong jDb)
   205 {
   206     JNI_Setup(jenv);
   208     sqlite3 *db = (sqlite3*)jDb;
   209     f_sqlite3_close(db);
   210 }
   212 static jobject
   213 sqliteInternalCall(JNIEnv* jenv,
   214                    sqlite3 *db,
   215                    jstring jQuery,
   216                    jobjectArray jParams,
   217                    jlongArray jQueryRes)
   218 {
   219     JNI_Setup(jenv);
   221     jobject jCursor = nullptr;
   222     jsize numPars = 0;
   224     const char *pzTail;
   225     sqlite3_stmt *ppStmt;
   226     int rc;
   228     const char* queryStr;
   229     queryStr = jenv->GetStringUTFChars(jQuery, nullptr);
   231     rc = f_sqlite3_prepare_v2(db, queryStr, -1, &ppStmt, &pzTail);
   232     if (rc != SQLITE_OK || ppStmt == nullptr) {
   233         throwSqliteException(jenv,
   234             "Can't prepare statement: %s", f_sqlite3_errmsg(db));
   235         return nullptr;
   236     }
   237     jenv->ReleaseStringUTFChars(jQuery, queryStr);
   239     // Check if number of parameters matches
   240     if (jParams != nullptr) {
   241         numPars = jenv->GetArrayLength(jParams);
   242     }
   243     int sqlNumPars;
   244     sqlNumPars = f_sqlite3_bind_parameter_count(ppStmt);
   245     if (numPars != sqlNumPars) {
   246         throwSqliteException(jenv,
   247             "Passed parameter count (%d) "
   248             "doesn't match SQL parameter count (%d)",
   249             numPars, sqlNumPars);
   250         return nullptr;
   251     }
   253     if (jParams != nullptr) {
   254         // Bind parameters, if any
   255         if (numPars > 0) {
   256             for (int i = 0; i < numPars; i++) {
   257                 jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i);
   258                 // IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf
   259                 // should be OK.
   260                 jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass);
   261                 if (isString != JNI_TRUE) {
   262                     throwSqliteException(jenv,
   263                         "Parameter is not of String type");
   264                     return nullptr;
   265                 }
   266                 jstring jStringParam = (jstring)jObjectParam;
   267                 const char* paramStr = jenv->GetStringUTFChars(jStringParam, nullptr);
   268                 // SQLite parameters index from 1.
   269                 rc = f_sqlite3_bind_text(ppStmt, i + 1, paramStr, -1, SQLITE_TRANSIENT);
   270                 jenv->ReleaseStringUTFChars(jStringParam, paramStr);
   271                 if (rc != SQLITE_OK) {
   272                     throwSqliteException(jenv, "Error binding query parameter");
   273                     return nullptr;
   274                 }
   275             }
   276         }
   277     }
   279     // Execute the query and step through the results
   280     rc = f_sqlite3_step(ppStmt);
   281     if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
   282         throwSqliteException(jenv,
   283             "Can't step statement: (%d) %s", rc, f_sqlite3_errmsg(db));
   284         return nullptr;
   285     }
   287     // Get the column count and names
   288     int cols;
   289     cols = f_sqlite3_column_count(ppStmt);
   291     {
   292         // Allocate a String[cols]
   293         jobjectArray jStringArray = jenv->NewObjectArray(cols,
   294                                                          stringClass,
   295                                                          nullptr);
   296         if (jStringArray == nullptr) {
   297             throwSqliteException(jenv, "Can't allocate String[]");
   298             return nullptr;
   299         }
   301         // Assign column names to the String[]
   302         for (int i = 0; i < cols; i++) {
   303             const char* colName = f_sqlite3_column_name(ppStmt, i);
   304             jstring jStr = jenv->NewStringUTF(colName);
   305             jenv->SetObjectArrayElement(jStringArray, i, jStr);
   306         }
   308         // Construct the MatrixCursor(String[]) with given column names
   309         jCursor = jenv->NewObject(cursorClass,
   310                                   jCursorConstructor,
   311                                   jStringArray);
   312         if (jCursor == nullptr) {
   313             throwSqliteException(jenv, "Can't allocate MatrixBlobCursor");
   314             return nullptr;
   315         }
   316     }
   318     // Return the id and number of changed rows in jQueryRes
   319     {
   320         jlong id = f_sqlite3_last_insert_rowid(db);
   321         jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id);
   323         jlong changed = f_sqlite3_changes(db);
   324         jenv->SetLongArrayRegion(jQueryRes, 1, 1, &changed);
   325     }
   327     // For each row, add an Object[] to the passed ArrayList,
   328     // with that containing either String or ByteArray objects
   329     // containing the columns
   330     while (rc != SQLITE_DONE) {
   331         // Process row
   332         // Construct Object[]
   333         jobjectArray jRow = jenv->NewObjectArray(cols,
   334                                                  objectClass,
   335                                                  nullptr);
   336         if (jRow == nullptr) {
   337             throwSqliteException(jenv, "Can't allocate jRow Object[]");
   338             return nullptr;
   339         }
   341         for (int i = 0; i < cols; i++) {
   342             int colType = f_sqlite3_column_type(ppStmt, i);
   343             if (colType == SQLITE_BLOB) {
   344                 // Treat as blob
   345                 const void* blob = f_sqlite3_column_blob(ppStmt, i);
   346                 int colLen = f_sqlite3_column_bytes(ppStmt, i);
   348                 // Construct ByteBuffer of correct size
   349                 jobject jByteBuffer =
   350                     jenv->CallStaticObjectMethod(byteBufferClass,
   351                                                  jByteBufferAllocateDirect,
   352                                                  colLen);
   353                 if (jByteBuffer == nullptr) {
   354                     throwSqliteException(jenv,
   355                         "Failure calling ByteBuffer.allocateDirect");
   356                     return nullptr;
   357                 }
   359                 // Get its backing array
   360                 void* bufferArray = jenv->GetDirectBufferAddress(jByteBuffer);
   361                 if (bufferArray == nullptr) {
   362                     throwSqliteException(jenv,
   363                         "Failure calling GetDirectBufferAddress");
   364                     return nullptr;
   365                 }
   366                 memcpy(bufferArray, blob, colLen);
   368                 jenv->SetObjectArrayElement(jRow, i, jByteBuffer);
   369                 jenv->DeleteLocalRef(jByteBuffer);
   370             } else if (colType == SQLITE_NULL) {
   371                 jenv->SetObjectArrayElement(jRow, i, nullptr);
   372             } else {
   373                 // Treat everything else as text
   374                 const char* txt = (const char*)f_sqlite3_column_text(ppStmt, i);
   375                 jstring jStr = jenv->NewStringUTF(txt);
   376                 jenv->SetObjectArrayElement(jRow, i, jStr);
   377                 jenv->DeleteLocalRef(jStr);
   378             }
   379         }
   381         // Append Object[] to Cursor
   382         jenv->CallVoidMethod(jCursor, jCursorAddRow, jRow);
   384         // Clean up
   385         jenv->DeleteLocalRef(jRow);
   387         // Get next row
   388         rc = f_sqlite3_step(ppStmt);
   389         // Real error?
   390         if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
   391             throwSqliteException(jenv,
   392                 "Can't re-step statement:(%d) %s", rc, f_sqlite3_errmsg(db));
   393             return nullptr;
   394         }
   395     }
   397     rc = f_sqlite3_finalize(ppStmt);
   398     if (rc != SQLITE_OK) {
   399         throwSqliteException(jenv,
   400             "Can't finalize statement: %s", f_sqlite3_errmsg(db));
   401         return nullptr;
   402     }
   404     return jCursor;
   405 }

mercurial