-
Combine C, C++ e Swift com segurança
Saiba como combinar C, C++ e Swift enquanto aumenta a segurança de seus apps. Mostraremos como identificar onde APIs do C e C++ não seguras são chamadas no seu código Swift, como chamá-las com mais segurança e como deixar o código em C e C++ existente no seu app mais seguro por padrão.
Capítulos
- 0:00 - Introdução
- 2:39 - Encontrar uma chamada não segura no Swift
- 4:55 - Chamar C/C++ com segurança
- 7:25 - Funções que usam ponteiros
- 17:13 - Funções que retornam ponteiros
- 20:16 - Importar tipos personalizados
- 26:57 - Melhorar a segurança do C/C++
- 30:48 - Conclusão
Recursos
Vídeos relacionados
WWDC25
-
Buscar neste vídeo...
-
-
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 - Introdução
Saiba mais sobre a segurança dos apps, especialmente ao combinar Swift, uma linguagem segura por padrão, com C e C++, que podem ser vulneráveis. O Swift apresenta novos recursos para aumentar a segurança ao integrar estas linguagens: segurança de memória rigorosa, anotações para funções e tipos C/C++ e ferramentas para tornar código C/C++ mais seguro.
- 2:39 - Encontrar uma chamada não segura no Swift
O exemplo demonstra um app escrito em Swift que faz interface com código C e C++ para filtros de imagens, o que pode apresentar riscos de segurança. O novo modo de compilador de segurança de memória rigorosa do Swift 6.2 é usado para identificar e sinalizar código inseguro, principalmente envolvendo ponteiros C e C++, possibilitando o aumento da segurança do app.
- 4:55 - Chamar C/C++ com segurança
O Swift 6.2 apresenta “Span”, um novo tipo de ponteiro seguro. Ao anotar código C/C++ com informações de limites e vida útil, o Swift pode converter ponteiros desprotegidos em “Spans”, possibilitando chamadas de funções seguras do Swift sem necessidade de modificar o código C/C++ original. Saiba mais sobre o Span em “Melhore o uso da memória e o desempenho com o Swift”.
- 7:25 - Funções que usam ponteiros
Quando uma função utiliza um ponteiro bruto, ela não tem informações de limites para evitar que a função leia ou grave fora dos seus limites. Para que o compilador entenda a relação entre os ponteiros brutos e os tamanhos correspondentes, são necessárias as anotações “counted_by” e “noescape”. Quando o ponteiro recebe essas anotações, é possível chamar a função do Swift usando um Span. O exemplo também aborda as diferenças entre os tipos “Span” do Swift e do C++. Embora ambos tenham como objetivo o acesso seguro à memória contígua, o “Span” do C++ não tem informações sobre a vida útil, o que pode causar erros de uso após a liberação. Para atenuar isso, a anotação “noescape” é utilizada para indicar que um parâmetro, seja um “Span” do C++ ou um ponteiro bruto, não pode ser armazenado e utilizado fora do escopo da função. Ao aplicar essas anotações e usar o “Span”, o exemplo elimina o uso desprotegido de ponteiros, reduz o risco de bugs relacionados à memória e facilita a leitura e a manutenção do código, ao mesmo tempo em que permite uma interação perfeita entre as funções Swift e C/C++.
- 17:13 - Funções que retornam ponteiros
Pode ser arriscado chamar uma função C/C++ que retorne um ponteiro ou um Span do C++, porque ela não controla a validade da memória para a qual está apontando. Para atenuar isso, uma anotação “lifetimebound” possibilita que o compilador imponha o uso seguro, eliminando bugs de uso após a liberação.
- 20:16 - Importar tipos personalizados
No Swift, determinados tipos idiomáticos do C++ podem ser importados diretamente e usados de forma segura com anotações. Os tipos de visualização personalizados, que são structs que contêm ponteiros ou referências à memória que não possuem, podem ser importados como tipos inescapáveis usando a anotação “SWIFT_NONESCAPABLE”. Assim, eles não sobrevivem à memória para a qual apontam, impedindo bugs de uso após a liberação. Os tipos de contagem de referências, que são aqueles que possuem a memória à qual se referem e rastreiam as referências por meio de contagem, podem ser importados usando a anotação “SWIFT_SHARED_REFERENCE”. Isso permite que o Swift gerencie automaticamente a vida útil deles. Além disso, os valores de retorno de função podem ser anotados na forma de “SWIFT_RETURNS_RETAINED” ou “SWIFT_RETURNS_UNRETAINED” para especificar se o chamador é responsável por liberar ou reter o objeto de contagem de referências retornado, tornando explícitas as expectativas de propriedade e permitindo que o Swift gerencie a memória de forma segura.
- 26:57 - Melhorar a segurança do C/C++
Há ferramentas para aumentar a segurança dos limites em C e C++. A proteção da biblioteca C++ padrão do Xcode possibilita verificações de limites em contêineres e views padrão, e também é possível ativar erros em caso de uso de ponteiros desprotegidos em C++. Para a segurança dos limites, defina “Impor o uso seguro de buffer dentro dos limites em C++” como “Sim” nas configurações de compilação do seu projeto. Para C, uma nova extensão de linguagem que pode ser usada no Xcode garante a segurança dos limites, exigindo anotações para expressar informações de limites e inserindo verificações dos limites em tempo de execução. Você pode ativar essa extensão de linguagem para todos os arquivos C nas configurações do seu projeto no Xcode.
- 30:48 - Conclusão
Usando as informações desta sessão, você pode combinar códigos Swift, C e C++ de forma segura. As principais etapas incluem a ativação da segurança de memória rigorosa no Swift, a anotação de APIs C/C++ desprotegidas e a utilização de novos recursos de segurança de limites em C/C++. Incentivamos o feedback e a participação da comunidade a fim de melhorar a interoperabilidade.