tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

mozStorageAsyncStatement.cpp (10990B)


      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/. */
      6 
      7 #include <limits.h>
      8 
      9 #include "nsError.h"
     10 #include "nsProxyRelease.h"
     11 #include "nsThreadUtils.h"
     12 #include "nsIClassInfoImpl.h"
     13 #include "Variant.h"
     14 
     15 #include "mozStorageBindingParams.h"
     16 #include "mozStorageConnection.h"
     17 #include "mozStorageAsyncStatementJSHelper.h"
     18 #include "mozStorageAsyncStatementParams.h"
     19 #include "mozStoragePrivateHelpers.h"
     20 #include "mozStorageStatementRow.h"
     21 #include "mozStorageStatement.h"
     22 
     23 #include "mozilla/Logging.h"
     24 
     25 extern mozilla::LazyLogModule gStorageLog;
     26 
     27 namespace mozilla {
     28 namespace storage {
     29 
     30 ////////////////////////////////////////////////////////////////////////////////
     31 //// nsIClassInfo
     32 
     33 NS_IMPL_CI_INTERFACE_GETTER(AsyncStatement, mozIStorageAsyncStatement,
     34                            mozIStorageBaseStatement, mozIStorageBindingParams,
     35                            mozilla::storage::StorageBaseStatementInternal)
     36 
     37 class AsyncStatementClassInfo : public nsIClassInfo {
     38 public:
     39  constexpr AsyncStatementClassInfo() = default;
     40 
     41  NS_DECL_ISUPPORTS_INHERITED
     42 
     43  NS_IMETHOD
     44  GetInterfaces(nsTArray<nsIID>& _array) override {
     45    return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_array);
     46  }
     47 
     48  NS_IMETHOD
     49  GetScriptableHelper(nsIXPCScriptable** _helper) override {
     50    static AsyncStatementJSHelper sJSHelper;
     51    *_helper = &sJSHelper;
     52    return NS_OK;
     53  }
     54 
     55  NS_IMETHOD
     56  GetContractID(nsACString& aContractID) override {
     57    aContractID.SetIsVoid(true);
     58    return NS_OK;
     59  }
     60 
     61  NS_IMETHOD
     62  GetClassDescription(nsACString& aDesc) override {
     63    aDesc.SetIsVoid(true);
     64    return NS_OK;
     65  }
     66 
     67  NS_IMETHOD
     68  GetClassID(nsCID** _id) override {
     69    *_id = nullptr;
     70    return NS_OK;
     71  }
     72 
     73  NS_IMETHOD
     74  GetFlags(uint32_t* _flags) override {
     75    *_flags = 0;
     76    return NS_OK;
     77  }
     78 
     79  NS_IMETHOD
     80  GetClassIDNoAlloc(nsCID* _cid) override { return NS_ERROR_NOT_AVAILABLE; }
     81 };
     82 
     83 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::AddRef() {
     84  return 2;
     85 }
     86 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::Release() {
     87  return 1;
     88 }
     89 NS_IMPL_QUERY_INTERFACE(AsyncStatementClassInfo, nsIClassInfo)
     90 
     91 static AsyncStatementClassInfo sAsyncStatementClassInfo;
     92 
     93 ////////////////////////////////////////////////////////////////////////////////
     94 //// AsyncStatement
     95 
     96 AsyncStatement::AsyncStatement() : mFinalized(false) {}
     97 
     98 nsresult AsyncStatement::initialize(Connection* aDBConnection,
     99                                    sqlite3* aNativeConnection,
    100                                    const nsACString& aSQLStatement) {
    101  MOZ_ASSERT(aDBConnection, "No database connection given!");
    102  MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(),
    103             "Database connection should be valid");
    104  MOZ_ASSERT(aNativeConnection, "No native connection given!");
    105 
    106  mDBConnection = aDBConnection;
    107  mNativeConnection = aNativeConnection;
    108  mSQLString = aSQLStatement;
    109 
    110  MOZ_LOG(gStorageLog, LogLevel::Debug,
    111          ("Inited async statement '%s' (0x%p)", mSQLString.get(), this));
    112 
    113 #ifdef DEBUG
    114  // We want to try and test for LIKE and that consumers are using
    115  // escapeStringForLIKE instead of just trusting user input.  The idea to
    116  // check to see if they are binding a parameter after like instead of just
    117  // using a string.  We only do this in debug builds because it's expensive!
    118  auto c = nsCaseInsensitiveCStringComparator;
    119  nsACString::const_iterator start, end, e;
    120  aSQLStatement.BeginReading(start);
    121  aSQLStatement.EndReading(end);
    122  e = end;
    123  while (::FindInReadable(" LIKE"_ns, start, e, c)) {
    124    // We have a LIKE in here, so we perform our tests
    125    // FindInReadable moves the iterator, so we have to get a new one for
    126    // each test we perform.
    127    nsACString::const_iterator s1, s2, s3;
    128    s1 = s2 = s3 = start;
    129 
    130    if (!(::FindInReadable(" LIKE ?"_ns, s1, end, c) ||
    131          ::FindInReadable(" LIKE :"_ns, s2, end, c) ||
    132          ::FindInReadable(" LIKE @"_ns, s3, end, c))) {
    133      // At this point, we didn't find a LIKE statement followed by ?, :,
    134      // or @, all of which are valid characters for binding a parameter.
    135      // We will warn the consumer that they may not be safely using LIKE.
    136      NS_WARNING(
    137          "Unsafe use of LIKE detected!  Please ensure that you "
    138          "are using mozIStorageAsyncStatement::escapeStringForLIKE "
    139          "and that you are binding that result to the statement "
    140          "to prevent SQL injection attacks.");
    141    }
    142 
    143    // resetting start and e
    144    start = e;
    145    e = end;
    146  }
    147 #endif
    148 
    149  return NS_OK;
    150 }
    151 
    152 mozIStorageBindingParams* AsyncStatement::getParams() {
    153  nsresult rv;
    154 
    155  // If we do not have an array object yet, make it.
    156  if (!mParamsArray) {
    157    nsCOMPtr<mozIStorageBindingParamsArray> array;
    158    rv = NewBindingParamsArray(getter_AddRefs(array));
    159    NS_ENSURE_SUCCESS(rv, nullptr);
    160 
    161    mParamsArray = static_cast<BindingParamsArray*>(array.get());
    162  }
    163 
    164  // If there isn't already any rows added, we'll have to add one to use.
    165  if (mParamsArray->length() == 0) {
    166    RefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
    167    NS_ENSURE_TRUE(params, nullptr);
    168 
    169    rv = mParamsArray->AddParams(params);
    170    NS_ENSURE_SUCCESS(rv, nullptr);
    171 
    172    // We have to unlock our params because AddParams locks them.  This is safe
    173    // because no reference to the params object was, or ever will be given out.
    174    params->unlock(nullptr);
    175 
    176    // We also want to lock our array at this point - we don't want anything to
    177    // be added to it.
    178    mParamsArray->lock();
    179  }
    180 
    181  return *mParamsArray->begin();
    182 }
    183 
    184 /**
    185 * If we are here then we know there are no pending async executions relying on
    186 * us (StatementData holds a reference to us; this also goes for our own
    187 * AsyncStatementFinalizer which proxies its release to the calling event
    188 * target) and so it is always safe to destroy our sqlite3_stmt if one exists.
    189 * We can be destroyed on the caller event target by
    190 * garbage-collection/reference counting or on the async event target by the
    191 * last execution of a statement that already lost its main-thread refs.
    192 */
    193 AsyncStatement::~AsyncStatement() {
    194  destructorAsyncFinalize();
    195 
    196  // If we are getting destroyed on the wrong event target, proxy the connection
    197  // release to the right one.
    198  if (!IsOnCurrentSerialEventTarget(mDBConnection->eventTargetOpenedOn)) {
    199    // NS_ProxyRelase only magic forgets for us if mDBConnection is an
    200    // nsCOMPtr.  Which it is not; it's a RefPtr.
    201    nsCOMPtr<nsIEventTarget> target(mDBConnection->eventTargetOpenedOn);
    202    NS_ProxyRelease("AsyncStatement::mDBConnection", target,
    203                    mDBConnection.forget());
    204  }
    205 }
    206 
    207 ////////////////////////////////////////////////////////////////////////////////
    208 //// nsISupports
    209 
    210 NS_IMPL_ADDREF(AsyncStatement)
    211 NS_IMPL_RELEASE(AsyncStatement)
    212 
    213 NS_INTERFACE_MAP_BEGIN(AsyncStatement)
    214  NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
    215  NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
    216  NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
    217  NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
    218  if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
    219    foundInterface = static_cast<nsIClassInfo*>(&sAsyncStatementClassInfo);
    220  } else
    221    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
    222 NS_INTERFACE_MAP_END
    223 
    224 ////////////////////////////////////////////////////////////////////////////////
    225 //// StorageBaseStatementInternal
    226 
    227 Connection* AsyncStatement::getOwner() { return mDBConnection; }
    228 
    229 int AsyncStatement::getAsyncStatement(sqlite3_stmt** _stmt) {
    230 #ifdef DEBUG
    231  // Make sure we are never called on the connection's owning event target.
    232  NS_ASSERTION(
    233      !IsOnCurrentSerialEventTarget(mDBConnection->eventTargetOpenedOn),
    234      "We should only be called on the async event target!");
    235 #endif
    236 
    237  if (!mAsyncStatement) {
    238    int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
    239                                             &mAsyncStatement);
    240    if (rc != SQLITE_OK) {
    241      MOZ_LOG(gStorageLog, LogLevel::Error,
    242              ("Sqlite statement prepare error: %d '%s'", rc,
    243               ::sqlite3_errmsg(mNativeConnection)));
    244      MOZ_LOG(gStorageLog, LogLevel::Error,
    245              ("Statement was: '%s'", mSQLString.get()));
    246      *_stmt = nullptr;
    247      return rc;
    248    }
    249    MOZ_LOG(gStorageLog, LogLevel::Debug,
    250            ("Initialized statement '%s' (0x%p)", mSQLString.get(),
    251             mAsyncStatement));
    252  }
    253 
    254  *_stmt = mAsyncStatement;
    255  return SQLITE_OK;
    256 }
    257 
    258 nsresult AsyncStatement::getAsynchronousStatementData(StatementData& _data) {
    259  if (mFinalized) return NS_ERROR_UNEXPECTED;
    260 
    261  // Pass null for the sqlite3_stmt; it will be requested on demand from the
    262  // async event target.
    263  _data = StatementData(nullptr, bindingParamsArray(), this);
    264 
    265  return NS_OK;
    266 }
    267 
    268 already_AddRefed<mozIStorageBindingParams> AsyncStatement::newBindingParams(
    269    mozIStorageBindingParamsArray* aOwner) {
    270  if (mFinalized) return nullptr;
    271 
    272  nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
    273  return params.forget();
    274 }
    275 
    276 ////////////////////////////////////////////////////////////////////////////////
    277 //// mozIStorageAsyncStatement
    278 
    279 // (nothing is specific to mozIStorageAsyncStatement)
    280 
    281 ////////////////////////////////////////////////////////////////////////////////
    282 //// StorageBaseStatementInternal
    283 
    284 // proxy to StorageBaseStatementInternal using its define helper.
    285 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
    286    AsyncStatement, if (mFinalized) return NS_ERROR_UNEXPECTED;)
    287 
    288 NS_IMETHODIMP
    289 AsyncStatement::Finalize() {
    290  if (mFinalized) return NS_OK;
    291 
    292  mFinalized = true;
    293 
    294  MOZ_LOG(gStorageLog, LogLevel::Debug,
    295          ("Finalizing statement '%s'", mSQLString.get()));
    296 
    297  asyncFinalize();
    298 
    299  // Release the params holder, so it can release the reference to us.
    300  mStatementParamsHolder = nullptr;
    301 
    302  return NS_OK;
    303 }
    304 
    305 NS_IMETHODIMP
    306 AsyncStatement::BindParameters(mozIStorageBindingParamsArray* aParameters) {
    307  if (mFinalized) return NS_ERROR_UNEXPECTED;
    308 
    309  BindingParamsArray* array = static_cast<BindingParamsArray*>(aParameters);
    310  if (array->getOwner() != this) return NS_ERROR_UNEXPECTED;
    311 
    312  if (array->length() == 0) return NS_ERROR_UNEXPECTED;
    313 
    314  mParamsArray = array;
    315  mParamsArray->lock();
    316 
    317  return NS_OK;
    318 }
    319 
    320 NS_IMETHODIMP
    321 AsyncStatement::GetState(int32_t* _state) {
    322  if (mFinalized)
    323    *_state = MOZ_STORAGE_STATEMENT_INVALID;
    324  else
    325    *_state = MOZ_STORAGE_STATEMENT_READY;
    326 
    327  return NS_OK;
    328 }
    329 
    330 ////////////////////////////////////////////////////////////////////////////////
    331 //// mozIStorageBindingParams
    332 
    333 BOILERPLATE_BIND_PROXIES(AsyncStatement,
    334                         if (mFinalized) return NS_ERROR_UNEXPECTED;)
    335 
    336 }  // namespace storage
    337 }  // namespace mozilla