Perché è così difficile rendere C meno incline a bufferizzare gli overflow?

23

Sto facendo un corso al college, dove uno dei laboratori è quello di eseguire exploit di buffer overflow sul codice che ci danno. Questo va da semplici exploit come cambiare l'indirizzo di ritorno di una funzione su uno stack per tornare a una funzione diversa, fino al codice che cambia uno stato di registro / memoria di un programma ma poi ritorna alla funzione che hai chiamato, il che significa che il la funzione che hai chiamato è completamente ignara dell'exploit.

Ho fatto qualche ricerca su questo, e questo tipo di exploit sono usati praticamente ovunque, anche ora, in cose come che eseguono homebrew sul Wii e il jailbreak untethered per iOS 4.3.1

La mia domanda è: perché questo problema è così difficile da risolvere? È ovvio che questo è uno dei principali exploit usato per hackerare centinaia di cose, ma sembra che sarebbe piuttosto facile da risolvere semplicemente troncando qualsiasi input oltre la lunghezza consentita e semplicemente disinfettando tutti gli input che prendi.

EDIT: un'altra prospettiva che mi piacerebbe che le risposte prendessero in considerazione: perché i creatori di C non risolvono questi problemi reimplementando le librerie?

    
posta Ankit Soni 18.02.2012 - 10:14
fonte

9 risposte

35

Hanno risolto le librerie.

Ogni moderna libreria standard C contiene varianti più sicure di strcpy , strcat , sprintf e così via.

Sui sistemi C99 - che è la maggior parte degli Unix - troverai questi con nomi come strncat e snprintf , la "n" che indica che accetta un argomento che è la dimensione di un buffer o un numero massimo di elementi a copia.

Queste funzioni possono essere utilizzate per gestire molte operazioni in modo più sicuro, ma in retrospettiva la loro usabilità non è eccezionale. Ad esempio alcune implementazioni snprintf non garantiscono che il buffer sia terminato con null. strncat prende un numero di elementi da copiare, ma molte persone erroneamente passano le dimensioni del buffer di destinazione.

Su Windows, si trova spesso strcat_s , sprintf_s , il suffisso "_s" che indica "sicuro". Anche questi hanno trovato la loro strada nella libreria C standard in C11, e forniscono un maggiore controllo su ciò che accade in caso di un overflow (troncamento vs. assert per esempio).

Molti venditori offrono ancora più alternative non standard come asprintf nella libc GNU, che allocherà automaticamente un buffer delle dimensioni appropriate.

L'idea che tu possa "aggiustare la C" è un equivoco. La correzione di C non è il problema - ed è già stata fatta. Il problema consiste nel fissare decadi di codice C scritti da programmatori ignoranti, stanchi o frettolosi, o codice che è stato trasferito da contesti in cui la sicurezza non era rilevante per i contesti in cui la sicurezza si verifica. Nessuna modifica alla libreria standard può risolvere questo codice, sebbene la migrazione a nuovi compilatori e librerie standard possa spesso aiutare a identificare automaticamente i problemi.

    
risposta data 18.02.2012 - 16:08
fonte
19

Non è davvero impreciso dire che C è in realtà "soggetto a errori" da design . A parte alcuni gravi errori come gets , il linguaggio C non può essere in alcun altro modo senza perdere la funzione principale che attira le persone in C in primo luogo.

C è stato progettato come un linguaggio di sistema per agire come una sorta di "assemblaggio portatile". Una delle caratteristiche principali del linguaggio C è che a differenza dei linguaggi di livello superiore, il codice C spesso mappa molto attentamente il codice macchina attuale. In altre parole, ++i è in genere solo un'istruzione inc e spesso è possibile avere un'idea generale di ciò che il processore eseguirà in fase di esecuzione osservando il codice C.

Ma l'aggiunta del controllo dei limiti impliciti aggiunge un sovraccarico extra - sovraccarico che il programmatore non ha chiesto e potrebbe non volere. Questo sovraccarico va ben oltre la memoria aggiuntiva necessaria per archiviare la lunghezza di ciascun array o le istruzioni aggiuntive per controllare i limiti degli array su ogni accesso di array. E riguardo l'aritmetica del puntatore? O cosa succede se hai una funzione che accetta un puntatore? L'ambiente runtime non ha modo di sapere se quel puntatore rientra nei limiti di un blocco di memoria legittimamente allocato. Per tenerne traccia, è necessaria una seria architettura di runtime in grado di controllare ogni puntatore su una tabella di blocchi di memoria attualmente allocati, al punto in cui stiamo già entrando nel territorio di runtime gestito in stile Java / C #.

    
risposta data 18.02.2012 - 21:34
fonte
15

Penso che il vero problema non sia che questi tipi di bug siano difficili da risolvere, ma che siano così facili da fare: se usi strcpy , sprintf e amici nel modo (apparentemente) più semplice che può funzionare, quindi probabilmente hai aperto la porta per un overflow del buffer. E nessuno se ne accorgerà fino a quando qualcuno non lo sfrutterà (a meno che tu non abbia ottime recensioni di codice). Aggiungiamo ora il fatto che ci sono molti programmatori mediocri e che sono sotto pressione per la maggior parte del tempo - e avete una ricetta per il codice che è così ridondante con buffer overflow che sarà difficile per sistemarli tutti semplicemente perché ce ne sono così tanti e si nascondono così bene.

    
risposta data 18.02.2012 - 10:30
fonte
7

È difficile fissare buffer overflow perché C non fornisce praticamente strumenti utili per risolvere il problema. È un difetto linguistico fondamentale che i buffer nativi non forniscono alcuna protezione e è praticamente, se non completamente, impossibile sostituirli con un prodotto superiore, come il C ++ ha fatto con std::vector e std::array , e è difficile anche in modalità debug per trovare buffer overflow.

    
risposta data 18.02.2012 - 10:47
fonte
7

Il problema non è con la C lingua .

IMO, l'unico grande ostacolo da superare è che C è semplicemente insegnato male . Decenni di cattive pratiche e informazioni errate sono stati istituzionalizzati in manuali di riferimento e appunti di lezioni, avvelenando le menti di ogni nuova generazione di programmatori fin dall'inizio. Agli studenti viene fornita una breve descrizione delle funzioni di I / O "facili" come gets 1 o scanf e quindi lasciati ai propri dispositivi. Non viene detto dove o come questi strumenti possono fallire, o come prevenirli. Non viene detto loro di usare fgets e strtol/strtod perché sono considerati strumenti "avanzati". Poi si scatenano nel mondo professionale per scatenare il loro caos. Non che molti dei programmatori più esperti ne sappiano di migliori, perché hanno ricevuto la stessa istruzione danneggiata dal cervello. È esasperante. Vedo così tante domande qui e su Stack Overflow e su altri siti in cui è chiaro che la persona che pone la domanda viene insegnata da qualcuno che semplicemente non sa di cosa sta parlando , e di Certo, non puoi semplicemente dire "il tuo professore è sbagliato", perché è un professore e tu sei solo un ragazzo su Internet.

E poi hai la folla che disapprova ogni risposta che inizia con, "beh, secondo lo standard della lingua ..." perché stanno lavorando nel mondo reale e secondo loro lo standard non si applica al mondo reale . Posso trattare con qualcuno che ha appena una cattiva istruzione, ma chiunque insiste a essere ignorante è solo una piaga del settore.

Non ci sarebbero problemi di overflow del buffer se la lingua fosse stata insegnata correttamente con enfasi sulla scrittura di codice sicuro. Non è "difficile", non è "avanzato", sta solo facendo attenzione.

Sì, questa è stata una sfuriata.

1 Che, per fortuna, è stato finalmente strappato dalle specifiche del linguaggio, sebbene si nasconderà per sempre in un codice legacy di 40 anni.     
risposta data 20.02.2012 - 18:01
fonte
5

Il problema è tanto di miopia manageriale che di incompetenza del programmatore. Ricorda, un'applicazione di 90.000 righe richiede solo una operazione non sicura per essere completamente non sicuro. È quasi oltre il regno delle possibilità che qualsiasi applicazione scritta su una gestione delle stringhe fondamentalmente insicura sia perfetta al 100% - il che significa che sarà insicuro.

Il problema è che i costi di essere insicuri non vengono addebitati al destinatario giusto (la società che vende l'app non dovrà quasi mai rimborsare il prezzo di acquisto) o non sono chiaramente visibili al momento in cui vengono prese le decisioni ("Noi devo spedire a marzo, non importa cosa! "). Sono abbastanza certo che se consideri costi e costi a lungo termine per i tuoi utenti piuttosto che profitti della tua azienda, scrivere in C o lingue correlate sarebbe molto più costoso, probabilmente così costoso che è chiaramente la scelta sbagliata in molti campi in cui oggigiorno la saggezza convenzionale afferma che è una necessità. Ma ciò non cambierà a meno che non venga introdotta una responsabilità software molto più rigida, che nessuno nel settore vuole.

    
risposta data 18.02.2012 - 10:53
fonte
4

Uno dei grandi poteri dell'uso di C è che ti permette di manipolare la memoria in qualsiasi modo tu ritenga opportuno.

Uno dei grandi punti deboli dell'uso di C è che ti consente di manipolare la memoria in qualsiasi modo tu ritenga opportuno.

Ci sono versioni sicure di tutte le funzioni non sicure. Tuttavia, i programmatori e il compilatore non impongono rigorosamente il loro uso.

    
risposta data 20.02.2012 - 09:39
fonte
2

why do the creators of C not fix these issues by reimplementing the libraries?

Probabilmente perché il C ++ lo ha già fatto ed è retrocompatibile con il codice C. Quindi se vuoi un tipo di stringa sicuro nel tuo codice C, devi semplicemente usare std :: string e scrivere il tuo codice C usando un compilatore C ++.

Il sottosistema di memoria sottostante può aiutare a prevenire gli overflow del buffer introducendo i blocchi di guardia e il controllo della validità di essi - così tutte le allocazioni hanno 4 byte di "fefefefe" aggiunti, quando questi blocchi sono scritti, il sistema può lanciare un wobbler. Non è garantito che impedisca la scrittura di una memoria, ma mostrerà che qualcosa è andato storto e deve essere corretto.

Penso che il problema sia che le vecchie routine strcpy etc sono ancora presenti. Se fossero stati rimossi a favore di strncpy ecc, sarebbe stato d'aiuto.

    
risposta data 19.02.2012 - 14:39
fonte
-2

È semplice capire perché il problema dell'overflow non è stato risolto. C era difettoso in un paio di aree. All'epoca questi difetti erano visti come tollerabili o addirittura come una caratteristica. Ora, a distanza di decenni, questi difetti non sono risolvibili.

Alcune parti della comunità di programmazione non vogliono che quei buchi siano tappati. Guarda tutte le guerre di fiamma che iniziano su archi, matrici, puntatori, raccolta dei rifiuti ...

    
risposta data 18.02.2012 - 14:17
fonte

Leggi altre domande sui tag