Открива стабилно рекурсия дори при наличие на нелокални скокове

Имам конкретна функция (манипулатор на сигнали), за която бих искал да открия рекурсия, т.е. да разбера дали функцията се е самоизвикала пряко или непряко. Трудното е, че функцията извиква код, който не е под нейния контрол в един момент и този код може да направи всичко.

Обикновено бих написал нещо подобно

void foo() {
    static int recursed = 0;
    if(recursed) {
        ...
    }
    recursed = 1;
    othercode();
    recursed = 0;
}

но в този случай съм загрижен, че othercode може да използва longjmp или подобно, за да излезе, в резултат на което recursed да остане на 1. В случай, че функцията ми бъде прескочена по този начин, искам да се уверя, че няма вижда себе си като рекурсивно, ако се извика по-късно (фактът, че е longjmp излязъл от не е проблем в противен случай).

Забележка: Считам longjmp за вероятно. othercode е манипулатор на верижен сигнал от някакъв друг in-the-wild код и съществуват манипулатори за напр. SIGSEGV, които използват longjmp за възстановяване на контекст (напр. като манипулатори на изключения за "защита от грешки"). Имайте предвид, че използването на longjmp в синхронен манипулатор на сигнали обикновено е безопасно. Във всеки случай не ме интересува особено дали другият код изобщо е безопасен, защото не е нещо под мой контрол.


person nneonneo    schedule 05.06.2013    source източник
comment
Притеснявате се дали longjmp е вероятно или просто това е възможност?   -  person Patashu    schedule 05.06.2013
comment
@Patashu: Вероятно. Вижте моята редакция.   -  person nneonneo    schedule 05.06.2013
comment
Заглавието на въпроса трябва да бъде редактирано, за да бъде малко по-конкретно, нещо като „Откриване на повторно влизане дори когато се използва longjmp“   -  person Patashu    schedule 05.06.2013
comment
@Patashu: благодаря за предложението, редактирано   -  person nneonneo    schedule 05.06.2013
comment
Ако знаете къде отива jongjmp, можете да поставите код там, за да изчистите индикацията за рекурсия.   -  person ugoren    schedule 05.06.2013


Отговори (2)


Не съм сигурен как точно ще изглежда кодът, за да го направите, но вместо static int, можете да имате static void *. Вместо да го зададете на 1, задайте го да сочи към текущата стекова рамка. В допълнение към проверката дали не е нула, вие проверявате, за да се уверите, че адресът за връщане от следващия кадър на стека след recursed всъщност сочи към местоположение в кода на foo и също така, че recursed е над текущия указател на стека, т.е. не е изваден.

Звучи много крехко и зависи от архитектурата.

person morningstar    schedule 05.06.2013
comment
Или може би е по-просто да погледнете адреса за връщане от точките на рамката на стека на foo веднага след инструкция за извикване на foo, но това определено би било непреносимо. - person morningstar; 05.06.2013
comment
Обмислих това. Работи наистина добре, с изключение на един малък ъглов случай: да предположим, че othercode прескача към функция доста над foo, която извиква друга функция, която разпределя (но не използва напълно) огромен стеков буфер, след което в крайна сметка задейства foo. Ако нямам късмет, гигантският стеков буфер едновременно премества указателя на стека под foo и запазва оригиналния foo стек, правейки го да изглежда много като рекурсия. - person nneonneo; 05.06.2013
comment
Разбира се, това е доста малко вероятно, поради което вероятно ще използвам това решение. Но в идеалния случай бих искал да намеря решение, което няма този проблем. - person nneonneo; 05.06.2013
comment
Може би мисля за указателя на основата / рамката, а не за указателя на стека. - person morningstar; 05.06.2013
comment
Няма значение, същият проблем. Разпределеният, но неинициализиран буфер може да е в предишен кадър. - person morningstar; 05.06.2013

Според стандарта POSIX за signal(7), longjmp( ) не е едно от повикванията, които са безопасни за извикване от вътрешността на манипулатора на сигнали. Преди дори да помислите за този проблем, документацията longjmp(3) казва , трябва да се уверите, че кодът, който извиквате, използва sigsetjmp() и siglongjmp()

Ако кодът, който извиквате, изскача от манипулатора на сигнали, тогава не виждам как можете да знаете кога да актуализирате вашата променлива recursed, освен ако не контролирате и функция за обратно извикване, която този неизвестен код извиква обратно във вашето приложение.

person Benjamin Leinweber    schedule 05.06.2013
comment
Те се отнасят само за асинхронни сигнали. Пиша манипулатор на сигнали за SIGSEGV и други нормално синхронни сигнали, така че не се очаква това да е проблем. - person nneonneo; 05.06.2013