L'uso di un compilatore C obsoleto rappresenta un rischio per la sicurezza?


140

Abbiamo alcuni sistemi di produzione in produzione di cui nessuno si preoccupa e queste macchine eseguono versioni antiche di GCC come GCC 3 o GCC 2.

E non riesco a convincere il management ad aggiornarlo a uno più recente: dicono "se non è rotto, non aggiustarlo".

Dato che manteniamo una base di codice molto vecchia (scritta negli anni '80), questo codice C89 si adatta perfettamente a questi compilatori.

Ma non sono sicuro che sia una buona idea usare queste vecchie cose.

La mia domanda è:

L'uso di un vecchio compilatore C può compromettere la sicurezza del programma compilato?

AGGIORNARE:

Lo stesso codice è stato creato da Visual Studio 2008 per destinazioni Windows e MSVC non supporta ancora C99 o C11 (non so se MSVC più recente lo fa) e posso costruirlo sul mio box Linux usando l'ultimo GCC. Quindi, se avessimo appena inserito un GCC più recente, probabilmente sarebbe stato costruito bene come prima.


5
Domanda interessante - questo potrebbe valere anche una breve lettura - developers.slashdot.org/story/13/10/29/2150211/… .. così i compilatori più recenti potrebbero anche compromettere la sicurezza durante l'ottimizzazione.
Neil,

6
Quelle vecchie versioni di gcc supportano la compilazione in PIC / PIE per ASLR? Supportano stack canarini? W ^ X (NX)? In caso contrario, la mancanza di mitigazioni per le vulnerabilità è una buona ragione per l'aggiornamento.
EOF,

12
Basta guardare gli avvertimenti di gcc 4.x per rivelare immediatamente un intero carico di falle di sicurezza esistenti che non sapevi di avere.
OrangeDog,

7
@OrangeDog: perché gcc 4.x? gcc6 è l'attuale serie di versioni e gcc 5 è in circolazione da un po 'di tempo. Ma sì, risolvere eventuali problemi identificati da -O3 -Wall -Wextra -fsanitize=undefinedgcc e clang moderni dovrebbe aiutare.
Peter Cordes,

4
@OrangeDog GCC è passato ai numeri di versione di marketing. GCC 5 meritava un grosso bump della versione, perché avevano cambiato gli standard C e C ++ predefiniti e l'ABI libstdc ++. GCC 6 avrebbe dovuto essere chiamato 5.1.
zwol,

Risposte:


102

In realtà direi il contrario.

Ci sono un certo numero di casi in cui il comportamento non è definito dallo standard C, ma è ovvio cosa succederebbe con un "compilatore stupido" su una data piattaforma. Casi come consentire l'overflow di un numero intero con segno o l'accesso alla stessa memoria tramite variabili di due tipi diversi.

Le versioni recenti di gcc (e clang) hanno iniziato a trattare questi casi come opportunità di ottimizzazione che non interessano se cambiano il comportamento del binario nella condizione di "comportamento indefinito". Questo è molto brutto se la tua base di codice è stata scritta da persone che hanno trattato C come un "assemblatore portatile". Col passare del tempo, gli ottimizzatori hanno iniziato a guardare blocchi di codice sempre più grandi quando eseguivano queste ottimizzazioni aumentando le possibilità che il binario finisse per fare qualcosa di diverso da "cosa farebbe un binario creato da un compilatore stupido".

Ci sono opzioni del compilatore per ripristinare il comportamento "tradizionale" (-fwrapv e -fno-strict-aliasing per i due che ho menzionato sopra), ma prima devi conoscerli.

Mentre in linea di principio un bug del compilatore potrebbe trasformare il codice conforme in una falla di sicurezza, considererei il rischio di ciò trascurabile nel grande schema delle cose.


13
Ma questo argomento funziona in entrambi i modi. Se un compilatore ha un "comportamento indefinito" prevedibile, allora può essere probabilmente più facile utilizzarlo in modo dannoso ...
André,

18
@Andre Il codice compilato ha comunque un comportamento indefinibile prevedibile. Cioè, una volta compilato il codice, qualsiasi comportamento imprevedibile è ora prevedibile, in quella particolare versione compilata.
user253751,

6
people who treated C like a "portable assembler"non è quello che C è però?
Max

10
@Max Questa risposta avverte proprio del fatto che il concetto di "assemblatore portatile" è almeno obsoleto nella pratica, a causa dei moderni ottimizzatori. E direi che all'inizio non è mai stato concettualmente corretto.
Theodoros Chatzigiannakis,

6
Nessuna simpatia qui per coloro che si affidano a comportamenti indefiniti e in seguito iniziano a raccogliere ciò che hanno seminato. Ciò non significa che i compilatori più recenti siano intrinsecamente meno sicuri - significa che il codice non conforme era una bomba a orologeria. La colpa dovrebbe essere ripartita di conseguenza.
underscore_d

52

Vi sono rischi in entrambi i corsi d'azione.


I compilatori più vecchi hanno il vantaggio della maturità, e tutto ciò che è stato rotto in loro probabilmente (ma non c'è garanzia) è stato risolto con successo.

In questo caso, un nuovo compilatore è una potenziale fonte di nuovi bug.


D'altra parte, i compilatori più recenti sono dotati di strumenti aggiuntivi :

  • GCC e Clang ora dispongono entrambi di disinfettanti che possono strumentare il tempo di esecuzione per rilevare comportamenti indefiniti di vario genere (Chandler Carruth, del team di Google Compiler, ha affermato l'anno scorso che si aspetta che abbiano raggiunto la piena copertura)
  • Clang, almeno, caratterizza l' indurimento , ad esempio Integrità del flusso di controllo riguarda il rilevamento di hi-jack del flusso di controllo, ci sono anche strumenti di indurimento per proteggere dagli attacchi di schiacciamento dello stack (separando la parte del flusso di controllo dello stack dalla parte di dati) ; le funzionalità di tempra sono generalmente basse (<1% di sovraccarico della CPU)
  • Clang / LLVM sta anche lavorando su libFuzzer , uno strumento per creare unit-fuzzing unit-test che esplorano lo spazio di input della funzione sotto test in modo intelligente (modificando l'input per prendere percorsi di esecuzione non ancora esplorati)

Strumentare il tuo binario con i disinfettanti (Address Sanitizer, Memory Sanitizer o Undefined Behavior Sanitizer) e quindi sfocarlo (usando American Fuzzy Lop per esempio) ha scoperto vulnerabilità in un numero di software di alto profilo, vedi ad esempio questo articolo di LWN.net .

Quei nuovi strumenti e tutti gli strumenti futuri sono inaccessibili per te a meno che non aggiorni il tuo compilatore.

Rimanendo su un compilatore poco potente, stai mettendo la testa nella sabbia e incrociando le dita che non viene rilevata alcuna vulnerabilità. Se il tuo prodotto è un obiettivo di alto valore, ti esorto a riconsiderare.


Nota: anche se NON si aggiorna il compilatore di produzione, è possibile che si desideri utilizzare un nuovo compilatore per verificare comunque la vulnerabilità; tieni presente che, poiché si tratta di compilatori diversi, le garanzie sono tuttavia ridotte.


1
+1 per preoccuparsi di menzionare i casi in cui i nuovi compilatori possono essere più sicuri, piuttosto che accumularsi sul carrozzone "b-my my good UB" delle altre risposte. questo è in aggiunta a molti altri miglioramenti che offrono che non sono direttamente correlati alla sicurezza ma forniscono ancora più slancio per essere ragionevolmente moderni.
underscore_d

Sebbene sia come sostenere la "sicurezza attraverso l'oscurità"; i bug che colpiscono i vecchi compilatori sono noti e pubblici. Mentre sono d'accordo che i nuovi compilatori introdurranno bug, questi bug non sono ancora pubblici come quelli delle versioni precedenti, il che è un po 'di sicurezza se si aggiorna spesso l'applicazione.
The6P4C,

Chandler Carruth è così carino e parla di cose così meravigliose. Lo sposerei se potessi.
Daniel Kamil Kozar,

46

Il codice compilato contiene bug che potrebbero essere sfruttati. I bug provengono da tre fonti: bug nel codice sorgente, bug nel compilatore e nelle librerie e comportamento indefinito nel codice sorgente che il compilatore trasforma in un bug. (Il comportamento indefinito è un bug, ma non ancora un bug nel codice compilato. Ad esempio, i = i ++; in C o C ++ è un bug, ma nel tuo codice compilato può aumentare i di 1 ed essere Ok, oppure impostare io un po 'di spazzatura ed essere un bug).

Il tasso di bug nel codice compilato è presumibilmente basso a causa dei test e della correzione dei bug a causa delle segnalazioni di bug dei clienti. Quindi potrebbe esserci stato un gran numero di bug inizialmente, ma questo è andato giù.

Se esegui l'upgrade a un compilatore più recente, potresti perdere bug introdotti dai bug del compilatore. Ma questi bug sarebbero tutti bug che a tua conoscenza nessuno ha trovato e nessuno ha sfruttato. Ma il nuovo compilatore può avere bug da solo, e soprattutto i compilatori più recenti hanno una tendenza più forte a trasformare il comportamento indefinito in bug nel codice compilato.

Quindi avrai un sacco di nuovi bug nel tuo codice compilato; tutti i bug che gli hacker potevano trovare e sfruttare. E a meno che tu non faccia un sacco di test e lasci il tuo codice ai clienti per trovare bug per lungo tempo, sarà meno sicuro.


6
Quindi, in altre parole ... non esiste un modo semplice per dire quali problemi presenta il compilatore e cambiando tutto ciò che fai è ottenere una serie diversa di problemi sconosciuti?
Jeremy Kato,

1
@JeremyKato: beh, ci sono alcuni casi in cui stai ricevendo anche una serie diversa di problemi noti. Non sono sicuro di quali siano i difetti di sicurezza noti nel compilatore stesso, ma per un esempio concreto supponiamo che l'aggiornamento a un nuovo compilatore significhi anche essere in grado di prendere l'ultimo libc (mentre usare quello vecchio significa non essere in grado per farlo), allora sapresti che stai risolvendo questo difetto getaddrinfo(): access.redhat.com/articles/2161461 . Quell'esempio non è in realtà un difetto di sicurezza del compilatore, ma in oltre 10 anni ci sono sicuramente alcuni difetti fissi noti.
Steve Jessop,

2
Heh, in realtà quel difetto è stato introdotto solo nel 2008, quindi l'interrogante potrebbe essere al sicuro da esso. Ma il mio punto non riguarda quel particolare esempio, è che esistono bug noti che una vecchia toolchain inserirà nel tuo codice. Quindi, quando si aggiorna, è vero che si introduce una nuova serie di incognite, ma non è tutto ciò che si fa . Fondamentalmente devi solo indovinare se sei "più sicuro" lasciando nel noto difetto critico che la più recente correzione della toolchain o se stai prendendo le conseguenze sconosciute di lanciare di nuovo i dadi su tutto il comportamento indefinito nel tuo codice.
Steve Jessop,

19

Se non si è rotto, non aggiustarlo

Il tuo capo suona bene nel dire questo, tuttavia, il fattore più importante è la protezione di input, output, buffer overflow. La mancanza di questi è invariabilmente l'anello più debole della catena da quel punto di vista, indipendentemente dal compilatore utilizzato.

Tuttavia, se la base di codice è antica e sono stati messi in atto lavori per mitigare i punti deboli del K&R C utilizzato, come la mancanza di sicurezza del tipo, budget insicuri, ecc., Soppeserebbe la domanda " Aggiornerebbe il compilatore al più moderno C99 / Gli standard C11 infrangono tutto? "

A condizione che esista un percorso chiaro per migrare ai più recenti standard C, che potrebbero indurre effetti collaterali, potrebbe essere meglio tentare un fork del vecchio codice di base, valutarlo e inserire ulteriori controlli di tipo, controlli di integrità e determinare se l'aggiornamento a il compilatore più recente ha alcun effetto sui set di dati di input / output.

Quindi puoi mostrarlo al tuo capo, " Ecco la base di codice aggiornata, refactored, più in linea con gli standard C99 / C11 accettati dal settore ... ".

Questa è la scommessa che dovrebbe essere ponderata, con molta attenzione , la resistenza al cambiamento potrebbe mostrarsi lì in quell'ambiente e potrebbe rifiutare di toccare le cose più nuove.

MODIFICARE

Sono rimasto indietro per qualche minuto, capito così tanto, il codice generato da K&R potrebbe essere in esecuzione su una piattaforma a 16 bit, è probabile che l'aggiornamento a un compilatore più moderno potrebbe effettivamente rompere la base di codice, sto pensando in termini di architettura, il codice a 32 bit verrebbe generato , questo potrebbe avere effetti collaterali divertenti sulle strutture utilizzate per i set di dati di input / output, che è un altro enorme fattore da valutare attentamente.

Inoltre, dal momento che OP ha menzionato l'uso di Visual Studio 2008 per costruire la base di codice, l'uso di gcc potrebbe indurre a portare nell'ambiente MinGW o Cygwin, che potrebbe avere un impatto sull'ambiente, a meno che la destinazione sia per Linux, quindi sarebbe vale la pena provare, potrebbe essere necessario includere ulteriori switch per il compilatore per ridurre al minimo il rumore sulla vecchia base di codice K&R, l'altra cosa importante è eseguire molti test per assicurarsi che nessuna funzionalità venga interrotta, potrebbe rivelarsi un esercizio doloroso.


Lo stesso codice è stato creato da Visual Studio 2008 per destinazioni Windows e MSVC non supporta ancora C99 o C11 (non so se MSVC più recente lo fa) e posso costruirlo sul mio box Linux usando l'ultimo GCC. Quindi, se avessimo appena inserito un GCC più recente, probabilmente sarebbe stato costruito bene come prima.
Calmarius,

@Calmarius grazie per il testa a testa, forse potrebbe essere meglio modificare la tua domanda per includere il commento, è importante :) E avrebbe dovuto essere lì; D
t0mm13b

@Calmarius ha modificato la mia risposta, che è il mio pensiero sulla domanda recentemente aggiornata.
t0mm13b,

2
"Potrebbe essere in esecuzione su una piattaforma a 16 bit, è probabile che l'aggiornamento a un compilatore più moderno potrebbe effettivamente rompere la base di codice, sto pensando in termini di architettura, codice a 32 bit" Non penso che la domanda riguardi il porting del codice su una nuova definizione definita dall'implementazione parametri.
Pascal Cuoq,

Concordato. È possibile che una vulnerabilità di runtime possa essere creata da un bug del compilatore. Ma è molto più probabile che il codice contenga vulnerabilità di runtime dovute a sovraccarico di buffer e stack. Quindi, quando investi tempo nel rendere questa base di codice più sicura, dovresti investirla nel fare cose come controllare la lunghezza delle stringhe di input per assicurarti che non superino i limiti del tuo programma. Ottenere un compilatore più recente non sarà di grande aiuto. Riscrivere il codice da zero in una lingua con stringa nativa e oggetti array aiuterà molto. Ma il tuo capo non pagherà per quello.
O. Jones,

9

L'uso di un vecchio compilatore C può compromettere la sicurezza del programma compilato?

Certo che può, se il vecchio compilatore contiene bug conosciuti che potrebbero influire sul programma.

La domanda è, vero? Per essere sicuro, dovresti leggere l'intero registro delle modifiche dalla tua versione alla data attuale e controllare ogni singolo bug corretto negli anni.

Se non trovi prove di bug del compilatore che potrebbero influenzare il tuo programma, aggiornare GCC solo per il gusto di farlo sembra un po 'paranoico. Dovresti tenere presente che le versioni più recenti potrebbero contenere nuovi bug, non ancora scoperti. Molte modifiche sono state apportate di recente con il supporto di GCC 5 e C11.

Detto questo, il codice scritto negli anni '80 è molto probabilmente già colmo di falle di sicurezza e dipendenza da comportamenti mal definiti, indipendentemente dal compilatore. Stiamo parlando di C pre-standard qui.


6
Non penso sia paranoia; Penso che l'OP stia cercando di inventare ragioni per convincere il suo capo. Probabilmente l'OP in realtà vuole un nuovo compilatore perché rende meglio l'asm (inclusa l'ottimizzazione dei file incrociati con LTO), ha diagnosi / avvertenze più utili e consente funzionalità e sintassi del linguaggio moderno. (es. C11 stdatomic).
Peter Cordes,

9

Esiste un rischio per la sicurezza in cui uno sviluppatore malintenzionato può intrufolarsi in una backdoor attraverso un bug del compilatore. A seconda della quantità di bug noti nel compilatore in uso, la backdoor può apparire più o meno appariscente (in ogni caso, il punto è che il codice è corretto, anche se contorto, a livello di sorgente. Il codice sorgente rivede e verifica usando un compilatore senza buggy non troverà la backdoor, perché la backdoor non esiste in queste condizioni). Per ulteriori punti di negabilità, lo sviluppatore malintenzionato può anche cercare da solo bug del compilatore precedentemente sconosciuti. Ancora una volta, la qualità del camuffamento dipenderà dalla scelta dei bug del compilatore trovati.

Questo attacco è illustrato nel programma sudo in questo articolo . bcrypt ha scritto un ottimo follow-up per i minificatori Javascript .

Oltre a questa preoccupazione, l'evoluzione dei compilatori C è stata quella di sfruttare un comportamento indefinito di più e più e più aggressivo, così vecchio codice C che è stato scritto in buona fede sarebbe in realtà essere più sicuro compilato con un compilatore C dal momento, o compilato a -O0 (ma alcune nuove ottimizzazioni per lo sfruttamento UB del programma vengono introdotte in nuove versioni dei compilatori anche a -O0 ).


7

I compilatori meno recenti potrebbero non avere protezione contro attacchi di hacking noti. La protezione da danneggiamento dello stack, ad esempio, non è stata introdotta fino a GCC 4.1 . Quindi sì, il codice compilato con compilatori più vecchi potrebbe essere vulnerabile nei modi in cui i compilatori più recenti proteggono.


6

Un altro aspetto di cui preoccuparsi è lo sviluppo di nuovo codice .

I compilatori più vecchi possono avere un comportamento diverso per alcune funzionalità del linguaggio rispetto a quanto standardizzato e previsto dal programmatore. Questa discrepanza può rallentare lo sviluppo e introdurre bug sottili che possono essere sfruttati.

I compilatori meno recenti offrono meno funzionalità (comprese le funzionalità del linguaggio!) E non ottimizzano altrettanto. I programmatori si aggireranno per ovviare a queste carenze, ad esempio reimplementando funzionalità mancanti o scrivendo un codice intelligente che è oscuro ma corre più veloce, creando nuove opportunità per la creazione di bug sottili.


5

no

Il motivo è semplice, il vecchio compilatore potrebbe avere vecchi bug e exploit, ma il nuovo compilatore avrà nuovi bug e exploit.

Non "correggere" alcun bug eseguendo l'aggiornamento a un nuovo compilatore. Il passaggio di vecchi bug e exploit per nuovi bug e exploit.


3
Questi sembrano molto semplicistici: il nuovo compilatore potrebbe avere i suoi punti deboli, ma mi aspetto che siano inferiori a quelli del vecchio compilatore ed è probabile che rilevi diverse vulnerabilità del codice che sono diventate note da allora.
PJTraill

Ma il nuovo compilatore potrebbe avere nuove debolezze sconosciute. Il compilatore da solo non rappresenta un rischio per la sicurezza che deve essere aggiornato. Non stai riducendo la tua superficie. Il tuo trading di una serie nota di problemi con una serie sconosciuta.
Coteyr,

Gli strumenti per aiutare a trovare i bug sono migliorati enormemente dai primi giorni del GCC e questi strumenti (analisi statica, analisi dinamica del codice strumentata / sanificanti, fuzzer, ecc.) Sono stati applicati anche al codice del compilatore, per contribuire a migliorare la qualità. È stato molto più difficile trovare tutte le classi di bug nell'era GCC 2. Confronto dei bug del compilatore rispetto alle versioni - vedere pagina 7: cs.utah.edu/~regehr/papers/pldi11-preprint.pdf GCC 4.5 e LLVM 2.8 (più recenti alla pubblicazione) hanno il minor numero di bug di fuzzing.
Jetski S-type

2

Bene, c'è una maggiore probabilità che tutti i bug nel vecchio compilatore siano ben noti e documentati rispetto all'utilizzo di un nuovo compilatore, in modo da poter intraprendere azioni per evitare tali bug codificandoli. Quindi in un modo che non è abbastanza come argomento per l'aggiornamento. Abbiamo le stesse discussioni in cui lavoro, usiamo GCC 4.6.1 su una base di codice per software incorporato e c'è una grande riluttanza (tra i dirigenti) ad aggiornare all'ultimo compilatore a causa della paura di nuovi bug non documentati.


0

La tua domanda si divide in due parti:

  • Esplicito: "Esiste un rischio maggiore nell'uso del compilatore più vecchio" (più o meno come nel tuo titolo)
  • Implicito: "Come posso convincere il management ad aggiornare"

Forse puoi rispondere ad entrambi trovando un difetto sfruttabile nella tua base di codice esistente e mostrando che un compilatore più recente l'avrebbe rilevato. Ovviamente il tuo management potrebbe dire "l'hai scoperto con il vecchio compilatore", ma puoi sottolineare che è costato un notevole sforzo. Oppure lo esegui attraverso il nuovo compilatore per trovare la vulnerabilità, quindi sfruttarla, se sei in grado / autorizzato a compilare il codice con il nuovo compilatore. Potresti chiedere aiuto a un hacker amichevole, ma ciò dipende dalla fiducia in loro e dall'essere in grado / permesso di mostrare loro il codice (e usare il nuovo compilatore).

Ma se il tuo sistema non è esposto agli hacker, dovresti forse essere più interessato al fatto che un aggiornamento del compilatore aumenterebbe la tua efficacia: MSVS 2013 Code Analysis abbastanza spesso trova potenziali bug molto prima di MSVS 2010 e supporta più o meno C99 / C11 - non sono sicuro che lo faccia ufficialmente, ma le dichiarazioni possono seguire le istruzioni e puoi dichiarare le variabili in for-loops.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.