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

Videos

Abrir menú Cerrar menú
  • Colecciones
  • Temas
  • Todos los videos
  • Información

Volver a WWDC25

  • Información
  • Resumen
  • Transcripción
  • Código
  • 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

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

    Videos relacionados

    WWDC25

    • Mejora el uso y el rendimiento de la memoria con Swift
  • Buscar este video…

    Hola, mi nombre es Yeoul. Soy la responsable del equipo Secure Language Extension de Apple. Cuando creas una app, es fundamental priorizar la seguridad: proteger la información privada de tus usuarios frente a posibles atacantes. Estos actores maliciosos suelen aprovechar vulnerabilidades en código escrito en C y C++, lenguajes inseguros. La buena noticia es que, si tu app ya usa Swift, el lenguaje es seguro por defecto. Es una noticia fantástica.

    Pero, aunque todo tu código nuevo esté en Swift, tu app puede conservar partes antiguas en C o C++, o depender de bibliotecas externas. Al mezclar estos lenguajes, es importante no comprometer las garantías de seguridad de Swift. En términos generales, el problema es que las funciones de C y C++ que reciben o devuelven punteros crudos son muy difíciles de invocar de forma segura. Si las llamas incorrectamente, puedes provocar errores de seguridad y estabilidad, como desbordamientos de búfer o errores de tipo use-after-free.

    Por eso, los punteros que llegan de C y C++ se importan como tipos inseguros en Swift. Por ejemplo, un puntero a entero de C se importa como UnsafeMutablePointer. Swift incluye la palabra “unsafe” en el nombre del tipo para avisarte de que no puedes confiar en sus garantías habituales al llamar a esa función. Ahora bien, sé que algunas funciones sí pueden usarse con seguridad desde Swift, pero hasta ahora C y C++ no tenían forma de indicar cómo hacerlo. De eso trata esta charla.

    Primero, describiré una funcionalidad nueva, Strict Memory Safety, que ayuda a identificar llamadas inseguras en Swift.

    Después, explicaré cómo anotar funciones de C y C++ para aportar la información que falta y permitir que Swift las invoque con seguridad. A continuación verás cómo anotar tipos personalizados de C++ para importarlos sin riesgo. Por último, aunque nunca será posible que los lenguajes basados en C alcancen la seguridad de Swift, mostraré herramientas para hacer el código en C y C++ un poco más seguro.

    Swift 6.2 incorpora una funcionalidad muy útil para detectar llamadas a funciones inseguras de C y C++. La demostraré con una app que escribí para que los usuarios compartan fotos adorables de sus mascotas. Esta app accede a información privada muy sensible, como el segundo nombre del perro, y quiero mantenerla segura. Está escrito en Swift. Sin embargo, también llama a código en C y C++ para aplicar filtros de imagen personalizados. Quiero encontrar todas las llamadas inseguras a C y C++ dentro de la app y volverlas seguras. El problema es que a veces esas llamadas son difíciles de detectar. Swift es seguro por defecto, pero permite constructos inseguros, sobre todo al interoperar con C y C++. Por ejemplo, entre bastidores, la variable imageData crea un UnsafeMutablePointer, algo que sé por el nombre que es inseguro, pero no es tan fácil de notar al leer el código. Para localizar todas las llamadas a funciones inseguras puedo usar un nuevo modo del compilador en Swift 6.2 llamado Strict Memory Safety. Cuando lo activo, el compilador me avisa de cualquier código inseguro y explica el motivo.

    No viene activado de serie, pero como mi app es sensible en materia de seguridad, voy a habilitarlo. Veamos esto en Xcode.

    Activaré Strict Memory Safety en la configuración de compilación del proyecto para optar por participar.

    Al recompilar, el compilador muestra advertencias que me ayudan a detectar constructos inseguros.

    Veo avisos nuevos ahora. La mayor parte del código inseguro implica punteros de C y C++.

    Ahora te mostraré cómo invocar de forma segura funciones que usan estos punteros desde Swift.

    Pero antes veamos por qué es tan difícil usar punteros de C y C++ sin riesgo.

    Los punteros son una herramienta poderosa y útil. Me permiten inspeccionar la memoria con alto rendimiento sin necesidad de copiarla. Pero son muy difíciles de usar de forma segura. El problema de fondo es que C y C++ no ayudan a los programadores a evitar errores. Por ejemplo, nada me impide usar accidentalmente un bloque de memoria después de haberlo liberado o acceder más allá de los límites de un búfer.

    Aquí hay algunas buenas noticias. Swift 6.2 introduce un nuevo tipo de puntero seguro llamado Span que ofrece las ventajas de los punteros y, al mismo tiempo, evita automáticamente esos errores. Un Span se comporta como un puntero, pero con seguridad integrada. Swift se encarga de que no puedas hacer operaciones inseguras con él. Y si necesitas modificar la memoria, también existe MutableSpan. Para saber más sobre Span, consulta “Improved memory usage and performance with Swift”. Sería ideal que Swift pudiera importar los punteros de C y C++ como Spans en lugar de como punteros inseguros.

    Lamentablemente, al compilador le faltan dos datos clave para poder hacerlo con seguridad. Sin información de C++ sobre los límites de un puntero, Swift no puede impedir accesos fuera de rango. Y sin información de C++ sobre el tiempo de vida del puntero, Swift no puede evitar que se use después de liberarlo. La idea clave es que, si el programador aporta esa información faltante, Swift puede tratar un puntero inseguro como un Span de Swift.

    En Swift 6.2 puedo proporcionar esa información faltante al compilador agregando anotaciones a mi código C y C++. Esto no cambia cómo funciona el código; solo hace explícitas sus suposiciones. Así, Swift puede invocar con seguridad código C y C++ que usa punteros. Hablemos de cómo anotar funciones que reciben y devuelven punteros. Regresemos a mi app.

    La primera advertencia aparece al usar la función invertImage, que recibe un puntero sin procesar.

    Te enseñaré a llamar con seguridad a funciones de ese tipo, las que aceptan punteros como parámetros.

    Como mencioné, los punteros crudos carecen de información sobre límites, de modo que no hay forma de comprobar si acceden dentro del rango. Si no los uso con cuidado, pueden provocar errores de memoria fuera de límites. Este es un ejemplo. La función invertImage se llama desde Swift. Recibe imagePointer, un puntero crudo a la imagen, y el tamaño en parámetros separados.

    Pero, al ser un puntero crudo, nada me impide pasar por error un tamaño demasiado grande; si lo hago, la función leerá y escribirá más allá del búfer. Justo ese es uno de los problemas que resuelve Span. Imagina que invertImage se importara como una función de Swift que recibe un Span. Entonces, podría pasar un Span directamente, en lugar de un puntero y un tamaño por separado. Eso me protegería automáticamente de errores como indicar un tamaño incorrecto, porque los Span siempre llevan la información de límites adecuada para la memoria a la que apuntan. Luego, en segundo plano, el compilador se encargará de desempaquetar el Span, extraer el puntero y el tamaño correctos y pasarlos a la función de C por mí. De esta manera, no habrá margen a errores.

    El compilador puede hacer esto, pero le falta la conexión entre el puntero crudo y el tamaño. La función invertImage asume que el puntero hace referencia a un búfer de imageSize elementos, pero eso es solo una suposición implícita. Necesito expresar explícitamente esa relación para que tanto las personas como el compilador la comprendan. Esto se logra con la anotación counted_by. La anotación indica al compilador la cantidad de elementos en la memoria señalada por el puntero. Además, el puntero necesita otra anotación llamada noescape para la información de tiempo de vida que falta, aunque por ahora puedes ignorarla; hablaré de ella más adelante. Una vez que aporte esta información adicional, podré llamar a la función con seguridad desde Swift pasando un Span directamente. El compilador se ocupará del resto de forma automática. De esta manera, ni siquiera es posible cometer errores. Vuelvo a mi función invertImage.

    Agrego las anotaciones counted_by y noescape en la función invertImage.

    Luego, voy a la decoración y coloco las mismas anotaciones.

    Vuelvo al punto de llamada en Swift.

    Y ahora paso imageDataSpan directamente para invocar la función desde Swift.

    Ya no interviene ningún puntero inseguro, así que la advertencia desapareció.

    La siguiente advertencia está en la función applyGrayScale. Indica que la función usa un tipo de C++ inseguro.

    Veamos la definición en C++. Como su nombre sugiere, applyGrayScale aplica un efecto de escala de grises a la imagen de entrada. La función recibe una vista de la imagen, que es un tipo Span de C++. Hasta ahora hemos hablado de Spans en Swift, pero C++ también tiene su propio concepto de Span. Y vemos la advertencia porque Swift considera inseguros los Span de C++, aunque intenten resolver el mismo problema. Al igual que los Span de Swift, los Span de C++ son un tipo estándar utilizado para acceder a memoria contigua que pertenece a otro objeto. Contienen un puntero a esa memoria y su tamaño. Como los Span de C++ conocen su tamaño, pueden verificar con seguridad que no se acceda fuera de los límites, igual que los Span de Swift. Sin embargo, a diferencia de los Span de Swift, los Span de C++ no incluyen información sobre el tiempo de vida, de modo que no impiden acceder a la memoria una vez liberada. Por ello, usar un Span de C++ puede causar un error de tipo use-after-free.

    Este es un ejemplo. Imagina que la función applyGrayScale recibe un Span de C++ llamado imageView, que apunta a un arreglo creado por Swift.

    Dentro de applyGrayScale podría almacenar ese puntero, quizá en una variable global como cachedView, para que otro código de C++ lo use después.

    Sin embargo, hay un problema. Cuando la función regresa, Swift podría desalojar el arreglo si supone que nadie más lo usa. Entonces, el código de C++ conserva un puntero que ya no apunta a memoria válida: un puntero colgante, y acceder a él es un clásico error use-after-free.

    En cambio, un Span de Swift es seguro. No se le permite vivir más que la memoria a la que apunta. Si una función recibe un Span de Swift, solo puede usarlo dentro de la función; no puede conservarlo, por ejemplo guardándolo en cachedView para uso posterior. Ese comportamiento, cuando un puntero se conserva tras terminar la función, se denomina escapar, y el compilador muestra un error cada vez que un Span intenta escapar de su ámbito.

    Así, los punteros colgantes no son posibles con Spans de Swift y los errores use-after-free se evitan por diseño.

    Un Span de C++ no ofrece esas garantías. Para usarlo con seguridad, debo revisar la función manualmente y asegurarme de que el parámetro imageView no escape, para evitar el puntero colgante.

    Una vez comprobado que el parámetro no escapa de la función de C++, debo reflejar esa información en la definición de la función. Esto permitirá que tanto las personas como el compilador entiendan su comportamiento. Agregar la anotación noescape me permite hacerlo. La anotación noescape también puede aplicarse a punteros crudos y a referencias.

    A partir de Swift 6.2, un parámetro Span de C++ con la anotación noescape puede tratarse como un Span de Swift. Esto significa que ahora puedo pasar un Span de Swift directamente a applyGrayScale, lo cual es muy práctico y elimina la necesidad de código inseguro de apoyo. Es impresionante lo seguro y fácil que puede ser llamar a una función de C++.

    Voy a la definición de applyGrayScale.

    Y agrego noescape al parámetro imageView.

    Así como en la decoración correspondiente.

    Regreso al punto de llamada en Swift.

    Ahora puedo eliminar el acceso temporal mediante puntero mutable sin guardar y pasar directamente el Span de Swift obtenido de imageData.

    La advertencia desaparece porque llamo a applyGrayScale de manera segura.

    La siguiente advertencia aparece en el uso de scanImageRow, que recibe un Span de C++ y devuelve otro Span de C++.

    ¿Cómo puedo hacer segura la llamada a una función que devuelve un puntero como un Span de C++?

    Devolver un Span de C++ puede ser peligroso porque no comprueba si la memoria a la que apunta sigue siendo válida. Este es un ejemplo. ScanImageRow recibe imageView como Span de C++ y devuelve otro Span de C++ que apunta a una fila seleccionada de imageData. Una vez que la función retorna, esos datos pueden desasignarse, pero el Span devuelto sigue apuntando a esa memoria, un puntero colgante. Acceder a él provocará un error use-after-free. Este no sería posible si el valor devuelto pudiera tratarse como un Span de Swift, en lugar de un Span de C++. Porque el compilador ni siquiera me permitiría devolver un Span de Swift a menos que sepa que la memoria sigue siendo válida. Entonces, ¿cuándo es seguro usar el Span devuelto? Apunta a parte de la misma memoria que el parámetro imageView, lo que significa que vive solo mientras viva imageView.

    Esta relación se denomina lifetimebound.

    Es justamente la información que necesita Swift para importar un Span de C++ devuelto como un Span de Swift. Puedo expresarlo con la anotación lifetimebound, y así el compilador garantiza que se use de forma segura.

    Voy a la definición de scanImageRow.

    Agregaré la anotación lifetimebound al parámetro imageView de la función scanImageRow

    y haré lo mismo en la declaración.

    Con lifetimebound, la función puede recibir un Span de Swift y devolver otro Span de Swift. Paso al punto de llamada en Swift.

    Aquí ya puedo eliminar el acceso mediante puntero inseguro y pasar el Span de Swift directamente.

    La función ahora devuelve un Span de Swift en lugar de un puntero inseguro. Cuando vuelva a compilar, la advertencia desaparecerá.

    Al resolver los problemas de seguridad al llamar a mi código C y C++, todas las advertencias se han ido. Hasta ahora he explicado cómo tratar los punteros de C y C++ como Spans de Swift. Pero existen otros tipos idiomáticos en C++ que se pueden importar directamente en Swift y usar de forma segura con anotaciones. Estos son tipos de vista personalizados y los tipos con recuento de referencias. Primero veremos cómo importar con seguridad tipos de vista personalizados de C++. Un tipo de vista es una estructura que contiene un puntero o referencia a memoria que no posee. Esto significa que Span de Swift también es un tipo de vista.

    Veamos qué hace realmente seguros a los Spans de Swift. Internamente, los Span están marcados como un tipo especial de Swift, nonescapable. Los tipos nonescapable suelen usarse para ofrecer una vista de la memoria de otro objeto sin copiarla. Al igual que con Span, Swift garantiza que los tipos nonescapable no escapen de su contexto actual. Así no sobreviven a la memoria a la que apuntan y están protegidos contra errores de tipo use-after-free. Los tipos de vista de C++ pueden importarse como nonescapable en Swift; basta con agregar una anotación.

    Este es un ejemplo. Mi app tiene una estructura personalizada de C++ llamada ImageView que almacena el ancho y el alto de una imagen, así como un puntero a los datos de píxeles. imageView no posee esos datos de píxeles. Pertenece a otro objeto, responsable de liberar la memoria cuando ya no se necesita.

    Por eso no es seguro que imageView escape. Si lo hiciera, la vista podría usar la memoria subyacente después de que se haya desasignado.

    Quiero asegurarme de que el tipo nunca escape. Para ello puedo agregar la anotación SWIFT_NONESCAPABLE. De ese modo, el compilador importa el tipo C++ como nonescapable. Como regla práctica, si tu estructura contiene una vista o un puntero a memoria que no posee, debes usar esta anotación.

    Además de los tipos de vista, es muy común en C++ y otros lenguajes que un tipo sea propietario de la memoria a la que hace referencia y administre sus referencias mediante recuento. Swift permite importar estos tipos de forma segura mediante anotaciones. Por ejemplo, mi estructura imageBuffer en C++ es dueña de su imageData subyacente. Cuando se desasigna la estructura, también se libera imageData. Quiero que imageBuffer se importe en Swift como un tipo con recuento de referencias para que el compilador gestione automáticamente su ciclo de vida. Para lograrlo, uso la anotación SWIFT_SHARED_REFERENCE y le indico al compilador qué funciones debe llamar para incrementar y decrementar el recuento. Ahora Swift ve imageBuffer como un tipo con recuento de referencias.

    Sin embargo, el compilador necesita más información para devolver imageBuffer de manera segura.

    Cuando una función de C++ devuelve un imageBuffer pueden darse dos situaciones. Primero, si la función devuelve una imagen recién creada, corresponde al llamador liberar la imagen cuando termine de usarla. En ese caso, anoto el método con SWIFT_RETURNS_RETAINED. Esto le indica al compilador de Swift que libere la imagen en el llamador cuando ya no se use. Segundo, si la función devuelve una referencia a una imagen ya existente, es responsabilidad del llamador retenerla si quiere conservarla. En este caso anoto el método con SWIFT_RETURNS_UNRETAINED. Esto indica al compilador de Swift que debe retener la imagen si desea mantenerla. Estas anotaciones explicitan las expectativas de propiedad y permiten a Swift gestionar la memoria con seguridad.

    Anotar el código para aportar la información faltante permite a Swift usar con seguridad funciones y tipos de C y C++. Las anotaciones no cambian el funcionamiento del código. Solo convierten en explícitas las suposiciones presentes en el código.

    Repasemos las anotaciones que hemos visto y cómo usarlas.

    Si un parámetro o valor de retorno es un puntero o un arreglo y apunta a memoria con más de un elemento, utiliza la anotación counted_by para indicar la cantidad de elementos. Si un parámetro hace referencia a memoria que pertenece a otro objeto y ese parámetro no escapa de la función, usa noescape.

    Si el valor de retorno hace referencia a memoria cuyo tiempo de vida depende del tiempo de vida de un parámetro, agrega la anotación lifetimebound. Al agregar esta información, permites que Swift importe el puntero como un Span seguro que no requiere código adicional en el punto de llamada.

    También puedes agregar anotaciones para que Swift administre con seguridad tus tipos personalizados de C++. Usa SWIFT_NONESCAPABLE si tu tipo de C++ almacena una vista, un puntero o una referencia a memoria que no posee. Así el compilador lo importará como tipo no escapable.

    Si tu tipo emplea recuento de referencias, deberías usar SWIFT_SHARED_REFERENCE. De este modo, el compilador administrará automáticamente su memoria.

    Sí, es mucha información. Si quieres pausar el video para tomar un refrigerio o agua, este es un buen momento. Solo prométeme que volverás, porque a continuación mostraré herramientas nuevas muy interesantes que hacen el código C y C++ mucho más seguro de manejar.

    Bien, cambiaré de tema y hablaré de cómo se puede hacer más seguro el código C y C++. En mi app, agregué anotaciones para asegurar que Swift pueda llamar con seguridad a C y C++, pero el código puro en C y C++ sigue siendo inseguro. Basta un error para introducir un error de seguridad. Lo ideal sería reescribir ese código en Swift para lograr seguridad total, pero a veces eso no es práctico. Nunca será posible que C y C++ alcancen la seguridad de Swift, pero existen herramientas que aportan seguridad parcial en C y C++. Primero, hablaré de una herramienta que desarrollamos para mejorar la seguridad de límites en C++.

    Quizá te preguntes por qué C++ todavía no es seguro en cuanto a límites, dado que los Span ya almacenan información de límites, como hablamos anteriormente. El problema es que los índices de arreglos sobre Span, como este, no se verifican por defecto en C++, y lo mismo sucede con otros contenedores estándar, como vectores. Xcode cuenta con una prestación llamada C++ Standard Library Hardening que garantiza que los índices de los vectores y vistas estándar de C++ tengan verificaciones de límites, además de agregar otras comprobaciones de seguridad a la biblioteca estándar.

    Incluso con C++ Standard Library Hardening activado persiste un problema.

    Todavía pueden usarse punteros crudos, que no pueden verificarse porque carecen de información de límites. La mejor manera de emplear C++ es evitar esos punteros y recurrir a tipos estándar como los Span de C++.

    Para facilitarlo, Xcode permite activar errores cuando se utilizan punteros inseguros en C++. Así, podrás revisar tu código y sustituir los punteros crudos por Span o contenedores estándar según sea necesario.

    Estos errores solo cubren la seguridad de límites, no la de tiempo de vida.

    Para que un proyecto C++ sea seguro en cuanto a límites, en los ajustes de compilación habilita la opción de reforzar el uso seguro de búferes en C++. Así, se habilitarán simultáneamente C++ Standard Library Hardening y los errores por uso inseguro de búfer.

    ¿Qué pasa con C? A diferencia de C++, C no dispone de tipos estándar como Span que permitan que los punteros lleven información de límites, por lo que hemos desarrollado una extensión del lenguaje que garantiza la seguridad de límites y que ya puedes usar en Xcode. Con la extensión activada, el compilador señalará en qué partes de tu código C falta información de límites. Después podrás agregar anotaciones de límites para aportar esa información. Por ejemplo, puedes agregar la anotación counted_by al búfer, la misma que se usa para la interoperabilidad segura entre Swift y C.

    Entonces, el compilador insertará comprobaciones en tiempo de ejecución que detendrán de forma segura los accesos fuera de rango.

    Puedes activar la extensión Bounds Safety para todos los archivos C en la configuración de tu proyecto en Xcode. Para obtener más información, consulta la documentación de Bounds Safety en el sitio web llvm.org.

    En esta presentación, expliqué cómo garantizar la seguridad en Swift y llamar de forma segura a código C y C++. Nunca será posible hacer que C y C++ sean tan seguros como Swift, pero sí se pueden hacer más seguros.

    Aquí hay algunos consejos para obtener la mayor seguridad al mezclar C, C++ y Swift.

    Activa la seguridad de memoria estricta en Swift. Así se te avisará cuando uses constructos inseguros y podrás detectar usos peligrosos de las API de C y C++. Agrega anotaciones a las API de C y C++ para que Swift interactúe con ellas de forma segura.

    Haz que C y C++ sean más seguros de forma predeterminada. Puedes hacer esto activando las nuevas funcionalidades de seguridad de límites para C y C++.

    Colaboramos con la comunidad de código abierto para que C, C++ y Swift funcionen de manera fluida y segura, así que tu opinión y participación son fundamentales.

    Pruébalo y cuéntanos qué te parece. Gracias por ver 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.

Developer Footer

  • Videos
  • WWDC25
  • Mezcle de forma segura C, C++ y 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