4 votes

Le compilateur ARM 5 ne respecte pas entièrement le qualificatif volatile

Considérez le code suivant :

volatile int status;

status = process_package_header(&pack_header, PACK_INFO_CONST);

if ((((status) == (SUCCESS_CONST)) ? ((random_delay() && ((SUCCESS_CONST) == (status))) ? 0 : side_channel_sttack_detected()) : 1))
{
    ...
}

Qui génère ce code machine (produit avec l'objdump de la chaîne d'outils) :

  60:   f7ff fffe       bl      0 
  64:   9000            str     r0, [sp, #0]     /* <- enregistré en mémoire car status est volatile */
  66:   42a0            cmp     r0, r4           /* <- où est la lecture avant la comparaison ? status est volatile, il aurait pu changer entre la dernière instruction de stockage (ligne précédente) et maintenant */
  68:   d164            bne.n   134 
  6a:   f7ff fffe       bl      0 

Maintenant, étant donné que status est volatile, il aurait dû être lu depuis la mémoire lorsque l'instruction if est atteinte. Je m'attendrais à voir une instruction de chargement avant de le comparer (cmp) à SUCCESS_CONST, peu importe le fait qu'il ait été assigné avec une valeur de retour de la fonction process_package_header() et stocké en mémoire, car status est volatile et aurait pu être modifié entre l'instruction str et l'instruction cmp.

Veuillez essayer d'ignorer la motivation de la condition if, son but est de tenter de détecter une attaque physique sur le CPU dans laquelle les drapeaux de condition et les registres peuvent être altérés de manière externe par un équipement physique.

Chaîne d'outils ARM DS-5_v5.27.0 compilateur ARM: ARMCompiler5.06u5 (armcc)

La cible est le processeur ARM CortexM0+

4voto

John Bollinger Points 16563

La règle principale régissant les objets volatile est la suivante, tirée du C11 6.7.3/7:

selon les règles de la machine abstraite, telles que décrites dans 5.1.2.3. De plus, à chaque point de séquence, la valeur stockée en dernier dans l'objet doit être conforme à celle prescrite par la machine abstraite, sauf si modifiée par les facteurs inconnus mentionnés précédemment.

Et cela continue en disant que

Ce qui constitue un accès à un objet de type volatile est défini par l'implémentation.

, ce qui s'applique à la manière dont d'autres règles (par exemple dans 5.1.2.3) doivent être interprétées. Le guide de l'utilisateur de votre compilateur discute des détails des accès volatils, mais il ne semble pas y avoir de surprise là-bas. La section 5.1.2.3 parle principalement des règles de séquençage; les règles d'évaluation des expressions se trouvent ailleurs (mais doivent toujours être suivies telles quelles en ce qui concerne les accès à votre objet volatile).

Voici les détails pertinents du comportement de la machine abstraite:

  1. l'opération d'assignation a pour effet secondaire de stocker une valeur dans l'objet identifié par status. Il y a un point de séquence à la fin de cette instruction, donc

    • l'effet secondaire est appliqué avant que toute évaluation apparaissant dans les instructions suivantes ne soit effectuée, et
    • comme status est volatile, l'assignation exprimée par cette ligne est la dernière écriture dans status effectuée par le programme avant le point de séquence.
  2. l'expression conditionnelle dans l'instruction if est évaluée ensuite, avec

    • la sous-expression (status) == (SUCCESS_CONST) étant évaluée en premier, avant toute autre sous-expression.
    • L'évaluation de status se fait avant l'évaluation de l'opération ==, et
    • sous forme de conversion de cet identifiant en la valeur stockée dans l'objet qu'il identifie (conversion en lvalue, selon paragraphe 6.3.2.1/2).
    • Pour faire quelque chose avec la valeur stockée dans status à ce moment-là, cette valeur doit d'abord être lue.

La norme n'exige pas qu'un objet volatile réside dans un stockage adressable, donc en principe, votre variable automatique volatile pourrait être assignée exclusivement à un registre. Dans ce cas, tant que les instructions machine utilisant cet objet lisent directement sa valeur à partir de son registre ou effectuent directement des mises à jour dans son registre, aucun chargement ou stockage distincts ne seraient nécessaires pour obtenir des sémantiques volatiles appropriées. Cependant, votre objet particulier ne semble pas entrer dans cette catégorie, car l'instruction de stockage dans votre assembleur généré semble indiquer qu'elle est en effet associée à un emplacement en mémoire.

De plus, si le programme implémente correctement les sémantiques volatiles pour un objet assigné à un registre, alors ce registre devrait être r0. Je ne suis pas familier avec les spécificités de ce langage d'assemblage et du processeur sur lequel s'exécute le code, mais il ne semble certainement pas que r0 soit un locus viable pour un tel stockage.

Dans ce cas, je suis d'accord sur le fait que status aurait dû être relu en mémoire, et il doit être relu en mémoire encore une fois si sa deuxième apparition dans l'expression conditionnelle doit être évaluée. C'est le comportement de la machine abstraite, que les implémentations conformes démontrent en ce qui concerne tous les accès volatils. Mon analyse est donc que votre implémentation est non conforme à cet égard, et je serais enclin à signaler cela comme un bug.

Quant à une solution de contournement, je pense que votre meilleur choix serait d'écrire les parties importantes en langage d'assemblage - en langage d'assemblage en ligne si votre implémentation le prend en charge, ou en tant que fonction complète implémentée en langage d'assemblage si nécessaire.

1voto

Eric Postpischil Points 36641

Le comportement décrit ne conforme pas à la norme C, sauf interprétations non conventionnelles. Si le compilateur est censé se conformer à cet égard, cela devrait être signalé comme un bogue.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X