J'ai été à essayer de comprendre de manière pratique comment la queue les appels sont gérés par le compilateur C#.
(Réponse: Ils ne sont pas. Mais le 64 bits JIT(s) FERA TCE (queue d'appel de l'élimination). Des Restrictions s'appliquent.)
J'ai donc écrit un petit essai à l'aide d'un appel récursif qui imprime combien de fois elle est appelée avant la StackOverflowException
tue le processus.
class Program
{
static void Main(string[] args)
{
Rec();
}
static int sz = 0;
static Random r = new Random();
static void Rec()
{
sz++;
//uncomment for faster, more imprecise runs
//if (sz % 100 == 0)
{
//some code to keep this method from being inlined
var zz = r.Next();
Console.Write("{0} Random: {1}\r", sz, zz);
}
//uncommenting this stops TCE from happening
//else
//{
// Console.Write("{0}\r", sz);
//}
Rec();
}
Droit sur la sélection, le programme se termine avec TELLEMENT d'Exception sur l'un de:
- 'Optimiser le build OFF (Debug ou Release)
- Cible: x86
- Cible: AnyCPU + "Préfèrent 32 bits" (ce qui est nouveau dans VS 2012 et la première fois que je l'ai vu. De plus ici.)
- Certains apparemment anodins de la branche dans le code (voir: "d'autre" branche).
À l'inverse, l'utilisation de 'Optimiser construire" SUR + (Cible = x64 ou AnyCPU avec "Préfèrent 32bit' OFF (sur un CPU 64 bits)), le TCE se passe et le compteur continue de tourner jusqu'à jamais (ok, c'sans doute tours vers le bas chaque fois que sa valeur dépasse).
Mais j'ai remarqué un comportement que je ne peux pas l'expliquer dans l' StackOverflowException
cas: il n'a jamais (?) arrive exactement la même pile de profondeur. Voici les résultats de quelques 32 bits s'exécute, Version validée:
51600 Random: 1778264579
Process is terminated due to StackOverflowException.
51599 Random: 1515673450
Process is terminated due to StackOverflowException.
51602 Random: 1567871768
Process is terminated due to StackOverflowException.
51535 Random: 2760045665
Process is terminated due to StackOverflowException.
Et Debug:
28641 Random: 4435795885
Process is terminated due to StackOverflowException.
28641 Random: 4873901326 //never say never
Process is terminated due to StackOverflowException.
28623 Random: 7255802746
Process is terminated due to StackOverflowException.
28669 Random: 1613806023
Process is terminated due to StackOverflowException.
La taille de la pile est constante (par défaut à 1 MO). La pile d'images grandeurs sont constantes.
Alors, ce qui peut rendre compte de la (parfois non-trivial) variation de la profondeur de la pile lorsque l' StackOverflowException
hits?
Mise à JOUR
Hans Passant soulève la question de l' Console.WriteLine
de toucher de P/Invoke, l'interopérabilité et peut-être non-déterministe de verrouillage.
Donc, j'ai simplifié le code pour ceci:
class Program
{
static void Main(string[] args)
{
Rec();
}
static int sz = 0;
static void Rec()
{
sz++;
Rec();
}
}
J'ai couru dans la Presse/32 bits/Optimisation sans un débogueur. Lorsque le programme se bloque, je attacher le débogueur et vérifier la valeur du compteur.
Et encore n'est-ce pas la même chose sur plusieurs pistes. (Ou mon test est imparfait.)
Mise à JOUR: Fermeture
Comme suggéré par fejesjoco, j'ai regardé dans l'ASLR (Address space layout randomization).
C'est un technique qui rend difficile pour les attaques par débordement de tampon pour trouver l'emplacement précis de (par exemple), des appels système, par randomisation des choses différentes dans l'espace d'adressage du processus, y compris la pile de position et, apparemment, de sa taille.
La théorie, c'est bon. Nous allons le mettre en pratique!
Pour tester cela, j'ai utilisé un outil de Microsoft spécifique pour la tâche: EMET ou de La Enhanced Mitigation Experience Toolkit. Il permet le réglage de l'ASLR drapeau (et beaucoup plus) sur un système ou d'un processus.
(Il est également à l'échelle du système, le registre de piratage alternative que je n'ai pas essayer)
Afin de vérifier l'efficacité de l'outil, j'ai aussi découvert que Process Explorer dûment rapports sur l'état de l'ASLR drapeau dans les "Propriétés" à la page du processus. N'a jamais vu ça jusqu'à aujourd'hui :)
Théoriquement, IL peut (re)mettre l'ASLR drapeau pour un seul et même processus. Dans la pratique, il ne semble pas de changer quoi que ce soit (voir l'image ci-dessus).
Cependant, j'ai désactivé l'ASLR pour l'ensemble du système et (un redémarrage plus tard), j'ai enfin pu vérifier qu'en effet, exception maintenant arrive toujours à la même pile de profondeur.
BONUS
ASLR liés, dans les anciennes news: Comment Chrome ai pwned