Sécuriser les demandes numériques qui utilisent des E/S étendues

Les requêtes SD (Secure Digital) qui lisent ou écrivent plus que quelques octets de données doivent utiliser une commande d’E/S étendue (appelée CMD53 dans la spécification SD). La commande d’E/S étendue indique au pilote de bus de transmettre des données sur les lignes DAT de la carte SD. Les caractéristiques d’un transfert de données dépendent des fonctionnalités du contrôleur SD. Par exemple, certains contrôleurs autorisent uniquement les E/S programmables (PIO) ; d’autres autorisent l’accès direct à la mémoire (DMA). Pour une compatibilité maximale entre différents types de contrôleurS SD, les pilotes de périphérique doivent charger le paquet de requête avec un pointeur vers un MDL qui décrit la mémoire tampon de données. Le pilote de périphérique doit construire son propre MDL, sauf si un pilote d’une couche supérieure construit le MDL et le transmet au pilote de périphérique.

L’exemple de code suivant montre comment un pilote peut effectuer une requête d’E/S étendue à l’aide d’une mémoire tampon de données décrite par un MDL. Cet exemple de code est similaire au format de l'exemple de code d'E/S directe décrit dans Demandes sécurisées numériques qui utilisent l'E/S directe. Il peut donc être utile d'étudier l'exemple de code d'E/S directe avant d'étudier l'exemple de code d'E/S étendue.

La différence principale entre les deux exemples est que l’exemple de code d’E/S étendu illustre l’utilisation de MDLs avec une requête SD. Il existe également de légères différences dans la façon dont les descripteurs et les paquets de requête sont définis pour les E/S directes et étendues.

    const SDCMD_DESCRIPTOR WriteIoExtendedDesc =
    {SDCMD_IO_RW_EXTENDED, SDCC_STANDARD,
    SDTD_WRITE, SDTT_SINGLE_BLOCK, SDRT_1};
    
    // first, get an MDL to map the data. Call IoAllocateMdl to
    // allocate an MDL and pass in a pointer to a buffer  
    // allocated from the non-paged pool.
    
    mdl = IoAllocateMdl(Data, Length, FALSE, FALSE, NULL);
    
    if (mdl == NULL) {
      return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    MmBuildMdlForNonPagedPool (mdl);
    
    // next, allocate a request packet for the arguments of the command
     
    sdrp = ExAllocatePool(NonPagedPool, sizeof(SDBUS_REQUEST_PACKET));
    
    if (!sdrp) {
      IoFreeMdl(mdl);
      return STATUS_INSUFFICIENT_RESOURCES;
    }
    RtlZeroMemory(sdrp, sizeof(SDBUS_REQUEST_PACKET));
    sdrp->RequestFunction = SDRF_DEVICE_COMMAND;
    sdrp->Parameters.DeviceCommand.CmdDesc = 
    WriteIoExtendedDesc;
    
    // then, set up the argument and command descriptor
    sdIoArgument.u.AsULONG = 0;
    sdIoArgument.u.bits.Address = Offset;
    
    // retrieve function number, the driver previously initialized 
    // this value with the SdBus GetProperty call
    sdIoArgument.u.bits.Function = pDevExt->FunctionNumber;
    sdIoArgument.u.bits.WriteToDevice = 1;
    
    sdrp->Parameters.DeviceCommand.Argument = 
        sdIoArgument.u.AsULONG;
    
    sdrp->Parameters.DeviceCommand.Mdl = mdl;
    sdrp->Parameters.DeviceCommand.Length = Length;
    // finally, submit the request
    status = SdBusSubmitRequest(pDevExt->BusInterface.Context,sdrp);
    
    IoFreeMdl(mdl);
    ExFreePool(sdrp);

Cet exemple de code inclut les étapes suivantes :

  1. Initialiser le descripteur

    La première étape de l’envoi d’une demande de commande d’appareil consiste à définir un descripteur de commande SD, SDCMD_DESCRIPTOR. Le descripteur dans l’exemple de code définit une opération d’écriture d’E/S étendue avec les éléments suivants :

    Élément Description

    SD_COMMAND_CODE

    L’opération définie par le descripteur effectue une écriture d’E/S étendue, de sorte que la valeur du code de commande est SDCMD_IO_RW_DIRECT.

    SD_COMMAND_CLASS

    Les opérations d’écriture d’E/S étendues appartiennent au jeu de commandes standard (codes de commande 0 à 63), de sorte que la valeur affectée à ce membre du descripteur est SDCC_STANDARD.

    SD_TRANSFER_DIRECTION

    Les opérations d’écriture nécessitent un transfert de l’hôte vers l’appareil. Par conséquent, la valeur affectée à ce membre du descripteur est SDTD_WRITE.

    SD_TRANSFER_TYPE

    Le descripteur d’une opération d’écriture d’E/S étendue doit inclure un type de transfert. L’exemple de code spécifie une seule écriture de bloc, SDTT_SINGLE_BLOCK, qui indique que l’hôte écrit un bloc de données sur l’appareil. Le pilote a établi la taille d’un bloc par une commande SET_BLOCKLEN antérieure (non illustrée dans cet exemple de code). Pour une explication de la commande SET_BLOCKLEN et du type de transfert SDTT_SINGLE_BLOCK, voir la spécification The MultiMedia Card, publiée par le comité technique de la MultiMedia Card Association (MMCA).

    SD_RESPONSE_TYPE

    Le descripteur spécifie un type de réponse de SDRT_1, qui spécifie une réponse R1 standard à la commande et contient des données d’état. Pour une explication de la réponse R1, voir la spécification de la MultiMedia Card Association.

  2. Configurer le MDL

    Appelez IoAllocateMdl pour allouer une MDL et transmettez un pointeur vers un tampon alloué à partir du pool non paginé. Ensuite, la routine MmBuildMdlForNonPagedPool prend la MDL nouvellement allouée qui spécifie un tampon de mémoire virtuelle dans le pool non paginé et la met à jour pour décrire les pages physiques sous-jacentes. Les appelants de MmBuildMdlForNonPagedPool doivent être exécutés à IRQL <= DISPATCH_LEVEL.

  3. Initialisez le paquet de demande en effectuant les étapes suivantes :

    • définir la fonction de requête:

      Après avoir créé un descripteur SD, l’exemple de code initialise le paquet de requête, SDBUS_REQUEST_PACKET. Le RequestFunction membre du paquet de requête spécifie si la requête contient une commande d’appareil (valeur de SDRF_DEVICE_COMMAND) ou une opération de propriété (valeur de SDRF_GET_PROPERTY ou SDRF_SET_PROPERTY). L’exemple de code envoie une commande de périphérique, donc il définit le membre RequestFunction sur SDRF_DEVICE_COMMAND.

    • Charger le descripteur de commande. Ensuite, l’exemple de code stocke le descripteur nouvellement initialisé dans le Parameters.DeviceCommand.CmdDesc membre du paquet de requête.

    • Initialiser l'argument de lecture/écriture :

      Le paquet de requêtes contient une structure SD_RW_DIRECT_ARGUMENT avec l'emplacement dans lequel le pilote de bus écrit. Cette structure stocke également le nombre de fonctions dont l’espace d’E/S est lu par le pilote de bus. L’exemple de code récupère le numéro de fonction de l’extension d’appareil, ce qui implique que le pilote a précédemment récupéré ces informations à partir de la carte (probablement quand il a démarré l’appareil avec une demande de SDRF_GET_PROPERTY et l’a stocké dans l’extension de l’appareil.

  4. Envoyer la Demande

    Après avoir initialisé le descripteur et le paquet de requête, l’exemple utilise la routine de requête synchrone, SdBusSubmitRequest pour envoyer la demande. Il transmet le paquet de requête et les informations de contexte de l’interface fournies par le système au pilote lorsqu’il a ouvert l’interface SD. Comme il s'agit d'une requête synchrone, le pilote doit être exécuté à un IRQL inférieur à DISPATCH_LEVEL.

  5. Résultats de la commande

    Étant donné que l’exemple de code utilise une commande d’E/S directe, aucune mémoire tampon de données autre que le champ ResponseData dans le paquet de requête SD.

L'exemple de code alloue un tampon de transfert de données à partir d'un pool non paginé. Un pilote peut utiliser le PagedPool pour un buffer de transfert de données, à condition qu’il verrouille les pages. Toutefois, les pilotes doivent toujours allouer des tampons de transfert de données à partir d'un pool non paginé lorsqu'ils effectuent des requêtes SDRF_GET_PROPERTY et SDRF_SET_PROPERTY. Les pilotes doivent également allouer les paquets de requêtes SD à partir d'un pool non paginé car la routine d'achèvement de l'IRP qui accompagne la requête SD peut s'exécuter dans un appel de procédure différé (DPC).

Pour tous les types de requêtes, l'allocation de tampons à partir du pool non paginé présente des avantages en termes de performances lorsque les tampons sont petits et que le pilote les conserve brièvement.