---
title: Task 2.3
---
# T2.3 API Model Extraction and Manifest Analysis
Each API or, more generally, an analyzed software function is followed by a manifest file that highlights its features intended as actions (e.g., switch on a device, record video, turn off heating), type of processed data (e.g., working hours, video streaming, home temperature), external HTTP connections, data storage, etc. This task aims to extract the model of a function and its manifest file with the assumption that the source code is available. After this activity, the manifest file is digitally signed to highlight possible future modifications.
# Documentation
Abstract Syntax Tree: https://en.wikipedia.org/wiki/Abstract_syntax_tree
Analyze source code: https://github.com/tree-sitter/tree-sitter to analyze both C/C++ and Rust source code. This library extracts an AST. Have a look at this [rust-code-analysis](https://github.com/mozilla/rust-code-analysis/blob/master/src/node.rs) abstraction to know how to interact with it
# Manifest Producer
A **manifest-producer**. It analyzes a firmware source code in search of **all** libraries APIs called inside the code. As APIs, they are also considered the functions contained in the various standard libraries. Once Ta JSON file for each API found in it and library usage.
Each JSON-file manifest **must** be **digitally signed** and its content corresponds to the list of hazards and features associated to the relative API.
1. Extract from firmware binary every public libfakes' APIs found in it
2. Retrieve every hazard and feature associated to an API using the API name as maps keys. Maps are obtained at compile-time from the analysis of the ontology and then exposed using proc-macros.
3. Write every API hazard and feature into a different JSON file
4. Sign digitally each JSON file
5. (Optional) Double verify all APIs using the code source whenever the firmware is open-source
Each firmware API called in the firmware and defined in the `libdevice` library should be labeled with the `#[ascot_api]` attribute in the comments. This approach allows to:
- retrieve functions in the blink of an eye
- evaluate all functions called in the API scope
## Problems
We cannot retrieve every library used in a C source code because that process is performed by the linker, so you can import a lot of libraries in a source code, but that does not mean they are **effectively** used. **THIS ROUTE IS INFEASIBLE!!**
In Rust we can:
- retrieve all dependencies from the `[dependencies]` field inside the TOML file
- search for each dependency found on:
- `crates.io`
- `a published Git repository`
But we would have the same problem with the unusued code
We can overcome all of this during the **building** process.
# Application Manifest Producer
A `manifest-producer` for T2.3 should be structured in this way
## 1
This task is performed in [dummy-firmware-device](https://github.com/SoftengPoliTo/dummy-firmware-device) library.
Invent a format which describes both APIs features and hazards. The format structure and semantic **must** be language invariant. Only the syntax should change according to the different programming languages, in our case `C/C++` and `Rust`. **Hazards and features must be written in the space reserved to API documentation, so as comments over API signatures**. The format should contain some syntax key pointers to allow an immediate match during source-code analysis. An example could be the `SAFETY:` keyword in Rust. We can also use some open and close markers to say which part of an API documentation is reserved to features and hazards information.
For **features**, we intend:
- **Actions**
- Switch on a device
- Record a video
- Turn on/off heating
- **Type of processed data**
- Working hours
- Video streaming
- Home temperature
- **External HTTP connections**
- **Data Storage**
Features does not have an ontology for now, so we can use a simple JSON file written by us in the T2.3 repository.
For **hazards**, instead, we are going to use the content of the ontology described above, and [here](https://github.com/sifis-home/sifis-api/blob/master/src/lib.rs#L357-L363) we have some examples of APIs decoration.
Features change alongside an API implementation. When an API change its internals, it is also necessary to modify **some** of the features. Actions do not usually change because related to the main goal of an API, while types of processed data, external HTTP connections and data storage are more subjected to changes.
Hazards do not change with APIs: they are version invariant. Once an hazard is added to a specific API, that hazard is assigned forever to that API signature. For instance, since we do not know which kind of lamps an house can have installed on, `turn_lamp_on` API will **always** have the `FireHazard` hazard. Indeed, adding hazards to an API is more a conceptual aspect defined by the creator of the firmware library who decides to adopt our format with the goal of stating API hazards.
## 2
This task is performed inside the [manifest-producer](https://github.com/SoftengPoliTo/prin-task-2.3) library.
### Compile time structure
```mermaid
stateDiagram-v2
Ontology: hazards-ontology.jsonld
Features: features-ontology.json
Binary: Hazards Producer
state Binary {
direction LR
HazardsTable: Hazards table
FeatTable: Feature table
MatchFeat: ApiToFeature
MatchHazards: ApiToHazards
HazardsTable
FeatTable
MatchFeat
MatchHazards
}
Ontology --> Binary
Features --> Binary
```
Two files need to be parsed and then converted into binary tables at compile time: [ontology.jsonld](https://github.com/sifis-home/generate-sifis-hazards/blob/master/ontology.jsonld) and `features.json` (not existent for now).
In Rust language we can obtain this outcome through [proc-macros](https://doc.rust-lang.org/reference/procedural-macros.html). `proc-macros` act during the compilation of a binary performing a series of operations. In our case we need to:
1. Read the two json files
2. Insert their information in the binary as tables which could be `HashMap`s or an integrated SQLite database (https://docs.rs/sqlite/latest/sqlite/)
3. Search for public APIs within `libfakedevice` library (using source code as reference since we are dealing with open-source libraries)
4. Associate API to features and hazards always using an `HashMap` or a SQLite database
A possible `HashMap` which associates APIs to features and hazards could be structured in this way: `HashMap<API_NAME, DATA_STRUCT>`
where `DATA_STRUCT` contains a series of information for its API, such as features validity (whether the feature is still associated to the API or not).
An example on how to generate hazards from an ontology: https://github.com/sifis-home/generate-sifis-hazards
**What happens when ontologies vary, for example, when some hazards or features are added or removed from it? Do we have to adopt some versioning policy, thus maintaining the history of each change?**
In my opinion, to solve this problem, we can **JUST** add hazards to an ontology or change their current values, but we **MUST NEVER** remove any hazard. An ontology is a dictionary and an entries can be removed, from one version to another, only when it is against a particular code of conduct, but not the case here.
Instead, if we decide to use the database approach, we can add to a record a column which represents the ontology version and another one which describes whether the hazard has been removed. In this way, we can also show some deprecations warnings during the binary analysis.
## 3
Extract `libfake` APIs from a firmware binary using [manifest](https://github.com/sifis-home/manifest) tool as reference. This is the most straightforward part and we can also use part of T2.2 to perform this job.
If the code is open-source, it will also possible to pass that information to the `hazards-producer` in order to simplify and improve the search for library APIs.
## Command example
```bash
manifest-producer --binary-path path/to/binary [--code-link /path/to/the/code or link/to/git/repository]
```
which outputs one JSON file per API with the associated features and hazards in it.
## Prerequisites to use the `ESP32-C3` board with `WSL`
Support for connecting `USB` devices is not natively available in `WSL`.
In order to use the `ESP32-C3` board with `WSL`, you must first follow this [guide](https://learn.microsoft.com/en-us/windows/wsl/connect-usb) and manually connect the `USB` port used by the board to `WSL`.
## Weighted Code Coverage
Prendiamo questo pezzo di codice come esempio:
```c=
#include <stdlib.h>
int main()
{
instruction1
instruction2
instruction3
instruction4; instruction5;
// Comment
}
```
`SLOC = 11`
`PLOC = 8`
### WCC quantized
```rust
Require: ploc of the file
Require: complexity of the file
sum = 0.0
if ploc == 0 then
return
end if
for line in lines do
if (line is not a comment or blank) then
continue
end if
if (line is covered)
if blockComplexity > 15.0 then
sum += 2.0
else
sum += 1.0
end if
end if
end for
return sum/ploc
```
Analizziamo i 4 casi estremi:
1. <font color="#ffcc00">**Bilanciato**</font>: file **POCO complesso** (complessità **1.0**) e **POCO coperto** (**1** linea su 7 (PLOC) è coperta).$$
wcc\_quantized = \frac{sum}{PLOC} = \frac{1 \times 1}{7} = \frac{1}{7} = 0.14
$$
2. <font color="red">**Peggiore**</font>: file **MOLTO complesso** (complessità **2.0**) e **POCO coperto** (**1** linea su 7 (PLOC) è coperta).$$
wcc\_quantized = \frac{2}{7} = 0.29
$$
3. <font color="#2eb82e">**Migliore**</font>: file **POCO complesso** (complessità **1.0**) e **MOLTO coperto** (**6** linee su 7 (PLOC) sono coperte).$$
wcc\_quantized = \frac{6}{7} = 0.86
$$
4. <font color="#ffcc00">**Bilanciato**</font>: file **MOLTO complesso** (complessità **2.0**) e **MOLTO coperto** (**6** linee su 7 (PLOC) sono coperte).$$
wcc\_quantized = \frac{12}{7} = 1.71
$$
---
Il valore **minimo** si ha quando nessuna linea è coperta da test, e dunque si ottiene **0**, mentre il valore **massimo** si ottiene quando si hanno complessità pari a 2 e copertura massima:
$$
wcc\_quantized\_MAX = \frac{2*7}{7} = 2
$$
I valori calcolati prima trasformati in percentuale diventano:
1. <font color="#ffcc00">**Bilanciato**</font>: file **POCO complesso** e **POCO coperto**.$$
wcc\_quantized_\% = \frac{wcc\_quantized}{MAX} \times 100 = \frac{0.14}{2} \times 100 = 7\%
$$
2. <font color="red">**Peggiore**</font>: file **MOLTO complesso** e **POCO coperto**.$$
wcc\_quantized_\% = \frac{0.29}{2} \times 100 = 14.5\%
$$
3. <font color="#2eb82e">**Migliore**</font>: file **POCO complesso** e **MOLTO coperto**.$$
wcc\_quantized_\% = \frac{0.86}{2} \times 100 = 43\%
$$
4. <font color="#ffcc00">**Bilanciato**</font>: file **MOLTO complesso** e **MOLTO coperto**.$$
wcc\_quantized_\% = \frac{1.71}{2} \times 100 = 85.5\%
$$
### WCC plain
```rust
Require: ploc of the file
Require: complexity of the file
sum = 0.0
if ploc == 0 then
return
end if
for line in lines do
if (line is not a comment or blank) then
continue
end if
if line is covered then
sum = sum + complexity
end if
end for
return sum/ploc
```
Mentre in **WCC quantized** il valore della complesittà poteva essere o 1.0 (blocco in cui si trova la linea ha complessità <= 15.0) o 2.0 (blocco in cui si trova la linea ha complessità > 15.0), **WCC plain** non analizza la complessità della linea in base al blocco di cui fa parte ma assegna come peso la complessità complessiva del file a tutte le righe del codice. Per i prossimi esempi consideriamo come valori **5** (complessità **bassa**) e **20** (complessità **alta**).
Analizziamo i 4 casi estremi:
1. <font color="#ffcc00">**Bilanciato**</font>: file **POCO complesso** (complessità **5.0**) e **POCO coperto** (**1** linea su 7 (PLOC) è coperta).$$
wcc\_plain = \frac{sum}{PLOC} = \frac{1 \times 5}{7} = \frac{5}{7} = 0.71
$$
2. <font color="red">**Peggiore**</font>: file **MOLTO complesso** (complessità **20.0**) e **POCO coperto** (**1** linea su 7 (PLOC) è coperta).$$
wcc\_plain = \frac{20}{7} = 2.86
$$
3. <font color="#2eb82e">**Migliore**</font>: file **POCO complesso** (complessità **5.0**) e **MOLTO coperto** (**6** linee su 7 (PLOC) sono coperte).$$
wcc\_plain = \frac{30}{7} = 4.29
$$
4. <font color="#ffcc00">**Bilanciato**</font>: file **MOLTO complesso** (complessità **20.0**) e **MOLTO coperto** (**6** linee su 7 (PLOC) sono coperte).$$
wcc\_plain = \frac{120}{7} = 17.14
$$
---
Il valore **minimo** si ha quando nessuna linea è coperta da test, e dunque si ottiene **0**, mentre il valore **massimo** si ottiene quando si copertura massima:
$$
wcc\_plain\_MAX = \frac{complexity(f)*7}{7} = complexity(f)
$$
I valori calcolati prima trasformati in percentuale diventano:
1. <font color="#ffcc00">**Bilanciato**</font>: file **POCO complesso** (complessità **5.0**) e **POCO coperto**.$$
wcc\_plain_\% = \frac{wcc\_plain}{MAX} \times 100 = \frac{wcc\_plain}{complexity(f)} \times 100 = \frac{0.71}{5.0} \times 100 = 14.2\%
$$
2. <font color="red">**Peggiore**</font>: file **MOLTO complesso** (complessità **20.0**) e **POCO coperto**.$$
wcc\_plain_\% = \frac{2.86}{20.0} \times 100 = 14.3\%
$$
3. <font color="#2eb82e">**Migliore**</font>: file **POCO complesso** (complessità **5.0**) e **MOLTO coperto**.$$
wcc\_plain_\% = \frac{4.29}{5.0} \times 100 = 85.8\%
$$
4. <font color="#ffcc00">**Bilanciato**</font>: file **MOLTO complesso** (complessità **20.0**) e **MOLTO coperto**.$$
wcc\_plain_\% = \frac{17.14}{20.0} \times 100 = 85.7\%
$$
### CRAP
Data una **funzione f**, lo score CRAP si calcola in questo modo:
$$CRAP(f) = comp(f)^2 \times (1 - cov(f))^3 + comp(f)$$
Come spiegato anche [qui](https://betterembsw.blogspot.com/2014/06/avoid-high-cyclomatic-complexity.html), il valore massimo della complessità di una funzione dovrebbe stare nel range [10, 15], anche se generalmente si consiglia di mantenerla sotto il 10. Una complexity maggiore di 15 indica infatti che la funzione è complessa e potrebbe aver bisogno di un refactor.
Per definire una threshold CRAP possiamo considerare come valore della **complexity 10**, mentre come valore della **coverage 60%**, che viene generalmente considerato il valore sopra il quale la coverage è accettabile. Così facendo si ottiene un valore pari a **16.4**:
$$CRAP(f) = 10^2 \times (1 - 0.6)^3 + 10 = 16.4$$
Questa soglia tra le altre cose fa sì che vengano considerati complessi tutti i file di complessià maggiore di 16. Ovviamente questo è un valore che può essere modificato, in modo da accettare o meno file più o meno complessi/coperti.
Per calcolare il CRAP score di un **file** possiamo utilizzare la stessa equazione di sopra, adottando come **comp(f)** la complexity media e come coverage la coverage complessiva dei code space analizzati.
### Skunk
Data una **funzione f**, lo score Skunk viene attualmente calcolato in questo modo:
$$
Skunk(f) =
\begin{cases}
\frac{comp(f)}{COMPLEXITY \ FACTOR}, & \text{se }cov(f) = 100 \\
\frac{comp(f)}{COMPLEXITY \ FACTOR} \times (1 - cov(f)), & \text{altrimenti}
\end{cases}
$$
dove [**COMPLEXITY_FACTOR**](https://github.com/whitesmith/rubycritic/blob/main/lib/rubycritic/core/analysed_module.rb), pari a **0.25**, è un valore ottenuto attraverso test empirici condotti dagli autori dell'algoritmo.
Utilizzando le stesse considerazioni fatte per CRAP per quanto riguarda **complexity** (**10**) e **coverage** (**60%**), si ha che la threshold Skunk sotto la quale bisogna rimanere è **16**:
$$Skunk(f) = \frac{10}{0.25} \times (1 - 0.6) = 16$$
Tuttavia effettuando alcune prove si nota che questa equazione presenta alcune problematiche. Se infatti consideriamo una funzione con **complexity 50** e **coverage 100%** si ottiene uno Skunk pari a **2**. Questo valore infatti è anomalo, in quanto nonostante la coverage sia massima non è ragionevole che una funzione così complessa abbia uno score così basso.
Per risolvere ciò si può modificare l'equazione in questo modo:
$$Skunk(f) = \frac{comp(f)}{COMPLEXITY \ FACTOR} \times (1 - cov(f)) + comp(f)$$
Il problema che resta con questa nuova equazione è che se consideriamo nuovamente **complexity 10** e **coverage 60%** si ottiene una threshold uguale a **26**:
$$Skunk(f) = \frac{10}{0.25} \times (1 - 0.6) + 10 = 26$$
che fa sì che vengano accettate anche funzioni con complessità pari fino a 26. Nel caso in cui invece si definisca un **COMPLEXITY_FACTOR** pari a **0.6** si ottiene una threshold uguale a **16.66**:
$$Skunk(f) = \frac{10}{0.6} \times (1 - 0.6) + 10 = 16.66$$
Per calcolare lo Skunk score di un **file** possiamo utilizzare la stessa equazione di sopra, adottando come **comp(f)** la complexity media e come coverage la coverage complessiva dei code space analizzati.
# hazard-analyzer
## Workflow
1. Prendere in input il path del firmware da analizzare.
2. Analizzare il modulo `devices` di `ascot-axum`, cercando al suo interno tutti i file corrispondenti ai device:
1. Per ogni file device cercare il metodo `new()` e per ogni parametro della funzione (che corrisponde ad una `DeviceAction`) cercare all’interno della funzione se per quella `DeviceAction` viene chiamato `miss_hazard()` o `miss_hazards()`.
2. Costruire quindi a partire dagli hazard passati a `miss_hazard()` o `miss_hazards()` una struct che associa ad ogni device la lista di `DeviceAction` e ad ogni `DeviceAction` la lista di `Hazard` obbligatori che deve avere.
3. Analizzare tutti i file `.rs` del firmware, in maniera simile a come abbiamo fatto in `weighted-code-coverage`, ovvero creare un **producer** che prende in input la lista di file e li passa tramite un channel ai **consumer** che li analizzano:
1. Per ogni file cercare tutti i punti in cui viene chiamata una `ascot_axum::devices::device::Device::new()` (es: per adesso abbiamo solo `ascot_axum::devices::light::Light::new()`)
2. Per ogni `new()` , verificare che le `DeviceAction` passate come parametro abbiano come `Hazard` almeno quelli presenti nel campo della struct creata al punto 2.2 (quelli obbligatori).
3. Restituire per adesso come risultato una lista di tutti i device creati con le loro `DeviceAction` , indicando quali sono ascot-compliant e quali no (ovvero create senza passare gli `Hazard` obbligatori).
4. Aggiungere in una parte del manifest json finale la lista delle funzioni pubbliche estratte da `ascot-axum` e `ascot-library`
## Domande
1. È proprio necessario (come dicevamo lunedì) analizzare `ascot_library` per vedere se `Light` ha come kind `DeviceKind::Light` visto che chi crea il firmware sta prendendo `Light` da `ascot_axum` e quindi in teoria sono sicuro che è la `Light` di `ascot`?
**No, possiamo tranquillamente cercare unicamente la `Light` presente in `ascot-axum`.**
In poche parole, non basta verificare che la `Light` che uso nel firmware sia quella di `ascot_axum` senza andare a verifica che il suo `DeviceKind` sia `Device::Light` di `ascot-library`?
**Esatto, possiamo usare solo `ascot-axum`, anche perché, per come l'avevo pensata, ogni `DeviceKind` avrà un file con lo stesso nome in `ascot-axum`. La cosa importante è che se dovesse servire per qualcosa, possiamo usare anche `ascot-library`**
2. Domanda che si collega in parte alla 1.
Bisogna verificare nel `Cargo.toml` del firmware che si stia effettivamente utilizzando `ascot-axum` di `ascot` ? Quindi che il developer non abbia magari messo come dependency:
```toml
[dependencies]
ascot_axum = { path = "./non_ascot_library"}
```
**Sì, è un ottima idea. Concordo**
3. Come strutturare `hazard-analyzer`, ovvero cosa mettere al suo interno? Per ora ho messo:
- `rust-code-analysis` del mio fork. **Sì, va inserito come dipendenza usando `path` in `Cargo.toml`**
- `ascot-axum` per poter appunto accedere ai file dei device al suo interno. Il mio dubbio però è: se qualcuno aggiorna `ascot-axum` (magari aggiungendo nuovi device o modificando gli attuali) in `hazard-analyzer` ci sarebbe una versione non aggiornata, bisogna quindi forse usare i submodule?
- `ascot-library`. Per adesso l’ho messo ma nel caso non sia necessario fare la verifica di `ascot_library::DeviceKind` di cui parlo alla domanda 1. possiamo toglierlo.
**Per gli altri due punti si potrebbe pensare di:**
**1. Usare un file `.toml` contenente i link ai due repository, scaricarli con `gitoxide` (dovrebbe essere possibile, se no git2) dentro `tmp` e poi usarli per fare l'analisi**
**2. Hardcodare il link al file `toml` nel binario e poi scaricare il file `toml`, parsarlo, recuperare i due link e procedere come il punto 1. In questo modo ridurremo il numero di input nella cli ed il certificatore potrebbe cambiare le varie versioni passandole in input e modificando il link trovato dal toml**
## Not necessary anymore
These ideas do **not** need to be implementable anymore.
## Bundle Manager
A **bundle manager** is a specular tool. On one hand, it joins both a **binary** and its metadata in a single unit called _bundle_, while on the other hand, it extracts the very same metadata from the bundle. Metadata are inserted in a file with a determined structure to be easier parsing its content.
We can describe the bundle as a docker image where metadata could be:
- Attached to an image as a docker label. This method is not advised though, because in that way, we have a minimum amount of space to save data https://docs.docker.com/config/labels-custom-metadata/. **Discarded idea.**
- In a specific directory of the docker image filesystem. https://dockerlabs.collabnix.com/beginners/dockerfile/ADD-command.html
```mermaid
stateDiagram-v2
direction LR
Image: Docker Image
Binary: Binary
Manifests: Manifest directory/
state Image {
direction LR
Binary
Manifests
}
```
To extract metadata from a docker image, we can use the static data hard-coded within in the bundle manager, such as the path to the docker image manifest.
As an additional functionality, this tool also checks whether a manifest has been digitally signed in the correct way. To perform this operation, the following command is run:
```
docker cp ${container_name}:${image_path_to_copy} ${host_output_dir}
```
The tool can then be split in two subcommands:
- **generate**: Generates the bundle. It needs a binary and textual files as input. It produces a docker image, the bundle, as output.
- **extract**: Extracts metadata from the bundle.
Attached data **must** be digitally signed. The [signature](https://github.com/RustCrypto/traits/tree/master/signature) crate could generate and eventually verify digital signatures during the extraction process.
This tool can be structured using this [example](https://github.com/tracel-ai/burn/tree/main/xtask) as reference, which already presents subcommands and makes use of [xtask](https://github.com/matklad/cargo-xtask), a Rust crate which allows to run external binaries from different operating systems without using scripts functionalities. In this way, the **bundle manager** can become a cross-platform solution.
# Discussione Tesi
#### SLIDE 1: Title
Il titolo di questa tesi è "Smart Home Devices: Firmware Analysis and Certification"...
#### SLIDE 2: Objectives
... e gli obiettivi sono principalmente due:
1. Il primo è fornire dei tool per l'analisi statica del firmware dei dispositivi IoT, in modo da identificare eventuali problematiche che potrebbero ridurne la mantenibilità.
2. Il secondo obiettivo consiste nel creare dei tool per certificare il comportamento del firmware di un dispositivo Smart Home, evidenziando i potenziali pericoli derivanti dalle azioni eseguite dal device all'interno di una casa.
---
#### SLIDE 3: Internet of Things
Il contesto di partenza è dunque l'Internet of Things, ovvero una rete di dispositivi fisici interconnessi che comunicano e si scambiano dati via Internet.
Un dispositivo IoT può raccogliere dati dall'ambiente fisico ed eseguire azioni in base a comandi o all'elaborazione dei dati raccolti.
---
#### SLIDE 4: Smart Home
Tra i vari esempi di sistemi IoT ci siamo focalizzati sulla Smart Home, ovvero un'abitazione nella quale i dispositivi vengono impiegati per automatizzare alcune attività, aumentare la sicurezza e la qualità della vita degli abitanti.
---
#### SLIDE 5: Firmware
Il comportamento di un dispositivo e le sue azioni sono guidate dal firmware, che funge da interfaccia tra componenti hardware e software a più alto livello.
---
#### SLIDE 6: Firmare Challenges
La rapida evoluzione dei sistemi IoT, e in particolare delle Smart Home, ha portato a una serie di problematiche e sfide ancora irrisolte.
Tra queste ci siamo concentrati sulla sicurezza del firmware e sulla mancanza di certificazioni adeguate per il suo comportamento.
---
#### SLIDE 7: Firmware Challenges (Security)
Per quanto riguarda la sicurezza, tra le varie soluzioni proposte in letteratura ci siamo focalizzati sull' ...
---
#### SLIDE 8: Firmware Analysis
... analisi del firmware, ed in particolare sulle tecniche di analisi statica e analisi dinamica.
---
#### SLIDE 9: Static Analysis
L'analisi statica valuta il software analizzando il codice sorgente per individuare bug, vulnerabilità o valutarne aspetti come la mantenibilità.
---
#### SLIDE 10: Dynamic Analysis
L'analisi dinamica, invece, valuta il software mentre è in esecuzione, identificando bug e vulnerabilità non rilevabili con l'analisi statica e permettendo di ottenere informazioni come la test coverage.
---
#### SLIDE 11: weighted-code-coverage
È in questo contesto di analisi del firmware che nasce anche weighted-code-coverage, ovvero un tool di analisi statica che combina coverage e code complexity per fornire 3 metriche che permettono di indicare se un codice è stato ampiamente testato e se ha una bassa complessità.
Le metriche implementate sono:
- Wcc, che viene rappresentata come valore percentuale ed è quella più precisa perché bilancia meglio coverage e complexity.
- Crap e Skunk, che sono meno precise perché danno un peso maggiore alla complexity. Esse sono rappresentate da degli score, ovvero dei valori numerici che bisogna cercare di minimizzare.
Questo tool non è stato creato da noi, ma abbiamo contribuito ad esso effettuando un ampio refactor, rendendo più precise le metriche e realizzando un redesign dei report generati.
---
#### SLIDE 12: weighted-code-coverage Report
In particolare, il report in formato HTML offre una panoramica completa dell'analisi e come vediamo dall'immagine:
- Permette di scegliere tramite il dropdown menu nella navbar se visualizzare le metriche calcolate considerando la cyclomatic o la cognitive complexity.
- Mostra le threshold delle varie metriche.
- Per ogni metrica c'è una card che indica il valore minimo, medio, massimo e totale per l'intero progetto.
- Inoltre, il grafico nella parte destra indica la proporzione tra file complessi, ovvero che hanno almeno una metrica il cui valore non rispetta la threshold, quelli non complessi e quelli ignorati, ovvero i file per i quali non è stato possibile ottenere le informazioni relative alla coverage.
- Infine, nella parte bassa c'è una tabella nella quale ogni riga rappresenta un singolo file del progetto insieme ai valori delle sue metriche.
---
#### SLIDE 13: weighted-code-coverage Report (functions)
Cliccando sul nome di un si viene reindirizzati a quest'altra pagina all'interno della quale in basso c'è una tabella con i valori delle metriche per ogni singola funzione.
---
#### SLIDE 14: complex-code-spotter
Il secondo tool di analisi statica a cui abbiamo contribuito è complex-code-spotter, che aiuta gli sviluppatori a identificare porzioni di codice con complessità troppo elevata.
Di questo tool abbiamo semplificato il workflow sostituendo il modello di esecuzione concorrente con un meccanismo più semplice ed efficiente.
---
#### SLIDE 15: Firmware Challenges (Behaviour Certification)
Per la certificazione del comportamento del firmware, ci siamo focalizzati sulla valutazione del comportamento di uno Smart Home device in base alle azioni che esegue.
---
#### SLIDE 16: Hazard
In questo contesto abbiamo introdotto il concetto di hazard, ovvero un pericolo che può sorgere come conseguenza di un'azione eseguita da un dispositivo Smart Home.
Considerando l'azione di accensione di una lampadina, una lampadina accessa potrebbe in seguito potenzialmente surriscaldarsi in maniera eccessiva portando così ad un pericolo di incendio.
Dunque, in questo scenario l'azione di accensione della lampadina dovrebbe essere associata ad un Fire Hazard per indicare il potenziale pericolo.
---
#### SLIDE 17: hazard-generator
Il primo tool che abbiamo creato si chiama hazard-generator e a partire da un'ontologia, che rappresenta il concetto di hazard, genera un'API che abbiamo integrato all'interno di una libreria per la creazione di firmware di dispositivi Smart Home.
Questa API da la possibilità di associare degli hazard alle azioni eseguite da un device, rendendo dunque espliciti i pericoli che possono verificarsi come effetti indesiderati dell'esecuzione di queste azioni.
---
#### SLIDE 18: Hazard Usage Example
La libreria all'interno della quale viene integrata l'API, definisce al suo interno tutte le tipologie di device che possono trovarsi in una casa.
Uno sviluppatore per creare il firmware di un device deve implementare il corrispondente device offerto dalla libreria e tenere in considerazione alcune proprietà lo caratterizzano.
Per esempio, possiamo vedere che una light è caratterizzata da due action: "turn light on" e "turn light off" che vengono definite come mandatory actions, ovvero azioni che il device deve necessariamente poter eseguire e che quindi lo definiscono come tale.
Dunque, il developer per creare il firmware di una light deve necessariamente definire l'implementazione di queste due action.
Inoltre alla "turn light on" viene assegnato un Fire Hazard che viene definito come mandatory hazard.
Dunque, ad ogni mandatory action la libreria associa 0 o più mandatory hazard, perciò quando si implementa la action bisogna assegnarle tutti i suoi mandatoy hazard.
In aggiunta ai mandatory hazards, ad una mandatory action si possono associare anche alcuni degli hazard definiti all'interno del set allowed hazards caratteristico di ogni device.
Essi indicano l'insieme di tutti gli hazards che possono essere ragionevolmente associati ad un device.
Per esempio per una light si può assumere che non ci sarà mai una action a cui associare un hazard di pericolo di allagamento.
---
#### SLIDE 19: Hazard Usage Example (Optional Actions)
Inoltre, oltre a quelle mandatory, la libreria permette anche di definire azioni aggiuntive specifiche per un dispositivo.
Per esempio, una lampadina può avere azioni come "set brightness" e "change color" che non sono necessariamente offerte da tutti i modelli di lampadina.
Queste azioni prendono il nome di optional actoin e a differenza delle mandatory actions non hanno dei mandatory hazard, ma ad esse possono essere associati solo gli hazard contenuti nel set di allowed hazards.
---
#### SLIDE 20: hazard-analyzer
Il secondo tool che abbiamo creato si chiama hazard-analyzer.
Esso prende in input il firmware di un device creato con la libreria che abbiamo precedentemente descritto e ne analizza il codice sorgente producendo un firmware manifest in formato JSON e un output a terminale.
Questi report forniscono una descrizione completa di tutte le azioni svolte dal device e degli hazards ad esse associati, verificando anche che il firmware rispetti tutte le condizioni imposte dalla libreria.
---
#### SLIDE 21: hazard-analyzer Manifest
In particolare prendendo in esame questo manifest di una light vediamo come al suo interno vengono innanzitutto listate tutte le mandatory actions definite dallo sviluppatore.
In questo esempio è stata definita la "turn_light_on" ed ad essa è stato associato un hazard di tipo SpoiledFood.
Tuttavia viene evidenziato che alla action non è stato assegnato il mandatory hazard FireHazard e che SpoiledFood non è allowed per la light.
Inoltre viene segnalato il fatto che non è stata implementata la mandatory action turn_light_on.
Tra le optional action è stata definita la "set_brightness", solo che tra gli hazard ad essa associati c'è WaterFlooding che il manifest segnala correttamente essere non allowed.
Il manifest offre quindi una descrizione del comportamento del device e dei suoi pericoli e si può dunque pensare di integrare questo risultato in un processo di certificazione del comportamento del firmware.
---
#### SLIDE 22: Performance Analysis
Nella parte finale della tesi, abbiamo analizzato le performance di weighted-code-coverage, complex-code-spotter e hazard-analyzer in temrini di tempo di esecuzione e gestione della memoria.
---
#### SLIDE 23: Execution Time: weighted-code-coverage
weighted-code-coverage e complex-code-spotter sono stati testati su tre progetti di diverse dimensioni.
I risultati mostrano che il tempo di esecuzione di weighted-code-coverage aumenta con la dimensione del progetto e che il refactoring ha permesso di ridurlo significativamente.
---
#### SLIDE 24: Memory Peak: weighted-code-coverage
Per la gestione della memoria abbiamo preso in considerazione vari parametri tra cui il Memory Peak, che indica il picco di quantità di memoria richiesta per le allocazioni dinamiche.
Come possiamo vedere dal grafico i valori di Memory Peak crescono all'aumentare della grandezza del progetto analizzato e anche per essi c'è stata una diminuzione in seguito alle nostre modifiche.
---
#### SLIDE 25: Peak RSS: weighted-code-coverage
Un altro parametro di memoria è il Peak RSS, che indica la quantità massima di RAM riservata al programma dal Sistema Operativo.
Anche questi valori seguono un andamento crescente e anche per essi il refactor ha portato ad un miglioramento.
---
#### SLIDE 26: Execution Time: complex-code-spotter
complex-code-spotter mostra tendenze analoghe a weighted-code-coverage per i tempi di esecuzione.
Tuttavia, il miglioramento è meno marcato poiché questo tool già inizialmente aveva buone performance.
---
#### SLIDE 27: Memory Peak: complex-code-spotter
Per complex-code-spotter, il Memory Peak è leggermente aumentato dopo le modifiche, probabilmente a causa del nuovo modello di esecuzione concorrente che necessita più allocazioni.
---
#### SLIDE 28: Peak RSS: complex-code-spotter
Si osserva invece un leggero miglioramento dei valori di Peak RSS.
---
#### SLIDE 29: Execution Time: hazard-analyzer
Per l'analisi di hazard-analyzer, abbiamo testato le performance su un solo firmware, poiché non avevamo a disposizione un ampio range di firmware di dimensioni diverse.
I risultati ottenuti mostrano che il tempo medio di esecuzione risulta essere sotto il secondo.
---
#### SLIDE 30: Memory Usage: hazard-analyzer
Per l'utilizzo della memoria si nota in particolare che il valore di Peak RSS è molto basso, e ciò indica che il tool gestice in maniera efficiente le allocazioni dinamiche.
---
#### SLIDE 31: Future Developments
Per quanto riguarda i future developments:
Per weighted-code-coverage, si prevede di:
- Implementare nuove feature per una personalizzazione più fine dell'analisi.
- Semplificare ulteriormente il workflow.
- E migliorare la precisione delle metriche.
Per hazard-generator, l'obiettivo è affinare una variante degli hazard, ancora nella fase iniziale, che permette di assegnare loro un risk score.
Infine, per hazard-analyzer, si pensa di:
- Affiancare l'analisi del codice sorgente a quella del binario
- E di estendere l'analisi a firmware definiti come la composizione di più dispositivi.