Alloca gli oggetti da un pool di memoria con un anti-pattern di sicurezza?

6

Sulla scia del bug heartbleed , OpenSSL è stato giustamente criticato per l'utilizzo del proprio freelist. Con% -cos% co_de semplice, l'errore sarebbe stato quasi certamente trovato molto tempo fa.

Tuttavia, alcuni tipi di wrapping di malloc sono in realtà molto comuni. Ad esempio, quando ho un oggetto grande che possiede molti piccoli oggetti di dimensioni fisse, li alloco spesso da un array.

/* BigObj is allocated with 'malloc', but LittleObj using 'alloc_little'. */
typedef struct {

    /* bigObj stuff here */

    int nlittle;
    LittleObj littles[MAX_LITTLE];
} BigObj; 

LittleObj *alloc_little(BigObj *big)
{
    if(big->nlittle == MAX_LITTLE)
        return NULL;
    return = big->littles + big->nlittle++;
}

malloc è più veloce di alloc_little , ma la ragione principale è la semplicità. Non c'è nessun malloc() da chiamare, perché free_little rimane valido fino a quando il LittleObjs non viene distrutto.

Quindi questo è un anti-pattern? In tal caso, posso mitigare i rischi o dovrei semplicemente abbandonarlo? Domande simili vanno per qualsiasi tipo di pool di memoria, come GNU Obstacks e BigObj utilizzato in Samba.

    
posta Adrian Ratnapala 01.05.2014 - 08:40
fonte

3 risposte

3

No, assolutamente no. E proporre malloc () come la soluzione è semplicemente terribile.

Le parti complesse del software di sistema hanno quasi sempre requisiti di memoria speciali e molto spesso la soluzione implica l'implementazione di uno o più allocatori di memoria dei clienti. Riusare oggetti da una lista libera è una buona strategia. Altri includono pool di memoria, allocatori di slab, allocatori di stack, conteggio dei riferimenti e persino garbage collection.

C / C ++ sembra essere uno strumento eccezionalmente fine per implementare gli allocatori di memoria, ma fornisce pochissima protezione da decisioni sbagliate o cattiva esecuzione. L'errore in OpenSSL non era la decisione di utilizzare una lista libera, ma di farlo in modo così grave.

Direi che la principale strategia di mitigazione del rischio, dopo aver fatto buone scelte e scritto un buon codice, è un esauriente test unitario, comprese le modalità di guasto e le prestazioni. Non uscire di casa senza di esso.

    
risposta data 10.05.2014 - 16:35
fonte
7

No.

C non è sicuro. C non è amichevole. C non cattura i tuoi errori per te. Come dici tu, l'utilizzo di un allocatore diverso potrebbe aver preso l'errore prima - ma non l'avrebbe impedito in primo luogo, in fase di progettazione. Probabilmente lo stesso errore o un errore simile sarebbe stato fatto, e mentre altre strategie potrebbero aver contribuito a catturarlo prima, lo sarebbe ancora dopo il fatto.

L'antipattern si sta dimenticando di questo e sta progettando codice che può potenzialmente traboccare o fare accessi non validi in primo luogo, che non verifica né ciò che viene chiesto di fare né dimostra che non lo farà fare qualcosa di pericoloso in fase di compilazione. Quindi c'è un antipattern qui se solo "non traccia" un LittleObj : gestisci questo con la possibilità di dimostrare dove i loro proprietari sono in ogni momento ( e che è meccanicamente impossibile per il proprietario di un LittleObj sopravvivere al suo pool associato), non sperando che un comportamento indefinito evidenzi un errore più tardi.

Esaminare questo tipo di tecnica come antipattern rende meno facile l'uso di C per uno dei suoi punti di forza: implementare dettagli di runtime di basso livello, non sicuri, in modo che i linguaggi e i sistemi di livello superiore possano essere costruiti su di esso ( non tutti i GC o altri runtime possono essere costruiti su malloc in modo efficace). In caso di dubbio, meglio non usare C per le complesse cose di alto livello - lascia che sia un sistema del tutto più sicuro piuttosto che provare a martellare C in uno strumento che non è, e lasciare che una macchina provi o assicuri che il tuo il programma è sicuro (linguaggio di programmazione più severo, API restrittiva, specifiche controllabili dalla macchina, ecc.). Altrimenti, quali implementerebbero le funzionalità di basso livello in?

Il controesempio finale è probabilmente ML Kit , il cui backend implementato da C utilizza regioni - una forma molto più avanzata della stessa idea di base dei pool - combinata con il sistema di tipo SML ironclad per prevenire accessi non validi alla memoria.

    
risposta data 01.05.2014 - 09:42
fonte
0

di causa, non è un anti-pattern, è solo un altro modo per sparare alla tua gamba può essere utile o non può essere utile, a seconda dello scenario di utilizzo
ci sono un sacco di tecniche per usare la pre-allocazione statica della memoria (e non usare malloc / free) e tali tecniche spesso usano nei dispositivi embedded
ma, utilizzando tali tecniche, è necessario comprendere chiaramente che sarà possibile esporre i dati a un altro utente dell'applicazione. ad esempio, se si utilizza la memoria pre-allocata su primitivi di tipo drammatico sullo schermo, è generalmente sicuro, se si utilizza tale memoria per restituire i dati firmati (con chiave memorizzata in alcuni dati di memorizzazione crittografica), è necessario cancellare i dati prima di restituire il record di memoria all'utente .
Voglio dire, quindi qualcuno chiama il tuo alloc_little () restituisce un po 'di memoria e può contenere dati precedenti. Se il tuo codice non funziona con alcuni dati sensibili (è davvero molto difficile specificare cosa sia un dato sensibile) - non puoi pulire la memoria. Se si pensa, i dati gestiti all'interno di LittleObj sono sensibili - è necessario cancellare questa memoria prima dell'uso.
Inoltre, devi tenere traccia di tutte le memorizzazioni allocate, perché su può chiamare alloc_little () / free_little () e mantenere l'indirizzo di struct. e periodicamente lo chiedono informazioni.
Quindi, non è un antipattern, in alcuni casi è una tecnica utile, ma dovresti capire chiaramente cosa fai e chi può accedere a questa memoria.

    
risposta data 09.05.2014 - 16:55
fonte

Leggi altre domande sui tag