Heap Corruption with the SDK - How to resolve?


#1

I’m writing a .dae to .skp importer, using SketchUp’s latest API. I’m also using ASSIMP to read the collada files in.

What I’m noticing is that, on the second iteration of the second inner loop (this happens every time), the call to SUFaceCreate causes a heap corruption exception to be thrown.

What I can say is that I’ve made sure all external libraries used have been compiled with under MSVC 2010 as release builds, in an attempt to make sure there aren’t any debug/release build conflicts going on with allocations and deallocations.

It might be worth noting that the code base I’m working in performs .skp file imports using the deprecated SkpReader API. I’m not sure if this could potentially cause any issues in terms of conflict.

The loop in particular in which the exception is thrown:

for (unsigned face = 0; face < mesh->mNumFaces; ++face)
{
    SUFaceRef faceRef = SU_INVALID;
    SULoopInputRef indexLoop = SU_INVALID;
                
    SU_CHECK_RESULT( SULoopInputCreate(&indexLoop) );
    
    SU_CHECK_RESULT( SULoopInputAddVertexIndex(indexLoop, mesh->mFaces[face].mIndices[0]) );
    SU_CHECK_RESULT( SULoopInputAddVertexIndex(indexLoop, mesh->mFaces[face].mIndices[1]) );
    SU_CHECK_RESULT( SULoopInputAddVertexIndex(indexLoop, mesh->mFaces[face].mIndices[2]) );
    
    const aiVector3D& a = mesh->mVertices[mesh->mFaces[face].mIndices[0]];
    const aiVector3D& b = mesh->mVertices[mesh->mFaces[face].mIndices[1]];
    const aiVector3D& c = mesh->mVertices[mesh->mFaces[face].mIndices[2]];
    
    SUPoint3D triFace[3];
    triFace[0].x = a.x;
    triFace[0].y = a.y;
    triFace[0].z = a.z;
    
    triFace[1].x = b.x;
    triFace[1].y = b.y;
    triFace[1].z = b.z;
    
    triFace[2].x = c.x;
    triFace[2].y = c.y;
    triFace[2].z = c.z;
    
    SU_CHECK_RESULT( SUFaceCreate(&faceRef, triFace, &indexLoop) ); // CRASH here, on second iteration.
    SU_CHECK_RESULT( SUEntitiesAddFaces(entitiesListRef, 1, &faceRef) );
    
    //faceBuffer.push_back(faceRef); // commented out to ensure deallocation here was not a culprit.
}

Of particular interest in an analysis performed by WinDbg is:

    775ad374 774f047b ntdll!RtlAllocateHeap+0xc6
    775ad378 69e70269 msvcr100!malloc+0x4b
    775ad37c 69e7233b msvcr100!operator new+0x1f
    775ad380 6adfd4bf slapi!SUStringSetUTF16+0x1d574f
    775ad384 6adfe75c slapi!SUStringSetUTF16+0x1d69ec
    775ad388 6adfe7c4 slapi!SUStringSetUTF16+0x1d6a54
    775ad38c 6adf1d84 slapi!SUStringSetUTF16+0x1ca014
    775ad390 6ac26311 slapi!SUVertexGetLoops+0x421
    775ad394 6ac1190e slapi!SUFaceToDrawingElement+0xfe
    775ad398 6ac12b43 slapi!SUFaceGetInnerLoops+0x483
    775ad39c 6ac1398b slapi!SUFaceCreate+0x7b
    775ad3a0 0107df53 myapp!DaeToSkp+0xff3

Full Source File (the marked faulty line is closer to the bottom):


// Will return false within the function on failure
#define SU_CHECK_RESULT(expr)            \
    do {                                \
        SUResult result = (expr);        \
        if (result != SU_ERROR_NONE)    \
        {                                \
            std::stringstream sstream;    \
            sstream << "SU IMPORT ERROR: " << ( int )result;    \
            LOG3(sstream.str().c_str());    \
            return false;                    \
        }                                \
    } while(0)                                                    

bool DaeToSkp(const char* inFilename, const char* outFilename)
{
    Assimp::Importer import;
    
    const aiScene* scene = import.ReadFile(inFilename, 0);

    if (!scene) 
        return false;

    aiCamera* daeCamera = scene->mCameras[0];

    SUModelRef model = SU_INVALID;
    
    SUEntitiesRef entitiesListRef = SU_INVALID;
    
    SU_CHECK_RESULT( SUModelCreate(&model) );
    SU_CHECK_RESULT( SUModelGetEntities(model, &entitiesListRef) );
    
    // Load .dae camera settings into .skp camera
    {
        SUCameraRef cameraRef = SU_INVALID;

        SUPoint3D position = { daeCamera->mPosition.x, daeCamera->mPosition.y, daeCamera->mPosition.z };
        SUPoint3D lookAt = { daeCamera->mLookAt.x, daeCamera->mLookAt.y, daeCamera->mLookAt.z };
        SUVector3D up = { daeCamera->mUp.x, daeCamera->mUp.y, daeCamera->mUp.z };

        SU_CHECK_RESULT( SUModelGetCamera(model, &cameraRef) );

        SU_CHECK_RESULT( SUCameraSetPerspective(cameraRef, true) );
        SU_CHECK_RESULT( SUCameraSetPerspectiveFrustumFOV(cameraRef, daeCamera->mHorizontalFOV) );

        SU_CHECK_RESULT( SUCameraSetOrientation(cameraRef, &position, &lookAt, &up) );
    }

    //SUGeometryInputRef geomRef = SU_INVALID;
    //SU_CHECK_RESULT( SUGeometryInputCreate(&geomRef) );

    for (unsigned i = 0; i < scene->mNumMeshes; ++i) 
    {
        aiMesh* mesh = scene->mMeshes[i];

        for (unsigned v = 0; v < mesh->mNumVertices; ++v)
        {
            aiVector3D& point = mesh->mVertices[v];
            
            if (point.Length() > 1.0f)
                point.Normalize();
            
            /*
            for (unsigned coord = 0; coord < 3; ++coord)
            {
                if (point[coord] < PLANE_TOLERANCE)
                    point[coord] = -PLANE_TOLERANCE;
            }
            */
        }

        //std::vector<SUFaceRef> faceBuffer; // commented out to ensure allocation here was not a culprit
        
        for (unsigned face = 0; face < mesh->mNumFaces; ++face)
        {
            SUFaceRef faceRef = SU_INVALID;
            SULoopInputRef indexLoop = SU_INVALID;
            
            SU_CHECK_RESULT( SULoopInputCreate(&indexLoop) );

            SU_CHECK_RESULT( SULoopInputAddVertexIndex(indexLoop, mesh->mFaces[face].mIndices[0]) );
            SU_CHECK_RESULT( SULoopInputAddVertexIndex(indexLoop, mesh->mFaces[face].mIndices[1]) );
            SU_CHECK_RESULT( SULoopInputAddVertexIndex(indexLoop, mesh->mFaces[face].mIndices[2]) );

            const aiVector3D& a = mesh->mVertices[mesh->mFaces[face].mIndices[0]];
            const aiVector3D& b = mesh->mVertices[mesh->mFaces[face].mIndices[1]];
            const aiVector3D& c = mesh->mVertices[mesh->mFaces[face].mIndices[2]];

            SUPoint3D triFace[3];
            triFace[0].x = a.x;
            triFace[0].y = a.y;
            triFace[0].z = a.z;

            triFace[1].x = b.x;
            triFace[1].y = b.y;
            triFace[1].z = b.z;

            triFace[2].x = c.x;
            triFace[2].y = c.y;
            triFace[2].z = c.z;

            SU_CHECK_RESULT( SUFaceCreate(&faceRef, triFace, &indexLoop) ); // CRASH here, on second iteration.
            SU_CHECK_RESULT( SUEntitiesAddFaces(entitiesListRef, 1, &faceRef) );

            //faceBuffer.push_back(faceRef); // commented out to ensure deallocation here was not a culprit.
        }

        LOG1("FACE LOOP SUCCESS");

        
    }

    //SUEntitiesFill(entitiesListRef, geomRef, true);

    LOG1("SAVING MODEL");

    SUModelSaveToFile(model, outFilename);
    SUModelRelease(&model);

    return true;
}

Full Analysis Report (from WinDbg x86):


0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

*** WARNING: Unable to verify checksum for C:\Dev\myapp\build\bin\x86\Release\slapi.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Dev\myapp\build\bin\x86\Release\slapi.dll - 
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\nvd3dum.dll - 

FAULTING_IP: 
ntdll!RtlReportCriticalFailure+46
7758f5f9 cc              int     3

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 7758f5f9 (ntdll!RtlReportCriticalFailure+0x00000046)
   ExceptionCode: 80000003 (Break instruction exception)
  ExceptionFlags: 00000000
NumberParameters: 1
   Parameter[0]: 00000000

CONTEXT:  00000000 -- (.cxr 0x0;r)
eax=00000000 ebx=00000000 ecx=c0000374 edx=00000021 esi=00000002 edi=0a10cb30
eip=7758f5f9 esp=00c6d958 ebp=00c6d9e8 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200244
ntdll!RtlReportCriticalFailure+0x46:
7758f5f9 cc              int     3

FAULTING_THREAD:  00001040

PROCESS_NAME:  myapp.exe

ERROR_CODE: (NTSTATUS) 0x80000003 - {EXCEPTION}  Breakpoint  A breakpoint has been reached.

EXCEPTION_CODE: (HRESULT) 0x80000003 (2147483651) - One or more arguments are invalid

EXCEPTION_PARAMETER1:  00000000

NTGLOBALFLAG:  0

APPLICATION_VERIFIER_FLAGS:  0

APP:  myapp.exe

ANALYSIS_VERSION: 6.3.9600.17237 (debuggers(dbg).140716-0327) x86fre

LAST_CONTROL_TRANSFER:  from 77592898 to 7758f5f9

BUGCHECK_STR:  APPLICATION_FAULT_ACTIONABLE_HEAP_CORRUPTION_heap_failure_lfh_bitmap_mismatch

PRIMARY_PROBLEM_CLASS:  ACTIONABLE_HEAP_CORRUPTION_heap_failure_lfh_bitmap_mismatch

DEFAULT_BUCKET_ID:  ACTIONABLE_HEAP_CORRUPTION_heap_failure_lfh_bitmap_mismatch

STACK_TEXT:  
775ad370 77526e97 ntdll!RtlpLowFragHeapAllocFromContext+0x3523b
775ad374 774f047b ntdll!RtlAllocateHeap+0xc6
775ad378 69e70269 msvcr100!malloc+0x4b
775ad37c 69e7233b msvcr100!operator new+0x1f
775ad380 6adfd4bf slapi!SUStringSetUTF16+0x1d574f
775ad384 6adfe75c slapi!SUStringSetUTF16+0x1d69ec
775ad388 6adfe7c4 slapi!SUStringSetUTF16+0x1d6a54
775ad38c 6adf1d84 slapi!SUStringSetUTF16+0x1ca014
775ad390 6ac26311 slapi!SUVertexGetLoops+0x421
775ad394 6ac1190e slapi!SUFaceToDrawingElement+0xfe
775ad398 6ac12b43 slapi!SUFaceGetInnerLoops+0x483
775ad39c 6ac1398b slapi!SUFaceCreate+0x7b
775ad3a0 0107df53 myapp!DaeToSkp+0xff3
775ad3a4 0105d3da myapp!BwData::Load+0xa1a
775ad3a8 01086b6e myapp!MainFrame::OpenFile+0x13e
775ad3ac 01086903 myapp!MainFrame::DoImportFile+0x1b3
775ad3b0 01086459 myapp!MainFrame::OnMenu_ImportColladaFile+0x19
775ad3b4 0125a2e0 myapp!wxAppConsoleBase::HandleEvent+0x10
775ad3b8 0125a31d myapp!wxAppConsoleBase::CallEventHandler+0x2d
775ad3bc 01250a7b myapp!wxEvtHandler::SearchDynamicEventTable+0x8b
775ad3c0 0125176f myapp!wxEvtHandler::ProcessEvent+0xef
775ad3c4 0125088b myapp!wxEvtHandler::SafelyProcessEvent+0x3b
775ad3c8 0132e9ef myapp!wxMenuBase::SendEvent+0x7f
775ad3cc 013291f9 myapp!wxFrameBase::ProcessCommand+0x79
775ad3d0 0131cef5 myapp!wxFrame::HandleCommand+0x45
775ad3d4 0131e02a myapp!wxFrame::MSWWindowProc+0x11a
775ad3d8 012f25af myapp!wxWndProc+0x8f
775ad3dc 750b7834 user32!_InternalCallWinProc+0x23
775ad3e0 750b7a9a user32!UserCallWinProcCheckWow+0x184
775ad3e4 750b988e user32!DispatchMessageWorker+0x208
775ad3e8 750d2626 user32!IsDialogMessageW+0x107
775ad3ec 012ef741 myapp!wxWindow::MSWProcessMessage+0x121


FOLLOWUP_IP: 
MSVCR100!operator new+1f
69e7233b 59              pop     ecx

SYMBOL_STACK_INDEX:  3

SYMBOL_NAME:  msvcr100!operator new+1f

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: MSVCR100

IMAGE_NAME:  MSVCR100.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  4df2be1e

STACK_COMMAND:  dps 775ad370 ; kb

FAILURE_BUCKET_ID:  ACTIONABLE_HEAP_CORRUPTION_heap_failure_lfh_bitmap_mismatch_80000003_MSVCR100.dll!operator_new

BUCKET_ID:  APPLICATION_FAULT_ACTIONABLE_HEAP_CORRUPTION_heap_failure_lfh_bitmap_mismatch_msvcr100!operator_new+1f

ANALYSIS_SOURCE:  UM

FAILURE_ID_HASH_STRING:  um:actionable_heap_corruption_heap_failure_lfh_bitmap_mismatch_80000003_msvcr100.dll!operator_new

FAILURE_ID_HASH:  {079776fe-d067-8afe-4b4f-bae4c6d17fa9}

Followup: MachineOwner
---------

Conclusion

It looks like allocating an SUFace for the second time is what causes this, given that the error has been thrown on the second iteration literally every-time this has happened. Is there any potential side effects with using the API I should be aware of, or any idea on how to resolve this?

I really appreciate any guidance one could offer in regards to this.


#2

@bugra, @Paul: any ideas?


@hgschutte1: our of curiosity, not related to the issue; why are you doing do {} while(0) in that macro?


@jody, @sam: Any way to control code blocks? I’ve never likes boxed up code like that. Having to interact with the scroll bar of the code block feels so awkward. I find scanning code much easier when it’s all flat. I can just use the scroll wheel or mouse cursor to navigate the page as a whole. Internal scrollbars is something I’ve always violates Fitts Law.


#3

@tt_su

Timely response - awesome! I’ll be checking this in the morning, as I’m about to head to sleep xD.

The do {} while(0) is literally nothing more than habit: I tend to add that in all of my macro “functions” to eliminate scoping issues and keep things contained. The only exception to this rule is with one liners and such. It’s not an uncommon practice - actually highly recommended :smile:

Thanks again for the help. Take care!


Scoping macros in C/C++
#4

One problem I see here is that you are passing a 3-point array to SUFaceCreate but the outer loop indices come from the main mesh. Your SULoopInputRef should basically just contain the vertex indices 0,1,2 in some order. Because the array contains just 3 points. I think they way you have it, slapi is reaching random memory for points and chaos ensues from there.

But the larger problem is that SUFaceCreate is only suitable for creating single faces and not suitable for what you are doing here (i.e. creating a mesh). The way to do this is to populate a SUGeometryInputRef and pass it to SUEntitiesFill. I see these commented-out in your code. Did you have trouble?


#5

you can easily add a custom css style that removes the max-height.

something like

pre code {max-height: auto;) 

#6

What’s interesting about the SUGeometryInputRef is I actually had suspected that would be better suited for the task of creating a mesh. Given that I wasn’t very familiar with the API, though, I felt it would be best to consult the experts first before I chose another approach. None-the-less, that definitely did the trick; thanks.

I had one more question, though: on SUFaceCreate one of the errors (SU_ERROR_GENERIC) is thrown if the points for the face are outside of what is said to be a “plane tolerance” of 1.0e-3. What I understand is that 1.0e-3 is equivalent to 0.001, but I fail to understand what plane tolerance is referring to, exactly. Can you clarify?


#7

Sure that’s not very clear in the docs.
We “fit” a plane to the given points, assuming they are coplanar. If the distance between any of the points to the plane ends up being greater than 0.001 inch, this fails the coplanarity check and we return SU_ERROR_GENERIC.


#8

0.001 is also the 3d point tolerance in SketchUp. If create faces with vertices closer than that you might find the face not being created.


#9

Fork? To my knowledge the only option you get is to Reply as a new Topic but I could be wrong. I don’t see any settings that let me fork it elsewhere in Admin settings.