2010-05-11 14 views
7

Sto scrivendo una macchina virtuale in C solo per divertimento. Lame, lo so, ma fortunatamente sono su SO quindi spero che nessuno si diverta :)Scrittura di una VM - bytecode ben formato?

Ho scritto una VM veramente veloce e decifrabile che legge linee di (mio) ASM e fa cose. Al momento, ho solo 3 istruzioni: add, jmp, end. Tutto va bene ed è in realtà piuttosto fresco in grado di alimentare le linee (che lo fanno qualcosa come write_line(&prog[1], "jmp", regA, regB, 0); e quindi eseguire il programma:

while (machine.code_pointer <= BOUNDS && DONE != true) 
{ 
    run_line(&prog[machine.cp]); 
} 

sto usando una tabella di codice operativo di ricerca (che non può essere efficiente ma è elegante) in C e tutto sembra funzionare OK

La mia domanda è più una domanda sulle "migliori pratiche" ma penso che ci sia una risposta corretta. Sto rendendo la VM in grado di leggere i file binari (memorizzazione dei byte in unsigned char[]) ed eseguire bytecode. La mia domanda è: è compito della VM accertarsi che il bytecode sia ben formato o sia solo il il lavoro del compilatore per assicurarsi che il file binario che sputa sia ben formato?

Chiedo solo questo perché cosa succederebbe se qualcuno modificasse un file binario e rovinasse tutto (cancella parti arbitrarie di esso, ecc.). Chiaramente, il programma sarebbe bacato e probabilmente non funzionante. È questo anche il problema della VM? Sono sicuro che le persone molto più intelligenti di me hanno escogitato soluzioni a questi problemi, sono solo curioso di sapere cosa sono!

risposta

14

E 'il lavoro del VM per assicurarsi che il bytecode è ben formato o è solo compito del compilatore assicurarsi che il file binario che sputa sia ben formato?

Si arriva a decidere.

La pratica migliore consiste nel fare un singolo controllo prima dell'esecuzione, il costo dello proporzionale alla dimensione del programma, che è abbastanza sofisticato da garantire che nulla possa accadere durante l'esecuzione. Quindi durante l'effettiva esecuzione del bytecode, si esegue senza controlli. Tuttavia, l'idea di check-before-running può richiedere alcune analisi molto sofisticate, e anche le macchine virtuali più attente alle prestazioni spesso hanno alcuni controlli in fase di esecuzione (esempio: limiti dell'array).

Per un progetto di hobby, manterrei le cose semplici e fare in modo che la VM verifichi il buon funzionamento ogni volta che si esegue un'istruzione. Il sovraccarico per la maggior parte delle istruzioni non sarà troppo grande.

+0

Risposta perfetta, grazie! –

0

Ha senso fare in modo che il compilatore esegua il maggior controllo possibile (poiché deve farlo solo una volta), ma ci saranno sempre problemi che non possono essere rilevati dall'analisi statica, come [cough] overflow dello stack, errori di intervallo dell'array e simili.

1

Lo stesso problema si presenta in Java e, come ricordo, in quel caso la VM deve fare alcuni controlli per assicurarsi che il bytecode sia ben formato. In tale situazione, si tratta di un problema serio a causa dei potenziali problemi di sicurezza: se qualcuno può modificare un file bytecode Java per contenere qualcosa che il compilatore non emetterebbe mai (come accedere a una variabile private di un'altra classe), potrebbe potenzialmente esporre i dati sensibili contenuti nella memoria dell'applicazione o potrebbero consentire all'applicazione di accedere a un sito Web a cui non dovrebbe essere consentito o qualcosa del genere. La macchina virtuale di Java include un verificatore di bytecode per assicurarsi, per quanto possibile, che questo genere di cose non avvenga.

Ora, nel tuo caso, a meno che il tuo linguaggio fatto in casa non decolli e diventi popolare, l'aspetto della sicurezza è qualcosa di cui non devi preoccuparti così tanto; dopo tutto, chi ha intenzione di hackerare i tuoi programmi, a parte te? Tuttavia, direi che è una buona idea assicurarsi che la tua VM abbia almeno una strategia di errore ragionevole per quando il bytecode non è valido. Come minimo, se incontra qualcosa che non capisce e non può elaborare, dovrebbe rilevarlo e fallire con un messaggio di errore, che renderà il debug più facile da parte tua.

+0

Non ho mai avuto l'impressione che rendere privati ​​i membri sia uno strumento di sicurezza. Le mie impressioni sono che si tratta semplicemente di uno strumento di progettazione per fornire buone astrazioni, nascondendo i dettagli di implementazione, ma non considerati sicuri. –

+0

@Chris, l'accesso ai membri 'private' potrebbe non essere un problema di sicurezza da solo, ma immaginare un'ottimizzazione del compilatore basata sul presupposto che tutti i riferimenti e le assegnazioni a una variabile privata siano noti. Ad esempio, il compilatore potrebbe provare che un accesso di matrice non è mai fuori limite a causa di un confronto precedente con una variabile privata nota per essere inferiore alla lunghezza dell'array, quindi elimina il controllo dei limiti. Se il bytecode "illegale" annulla tale presupposto, il controllo dei limiti mancanti potrebbe portare a un problema di sicurezza. –

+1

usando 'Reflection' è possibile accedere a qualsiasi' privato' o altro membro e quindi 'private'ness è semplicemente superficiale e un buon compilatore non si fida di esso –

1

Le macchine virtuali che interpretano il codice byte hanno in genere un modo di convalidare il loro input; ad esempio, Java genererà un VerifyError se il file di classe si trova in uno stato incoerente

Tuttavia, sembra che tu stia implementando un processore e, poiché tendono ad essere di livello inferiore, ci sono meno modi che puoi ottenere per ottenere cose in uno stato invalicabile rilevabile - dargli un opcode indefinito è un modo ovvio.processori reali segnaleranno che il processo ha tentato di eseguire un'istruzione illegale, e il sistema operativo si occuperà con esso (Linux uccide con SIGILL, per esempio)

0

Direi che è legittimo per la tua VM lasciare che il processore emulato si incendi, purché l'implementazione della VM stessa non si blocchi. Come implementatore di VM, puoi impostare le regole. Ma se vuoi che le aziende di hardware virtuale comprino virtualmente il tuo chip virtuale, dovrai fare qualcosa di un po 'più tollerante di errori: le buone opzioni potrebbero essere di sollevare un'eccezione (più difficile da implementare) o resettare il processore (molto più facile). O forse semplicemente definisci tutti gli opcode come validi, tranne che alcuni sono "non documentati" - fanno qualcosa di non specificato, a parte il crash della tua implementazione. Spiegazione logica: se (!) L'implementazione della VM deve eseguire più istanze dell'ospite contemporaneamente, sarebbe molto brutto se un guest fosse in grado di causare il fallimento di altri.

1

Se sei preoccupato per qualcuno che ha modificato il file binario, allora c'è una sola risposta alla tua domanda: la VM deve fare il controllo. È l'unico modo in cui hai la possibilità di rilevare la manomissione. Il compilatore crea solo il binario. Non ha modo di rilevare manomissioni a valle.

Problemi correlati