Показать сообщение отдельно
Старый 05.08.2010, 15:49   #1  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5803 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Thumbs down Как завалить AOS AX 2009
Версия ядра клиента и сервера AX 2009 - 5.0.1500.2985, т.е. RU5.
Ниже приводится тестовый пример, который при вызове с клиента гарантированно валит AOS на test2():
X++:
CustTable test1(CustTable _custTableOther = null, CustGroupId _custGroupId = '')
{
    CustTable   custTable;
    ;
    if (_custTableOther)
    {
        select firstonly custTable where custTable.CustGroup == _custTableOther.CustGroup;
    }
    else
    {
        select firstonly custTable where custTable.CustGroup == _custGroupId;
    }
    return custTable;
}
CustTable test2(CustTable _custTableOther = null, CustGroupId _custGroupId = '')
{
    CustTable   custTable;
    ;
    select firstonly custTable
        where   (   (   _custTableOther
                    &&  custTable.CustGroup == _custTableOther.CustGroup
                    )
                ||  (  !_custTableOther
                    &&  custTable.CustGroup == _custGroupId
                    )
                )
                        ;
    return custTable;
}
;
test1();
test2();
Дело, судя по всему в том, что AOS как-то некорректно пытается получить доступ к буферу _custTableOther в том случае, если в качестве него передан null. И воспроизводится это именно при вызове с клиента - при выполнении кода на сервере все отрабатывает нормально; тот же тестовый джобик можно запустить на сервер, создав для него специальный пункт меню.
Ниже приводится еще один джобик, который эвристически пытается найти схожие конструкции в коде (для ускорения запускать его лучше на сервере). При этом сравнительно часто встречаются ложные срабатывания, но в целом если удастся отловить 1-2 таких места (преимущественно в своем коде ), то уже время будет потрачено не впустую. В стандартном коде было найдено несколько "подозрительных" мест, но при попытке их эксплойта выяснилось, что они к сваливанию AOS'а не приводят. Подозрения пали на следующие методы, схожим образом использующие свои табличные параметры:
  • \Classes\SalesFormLetter\reselect - но в нем явно проверяется, что метод отрабатывает на сервере;
  • \Classes\RTax25RegTransEngine\updateTotals - здесь перед выполнением запроса идет проверка if (!_mainTrans && !_origTrans), которая неявно инициализирует оба табличных буфера;
  • \Classes\RTax25RegTransEngine_TaxRemainGoods\updateTotals - аналогично RTax25RegTransEngine
X++:
UtilEntryLevel  currentLayer;
UtilElements    utilElements;
DictMethod      dictMethod;
xRefPath        nodePath;
str             source;
str             pattern;
Set             setOfParmNameExclusions;
Set             setOfRecordParmNames;
SetEnumerator   setEnum;
boolean         currentAOLayerOnly;
Counter         cnFound;
Counter         n;
;
setOfParmNameExclusions = new Set( Types::String );
setOfParmNameExclusions.add( '_inventDimCriteria' );
setOfParmNameExclusions.add( '_inventDimParm' );
setOfParmNameExclusions.add( '_inventDimParmCriteria' );

currentLayer = currentAOLayer();
currentAOLayerOnly = Box::yesNo( strfmt( @"Искать только на слое %1?", currentLayer ), DialogButton::Yes ) == DialogButton::Yes;
while select utilElements
    where   (   utilElements.recordType   == UtilElementType::ClassInstanceMethod
            ||  utilElements.recordType   == UtilElementType::ClassStaticMethod
            ||  utilElements.recordType   == UtilElementType::TableInstanceMethod
            ||  utilElements.recordType   == UtilElementType::TableStaticMethod
            )
        &&  (  !currentAOLayerOnly
            ||  utilElements.utilLevel    == currentLayer
            )
{
    dictMethod = new DictMethod( utilElements.recordType, utilElements.parentId, utilElements.name );
    if (    dictMethod
        &&  dictMethod.compiledOk()
        &&  dictMethod.parameterCnt() > 0
       )
    {
        setOfRecordParmNames = null;
        for (n = 1; n <= dictMethod.parameterCnt(); n++)
        {
            if (        dictMethod.parameterType( n )   == Types::Record
                &&  (   utilElements.utilLevel          == currentLayer
                    || !setOfParmNameExclusions.in( dictMethod.parameterName( n ) )
                    )
               )
            {
                if (!setOfRecordParmNames)
                {
                    setOfRecordParmNames = new Set( Types::String );
                }
                setOfRecordParmNames.add( dictMethod.parameterName( n ) );
            }
        }
        if (setOfRecordParmNames && !setOfRecordParmNames.empty())
        {
            source = conpeek( utilElements.source, 1 );
            setEnum = setOfRecordParmNames.getEnumerator();
            while (setEnum.moveNext())
            {
                pattern = strfmt( @"select [^;{]+ where [^;{]*[^a-z0-9_;{.]%1[^a-z0-9_.]", setEnum.current() );
                if (match( pattern, source ))
                {
                    nodePath = xUtilElements::getNodePathRough( utilElements );
                    info( strfmt( @"%1 - %2 (%3)", nodePath, setEnum.current(), utilElements.utilLevel ), '', SysInfoAction_Editor::newOpen( nodePath ) );
                    break;
                }
            }
        }
    }
}
За это сообщение автора поблагодарили: AlGol (2), Vadik (1), Hyper (1), Logger (12).