swiftlang/swift

[SR-8211] Support a mechanism for zeroing an UnsafeMutable{,Raw}BufferPointer

開放

#50,743 建立於 2018年7月9日

 (9 則留言) (0 個反應) (0 位負責人)Swift (10,719 個分叉)batch import
featuregood first issuestandard libraryswift evolution proposal needed

倉庫指標

星標
 (69,989 顆星)
PR 合併指標
 (PR 指標待抓取)

描述

Previous ID SR-8211
Radar None
Original Reporter @Lukasa
Type New Feature
Votes 0
Component/s Standard Library
Labels New Feature, StarterProposal
Assignee None
Priority Medium

md5: 3a2dea04d2903bbfde833b4186202d1f

Issue Description:

Currently Swift provides no mechanism to zero a buffer of memory that is safe from the compiler "helpfully" optimising it away. The natural thing to try to do, {{ pointer.initialize(repeating: 0) }} can be elided by the compiler, as demonstrated by this short Swift program:

let myBufferPtr = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: 1000)
myBufferPtr.initialize(from: 0...255)
myBufferPtr.initialize(repeating: 0)
myBufferPtr.deallocate()

When compiled with optimisations turned on, the following SIL is emitted for the main function:

let myBufferPtr: UnsafeMutableBufferPointer<UInt8>

// myBufferPtr
sil_global hidden [let] @$S4test11myBufferPtrSrys5UInt8VGvp : $UnsafeMutableBufferPointer<UInt8>

// _swiftEmptyArrayStorage
sil_global @_swiftEmptyArrayStorage : $_SwiftEmptyArrayStorage

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @$S4test11myBufferPtrSrys5UInt8VGvp // id: %2
  %3 = global_addr @$S4test11myBufferPtrSrys5UInt8VGvp : $*UnsafeMutableBufferPointer<UInt8> // user: %22
  %4 = integer_literal $Builtin.Int64, 1000       // users: %42, %10, %5
  %5 = struct $Int (%4 : $Builtin.Int64)          // user: %21
  %6 = metatype $@thick UInt8.Type                // users: %15, %7
  %7 = builtin "strideof"<UInt8>(%6 : $@thick UInt8.Type) : $Builtin.Word // user: %8
  %8 = builtin "zextOrBitCast_Word_Int64"(%7 : $Builtin.Word) : $Builtin.Int64 // user: %10
  %9 = integer_literal $Builtin.Int1, -1          // users: %37, %10
  %10 = builtin "smul_with_overflow_Int64"(%8 : $Builtin.Int64, %4 : $Builtin.Int64, %9 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // users: %12, %11
  %11 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 0 // user: %14
  %12 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 1 // user: %13
  cond_fail %12 : $Builtin.Int1                   // id: %13
  %14 = builtin "truncOrBitCast_Int64_Word"(%11 : $Builtin.Int64) : $Builtin.Word // user: %16
  %15 = builtin "alignof"<UInt8>(%6 : $@thick UInt8.Type) : $Builtin.Word // user: %16
  %16 = builtin "allocRaw"(%14 : $Builtin.Word, %15 : $Builtin.Word) : $Builtin.RawPointer // users: %46, %34, %19, %18
  %17 = integer_literal $Builtin.Word, 1000       // user: %18
  bind_memory %16 : $Builtin.RawPointer, %17 : $Builtin.Word to $*UInt8 // id: %18
  %19 = struct $UnsafeMutablePointer<UInt8> (%16 : $Builtin.RawPointer) // user: %20
  %20 = enum $Optional<UnsafeMutablePointer<UInt8>>, #Optional.some!enumelt.1, %19 : $UnsafeMutablePointer<UInt8> // user: %21
  %21 = struct $UnsafeMutableBufferPointer<UInt8> (%20 : $Optional<UnsafeMutablePointer<UInt8>>, %5 : $Int) // users: %30, %22
  store %21 to %3 : $*UnsafeMutableBufferPointer<UInt8> // id: %22
  %23 = alloc_stack $IndexingIterator<ClosedRange<UInt8>> // users: %30, %31
  %24 = integer_literal $Builtin.Int8, 0          // user: %27
  %25 = integer_literal $Builtin.Int8, -1         // user: %28
  // function_ref specialized UnsafeMutableBufferPointer.initialize<A>(from:)
  %26 = function_ref @$SSr10initialize4from8IteratorQyd___Sitqd___t7ElementQyd__Rszs8SequenceRd__lFs5UInt8V_SNyAIGTg5 : $@convention(method) (ClosedRange<UInt8>, UnsafeMutableBufferPointer<UInt8>) -> (@out IndexingIterator<ClosedRange<UInt8>>, Int) // user: %30
  %27 = struct $UInt8 (%24 : $Builtin.Int8)       // users: %41, %29
  %28 = struct $UInt8 (%25 : $Builtin.Int8)       // user: %29
  %29 = struct $ClosedRange<UInt8> (%27 : $UInt8, %28 : $UInt8) // user: %30
  %30 = apply %26(%23, %29, %21) : $@convention(method) (ClosedRange<UInt8>, UnsafeMutableBufferPointer<UInt8>) -> (@out IndexingIterator<ClosedRange<UInt8>>, Int)
  dealloc_stack %23 : $*IndexingIterator<ClosedRange<UInt8>> // id: %31
  %32 = integer_literal $Builtin.Int64, 0         // user: %35
  %33 = integer_literal $Builtin.Int64, 1         // user: %37
  %34 = pointer_to_address %16 : $Builtin.RawPointer to [strict] $*UInt8 // user: %40
  br bb1(%32 : $Builtin.Int64)                    // id: %35

// %36                                            // users: %39, %37
bb1(%36 : $Builtin.Int64):                        // Preds: bb2 bb0
  %37 = builtin "sadd_with_overflow_Int64"(%36 : $Builtin.Int64, %33 : $Builtin.Int64, %9 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // user: %38
  %38 = tuple_extract %37 : $(Builtin.Int64, Builtin.Int1), 0 // users: %44, %42
  %39 = builtin "truncOrBitCast_Int64_Word"(%36 : $Builtin.Int64) : $Builtin.Word // user: %40
  %40 = index_addr %34 : $*UInt8, %39 : $Builtin.Word // user: %41
  store %27 to %40 : $*UInt8                      // id: %41
  %42 = builtin "cmp_eq_Int64"(%38 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1 // user: %43
  cond_br %42, bb3, bb2                           // id: %43

bb2:                                              // Preds: bb1
  br bb1(%38 : $Builtin.Int64)                    // id: %44

bb3:                                              // Preds: bb1
  %45 = integer_literal $Builtin.Word, -1         // users: %46, %46
  %46 = builtin "deallocRaw"(%16 : $Builtin.RawPointer, %45 : $Builtin.Word, %45 : $Builtin.Word) : $()
  %47 = integer_literal $Builtin.Int32, 0         // user: %48
  %48 = struct $Int32 (%47 : $Builtin.Int32)      // user: %49
  return %48 : $Int32                             // id: %49
} // end sil function 'main'

Note that we never call the initializer with zero. Instead we see only the initialisation with the range, and then the deallocation.

There should be some supported Swift way to securely overwrite a buffer of memory that does not involve making a call through libc. This is useful with system programs that are handling sensitive data (e.g. passphrases).

貢獻者指南