✔ La Fabrique du MCU

Protéger son programme avec Mk

Mk offre un ensemble de mécanismes pour protéger un programme. Cet article présente la méthodologie à utiliser pour déclarer et placer ses variables en mémoire, créer des tâches privilégiées, partager des données et gérer les erreurs de permissions. Il fait directement suite à l'article précédent Le modèle de protection de Mk.

1. Déclarer et protéger ses variables

Dans un programme informatique, les variables globales et statiques sont positionnées par défaut dans les sections .bss, .data et .rodata. Mk configure ces sections de la manière suivante dans le fichier d'édition de liens :

Section Contenu Mémoire MPU Privilégié / Non privilégié Exécution
.rodata Constantes FLASH_ITCM 2 RW* / RO Autorisée
.bss Variables non initialisées privilégiées DTCM_RAM 3 RW / Aucun accès Interdite
.data Variables initialisées privilégiées DTCM_RAM 3 RW / Aucun accès Interdite

*L'attribut 'write' de la section .rodata est sans effet lorsque celle-ci est positionnée dans une mémoire FLASH.


L'extrait de code ci-dessous montre un exemple de déclaration dans chacune de ces sections :



/* Déclaration d'une variable dans la section .rodata */
const uint32_t g_rodata = 0x12345678 ;

/* Déclaration d'une variable dans la section .data */
uint32_t g_data = 5 ;

/* Déclaration de 2 variables dans la section .bss */
uint32_t g_bss1 = 0 ;
uint32_t g_bss2;

/* Une tâche privilégiée */
void mk_task_privileged ( T_mkAddr p_param )
{
   /* Déclaration d'une variable statique dans la section .data */
   static uint32_t s_data = 6 ;
   
   /* Déclaration d'une variable dans la pile de la tâche privilégiée */ 
   uint32_t l_stack = 6 ;

   /* ... */
}

Du fait des attributs de la MPU (aucun accès autorisé pour .bss et .data), les tâches non privilégiées ne peuvent pas accéder à ces variables en lecture et en écriture. Elles doivent donc déclarer leurs variables avec un attribut spécifique afin de les localiser dans la bonne section :

Section Contenu Mémoire MPU Privilégié / Non privilégié Exécution
.mk_unprivileged_bss_memory Variables non initialisées non privilégiées SRAM1 4 RW / RW Interdite
.mk_unprivileged_data_memory Variables initialisées non privilégiées SRAM1 4 RW / RW Interdite

L'extrait de code ci-dessous montre une déclaration dans les sections non privilégiées :



/* Déclaration d'une variable dans la section .mk_unprivileged_data_memory */
K_MK_UNPRIVILEGED_DATA_MEMORY uint32_t g_data = 5 ;

/* Déclaration d'une variable dans la section .mk_unprivileged_bss_memory */
K_MK_UNPRIVILEGED_BSS_MEMORY uint32_t g_bss;

/* Une tâche non privilégiée */
void mk_task_unprivileged ( T_mkAddr p_param )
{
   /* Déclaration d'une variable statique */ 
   /* dans la section .mk_unprivileged_data_memory */
   K_MK_UNPRIVILEGED_DATA_MEMORY static uint32_t s_data = 6 ;
    
   /* Déclaration d'une variable dans la pile de la tâche non privilégiée */ 
   uint32_t l_stack = 6 ;
     

   /* ... */
}

En addition, le noyau offre deux macros supplémentaires afin d'allouer un buffer DMA ou un bloc mémoire partiellement partagé (RW/RO) :

Section Contenu Mémoire MPU Privilégié / Non privilégié Exécution
.mk_privileged_dma_memory Données DMA privilégiées SRAM1 1 RW / Aucun accès Interdite
.mk_privileged_ro_memory Données privilégiées partagées en lecture SRAM2 6 RW / RO Interdite

À l'instar des sections de type .bss, ces zones mémoire sont initialisées à la valeur nulle par le code de démarrage. On les utilise de la manière suivante :



/* Déclaration d'une variable dans la section .mk_privileged_dma_memory */
/* Les accès au cache sont désactivés pour conserver une cohérence */ 
/* entre le CPU et la DMA */
K_MK_PRIVILEGED_DMA_MEMORY uint32_t g_dmaBuf [ 512 ];

/* Déclaration d'une variable dans la section .mk_privileged_ro_memory */
/* Les tâches privilégiées peuvent lire et écrire dans cette variable */
/* Les tâches non privilégiées peuvent seulement lire */
K_MK_PRIVILEGED_RO_MEMORY uint32_t g_shared;
            

Lors de l'écriture d'un programme, il est important d'avoir conscience des règles ci-dessous :


Une variable globale ou statique déclarée sans macro est placée dans les sections .bss ou .data. Elle est donc inaccessible aux tâches non privilégiées. Une tâche non privilégiée qui la manipule provoque une exception MemManageFault.


Une variable globale ou statique déclarée avec le mot clé "const" est placée dans la section .rodata. Elle n'est accessible qu'en lecture (tâches privilégiées et non privilégiées).


Une variable locale déclarée dans une fonction est placée dans la pile de la tâche qui exécute cette fonction.


Les sections .mk_unprivileged_bss_memory, .mk_privileged_ro_memory et .mk_privileged_dma_memory sont des sections initialisées à zéro. Un initialiseur non nul placé sur ces déclarations n'a aucun effet.


2. Créer des tâches privilégiées

Une tâche privilégiée est créée en utilisant la fonction mk_task_create et en spécifiant l'attribut K_MK_TYPE_PRIVILEGED lors de la configuration de la structure T_mkTaskCtrlBlock.



/* Déclaration du buffer où sera stockée la pile de la tâche */
/* Celui-ci est positionné dans une zone privilégiée */
K_MK_PRIVILEGED_BSS_MEMORY uint32_t g_privilegedStackBuf [ 512 ];

/* Une tâche privilégiée quelconque */
void mk_task_myPrivilegedTask ( T_mkAddr p_param )
{
   /* Déclaration de la variable de retour */
   T_mkCode l_result;

   /* Déclaration d'un pointeur de tâche */
   T_mkTask* l_task;

   /* Déclaration d'une structure contenant les informations de la stack */
   T_mkStack l_stack;

   /* Déclaration d'une structure contenant les informations de la tâche */
   T_mkTaskCtrlBlock l_taskCtrlBlock;

   /* Configuration de la structure l_stack avec les attributs de la pile */
   l_result = mk_stack_create ( &l_stack, K_MK_TYPE_DEFAULT, 
      g_privilegedStackBuf, 512 );

   /* Configuration de la structure l_taskCtrlBlock */
   /* Type : tâche privilégiée non flottante */
   /* Pour une tâche privilégiée flottante : K_MK_TYPE_FLOATING_PRIVILEGED */
   l_result |= mk_task_setTaskCtrlBlock ( &l_taskCtrlBlock,
      K_MK_TYPE_PRIVILEGED, K_MK_TASK_ID_MY_TASK,
      10, K_MK_NULL );

   /* Création de la tâche */
   /* Point d'entrée de la tâche : mk_task_privileged */
   /* Argument de la fonction : "Hello world !" */
   l_result |= mk_task_create ( &l_task, &l_stack, K_MK_NULL,
      &l_taskCtrlBlock, mk_task_privileged, ( T_mkAddr ) 
      "Hello world !" );
}

Une tâche privilégiée ne peut être créée que depuis une autre tâche privilégiée, un vecteur d'interruption ou depuis le programme principal. Lorsqu'une tâche privilégiée est créée, elle s'exécute avec le bit nPRIV positionné à 0, elle peut donc accéder à toutes les ressources du système sauf restriction spéciale imposée par la MPU.


À l'instar des tâches non privilégiées, les tâches privilégiées utilisent aussi le pointeur de pile PSP. Leur contexte reste protégé par la MPU.

3. Créer des tâches non privilégiées

Une tâche non privilégiée peut être créée en spécifiant l'attribut K_MK_TYPE_DEFAULT. Elle s'exécute avec le bit nPRIV positionné à 1 et n'accède qu'aux ressources non privilégiées autorisées par la MPU.



/* Déclaration du buffer où sera stockée la pile de la tâche */
/* Celui-ci est positionné dans une zone non privilégiée */
K_MK_UNPRIVILEGED_BSS_MEMORY uint32_t g_unprivilegedStackBuf [ 512 ];

/* ... */

/* Une tâche non privilégiée quelconque */
void mk_task_myUnprivilegedTask ( T_mkAddr p_param )
{   
   /* ... */
   
   /* Configuration de la structure l_stack avec les attributs de la pile */
   l_result = mk_stack_create ( &l_stack, K_MK_TYPE_DEFAULT, 
      g_unprivilegedStackBuf, 512 );
      
   /* Configuration de la structure l_taskCtrlBlock */
   /* Type : tâche non privilégiée non flottante */
   /* Pour une tâche non privilégiée flottante : K_MK_TYPE_FLOATING */
   l_result |= mk_task_setTaskCtrlBlock ( &l_taskCtrlBlock,
      K_MK_TYPE_DEFAULT, K_MK_TASK_ID_MY_TASK,
      10, K_MK_NULL );

   /* Création de la tâche */
   /* Point d'entrée de la tâche : mk_task_unprivileged */
   /* Argument de la fonction : "Hello world !" */
   l_result |= mk_task_create ( &l_task, &l_stack, K_MK_NULL,
      &l_taskCtrlBlock, mk_task_unprivileged, ( T_mkAddr ) 
      "Hello world !" );
}


Une tâche non privilégiée peut être créée par n'importe quel type de tâche, depuis un vecteur d'interruption ou encore depuis le programme principal.

4. Partager des données

Dans l'écosystème Mk, les boîtes de messages peuvent être utilisées pour partager des données entre les tâches.

Pour ce faire, une tâche, le poster, transmet au noyau un buffer de données source qui est copié dans un emplacement de la boîte de messages. Le message est alors à disposition. Une autre tâche, le pender, peut venir le récupérer en demandant au noyau de le copier dans un buffer de destination.

Les boîtes de messages permettent à n'importe quel type de tâche de communiquer. Néanmoins, lorsqu'une donnée doit transiter du monde privilégié au monde non privilégié, la boîte doit être déclarée avec l'attribut K_MK_AREA_UNPROTECTED.

Cet attribut autorise la boîte de messages à être manipulée par une tâche non privilégiée.

L'extrait de code ci-dessous montre un exemple d'utilisation :



/* Déclaration du buffer de la boîte de messages */
/* Celui-ci n'est pas accessible à la tâche non privilégiée */
/* Taille: 4 messages 32 bits */
K_MK_PRIVILEGED_BSS_MEMORY uint32_t g_mailBuf [ 4 ];

/* Déclaration d'un pointeur de boîte de messages */
/* Celui-ci doit être accessible en lecture à la tâche non privilégiée */
K_MK_PRIVILEGED_RO_MEMORY T_mkMail* g_mail;

/* Code de la tâche privilégiée */
void mk_task_privileged ( T_mkAddr p_param )
{
   /* Déclaration de la variable de retour */
   T_mkCode l_result = K_MK_OK;

   /* Déclaration d'une structure de type T_mkPoolArea */
   /* Cette structure est utilisée pour créer la boîte de messages */
   T_mkPoolArea l_area;

   /* Déclaration du message à transmettre à la tâche non privilégiée */
   uint32_t l_incomingMessage = 1;

   /* Suppression warning */
   ( void ) p_param;

   /* Création de la boîte de messages */
   /* On crée 4 messages de 32 bits chacun */
   /* La boîte n'est pas protégée, elle peut être utilisée par une tâche */
   /* non privilégiée */
   l_result = mk_pool_initArea ( &l_area, g_mailBuf, 4 );
   l_result |= mk_mail_create ( &g_mail, 
      K_MK_AREA_UNPROTECTED, &l_area, 1, 4 ); 

   /* Boucle pour toujours... */
   while ( 1 )
   {
      /* Écriture d'un message dans la messagerie */
      l_result |= mk_mail_post ( g_mail, &l_incomingMessage, 
         K_MK_STATE_READY, K_MK_TASK_WAIT_FOREVER );

      /* Temporisation de la tâche */
      l_result |= mk_task_sleep ( 10 );            
   }

   /* Retour */
   return;
}

/* Code de la tâche non privilégiée */
void mk_task_unprivileged ( T_mkAddr p_param )
{
   /* Déclaration de la variable de retour */
   T_mkCode l_result = K_MK_OK;

   /* Déclaration de la variable où sera stocké le message reçu */
   uint32_t l_outgoingMessage = 0;

   /* Suppression warning */
   ( void ) p_param;

   /* Boucle pour toujours... */
   while ( 1 )
   {
      /* Récupération d'un message dans la messagerie */
      /* La variable l_outgoingMessage passe à 1 */
      l_result |= mk_mail_pend ( K_MK_NULL, g_mail, 
         &l_outgoingMessage, K_MK_TASK_WAIT_FOREVER );

      /* Temporisation de la tâche */
      l_result |= mk_task_sleep ( 10 );            
   }

   /* Retour */
   return;
}

            

La communication par boîte de messages entre des tâches privilégiées et non privilégiées peut se dérouler dans les deux sens. Dans l'exemple ci-dessus, la tâche privilégiée poste le message et la tâche non privilégiée le récupère. L'organisation est donc la suivante :

  • ✔Le buffer source l_incomingMessage est situé dans la pile de mk_task_privileged inaccessible aux tâches non privilégiées.
  • ✔La boîte de messages et son pointeur (g_mail) sont situés dans la zone privilégiée accessible seulement en lecture aux tâches non privilégiées. Le buffer de la boîte (g_mailBuf) est situé dans une zone inaccessible.
  • ✔Le buffer de destination l_outgoingMessage est situé dans la pile de mk_task_unprivileged accessible aux tâches privilégiées et non privilégiées.

La figure ci-dessous présente une représentation visuelle de cette configuration :

organisation mémoire d'un transfert privilégié par
                  boîte de
                  messages.
Figure 1 : organisation mémoire d'un transfert privilégié par boîte de messages.

Si la communication se déroule dans le sens inverse, c'est-à-dire que la tâche non privilégiée poste la donnée pendant que la tâche privilégiée la récupère, le buffer de la boîte de messages doit obligatoirement se situer dans la zone non privilégiée. Dans le cas contraire, le noyau détecte une écriture non autorisée et rejette l'appel système.



/* Déclaration du buffer de la boîte de messages */
/* Celui-ci doit obligatoirement être dans une zone non privilégiée */
/* Taille: 4 messages de 32 bits */
K_MK_UNPRIVILEGED_BSS_MEMORY uint32_t g_mailBuf [ 4 ];

/* Déclaration d'un pointeur de boîte de messages */
/* Celui-ci doit être accessible en lecture et en écriture */
/* à la tâche non privilégiée */
K_MK_UNPRIVILEGED_BSS_MEMORY T_mkMail* g_mail;

/* Code de la tâche non privilégiée */
void mk_task_unprivileged ( T_mkAddr p_param )
{
   /* Déclaration de la variable de retour */
   T_mkCode l_result = K_MK_OK;

   /* Déclaration d'une structure de type T_mkPoolArea */
   /* Cette structure est utilisée pour créer la boîte de messages */
   T_mkPoolArea l_area;

   /* Déclaration du message à transmettre à la tâche privilégiée */
   uint32_t l_incomingMessage = 1;

   /* Suppression warning */
   ( void ) p_param;

   /* Création de la boîte de messages */
   /* On crée 4 messages de 32 bits chacun */
   /* La boîte n'est pas protégée, elle peut être utilisée par une tâche */
   /* non privilégiée */
   l_result = mk_pool_initArea ( &l_area, g_mailBuf, 4 );
   l_result |= mk_mail_create ( &g_mail, 
      K_MK_AREA_UNPROTECTED, &l_area, 1, 4 ); 

   /* Boucle pour toujours... */
   while ( 1 )
   {
      /* Écriture d'un message dans la messagerie */
      l_result |= mk_mail_post ( g_mail, &l_incomingMessage, 
         K_MK_STATE_READY, K_MK_TASK_WAIT_FOREVER );

      /* Temporisation de la tâche */
      l_result |= mk_task_sleep ( 10 );            
   }

   /* Retour */
   return;
}

/* Code de la tâche privilégiée */
void mk_task_privileged ( T_mkAddr p_param )
{
   /* Déclaration de la variable de retour */
   T_mkCode l_result = K_MK_OK;

   /* Déclaration de la variable où sera stocké le message reçu */
   uint32_t l_outgoingMessage = 0;

   /* Suppression warning */
   ( void ) p_param;

   /* Boucle pour toujours... */
   while ( 1 )
   {
      /* Récupération d'un message dans la messagerie */
      /* La variable l_outgoingMessage passe à 1 */
      l_result |= mk_mail_pend ( K_MK_NULL, g_mail, 
         &l_outgoingMessage, K_MK_TASK_WAIT_FOREVER );

      /* Temporisation de la tâche */
      l_result |= mk_task_sleep ( 10 );            
   }

   /* Retour */
   return;
}
            

Dans l'exemple ci-dessus, l'organisation mémoire est la suivante :

  • ✔Le buffer source l_incomingMessage est situé dans la pile de mk_task_unprivileged qui est accessible à toutes les tâches.
  • ✔La boîte de messages est située dans la zone privilégiée qui n'est accessible qu'en lecture pour les tâches non privilégiées. Le buffer (g_mailBuf) et le pointeur (g_mail) de boîte sont accessibles à tous les types de tâches.
  • ✔Le buffer de destination l_outgoingMessage est situé dans la pile de mk_task_privileged inaccessible aux tâches non privilégiées.

La figure ci-dessous présente une représentation visuelle de cette configuration :

organisation mémoire d'un transfert non privilégié par
                  boîte de messages.
Figure 2 : organisation mémoire d'un transfert non privilégié par boîte de messages.

Lorsqu'une tâche non privilégiée poste un message, le buffer source doit obligatoirement être situé en zone non privilégiée (.mk_unprivileged_bss_memory et .mk_unprivileged_data_memory). Dans le cas contraire, l'appel système sera rejeté par le noyau. Le but étant de ne pas recopier le contenu d'une mémoire protégée en lecture dans un emplacement non privilégié.

De la même manière, lorsqu'une tâche non privilégiée récupère un message (pend), son buffer de destination doit aussi être situé dans une zone non privilégiée.


Lorsque l'application le permet, c'est-à-dire lorsque les données sont produites par la tâche privilégiée et consommées par la tâche non privilégiée, il est préférable de créer la boîte de messages au sein de la tâche privilégiée.


Cette configuration permet de protéger l'ensemble des données contre les écritures intempestives venant d'une tâche non privilégiée. En effet, dans la situation où le buffer de la boîte est en zone non privilégiée, n'importe quelle tâche non privilégiée peut venir modifier un message entre un post et un pend. Il est donc important que la tâche privilégiée traite le contenu des messages comme non fiable et réalise des contrôles supplémentaires.


Les boîtes de messages permettent le partage de données entre le monde privilégié et le monde non privilégié. Cependant, une mauvaise configuration de celles-ci peut briser la barrière qui les sépare.


Lors de sa création, une boîte de messages est configurée avec une taille de messages. À chaque lecture de la boîte (pend), le noyau copie un message de cette taille dans le buffer fourni par l'utilisateur.

Lorsque l'appelant est une tâche non privilégiée, le noyau vérifie que toute la plage copiée se situe dans la bonne zone mémoire. Pour les tâches privilégiées, ce contrôle n'est pas réalisé. Un buffer trop petit côté privilégié permet donc à une tâche non privilégiée d'écraser la mémoire voisine, ouvrant ainsi une brèche de sécurité.

Un comportement analogue s'applique lors de l'écriture d'un message (post) par une tâche privilégiée. Le noyau ne connaissant pas la taille de la variable source, des segments de mémoire normalement inaccessibles aux tâches non privilégiées peuvent être copiés dans la boîte et rendus publics lorsque le buffer est trop petit.


Les buffers l_incomingMessage et l_outgoingMessage d'une tâche privilégiée doivent avoir une taille au moins égale à celle des messages de la boîte. Dans le cas contraire, le noyau copie, lors d'un post, la mémoire située après l_incomingMessage dans la boîte, et écrase, lors d'un pend, la mémoire située après l_outgoingMessage.


5. Gérer les erreurs

Les erreurs peuvent être gérées par Mk de plusieurs manières. La première consiste à utiliser le code de retour des fonctions. Chaque fonction qui réalise un appel système retourne une valeur indiquant à la tâche appelante la réussite ou l'échec de l'appel système. Lorsque la valeur K_MK_ERROR_RIGHT est renvoyée, le noyau signale un défaut de permissions :



/* Récupération d'un message dans la messagerie */
l_result = mk_mail_pend ( K_MK_NULL, g_mail, 
   &l_outgoingMessage, K_MK_TASK_WAIT_FOREVER );

/* Si aucune erreur ne s'est produite */
/* L'appel système a réussi, on continue le programme... */
if ( l_result == K_MK_OK )
{
   /* Faire quelque chose ici... */
}

/* Sinon si la tâche ne possède pas les permissions suffisantes */
else if ( l_result == K_MK_ERROR_RIGHT )
{
   /* Faire quelque chose ici... */
}

/* Gestion des autres cas d'erreurs ici */
/* ... */

/* Sinon */
else
{
   /* Faire quelque chose ici... */
}
            

Le noyau diffuse aussi ces erreurs par le biais des fonctions de rappel mk_handler_handleFault et mk_handler_rightFault.

Ces fonctions sont exécutées par le noyau en mode privilégié de suite après la détection de l'erreur. Leur corps est vide par défaut mais elles peuvent être implémentées par l'utilisateur en utilisant la fonction mk_handler_set.


/* On indique au noyau les fonctions à utiliser lorsque les erreurs */
/* se produisent. Si ces fonctions ne sont pas configurées, */
/* le noyau exécute les fonctions par défaut */
T_mkKernelCallback l_kernelCallbackHandler = { K_MK_NULL };
l_kernelCallbackHandler.rightFault = mk_rightFault;
l_kernelCallbackHandler.handleFault = mk_handleFault;
l_kernelCallbackHandler.memFault = mk_memFault;

/* Référencement des fonctions de rappel dans le noyau */
mk_handler_set ( &l_kernelCallbackHandler );

L'exécution d'appels système dans ces fonctions est interdite du fait de leur niveau d'exécution. La suspension de la tâche fautive doit donc être déléguée au vecteur d'interruption MemManageFault en utilisant la fonction nvic_setPendingException.


void mk_rightFault ( 
   T_mkAddr p_param1, T_mkAddr p_param2, T_mkAddr p_param3 )
{
   /* Fonction exécutée lorsque les permissions de la tâche */
   /* appelante ne sont pas suffisantes */ 

   /* Ces 3 paramètres ont toujours la valeur K_MK_NULL */
   /* pour cette callback */ 
   ( void ) p_param1, ( void ) p_param2, ( void ) p_param3;
   
   /* L'interruption matérielle MemManageFault sera déclenchée */
   /* après la fin de l'appel système */
   nvic_setPendingException ( K_NVIC_PENDING_MEMFAULT );
   
   return;
}

void mk_handleFault ( 
   T_mkAddr p_param1, T_mkAddr p_param2, T_mkAddr p_param3 )
{
   /* Fonction exécutée lorsque l'instance de la tâche ou de l'objet */
   /* de synchronisation est invalide */ 
   
   /* Ces 3 paramètres ont toujours la valeur K_MK_NULL */
   /* pour cette callback */ 
   ( void ) p_param1, ( void ) p_param2, ( void ) p_param3;
   
   /* L'interruption matérielle MemManageFault sera déclenchée */
   /* après la fin de l'appel système */
   nvic_setPendingException ( K_NVIC_PENDING_MEMFAULT );
   
   return;
}

En plus des appels explicites réalisés par les fonctions ci-dessus, le vecteur d'interruption MemManageFault est aussi exécuté lorsqu'une violation d'accès mémoire est détectée par la MPU. C'est dans ce gestionnaire que la tâche fautive peut être suspendue.


void mk_memFault ( 
   T_mkAddr p_param1, T_mkAddr p_param2, T_mkAddr p_param3 )
{
   /* Fonction exécutée explicitement (handleFault, rightFault) */  
   /* ou lors d'une violation d'accès mémoire */ 

   /* Ces 3 paramètres ont toujours la valeur K_MK_NULL */
   /* pour cette callback */ 
   ( void ) p_param1, ( void ) p_param2, ( void ) p_param3;
   
   /* Les registres CFSR ou MMFAR peuvent être lus pour déterminer */
   /* la source de la violation mémoire. */

   /* La tâche fautive est la tâche courante. */
   /* On peut récupérer son adresse directement dans le scheduler */
   if ( g_mkScheduler.currentTask != K_MK_NULL )
   {
      /* Suspension de la tâche */
      ( void ) mk_task_suspend ( g_mkScheduler.currentTask );
   }

   /* Si l'adresse est nulle, une erreur grave s'est produite */
   else 
   {
      /* Faire quelque chose ici (reset du CPU, blocage, ...) */
   }
  
   /* Fin du contexte d'interruption */
   return;
}


Ce vecteur d'interruption est exécuté à la priorité K_MK_SCHEDULER_MASK_PRIORITY. Par conséquent, l'ensemble des appels système utilisables depuis une ISR est autorisé.

Lorsque ce vecteur n'est pas implémenté avec la fonction mk_handler_set et qu'une violation mémoire se produit, le noyau bloque son exécution par le biais d'une boucle infinie. Le but étant de stopper le déclenchement intempestif de l'exception MemManageFault qui continue tant que la tâche fautive n'a pas été arrêtée.

6. Conclusion

Le modèle de protection de Mk repose sur le placement des données dans la mémoire et sur le contrôle des permissions lors des appels système.

Correctement mis en œuvre, ce modèle permet de maintenir la séparation entre le monde privilégié et le monde non privilégié. Néanmoins, une mauvaise utilisation de l'API n'est pas sans risque. Elle peut mener à l'ouverture d'une brèche de sécurité, à une élévation de privilège ou à un arrêt complet du système.

L'emplacement et la taille des buffers, l'attribut des boîtes de messages et le traitement des erreurs doivent donc être choisis avec rigueur.

<

Le modèle de protection de Mk

Les allocateurs mémoires de taille fixe

>