NIER Ingegneria è diventata Società Benefit →
TORNA AGLI ARTICOLI
17 Maggio 2022

CRITICAL CODE REVIEW: IL SOFTWARE NATO PER LA SICUREZZA DI TUTTI.

L’importanza dell’analisi critica del codice sorgente garantisce che il software funzioni alla perfezione.

A cura di Marilena Cerbone ed Emiliano La Cara
[Responsabile V&V del Software e Direttore Area Ingegneria del Software]


INTRODUZIONE.

Un sistema critico per la sicurezza (safety-related) è un sistema il cui guasto, se non mitigato con contromisure opportune, potrebbe causare danni significativi a persone e/o cose. Uno dei principi fondamentali della progettazione dei sistemi safety-related è che debbano “fallire” in modo sicuro: qualunque sia lo scenario in cui viene impiegato, il sistema deve essere progettato in maniera tale da ridurre al minimo i rischi, evitando situazioni pericolose.

La riduzione del rischio correlato al suo impiego rappresenta uno dei target della progettazione di un sistema sicuro; a tal proposito la normativa IEC 61508 definisce il SIL (Safety Integrity Level), una misura su 4 livelli del grado di affidabilità richiesto ad una funzione di sicurezza: SIL4 indica il massimo livello di sicurezza mentre SIL1 indica il minimo livello di sicurezza.

I sistemi “sicuri” fanno parte della nostra quotidianità, essendo presenti in numerosi contesti:

  • Si pensi al sistema che, in avionica, si occupa di monitorare lo spazio aereo attorno ad un aereomobile, al fine di segnalare la presenza di altri velivoli. Il TCAS (Traffic Alert and Collision Avoidance System) monitora distanza e altitudine ed emette degli avvisi che comportano delle istruzioni specifiche per i piloti: un errore nella rilevazione della distanza o la mancata interrogazione del trasponder di un velivolo nelle vicinanze potrebbero dare origine a collisioni in volo.
  • In ambito automotive, si pensi ai software applicativi per la guida autonoma: la guida autonoma è proprio un esempio in cui la tecnologia si può completamente sostituire al guidatore (Level 5 SAE), per cui la sicurezza del veicolo dipende direttamente dal software.
  • Un interlocking, o apparato di stazione, è un calcolatore che si occupa di gestire il traffico ferroviario: un errore di computazione o un suo malfunzionamento potrebbe portare a situazioni molto pericolose (ad es. un semaforo potrebbe essere verde, e quindi consentire il passaggio di un treno, quando la tratta che segue è già occupata).

COME SI IMPLEMENTA UN SISTEMA SICURO? RISCHI E CONTROMISURE.

In prima istanza è necessario chiedersi:

Che cosa voglio che faccia? Quali sono gli User needs? Quali sono i requisiti che deve implementare?

Affinché il sistema risponda a precisi standard di sicurezza è necessario individuare tutti i suoi potenziali fallimenti e le conseguenze che ne derivano. E quindi:  

Quali sono gli hazard[1] applicabili al sistema? Quali sono i possibili disturbi o interferenze dell’ambiente?

E a seguire:

Quali sono le cause di questi hazard?

La risposta a quest’ultima domanda si ottiene attraverso una fase metodica e normata di studio del sistema, detta Hazard Analysis.

Definite le cause degli hazard, ognuna di esse viene “rianalizzata” attraverso un processo iterativo: le cause possono diventare nuovi hazard, con a loro volta diverse possibili cause.

E andando sempre più in profondità nell’analisi delle cause, si arriva ad identificare quelle direttamente associate all’utilizzo del sistema e ai suoi requisiti funzionali.

Ma una volta individuati gli hazard e le cause, ci si deve chiedere: Come si può quindi rendere il sistema “sicuro”?

La risposta è: riducendo il rischio[2].

La mitigazione dei rischi di un sistema safety critical, si traduce, spesso, in scelte che hanno impatto sul software; in altre parole, il software diventa una delle principali contromisure per l’attenuazione del rischio, attraverso la progettazione e l’implementazione di specifici algoritmi di sicurezza.

Le mitigazioni messe in atto dal software vanno verificate. Di norma questa verifica può essere fatta a diversi livelli, seguendo le fasi della validazione di un modello a V.

IL SOFTWARE SOTTO LA LENTE DI INGRANDIMENTO.

In questo contesto si inseriscono le attività di revisione critica e analisi di sicurezza del codice sorgente, di cui NIER si è resa protagonista, affiancando sia i clienti che il team di design interno in tutte le fasi dello sviluppo di un software sicuro (per saperne di più leggi questo articolo).

L’analisi del codice sorgente consente di intercettare tempestivamente eventuali errori, mancanze di contromisure “sicure” e di algoritmi safety nell’implementazione: la possibilità di guardare da vicino il software, navigandolo ed ispezionandolo sia nel suo complesso che nel suo dettaglio (comprendendo la logica che ne ha determinato la scrittura), consente di individuare scenari di fallimento, difficilmente rintracciabili nelle altre fasi di validazione

Perdipiù, i feedback derivanti da questa attività, che si colloca appena dopo il rilascio del codice sorgente, hanno la caratteristica di poter essere tempestivi, e la tempestività si traduce in ottimizzazione.

L’importanza della tempestività si coglie ancor di più se si pensa al fatto che il codice sorgente è composto di norma da centinaia di migliaia di righe, organizzate in blocchi funzionali (moduli) in cui ogni blocco prevede delle specifiche interfacce con tutti gli altri, nell’ambito di un’architettura composita e strutturata.

L’ispezione dei singoli moduli e la navigazione dell’intero progetto (quindi anche delle relazioni tra i vari moduli del software) vengono condotte con l’ausilio di tools dedicati quali GPS, Visual Studio, Understand che offrono diverse utilities per muoversi nell’ambito delle diverse chiamate tra le funzioni presenti nel codice.

Gli strumenti più adatti per queste code review possono essere scelti in funzione del linguaggio di programmazione, ma il vero valore aggiunto è “l’occhio” e la sensibilità del verificatore.

Affinché la revisione del codice sorgente sia “critica”, è necessario mantenere una visione a 360 gradi del dominio di applicazione, comprendere compiutamente ogni hazard e la sua contromisura (cosa deve fare il codice e perché), e calarli nel contesto in cui il software deve operare.

Solo attraverso lo studio approfondito delle interdipendenze tra le funzioni sarà possibile individuare tutte le condizioni di fallimento, verificando che siano tutte correttamente gestite dal software.

L’ESPERIENZA DI NIER.

NIER vanta un’esperienza decennale nell’ambito delle attività di Critical Code Review, che vengono condotte attraverso diverse fasi:

  • Studio del dominio di applicazione
  • Studio degli item di rischio e delle contromisure (algoritmi e requisiti di sicurezza)
  • Studio dell’architettura del software
  • Verifica della corretta implementazione dei singoli moduli
  • Verifica del corretto interfacciamento tra i moduli

Il software viene esaminato nel particolare senza perdere la visione generale.  

Ogni singola funzione viene analizzata in tutti i suoi aspetti ma non solo; oltre a verificare il comportamento della singola funzione, il codice viene navigato nella sua interezza per controllare anche quando e in quali condizioni queste funzioni vengono chiamate. Ciò permette individuare comportamenti inattesi dovuti al “quando” e al “come” i singoli algoritmi vengono impiegati, pur essendo di per sé implementati correttamente.

Viene inoltre verificato che siano implementate le misure di defensive check programming, una metodologia di programmazione “difensiva” atta a garantire che il software si comporti in modo prevedibile in ogni condizione di utilizzo. Ciò vuol dire verificare che il software preveda delle specifiche azioni anche in caso di scenari non ordinari e che quindi non esistano condizioni applicative non gestite. Classiche misure di defensive check programming sono la verifica di assenza puntatori a NULL o il range check dei parametri di una funzione.

Tra le esperienze di NIER nella critical code review, vanno annoverate quelle condotte nell’ambito ferroviario sul sorgente scritto in Ada di un Radio Block Center (RBC) o anche l’analisi di sicurezza del codice scritto in C e in Ada di un Interlocking.

Tocchiamo il codice con mano.

Facciamo degli esempi concreti.

Partiamo dallo studio di una contromisura derivante dall’analisi dei rischi legati ai requisiti funzionali del sistema (in questo caso un RBC).

Un esempio di requisito (univocamente individuato da un identificativo numerico) potrebbe essere il seguente: 

[RBC_HA-RS_0001]

Tutte le seguenti caratteristiche del treno devono essere incluse tra i valori ammissibili delle linee su cui è previsto il transito:  

  • Dimensioni del treno; 
  • Sistema di trazione; 
  • Velocità; 
  • Tipo di treno. 

Di seguito la sua possibile contromisura: 

Se almeno una delle seguenti caratteristiche non rientra tra i valori ammissibili dalla linea allora il treno non deve transitare sulla stessa: 

  • Dimensioni del treno; 
  • Sistema di trazione; 
  • Velocità; 
  • Tipo di treno. 

Il software dovrà implementare la contromisura e quindi lo scopo della code review sarà quello di verificare che, per ogni caratteristica elencata, esista una condizione sui valori ammissibili che viene gestita e controllata all’interno del codice sorgente della procedura Verifica_Caratteristiche_Treno, scritto in Ada.

Al suo interno troviamo la definizione di una funzione di controllo per ciascuna delle caratteristiche del treno: se per ogni caratteristica la condizione prevista dalla linea è verificata, il treno viene ammesso (Treno_ammesso := True); altrimenti la violazione anche di una sola di queste condizioni comporta la sua non ammissibilità (Treno_ammesso := False).

Consideriamo un altro esempio, in questo caso un Interlocking ferroviario: è necessario verificare la corretta gestione di un contatore che rappresenta il tempo di persistenza di un certo dato nella logica del sistema.

L’interlocking riceve informazioni dagli enti di campo che si trovano in stazione o lungo la linea, attraverso lo scambio di pacchetti dati. Le informazioni contenute in questi pacchetti vengono usate per comporre i messaggi che poi l’interlocking invia ad altri enti di campo, o in generale agli altri stakeholders del sistema di segnalamento.

E’ importante garantire, quindi, la vitalità di tutte le informazioni che vengono usate dalla logica dell’interlocking: considerare “fresco” (quindi ancora valido) un dato che non lo è più, potrebbe portare il sistema a compiere scelte, che potrebbero minare la sicurezza del sistema.

Da qui la necessità di una contromisura, ovvero l’implementazione di un algoritmo di controllo della freschezza di un’informazione.

Per quanto tempo un dato (contenuto all’interno di un messaggio ricevuto) può essere considerato valido? Esaminiamo i diversi casi possibili:

  • Se un pacchetto dati corretto è stato ricevuto, il suo contatore di persistenza deve essere settato al valore massimo e la variabile di allarme deve essere falsa.
  • Se non è stato ricevuto nessun pacchetto dati corretto e il contatore di persistenza non è ancora “scaduto”, la variabile di allarme rimane falsa ma il contatore deve essere decrementato. I dati ricevuti possono essere ancora riutilizzati.
  • Se non è stato ricevuto nessun pacchetto dati corretto e il contatore di persistenza è “scaduto”, la variabile di allarme deve diventare vera e i dati ricevuti non devono essere più utilizzati.

Di seguito un esempio scritto in C del codice sorgente di una funzione che si occupa di controllare la validità di un messaggio dati e che ne aggiorna il contatore di persistenza (Survival Timer).

Anche in questo esempio (come nel precedente) compresa appieno la contromisura, sarà necessario verificare che tutti gli scenari possibili siano stati gestiti nel codice sorgente, ma non solo. Bisognerà controllare che le variabili vengano adeguatamente inizializzate, che nessun’altra funzione possa cambiarne il valore indebitamente, che l’algoritmo venga eseguito per tutti i possibili pacchetti dati che l’interlocking può ricevere e/o per tutti i possibili enti con cui può scambiare informazioni.


RISULTATI E CONCLUSIONI.

L’intervento e l’esperienza di NIER nell’ambito delle attività di Critical code review ha permesso di intercettare bachi[3] potenzialmente molto critici:

  • Errore nel popolamento di un array di variabili: il valore di ogni variabile veniva scritto nell’indice di posizione corrispondente ad un’altra variabile.
  • Mancato reset delle informazioni usate da una specifica funzione a seguito della cancellazione di un treno[4]. In questo caso, il software non prevedeva, erroneamente, l’implementazione di una contromisura.
  • Mancato riavvio della procedura di invio periodico del messaggio di sincronizzazione tra le due sezioni di un sistema ridondato[5], a seguito del fallimento di un cambio dello stato di servizio
  • Indebita accettazione di un pacchetto dati corrotto, a causa della sovrascrittura (e dell’indebita gestione) di un flag in cui veniva salvata l’informazione circa la validità del pacchetto ricevuto.

La tempestività con cui bachi come quelli descritti sono stati rintracciati, ha consentito di ottimizzare il processo di sviluppo del software, riducendo il rework ad ogni successivo rilascio del codice sorgente oltre che, ovviamente, scongiurare condizioni che avrebbero potuto minare la sicurezza del sistema.

Ciò si traduce in un guadagno effettivo per il cliente e in una soddisfazione completa del verificatore, che ha la possibilità di partecipare concretamente alla realizzazione di un software sicuro e alle sue successive evoluzioni.


[1] Si definisce Hazard, una fonte potenziale di danno all’ambiente e alla salute, legata alle caratteristiche intrinseche di un oggetto o di una situazione.

[2] Il Rischio rappresenta una misura della probabilità che dall’esposizione ad un pericolo derivi un danno a persone o cose.

[3] Un baco è un’anomalia che porta al malfunzionamento del software, producendo un risultato inatteso o errato.

[4] A seguito dell’eliminazione di un treno dalla tratta, tutte le informazioni ad esso relative (velocità, posizione, …) vanno resettate.

[5] Per saperne di più leggi questo articolo

DALLE PAROLE AI FATTI .

Contattaci per saperne di più sull’argomento dell’articolo.

    Condividi .