9
votes

Existe-t-il un moyen de définir une variable comme ininitialisée dans GCC / Clang?

Je serais intéressé de savoir s'il est possible de désactiver explicitement une variable en C, comme étant ininitialisé.

pseudo code ... p> xxx pré>

voici un Exemple d'une manière de corrompre une variable, mais GCC augmente un avertissement lorsque "A" est attribué au VaR ininitialisé, plutôt la seconde utilisation de "A". P>

#define TAINT_MACRO_BEGIN(array) (void)(array); { void **array; (void)array;
#define TAINT_MACRO_END(array) } (void)(array);
{
    int *array;
    array = some_alloc();
    b = array[0];
    some_free(array);
    TAINT_MACRO_BEGIN(array);

    /* the compiler should raise an uninitialized warning here */
    b = array[0];
    TAINT_MACRO_END(array);
}


19 commentaires

Et si un pointeur a été libéré dans une unité de traduction, ensuite utilisé dans un autre? Le compilateur ne peut pas attraper ça.


Certains_index n'est pas déclaré dans votre premier exemple. Je m'attendrais à ce que tout le compilateur se plaint de cela plutôt que d'initialisation.


Je ne sais pas pourquoi vous n'aimez pas l'avertissement dans votre 2e exemple à a = b; un compilateur qui s'est également plaint de mauvaises utilisations ultérieures ( printf ("Deuxième% D \ N", a) ; ) serait verbeux. Le premier avertissement suffira pour la plupart des débogage.


@chux renommé quelque_index à 0 pour éviter la confusion, l'intention d'attribuer a = b est de définir "A" pour être une variable non initialisée. Alors que je suis d'accord sur la manière dont le compilateur fonctionne n'est pas faux , je cherche un moyen de souiller une variable pour utiliser un avertissement d'un compilateur, alors j'accepte un tour (attribut compilateur / intégré) peut Être requis, cela peut être enveloppé dans une macro par exemple.


@Collin, votre droit - cela n'atteint pas la mémoire libérée sur les fonctions, mais même sans cette capacité, je pense qu'il serait utile d'attraper une simple utilisation de bugs à la compilation.


Vous demandez-vous comment implémenter un outil de débogage de la mémoire? Avez-vous regardé la clôture électrique?


@ user315052, non - outils comme Valgrind, Duma, clôture eletrique ... etc. sont d'exécution, je cherche la possibilité de dire au compilateur qu'une variable est ininitialisée afin de mettre en garde sur l'utilisation jusqu'à ce que cela soit repensé.


Je vois que vous essayez de mettre en œuvre un outil d'analyse statique de l'homme pauvre. Pourquoi ne pas simplement utiliser un véritable outil d'analyse statique? Depuis que vous êtes prêt à décorer votre code, Splint peut être un bon ajustement pour vous.


@ user315052, je prévoyais que cela viendrait, et oui - j'utilise des outils d'analyse statiques réels, y compris celui que vous avez mentionné - le problème est qu'ils ont tellement de faux positifs que sa pratique ne leur calme de tous. L'affaire que je parle est simple, limitée, mais on pourrait être faite d'avoir zéro faux positifs - avec un bonus qu'il arrive à la compilation afin que les problèmes ne glissent pas avant de lancer une analyse statique prochaine.


@ user315052: Toutes les analyses statiques fournissent-elles également des fonctionnalités d'annotation qui permettent à une variables? En fait, je serais bon de savoir quels outils ont cette fonctionnalité.


@Andreyt: Tous les outils d'analyse statiques que je suis au courant peuvent suivre lorsque le code accède à la mémoire libérée.


@Andreyt, ils font mais ils font des hypothèses souvent incorrectes, donc - de faux positifs.


@Andreyt: Je suis d'accord il y a beaucoup de faux positifs. Ils fournissent tous des fonctionnalités de suppression, mais impliquent l'ajout de règles de base de connaissances à leur système ou en ajoutant des commentaires spéciaux au code pour calmer ces cas, voire de modifier le code d'une certaine manière que l'outil comprend mieux.


Merci de me corriger si je me trompe, mais n'est pas int b; A = B invalide (ou Veeeery Bad) à cause d'une lecture sans écrire avant?


@ UROC327, son invalidation intentionnelle de la valeur, de sorte qu'un compilateur puisse remarquer cela et suivre, «A» comme étant ininitialisé. Comme il s'avère que GCC et clang ne le font pas, mais un aspect similaire pourrait éventuellement être fait pour travailler.


@ IdeasMan42: Il ne s'agit pas d'une "mémoire libérée". Il s'agit d'une fonctionnalité beaucoup plus générale, qui peut être facilement démontrée à une plus grande convivialité que tout autre outils statiques peut détecter automatiquement. Si j'ai un invariant dans mon programme qui lie deux (ou plus) entités les uns aux autres, l'invalidation d'une entité doit contaminer tous les autres. Un outil statique ne sera pas capable de comprendre cela pour moi.


@Andreyt, droite - vous pouvez configurer un tableau sur la pile (ou même un seul VaR pour cette affaire) et en raison de vos codes propre à la logique interne, le contenu devient invalide (mais initialisé en ce qui concerne le compilateur), donc la capacité de La souillure montre l'intention, la possibilité d'avoir un avertissement de compilateur serait aussi utile pour attraper une situation que le développeur original n'a pas intenu.


Je peux voir comment cela serait possible sans nuire à une variable locale, mais je ne peux pas voir une solution pour les paramètres de fonction sans ombrage.


AFAICS Il faudrait être pris en charge par le compilateur, ou - le compilateur devra interpréter une action (par exemple, attribuer explicitement une variable non initialisée de manière non ambiguë) comme réémayant la variable existante en tant que non initialisée. de nouveau.


3 Réponses :


1
votes

Je vais aller dans l'inverse et envelopperais des macros souillées autour de l'allocation et des fonctions libres. C'est ce que j'ai à l'esprit: xxx

Donc, votre exemple ressemblerait à ceci: xxx

Ce n'est pas parfait. Il ne peut y avoir qu'un appel à gratuit () par variable contaminée, car la étiquette goto est liée au nom de la variable. Si le saut saute sur d'autres initialisations, vous pouvez obtenir d'autres faux positifs. Cela ne fonctionne pas si l'allocation se produit dans une seule fonction et la mémoire libérée dans une fonction différente.

mais, il fournit le comportement que vous avez demandé votre exemple. Lors de la compilation normalement, aucun avertissement n'apparaîtrait. Si compilé avec -do_taint , un avertissement apparaît à la seconde affectation à B .


J'ai travaillé une solution assez générale, Mais il s'agit de la suppression de la fonction entière avec les macros de début / de fin et repose sur l'extension GCC typeof . La solution finit par ressembler à ceci: xxx

ici, taint_begin2 est utilisé pour déclarer les deux paramètres de fonction qui obtiendront le traitement corrompu. Malheureusement, les macros sont une sorte de désordre, mais facile à prolonger: xxx


8 commentaires

En ce qui concerne votre solution, il semble que cela utilise réellement «Taint_me» avant son initialisation ?, ou définiriez-vous une ombre pour elle ailleurs?


@ IdeasMan42: TAINT_ME est un global, de sorte qu'il soit initialisé à 0. Mais puisqu'il est déclaré volatile, le compilateur ne sait pas si c'est toujours 0 ou non pendant la compilation.


En ce qui concerne l'utilisation d'une macros de début / d'extrémité, je suis sûr que cela peut être fait pour fonctionner, le problème est-il trop perturbateur et non quelque chose que je pourrais vous engager dans mes projets Codebase - changerait cings (peut-être 1000) des lignes et IMHO ne lit pas très agréable. Je pense que ce serait délicat d'autoriser également la ré-affectation dans le bloc (peut-être possible avec typeOf () et Macro Magic, je suppose). S'il était possible de faire dans un seul bloc, il pourrait être enveloppé dans une macro qui enveloppe un appel de fonction (gratuitement par exemple), qui peut ensuite être utilisé sans avoir à passer sur tous les fichiers source et à les modifier.


@ IdeasMan42: Je peux redéfinir gratuit () pour faire ce que taint_end si cela rendrait la solution plus agréable. Vous devez vous rendre compte que votre approche est une sorte de pirate de piraque. Les solutions sont donc susceptibles d'être hackish.


Il y a tant d'endroits dans le code où Malloc () et Gratuit () ne sont pas dans la même fonction que je ne pense pas que cela vaut la peine de tenter de rendre Wrap Malloc () et gratuit () dans les macros qui commencent / mettre fin à un bloc de code. Étant donné que le compilateur est déjà au courant d'un état initialisé des variables, j'espérais qu'il y avait un moyen de contrôler sans avoir à apporter des modifications plus importantes pour le contrôle de l'écoulement ou la limitation d'une portée de variables. Météo La solution est pirate ou non, si elle peut être faite pour travailler de manière fiable, je pense que cela serait utile.


Vous avez des objections valides à la solution proposée. J'ai indiqué ses limites dans la réponse. Je crois que vous devrez faire une demande de fonctionnalité à votre fournisseur de compilateur pour obtenir quelque chose de mieux (que votre solution ou votre mine).


@ IdeasMan42 Puisque le compilateur est déjà au courant d'un état initialisé de variables, ... Vous faites une fausse hypothèse. En général, il n'est pas possible pour le compilateur de le savoir. L'analyse des flux de données est a) optionnelle et b) impossible pour de nombreux cas mondiaux réels où l'état de l'initialisation dépend de l'entrée à Runtime .


@Jens, dans certains cas, GCC / SLIG peut savoir avec certitude, et mettra en garde s'il est ininitialisé, il y a le cas où il n'est pas sûr, donc -wmaybe-ininitialisé . Être capable de réinitialiser l'état initialisé aurait les mêmes limites qui existent maintenant. Mais je vois ton point.



3
votes

J'ai envoyé une réponse sur la liste de la GCC, mais depuis que j'utilise donc moi-même, moi-même ...

dans le moderne C et C ++, je m'attendrais à ce que les programmeurs utilisent limité Champ d'application variable pour contrôler ce type d'exposition. P>

Par exemple, je pense que vous voulez quelque chose comme ça (notez que le l'attribut que j'utilise n'existe pas réellement, j'essaie juste de paraphraser votre demande). p> xxx pré>

Si tel est le cas, j'utiliserais des champs supplémentaires en C99 (ou ultérieure) ou C ++ (jolie Bien sûr, il a eu "déclarer au point d'utilisation" depuis au moins bras en 1993 ...): p>

int x = 1; // initialized 

{ 
    int y; // uninitialized 
    x = y; // warn here 
    y = 2; // ok, now it's initialized 
    x = y; // fine, no warning 
} 

{ 
    int y; // uninitialized again! 
    x = y; // warns here 
} 


3 commentaires

Je fais parfois cela, lorsque des morceaux de code peuvent être divisés en blocs et chacun a ses propres vars, il est parfois logique. Cependant, avec de grandes fonctions avec beaucoup de blocs imbriqués, je préférerais vraiment ne pas causer plus de trindages en ajoutant des blocs simplement en raison du risque qu'une variable est utilisée quand elle ne devrait pas être. Fondamentalement, indenter de gros blocs de code existant est tout à fait perturbateur (des dégâts avec des antécédents de validation) et, dans la plupart des cas, il dit que le compromis ne valait pas la peine. Une seule ligne pour souiller une variable d'autre part ne provoque pas trop de bruit, donc iMho son acceptable.


Je comprends vos objections, mais je crois sincèrement que vous serez mieux à long terme si vous restructuez votre code pour utiliser plus de scopes - en ajoutant des étanches supplémentaires aux fonctions existantes ou en divisant vos fonctions en plus petites. Si vous rencontrez des limitations de longueur et que vous êtes à court de place latérale pour l'indentation, ce sont les deux astuces que vous devez prendre en compte cette fonction en plus petites. Enfin, vous demandez essentiellement au compilateur de traiter la variable "comme neuf"; En réalité, ce serait "nouveau", IMHO, être moins déroutant. Bonne chance!


C'est la réponse de bonne pratique. En réalité, le problème dans la question peut être mieux évité de ne pas réutiliser de variables dans une seule portée, ce qui est une odeur de code, et b) une odeur vraiment mauvais si les variables peuvent devenir invalides à mi-chemin à travers leur durée de vie. Si cela peut arriver, ce que vous avez réellement est deux variables, alors pourquoi ne pas la rendre explicite?



2
votes

basé sur une réponse à une question différente , vous pouvez utiliser setJMP et longjmp pour modifier une variable locale modifiée a une valeur indéterminée. xxx

si x est une variable locale, la valeur sera indéterminée dans les lignes de code après souillure est appliqué à celui-ci. Ceci est dû au C.11 §7.13.2 ¶3 (emphase des mien):

Tous les objets accessibles ont des valeurs et tous les autres composants de la La machine abstraite a un état, à compter du temps que la fonction longjmp était appelé, sauf que les valeurs d'objets de stockage automatique durée locale à la fonction contenant l'invocation de la macro correspondante SETJMP qui n'a pas de qualification volatile Tapez et ont été modifiés entre le SETJMP Invocation et LONDJMP appel sont indéterminés .

Notez qu'aucun diagnostic n'est requis pour utiliser une variable si transcée. Cependant, les écrivains du compilateur détectent de manière agressive de comportement non défini pour améliorer l'optimisation, et donc je serais surpris si cela reste non diagnostiqué pour toujours.


0 commentaires