View in English

  • Global Nav Open Menu Global Nav Close Menu
  • Apple Developer
Search
Cancel
  • Apple Developer
  • News
  • Discover
  • Design
  • Develop
  • Distribute
  • Support
  • Account
Only search within “”

Quick Links

5 Quick Links

Vidéos

Ouvrir le menu Fermer le menu
  • Collections
  • Sujets
  • Toutes les vidéos
  • À propos

Retour à WWDC25

  • À propos
  • Résumé
  • Transcription
  • Code
  • Mixez en toute sécurité C, C++ et Swift

    Découvrez comment combiner C, C++ et Swift tout en améliorant la sécurité de vos apps. Nous allons vous montrer comment trouver où les API C et C++ non sécurisées sont appelées dans votre code Swift, comment les appeler de manière plus sûre et comment rendre le code C et C++ existant de votre application plus sûr par défaut.

    Chapitres

    • 0:00 - Introduction
    • 2:39 - Trouver un appel dangereux en Swift
    • 4:55 - Appel de C/C++ en toute sécurité
    • 7:25 - Fonctions prenant des pointeurs
    • 17:13 - Fonctions renvoyant des pointeurs
    • 20:16 - Importation de types personnalisés
    • 26:57 - Amélioration de la sécurité de C/C++
    • 30:48 - Conclusion

    Ressources

    • -fbounds-safety: Enforcing bounds safety for C
    • Safely Mixing Swift and C++
      • Vidéo HD
      • Vidéo SD

    Vidéos connexes

    WWDC25

    • Améliorer l’utilisation de la mémoire et les performances avec Swift
  • Rechercher dans cette vidéo…

    Bonjour, je suis Yeoul. Je suis responsable de l’équipe Secure Language Extension chez Apple. Lors de la création d’une app, il faut avant tout assurer sa sécurité. Cela signifie protéger les données privées des utilisateurs contre d’éventuelles attaques. Ces acteurs malveillants exploitent souvent des failles dans du code écrit en C ou C++, des langages peu sécurisés. Bonne nouvelle : si votre app utilise déjà Swift, ce langage est sécurisé par défaut. Excellente nouvelle !

    Mais même si votre nouveau code est écrit en Swift, votre app peut encore contenir du code C ou C++ hérité des anciennes versions. Ou, il peut s’appuyer sur des bibliothèques externes. Lorsque vous combinez ces langages, il est important de préserver les garanties de sécurité de Swift. Le problème c’est que les fonctions C et C++ qui utilisent des pointeurs bruts sont très difficiles à appeler en toute sécurité. Un appel incorrect peut causer des bogues de sécurité, comme des dépassements de tampon ou des accès après libération.

    C’est pourquoi les pointeurs C et C++ sont importés comme types non sécurisés en Swift. Un pointeur vers un entier en C est importé comme un UnsafeMutablePointer. Swift met volontairement unsafe dans le nom du type. Ainsi, je sais que je ne peux pas compter sur les garanties de sécurité habituelles en appelant cette fonction. Mais, je sais qu’il est possible d’appeler certaines fonctions en toute sécurité depuis Swift. Mais jusqu’ici, C et C++ n’avaient aucun moyen d’indiquer comment le faire. C’est le sujet de cette présentation.

    Je vais présenter une nouvelle fonctionnalité, Strict Memory Safety, qui détecte les appels non sécurisés en Swift.

    J’expliquerai comment annoter les fonctions C et C++ pour transmettre les infos manquantes pour que Swift les appelle en toute sécurité. J’expliquerai comment annoter des types C++ personnalisés pour une importation sécurisée. Enfin, je présenterai des outils pour renforcer la sécurité du code C et C++, même s’ils ne seront jamais aussi sûrs que Swift.

    Swift 6.2 a une fonctionnalité pour détecter les appels à des fonctions C et C++ non sécurisées. Je vais vous la montrer avec une app que j’ai créée, où les utilisateurs partagent des photos de leurs animaux. Cette app a accès à des infos très sensibles, comme le prénom du chien, donc je veux qu’elle reste sécurisée. Elle est écrite en Swift. Elle fait aussi appel à du code C et C++ pour appliquer des filtres d’image personnalisés. Je veux repérer tous les appels non sécurisés au code C et C++ dans l’app pour les sécuriser. Le plus dur, c’est de repérer ces appels. Swift est sûr par défaut, mais il autorise les constructions non sécurisées, surtout en interopérabilité avec le C et le C++. Par exemple, en coulisses, imageData crée un UnsafeMutablePointer, ce qui, comme son nom l’indique, n’est pas sécurisé. Mais ce n’est pas si évident à voir quand je lis ce code. Pour détecter tous les appels non sécurisés, j’utilise le nouveau mode Strict Memory Safety de Swift 6.2. Lorsque ce mode est activé, le compilateur signale tout code non sécurisé et explique pourquoi.

    Strict Memory Safety n’est pas activé par défaut, mais comme mon app est sensible à la sécurité, je vais l’activer sans hésiter. Voyons ça dans Xcode.

    Je vais activer Strict Memory Safety dans les réglages de build de mon projet pour l’utiliser.

    Lorsque je reconstruis, le compilateur m’avertit des constructions non sécurisées.

    Je vois apparaître de nouveaux avertissements. La plupart de ce code non sécurisé implique des pointeurs C et C++.

    Je vais vous montrer comment appeler ces fonctions avec des pointeurs en toute sécurité depuis Swift.

    Mais, voyons pourquoi il est si difficile d’utiliser les pointeurs C et C++ en toute sécurité.

    Les pointeurs sont des outils puissants. Ils permettent d’accéder efficacement à la mémoire sans avoir à la copier. Mais ils sont difficiles à utiliser en toute sécurité. Le vrai problème, c’est que le C et le C++ n’aident pas les développeurs à éviter les erreurs. Par exemple, rien ne m’empêche d’utiliser par erreur une zone mémoire après sa libération ou de dépasser les limites d’un tampon.

    Bonne nouvelle. Swift 6.2 introduit un nouveau type de pointeur sécurisé appelé Span qui offre les avantages des pointeurs tout en évitant automatiquement ces erreurs. Il agit comme un pointeur, mais avec la sécurité intégrée. Swift vous empêche de faire des opérations non sécurisées avec lui. Si vous devez modifier la mémoire, il existe aussi un MutableSpan. Pour en savoir plus sur Span, regardez « Improved memory usage and performance with Swift ». Ce serait idéal que Swift importe les pointeurs C et C++ en Spans et non en pointeurs non sécurisés.

    Malheureusement, il manque deux infos clés au compilateur pour le faire en toute sécurité. Sans infos du C++ sur les limites d’un pointeur, Swift ne peut pas empêcher les accès hors limites. Et sans infos de C++ sur la durée de vie du pointeur, Swift ne peut pas empêcher son utilisation après sa libération. L’idée clé, c’est que si le développeur fournit ces infos manquantes, Swift peut traiter un pointeur non sécurisé comme un Span.

    Dans Swift 6.2, je peux fournir ces infos manquantes au compilateur en ajoutant des annotations à mon code C et C++. Cela ne change en rien le fonctionnement du code. Cela rend simplement ses hypothèses explicites. Cela permet à Swift d’appeler en toute sécurité du code C et C++ utilisant des pointeurs. Voyons comment annoter les fonctions qui prennent ou renvoient des pointeurs. Pour cela, revenons à mon app.

    Le 1er avertissement concerne l’utilisation de la fonction invertImage qui prend un pointeur brut.

    Je vais vous montrer comment appeler en toute sécurité ce type de fonctions, qui prennent des pointeurs en paramètre.

    Comme indiqué, les pointeurs bruts n’ont pas d’infos sur leurs limites, donc impossible de vérifier s’ils accèdent bien à la bonne zone mémoire. Mal utilisés, ils peuvent entraîner des erreurs mémoire hors limites. Voici un exemple. La fonction InvertImage est appelée depuis Swift. La fonction prend un pointeur brut vers l’image, et la taille est fournie séparément via d’autres paramètres.

    Mais comme le pointeur image est brut, rien ne m’empêche de passer par erreur une taille trop grande. Et dans ce cas, la fonction lira et écrira au-delà des limites du tampon. C’est justement l’un des problèmes que Span permet de résoudre. Imaginez que invertImage soit importée comme une fonction Swift prenant un Span. Je pourrais passer un Span directement, au lieu d’un pointeur brut et d’une taille séparément. Cela me protégerait automatiquement d’erreurs comme une mauvaise taille, car les Spans incluent toujours les bonnes infos de limites mémoire. En coulisses, le compilateur se charge de décomposer le Span, d’en extraire le pointeur et la taille, puis de les transmettre à la fonction C. Ainsi, il n’y a plus de place pour les erreurs.

    Le compilateur peut le faire, mais il lui manque le lien entre le pointeur brut et la taille. La fonction invertImage suppose que le pointeur fait référence à un tampon de imageSize éléments, mais cette supposition reste implicite. Je dois rendre cette relation explicite pour qu’elle soit claire à la fois pour les développeurs et le compilateur. Cela peut être exprimé avec l’annotation counted_by. L’annotation indique au compilateur combien d’éléments se trouvent en mémoire à l’adresse pointée. Le pointeur a aussi besoin d’une autre annotation, appelée noescape, pour compenser l’absence d’infos sur sa durée de vie. Mais vous pouvez l’ignorer pour l’instant. J’y reviendrai plus tard. Une fois ces infos ajoutées, je peux appeler la fonction en toute sécurité depuis Swift en passant simplement un Span. Ensuite, le compilateur s’occupe automatiquement du reste. Ainsi, les erreurs ne sont même pas possibles. Je reviens à ma fonction invertImage.

    J’ajoute des annotations counted_By et noescape sur la fonction invertImage.

    Et je passe à la déclaration et j’ajoute les mêmes annotations.

    Je retourne au point d’appel dans le code Swift.

    Je passe directement imageData en tant que Span pour appeler la fonction depuis Swift.

    Il n’y a plus de pointeur non sécurisé, donc l’avertissement a disparu.

    L’avertissement suivant concerne la fonction applyGrayScale. Il indique que la fonction utilise un type C++ non sécurisé.

    Jetons un œil à la définition C++ de cette fonction. Comme son nom l’indique, applyGrayScale applique un effet de niveaux de gris à l’image d’entrée. La fonction prend une vue de l’image, qui est de type Span en C++. Jusqu’ici, nous avons parlé des Spans en Swift. Mais C++ a aussi sa propre notion de Span. On voit cet avertissement parce que Swift considère les Span C++ comme non sécurisés, même s’ils cherchent à résoudre le même problème. Comme les Spans Swift, les Spans C++ sont un type standard pour accéder à une mémoire contiguë appartenant à un autre propriétaire. Ils contiennent un pointeur vers cette mémoire et sa taille. Comme les Spans C++ connaissent leur taille, ils peuvent vérifier en toute sécurité les accès hors limites, tout comme les Spans Swift. Contrairement aux Spans Swift, les Spans C++ n’ont pas d’infos sur la durée de vie, et ne peuvent pas empêcher l’accès à la mémoire allouée. Utiliser un Span C++ peut provoquer un bug d’accès après libération.

    Voici un exemple. Imaginez que la fonction ApplyGrayScale prenne un Span C++ nommé ImageView, qui pointe vers un tableau créé par Swift.

    Dans applyGrayScale, je peux stocker ce pointeur dans une variable globale comme cachedView pour qu’il soit réutilisé ailleurs.

    Mais voici le problème. Une fois la fonction terminée, Swift peut libérer le tableau, pensant qu’il n’est plus utilisé. Le code C++ conserve alors un pointeur vers une mémoire non valide. C’est un pointeur pendu qui provoque un bug d’accès après libération.

    En revanche, un Span Swift est sécurisé. Il n’est pas autorisé à survivre à la mémoire vers laquelle il pointe. Si une fonction prend un Span Swift, elle ne peut l’utiliser qu’à l’intérieur de cette fonction. Elle ne peut pas le conserver, par exemple dans un cachedView pour un usage ultérieur. Ce comportement, quand un pointeur est conservé après la fin de la fonction, s’appelle « escaping ». Le compilateur signale une erreur chaque fois qu’un Span tente de sortir de sa portée.

    Ainsi, les Spans Swift empêchent les pointeurs pendus et évitent les bugs d’accès après libération.

    Un Span C++ n’offre pas les mêmes garanties. Pour l’utiliser en sécurité, je dois vérifier manuellement que la fonction ne conserve pas le paramètre imageView afin d’éviter un pointeur pendu.

    Une fois que j’ai vérifié que le paramètre ne sort pas de la fonction C++, je dois indiquer cette info dans la définition de la fonction. Cela permet aux développeurs et au compilateur de comprendre le comportement de la fonction. L’ajout de l’annotation noescape m’aide à le faire. L’annotation noescape peut aussi s’appliquer aux pointeurs bruts et aux références.

    À partir de Swift 6.2, un paramètre Span C++ annoté avec noescape peut être traité comme un Span Swift. Cela signifie que je peux désormais passer un Span Swift directement à applyGrayscale, ce qui est pratique et supprime le besoin de code non sécurisé et répétitif. Appeler une fonction C++ c’est vraiment sûr et simple.

    Je vais passer à la définition de applyGrayscale et ajouter l’annotation noescape sur le paramètre imageView et sur la déclaration.

    Je retourne au point d’appel dans le code Swift.

    Je peux supprimer l’accès temporaire au pointeur mutable non sauvegardé et passer directement le Span Swift extrait de imageData.

    L’avertissement a disparu puisque j’appelle applyGrayscale en toute sécurité.

    Le prochain avertissement concerne l’utilisation de scanImageRow, qui prend un Span C++ et en retourne un autre.

    Comment sécuriser l’appel d’une fonction qui retourne un pointeur comme un Span C++ ?

    Retourner un Span C++ peut être risqué car il ne vérifie pas si la mémoire vers laquelle il pointe est toujours valide. Voici un exemple. ScanImageRow prend imageView comme Span C++ et retourne un autre Span C++ pointant vers une ligne sélectionnée dans imageData. Une fois la fonction terminée, les données sont libérées. Mais le Span C++ retourné pointe toujours vers cette mémoire, cela crée un pointeur pendu. Y accéder provoquera un bug d’accès après libération. Ce bug n’existerait pas si la valeur retournée était traitée comme un Span Swift plutôt qu’un Span C++. En effet, le compilateur n’autorise pas le retour d’un Span Swift sans être sûr que la mémoire retournée est toujours valide et donc sûre à utiliser. Quand est-il réellement sûr d’utiliser le Span retourné ? Il pointe vers une partie de la même mémoire que le paramètre imageView. Cela signifie qu’il est valide uniquement tant que imageView l’est.

    Cette relation s’appelle un lifetimebound.

    C’est cette info manquante que Swift doit connaître pour importer un Span C++ retourné comme un Span Swift. Je peux exprimer cela avec l’annotation lifetimebound. Cela permet au compilateur de garantir une utilisation sûre.

    Je vais passer à la définition de scanImageRow.

    Je vais ajouter l’annotation lifetimebound au paramètre imageView de la fonction scanImageRow.

    Et je ferai de même sur la déclaration.

    Avec l’annotation lifetimebound, la fonction peut maintenant recevoir un Span Swift et en retourner un autre. Je reviens au point d’appel dans Swift.

    Je peux supprimer l’accès au pointeur non sécurisé et passer directement le Span Swift.

    La fonction renvoie désormais un Span Swift au lieu d’un pointeur non sécurisé. Une fois recompilé, l’avertissement disparaît.

    Les risques liés aux appels C et C++ sont résolus et tous les avertissements ont disparu. Jusqu’ici, j’ai expliqué comment traiter les pointeurs C et C++ comme des Spans Swift. D’autres types idiomatiques C++ peuvent être importés et utilisés en toute sécurité dans Swift grâce aux annotations. Les types personnalisés de vues et à comptage de références. D’abord, nous verrons comment importer des types personnalisés de vues C++. Un type de vue est une structure avec un pointeur vers une mémoire qu’elle ne possède pas. Cela signifie que Span Swift est aussi un type de vue.

    Voyons de plus près ce qui rend vraiment les Spans Swift sécurisés. En coulisses, les Spans sont marqués comme un type Swift spécial, nonescapable. Les types nonescapable servent souvent à voir la mémoire d’un autre type sans en faire une copie. Comme avec Span, Swift s’assure que tous les types non-escapable restent dans leur contexte actuel. Cela garantit qu’ils ne dépassent pas la durée de vie de la mémoire, évitant ainsi les bugs d’accès après libération. Les types de vue C++ peuvent être importés en toute sécurité comme non-escapable dans Swift. Il me suffit d’ajouter une annotation.

    Voici un exemple. Mon app utilise une structure C++ personnalisée, ImageView, contenant la largeur, la hauteur et un pointeur vers les pixels de l’image. imageView ne possède pas ces données pixels. Ces données appartiennent à un autre objet, chargé de libérer la mémoire quand elle n’est plus nécessaire.

    Cela signifie que imageView ne doit pas sortir de son contexte. Sinon, la vue pourrait accéder à une mémoire déjà libérée.

    Je veux donc m’assurer que le type ne s’échappe jamais. Pour cela, je peux ajouter l’annotation SWIFT_NONESCAPABLE. Ainsi, le compilateur importe le type C++ comme nonescapable. En règle générale, si une structure contient une vue ou un pointeur vers une mémoire qu’elle ne possède pas, il faut utiliser cette annotation.

    Ce n’est pas réservé aux types de vue : en C++ et d’autres langages, il est courant qu’un type possède la mémoire qu’il référence et gère ses références via un comptage de références. Swift permet d’importer ces types en toute sécurité via des annotations. Ma structure de tampon d’image en C++ possède ses données imageData sous-jacentes. Lorsque la structure est libérée, imageData l’est aussi. Je veux que le tampon d’image soit importé en type à comptage de références dans Swift pour gérer automatiquement sa durée de vie. Pour cela, j’utilise l’annotation SWIFT_SHARED_REFERENCE pour indiquer au compilateur quelles fonctions Swift doit appeler pour augmenter et diminuer le compteur de références. Swift considère le tampon d’image comme un type à comptage de références.

    Mais le compilateur a besoin d’autres infos pour retourner le tampon d’image en toute sécurité.

    Quand une fonction C++ retourne un tampon d’image, deux situations sont possibles. D’abord, si la fonction retourne une image nouvellement créée, c’est au code appelant de la libérer une fois qu’il en a fini. Dans ce cas, je vais annoter la méthode SWIFT_RETURNS_RETAINED. Cela indique au compilateur Swift de libérer l’image dans l’appelant lorsqu’elle n’est plus utilisée. Ensuite, si la fonction retourne une référence à une image existante, c’est au code appelant de retenir cette image s’il souhaite la conserver. Dans ce cas, je vais annoter la méthode SWIFT_RETURNS_UNRETAINED. Cela indique au compilateur Swift qu’il doit conserver l’image s’il veut la conserver. Ajouter ces annotations rend explicites les attentes de propriété, afin que Swift puisse gérer la mémoire en toute sécurité.

    Annoter le code pour fournir les infos manquantes permet à Swift d’utiliser en toute sécurité les fonctions et types C et C++. Ces annotations ne changent pas le fonctionnement du code. Elles rendent simplement explicites les hypothèses du code.

    Faisons le point sur les annotations abordées et leur utilisation.

    Si un paramètre ou une valeur de retour est un pointeur ou un tableau pointant vers une mémoire contenant plusieurs éléments, utilisez l’annotation counted_by pour indiquer leur nombre. Si un paramètre fait référence à une mémoire appartenant à d’autres et ne sort pas de la fonction, utilisez noescape.

    Si la valeur retournée par la fonction référence une mémoire dont la durée de vie dépend d’un paramètre, ajoutez l’annotation lifetimebound. En ajoutant ces infos, vous permettez à Swift d’importer le pointeur comme un Span Swift sûr, utilisable sans code complexe au point d’appel.

    Vous pouvez ajouter des annotations pour aider Swift à gérer en toute sécurité vos types C++ personnalisés. Utilisez SWIFT_NONESCAPABLE si votre type C++ contient une vue, un pointeur ou une référence vers une mémoire qu’il ne possède pas. Cela indique au compilateur d’importer votre type comme un type non-escapable.

    Si votre type utilise un comptage de références, vous devez utiliser SWIFT_SHARED_REFERENCE. Ensuite, le compilateur gère sa mémoire automatiquement.

    Oui, ça fait beaucoup. Si vous voulez faire une pause pour prendre une collation ou un verre d’eau, c’est le moment idéal. Promettez-moi juste de revenir. Car je vais montrer de nouveaux outils vraiment passionnants qui rendent le code C et C++ beaucoup plus sûr à utiliser.

    Très bien, changeons de sujet pour voir comment rendre le code C et C++ réellement plus sûr. Dans mon app, j’ai ajouté des annotations pour garantir que Swift peut appeler en toute sécurité C et C++. Mais, le code C et C++ pur reste toujours non sécurisé. Je ne suis pas loin du bug de sécurité. Idéalement, je réécrirais ce code en Swift pour une sécurité totale. Mais parfois, ce n’est pas pratique. Il est impossible de rendre le C et le C++ aussi sûrs que Swift, mais voici des outils pour renforcer partiellement leur sécurité. D’abord, je vais parler d’un outil développé pour améliorer la sécurité des limites en C++.

    Certains se demandent pourquoi le C++ n’est pas déjà sécurisé sur les limites, alors que les Spans stockent déjà ces infos, comme je l’ai dit plus tôt. Le problème, c’est que les indices de tableau sur les Spans comme celui-ci ne sont pas contrôlés par défaut en C++. Il en va de même pour d’autres conteneurs standards comme les vecteurs. Xcode a une fonctionnalité appelée C++ Standard Library Hardening. Elle vérifie que les indices des tableaux dans les vues et conteneurs C++ respectent les limites. Elle ajoute aussi d’autres contrôles de sécurité à la bibliothèque standard.

    Même activé, le renforcement de la bibliothèque standard C++ ne règle pas tous les problèmes.

    Vous pouvez toujours utiliser des pointeurs bruts, non vérifiables car sans info de limites. La meilleure pratique en C++, c’est d’éviter les pointeurs bruts au profit des Spans C++ standard.

    Pour ça, Xcode permet d’activer des erreurs lorsqu’on utilise des pointeurs non sécurisés en C++. Vous pouvez vérifier votre code et remplacer les pointeurs bruts par des Span C++ ou des conteneurs standards selon les besoins.

    Pour info, ces erreurs concernent la sécurité des limites, pas la durée de vie de la mémoire.

    Pour sécuriser les limites dans vos projets C++, activez enforce bounds safe buffer usage in C++ dans les réglages de build de votre projet. Cela active le renforcement de la bibliothèque standard C++ et les erreurs d’utilisation non sécurisée des buffers.

    Qu’en est-il du C ? C ne dispose pas de types standard comme les Spans permettant aux pointeurs de transporter des infos de limites. Pour le C, nous avons développé une extension du langage qui garantit la sécurité des limites. Vous pouvez l’utiliser dans Xcode. Avec cette extension activée, le compilateur vous indiquera où il manque des infos de limites dans votre code C. Vous pouvez ensuite ajouter des annotations de limites pour fournir les infos manquantes. Ici, vous pouvez ajouter l’annotation counted_by sur le buffer, la même utilisée précédemment pour l’interopération sûre entre Swift et C.

    Le compilateur insère alors des vérifications de limites à l’exécution pour intercepter en toute sécurité les accès mémoire hors limites.

    Je peux activer l’extension Bounds Safety pour tous les fichiers C dans les réglages de mon projet Xcode. Pour en savoir plus, consultez la documentation Bounds Safety sur le site llvm.org.

    Dans cette présentation, j’explique comment sécuriser Swift et interagir en toute sécurité avec du code C et C++. C et C++ ne seront jamais aussi sûrs que Swift, mais on peut les rendre plus sûrs.

    Voici des conseils pour maximiser la sécurité lorsque vous combinez C, C++ et Swift.

    Activez Strict Memory Safety dans Swift. Cela vous alerte dès que vous utilisez des constructions non sécurisées et vous aide à repérer toute utilisation risquée des API C et C++. Ajoutez des annotations aux API C et C++ pour permettre une interaction sécurisée avec Swift.

    Rendez C et C++ plus sûrs par défaut. Pour cela, activez les nouvelles fonctionnalités de sécurité des limites pour C et C++.

    Nous collaborons avec la communauté open source pour que C, C++ et Swift fonctionnent ensemble de façon fluide et sécurisée. Vos retours et votre participation sont donc très importants pour nous.

    Essayez-les et dites-nous ce que vous en pensez. Merci de votre attention !

    • 3:19 - Unsafety can be subtle

      // Swift
      var imageData = [UInt8](repeating: 0, count: imageDataSize)
      filterImage(&imageData, imageData.count)
    • 4:01 - Strict memory safety

      // Swift
      var imageData = [UInt8](repeating: 0, count: imageDataSize)
      filterImage(&imageData, imageData.count)
      //warning: Expression uses unsafe constructs but is not marked with 'unsafe'
    • 8:00 - Raw pointers don't prevent out-of-bounds errors

      // C/C++
      void invertImage(uint8_t *imagePtr, size_t imageSize);
    • 8:21 - Raw pointers don't prevent out-of-bounds errors

      // Swift
      var imageData = [UInt8](repeating: 0, count: imageSize)
      invertImage(&imageData, imageSize)
    • 8:30 - Raw pointers don't prevent out-of-bounds errors

      // Swift
      var imageData = [UInt8](repeating: 0, count: imageSize)
      invertImage(&imageData, 1000000000000)
    • 8:48 - Solution for out-of-bounds error

      // Swift
      func invertImage(_ imagePtr : inout MutableSpan<UInt8>)
    • 8:54 - Solution for out-of-bounds error

      // Swift
      var imageDataSpan = imageData.mutableSpan
      invertImage(&imageDataSpan)
    • 9:58 - Express bounds information using __counted_by

      // C/C++
      void invertImage(uint8_t *__counted_by(imageSize) imagePtr __noescape, size_t imageSize);
    • 12:10 - Unsafe function declaration taking a C++ span

      // C++
      using CxxSpanOfByte = std::span<uint8_t>;
      void applyGrayscale(CxxSpanOfByte imageView);
    • 13:21 - Unsafe C++ function caching a C++ span

      // C++
      CxxSpanOfByte cachedView;
      void applyGrayscale(CxxSpanOfByte imageView) {
        cachedView = imageView;
        // Apply effect on image ...
      }
    • 14:08 - Swift Span prevents escaping scope

      // Swift
      var cachedView: MutableSpan<UInt8>?
      func applyGrayscale(_ imageView: inout MutableSpan<UInt8>) {
        cachedView = imageView // error: lifetime dependent value escapes its scope
        // Apply effect on image ...
      }
    • 15:18 - Express lifetime information using __noescape

      // C++
      CxxSpanOfByte cachedView;
      void applyGrayscale(CxxSpanOfByte imageView __noescape) {
        // Apply effect on image ...
      }
    • 15:56 - Safely use a C++ Span as a Swift Span

      // Swift
      var imageDataSpan = &imageData.mutableSpan
      applyGrayscale(&imageDataSpan)
    • 17:21 - Returned C++ Span is unsafe

      // C++
      CxxSpanOfByte scanImageRow(CxxSpanOfByte imageView,
                                 size_t width, size_t rowIndex);
    • 18:06 - Swift Spans prevent use-after-free by design

      // Swift
      func scanImageRow(_ imageView : inout MutableSpan<UInt8>,
                        _ width : Int, _ rowIndex : Int) -> MutableSpan<UInt8>
      // error: a function with a ~Escapable result requires '@lifetime(...)'
    • 18:47 - Express lifetime dependency with __lifetimebound

      // C++
      CxxSpanOfByte scanImageRow(CxxSpanOfByte imageView __lifetimebound,
                                 size_t width, size_t rowIndex);
    • 18:50 - Safely return a C++ Span as a Swift Span

      // Swift
      var imageDataSpan = imageData.mutableSpan
      var rowView = scanImageRow(&imageDataSpan, width, y)
    • 22:29 - Import a C++ view type as SWIFT_NONESCAPABLE

      // C++
      struct ImageView {
        std::span<uint8_t> pixelBytes;
        int width;
        int height;
      } SWIFT_NONESCAPABLE;
    • 23:31 - Import a C++ reference-counted type

      // C++
      struct ImageBuffer {
        std::vector<uint8_t> data;
        int width;
        int height;
        std::atomic<unsigned> refCount;
      } SWIFT_SHARED_REFERENCE(retain_image_buffer, release_image_buffer);
      
      void retain_image_buffer(ImageBuffer *_Nonnull buf);
      void release_image_buffer(ImageBuffer *_Nonnull buf);
    • 23:57 - Safely return a reference-counted type

      // C++
      ImageBuffer *_Nonnull createImage() SWIFT_RETURNS_RETAINED;
      ImageBuffer *_Nonnull getCachedImage() SWIFT_RETURNS_UNRETAINED;
    • 27:51 - C++ standard library hardening

      // C++
      void fill_array_with_indices(std::span<uint8_t> buffer) {
        for (size_t i = 0; i < buffer.size(); ++i) {
          buffer[i] = i;
        }
      }
    • 28:59 - C++ unsafe buffer usage errors

      // C++
      void fill_array_with_indices(uint8_t *buffer, size_t count) {
        for (size_t i = 0; i < count; ++i) {
          buffer[i] = i; // error: unsafe buffer access
        }
      }
    • 30:11 - Bounds safety extension for C

      // C
      void fill_array_with_indices(uint8_t *__counted_by(count) buf, size_t count) {
        for (size_t i = 0; i < count; ++i) {
          buf[i] = i;
        }
      }
    • 0:00 - Introduction
    • Découvrez la sécurité des apps, en particulier lorsque vous alliez Swift, un langage sécurisé pour la mémoire par défaut, à C et C++, qui peuvent être vulnérables. Swift inclut de nouvelles fonctionnalités pour améliorer la sécurité lors de l’intégration de ces langages : une sécurité stricte de la mémoire, des annotations pour les fonctions et les types C/C++ et des outils pour rendre le code C/C++ plus sûr.

    • 2:39 - Trouver un appel dangereux en Swift
    • Cet exemple illustre une app écrite en Swift qui interagit avec du code C et C++ pour les filtres d’images, ce qui peut présenter des risques de sécurité. Le nouveau mode de compilation Sécurité stricte de la mémoire de Swift 6.2 permet d’identifier et de signaler le code non sécurisé, impliquant principalement des pointeurs C et C++, ce qui vous permet d’améliorer la sécurité de l’application.

    • 4:55 - Appel de C/C++ en toute sécurité
    • Swift 6.2 inclut désormais 'Span', un nouveau type de pointeur sécurisé. En annotant le code C/C++ avec des limites et des informations de durée de vie, Swift peut convertir les pointeurs non sécurisés en 'Spans', ce qui permet des appels de fonctions sécurisés à partir de Swift sans modifier le code C/C++ d’origine. Pour en savoir plus sur Span, consultez « Améliorer l’utilisation de la mémoire et les performances avec Swift ».

    • 7:25 - Fonctions prenant des pointeurs
    • Lorsqu’une fonction utilise un pointeur brut, elle n’a pas d’informations de limites pour empêcher cette fonction de lire ou d’écrire en dehors de ses limites. Pour permettre au compilateur de comprendre la relation entre les pointeurs bruts et leurs tailles correspondantes, les annotations 'counted_by' et 'noescape' sont nécessaires. Lorsque le pointeur contient ces annotations, la fonction peut être appelée à partir de Swift, à l’aide d’un span. L’exemple traite également des différences entre les types 'Span' Swift et C++. Alors que les deux types 'Span' visent à accéder en toute sécurité à la mémoire contiguë, le 'Span' C++ manque d’informations sur la durée de vie, ce qui peut entraîner des bogues d’utilisation de mémoire après libération. Pour résoudre ce problème, l’annotation 'noescape' est utilisée pour indiquer qu’un paramètre, qu’il s’agisse d’un 'Span' C++ ou d’un pointeur brut, ne doit pas être stocké et utilisé en dehors de la portée de la fonction. En appliquant ces annotations et en utilisant 'Span', l’exemple élimine l’utilisation de pointeurs non sécurisés, réduit le risque de bogues liés à la mémoire et rend le code plus facile à gérer et à lire, tout en permettant une interaction transparente entre les fonctions Swift et C/C++.

    • 17:13 - Fonctions renvoyant des pointeurs
    • Il peut être risqué d’appeler une fonction C/C++ qui renvoie un pointeur ou un Span C++, car elle ne vérifie pas si la mémoire vers laquelle elle pointe est toujours valide. Pour pallier ce problème, une annotation 'lifetimebound' permet au compilateur d’appliquer une utilisation sûre, éliminant ainsi les bogues d’utilisation de mémoire après libération.

    • 20:16 - Importation de types personnalisés
    • En Swift, certains types idiomatiques C++ peuvent être directement importés et utilisés en toute sécurité avec des annotations. Les types de vue personnalisés, qui sont des structures contenant des pointeurs ou des références à la mémoire qu’ils ne possèdent pas, peuvent être importés en tant que types non échappables à l’aide de l’annotation 'SWIFT_NONESCAPABLE'. Cela garantit qu’ils ne survivent pas à la mémoire vers laquelle ils pointent, évitant ainsi les bogues d’utilisation de mémoire après libération. Les types comptés par références, qui possèdent la mémoire à laquelle ils se réfèrent et suivent les références via le comptage, peuvent être importés à l’aide de l’annotation 'SWIFT_SHARED_REFERENCE'. Swift peut ainsi gérer automatiquement leur durée de vie. De plus, les valeurs de retour de fonction peuvent être annotées 'SWIFT_RETURNS_RETAINED' ou 'SWIFT_RETURNS_UNRETAINED' pour spécifier si l’appelant est responsable de la libération ou de la conservation de l’objet compté par référence renvoyé. Cela permet de définir clairement les attentes en matière de responsabilité et permet à Swift de gérer la mémoire en toute sécurité.

    • 26:57 - Amélioration de la sécurité de C/C++
    • Il existe des outils pour améliorer la sécurité des limites en C et C++. Le renforcement de la bibliothèque standard C++ de Xcode permet de vérifier les limites sur les conteneurs et les vues standard, et des erreurs peuvent être activées en cas d’utilisation non sécurisée de pointeurs en C++. Pour la sécurité des limites, définissez « Appliquer l’utilisation de la mémoire tampon sécurisée par les limites en C++ » sur Oui dans les paramètres de compilation du projet. Pour C, une nouvelle extension de langage que vous pouvez utiliser dans Xcode garantit la sécurité des limites, en exigeant des annotations pour exprimer les informations sur les limites et en insérant des vérifications de limites au moment de l’exécution. Vous pouvez activer cette extension de langage pour tous les fichiers C dans les réglages de votre projet Xcode.

    • 30:48 - Conclusion
    • À l’aide des informations de cette session, vous pouvez combiner en toute sécurité du code Swift, C et C++. Les étapes clés incluent l’activation d’une sécurité mémoire stricte dans Swift, l’annotation des API C/C++ non sécurisées et l’utilisation de nouvelles fonctionnalités de sécurité des limites en C/C++. Les retours et la participation de la communauté sont encouragés pour améliorer l’interopérabilité.

Developer Footer

  • Vidéos
  • WWDC25
  • Mixez en toute sécurité C, C++ et Swift
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • App Store
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Fonts
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Open Source
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Sample Code
    • Tutorials
    • Downloads
    • Forums
    • Videos
    Open Menu Close Menu
    • Support Articles
    • Contact Us
    • Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Get the Apple Developer app.
    Copyright © 2025 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines