Issue 97539 - How to crash OOo using Basic :)
Summary: How to crash OOo using Basic :)
Status: CONFIRMED
Alias: None
Product: App Dev
Classification: Unclassified
Component: scripting (show other issues)
Version: 3.3.0 or older (OOo)
Hardware: All All
: P2 Trivial
Target Milestone: ---
Assignee: AOO issues mailing list
QA Contact:
URL:
Keywords: crash
Depends on:
Blocks:
 
Reported: 2008-12-23 17:30 UTC by erpel
Modified: 2013-02-24 21:01 UTC (History)
4 users (show)

See Also:
Issue Type: DEFECT
Latest Confirmation in: ---
Developer Difficulty: ---


Attachments
Calc document containing the macro that will cause AOO to crash. (7.89 KB, application/vnd.oasis.opendocument.spreadsheet)
2012-12-17 18:44 UTC, andrew
no flags Details

Note You need to log in before you can comment on or make changes to this issue.
Description erpel 2008-12-23 17:30:57 UTC
Try this little basic macro - but save your documents first: :-)

crashMe() instantly kills my OOo 2.4.1 (Linux) and OOo 3.0.0 (WinXP).

--- snip ---
REM  *****  BASIC  *****
Option explicit

' This crashes OOo
sub crashMe()
	dim oMerged()
	oMerged = getMergedArea(0, 0)
	msgbox (oMerged(2) - 0)
end sub

' If you save oMerged(2) in a variable first, it works
sub working1()
	dim oMerged()
	dim value%
	oMerged = getMergedArea(0, 0)
	value = oMerged(2)
	msgbox (value - 0)
end sub

' If you remove the "substract 0" part, it works
sub working2()
	dim oMerged()
	oMerged = getMergedArea(0, 0)
	msgbox (oMerged(2))
end sub

' The following two functions are literally the same
' Only the name is different (getMergedArea and getMergedAreaNoCrash)
Function getMergedArea (iCol as integer, iRow as integer)
	dim oCursor, oRangeAddress, oCell, oSheet
	oSheet = ThisComponent.CurrentController.getActiveSheet
	oCell = oSheet.getCellByPosition(iCol, iRow)
    oCursor = oSheet.createCursorByRange(oCell)
    oCursor.collapseToMergedArea()
    oRangeAddress = oCursor.getRangeAddress()
    ' Return {startCol, startRow, endCol, endRow} as array
    getMergedArea = array (oRangeAddress.StartColumn, _
    						oRangeAddress.StartRow, _
			    			oRangeAddress.EndColumn, _
			    			oRangeAddress.EndRow)
End Function 
--- snip ---
Comment 1 erpel 2008-12-23 17:33:52 UTC
Please ignore these two comment lines in my previous posting, they are left from
an earlier test: :-)

--- ignore me ---
' The following two functions are literally the same
' Only the name is different (getMergedArea and getMergedAreaNoCrash)
--- ignore me ---
Comment 2 kpalagin 2008-12-26 13:28:06 UTC
Confirming with m37 on WinXP - as described. You need to run macro from Calc.
Priority, crash.
Comment 3 damjan 2012-10-31 04:31:48 UTC
Doesn't crash in AOO 3.4. Fixed?
Comment 4 andrew 2012-12-17 17:52:50 UTC
Sadly, this is not yet fixed. Although the macro as shown does indeed cause a crash, something strange is occurring, because a slight change still causes this to crash. For example, change the first crashMe subroutine to a function and access oMerged(2) twice, and things still crash AOO. I ran this test on 64-bit Fedora Linux and it crashed. 

REM  *****  BASIC  *****
Option explicit

' This crashes OOo
Function crashMe() As Variant 
	dim oMerged()
	oMerged = getMergedArea(0, 0)
	msgbox (oMerged(2) - 0)
	crashMe() = oMerged(2) - 0
end Function

' If you save oMerged(2) in a variable first, it works
sub working1()
	dim oMerged()
	dim value%
	oMerged = getMergedArea(0, 0)
	value = oMerged(2)
	msgbox (value - 0)
end sub

' If you remove the "substract 0" part, it works
sub working2()
	dim oMerged()
	oMerged = getMergedArea(0, 0)
	msgbox (oMerged(2))
end sub

Function getMergedArea (iCol as integer, iRow as integer)
	dim oCursor, oRangeAddress, oCell, oSheet
	oSheet = ThisComponent.CurrentController.getActiveSheet
	oCell = oSheet.getCellByPosition(iCol, iRow)
    oCursor = oSheet.createCursorByRange(oCell)
    oCursor.collapseToMergedArea()
    oRangeAddress = oCursor.getRangeAddress()
    ' Return {startCol, startRow, endCol, endRow} as array
    getMergedArea = array (oRangeAddress.StartColumn, _
    						oRangeAddress.StartRow, _
			    			oRangeAddress.EndColumn, _
			    			oRangeAddress.EndRow)
End Function
Comment 5 andrew 2012-12-17 18:42:16 UTC
Call me a little crazy, but I have an even shorter macro. I expect that the problem is related to the use of the variant data type and how assignment works with arrays. For example, this little bit also crashes, and it is much shorter:

REM  *****  BASIC  *****
Option explicit

' This crashes OOo
Function crashMe() As Variant 
	Dim oMerged()
	oMerged = getMergedArea()
	msgbox (oMerged(2) - 0)
	crashMe() = oMerged(2) - 0
End Function

Function getMergedArea()
	Dim oRangeAddress As New com.sun.star.table.CellRangeAddress
    getMergedArea = array (oRangeAddress.StartColumn, _
    			oRangeAddress.StartRow, _
			oRangeAddress.EndColumn, _
			oRangeAddress.EndRow)
End Function 

So, first we create a CellRangeAddress and then pull four long values out. We use Array to create an array. What is returned is a Variant array with four long values. This is returned as a variant type (because getMergedArea returns a Variant). So, ultimately, a variant references an array, that contains an array of Longs. If I change getMergedArea() to use hard coded long values, the problem does not occur:

Dim l As Long : l = 0
getMergedArea = Array(l, l, l, l)

Clearly something crazy is occurring. I will attach a Calc document containing a macro that fails.
Comment 6 andrew 2012-12-17 18:44:53 UTC
Created attachment 80032 [details]
Calc document containing the macro that will cause AOO to crash.

You can likely run this by entering the formula =crashMe() into a cell. Loading the sheet will not crash OOo, only running the macro.
Comment 7 andrew 2013-01-21 02:25:01 UTC
So this is what happens (no comment on fixing). 

There are two variants of the bug shown. In the first variant, we have:

oRangeAddress = oCursor.getRangeAddress()

In this case, getRangeAddress() returns a COPY (because struts are always -- or at least almost always -- returned as a copy rather than a reference). This is copied into oRangeAddress. 

In the smaller version, oRangeAddress is declared to be of type  com.sun.star.table.CellRangeAddress.

In both cases, this variable lives as a UNO struct inside a function. 

Now, the individual properties are stored in array, so I looked in methods1.cxx at RTLFUNC(Array) and I see code used to build the array:

    for( sal_uInt16 i = 0 ; i < nArraySize ; i++ )
    {
        SbxVariable* pVar = rPar.Get(i+1);
        SbxVariable* pNew = new SbxVariable( *pVar );
        pNew->SetFlag( SBX_WRITE );
        short index = static_cast< short >(i);
        if ( bIncIndex )
        {
            ++index;
        }
        pArray->Put( pNew, &index );
    }

Although you can look in sbxarray.cxx for the method void SbxArray::Put( SbxVariable* pVar, sal_uInt16 nIdx ) to see how things are stored (end of this comment), the damage is probably already done with this:

        SbxVariable* pVar = rPar.Get(i+1);
        SbxVariable* pNew = new SbxVariable( *pVar );

We have properties (SbUnoProperty) that are lazy loaded when they are actually accessed, and we did not actually access them; we only stored a reference to them. By the time we do get around to referencing the values, which causes the values to be populated, the parent object has destructed and no longer exists (it is long out of scope).

See this code that stores stuff.

void SbxArray::Put( SbxVariable* pVar, sal_uInt16 nIdx )
{
    if( !CanWrite() )
        SetError( SbxERR_PROP_READONLY );
    else
    {
        if( pVar )
            if( eType != SbxVARIANT )
                // Convert no objects
                if( eType != SbxOBJECT || pVar->GetClass() != SbxCLASS_OBJECT )
                    pVar->Convert( eType );
        SbxVariableRef& rRef = GetRef( nIdx );
        if( (SbxVariable*) rRef != pVar )
        {
            rRef = pVar;
            SetFlag( SBX_MODIFIED );
        }
    }
}

So, how can you avoid the problem in your macro? You can store a reference to the struct in the array (feels silly, but it works, I checked), or you can first create your array and access the values in that array to cause them to load, and then return that array. 

Probably not the only solutions (like extract the values so that they are stored as integers, can probably accomplish this by casting the values as they are stored in the array). 

I expect that it might be a large behavior change (and performance hit) to cause all properties stored into an array to be accessed on storage, and perhaps convert them. I have not tried some of the obvious possible unexpected side effects yet (like store the value in the array, change the actual property value and then see if it changes in the array itself).