La différence entre #if DEBUG et Conditional("DEBUG")
Par amethyste le Déc 10, 2006 | Dans focus | 2 retours »
Rappelons peut être que Conditional est un attribut de méthode qui indique au compilateur d'ignorer les appels à celle-ci si un certain identificateur de prétraitement n'est pas définit. Considérons par l'exemple suivant:
using System; using System.Diagnostics; namespace TestConditionnel { class Program { static void Main(string[] args) { CreationMessage(); } [Conditional("DEBUG")] public static void CreationMessage() { Console.WriteLine("Hello"); } } }
L'appel à la méthode CreationMessage() de la ligne 10 ne sera pas compilé en mode DEBUG.
Cet exemple de code est donc en tout point identique à celui-ci:
using System; using System.Diagnostics; namespace TestConditionnel { class Program { static void Main(string[] args) { #if DEBUG CreationMessage(); #endif } public static void CreationMessage() { Console.WriteLine("Hello"); } } }
Mais il est beaucoup plus lourd avec un risque d'en oublier.
Venons en au cœur du débat.
La documentation de l'attribut Conditional précise un point qu'en général on néglige de lire:
Les méthodes décorées avec l'attribut ConditionalAttribute sont toujours compilées en MSIL (Microsoft Intermediate Language), mais les appels aux méthodes ne peuvent pas être effectués au moment de l'exécution.
Si l'on reprend notre premier exemple compilé en mode RELEASE on constate qu'effectivement l'appel à CreationMessage() ne se fait pas. Par contre si on ouvre l'exécutable avec ILDASM j'y trouve le code MSIL toujours présent. Seuls les appels ont disparus dans Main.
Et alors allez vous dire.
Et alors rien n'interdit à une application malveillante de procéder par réflexion à un appel de cette méthode et voir ce qui s'y passe. En soit il s'agit d'un trou potentiel de sécurité, soit parce qu'il révèle des indices sur le fonctionnement de votre classe, soit parce que la méthode CreationMessage() va être exécutée à un moment où le code ne s'y attend pas. Il est peu probable que ce scénario ait été validé et pour peu que la méthode ne soit pas trop bien écrite elle peut ne pas laisser l'état de l'appli exactement comme elle l'a trouvé.
Peu importe que ce soit grave ou non, on dit avoir en tête ce problème si l'on écrit une application où la sécurité est un point sensible.
Alors que faire?
On peut essayer de protéger la méthode en veillant à ce que rien de sensible ne se produise en ajoutant des tests de permissions…, mais ce n'est pas toujours possible. Le mieux est vraiment de supprimer ce code.
C'est justement ici qu'intervient #if DEBUG qui lui ne fait pas dans le sophistiqué: tel Léon, il nettoie. Voici une version modifiée parfaitement sûre due à Spencer Low:
[Conditional("DEBUG")] public static void CreationMessage() { #if DEBUG Console.WriteLine("Hello"); #endif }
La déclaration de CreationMessage() subsiste, mais le corps de la méthode a disparu.
On peut se demander s'il n'aurait pas été plus simple que le compilateur fasse le nettoyage complet? Plus simple sans doute, seulement il ne serait alors plus possible d'écrire certaines classes. Si vous vous amusez à décompiler Debug.Write() par exemple vous trouvez ceci:
[Conditional("DEBUG")] public static void Write(object value) { TraceInternal.Write(value); }
C'est l'attribut Conditional qui permet justement à la classe Debug de fonctionner comme elle le fait, autrement Microsoft ne pourrait pas la compiler en mode RELEASE. Bien sûr on aurait pu imaginer d'autres types d'attributs ou d'autres bidouilles, mais je trouve tout de même que le choix fait reste le plus élégant. Le trou de sécurité n'est pas critique dans la majorité des cas.
Bibliographie
Un livre excellent de John Robbins édité par Microsoft Press où j'ai trouvé l'astuce présentée:
Debugging Microsoft .net 2.0 applications
2 commentaires
un détail complémentaire
Si vous réalisez une méthode du genre
[Conditional("DEBUG")]
private void Check(int errorCode);
{
if (errorCode == UnCodeDErreur)
{
bla bla bla bla
}
}
et un appel du genre
...;
Check(MyAPI.UneMethodeQuelconque());
...;
en mode release, l'appel à Check n'est pas réalisé ET DONC l'appel à UneMethodeQuelconque non plus !!
evident, mais dans le feux de l'action, on peut potentiellement se faire avoir
bonne journée à tous
Laisser un commentaire
| « Ma bibliographie personnelle | ASP: adapter le rendu au périphérique de sortie » |