[NBS] Add validation of instance to prevent crash 79/246979/5
authorPiotr Kosko <p.kosko@samsung.com>
Thu, 5 Nov 2020 11:20:21 +0000 (12:20 +0100)
committerPiotr Kosko <p.kosko@samsung.com>
Fri, 6 Nov 2020 10:36:26 +0000 (11:36 +0100)
[Bug] In case of destroying instance before the operation:
* requestRouteToHost
* releaseRouteToHost
is finished, invalidated instance was used in code.

[Reproduction] In my case it was execution of below code max 10 times:
function crashApplicationWithNBS() {
    try {
        tizen.networkbearerselection.releaseRouteToHost(
            "CELLULAR", "www.onet.pl", addRouteAndReload, addRouteAndReload);
    } catch (e) {
        addRouteAndReload();
    }
}

function addRouteAndReload() {
    var statuscb =
    {
        onsuccess: function () {
            console.log("onsuccess func is called");
        },
        ondisconnected: function () {
            console.log("ondisconnected func is called");
        }
    };
    function ecb(e) {
        console.log("error callback is called: " + e.message + ": " + e.code);
    }
    setTimeout(() => {
        try {
            tizen.networkbearerselection.requestRouteToHost(
                "CELLULAR", "www.onet.pl", statuscb, ecb)
        } catch (e) { console.log(e) }
    }, 500);
    setTimeout(() => { location.href = "index.html"; }, 490);
}

[Verification]
* Crash does not occur. Crash situation is only signalled
with log:
DoAndPostMessage(264) > Trying to post message to non-existing instance: [0xb8cb1730], ignoring
* Success and error callbacks are properly triggered

Change-Id: I6adac13a8a99dbc114102bbd2ffced6bf012cfce

src/common/extension.cc
src/common/extension.h
src/networkbearerselection/networkbearerselection_instance.cc

index 713e03e..6a0a2c3 100644 (file)
@@ -238,6 +238,20 @@ Instance::~Instance() {
   Assert(xw_instance_ == 0);
 }
 
+void Instance::DoAndPostMessage(Instance* that, const std::function<void()>& work,
+                                const picojson::value& json) {
+  ScopeLogger();
+  if (nullptr != that) {
+    std::lock_guard<std::mutex> lock{instances_mutex_};
+    if (all_instances_.end() != all_instances_.find(that)) {
+      work();
+      that->PostMessage(json.serialize().c_str());
+      return;
+    }
+  }
+  LoggerE("Trying to post message to non-existing instance: [%p], ignoring", that);
+}
+
 void Instance::PostMessage(Instance* that, const char* msg) {
   ScopeLogger();
   if (nullptr != that) {
index f9e0e68..f35c822 100644 (file)
@@ -93,6 +93,7 @@ class Instance {
   Instance();
   virtual ~Instance();
 
+  static void DoAndPostMessage(Instance* that, const std::function<void()>& work, const picojson::value& json);
   static void PostMessage(Instance* that, const char* msg);
   static void PostMessage(Instance* that, const picojson::value& json);
   void PostMessage(const char* msg);
index f4175fe..d7e1dd0 100644 (file)
@@ -86,21 +86,22 @@ void NetworkBearerSelectionInstance::NetworkBearerSelectionRequestRouteToHost(
   auto request = [=]() -> void {
     auto response = [this, domain_name, id](const common::PlatformResult result) -> void {
       ScopeLogger();
-
       picojson::value value{picojson::object{}};
       picojson::object& obj = value.get<picojson::object>();
 
       obj["id"] = picojson::value(static_cast<double>(id));
       obj["listenerId"] = picojson::value(kNBSCallback + std::to_string(id));
 
-      if (result.IsSuccess()) {
-        ReportSuccess(obj);
-        this->addDomainListener(domain_name, id);
-      } else {
-        LogAndReportError(result, &obj);
-      }
-
-      Instance::PostMessage(this, value.serialize().c_str());
+      Instance::DoAndPostMessage(this,
+                                 [this, &domain_name, &id, &result, &obj]() -> void {
+                                   if (result.IsSuccess()) {
+                                     ReportSuccess(obj);
+                                     this->addDomainListener(domain_name, id);
+                                   } else {
+                                     LogAndReportError(result, &obj);
+                                   }
+                                 },
+                                 value);
     };
 
     NetworkBearerSelectionManager::GetInstance()->requestRouteToHost(domain_name, response);
@@ -138,20 +139,21 @@ void NetworkBearerSelectionInstance::NetworkBearerSelectionReleaseRouteToHost(
   auto release = [=]() -> void {
     auto response = [this, domain_name, id](const common::PlatformResult result) -> void {
       ScopeLogger();
-
       picojson::value value{picojson::object{}};
       picojson::object& obj = value.get<picojson::object>();
 
       obj["callbackId"] = picojson::value(id);
 
-      if (result.IsSuccess()) {
-        ReportSuccess(obj);
-        this->removeDomainListener(domain_name);
-      } else {
-        LogAndReportError(result, &obj);
-      }
-
-      Instance::PostMessage(this, value.serialize().c_str());
+      Instance::DoAndPostMessage(this,
+                                 [this, &domain_name, &result, &obj]() -> void {
+                                   if (result.IsSuccess()) {
+                                     ReportSuccess(obj);
+                                     this->removeDomainListener(domain_name);
+                                   } else {
+                                     LogAndReportError(result, &obj);
+                                   }
+                                 },
+                                 value);
     };
 
     NetworkBearerSelectionManager::GetInstance()->releaseRouteToHost(domain_name, response);