olha
olha

Reputation: 2272

How XCTest is loaded to process memory along with iOS App (Heap Corruption issue)?

I have a quite strange crash in my XCTest suite.
The test target is Application tests with Allow testing Host Application API checkbox enabled.
Build for active arch only is YES.

Now I have only ONE test in my test suite, and it still crashes.
Xcode says that the cause of crash is "Heap corruption". I've seen the possible reasons of heap corruption, it seems none applies in my case.

When testing with Address Sanitiser and Behaviour Sanitiser, I get smth like this:

enter image description here

The test looks like this:

- (void)testHeapCorruption {
    MyCppClassDictionary dict;
    int file_index1 = 41;

    // MyCppClassPtr is custom_ptr<MyCppClass> 
    MyCppClassPtr data_to1 = new MyCppClass(0, 0, 41);

    dict.pushDataItem(data_to1);
        
    MyCppClassPtr data_from1 = dict.find(file_index1);
}

and MyCppClass like this:


typedef custom_ptr<MyCppClass> MyCppClassPtr;

MyCppClass::MyCppClass(const void* data, int len, int file_index) : m_file_index(file_index)
{
    if(len > 0)
    {
        m_data = malloc(len);
        memcpy(m_data, data, len);
    }
    
    this->m_len = len;
    m_len_initial = len;
}

MyCppClass::~MyCppClass()
{
    if (m_data) free(m_data);
}

void MyCppClassDictionary::pushDataItem(MyCppClassPtr item)
{
    assert(item);
    assert( item->m_file_index != -1 );
    m_undo_map[item->m_file_index] = item;
}

MyCppClassPtr MyCppClassDictionary::find(int file_index)
{
    auto it = m_undo_map.find(file_index);
    if ( it != m_undo_map.end() )
        return it->second;
    
    return 0;
}

A custom_ptr is a template class which works like std::shared_ptr: it stores the number of references to an object, and calls a destructor only when there're no references.

The crash happens only when the object code of MyCppClass and MyCppClassDictionaryis located in Application target. When I duplicate the classes (MyCppClass2, MyCppClassDictionary2) and link them only to Test target, everything is OK.


Now the low-level part

Inside ApplicationTarget package there's ./ApplicationTarget binary and PlugIns/TestTarget.xctest/./TestTarget binary.

When see the symbols of TestTarget binary, MyCppClass functions are "extern function" in "Segment External Symbols" segment.

I assume that "Heap" in "Heap corruption" looks similar to this:

enter image description here

I'd like to know:

  1. how exactly TestTarget binary is loaded into process memory, relatively to main target.
  2. Is it possible that they have different heaps: one for main target, and one for test target?
  3. Is it possible to debug the scenario when data is allocated on one heap, and deallocated on the other one? Inside/before the malloc, it is possible to print heap's start address?
  4. What kind of logging can I try? Because Address Sanitiser tells the problem post-factum.

I'd like to understand the root of the problem, but feel that my memory management/assembly skills are not enough.
Any comments/links are highly appreciated.

Upvotes: 0

Views: 167

Answers (1)

olha
olha

Reputation: 2272

The reason of the problem was that the object code for Test Target and Main Target was slightly different.

A custom_ptr looked like this:

//file custom_ptr.h

template<class T> class custom_ptr
{
#ifdef DEBUG
    T* some_debug_pointer;
#endif  
};

And I passed DEBUG=1 in Preprocessor Definitions only for Main Target. So in Main target custom_ptr had a member some_debug_pointer, thus size of custom_ptr object was bigger than in Test target, where custom_ptr didn't have some_debug_pointer member.

Upvotes: 0

Related Questions