2012-09-04 2 views
6

È un modello comune utilizzare i modelli per forzare il compilatore a inizializzare i valori dei tipi primitivi/POD (https://stackoverflow.com/a/11493744/16673 o http://www.codeproject.com/Articles/825/Using-templates-for-initialization).Modello per eseguire la sovrascrittura automatica della memoria in caso di distruzione

Esiste un modello simile che potrebbe essere utilizzato per cancellare il valore una volta che è fuori dall'ambito per ragioni di sicurezza, per assicurarsi che il valore non sia lasciato nello stack una volta che la variabile è stata distrutta? Temo che una ingenua implementazione analoga potrebbe non funzionare, in quanto il compilatore è libero di ignorare qualsiasi assegnazione a un valore che sta andando fuori dal campo di applicazione, in quanto il valore può essere banalmente dimostrato non essere più utilizzato. Esiste una soluzione coerente e ragionevolmente portatile, ad es. usando volatile?

risposta

3

Si potrebbe utilizzare qualche C++ 11 caratteristiche per rendere questo più portabile, ma questo può essere sufficiente come punto di partenza:

Classe

template<typename T> 
class t_secure_destruct { 
    static const size_t Size = sizeof(T); 
    static const size_t Align = alignof(T); 
public: 
    t_secure_destruct() : d_memory() { 
    new(this->d_memory)T; 
    } 

    ~t_secure_destruct() { 
    reinterpret_cast<T*>(this->d_memory)->~T(); 
    this->scribble(); 
    } 

    // @todo implement or delete op-assign and remaining constructors 

public: 
    T& get() { 
    return *reinterpret_cast<T*>(this->d_memory); 
    } 

    const T& get() const { 
    return *reinterpret_cast<const T*>(this->d_memory); 
    } 

private: 
    void scribble() { 
    for (size_t idx(0); idx < Size; ++idx) { 
     this->d_memory[idx] = random(); 
    } 
    } 

private: 
    __attribute__((aligned(Align))) char d_memory[Size]; 
}; 

Demo

#include <iostream> 

class t_test { 
public: 
    t_test() : a(-1) { 
    std::cout << "construct\n"; 
    } 

    ~t_test() { 
    std::cout << "destruct\n"; 
    } 

public: 
    void print() const { 
    std::cout << "a = " << a << "\n"; 
    } 

public: 
    int a; 
}; 

int main(int argc, const char* argv[]) { 
    t_secure_destruct<t_test>test; 
    test.get().print(); 
    test.get().a = 100; 
    test.get().print(); 
    return 0; 
} 

Ovviamente, se si preferisce, si può anche eseguire il backup di tale allocazione con un'allocazione dell'heap. Se è necessario superare in astuzia un ottimizzatore, potrebbe essere necessario mettere lo scribbler fuori dalla sua portata.

+0

Suono ragionevole. Cancellare i randoms sembra tuttavia portare piccoli benefici, l'azzeramento normale dovrebbe fare il lavoro. La soluzione non tenta di proteggere qualcuno che legge i chip di memoria o qualcosa del genere, proprio contro un altro processo che accede alla memoria. O c'è una ragione per usare dati casuali anche in questo caso? – Suma

+0

La soluzione sembra utilizzare le caratteristiche specifiche di GCC (attributi). C'è un motivo per usare il char per lo storage? Perché non usare il tipo originale? – Suma

+0

@ Suma dipende interamente da ciò che si vuole realizzare. anche gli zeri sarebbero buoni - è solo un'illustrazione. – justin

4

C'è una funzione nell'API di Windows denominata SecureZeroMemory. Potresti vedere la sua implementazione.

Tuttavia, in generale, il compilatore è costretto a rispettare le scritture volatili. Se hai reso la variabile volatile, non dovrebbe essere in grado di rimuovere le scritture.

+0

Quello che scrivi su volatile è probabilmente un suggerimento su come sarebbe la risposta a questa domanda (il modello dovrebbe probabilmente usare volatile nel distruttore), ma a me sembra che risponda effettivamente alla domanda (la variabile protetta non è marcata come volatile e non mi aspetto che sia contrassegnato come tale, questo dovrebbe essere gestito da un modello se necessario). – Suma

+1

@Suma: non importa che la variabile stessa non sia volatile. È sufficiente che la scrittura sia attraverso un'espressione volatile, come '* const_cast (this)'. Rendere volatile la variabile assicura che 'questo 'stesso sia' Foo volatile * 'ovunque senza calchi, ma nel dtor è necessaria solo un'espressione volatile. – MSalters