User/custom soap headers crash application when ServiceClient::Invoke fails

43 views
Skip to first unread message

Mike

unread,
Sep 4, 2014, 7:04:12 AM9/4/14
to wsf-...@googlegroups.com
User/custom soap headers crash application when ServiceClient::Invoke fails (when server does not respond).


void staff::ServiceClient::AddHeader(DataObject& rdoHeader) 

adds headers this way:

void ServiceClient::AddHeader(DataObject& rdoHeader)
{
    STAFF_ASSERT(m_pSvcClient, "Service client is not initialized");
    axis2_svc_client_add_header(m_pSvcClient, m_pEnv, rdoHeader);
}  

Observations:
AddHeader takes axiom node without doing any copy (does not clone) what implies that  rdoHeader MUST exist when DataObject ServiceClient::Invoke(DataObject& rdoPayload)  is called. 

Implifications:
In DataObject ServiceClient::Invoke(DataObject& rdoPayload)

Custom headers are cleared to avoid double free.

    if (m_pOptions) // unset http headers to avoid double free
       axis2_options_set_http_headers(*m_pOptions, m_pEnv, NULL);

It can be accepted as long as there is no http transmission error type (endpoint does not respond).

When it happens we have:
- pAxiomResponseNode is null
- axis2c implementation frees also axiom nodes of headers passed via ServiceClient::AddHeader (It is done during axis2_svc_client_send_receive call)
so in this case, rdoHeader object points to axiom node (memory block) that has been freed by axis2c code and application crashes.

I think that I should be done this way:

// Safe copy
  void ServiceClient::AddHeader(DataObject& rdoHeader)
  {
    STAFF_ASSERT(m_pSvcClient, "Service client is not initialized");
    staff::DataObject clone = rdoHeader.Clone();
    axis2_svc_client_add_header(m_pSvcClient, m_pEnv, clone);
  } 

and in this case 
DataObject ServiceClient::Invoke(DataObject& rdoPayload)

should be without

    if (m_pOptions) // unset http headers to avoid double free
       axis2_options_set_http_headers(*m_pOptions, m_pEnv, NULL);

Note that this way passed headers via ServiceClient::AddHeader
will be freed on service end or on next client call/request (according to axis2c code).


 

Dmitry Utkin

unread,
Sep 4, 2014, 7:16:57 AM9/4/14
to wsf-...@googlegroups.com
Hi Mike,

Could you please provide the example to reproduce the issue?

Please attach it to your message.

четверг, 4 сентября 2014 г., 15:04:12 UTC+4 пользователь Mike написал:

Mike

unread,
Sep 4, 2014, 8:13:33 AM9/4/14
to wsf-...@googlegroups.com
Test code:

std::string address("http://10.0.0.222/notexisting");
std::string sessionId;
std::string instanceId;
service.Init(address, sessionId, instanceId); 
service.test();

// ---------------------------------------------------------------------------------------

#include <staff/common/IService.h>

class MyService : public staff::IService
{
public:
    virtual bool test() = 0;
};

// -----------------------------------------------------------------------

// This file generated by staff_codegen
// For more information please visit: http://code.google.com/p/staff/
// DO NOT EDIT

#ifndef _myserviceProxy_h_
#define _myserviceProxy_h_
#include <staff/client/ServiceClient.h>
#include "myservice.h"


//! Proxy for component service MyService
class MyServiceProxy: public MyService
{
public:
  MyServiceProxy();
  virtual ~MyServiceProxy();
  void Init(const std::string& sServiceUri, const std::string& sSessionId, const std::string& sInstanceId);
  void Deinit();
  virtual staff::ServiceClient* GetClient();
  bool test();
private:
  mutable staff::ServiceClient m_tClient;
  std::string m_sServiceUri;
};


namespace staff
{
  class DataObject;
}

#endif

// ---------------------------------------------------------------------------

// This file generated by staff_codegen
// For more information please visit: http://code.google.com/p/staff/
// DO NOT EDIT
#include "stdafx.h"
#include <memory>
#include <staff/utils/SharedPtr.h>
#include <staff/utils/Log.h>
#include <staff/utils/fromstring.h>
#include <staff/utils/tostring.h>
#include <staff/utils/HexBinary.h>
#include <staff/utils/Base64Binary.h>
#include <staff/common/DataObject.h>
#include <staff/common/Attribute.h>
#include <staff/common/Exception.h>
#include <staff/common/Operation.h>
#include <staff/client/ServiceFactory.h>
#include <staff/client/IProxyAllocator.h>
#include <staff/client/ICallback.h>
#include <staff/client/Options.h>
#include "myserviceProxy.h"

namespace staff
{

///////////////////////////////////////////////////////////////////////////////////////////////////////
// typedef deserializators
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
// classes implementation



// proxy allocator
class MyServiceProxyAllocator: public staff::IProxyAllocator
{
public:
  MyServiceProxyAllocator()
  {
    try
    {
      staff::ServiceFactory::Inst().RegisterProxyAllocator(typeid(MyService).name(), *this);
    }
    STAFF_CATCH_ALL_DESCR("Failed to register proxy allocator MyService");
  }

  virtual staff::IService* AllocateProxy(const std::string& sServiceUri,
                                         const std::string& sSessionId,
                                         const std::string& sInstanceId)
  {
    std::auto_ptr<MyServiceProxy> tpProxy(new MyServiceProxy);
    tpProxy->Init(sServiceUri, sSessionId, sInstanceId);
    return tpProxy.release();
  }

  virtual staff::IService* AllocateProxy(const std::string& sBaseUri,
                                         const std::string& sServiceName,
                                         const std::string& sSessionId,
                                         const std::string& sInstanceId)
  {
    std::auto_ptr<MyServiceProxy> tpProxy(new MyServiceProxy);
    tpProxy->Init(sBaseUri + (sServiceName.empty() ? "MyService" : sServiceName),
                  sSessionId, sInstanceId);
    return tpProxy.release();
  }
};

MyServiceProxyAllocator tMyServiceProxyAllocatorInitializer;

// asynch proxies


// service proxy
MyServiceProxy::MyServiceProxy()
{
}

MyServiceProxy::~MyServiceProxy()
{
  try
  {
    Deinit();
  }
  STAFF_CATCH_ALL;
}

void MyServiceProxy::Init(const std::string& sServiceUri, const std::string& sSessionId, const std::string& sInstanceId)
{
  staff::IService::Init("MyService", sSessionId, sInstanceId);
  m_sServiceUri = !sServiceUri.empty() ? sServiceUri : "http://localhost:9090/axis2/services/MyService";
  m_tClient.Init(m_sServiceUri);
  staff::Options& rOptions = m_tClient.GetOptions();

  rOptions.SetSessionId(sSessionId);

  if (!sInstanceId.empty())
  {
    staff::Operation tOperation("CreateInstance");
    tOperation.Request().CreateChild("sInstanceId").SetText(sInstanceId);
    tOperation.SetResponse(m_tClient.Invoke(tOperation.Request()));
    if (m_tClient.GetLastResponseHasFault())
    {
      STAFF_ASSERT_SOAPFAULT(!tOperation.IsFault(), tOperation.GetFaultCode(),
                             tOperation.GetFaultString(), tOperation.GetFaultDetail()); // soap fault
      STAFF_THROW(::staff::RemoteException, "Failed to invoke service: " +
                  tOperation.GetResponse().ToString()); // other fault
    }
    rOptions.SetInstanceId(sInstanceId);
  }
}

void MyServiceProxy::Deinit()
{
  if (!staff::IService::GetInstanceId().empty())
  {
    staff::Operation tOperation("FreeInstance");
    tOperation.Request().CreateChild("sInstanceId").SetText(staff::IService::GetInstanceId());
    tOperation.SetResponse(m_tClient.Invoke(tOperation.Request()));
    if (m_tClient.GetLastResponseHasFault())
    {
      STAFF_ASSERT_SOAPFAULT(!tOperation.IsFault(), tOperation.GetFaultCode(),
                             tOperation.GetFaultString(), tOperation.GetFaultDetail()); // soap fault
      STAFF_THROW(::staff::RemoteException, "Failed to invoke service: " +
                  tOperation.GetResponse().ToString()); // other fault
    }
  }
}

staff::ServiceClient* MyServiceProxy::GetClient()
{
  return &m_tClient;
}


bool MyServiceProxy::test()
{
  staff::Operation tOperation("test");
  staff::DataObject& rdoRequest = tOperation.Request();
  rdoRequest.SetNamespaceUriGenPrefix("http://tempui.org/MyService");
  // synchronous call
  
  staff::DataObject myHeader("MyHeader");
  myHeader.SetText("My data.");

  m_tClient.AddHeader(myHeader);

  tOperation.SetResponse(m_tClient.Invoke(rdoRequest));
  if (m_tClient.GetLastResponseHasFault())
  {
    STAFF_ASSERT_SOAPFAULT(!tOperation.IsFault(), tOperation.GetFaultCode(),
                           tOperation.GetFaultString(), tOperation.GetFaultDetail()); // soap fault
    STAFF_THROW(::staff::RemoteException, "Failed to invoke service: " + tOperation.GetResponse().ToString()); // other fault
  }

  const staff::DataObject& rdoResult = tOperation.ResultOpt();
  bool tReturn = 0;
  STAFF_ASSERT(rdoResult.GetValue(tReturn), "Invalid value for element tReturn");
  return tReturn;
}

// ----------------------------------------------------------------------------------

Dmitry Utkin

unread,
Sep 4, 2014, 3:56:55 PM9/4/14
to wsf-...@googlegroups.com
Hi Mike,

I tried to to reproduce the issue you described, but was unable to do that (under Linux).
I used interface and modified version of proxy you provided.

Valgrind does not show any memory errors too.

Which version of Axis2/C you are using? Which is the version of Visual Studio?


четверг, 4 сентября 2014 г., 16:13:33 UTC+4 пользователь Mike написал:

Mike

unread,
Sep 18, 2014, 1:01:52 PM9/18/14
to wsf-...@googlegroups.com
Hi,

Sorry for late response.

I have both staff and axis compiled under windows 7 with visual studio 2010
where 
staff is
    LIBRARY_VERSION=1.99.2
    PACKAGE_VERSION=2.0.0-b1
unofficial axis
   in changelog it says that it is Axis2/C (1.6.0)

Mike
Reply all
Reply to author
Forward
0 new messages