1. Си / Говнокод #12540

    +139

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    long __stdcall wndproc(HWND wnd, unsigned int message, WPARAM wparam, LPARAM lparam)
    {
    	switch(message)
    	{
    	case WM_USER + 100:
    		{
    			char data[128];
    			fill_data(data);
    			PostMessage(wnd, WM_USER + 666,  0, (LPARAM)data);
    			return 0;
    		}
    	case WM_USER + 666:
    		{
    			char * data = (char *)lparam;
    			use_data(data);
    			return 0;
    		}
    //etc

    Wandering of the pointer или как выжить вне стека.

    Запостил: Xom94ok, 05 Февраля 2013

    Комментарии (26) RSS

    • Если бы вместо PostMessage было SendMessage, ещё куда ни шло - просто wndproc вызвалась бы рекурсивно (хотя тоже не фонтан). А так между двумя циклами обработки сообщений стек затрётся.

      Кстати, на 64-битной архитектуре возвращаемое значение поплывёт - скажем спасибо MS за 32-битный long. Нельзя было объявить как LRESULT или тогда уж LONG_PTR?
      Ответить
      • >>скажем спасибо MS за 32-битный long
        Видимо, комплексы свои они так компенсируют; косвенно на это указывает псевдоним LONG, выглядящий "толще" обычного long-а.
        Ответить
        • Нет, это чтобы хомячкам не пришлось переписывать тонны ГК, написанного в предположении, что sizeof(int) == sizeof(long) == 4.
          Ответить
          • Угу, ещё надо вспомнить про то, что в DWORD помещается любой указатель
            Ответить
    • воистину знатный гк
      Ответить
    • Есть всё написать определённым образом, то этот код будет стабильно работать.
      Ответить
      • >стабильно работать.
        Хуйню сказал.
        Ответить
        • Надо, чтобы при каждом вызове этой функции указатель на стек был один и тот же. Вроде бы это в винде выполняется, при условии, что функция зовётся виндой, а не прямо кодом пользователя через SendMessage.
          Ответить
          • И чем это объясняется?
            Ответить
            • я могу банально перед DispatchMessage() воткнуть alloca(rand())
              Ответить
              • А я могу воткнуть 1/0 в любое место.
                Ответить
                • а бомбу в самолете можешь взорвать?
                  Ответить
                  • Я много чего могу сделать, но мы обсуждаем, можно ли дописать остальную часть программы так, чтобы она стабильно работала, а не существование способа вставить в другое место кусок, который сделает её нерабочей. Вообще обсуждать второе бессмысленно, потому что это и так очевидно.
                    Ответить
                    • Да я на самом деле с тобой согласен. Должен быть контракт.
                      http://bit.ly/14RuoiA
                      А если соглашения нет, то порушить можно всё что угодно.
                      Ответить
                      • ну покажи мне контракт на значение sp
                        Ответить
                        • Допустим, он есть. Если его нету, то обсуждать что-либо неинтересно.
                          Я проверил, на WM_SIZE написал вывод в заголовок указателя на параметр Handle, при старте он не такой, как при работе, но во время работы он у меня не менялся.
                          Ответить
                          • Кстати, интересно, почему не такой
                            Ответить
                            • точнее, понятно, почему не такой, не понятно, почему потом не меняется
                              Ответить
                              • А чё ему меняться? Напиши простую прогу на ВинАПИ, и внутри оконной функции посмотри стек.
                                Не такой он потому, что при первом вызове WM_SIZE (происходит при создании окна) на стеке отладчик видит только оконную функцию (видимо глубже там системные функции, которые он не различает), а при других WM_SIZE, инициированных пользователем, на стеке видно процедуру, в которой вызывается цикл обработки сообщений и 4 вызова оконной функции.
                                Ответить
                              • А почему потом не меняется, да потому что не с чему ему меняться, DispatchMessage вызывается в цикле каждый раз на одной глубине стека, а внутри него, скорее всего, делается тупо вызов оконной функции по указателю (с проверкой описателя и указателя, конечно), короче причин для разной вершины стека нету.
                                В общем, автор кода - очень везучий.
                                Ответить
                                • Да глубина тут вообще ни на что не влияет. В сообщении же передается указатель, поэтому даже при разных глубинах стека доступ будет к одному и тому же куску памяти. Другое дело - содержимое этого куска...

                                  Если бы между приходами WM_USER+100 и WM_USER+666 закралось бы еще одно сообщение, то его обработчик вполне мог засрать область памяти, в которой лежала data, своими локальными переменными, и WM_USER+666 прочитал бы мусор.
                                  Ответить
                                  • Ну глубина влияет на то, что есть риск обратиться к памяти, лежащей на меньшей глубине в стеке, чем функция, и тем самым можно затереть адрес возврата например.
                                    Ответить
                                • Так и где гарантия, что DispatchMessage() будет всегда вызываться на одной глубине стека?
                                  Ответить
                                  • > на одной глубине стека
                                    Ну если память не изменяет - типичный цикл обработки сообщений выглядит так:
                                    while (GetMessage(&msg, 0, 0, ..)) {
                                        TranslateMessage(&msg);
                                        DispatchMessage(&msg);
                                    }
                                    Отсюда и гарантия одной глубины (но нет никакой гарантии, что тот же TranslateMessage или обработчики соседних сообщений не похерят данные в data).
                                    Ответить
                                  • А что, где-то принято делать не так?
                                    Впрочем да, есть подвох в том, что можно вызвать ProcessMessages посреди сложного расчёта, но это не самый типичный случай.
                                    Ответить

    Добавить комментарий