Not a kernel guy

… in the Windows kernel team

Monday, March 26, 2007

Проверка параметров функций на корректность.

В позапрошлую пятницу я поучаствовал в одном интересном обсуждении. В общих чертах, речь шла о том, как нужно и как не нужно проверять параметры функции. В частности же, были окончательно затоптаны и преданы анафеме бренные останки функций IsBadReadPtr и IsBadWritePtr.

Если вы читали «Should I check the parameters to my function?» в блоге Larry Osterman, и являетесь сторонником второго подхода – можете дальше не читать. :-)

Когда то давно, функции IsBadReadPtr и IsBadWritePtr использовались для проверки валидности переданных указателей. Правда, со временем выяснилось, что от этих функций больше вреда, чем пользы. Во-первых, вместо обнаружения некорректных указателей (что по идее было целью их создателей), эти функции скорее скрывали ошибки. Во-вторых, даже успешное тестирование указателя с помощью одной из функций не гарантировало успешность следующей операции с этой памятью в многозадачной среде. В-третьих, IsBadWritePtr портит память по фактически случайному адресу – гарантия того, что приложение всё-таки упадёт позднее, но только с большими неприятностями. В-четвёртых, даже IsBadReadPtr может быть виновником в сбое приложения, если проверяемый адрес приходится на зашитную страницу (guard page) стека. В результате система потеряет возможность увеличивать стек, по мере надобности, и приложение с грохотом упадёт, если попытается это сделать.

Как же правильно проверять переданные указатели? Правило простое – любое значение указателя не равное 0 считается корректным. Если указатель на самом деле указывает «не туда», то приложение завершиться ошибкой доступа, что, в общем-то, плохо, но помогает найти и исправить ошибки быстрее.

Это правило не работает для специальных случаев, вроде точки входа в ядре операционной системы или RPC. Хотя в случае RPC каждая из сторон сама контролирует выделение буферов, так что функциональность IsBadXxxPtr все равно не требуется.

В случае ядерного вызова указатели должны проверяются на корректность, однако и в этом случае IsBadXxxPtr оказывается не у дел. NT ядро использует функции ProbeForRead и ProbeForWrite, которые во много похожи на IsBadXxxPtr. Не смотря на похожесть, ProbeForXxx главным образом проверяют, что переданный буфер целиком находится в пользовательском адресном пространстве, а не то, что буфер размещён в валидной памяти. Дальнейшие обращения к переданному буферу в любом случае окружаются блоком __try - __except. Иными словами такая проверка гарантирует, что пользовательский код не сможет заставить ядерный код обратится к какому-либо адресу в адресном пространстве ядра. Тем самым ситуация с порчей защитной страницы ядерного стека, как результат запроса из пользовательского кода, не возможна. Само собой, что защитная страница пользовательского стека по-прежнему может пострадать. Но тут уж само приложение виновато.

Posted at 1:42 pm •

RSS feed | Trackback URI

5 Comments »

Comment by soapes — March 27, 2007 @ 7:35 am

Hm. Seems that you never see STATUS_GUARD_PAGE exception from usermode code caused by _chkstk (a.k.a. _alloca_probe) function that touches the stack.

__try
{
    сhar temp[ 256 * 1024 ];
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
    printf("Handler\n");
}

Hence IsBadReadPtr is not so bad.
Actually what is the difference who touches the pages - your code when trying to read something or IsBadReadPtr() which does the same?

 
Comment by Not a kernel guy — March 27, 2007 @ 9:15 am

 

__try
{
    char temp[ 256 * 1024 ];
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
    printf(”Handler\n”);
}

Когда дело доходит до __try, STATUS_GUARD_PAGE уже был обработан. Кроме того, к защитной странице нужно обратиться из другого потока. Тогда обработчик STATUS_GUARD_PAGE не сможет правильно выделить следующую защитную страницу. Вот пример:

#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <malloc.h>

const int PROBE_DEPTH = 4;

static SYSTEM_INFO si;

unsigned
worker(
    void* ptr
    )
{
    // Touch pages
    int* vptr = reinterpret_cast<int*>(ptr);

    for (int i = PROBE_DEPTH; i > 0; --i)
    {
        vptr -= si.dwPageSize / sizeof(int);
        IsBadReadPtr(vptr, sizeof(int));
    }

    return 0;
}

void
doStuff(
    int i
    )
{
    // Just allocate a buffer from the stack
    void* ptr = _alloca(si.dwPageSize);

    if (i > 0)
    {
        doStuff(i - 1);
    }
}

int
main(
    int argc,
    const char* argv[]
    )
{
    int anchor;

    // Get memory page size
    GetSystemInfo(&si);

    // Start the worker thread and wait until it finishes
    uintptr_t thread = _beginthreadex(0, 0, worker, &anchor,0, 0);
    WaitForSingleObject(reinterpret_cast<HANDLE>(thread), INFINITE);

    // Do some work
    doStuff(PROBE_DEPTH);

    return 0;
}
 
Comment by Alexey — March 27, 2007 @ 11:12 pm

Не совсем по поводу топика, но с него всё началось :)
Я читаю ваш блог через КПК (впрочем я все блоги так читаю) и постоянно раздражаюсь когда в блоге одна две строки текста а потом ссылочка (more….), т.к. там где я читаю инета нет и я не могу перейти на этот самый “more…”.
Читал ваш блог и всегда радовался (блог интересный!!!!), но вдруг обнаружил, что теперь и ваш блог по rss выдает урезанную версию.
————————————————————————————————————————
http://feeds.feedburner.com/not-a-kernel-guy

В позапрошлую пятницу я поучаствовал в одном интересном обсуждении. В общих чертах, речь шла о том, как нужно и как не нужно проверять параметры функции. В частности же, были окончательно затоптаны и преданы анафеме бренные останки функций IsBadReadPtr и IsBadWritePtr.

Если вы читали «Should I check the parameters to my function?» в блоге Larry Osterman, и являетесь сторонником второго подхода – можете дальше не читать. :-)

(more…)

————————————————————————————————————————

Можно вернуть как всё было? Был бы очень признателен!
Но в любом случае спасибо за блог!!!

 
Comment by Not a kernel guy — March 28, 2007 @ 8:48 am

Это был подлый удар в спину со стороны разработчиков WordPress. :-(

Ticket #2582: Full text feed problem when using <!–more–> tag.
Full-text feed and the <!–more–> tag.
Full Text Feed.

Вернул всё как было.

 
Comment by Alexey — March 28, 2007 @ 1:10 pm

Спасибо!!!

 

Your Comment (smaller | larger)

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Powered by WordPress