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

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

March 26th, 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. Иными словами такая проверка гарантирует, что пользовательский код не сможет заставить ядерный код обратится к какому-либо адресу в адресном пространстве ядра. Тем самым ситуация с порчей защитной страницы ядерного стека, как результат запроса из пользовательского кода, не возможна. Само собой, что защитная страница пользовательского стека по-прежнему может пострадать. Но тут уж само приложение виновато.

  1. soapes
    March 27th, 2007 at 07:35 | #1

    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?

  2. Not a kernel guy
    March 27th, 2007 at 09:15 | #2

     

    __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;
    }
    
  3. Alexey
    March 27th, 2007 at 23:12 | #3

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

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

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

    (more…)

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

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

  4. Not a kernel guy
    March 28th, 2007 at 08:48 | #4

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

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

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

  5. Alexey
    March 28th, 2007 at 13:10 | #5

    Спасибо!!!

Comments are closed.