storage/src/mozStorageAsyncStatement.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 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <limits.h>
     8 #include <stdio.h>
    10 #include "nsError.h"
    11 #include "nsMemory.h"
    12 #include "nsProxyRelease.h"
    13 #include "nsThreadUtils.h"
    14 #include "nsIClassInfoImpl.h"
    15 #include "nsIProgrammingLanguage.h"
    16 #include "Variant.h"
    18 #include "mozIStorageError.h"
    20 #include "mozStorageBindingParams.h"
    21 #include "mozStorageConnection.h"
    22 #include "mozStorageAsyncStatementJSHelper.h"
    23 #include "mozStorageAsyncStatementParams.h"
    24 #include "mozStoragePrivateHelpers.h"
    25 #include "mozStorageStatementRow.h"
    26 #include "mozStorageStatement.h"
    27 #include "nsDOMClassInfo.h"
    29 #include "prlog.h"
    31 #ifdef PR_LOGGING
    32 extern PRLogModuleInfo *gStorageLog;
    33 #endif
    35 namespace mozilla {
    36 namespace storage {
    38 ////////////////////////////////////////////////////////////////////////////////
    39 //// nsIClassInfo
    41 NS_IMPL_CI_INTERFACE_GETTER(AsyncStatement,
    42                             mozIStorageAsyncStatement,
    43                             mozIStorageBaseStatement,
    44                             mozIStorageBindingParams,
    45                             mozilla::storage::StorageBaseStatementInternal)
    47 class AsyncStatementClassInfo : public nsIClassInfo
    48 {
    49 public:
    50   MOZ_CONSTEXPR AsyncStatementClassInfo() {}
    52   NS_DECL_ISUPPORTS_INHERITED
    54   NS_IMETHODIMP
    55   GetInterfaces(uint32_t *_count, nsIID ***_array)
    56   {
    57     return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_count, _array);
    58   }
    60   NS_IMETHODIMP
    61   GetHelperForLanguage(uint32_t aLanguage, nsISupports **_helper)
    62   {
    63     if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
    64       static AsyncStatementJSHelper sJSHelper;
    65       *_helper = &sJSHelper;
    66       return NS_OK;
    67     }
    69     *_helper = nullptr;
    70     return NS_OK;
    71   }
    73   NS_IMETHODIMP
    74   GetContractID(char **_contractID)
    75   {
    76     *_contractID = nullptr;
    77     return NS_OK;
    78   }
    80   NS_IMETHODIMP
    81   GetClassDescription(char **_desc)
    82   {
    83     *_desc = nullptr;
    84     return NS_OK;
    85   }
    87   NS_IMETHODIMP
    88   GetClassID(nsCID **_id)
    89   {
    90     *_id = nullptr;
    91     return NS_OK;
    92   }
    94   NS_IMETHODIMP
    95   GetImplementationLanguage(uint32_t *_language)
    96   {
    97     *_language = nsIProgrammingLanguage::CPLUSPLUS;
    98     return NS_OK;
    99   }
   101   NS_IMETHODIMP
   102   GetFlags(uint32_t *_flags)
   103   {
   104     *_flags = 0;
   105     return NS_OK;
   106   }
   108   NS_IMETHODIMP
   109   GetClassIDNoAlloc(nsCID *_cid)
   110   {
   111     return NS_ERROR_NOT_AVAILABLE;
   112   }
   113 };
   115 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::AddRef() { return 2; }
   116 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::Release() { return 1; }
   117 NS_IMPL_QUERY_INTERFACE(AsyncStatementClassInfo, nsIClassInfo)
   119 static AsyncStatementClassInfo sAsyncStatementClassInfo;
   121 ////////////////////////////////////////////////////////////////////////////////
   122 //// AsyncStatement
   124 AsyncStatement::AsyncStatement()
   125 : StorageBaseStatementInternal()
   126 , mFinalized(false)
   127 {
   128 }
   130 nsresult
   131 AsyncStatement::initialize(Connection *aDBConnection,
   132                            sqlite3 *aNativeConnection,
   133                            const nsACString &aSQLStatement)
   134 {
   135   MOZ_ASSERT(aDBConnection, "No database connection given!");
   136   MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
   137   MOZ_ASSERT(aNativeConnection, "No native connection given!");
   139   mDBConnection = aDBConnection;
   140   mNativeConnection = aNativeConnection;
   141   mSQLString = aSQLStatement;
   143   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Inited async statement '%s' (0x%p)",
   144                                       mSQLString.get()));
   146 #ifdef DEBUG
   147   // We want to try and test for LIKE and that consumers are using
   148   // escapeStringForLIKE instead of just trusting user input.  The idea to
   149   // check to see if they are binding a parameter after like instead of just
   150   // using a string.  We only do this in debug builds because it's expensive!
   151   const nsCaseInsensitiveCStringComparator c;
   152   nsACString::const_iterator start, end, e;
   153   aSQLStatement.BeginReading(start);
   154   aSQLStatement.EndReading(end);
   155   e = end;
   156   while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
   157     // We have a LIKE in here, so we perform our tests
   158     // FindInReadable moves the iterator, so we have to get a new one for
   159     // each test we perform.
   160     nsACString::const_iterator s1, s2, s3;
   161     s1 = s2 = s3 = start;
   163     if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
   164           ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
   165           ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
   166       // At this point, we didn't find a LIKE statement followed by ?, :,
   167       // or @, all of which are valid characters for binding a parameter.
   168       // We will warn the consumer that they may not be safely using LIKE.
   169       NS_WARNING("Unsafe use of LIKE detected!  Please ensure that you "
   170                  "are using mozIStorageAsyncStatement::escapeStringForLIKE "
   171                  "and that you are binding that result to the statement "
   172                  "to prevent SQL injection attacks.");
   173     }
   175     // resetting start and e
   176     start = e;
   177     e = end;
   178   }
   179 #endif
   181   return NS_OK;
   182 }
   184 mozIStorageBindingParams *
   185 AsyncStatement::getParams()
   186 {
   187   nsresult rv;
   189   // If we do not have an array object yet, make it.
   190   if (!mParamsArray) {
   191     nsCOMPtr<mozIStorageBindingParamsArray> array;
   192     rv = NewBindingParamsArray(getter_AddRefs(array));
   193     NS_ENSURE_SUCCESS(rv, nullptr);
   195     mParamsArray = static_cast<BindingParamsArray *>(array.get());
   196   }
   198   // If there isn't already any rows added, we'll have to add one to use.
   199   if (mParamsArray->length() == 0) {
   200     nsRefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
   201     NS_ENSURE_TRUE(params, nullptr);
   203     rv = mParamsArray->AddParams(params);
   204     NS_ENSURE_SUCCESS(rv, nullptr);
   206     // We have to unlock our params because AddParams locks them.  This is safe
   207     // because no reference to the params object was, or ever will be given out.
   208     params->unlock(nullptr);
   210     // We also want to lock our array at this point - we don't want anything to
   211     // be added to it.
   212     mParamsArray->lock();
   213   }
   215   return *mParamsArray->begin();
   216 }
   218 /**
   219  * If we are here then we know there are no pending async executions relying on
   220  * us (StatementData holds a reference to us; this also goes for our own
   221  * AsyncStatementFinalizer which proxies its release to the calling thread) and
   222  * so it is always safe to destroy our sqlite3_stmt if one exists.  We can be
   223  * destroyed on the caller thread by garbage-collection/reference counting or on
   224  * the async thread by the last execution of a statement that already lost its
   225  * main-thread refs.
   226  */
   227 AsyncStatement::~AsyncStatement()
   228 {
   229   destructorAsyncFinalize();
   230   cleanupJSHelpers();
   232   // If we are getting destroyed on the wrong thread, proxy the connection
   233   // release to the right thread.  I'm not sure why we do this.
   234   bool onCallingThread = false;
   235   (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onCallingThread);
   236   if (!onCallingThread) {
   237     // NS_ProxyRelase only magic forgets for us if mDBConnection is an
   238     // nsCOMPtr.  Which it is not; it's an nsRefPtr.
   239     Connection *forgottenConn = nullptr;
   240     mDBConnection.swap(forgottenConn);
   241     (void)::NS_ProxyRelease(forgottenConn->threadOpenedOn,
   242                             static_cast<mozIStorageConnection *>(forgottenConn));
   243   }
   244 }
   246 void
   247 AsyncStatement::cleanupJSHelpers()
   248 {
   249   // We are considered dead at this point, so any wrappers for row or params
   250   // need to lose their reference to us.
   251   if (mStatementParamsHolder) {
   252     nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
   253       do_QueryInterface(mStatementParamsHolder);
   254     nsCOMPtr<mozIStorageStatementParams> iParams =
   255       do_QueryWrappedNative(wrapper);
   256     AsyncStatementParams *params =
   257       static_cast<AsyncStatementParams *>(iParams.get());
   258     params->mStatement = nullptr;
   259     mStatementParamsHolder = nullptr;
   260   }
   261 }
   263 ////////////////////////////////////////////////////////////////////////////////
   264 //// nsISupports
   266 NS_IMPL_ADDREF(AsyncStatement)
   267 NS_IMPL_RELEASE(AsyncStatement)
   269 NS_INTERFACE_MAP_BEGIN(AsyncStatement)
   270   NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
   271   NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
   272   NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
   273   NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
   274   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
   275     foundInterface = static_cast<nsIClassInfo *>(&sAsyncStatementClassInfo);
   276   }
   277   else
   278   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
   279 NS_INTERFACE_MAP_END
   282 ////////////////////////////////////////////////////////////////////////////////
   283 //// StorageBaseStatementInternal
   285 Connection *
   286 AsyncStatement::getOwner()
   287 {
   288   return mDBConnection;
   289 }
   291 int
   292 AsyncStatement::getAsyncStatement(sqlite3_stmt **_stmt)
   293 {
   294 #ifdef DEBUG
   295   // Make sure we are never called on the connection's owning thread.
   296   bool onOpenedThread = false;
   297   (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
   298   NS_ASSERTION(!onOpenedThread,
   299                "We should only be called on the async thread!");
   300 #endif
   302   if (!mAsyncStatement) {
   303     int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
   304                                              &mAsyncStatement);
   305     if (rc != SQLITE_OK) {
   306       PR_LOG(gStorageLog, PR_LOG_ERROR,
   307              ("Sqlite statement prepare error: %d '%s'", rc,
   308               ::sqlite3_errmsg(mNativeConnection)));
   309       PR_LOG(gStorageLog, PR_LOG_ERROR,
   310              ("Statement was: '%s'", mSQLString.get()));
   311       *_stmt = nullptr;
   312       return rc;
   313     }
   314     PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
   315                                         mSQLString.get(),
   316                                         mAsyncStatement));
   317   }
   319   *_stmt = mAsyncStatement;
   320   return SQLITE_OK;
   321 }
   323 nsresult
   324 AsyncStatement::getAsynchronousStatementData(StatementData &_data)
   325 {
   326   if (mFinalized)
   327     return NS_ERROR_UNEXPECTED;
   329   // Pass null for the sqlite3_stmt; it will be requested on demand from the
   330   // async thread.
   331   _data = StatementData(nullptr, bindingParamsArray(), this);
   333   return NS_OK;
   334 }
   336 already_AddRefed<mozIStorageBindingParams>
   337 AsyncStatement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
   338 {
   339   if (mFinalized)
   340     return nullptr;
   342   nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
   343   return params.forget();
   344 }
   347 ////////////////////////////////////////////////////////////////////////////////
   348 //// mozIStorageAsyncStatement
   350 // (nothing is specific to mozIStorageAsyncStatement)
   352 ////////////////////////////////////////////////////////////////////////////////
   353 //// StorageBaseStatementInternal
   355 // proxy to StorageBaseStatementInternal using its define helper.
   356 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
   357   AsyncStatement,
   358   if (mFinalized) return NS_ERROR_UNEXPECTED;)
   360 NS_IMETHODIMP
   361 AsyncStatement::Finalize()
   362 {
   363   if (mFinalized)
   364     return NS_OK;
   366   mFinalized = true;
   368   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s'",
   369                                       mSQLString.get()));
   371   asyncFinalize();
   372   cleanupJSHelpers();
   374   return NS_OK;
   375 }
   377 NS_IMETHODIMP
   378 AsyncStatement::BindParameters(mozIStorageBindingParamsArray *aParameters)
   379 {
   380   if (mFinalized)
   381     return NS_ERROR_UNEXPECTED;
   383   BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
   384   if (array->getOwner() != this)
   385     return NS_ERROR_UNEXPECTED;
   387   if (array->length() == 0)
   388     return NS_ERROR_UNEXPECTED;
   390   mParamsArray = array;
   391   mParamsArray->lock();
   393   return NS_OK;
   394 }
   396 NS_IMETHODIMP
   397 AsyncStatement::GetState(int32_t *_state)
   398 {
   399   if (mFinalized)
   400     *_state = MOZ_STORAGE_STATEMENT_INVALID;
   401   else
   402     *_state = MOZ_STORAGE_STATEMENT_READY;
   404   return NS_OK;
   405 }
   407 ////////////////////////////////////////////////////////////////////////////////
   408 //// mozIStorageBindingParams
   410 BOILERPLATE_BIND_PROXIES(
   411   AsyncStatement, 
   412   if (mFinalized) return NS_ERROR_UNEXPECTED;
   413 )
   415 } // namespace storage
   416 } // namespace mozilla

mercurial