2015-09-27 3 views
7

Dans le code suivant, la fonction foo s'appelle récursivement une fois. L'appel interne provoque une violation d'accès. L'appel externe intercepte l'exception.Est-ce que __finally est supposé s'exécuter après EXCEPTION_CONTINUE_SEARCH?

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

void foo(int cont) 
{ 
    __try 
    { 
     __try 
     { 
      __try 
      { 
       if (!cont) 
        *(int *)0 = 0; 
       foo(cont - 1); 
      } 
      __finally 
      { 
       printf("inner finally %d\n", cont); 
      } 
     } 
     __except (!cont? EXCEPTION_CONTINUE_SEARCH: EXCEPTION_EXECUTE_HANDLER) 
     { 
      printf("except %d\n", cont); 
     } 
    } 
    __finally 
    { 
     printf("outer finally %d\n", cont); 
    } 
} 

int main() 
{ 
    __try 
    { 
     foo(1); 
    } 
    __except (EXCEPTION_EXECUTE_HANDLER) 
    { 
     printf("main\n"); 
    } 
    return 0; 
} 

Le résultat attendu ici devrait être

inner finally 0 
outer finally 0 
inner finally 1 
except 1 
outer finally 1 

Cependant, outer finally 0 manque manifestement de la production réelle. Est-ce un bug ou y a-t-il des détails que je néglige? Pour l'exhaustivité, arrive avec VS2015, compilant pour x 64. Étonnamment, cela n'arrive pas sur x86, ce qui me fait croire que c'est vraiment un bug.

+0

Cela pourrait techniquement tomber sous le coup d'un comportement indéfini, comme vous l'affectez à un pointeur nul. Avez-vous essayé de lancer une exception régulière en utilisant 'RaiseException'? – OmnipotentEntity

+1

Eh bien, pas bon. Ce n'est pas un nouveau problème, VS2013 se comporte de la même manière. On dirait une limitation structurelle de/SAFESEH pour moi, spécifique à la récursivité, ça marche bien dans un cas non récursif. Assez douteux n'importe qui ici peut résoudre ce problème, mieux pour envoyer un ping à connect.microsoft.com à ce sujet. –

+0

@OmnipotentEntity: L'affectation à un pointeur nul est un comportement * non défini, en ce qui concerne la norme de langage C++. Sur la plate-forme Windows, cependant, ceci est bien défini: L'instruction déclenche une violation d'accès, qui est communiquée au code utilisateur via une exception SEH. – IInspectable

Répondre

0

existe et plus simplement par exemple (on peut retirer le bloc try/finally intérieur.

void foo(int cont) 
{ 
    __try 
    { 
     __try 
     { 
      if (!cont) *(int *)0 = 0; 
      foo(cont - 1); 
     } 
     __except (cont? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) 
     { 
      printf("except %d\n", cont); 
     } 
    } 
    __finally 
    { 
     printf("finally %d\n", cont); 
    } 
} 

avec sortie

except 1 
finally 1 

si finally 0 bloc non exécuté, mais dans le cas non récurrent - pas de bug:

__try 
{ 
    foo(0); 
} 
__except(EXCEPTION_EXECUTE_HANDLER) 
{ 
    printf("except\n"); 
} 

sortie:

finally 0 
except 

c'est bogue dans la fonction suivante

EXCEPTION_DISPOSITION 
__C_specific_handler (
    _In_ PEXCEPTION_RECORD ExceptionRecord, 
    _In_ PVOID EstablisherFrame, 
    _Inout_ PCONTEXT ContextRecord, 
    _Inout_ PDISPATCHER_CONTEXT DispatcherContext 
    ); 

ancienne implémentation de cette fonction avec bug here:

    // 
        // try/except - exception filter (JumpTarget != 0). 
        // After the exception filter is called, the exception 
        // handler clause is executed by the call to unwind 
        // above. Having reached this point in the scan of the 
        // scope tables, any other termination handlers will 
        // be outside the scope of the try/except. 
        // 

        if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) { // bug 
         break; 
        } 

si nous avons dernier compilateur/bibliothèques VC installé, recherche chandler.c (dans mon installation situé au \VC\crt\src\amd64\chandler.c)

et dans le fichier peut maintenant afficher code suivant:

   if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget 
        // Terminate only when we are at the Target frame; 
        // otherwise, continue search for outer finally: 
        && IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags) 
        ) { 
        break; 
       } 

donc la condition supplémentaire est ajoutée IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags) qui corriger ce bug

__C_specific_handler mis en œuvre dans différentes bibliothèques crt (dans certains cas avec lien statique, dans certains cas sera importé de vcruntime*.dll ou msvcrt.dll (a été transmis à ntdll.dll)). aussi ntdll.dll exporter cette fonction - cependant dans les dernières générations win10 (14393) il n'est toujours pas corrigé