-
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
Vidéos connexes
WWDC25
-
Rechercher dans cette vidéo…
-
-
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é.