Подтвердить что ты не робот

Безопасная память для быстрых объектов

Я пишу быстрое приложение, которое требует обработки секретных ключей в памяти. Из-за чувствительности таких объектов ключи должны быть очищены (a.k.a., записанные ко всем нулям), когда объект освобожден, и память не может быть выгружена на диск (что обычно делается с помощью mlock()).

В Objective-C вы можете предоставить собственный объект CFAllocator, который позволяет использовать ваши собственные функции для распределения/освобождения/перераспределения памяти, используемой объектом.

Итак, одним из решений является просто реализовать объект "SecureData" в Objective-C, который внутренне создает объект NSMutableData с помощью специального CFAllocator (также в objective-c).

Однако, есть ли способ предоставить свои собственные функции выделения памяти для чистого swift объекта (например, struct или [UInt8])? Или есть лучший, "правильный" способ реализовать безопасную память, подобную этой, в быстрой?

4b9b3361

Ответ 1

Если вы хотите получить полный контроль над областью памяти, которую вы сами выделяете, вы можете использовать UnsafePointer и co:

// allocate enough memory for ten Ints
var ump = UnsafeMutablePointer<Int>.alloc(10)
// memory is in an uninitialized raw state

// initialize that memory with Int objects
// (here, from a collection)
ump.initializeFrom(reverse(0..<10))

// memory property gives you access to the underlying value
ump.memory // 9

// UnsafeMutablePointer acts like an IndexType
ump.successor().memory // 8
// and it has a subscript, but it not a CollectionType
ump[3] // = 6

// wrap it in an UnsafeMutableBufferPointer to treat it
// like a collection (or UnsafeBufferPointer if you don't
// need to be able to alter the values)
let col = UnsafeMutableBufferPointer(start: ump, count: 10)
col[3] = 99
println(",".join(map(col,toString)))
// prints 9,8,7,99,5,4,3,2,1,0

ump.destroy(10)
// now the allocated memory is back in a raw state
// you could re-allocate it...
ump.initializeFrom(0..<10)
ump.destroy(10)

// when you're done, deallocate the memory
ump.dealloc(10)

Вы также можете указать UnsafePointer на другую память, такую ​​как память, которую вы передали некоторым C API.

UnsafePointer может быть передан в C-функции, которые принимают указатель на непрерывный блок памяти. Поэтому для ваших целей вы можете передать этот указатель в функцию типа mlock:

let count = 10
let ump = UnsafeMutablePointer.allocate<Int>(count)
mlock(ump, UInt(sizeof(Int) * count))
// initialize, use, and destroy the memory
munlock(ump, UInt(sizeof(Int) * count))
ump.dealloc(count)

Вы даже можете хранить свои собственные пользовательские типы:

struct MyStruct {
    let a: Int
    let b: Int
}

var pointerToStruct = UnsafeMutablePointer<MyStruct>.alloc(1)
pointerToStruct.initialize(MyStruct(a: 1, b: 2))
pointerToStruct.memory.b  // 2
pointerToStruct.destroy()
pointerToStruct.dealloc(1)

Однако помните, что если вы делаете это с помощью классов или даже массивов или строк (или структуры, которая их содержит), то все, что вы будете держать в своей памяти, указывает на другую память, что эти объекты распределять и владеть. Если это имеет значение для вас (т.е. Вы делаете что-то особенное для этой памяти, например, обеспечиваете ее, в вашем примере), это, вероятно, не то, что вы хотите.

Таким образом, либо вам нужно использовать объекты фиксированного размера, либо использовать дальнейшее использование UnsafePointer для хранения указателей на большее количество областей памяти. Если им не нужно динамически изменять размер, то только одно выделение небезопасного указателя, возможно, завернутое в UnsafeBufferPointer для интерфейса коллекции, могло бы это сделать.

Если вам нужно больше динамического поведения, ниже приведена очень простая реализация коллекции, которая может по мере необходимости изменять размер, которая может быть улучшена, чтобы охватить специальную логику обработки памяти:

// Note this is a class not a struct, so it does NOT have value semantics,
// changing a copy changes all copies.
public class UnsafeCollection<T> {
    private var _len: Int = 0
    private var _buflen: Int = 0
    private var _buf: UnsafeMutablePointer<T> = nil

    public func removeAll(keepCapacity: Bool = false) {
        _buf.destroy(_len)
        _len = 0
        if !keepCapacity {
            _buf.dealloc(_buflen)
            _buflen = 0
            _buf = nil
        }
    }

    public required init() { }
    deinit { self.removeAll(keepCapacity: false) }

    public var count: Int { return _len }
    public var isEmpty: Bool { return _len == 0 }
}

Чтобы покрыть требования MutableCollectionType (т.е. CollectionType плюс присваиваемый индекс):

extension UnsafeCollection: MutableCollectionType {
    typealias Index = Int
    public var startIndex: Int { return 0 }
    public var endIndex: Int { return _len }

    public subscript(idx: Int) -> T {
        get {
            precondition(idx < _len)
            return _buf[idx]
        }
        set(newElement) {
            precondition(idx < _len)
            let ptr = _buf.advancedBy(idx)
            ptr.destroy()
            ptr.initialize(newElement)
        }
    }

    typealias Generator = IndexingGenerator<UnsafeCollection>
    public func generate() -> Generator {
        return Generator(self)
    }
}

И ExtensibleCollectionType, чтобы обеспечить динамический рост:

extension UnsafeCollection: ExtensibleCollectionType {
    public func reserveCapacity(n: Index.Distance) {
        if n > _buflen {
            let newBuf = UnsafeMutablePointer<T>.alloc(n)
            newBuf.moveInitializeBackwardFrom(_buf, count: _len)
            _buf.dealloc(_buflen)
            _buf = newBuf
            _buflen = n
        }
    }

    public func append(x: T) {
        if _len == _buflen {
            reserveCapacity(Int(Double(_len) * 1.6) + 1)
        }
        _buf.advancedBy(_len++).initialize(x)
    }

    public func extend<S: SequenceType where S.Generator.Element == T>
      (newElements: S) {
        var g = newElements.generate()
        while let x: T = g.next() {
            self.append(x)
        }
    }
}