-
Mezcle de forma segura C, C++ y Swift
Aprende a combinar C, C++ y Swift mientras mejoras la seguridad de tus apps. Te mostraremos cómo encontrar desde dónde se convoca a las API C y C++ no seguras en tu código Swift, cómo convocarlas con seguridad y cómo hacer que el código C y C++ existente sea más seguro.
Capítulos
- 0:00 - Introducción
- 2:39 - Encuentra convocatorias no seguras en Swift
- 4:55 - Convoca C/C++ de forma segura
- 7:25 - Funciones que toman punteros
- 17:13 - Funciones que devuelven punteros
- 20:16 - Importación de tipos personalizados
- 26:57 - Mejora de la seguridad de C/C++
- 30:48 - Conclusión
Recursos
Videos relacionados
WWDC25
-
Buscar este video…
-
-
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 - Introducción
Obtén información sobre la seguridad de las apps, en particular al mezclar Swift, un lenguaje seguro por defecto, con C y C++, que pueden ser vulnerables. Swift presenta nuevas funcionalidades para mejorar la seguridad al integrar estos lenguajes: seguridad estricta de memoria, anotaciones para funciones y tipos de C/C++ y herramientas para hacer que el código C/C++ sea más seguro.
- 2:39 - Encuentra convocatorias no seguras en Swift
El ejemplo demuestra una app escrita en Swift que interactúa con códigos C y C++ para filtros de imágenes, lo que puede introducir riesgos de seguridad. El nuevo modo de compilador de seguridad estricta de memoria de Swift 6.2 se usa para identificar y marcar código inseguro, que en su mayoría involucra punteros de C y C++, lo que te permite mejorar la seguridad de la app.
- 4:55 - Convoca C/C++ de forma segura
Swift 6.2 presenta Span, un nuevo tipo de puntero seguro. Al anotar el código C/C++ con límites e información de duración, Swift puede convertir punteros no seguros en Spans, lo que permite realizar llamadas de funciones seguras desde Swift sin modificar el código C/C++ original. Obtén más información sobre Span en “Improved memory usage and performance with Swift”.
- 7:25 - Funciones que toman punteros
Cuando una función toma un puntero sin formato, no tiene información de límites que impida que la función lea o escriba fuera de sus límites. Para permitir que el compilador comprenda la relación entre los punteros sin formato y sus tamaños correspondientes, son necesarias las anotaciones counted_by y noescape. Cuando el puntero lleva estas anotaciones, se puede llamar a la función desde Swift, usando un Span. El ejemplo también analiza las diferencias entre los tipos Span de Swift y C++. Si bien ambos tienen como objetivo acceder de forma segura a la memoria contigua, Span de C++ carece de información sobre la duración, lo que puede generar errores de uso después de la liberación. Para mitigar esto, se utiliza la anotación noescape para indicar que un parámetro, ya sea un Span de C++ o un puntero sin formato, no debe almacenarse ni usarse fuera del alcance de la función. Al aplicar estas anotaciones y usar Span, el ejemplo elimina el uso inseguro de punteros, reduce el riesgo de errores relacionados con la memoria y permite que sea más fácil mantener y leer el código, al tiempo que permite una interacción fluida entre Swift y las funciones de C/C++.
- 17:13 - Funciones que devuelven punteros
Puede ser arriesgado llamar a una función C/C++ que devuelva un puntero o un Span C++, porque este no rastrea si la memoria a la que apunta sigue siendo válida. Para mitigar esto, una anotación lifetimebound permite que el compilador aplique un uso seguro, eliminando errores de uso después de la liberación.
- 20:16 - Importación de tipos personalizados
En Swift, ciertos tipos idiomáticos de C++ pueden importarse directamente y usarse de forma segura con anotaciones. Los tipos de vista personalizados, que son estructuras que contienen punteros o referencias a memoria que no les pertenecen, se pueden importar como tipos no escapables usando la anotación SWIFT_NONESCAPABLE. Esto garantiza que no sobrevivan a la memoria a la que apuntan, evitando errores de uso después de la liberación. Los tipos contados por referencia, que poseen la memoria a la que hacen referencia y rastrean referencias a través del conteo, se pueden importar con la anotación SWIFT_SHARED_REFERENCE. Esto permite que Swift administre automáticamente su duración. Además, los valores de retorno de la función se pueden anotar como SWIFT_RETURNS_RETAINED o SWIFT_RETURNS_UNRETAINED para especificar si el llamador es responsable de liberar o retener el objeto con referencia contada devuelto, lo que hace que las expectativas de propiedad sean explícitas y permite que Swift administre la memoria de manera segura.
- 26:57 - Mejora de la seguridad de C/C++
Existen herramientas para mejorar la seguridad de los límites en C y C++. El fortalecimiento de la biblioteca estándar C++ de Xcode permite realizar verificaciones de límites en contenedores y vistas estándar, y se pueden activar errores para el uso inseguro de punteros en C++. En el caso de la seguridad de los límites, establece la opción de asegurar el uso del búfer con límites seguros en C++ en “sí” en la configuración de compilación de tu proyecto. En el caso de C, hay una nueva extensión del lenguaje que puedes utilizar en Xcode para garantizar la seguridad de los límites, que requiere anotaciones para expresar la información de los límites e inserta verificaciones de los límites en tiempo de ejecución. Puedes habilitar esta extensión de lenguaje en todos los archivos C en la configuración de tu proyecto Xcode.
- 30:48 - Conclusión
Con la información de esta sesión, podrás combinar de forma segura código Swift, C y C++. Los pasos clave incluyen habilitar una seguridad de memoria estricta en Swift, anotar API de C/C++ no seguras y usar nuevas funcionalidades de seguridad de límites en C/C++. Se fomenta la retroalimentación y la participación de la comunidad para mejorar la interoperabilidad.