# Side Channels in Web Browsers: Applications to Security and Privacy

Thomas Rokicki

<figure><img src="image_1.png" /></figure>

## To cite this version:

Thomas Rokicki. Side Channels in Web Browsers: Applications to Security and Privacy. Cryptography and Security [cs.CR]. INSA Rennes, 2022. English. NNT: tel-04493651

HAL Id: tel-04493651

https://hal.science/tel-04493651

Submitted on 7 Mar 2024

HAL is a multi-disciplinary open access archive for the deposit and dissemination of scientific research documents, whether they are published or not. The documents may come from teaching and research institutions in France or abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant des établissements d'enseignement et de recherche français ou étrangers, des laboratoires publics ou privés.

<figure><img src="image_2.png" /></figure>
# THÈSE DE DOCTORAT DE

L'INSTITUT NATIONAL DES
SCIENCES APPLIQUÉES RENNES

ÉCOLE DOCTORALE N° 601

Mathématiques et Sciences et Technologies
de l'Information et de la Communication

Spécialité : Informatique

Par

Thomas ROKICKI

## Side Channels in Web Browsers :
Applications to Security and Privacy

Thèse présentée et soutenue à IRISA, Rennes, le 29 Novembre 2022

Unité de recherche : UMR 6074

## Rapporteurs avant soutenance :

Jan REINEKE Professeur, Saarland University

Billy Bob BRUMLEY Maître de Conférence, Tampere University

## Composition du Jury :

Président : Lilian BOSSUET

Professeur, Université Jean Monnet

Examinateur·rice·s : Jan REINEKE

Professeur, Saarland University

Billy Bob BRUMLEY

Maître de Conférence, Tampere University

Veelasha MOONSAMY

Chargée de Recherche, Ruhr University Bochum

Walter RUDAMETKIN

Professeur, Université de Rennes 1

Dir. de thèse : Gildas AVOINE

Professeur, INSA Rennes, CNRS, IRISA, France

Encadrante de thèse : Clémentine MAURICE

Chargée de Recherche, Univ Lille, CNRS, Inria, France
<figure><img src="image_3.png" /></figure>
Thèse de doctorat de Thomas ROKICKI

Thomas ROKICKI

Supervisor: Clémentine MAURICE

Co-supervisor: Gildas AVOINE

29/11/2022
<figure><img src="image_4.png" /></figure>
<figure><img src="image_5.png" /></figure>
# Résumé en Français

Les processeurs modernes sont des objets technologiques remarquablement optimisés. La vitesse de traitement étant le principal argument de vente des microprocesseurs, les fabricants se sont attachés à augmenter les performances des processeurs lors de leur développement, chaque année proposant de nouvelles générations dotées de meilleures caractéristiques que les précédentes.

Comme les progrès en matière de taille et de performances des semi-conducteurs ont ralenti dans l'ensemble de l'industrie au cours des dernières décennies, le simple fait d'ajouter des transistors à une puce est devenu insuffisant pour continuer l'augmentation régulière des performances. En réaction, les fabricants de processeurs se sont concentrés sur les optimisations microarchitecturales pour améliorer davantage les performances de génération en génération. Ces optimisations visent à augmenter la vitesse de traitement en proposant plus de parallélisme et en réduisant les temps de latence du matériel. Les processeurs multicœurs, les caches, ou l'exécution spéculative sont quelques exemples d'optimisations microarchitecturales. Chaque génération apporte des modifications à la microarchitecture, avec de plus en plus de caches ou d'unités d'exécutions dans un pipeline complexe. Par conception, ces optimisations visent à apporter plus de performance en accélérant l'exécution. Dans cette course à la performance, la sécurité de la microarchitecture tend à être négligée. En particulier, les effets secondaires de ces optimisations sur l'execution des calculs peuvent entraîner des fuites d'informations sur les valeurs secrètes utilisées par le système dans ce qu'on appelle des *canaux auxiliaires*.

En 1996, Paul Kocher [Koc96] a révélé que l'accélération dynamique apportée par les caches introduit des différences de temps d'exécution dans les implémentations à temps constant des algorithmes cryptographiques. Un utilisateur malveillant peut utiliser ces différences de temps microscopiques pour extraire des secrets telles que des clés privées. Cette découverte de la première attaque par minutage a ouvert la voie à un champ de recherche plus large sur les canaux auxiliaires.

Les canaux auxiliaires physiques ont fait l'objet de nombreuses recherches. Ils exploitent les effets secondaires physiques du processeur pendant le calcul pour en extraire les secrets des calculs délicats. Le premier canal auxiliaire physique exploitait la consommation d'énergie de la puce [KJJ99] pour attaquer les implémentations cryptographiques, mais de nombreuses sources de fuites physiques ont été étudiées, notamment les émissions acoustiques [ST04, GST14], les émissions électromagnétiques [RR01], les émissions optiques [SNK+12] ou encore la température [HS13]. Cependant, sauf exception notable [LKO+21], ces attaques nécessitent un accès physique au dispositif cible afin de mesurer les effets secondaires du calcul.

Par opposition, les *canaux auxiliaires microarchitecturaux* sont purement logiciels. Ils exploitent les optimisations basées sur les données des processeurs modernes pour déduire des informations privées. Depuis la première attaque temporelle de Kocher [Koc96], les caches
ont été la source la plus étudiée de canaux auxiliaires microarchitecturaux, mais la recherche a prouvé que d'autres composants peuvent également être exploités pour monter des attaques par canal auxiliaire. Au cours de la dernière décennie, les chercheurs ont ciblés de nombreux composants microarchitecturaux, en ont compris le fonctionnement, et ont utilisé leur comportement pour en extraire des secrets. Les canaux auxiliaires ont été exploités, entre autres, à partir des prédicteurs de branchement [AKS07], de la DRAM [PGM+16], ou de la traduction d'adresses [GRBG18]. Les canaux auxiliaires microarchitecturaux ont été initialement mis en place pour faire fuir les données des implémentations cryptographiques, mais leur portée s'est considérablement élargie, avec notamment des attaques contre l'ASLR [HWH13], la communication entre machines virtuelles [MWS+17], la détection des frappes au clavier [GSM15] et la surveillance des utilisateurs [SKH+19]. Plus inquiétant encore, les canaux auxiliaires microarchitecturaux constituent une brique essentielle d'une nouvelle classe d'attaques microarchitecturales : les attaques par exécution transiente. Introduites en 2018 avec Spectre [KHF+19] et Meltdown [LSG+20], ces attaques utilisent les effets secondaires des instructions transientes, i.e., des instructions exécutées mais jamais engagées dans l'architecture, pour faire fuir des données à travers les barrières de sécurité logicielle et matérielles.

Les canaux auxiliaires microarchitecturaux exploitent le partage des composants matériels entre l'attaquant et la victime pour divulguer des informations. Par conséquent, ils ont un prérequis commun : *l'attaquant doit exécuter du code sur la machine de la victime*. Cette condition préalable essentielle restreint considérablement la surface d'attaque des canaux auxiliaires microarchitecturaux, car les utilisateurs se méfient de plus en plus des logiciels inconnus et ont tendance à ne télécharger des logiciels qu'à partir de sources fiables. Le risque de télécharger des logiciels malicieux est même réduit sur les plateformes mobiles, où les applications sont téléchargées à partir d'un magasin d'applications contrôlé par un éditeur, où chaque morceau de code est soigneusement analysé avant d'être publié. Les chercheurs ont étudié d'autres modèles d'attaques, où l'exécution du code sur le matériel de la victime est plus facile. Un attaquant peut attaquer la victime à distance, souvent via le web, et monter des attaques sur le cache à travers le réseau [BB05, KGA+20]. Schwarz et collab. [SSL+19] ont montré qu'il est possible de monter une attaque Spectre via le web. Les environnements d'informatique en nuage sont également une cible idéale pour les attaques microarchitecturales. Avec le développement de l'infrastructure en tant que service, les utilisateurs peuvent acheter des machines virtuelles ou des conteneurs fonctionnant sur des services comme Amazon EC2 ou OVHCloud. Toutefois, l'informatique en nuage n'est qu'une couche d'abstraction pour des serveurs colossaux. Bien qu'isolées d'un point de vue purement logiciel, les machines virtuelles contrôlées par différents utilisateurs peuvent être co-résidentes de processeur, c'est-à-dire partager un ou plusieurs processeurs. Un attaquant peut donc acheter de l'espace dans le nuage et monter des attaques sur toutes les machines co-résidentes. Ce scénario a été exploité pour détecter la co-résidence [ZJOR11], attaquer l'ASLR [BRPG15] ou monter des canaux à large bande passante entre deux machines virtuelles isolées [MWS+17].

En 2015, Oren et collab. [OKSK15] ont présenté une attaque sur le cache s'exécutant entièrement dans le navigateur. Ce modèle de menace augmente considérablement la surface d'attaque des canaux auxiliaires microarchitecturaux. Comme le web moderne est construit autour de pages web dynamiques et interactives, l'architecture des sites web est séparée en deux composants majeurs. Le côté serveur gère tous les calculs concernant le serveur, la base de données, l'authentification et l'envoi des informations de la page web aux utilisateurs. Le côté client gère la page web telle qu'elle s'affiche dans le navigateur de l'utilisateur. Les
langages de script côté client, tels que JavaScript, sont responsables des composants interactifs des sites. Le code côté client est envoyé à l'utilisateur par le serveur et est exécuté dans le navigateur de l'utilisateur, et donc *est exécuté sur la machine de l'utilisateur*. Cela permet à un attaquant d'exécuter un script sur de nombreuses victimes différentes sans effort. Un utilisateur visitant un site web contrôlé par un attaquant téléchargera du code JavaScript et l'exécutera dans son navigateur sans s'en rendre compte, devenant ainsi potentiellement victime d'attaques microarchitecturales. Plus inquiétant encore, un attaquant peut acheter de l'espace publicitaire sur un site web légitime et exécuter le code JavaScript sur tous les utilisateurs qui voient la publicité.

Pour des raisons de sécurité, le code JavaScript s'exécute dans un environnement virtualisé sécurisé de type *bac à sable*. Ce bac à sable empêche l'accès aux adresses mémoire, aux instructions natives ou au système de fichiers. En outre, JavaScript est un langage abstrait de haut niveau, et il est difficile de quantifier son impact microarchitectural. Ces couches de sécurité et d'abstraction rendent difficile la mise en place de canaux auxiliaires microarchitecturaux à partir du bac à sable. Avec son attaque sur le cache en JavaScript, Oren et collab. [OKSK15] ont démontré la possibilité de provoquer et de mesurer des fuites de données microarchitecturales depuis le navigateur. Les caches ne sont pas le seul composant microarchitectural visé, et des chercheurs ont monté des attaques sur la DRAM [SMGM17] ou les unités à virgule flottante [AKM+15]. Bien qu'en général, les canaux auxiliaires JavaScript aient tendance à offrir moins de résolution aux attaquants, ils représentent une menace inquiétante pour la sécurité et la vie privée des utilisateurs. Les canaux auxiliaires microarchitecturaux ont été exploités dans le navigateur pour extraire des clés cryptographiques [GPTY18], surveiller les sites parcourus par un utilisateur [OKSK15, SKH+19], identifier les utilisateurs [LMD+22] ou communiquer en dehors du bac à sable [SMGM17].

Bien que les canaux auxiliaires basés sur le JavaScript aient été grandement étudiés, l'ensemble de leurs implications en matière de sécurité et de confidentialité n'est pas clair. En particulier, plusieurs zones grises subsistent :

**Q1** Depuis les premières attaques sur le cache basées sur le JavaScript, les navigateurs ont évolué, notamment en réaction aux attaques par minutage. Quel est l'état actuel des attaques par minutage dans sur les navigateurs et leurs contre-mesures ? En particulier, les contre-mesures basées sur les minuteurs sont une approche populaire. Cependant, ces mesures ont souvent été adoptées dans l'urgence. Quel est leur impact sur la sécurité et la confidentialité des navigateurs ?

**Q2** Quels autres composants sont vulnérables aux canaux auxiliaires depuis la sandbox JavaScript ?

**Q3** Quelles informations un attaquant peut-iel extraire de ces canaux auxiliaires ?

## Contributions

Cette thèse se concentre sur l'exploration partielle des réponses à ces questions. Les contributions de ce manuscrit sont divisées en deux catégories. La première direction de recherche consistait à redéfinir la portée des attaques par minutage en JavaScript. En particulier, nous avons étudié l'impact de contre-mesures répandues aux attaques par minutage : la suppression de l'accès aux minuteurs à haute résolution. La deuxième catégorie de contributions concerne un type spécifique de canal auxiliaire : la contention des ports du microprocesseur. Dans une
première contribution, nous montrons comment, pour la première fois, nous avons implémenté
la contention de port dans le bac à sable du navigateur. Dans une deuxième contribution,
nous étendons le champ d'application de la contention de port pour changer complètement
son modèle de menace. Nous montrons également comment cette nouvelle version du canal
auxiliaire peut être utilisée à des fins d'identifications des navigateurs.

Évaluation méthodique des minuteurs JavaScript (Q1) En réaction à la menace croissante que représentent les attaques par minutage basées sur JavaScript, les développeurs de navigateurs ont proposé diverses contre-mesures. Au milieu de tous ces changements, il peut être difficile de suivre les différentes évolutions des navigateurs. En particulier, l'impact des contre-mesures actuelles sur les attaques décrites dans la littérature n'est pas clair.

Nous présentons l'évolution des attaques par minutage dans les navigateurs, et fournissons
des outils statistiques pour caractériser les minuteurs disponibles. Notre objectif est de
présenter une vue claire de la surface d'attaque et de comprendre quelles sont les principales
conditions préalables et classes d'attaques par minutage dans les navigateurs et quelles sont
les principales contre-mesures. Nous fournissons une classification des attaques par minutage
dans les navigateurs, en soulignant leurs conditions préalables communes. Nous proposons
ensuite une taxonomie des contre-mesures en fonction des ressources qu'elles ciblent. Ces
classifications permettent de déterminer dans quelle mesure les changements récents dans les
contre-mesures ont un impact sur la sécurité des navigateurs.

Nous nous intéressons particulièrement à une contre-mesure très répandue : la suppression
des minuteurs à haute résolution. L'idée est simple : si les attaquants ne peuvent pas mesurer
les différences de temps qui créent les fuites des données, ils ne peuvent pas monter leur attaque
par minutage. Nous avons créé des outils analytiques pour évaluer la menace que représente
un minuteur. Nous avons développé un système automatique pour évaluer la sécurité des
minuteurs dans différentes versions des navigateurs les plus populaires. En particulier, nous
montrons que l'évolution de la protection contre les attaques par exécution transientes a rendu
possible d'autres attaques, telles que les attaques par canal auxiliaire microarchitectural avec
une bande passante plus importante que ce qui était possible il y a seulement quelques années.

Ce travail est le résultat d'une collaboration avec Clémentine Maurice (Univ Lille, CNRS, Inria) et Pierre Laperdrix (Univ Lille, CNRS, Inria). Il a été publié à EuroS&P 2021 [RML21].

**Contention de port dans les navigateur web (Q2,Q3)** La contention des ports du processeur est un canal auxiliaire microarchitectural introduit par Aldaya et collab. [ABuH<sup>+</sup>19] en 2019. Il utilise les ports du CPU, un composant du pipeline d'exécution, comme goulot d'étranglement pour créer des différences d'exécution et faire fuir des données confidentielles.

Dans cet article, nous présentons le premier canal auxiliaire de contention de port s'exécutant
entièrement dans un navigateur web, malgré un environnement très restreint :

C1 Les minuteurs JavaScript ont une résolution inférieure à celle des minuteurs matérielles
natives, ce qui augmente le bruit de mesure de l'attaquant.

C2 L'attaquant n'a aucun contrôle sur le coeur physique sélectionné par l'ordonnanceur pour exécuter le code d'attaque.

C3 Dans ce contexte, le code de l'attaquant est écrit dans un langage hautement abstrait qui est converti en code machine par un compilateur.
Alors que **C1** a été étudié par des travaux antérieurs [SMGM17, RML21], **C2** et **C3** nécessitent de nouvelles approches. Pour résoudre **C2**, nous proposons une heuristique entièrement basée sur JavaScript pour permettre la co-résidence de coeur entre l'attaquant et la victime. **C3** est probablement le plus grand défi : nous ne savons pas comment notre code de haut niveau sera traduit en code machine, donc son impact sur la microarchitecture et plus spécifiquement sur les ports du processeur n'est pas clair. Dans ce contexte, nous fournissons un système pour évaluer la contention de port causée par les instructions WebAssembly sur les processeurs Intel, permettant d'augmenter la portabilité des canaux auxiliaires de contention de port. Nous avons trouvé plus de 100 instructions créant de la contention sur 4 ports différents sur des microprocesseurs x86.

Notre attaque peut être utilisée pour construire un canal caché inter-navigateurs avec un débit de 200 bit/s, un ordre de grandeur au-dessus de l'état de l'art. Ce canal caché est inquiétant car il brise le modèle de sécurité d'isolation fondamental des navigateurs, permettant à deux onglets d'échanger des informations de cookies ou de communiquer avec un processus natif pour extraire des données privées. La contention de port web a une résolution spatiale de 1024 instructions natives dans une attaque par canal auxiliaire, ce qui est comparable aux meilleures attaques par cache dans le navigateur.

Nous concluons de notre travail que les attaques par contention de port sont non seulement rapides, mais sont aussi moins sensibles au bruit que les attaques par cache, et sont immunisés contre les contre-mesures implémentées dans les navigateurs ainsi que la plupart des contre-mesures par canal auxiliaire, qui ciblent le cache dans leur grande majorité.

Ce travail est le résultat d'une collaboration avec Clémentine Maurice (Univ Lille, CNRS, Inria), Marina Botvinnik (Ben-Gurion University of the Negev), et Yossi Oren (Ben-Gurion University of the Negev). Il a été publié à AsiaCCS 2022 [RMBO22].

## Port Contention sans SMT (Q3)

L'un des principaux prérequis de Port Contention est qu'il exploite un composant dépendant du coeur qui doit être partagé entre l'attaquant et la victime. De ce fait, elle s'appuie fortement sur SMT, une technologie permettant de partager un cœur physique entre plusieurs threads. Cependant, certains microprocesseurs n'utilisent pas le SMT, ou certains systèmes d'exploitations le désactivent pour des raisons de sécurité, à l'instar de ChromeOS [Goob] ou RedHat [Lar].

Dans cet article, nous présentons la contention séquentielle de port, qui ne nécessite pas de SMT. Au lieu d'exploiter le parallélisme au niveau du fil d'exécution, nous exploitons le parallélisme au niveau de l'instruction. Il exploite l'ordonnancement sous-optimal des ports d'exécution pour la parallélisation au niveau des instructions. Par conséquent, des séquences d'instructions spécifiquement conçues sur un seul thread souffrent d'une latence accrue. Nous démontrons une mise en œuvre de la contention de port séquentielle native sur les processeurs x86.

Nous montrons que la contention de port séquentielle peut être exploitée à partir des navigateurs web en WebAssembly, y compris sur les navigateurs orientés vers la confidentialité tels que Tor Browser ou Brave. Nous présentons un système automatisé pour rechercher les séquences d'instructions menant à la contention de port séquentielle pour des générations de microprocesseurs spécifiques, que nous avons évalué sur 50 microprocesseurs différents, y compris x86 et AMD. Un attaquant peut utiliser ces séquences à partir du navigateur pour déterminer la génération de processeur dans les 12 s avec une précision de 92%. Cette empreinte digitale est très stable dans le temps et résistante au bruit du système, ce qui la
rend très précieuse pour compléter les empreintes digitales basées sur des attributs logiciels plus volatiles. En outre, nous montrons que les mesures de protection sont soit coûteuses, soit uniquement probabilistes.

Ce travail est le résultat d'une collaboration avec Clémentine Maurice (Univ Lille, CNRS, Inria) et Michael Schwarz (CISPA Helmholtz Center for Information Security). Il a été publié à ESORICS 2022 [RMS22].
# Contents

<table><tr><td>1. Introduction</td><td>1</td></tr><tr><td>List of Productions</td><td>9</td></tr><tr><td>2. Background</td><td>11</td></tr><tr><td>2.1. CPU Overview</td><td>12</td></tr><tr><td>2.2. Web Browsers</td><td>20</td></tr><tr><td>2.3. High-Resolution Timers</td><td>24</td></tr><tr><td>2.4. Microarchitectural Attacks</td><td>26</td></tr><tr><td>2.5. Side-Channel Attacks on Software and Browser Resources</td><td>42</td></tr><tr><td>2.6. Countermeasures to Side Channels</td><td>44</td></tr><tr><td>2.7. Browser Fingerprinting</td><td>51</td></tr><tr><td>3. High Resolution Timers in the Browser</td><td>53</td></tr><tr><td>3.1. Timing attacks in browsers</td><td>54</td></tr><tr><td>3.2. Countermeasures in browsers</td><td>57</td></tr><tr><td>3.3. Evaluation tools</td><td>58</td></tr><tr><td>3.4. Results</td><td>62</td></tr><tr><td>3.5. Discussion</td><td>69</td></tr><tr><td>3.6. Conclusion</td><td>71</td></tr><tr><td>3.7. Evolution of Timer Security Since The Publication of the Results</td><td>71</td></tr><tr><td>4. Port Contention in the Browser</td><td>73</td></tr><tr><td>4.1. Web-Assembly-Based Port Contention</td><td>74</td></tr><tr><td>4.2. PC-detector</td><td>77</td></tr><tr><td>4.3. Side-channel Attack on Artificial Applications</td><td>79</td></tr><tr><td>4.4. Covert Channel</td><td>81</td></tr><tr><td>4.5. Discussion</td><td>87</td></tr><tr><td>4.6. Conclusion</td><td>88</td></tr><tr><td>5. Port Contention Without SMT and its Privacy Implications</td><td>89</td></tr><tr><td>5.1. Threat Model</td><td>90</td></tr><tr><td>5.2. Port Contention Without SMT</td><td>90</td></tr><tr><td>5.3. Fingerprinting CPU Generations</td><td>94</td></tr><tr><td>5.4. Discussion</td><td>98</td></tr></table>
<table><tr><td>5.5. Conclusion</td><td>100</td></tr><tr><td><b>6. Conclusion and Perspectives</b></td><td><b>101</b></td></tr><tr><td><b>A. Appendices</b></td><td><b>105</b></td></tr><tr><td>A.1. Custom RDTSC implementation</td><td>105</td></tr><tr><td>A.2. Port Contention on Other WebAssembly Instructions</td><td>107</td></tr><tr><td>A.3. Training set</td><td>108</td></tr><tr><td><b>Bibliography</b></td><td><b>109</b></td></tr></table>
Introduction 1

Modern processors are remarkably optimized pieces of technology. As processing speed is
probably the major selling point of CPUs, vendors have focused on increasing processors'
performances in their development, each year bringing new generations with better features
than the last.

As the progress in semiconductors’ size and performances slowed industry-wide over the
last decades, simply adding more transistors to a chip became insufficient to follow a steady
performance rise. In reaction, CPU vendors focused on microarchitectural optimizations
to further improve performance from generation to generation. These optimizations aim
at increasing processing speed by proposing more parallelism and reducing stall times of
the hardware. Examples of microarchitectural optimizations include multi-core processors,
caches, hyperthreading, or speculative execution. Each generation sees changes to the
microarchitecture, bringing more and more buffers or units in a complex pipeline. By design,
these optimizations aim to bring more performance by speeding the execution. The security
aspect of the microarchitecture tends to be secondary. In particular, side effects of these
optimizations on computation may leak information about secret values used by the system
in what are called *side channels*.

In 1996, Paul Kocher [Koc96] discovered that the dynamic speedup brought by caches
introduces runtime differences in constant-time implementations of cryptographic algorithms.
A malicious user can use these subtle timing differences to extract private information such
as decryption keys on Diffie-Hellman. This discovery of the first timing attack paved the way
for the broader research field on side channels.

Physical side channels have been heavily investigated. They exploit the physical side effects of the processor during the computation to detect sensible computation. The first physical side channel leveraged the power consumption of the chip [KJJ99] to attack cryptographic implementations, but many sources of physical leakage have been studied, including acoustic emissions [ST04, GST14], electromagnetic emissions [RR01], optical emissions [SNK+12] or temperature [HS13]. However, except for notable exceptions [LKO+21], these attacks require physical access to the target device to measure the side effects of computation.

In constrast, *microarchitectural side channels* are purely software-based. They exploit
the data-based optimizations of modern processors to infer private information. Since
Kocher's first timing attack [Koc96], caches have probably been the most studied source of
microarchitectural side channels, but research has proven that other components can also
be exploited to mount side-channel attacks. Over the last decade, researchers have picked
many microarchitectural components, reversed them, and used their behavior to extract
secrets. Side channels have been leveraged, among others, out of branch predictors [AKS07],
DRAM [PGM+16], or address translation [GRBG18]. Microarchitectural side channels were
initially mounted to leak data from constant-time cryptographic implementations, but their
scope has vastly broadened, including attacks against kernel-space ASLR [EPA16], cross-VM communication [MWS+17], keystroke detection [SLG+18b] and user monitoring [SKH+19]. Even more worrisome, microarchitectural side channels are an essential building block of a new class of microarchitectural attacks: transient execution attacks. Introduced in 2018 with Spectre and Meltdown, these attacks use the side effects of transient instructions, i.e., instruction executed but never committed to the architecture, to leak data through software and hardware security barriers.

Microarchitectural side channels exploit the sharing of hardware components between the attacker and the victim to leak information. Consequently, they have one common prerequisite: *the attacker must run code on the victim's machine*. This essential precondition brings severe restrictions to the attack surface of microarchitectural side channels, as system users grow increasingly wary of running unknown code on their personal machines. Awareness campaigns about the risk of unknown code have reached the ears of personal computer users, who tend to only download software from trusted sources. The risk of downloading attacker code is even reduced on mobile platforms, where applications are downloaded from an editor-controlled marketplace, each piece of code being carefully analyzed before release. Researchers have studied other threat models, where running code on the victim's hardware is elementary. An attacker can attack the victim remotely, often through the web, and mount network cache attacks [BB05, KGA+20]. Schwarz et al. [SSL+19] showed that it is possible to mount a Spectre attack over the web. Cloud environments are also an ideal target for microarchitectural attacks. With the development of Infrastructure as a Service (IaaS), users can buy Virtual Machines (VM) or containers running in the cloud on services like Amazon EC2 or OVHCloud. However, the cloud is just an abstraction layer for colossal servers. Although isolated from a purely software point of view, VMs controlled by different users can achieve processor co-residency, i.e., share one or more processors. This means an attacker can buy space in the cloud and mount attacks on all co-resident machines. This scenario has been exploited to detect co-residency [ZJOR11], reversing ASLR [BRPG15] or mounting high-bandwidth channels between two isolated virtual machines [MWS+17].

In 2015, Oren et al. [OKSK15] presented a cache attack running entirely in the browser. This threat model considerably enhances the attack surface of microarchitectural side channels. As the modern web is built around dynamic and interactive web pages, the architecture of websites is separated into two major components. The server side, or back-end, handles all computations regarding the server, the database, authentication, and sending the webpage information to the users. The client side, or front-end, handles the webpage as displayed in the user's browser. Client-side scripting languages such as JavaScript are responsible for the interactive components of sites. Client-side code is sent to the user by the server and is executed in the user's browser, and therefore *is executed on the user's machine*. This allows an attacker to run arbitrary code on many different victims effortlessly. A user visiting an attacker-controlled website will download JavaScript code and run it in his browser without noticing, potentially falling victim to microarchitectural attacks. Even more worrisome, an attacker can buy advertisement space on a legitimate website and run JavaScript code on all users seeing the ad.

For security reasons, JavaScript code runs in a secured virtualized environment called a sandbox. This sandbox prevents access to memory addresses, native instructions, or file system accesses. Furthermore, JavaScript is a high-level abstracted language, and it is hard to quantify its microarchitectural impact. These layers of security and abstraction make it hard to mount microarchitectural side channels from the sandbox. With their JavaScript cache attack, Oren
et al. [OKSK15] demonstrated the possibility of causing and measuring microarchitectural data leakage from the browser. Caches are not the only microarchitectural component targeted, and researchers have mounted attacks on DRAM [SMGM17] or floating-point units [AKM+15]. Although in general, JavaScript side channels tend to offer less resolution to the attackers, they represent a worrying threat to users’ security and privacy. Microarchitectural side channels have been leveraged in the browser to extract cryptographic keys [GPTY18], monitor sites browsed by a user [OKSK15, SKH+19], fingerprint users [LMD+22] or communicate outside of the sandbox [SMGM17].

Although JavaScript-based side channels have been studied by academia, the entire landscape of their security and privacy implications is unclear. In particular, several gray areas remain:

**Q1** Since the first JavaScript-based cache attacks, browsers have evolved, including in reaction to timing attacks. What is the current landscape of browser-based attacks and countermeasures?

**Q2** Are other components vulnerable to side channels from the JavaScript sandbox?

**Q3** What can an attacker extract from these side channels?

## Contributions

This thesis is focused on partially exploring the answers to these questions. The following contributions of this manuscript are split into two categories. The first research direction was to reframe the scope of JavaScript-based timing attacks. In particular, we studied the impact of widespread countermeasures to timing attacks: removing access to high-resolution timers. The second class of contributions concerns a specific type of side-channel: CPU port contention. In a first contribution, we show how, for the first time, we implemented port contention in the browser sandbox. In a second contribution, we extend the scope of port contention to change its threat model completely. We also show how this new version of the side channel can be used in browser fingerprinting.

**Systematic evaluation of JavaScript timers (Q1)** In reaction to the developing threat posed by JavaScript-based timing attacks, browser vendors have proposed various countermeasures. However, as the attack multiplied in the last years, so did the countermeasures, in a cat-and-mouse game fashion. Amid all these changes, it can be hard to keep track of all the different evolutions browsers underwent. Notably, it is unclear how current countermeasures impact the attacks described in the literature.

We present the evolution of timing attacks in browsers, and provide statistical tools to characterize available timers. Our goal is to present a clear view of the attack surface and understand what are the main prerequisites and classes of browser-based timing attacks and what are the main countermeasures. We provide a classification of browser-based timing attacks, highlighting their common prerequisites. We then propose a taxonomy of countermeasures based on the resources they target. These classifications focus on determining to what extent the recent changes in countermeasures impact browser security.

We take a particular interest in a widespread countermeasure: removing high-resolution timers. The idea is simple: if attackers cannot measure data-leaking timing differences, they
cannot mount their timing attack. We created analytical tools to evaluate the threat posed
by a timer. We developed an automatic framework to evaluate timer security in different
versions of the most popular browsers. In particular, we show that the shift in protecting
against transient execution attacks has re-enabled other attacks such as microarchitectural
side-channel attacks with a higher bandwidth than what was possible just a few years ago.

This work is the outcome of a collaboration with Clémentine Maurice (Univ Lille, CNRS, Inria) and Pierre Laperdrix (Univ Lille, CNRS, Inria). It has been published in the proceedings of EuroS&P 2021 [RML21].

**Port Contention in the Web Browser (Q2,Q3)** CPU Port contention is a microarchi-
tectural side-channel introduced by Aldaya et al. [ABuH$^+$19] in 2019. It uses CPU ports, a
component of the execution pipeline, as a bottleneck to create runtime differences and leak
confidential data.

In this contribution, we present the first port contention side channel running entirely in a
web browser, despite a highly challenging environment:

**C1** Web-based timers have a lower resolution than native hardware-based timers, increasing the attacker's measurement noise.

**C2** The attacker has no control over the physical core selected by the browser to execute the attack code.

**C3** In this setting, the attacker's code is written in a highly-abstracted language which is translated into machine code by a just-in-time compiler.

Whereas **C1** has been studied by previous work [SMGM17, RML21], **C2** and **C3** require new approaches. To solve **C2**, we propose a fully JavaScript-based heuristic to allow core co-residency between the attacker and the victim. **C3** is probably the biggest challenge: we do not know how our high-level code will be translated into machine code, thus its impact on the microarchitecture and more specifically on the CPU ports is unclear. To that extent, we provide a framework to evaluate the port contention caused by WebAssembly instructions on Intel processors, allowing to increase the portability of port contention side channels. We found over 100 instructions creating contention on 4 different ports on x86 CPUs.

Our attack can be used to build a cross-browser covert channel with a bit rate of 200 bit/s, one order of magnitude above state of the art. This covert channel is worrisome as it breaks the fundamental isolation security model of browsers, allowing two tabs to exchange cookie information or communicate with a native process to extract private data. Web port contention has a spatial resolution of 1024 native instructions in a side-channel attack, performing on-par with the best cache attacks in the browser.

We conclude from our work that port contention attacks are not only fast, but are also less susceptible to noise than cache attacks, and are immune to countermeasures implemented in browsers as well as most side-channel countermeasures, which target the cache in their vast majority.

This work is the outcome of a collaboration with Clémentine Maurice (Univ Lille, CNRS, Inria), Marina Botvinnik (Ben-Gurion University of the Negev), and Yossi Oren (Ben-Gurion University of the Negev). It has been published in the proceedings of AsiaCCS 2022 [RMBO22].
**Port Contention Without SMT (Q3)** A major prerequisite of Port Contention is that it exploits an on-core component that must be shared between the attacker and the victim. To that extent, it heavily relies on simultaneous multi-threading (SMT), a technology allowing to share a physical core between several threads. However, certain CPUs do not implement SMT, or operating systems disable it for security purposes, such as ChromeOS [Goob] or RedHat [Lar].

In this contribution, we present sequential port contention, which does not require SMT. Instead of exploiting thread-level parallelism, we exploit instruction-level parallelism. It leverages sub-optimal scheduling to execution ports for instruction-level parallelization. As a result, specifically-crafted instruction sequences on a single thread suffer from an increased latency. We demonstrate an implementation of native sequential port contention on x86 processors.

We show that sequential port contention can be exploited from web browsers in Web-Assembly, including on privacy-oriented browsers such as Tor Browser or Brave. We present an automated framework to search for instruction sequences leading to sequential port contention for specific CPU generations, which we evaluated on 50 different CPUs, including x86 and AMD. An attacker can use these sequences from the browser to determine the CPU generation within 12s with a 92% accuracy. This fingerprint is highly stable in time and resistant to system noise, making it highly valuable to complement more volatile software-attribute fingerprinting. Furthermore, we show that mitigations are either expensive or only probabilistic.

This work is the outcome of a collaboration with Clémentine Maurice (Univ Lille, CNRS, Inria) and Michael Schwarz (CISPA Helmholtz Center for Information Security). It has been published in the proceedings of ESORICS 2022 [RMS22].

## Terminology

The terminology of classes of attacks explored in this manuscript can be confusing, as classes intertwine and denominations can be orthogonal. This manuscript is generally focusing on three major classes of attacks:

**Timing Attacks:** They exploit timing differences caused by the execution to infer private data. They can be microarchitectural timing attacks, e.g., cache attacks, but also purely based on timing differences created by software execution.

**Side Channels:** They exploit the side effects of execution to infer information about the victim. Timing attacks typically are side-channel attacks, but side channels may exploit other side effects than time differences. They can be physical side channels, e.g., electromagnetic side channels, microarchitectural, e.g., cache attacks, or purely software-based.

**Microarchitectural attacks:** Microarchitectural attacks exploit the microarchitectural insecure implementation to break security rings. They comprehend microarchitectural side channels, microarchitectural fault attacks, and transient execution attacks.

Figure 1.1 illustrates the relations between these classes of attacks. This manuscript takes a particular interest in microarchitectural side channels with port contention [RMBO22, RMS22] and timing attacks in the browser [RML21].
<figure><img src="image_6.png" /><figcaption>**Figure 1.1.** – Visualisation of various attack classes mentioned in this manuscript, and scope of our publications</figcaption></figure>

## Artifacts

Reproducible research is an essential value of this thesis. To that extent, we released all source code and evaluation data for our works. In particular, JavaScript exploits are highly portable by nature, and all client-side attacks can be run in the latest versions of browsers as of 2022. We also provide the various frameworks proposed in that work, hoping it can bring a more systematic approach to web-based side channel research. The artifacts are separated into three repositories:

* Framework, modified browsers, and evaluation data on our survey of timer security [RML21] are available on https://github.com/thomasrokicki/in-search-of-lost-time.

* PoCs and framework of our web-based port contention implementation [RMBO22] are available on https://github.com/MIAOUS-group/web-port-contention.

* PoCs, framework, and classification models for sequential port contention [RMS22] are available on https://github.com/MIAOUS-group/port-contention-without-smt.

## Thesis Outline

The rest of this manuscript is divided into 5 chapters:

* Chapter 2 introduces some fundamental notions to understand the contributions of this manuscript. In particular, Section 2.1 introduces hardware notions about modern
processors, especially Intel CPUs. Section 2.2 describes the architecture of web browsers. Section 2.3 provides a formalization of timers, and examples of timers in native and web environments. Section 2.4 introduces state of the art on microarchitectural attacks while Section 2.5 presents a short overview of other timing attacks with a strong focus on the browser. Section 2.6 presents the different classes of countermeasures proposed by the industry and academia. Finally, Section 2.7 briefly introduces browser fingerprint.

* Chapter 3 details our work on a systematic approach to timing attacks and timers in web browsers. It provides a taxonomy of browser timing attacks and countermeasures, and statistical tools to evaluate timer security in recent versions of browsers.

* Chapter 4 describes our implementation of port contention in the browser's sandbox. It also provides several applications of this side channel: a high-bandwidth covert channel and a side-channel artificial example.

* Chapter 5 introduces sequential port contention, a new development of port contention with fewer prerequisites. We show how sequential port contention can be used in web environments and its implication on the user's privacy via an application on browser fingerprinting.

* Chapter 6 concludes this thesis and opens perspectives to future work.
<figure><img src="image_7.png" /></figure>
List of Productions 1

[RML21] Thomas Rokicki, Clémentine Maurice, and Pierre Laperdrix. Sok: In search of lost time: A review of javascript timers in browsers. In *EuroS&P*, pages 472–486. IEEE, 2021. Artifacts: https://github.com/thomasrokicki/in-search-of-lost-time

[RMBO22] Thomas Rokicki, Clémentine Maurice, Marina Botvinnik, and Yossi Oren. Port contention goes portable: Port contention side channels in web browsers. In *AsiaCCS*, pages 1182–1194. ACM, 2022. Artifacts: https://github.com/MIAOUS-group/web-port-contention

[RMS22] Thomas Rokicki, Clémentine Maurice, and Michael Schwarz. CPU port contention without SMT. In *ESORICS*, 2022. Artifacts: https://github.com/MIAOUS-group/port-contention-without-smt
<figure><img src="image_8.png" /></figure>
Background 2

In this chapter, we provide the necessary background to appreciate and discuss the contribu-
tions of this manuscript on microarchitectural attacks, especially in the web browser.

It is decomposed in 7 sections:

* Section 2.1 provides an overview of the execution pipeline and memory subsystem of modern Intel processors.

• Section 2.2 presents the architecture of a web browser.

* Section 2.3 introduces an important notion for this manuscript: high-resolution timers. In particular, it explains several techniques to build high-resolution timers in web browsers.

* Section 2.4 explores the state of the art on microarchitectural attacks. In particular, we present microarchitectural side channels, fault attacks and transient attacks. For each class, we present native attacks and attacks running in the browser.

* Section 2.5 presents other timing attacks exploiting software resources, with a strong emphasis on browser-based attacks.

* Section 2.6 explores state of the art countermeasures to side channels.

• Finally, Section 2.7 briefly introduces browser fingerprinting.
## 2.1. CPU Overview

This section presents a high-level overview of a modern Intel processor. CPUs are typically built in a multi-core architecture. These *physical cores* are highly independent and can process data in parallel, effectively multiplying performances. Each core contains its own execution pipeline, able to fetch instructions and execute them, as well as an on-core memory subsystem. Cores communicate with other cores through a ring interconnect. Some components, e.g., the RAM are shared by all physical cores.

### 2.1.1. Execution Pipeline

Pipelining is an essential model of modern processor architecture. A simplified RISC architectural pipeline can be decomposed into 5 stages:

**Instruction Fetch:** fetches, i.e., loads, an instruction in the pipeline.

**Instruction Decode:** translates this instruction and address registers.

**Execute:** sends the instruction to execution units and executes it.

**Memory:** read from memory to a register, or write a register to memory.

**Writeback:** store the results in a register, committing changes to the architecture.

The main idea of the pipeline is that to retrieve maximal performances, all stages of the pipeline must function at all times. At the first clock cycle, the pipeline fetches instruction $i_0$. Then, at the next cycle, $i_0$ is decoded while the pipeline fetches instruction $i_1$. On standard execution, each pipeline stage handles an instruction per cycle. If a stage does not handle an instruction in a cycle, this cycle is effectively lost in terms of performance. This stall propagates to the rest of the pipeline, and is designated as a bubble. Bubbles can be introduced by unavailable resource or by waiting for dependencies. Modern processor implement several mechanisms to reduce this stalling times, such as out of order execution or branch prediction.

The microarchitectural pipeline is the implementation of this architectural pipeline. Figure 2.1 illustrates Intel's Skylake CPUs execution pipeline. It is separated into a *front end*, handling fetch and decode stages, as well as a *back end*, handling the execute, memory, and writeback stages. Each stage is highly parallelized, meaning several instructions are fetched, decoded, and executed every cycle.
<figure><img src="image_9.png" /><figcaption>**Figure 2.1.** – Simplification of the execution pipeline and memory subsystem for a single core in the Skylake microarchitecture.</figcaption></figure>
2.1.1.1. Front End

The front end is responsible for fetching and decoding instructions and forwarding them to the
execution engine or back end. The Instruction Fetch Unit fetches data from the L1 instruction
cache, reading packets of 16 bytes. These packets are then split into x86 instructions by the
predecoder. These macro-operations are then forwarded to the FIFO Instruction Queue (IQ).
The IQ implements macro-op fusion. Certain combinations of x86 instructions, e.g., a compare
test and a subsequent jump, are merged into a single x86 instruction, saving bandwidth in the
rest of the pipeline. At this point, instructions vary greatly in size and can have inconsistent
encoding. The decoder decomposes instructions in atomic, fixed-length operations, called
micro-operations or µops. This decomposition is deterministic, i.e., a given instruction is
decomposed in the same µops regardless of the context. Abel and Reineke [AR19] have
characterized the decomposition of x86 instructions<fnref n="1" />. They propose a benchmarking tool
empirically measuring the number of µops decomposed from the instruction, the instruction's
latency, i.e., the number of cycles required to execute all the instruction's µops [Intc] or
the instruction's throughput, i.e., the average number of cycles per instruction when the
instruction is repeatedly called [F+11]. The µops are then sent to the Instruction Decode
Queue (IDQ).

Data or control dependencies can introduce pipeline stalls, reducing the processor's overall performance. For instance, when executing a conditional branch, e.g., if/else instructions, the pipeline has to wait for the conditional instruction to be executed and committed to the architecture before knowing which branch to take. To reduce control-dependencies-based delay, modern CPUs implemented branch prediction. Intel processors handle it with the Branch Prediction Unit (BPU). The BPU predicts, among others, the branch taken in a conditional branch. It implements a Pattern History Table (PHT) recording if a specific branch was taken or not in previous executions. The PHT is indexed according to virtual address bits and a Branch History Buffer (BHB), which stores branch decisions for all branches on the core. The advantage of the PHT is to remember which branch was taken on multiple sequential executions, allowing the identification of this pattern to increase the accuracy of predictions. For instance, if a branch is taken once every two executions, simply choosing the same branch as the last execution is insufficient. However, remembering two or more executions in the PHT allows to identify that pattern. The processor also predicts branch targets of direct or indirect branches before they are actually computed. The Branch Target Buffer (BTB) stores branch targets of previous branch executions. The BTB indexation scheme is not documented, but reverse-engineering [Hor] has shown that it uses a mixture of virtual-address bits of the branch instruction's address as well as previous, core-wide branches stored in the BHB. Return targets have a specific prediction component called the Return Stack Buffer (RSB). Similarly to the BTB, it dynamically stores the latest targets of RET instructions for a function and uses them for prediction. Mispredictions are only detected when the actual dependency is resolved. In that case, the pipeline is flushed, and the architecture is reset to its previous state, then executes the accurate branch.

Due to the inconsistent sizes and forms of x86 instructions, the fetching and decoding
steps can be costly in performance and power. To optimize the front end, Intel introduced
the Decode Stream Buffer (DSB) or µop cache. The µop cache dynamically stores the
decomposition of the most frequent instructions in µops. It allows bypassing the costly
decoding phase, and delivering µops to the IDQ faster. On Skylake architecture, the µop

<footnote n="1">¹All the results can be found at uops.info</footnote>
cache has an 80% hit rate [Intc], significantly boosting performances. Similarly to the standard decode queue, the µops are then pushed to the IDQ.

The IDQ implements an optimization known as Loop Stream Detector (LSD). It detects tight loops with many iterations and automatically fits all µops directly in the IDQ without fetching and decoding them. Such small-body-size<fnref n="2" /> and high-iterations loops are often found in modern software, e.g., searches or string moves. It allows the prior stages of the front-end to serve on other threads, thus improving overall performances. The IDQ delivers its µops orderly to the back end.

### 2.1.1.2. Back End

At this point, µops from all threads are orderly stored in the IDQ and enter the back end. The goal of the back end is to execute the µops in the most optimized fashion possible. To take advantage of available resources, the backend promotes parallelism through *Out of Order execution* (or OoO). µops are not executed in their emission order, but rather as soon as all necessary resources are free.

µops are stored in the Re-Order Buffer (or ROB) until they are retired. The µops are sent to the scheduler to be executed. It queues the µops and forwards them to the scheduler. At the same level, the Branch Order Buffer (BOB) keeps track of the previous architectural states before speculation allowing for a fast roll-back in case of misspeculation. The ROB features many optimizations. Move Eliminations eliminates the µops of register-to-register moves, preventing latency in the back end. Zeroing Idioms, e.g., computing XOR over the same registers, are also eliminated from the ROB. Similarly, One Idioms, i.e., µops setting all bits of a register to 1 are removed from the ROB, effectively causing zero latency.

The instructions are then forwarded to the scheduler. The scheduler stores µops until all necessary resources are available e.g., the execution unit is ready, or all operands have been loaded. The scheduler then forwards µops to the execution units.

The execution engine is composed of several execution units, each specialized in a specific type of operations, e.g., algebraic operations for the ALUs. The number and types of execution units vary between microarchitectures. Execution units are grouped by *execution ports*. Each port leads to several specialized execution units; therefore, ports are also specialized. Figure 2.2 illustrates the 8-port structure for a Skylake processor. If possible, one µop is forwarded to each available port per cycle. As the decomposition of an x86 instruction in µops is deterministic, and each µop can only be executed by a specific type of execution unit, we can determine the *port usage* of an instruction, i.e., the ports used by this instruction. For instance, on Skylake processors, the IMUL R32 M32 instruction has a port usage of 1*p1+1*p23, i.e., emits a µop on P1 and a µop either on P2 or P3, as both ports can handle memory loads.

After their execution, µops are retired in an orderly fashion, and changes are committed to the architecture. All the pipeline resources are freed during retirement, including ROB and BOB entries or execution units. Skylake processors can commit up to 6 µops per cycle.

### 2.1.2. Hyperthreading

Modern CPUs are often separated into several physical cores. This boosts performance and grants more parallelism during computation, as each core has its own execution pipeline and low-level caches. However, most standard computation does not exploit all hardware

<footnote n="2">²typically 64 µops for Skylake</footnote>
<figure><img src="image_10.png" /><figcaption>**Figure 2.2.** – Illustration of the execution unit distribution on ports for a Skylake microprocessor.</figcaption></figure>

resources of a physical core at the same time. To exploit the hardware resources in the most optimized way possible, processor vendors have introduced Simultaneous MultiThreading (SMT). SMT allows multiple independent threads to use the physical resources of a physical core at the same time. The front end fetches instructions from all threads and execute them independently of their origin. All threads on the physical core share their execution pipeline, L1 and L2 caches, and bus. In addition, they have independent interruption systems and data and instruction registers. Sharing physical components between threads can be achieved in two settings:

**Static Sharing:** The component is definitively split, and a part of a component is only used by a single thread

**Dynamic Sharing:** The whole component is accessible by all threads competitively

This separation of physical cores is abstracted at the OS and software level. The OS considers each of these threads as a **logical core**, independent from the other logical cores. Hyperthreading is the name of Intel's implementation of SMT. It splits each physical core into two logical cores. On Skylake processors, the BPU or execution units are dynamically shared, whereas the IDQ, ROB, or μop cache are statically shared between threads.

### 2.1.3. Memory

During the execution, the CPU requires access to many values stored in the memory. On a single core, the system can handle 2 (P23) memory loads and 1 (P4) memory write per cycle for a Skylake processor. Access time to the memory is crucial as it can be one of the execution bottlenecks. The memory subsystem is highly optimized to reduce latency. This section presents an overview of the different components of the memory subsystem and shared memory focusing on caches as they are an essential component of microarchitectural attacks.
### 2.1.3.1. Virtual Memory

At the lowest level, RAM is indexed with physical addresses. However, processors introduced *virtual memory* to bring more isolation between different processes. Processes do not directly use the physical addresses but access them with a level of abstraction through *virtual addresses*. Modern virtual memory is implemented through pages, i.e., fixed-size memory blocks. The physical memory, respectively virtual memory, is split into physical pages, respectively virtual pages. Intel's processors typically use 4 kB pages, but larger pages can be requested by processes. Both physical and virtual pages are indexed through *page numbers*. Virtual and physical pages are aligned, i.e., the start of a virtual page is always the size of a physical page. This also means that, for 4 kB pages, the 12 least significant bits of a virtual address are equal to those of the physical address. The translation from virtual to physical address is handled by the Memory Management Unit (MMU). The virtual-physical mapping is decomposed in Page Tables (PT), and follows a 4 or 5-level hierarchical structure. When a processor wants to access a virtual address, the processor looks for the appropriate Page Table Entry (PTE) in Page Tables. This process is called a page walk. As almost any memory operation requires a translation, the translation latency is a significant bottleneck in modern systems. Intel implemented on-core Translation Lookaside Buffers (TLB) to lower this latency, dynamically caching the most frequent translations. A translation served from the TLB is significantly faster than from the standard PT structure. The TLB is often separated into an instruction TLB (iTLB) and a data TLB (dTLB).

Virtual pages are also the base unit for *Address-Space Layout Randomization (ASLR)*, a standard security measure to prevent code-injection attacks. When a process starts or requires new stack allocation, ASLR randomizes the address blocks. This implies that other processes cannot know which virtual memory pages are used by a specific process, thus preventing an attacker from modifying them. ASLR is also implemented on *kernel-space addresses (KASLR)*.

Sharing memory between processes is a critical optimization of the memory space. At the system level, the OS implements *memory deduplication*. It is implemented on a content-based approach: if several memory pages are identical, the OS frees all pages except one. This page is set as copy-on-write, i.e., shared by all processes generating this page before the duplication. When a process tries to write on the page, a page fault is thrown. The page is then duplicated, keeping the original page and the modified copy for the writing process.

### 2.1.3.2. Caches

Caches are small memories that sit close to the CPU core. They are relatively small, typically in the order of 1 to 10 MB. As they sit close to the execution pipeline, the access latency is significantly smaller than an access to the DRAM. The goal of caches is to dynamically store the most used values to serve most calls from the cache and not the DRAM, thus significantly improving performances.

Modern Intel CPUs often have three levels of caches of different sizes. The L1 cache is the smallest and fastest, while the L3, or last-level Cache, is the biggest and slowest. Both L1 and L2 are private to each physical core, whereas the last-level cache is shared by all cores. The last-level cache is *inclusive* to L1 and L2, meaning that all values in the L1 or L2 are also stored in the last-level cache.

Modern caches follow a set-associative organization illustrated in Figure 2.3. They are
<figure><img src="image_11.png" /><figcaption>**Figure 2.3.** – Illustration of a 2-way set associative cache with $2^n$ cache sets</figcaption></figure>

composed of $2^n$ cache sets, each composed of *m* ways. Such a cache is called an *m*-way associative cache. Each way consists of a tag and $2^b$ bytes of data. Most modern Intel caches use $b = 6$ and have 64-bytes lines. The tag is computed from the memory address stored in the cache line. The 6 least significant bits of the address are used as an *offset* in the data section of the line. The *n* middle bits of the addresses are used as a *set index*, indicating in which cache set the data must be stored. Two different addresses with the same set index are stored in the same cache set and are called *congruent* addresses. When the processor fetches an address from the cache, it first computes the set index to find the associated cache set. Then, it checks the tags from all the *m* lines present in the set and compare them with the tag computed from the wanted address. If one line has the sought tag, the data is served from the cache, causing a cache hit. If no line in the set has the wanted tag, the data is served from the DRAM, causing a cache miss.

The computation for the set index and the tag can be executed on the virtual address or the physical address. In a Virtually-Indexed Virtually-Tagged (VIVT) cache, both the tag and the set index are computed from the virtual address. This is interesting regarding latency, as the processor does not need to translate the virtual address into a physical one. However, this comes at a space cost, as shared memory is not shared in virtual addresses, meaning that some data could occupy several different cache lines. The opposite solution is to use a Physically-Indexed Physically-Tagged (PIPT) cache, where both the tag and the set are computed from the physical address. This grants a physically-unique tag, resulting in optimized use of cache space for shared memory but comes at a latency cost due to address translation. This is the most commonly used addressing mode, particularly in L2 and last-level caches for modern Intel processors. Finally, some vendors proposed a hybrid approach: Virtually-Indexed Physically-Tagged (VIPT) caches. This allows the processor to immediately compute the cache set index to remove the latency. The tag is then computed from the physical address while the set is looked up, thus reducing the latency. This mode is particularly interesting when the index does not use bits from a different page. This means, for a 4 kB page structure, the page offset is 12 bits long. As the line offset is computed of the 6 least significant bits, it leaves 6 bits to calculate the tag and stay on the same page offset, resulting in an optimal cache size of $2^8$ cache sets. The L1 of most Intel processors follows this optimized structure and is composed of 2 32 kB, 8-way associative VIPT caches, one for
the data and one for the instructions.

The design goal of the cache is to dynamically store values to reduce latency by preventing DRAM calls. To do so, it must store new data constantly and evict previously stored data. When new data is loaded in the cache, the cache uses a *replacement policy* to determine which line must be evicted. An intuitive replacement policy is Least Recently Used (LRU), where the data with the oldest last usage is evicted to store the new data. In practice, as this policy requires the hardware implementation of a timestamp for each cache load, processors used a pseudo-LRU policy with an approximation of last-used timestamps. However, this policy is suboptimal in some instances. For instance, when the user is browsing a congruent set of addresses larger than the cache associativity, all calls will result in a cache miss. Modern processors use more complex and hybrid solutions to adapt to various situations.

### 2.1.3.3. DRAM

<figure><img src="image_12.png" /><figcaption>**Figure 2.4.** – Organization of a DRAM DIMM. It is composed of two ranks, each handling two banks. Each bank is composed of 3 columns and 2 rows.</figcaption></figure>

Dynamic Random Access Memory (DRAM) is the main memory component in modern computer systems. Its access time is significantly higher than the cache's, but it can store more data. When loading an address, the processor first checks if the address is cached. If not, it then loads it from the DRAM. The processor handles DRAM with an on-chip memory controller. The memory controller communicates with the DRAM through one or more parallel channels. The DRAM is organized in Dual Inline Memory Modules (DIMMs). One or more DIMMs can be connected to the same channel. Figure 2.4 illustrates the organization of a DIMM. DIMMs are separated into one or two *ranks*, corresponding to the physical sides of the module. Each rank handles 16 *banks* on DDR4 and 32 on DDR5. The bank manages the actual memory, typically decomposed in 2¹⁷ *rows* and 2¹³ *columns*. The mapping of an address to channel, DIMMs, rank, and bank is not documented for Intel but has been
reversed by Seaborn et al. [Sea] on a Sandy Bridge processor and by Pessl et al. [PGM+16] in 2016 by exploiting a timing side channel. More recently, Wang et al. [WZCN20] introduced DRAMDig, a generic tool to reverse the address mapping of documented Intel processors.

When the processor wants to load a value from the DRAM, it looks for the corresponding channel, DIMM, rank, bank, and row. It then *opens* the row, i.e., copies the whole row to the bank's *row buffer*. The processor then reads the sought-after column from the row buffer. Repeatedly reading data from the same column always loads data from the row buffer without reloading it. However, reading data from the same bank but another row flushes the row buffer and loads the new row.

The DRAM's cells hold a volatile charge that decreases over time. When this charge gets under a threshold, the bit held switches from 1 to 0. Therefore, all the data in a cell is regularly refreshed to prevent data loss. This *refresh interval* is 32 ms in DDR4.

### 2.1.3.4. In-Flight Data

In-Flight Data are pieces of data temporarily stored in special buffers outside of the standard memory subsystem.

Store Buffers (SBs) are small buffers, connected to the store port and the L1 Data Cache. They aim at tracking pending stores. They implement the store-to-load optimization: the processor speculates if a load and a store have the same physical address before their translation from virtual address. If so, it will send data from the store buffer to the load buffer.

The Line-Fill Buffer (LFB) is a small buffer (typically 10 entries) handling outstanding memory accesses. CPU can speculatively load data from the LFB when it is not found in the L1 cache.

### 2.1.4. Hardware Performance Counters

Hardware Performance Counters (HPC) are special registers built in the micro-architecture of modern CPUs. They collect information about microarchitectural events at runtime, and are accessible from the software [Wikb]. They monitor various events, e.g., cache hits, misses, µops dispatched by ports. Initially used for debugging purposes, they can be used to evaluate the behavior of the microarchitecture at runtime.

## 2.2. Web Browsers

This section provides the necessary background on modern browsers' architecture to understand the attacks described in this manuscript. This thesis mainly focuses on two different browsers: Google Chrome and Mozilla Firefox. To that extent, we will provide more depth about these two browsers, but some generic aspects of browser architecture or microarchitectural attacks may be implementable on more browsers with some fine tuning. Furthermore, many browsers are forks of Chrome (e.g., Brave, Opera, or Microsoft Edge) or Firefox (e.g., ToR Browser) and architectural design or components remain the same. Firefox and Chrome represent 75% of the market share of desktop browser. If we count all Firefox-based and Chrome-based browsers, this marker share reaches 91%.
<figure><img src="image_13.png" /><figcaption>**Figure 2.5.** – Simplification of the structure of a modern web browser</figcaption></figure>

### 2.2.1. Browser Architecture

Browsers are complex software composed of many different processes. Although Chrome and Firefox vary significantly in architecture, they have a similar high-level architecture illustrated in Figure 2.5.

**Browser process:** The browser process, or browser engine, is the central component of the web browser. Its main task is to allow communication between all components and synchronization and handle privileged options such as local file accesses. It is directly communicating with the User Interface (UI).

**User Interface:** The UI encompasses all the browser's components presented to the user, e.g., the URL bar, previous and next buttons, or the settings menu.

**Network stack:** The network stack is responsible for fetching the URLs provided by the user and forwarding the result to the browser process.

**Rendering engine:** The rendering engine, or renderer, controls the actual webpage in the browser. It is responsible for parsing the HTML/CSS code sent by the server and displaying it. The rendering engine parses all HTML code with the XML parser into a tree-like interface called Document Object Model (DOM). The Document node is the top node of the tree, and each element displayed on the page is a node below in the tree. In the meantime, the rendering engine parses the CSS attributes of the site,
essentially controlling the layout of the displayed page. Both the UI and the rendering engine are responsible for the display component of the browser, i.e., what the users see on their screen. The rendering engine is also responsible for parsing scripts in the page and sending them to the JavaScript engine. Chrome uses the Blink rendering engine, except on iOS versions where it uses WebKit. Firefox's rendering engine is called Gecko and is developed internally by Mozilla.

**JavaScript engine:** The JavaScript engine is the main component of client-side scripting on the web. It inputs client-side scripting code and compiles it to native machine code at runtime. Client-side code handles all interactions between the user and the webpage without communicating with the server. It typically handles forms, animations, modifications to the DOM, and generally makes the page dynamic. We will provide more details on client-side computing in Section 2.2.2.

As the webpage is an interactive environment, the renderer process is highly event-oriented. The browser must react to users' inputs. To handle asynchronicity, the renderer implements an *event loop*. The event loop is a FIFO queue, where each asynchronous event is registered. When browser resources are available, it will pop the first ready asynchronous event and execute the callback function. Modern renderer processes contain several event loops with different priorities, handling DOM events, network requests, or page repaints.

This multi-process architecture is also parallelized in the browsers. Each tab or origin runs its own renderer process, along with JavaScript engine, XML parser, or event loops. This allows more performant browsers, as processes cannot block each other's execution through the event loop. For instance, if a tab goes unresponsive because of heavy computations, the other tabs are not affected by the slowdown. It also grants more isolation between contexts. However, as some common infrastructure, e.g., JavaScript engine, are duplicated, having several processes can result in higher memory usage.

## 2.2.2. Client Side Languages

To enhance user experience with more dynamic pages, most websites encompass scripts aimed at being executed locally in the users' browsers. This provides faster computation, without the delay introduced by network computation, and reduces the servers' workload. JavaScript is the most used client-side scripting language, but other languages are used to fill specific needs. This section presents the necessary background on the principle and execution of JavaScript and WebAssembly, as they are the languages used in our attacks.

### 2.2.2.1. JavaScript

JavaScript is a lightweight, high-level scripting language. It is used as the client-side scripting language for most websites [w3t], but is also used in back-end programming with Node.js, managing databases, or as a framework to develop webpages. This section focuses on JavaScript's use as a web client-side language. JavaScript is defined by the ECMA standards [ECMc] as a single-threaded scripting language. However, due to its usage in web browsers, it has been enriched with APIs to manage multi-threading, DOM manipulation, animations, and many browser-specific features. Most of these applications are also standardized by the ECMA [ECMd]. For security reasons, JavaScript is executed in a secured environment, also
<figure><img src="image_14.png" /><figcaption>**Figure 2.6.** – JavaScript execution in the engine.</figcaption></figure>

called a sandbox. JavaScript scripts have no access to virtual or physical addresses, native instructions, reads or writes on the filesystem, or to system information.

JavaScript engines use Just-In-Time compilation, i.e., they compile the code directly at runtime and not beforehand. The translation from JavaScript to machine code in the browser is handled by the JavaScript engine, e.g., v8 [Gooe] for Chrome and SpiderMonkey [Mozb] for Firefox. The translation from JavaScript to executable machine code is decomposed in several stages illustrated in Figure 2.6. The JavaScript is first parsed into an Abstract Syntax Tree (AST), i.e., a formal tree representation of the code. This AST is then used to generate bytecode, i.e., intermediary abstract code. At this point, the code is still portable, but the bytecode is loaded in memory and specific to a JavaScript engine. Both v8 and SpiderMonkey use a stack-like register machine [Mozc, Goof] to be as close to actual machine code as possible. Chrome's v8 use a parser and interpreter called Ignition [vba].

This bytecode is then compiled just-in-time to executable machine code. The JIT compiler introduces many different optimizations to the code, e.g., redundancy elimination, inlining, caching, or speculating on operand types. Firefox's JIT compiler is known as Warpmonkey [Mozb], and Chrome's is known as TurboFan [vbc].

### 2.2.2.2. WebAssembly

WebAssembly is a portable binary-code language standardized by the W3C [W3C]. It is designed to be used as a client-side language of the web, in addition to JavaScript. It is a bytecode-like language aimed at being run in a portable stack virtual machine to offer better performance than JavaScript. WebAssembly can be compiled directly from other languages, e.g., C or Rust, or written in the `wat` text format, an assembly-like S-expression representation of the binary code. It is built as a stack machine and has its own memory pages. To this day, WebAssembly supports more than 200 standardized instructions, including algebraic operations, memory management, and SIMD operations. WebAssembly is executed in the JavaScript sandbox and suffers from the same security restrictions.

WebAssembly is also compiled by the browser's JavaScript engine. However, WebAssembly being a binary language, the source files are already bytecodes. Thus, instead of being interpreted, they are directly compiled. Chrome's v8 implements a two-tiered compiler for WebAssembly illustrated in Figure 2.7. It is composed of Liftoff [vbb], a simple one-pass compiler. Liftoff has the advantage of swiftly compiling the WebAssembly bytecodes to reduce pages' startup time. TurboFan is also used to compile WebAssembly to machine code. It is a slower compiler but brings significant performance optimizations. When a WebAssembly script is loaded on a page, it is first quickly compiled by Liftoff, while the slower TurboFan compiles a more optimized version of the code. When the second compilation is done, the browser executes the optimized version. Firefox's SpiderMonkey uses a similar two-tiered compilation for WebAssembly, using a fast, one-pass compiler called RabaldrMonkey [Moza] and an optimized compiler, BaldrMonkey.
<figure><img src="image_15.png" /><figcaption>**Figure 2.7.** – Illustration of WebAssembly two-tiered compilation: the one-pass compiler compiles WebAssembly swiftly to reduce startup time, then when the optimized compilation has finished, the execution switches to the optimized compiled code.</figcaption></figure>

## 2.3. High-Resolution Timers

Side-channel attacks often rely on small timing differences to infer sensitive data. A common prerequisite for these attacks is to be able to distinguish events in the order of the nanosecond. To do so, the attacker needs to have access to high-resolution timers. This section provides an overview of high-resolution timers, particularly in the restricted JavaScript sandbox.

### 2.3.1. Definition

In this manuscript, we call a timer, or a clock, a tool that differentiates two events based on their respective timings. To do so, a timer relies on an operation that needs to:

* Be constant over time to provide a reliable non-varying unit of time.

* Be free running to allow the computation of time differences without blocking the program execution.

We call clock edge the moment where a constant time operation ends and the next starts. A timer can differentiate two events if each crosses a different number of clock edges. The duration of the constant time operation is the smallest amount of time this timer can measure, i.e., its resolution.

### 2.3.2. High-resolution Timers in Native Environments

Intel x86 processors propose the TIME STAMP COUNTER, a 64-bit register counting the number of cycles since its last reset. The x86 rdtsc [Intd] instruction allows an unprivileged user to read and return the value of the TIME STAMP COUNTER.

This timer is *cycle accurate*, meaning it has a resolution of a single CPU cycle. It is the smallest time unit available for the processor. However, cycles are, by design, dependent on the CPU frequency. For instance, power-saving policies can reduce the processor frequency when the workload is low, reducing rdtsc resolution.

Out-of-order execution can also introduce significant overhead or randomness to the measurement. In particular, to measure a specific operation, we call rdtsc before and after
executing the operation. The difference between the two timestamps will correspond to the
execution time of our operation in cycles. However, because of out-of-order execution, the
processor will often execute both calls to rdtsc one after the other to boost performances. In
that case, the difference in the timestamps is not the execution time of our operation. Adding
MFENCE before and after each call to rdtsc prevents re-ordering [Intb].

2.3.3. High-resolution Timers in Web Browsers

Client-side languages of the web are executed in a heavily-restricted sandbox. This sandbox prevents access to native x86 instructions. Thus, attackers sitting inside of the JavaScript sandbox do not have access to rdtsc or other native cycle-accurate timers. JavaScript offers access to built-in timing sources, but academics have also leveraged other functionalities to create auxiliary timers.

performance.now() The JavaScript High-Resolution Time API [Gri] offers access to the performance.now() method, which returns a high-resolution timestamp. The timestamp value represents the time elapsed since the beginning of the current document lifetime in ms, initially with a resolution in the order of 1 ns.

For security reasons, `performance.now()`'s resolution has evolved over time. However, in 2017, Schwarz et al. [SMGM17] demonstrated that it is still possible to recover high-resolution timers regardless of the base resolution by using clock interpolation. The idea behind it is straightforward: one counts the number of times a shorter non-free running operation can be executed. We refer to such an operation as a tick. This tick can simply be writing repeatedly some data to memory or increasing a custom counter by one. In this manuscript, we use a simple JavaScript variable increment.

<figure><img src="image_16.png" /><figcaption>**Figure 2.8.** – Clock interpolation: Counting the number of ticks between the end of the event and the end of the clock period. Even if both events are shorter than the clock period, we can distinguish them by their interpolated time: event 1 has an interpolated time of 4 ticks, whereas event 2 has only 3 ticks.</figcaption></figure>

Figure 2.8 provides an example of how timer interpolation works. Events 1 and 2 have a shorter execution time than the clock period i.e., the resolution. Interpolation is then needed to differentiate them based on their timing. By running events at the beginning of a clock period and counting ticks when they are finished, we can conclude how fast each of them is when the next clock edge is reached. The more ticks are counted, the faster the event is. In Figure 2.8, Event 1 is faster than Event 2 as it has 4 ticks against 3. It should be noted that the interpolated timer is equivalent to a timer with a resolution equal to the duration of a tick.
**SharedArrayBuffer** Initially a single-threaded language, JavaScript has evolved into a multi-threaded paradigm. ECMA2017 introduced the `SharedArrayBuffer` API [ECMb] to accelerate computations between threads. It allows creating an array shared between the main thread and a sub-thread, or web worker. First implemented in Firefox 46 and Chrome 60, Schwarz et al. [SMGM17] used them to build a high-resolution timer. The generic idea of using shared array to build clocks has also been exploited natively when high-resolution timers were not available [LGS+16, LHS+20].

<figure><img src="image_17.png" /><figcaption>**Figure 2.9.** – Simple `SharedArrayBuffer` based clock: To time an event, the main thread evaluates the shared value before and after its event.</figcaption></figure>

Figure 2.9 illustrates a simple `SharedArrayBuffer`-based clock. The attacker creates a *clock* web worker and shares a `SharedArrayBuffer` between the threads. The clock thread is an infinite loop, perpetually incrementing a value in the `SharedArrayBuffer`. This value can be consulted at any time by the attacker and represents a timestamp. The resolution of this timer is very high as it is of the order of the computation time of a shared operation, such as incrementing or reading a shared variable.

**Other sources of timing** The W3C standards describe other API timers accessible in JavaScript, such as `Date.now()` [Cond], `Window.requestFrame()` [Coni] or `Window.setTimeout()` [Conj], but they offer a resolution lower than the one of `performance.now()`. Schwarz et al. [SMGM17] also designed other auxiliary timers. They presented clocks based on CSS animations. An attacker can run JavaScript code at each screen refresh, i.e., every 16.66 ms for a typical refresh rate of 60 Hz, thus this can be used for interpolation as well. This is equivalent to using `Window.requestFrame()` directly in JavaScript.

## 2.4. Microarchitectural Attacks

In this section, we present an overview of microarchitectural attacks. They exploit the microarchitecture's optimizations to pass through software security barriers. We present three types of attacks:

**Side Channels:** the attacker exploits timing differences caused by the microarchitectural state.

**Fault Attacks:** the attacker actively injects faulty values in the microarchitecture.

**Transient Execution Attacks:** the attacker exploits instructions that are computed but never actually committed to the architecture.
Most of these attacks share a common threat model, where the attacker is running code on the victim's hardware. This can be an unprivileged native process or an attacker sitting in the JavaScript sandbox. Microarchitectural attacks exploit shared resources between legitimate and malicious processes.

### 2.4.1. Microarchitectural Side Channels

This subsection describes microarchitectural side channels. They leverage differences in hardware behavior caused by microarchitectural optimizations to leak sensitive information. They can be modeled as *channels* between two entities, similarly to network communication. An attacker can exploit these microarchitectural channels to communicate between two attacker-controlled entities with a *covert channel*. If an attacker creates a channel with a victim-controlled entity, leaking data unknowingly, this channel is a *side channel* attack.

Co-residency between the attacker and the victim is a significant part of the threat model. An attacker sitting on the same core as the victim can exploit on-core resources, e.g., CPU ports, to mount attacks, whereas an attacker sitting on a different core is restricted to cross-core components, e.g., the ring interconnect. Microarchitectural attacks can also be mounted cross-CPU, targeting components shared between processors, i.e., DRAM for large cloud environment.

#### 2.4.1.1. Cache attacks

Cache attacks are a group of timing attacks exploiting the internal state of the CPU caches to leak information. The attacker modifies and monitors the cache state to spy on the victim process or create a covert channel. Cache attacks have been greatly explored, and several different attack primitives exist. As L1 and L2 caches are split between cores and the last-level cache is shared between all cores, cache attacks include on-core, cross-core and even cross-CPU attacks.

**In native environments** Hu [Hu92] mentioned for the first time the possibility of exploiting the cache state to mount attacks. They built a covert channel, allowing internal communication in the VAX security kernel. Cache attacks have largely been used to attack cryptographic implementations. The idea of exploiting timing differences resulting from the cache state was mentioned by Kocher [Koc96] and Kelsey et al. [KSWH00]. They analyzed various side-channel techniques on S-Box cryptography and highlighted the potential of attacks based on the cache hit ratio. This attack scenario was applied by Bernstein [Ber05] on AES. By exploiting how secrets were used as indexes in arrays, they recovered secret keys based on AES design and not exploiting a specific implementation. Osvik et al. [OST06] standardized the approach of cache attacks. Their attacks were mounted on real-world implementations of AES, such as OpenSSL or Linux's `dm-crypt`. By defining attack primitives, such as Prime+Probe or Evict+Time, they paved the way for a systematized approach to cache timing attacks. We will now describe the most common cache-attack primitives.

In Evict+Time [OST06], the attacker evicts a specific cache set $s$ and then times the execution of the attacked function. If this execution time is longer than usual, then the function accessed data stored in $s$. This attack has a major prerequisite: the attacker must be able to repeatedly trigger the attacked function. It has a resolution of a single cache set. Osvik et al. [OST06] have been the first to exploit Evict+Time in an attack against OpenSSL's
AES device encryption in 500 000 samples, running in half a minute. Hund et al. [HWH13] leveraged Evict+Time to circumvent kernel space ASLR. By setting privileged code portions in the cache using system calls, accessing a set of user-space designated addresses, then rerunning the system calls, they can gather timing differences created by the cache state. If the second execution of the system calls is longer than the first, then the user-space addresses have evicted the privileged addresses. Due to cache indexation being based on addresses, this difference in timing leaks information on the physical or virtual addresses of the system-call addresses. Evict+Time has also been proven applicable to other microarchitectures. In particular, Spreitzer and Plos [SP13] mounted an attack on unaligned AES T-tables, managing to recover the private key in the first AES round. This attack was applied to ARM microarchitectures, particularly on mobile devices. It was mounted from an unrooted user on a Google Nexus S smartphone.

<figure><img src="image_18.png" /><figcaption>**Figure 2.10.** – Flush+Reload attack.</figcaption></figure>

In Flush+Reload [YF14], an attacker can monitor a single cache line. Figure 2.10 illustrates the workflow of the attack. The attacker regularly flushes data from a specific cache line with the native `clflush` instruction and measures the time it takes to reload the same data after a short time. As Intel's caches are inclusive, the data is also flushed from the L2 and L1 caches. If the reload time is short, i.e., a cache hit, the data has been reloaded in the cache by the victim. If it is long, i.e., a cache miss, the data is not in the cache, thus, the victim process did not access it. This attack offers a single cache line spatial resolution and shows the best accuracy of existing cache attack primitives. It, however, has three major prerequisites:

* Access to the native instruction `clflush`.

* Inclusive caches.
* Shared data between the attacker and the victim.

The first attack in the Flush+Reload category was proposed by Gullash et al. [GBK11] in 2011. They recovered OpenSSL's AES secret keys in under 100 encryptions. Yarom and Falkner [YF14] standardized Flush+Reload and used it to mount an attack against RSA. Flush+Reload has been leveraged on attacks against AES implementations [GBK11, IIES14, AIES15, AES15, GIA⁺15, GSM15, AAG17, SS20], DSA [YB14, BvdPSY14, vdPSY15, ANT⁺20] or PAKE [BFS20, BFS21]. Flush+Reload attacks have also been applied to other microarchitectures, most notably ARM smartphones [LGS⁺16] in a variant named Evict+Reload. In this attack, the Flush phase is replaced by a cache eviction. By filling a cache set with its own value, the attack can remove other addresses in the cache, effectively equivalent to using `clflush` when the instruction is not available. Flush+Flush [GMWM16] is a variant of Flush+Reload where the Reload phase is replaced by another call to `clflush`. By measuring the execution time of flushes, the attacker can determine whether new addresses have been set in the cache.

<figure><img src="image_19.png" /><figcaption>**Figure 2.11.** – Prime+Probe attack.</figcaption></figure>

In Prime+Probe [OST06], the attacker first fills a cache set $s$ with its own data (Prime). Then after a short time, it times the load of its data (Probe). If the reload time is short, i.e., only cache hits, it means that all the attacker data are still standing in the cache, thus the victim process has not loaded data stored in $s$. If the reload time is long, some attacker data have been evicted from $s$, i.e., the victim has loaded data stored in the monitored cache set. This attack offers a spatial resolution of a cache set, lower than Flush+Reload. However, the requirements are less constraining as the attack does not require access to native instructions or shared memory, allowing its usage in systems where an attacker cannot mount Flush+Reload. The main prerequisite for this attack is to build an eviction set, i.e., a set of data congruent to the victim, able to fill a specific cache set. Prime+Probe initially focused on the L1 cache, as building an L1 eviction set is trivial. As the L1 is virtually indexed, the attacker just selects addresses with the same index bits in the virtual address. Percival [Per05] was
the first to leverage it in an attack against RSA. L1 cache has been targeted by attacks against AES [OST06, NS06, OST06, BEPW10] or digital signatures [BH09, ABG10].

Creating an eviction set for the last-level cache is not a trivial task. As the last-level cache is indexed on the physical address, the attacker cannot determine the index bits of addresses. Liu et al. [LYG$^+$15] proposed an algorithm to empirically create a minimal eviction set for an arbitrary cache set that is oblivious to memory addresses, thus usable in the JavaScript sandbox. For a cache with w-way associativity, we need at least w congruent addresses to evict a cache set. The algorithm is detailed in algorithm 1.

<caption>**Algorithm 1:** Naive algorithm to find an eviction set for a specific address.</caption>

1. Let $A$ be a buffer in virtual memory, covering all the cache lines;
2. Let $x$ be the address we want to evict;
3. Let $\tau$ be the threshold to distinguish a cache hit from a cache miss;
4. **while** $|A| > w$ **do**
5. &nbsp;&nbsp;&nbsp;&nbsp;Access all the values in $A$ to evict the whole cache;
6. &nbsp;&nbsp;&nbsp;&nbsp;Access $x$ to set it in the cache;
7. &nbsp;&nbsp;&nbsp;&nbsp;Let $y$ be a value selected at random in $A$. Access $A\setminus y$;
8. &nbsp;&nbsp;&nbsp;&nbsp;Let $t$ be the access latency for $x$;
9. &nbsp;&nbsp;&nbsp;&nbsp;**if** $t < \tau$ **then**
10. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The call was a cache hit and $x$ was not evicted from the cache $\rightarrow y$ was a part of $x$ eviction set;
11. &nbsp;&nbsp;&nbsp;&nbsp;**else**
12. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The call was a cache miss, $x$ was evicted $\rightarrow y$ was not a part of $x$ eviction set;
13. &nbsp;&nbsp;&nbsp;&nbsp;Let $A = A\setminus y$;
14. &nbsp;&nbsp;&nbsp;&nbsp;**end**
15. **end**
16. Return $A$;

This algorithm creates a minimal eviction set for an address $x$ by reducing a large, cache-filling buffer $A$. It removes a randomly selected address $y$ at each step, then tries to evict $x$ by browsing the set. If $x$ is evicted, $y$ is not necessary to build a minimal eviction set and can be safely removed. If $x$ is not evicted, $y$ is necessary to build a minimal eviction set. It is added back to $A$. We repeat this process until we reach $|A| = w$. It has a complexity of $O(|A|^2)$ memory accesses and $O(|A|)$ time measurements.

Vila et al. [VKM19] proposed an enhancement illustrated in algorithm 2. Based on group tests [Dam06], it separates the initial set $A$ in $w + 1$ subsets. Instead of testing a single address at a time, it tests one of the subsets. At each set separation, there are $w$ values in the final eviction set and $w + 1$ subsets, so at least one of the subsets does not contain a significant value. At every cache miss, $\frac{1}{w+1}$ of $A$ is removed. This algorithm has a complexity of $O(w^2 |A|^2)$ memory accesses and $O(w^2)$ time measurements.
**Algorithm 2:** Group testing reduction for finding eviction sets on a cache with *w*-way associativity.

1. Let $A$ be a buffer in virtual memory, covering all the cache lines;
2. Let $x$ be the address we want to evict;
3. Let $\tau$ be the threshold to distinguish a cache hit from a cache miss;
4. **while** $|A| > w$ **do**
5. &nbsp;&nbsp;&nbsp;&nbsp;Divide $A$ in $w + 1$ size-equivalent subsets;
6. &nbsp;&nbsp;&nbsp;&nbsp;let $C$ be one of the subset;
7. &nbsp;&nbsp;&nbsp;&nbsp;Evict the whole cache;
8. &nbsp;&nbsp;&nbsp;&nbsp;Access $x$ to set it in the cache;
9. &nbsp;&nbsp;&nbsp;&nbsp;Access $A \setminus C$;
10. &nbsp;&nbsp;&nbsp;&nbsp;Let $t$ be the access latency for $x$;
11. &nbsp;&nbsp;&nbsp;&nbsp;**if** $t > \tau$ **then**
12. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The call was a cache miss, $x$ was evicted from the cache. $C$ did not contain any of the addresses in the eviction set;
13. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Let $A = A \setminus C$;
14. &nbsp;&nbsp;&nbsp;&nbsp;**else**
15. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The call was a cache hit, $x$ was not evicted: $C$ contains at least one address of the eviction set;
16. &nbsp;&nbsp;&nbsp;&nbsp;**end**
17. **end**
18. Return $A$;

With the introduction of an algorithm to build an eviction set for the last-level cache, new attacks exploiting Prime+Probe appeared, targeting the last-level cache. It allows to mount cross-core attacks in environments where Flush+Reload is not implementable, e.g., cloud environments. Zhang et al. [ZJOR11] exploited Prime+Probe in the cloud to monitor other containers running on the same hardware. Maurice et al. [MWS+17] used Prime+Probe to mount a 45 kB/s covert channel on Amazon EC2 cloud. It also has applications in secured enclaves such as Intel SGX [SWG+17, BMD+17].

Another critical factor of eviction-based cache attacks in the browser is the need to evict the set reliably, i.e., to evict the whole set rapidly and with a high probability. To do so, the attacker needs to consider the cache replacement policy. For a *w*-way associative cache, we have an eviction set $A = \{a_1, \dots, a_n\}$. In a simple LRU policy, linear access $a_1, a_2, \dots, a_n$ with $n = w$ is sufficient to reliably evict the whole set as each access to an address of $A$ evicts a single address in the cache set. However, modern caches use more complex hybrid replacement policies, and a simple linear access pattern may not be sufficient. The attacker needs to access the eviction set in an optimized way for a specific replacement policy. We refer to this access pattern as an *eviction strategy*. Optimal eviction strategies vary with processor generation, cache associativity, or size. Gruss et al. [GMM16] propose a heuristic to find fast and reliable eviction strategies in JavaScript. They propose three parameters to modelize eviction strategies. The first is the number of repetitions for each address. For instance, accessing $a_1, a_1, a_2, a_2, \dots, a_n, a_n$ is on average 33% faster than a simple linear access on a Haswell processor. The second parameter represents a sliding window on the eviction
set to repeatedly access subsequences of the eviction set, e.g., $a_1, a_2, a_1, a_2, a_3, a_4, ..., a_n$. Finally, the last parameter represents the step/overlap of the sliding window. By varying these parameters, Gruss et al. were able to get a 99.9% eviction rate with a faster eviction time than simple linear access on a Haswell i7-4790. An attacker can find the best eviction strategy to mount Prime+Probe attacks by empirically determining the best set of parameters for a particular system. However, this computation can be time-consuming, and Gruss et al. [GMM16] decompose it into an offline phase, where the best strategies for most systems are determined, and an online phase where the attacker tests previously computed strategies to isolate the most suitable.

Other cache attacks variants have been proposed to fit different threat models. Irazoqui et al. [IES16] propose Invalidate+Transfer, a new cache attack primitive working in a cross-CPU setting. It targets the coherency directory protocol of CPU interconnects. The attacker and victim sit on two different CPUs with different cache hierarchies but share a memory block. In the Invalidate phase, the attacker invalidates a block in their hierarchy, e.g., by flushing it. The directory protocol of the interconnect sends a message to other CPU's caches sharing this block to uncache it. Then, in the Transfer step, the attacker reaccesses the memory block. If another processor cached this block between the two steps, the directory is updated, resulting in a fast direct transfer access. If no other processors have accessed the block, the access is served from the DRAM, resulting in a longer access. This attack was leveraged to extract ElGamal keys on a shared AMD Opteron server. Prime+Abort is a variant of Prime+Probe introduced by Disselkoen et al. [DKPT17]. It replaces the timing phase of Prime+Probe by leveraging aborts from Intel hardware transactional memory implementation TSX, allowing to mount this attack on systems without access to high-resolution timers. Purnal et al. [PTV21] introduced Prime+Scope, a variant of Prime+Probe offering a higher time precision while having a similarly general threat model. The Prime phase is similar to Prime+Probe. Instead of probing the whole cache set for results, the attacker accesses the eviction candidate in the L1 cache. This prevents the observer effect, where the measurement of the attacker modifies the state of the victim's cache. For the attack to work, the attacker and the victim must share a last-level cache but not a L1 cache, i.e., sit on different cores.

**In web environments** Restrictions introduced by the JavaScript sandbox mitigate the implementations of some cache-attack primitives. In particular, the lack of access to native instructions and shared memory prevents the implementation of side channels based on Flush+Reload or Flush+Flush. Oren et al. [OKSK15] introduced the first cache-based side channel in the JavaScript sandbox. This attack is based on Prime+Probe and has been used to track the victim's browsing behavior. In particular, an attacker could monitor the cache state using Prime+Probe to determine which websites were browsed by the user in real-time.

Oren et al.'s attack uses Liu et al.'s algorithm [LYG$^+$15], described in Algorithm 1 to build an eviction set for a target JavaScript variable. They proposed optimizations for this algorithm by considering the page structure. The addresses of the consecutive elements of the page are consecutive. Most modern processors have a cache line size of 64 bytes, meaning the offset requires $bit_{offset} = 6$ bits to encode. Knowing our array is composed of 4 kB, we can compare the last $bit_{page} = 12$ bits of addresses on a same page. This means we can guess the $bit_{page} - bit_{offset} = 6$ least significant bits of the cache index. Two addresses are congruent if they share the same index bits, so we can assume only one in $2^6$ can be congruent with a particular set. Using this in the algorithm, we can only check the subset of possibly congruent
addresses, i.e., 1 in 64, speeding up considerably the algorithm.

Gras et al. [GRB+17] leveraged a JavaScript-based Evict+Time to fully break ASLR from a virtual environment. Genkin et al. [GPTY18] used a WebAssembly-based Prime+Probe to extract ElGamal, ECDH and RSA decryption keys from Google's End-to-End [Gooa] and OpenPGP.js [Ope] JavaScript cryptographic libraries. Shusterman et al. [SKH+19] define cache occupancy, a last-level-cache-wide Prime+Probe. By monitoring the usage of the whole last-level cache, they create memorygrams to identify which sites are being browsed by the user without building an eviction set. Shusterman et al. [SAO+21] mounted a Prime+Probe side channel entirely running in CSS and HTML, working in browser environments without JavaScript. They show how current browser restrictions, mainly based on JavaScript, can be circumvented to still fingerprint the website browsed by the users.

### 2.4.1.2. Cross-CPU attacks

Cross-CPU attacks target components that can be shared between different CPUs on the same system, e.g., DRAM. They mainly target multi-CPU environments, e.g., the cloud or portable Systems on a Chip (SoC), but can also be used in single-CPU environments in a cross-core setting.

DRAMA [PGM+16] is a side channel based on DRAM bank row buffer collisions. By exploiting the timing difference caused by row conflicts, the authors created a low-noise covert channel with a bandwidth of 2 MB/s. The receiver times the access to a specific address in the DRAM. To send a 1-bit, the sender accesses a different address in the same bank but in a different row. This causes a row conflict, thus a slower access time for the receiver. To send a 0-bit, the sender simply idles for a fixed time. DRAMA also proposed a side-channel attack. By identifying rows used by the victim in secret-dependent computations, the attacker can infer sensitive data. First, the attacker accesses an address in the targeted bank but in a different row than the victim's address. Then, it waits a short time for the victim to compute. Finally, it times the reaccess to its address. If the access is longer, it means a row conflict happened, i.e., the victim accessed an address in the targeted bank. This attack allows the attacker to monitor in real-time the victim's keystrokes.

The DRAM covert channels have also been exploited from the JavaScript sandbox. Schwarz et al. [SMGM17] implemented a covert channel based on DRAM row conflicts running from a native component to a JavaScript process. This sandboxed implementation brought challenges on fast cache eviction, as values served from the cache do not reach the DRAM and cannot cause conflict. Schwarz et al. also proposed a heuristic for address selection. JavaScript does not offer access to virtual or physical addresses. They exploited the on-demand heap allocation of JavaScript. These pages are backed by 2 MB Transparent Huge Pages. By browsing through a large array, the JavaScript receiver can detect a delay caused by a page change, indicating that the accessed address is at the start of a new page, i.e., the 21 least significant bits of the addresses are 0, granting information on the associated DRAM bank.

### 2.4.1.3. Cross-core attacks

Cross-core attacks target microarchitectural components shared between all cores, e.g., last-level cache, ring interconnect, or the DRAM. A cross-core attacker can target processes that do not achieve core co-residency.

Evtyushkin and Ponomarev [EP16] proposed a covert channel based on Random Number
Generation in Intel ISAs. It transmits information by creating contention on the Conditioner Buffer, which holds 4 64-bits random values. If values in this buffer are exhausted, it introduces a delay to regenerate them. The receiver times the *rdseed* execution. To send a 1, the sender exhausts values of the Conditioner Buffer, thus slowing down the receiver's *rdseed*. This cross-core covert channel shows a resolution of 100 kbit/s.

Paccagnella et al. [PLF21] proposed to mount side-channel attacks on the On-Chip Ring Interconnect. The attacker can create ring contention by "bombarding" specific ring regions with traffic. This contention causes differences in timing that can be used to mount a side channel attack against RSA implementations or to monitor keystrokes.

In the browser sandbox, Schwarz et al. [SLG19] automatically inferred microarchitectural information in JavaScript, such as the instruction-set architecture or the used memory allocator. Sanchez-Rola et al. [SSB18] exploited a timing side-channel based on the imperfections of processors' internal clocks. In particular, minor differences in the clock's quartz crystal can result in subtle timing differences for the same computations. By gathering execution times of cryptographic operations, they create unique hardware fingerprints for a machine.

### 2.4.1.4. On-core attacks

On-core attacks target core-dependent microarchitectural optimizations, i.e., resources exploited only by processes running on a single physical core. On-core resources include the execution pipeline and its memory subsystem, including L1 and L2 caches. These attacks require the attacker and the victim to sit on the same physical core and thus are highly dependent on SMT.

**Port contention side channels** Aldaya et al. [ABuH$^+$19] introduced PortSmash, the first port contention side-channel attack. Figure 2.12 illustrates how port contention can be leveraged in a side-channel attack. As a CPU port can handle a single $\mu$op per cycle, it can act as a bottleneck in the flow of operations. Thus, by repeatedly calling and timing instructions with a specific port usage, a spy process can monitor $\mu$ops from other threads on the same physical core. For instance, an attacker can repeatedly call the `crc32` instruction, which is decomposed into a single P1 $\mu$op. This creates a bottleneck on P1. Next, by measuring the instruction's execution time, the attacker knows if instructions from other processes co-located on the same physical core are distributed on the same port. As illustrated in Figure 2.12a, if the attacker is the only source of $\mu$ops on P1, all of its $\mu$ops are executed in a row, resulting in a fast execution time. On the contrary, if the attacker's instruction has a longer execution time than usual, it means that another process has issued one or more $\mu$ops to P1, as illustrated in Figure 2.12b. This difference in execution time allows an attacker to monitor other processes' port usage in real-time. Aldaya et al. were capable of creating and measuring contention on P1, P5 and P0156. Their side channel offers a spatial resolution of a single instruction. The attack is SMT-dependent, as the attacker and the victim must sit on the same physical core and share CPU ports. Aldaya et al. exploited this vulnerability to mount an end-to-end attack on OpenSSL's TLS implementation and recover private keys. In particular, they targeted the scalar multiplication code of OpenSSL 1.1.0h, in which port usage depends on the secret. Port contention was also leveraged by Aldaya et al. to leak data from an Intel SGX enclave.
<figure><img src="image_20.png" /><figcaption>(a) Victim has not used port 1: all attacker instructions are executed in a row.</figcaption></figure>

<figure><img src="image_21.png" /><figcaption>(b) Victim emitted one $\mu$op on port 1: attacker instruction will be delayed.</figcaption></figure>

<caption>**Figure 2.12.** – Illustration of port contention.</caption>

**Other on-core sources of contention** Other on-core resources have also been proven vulnerable to side channels. The Branch Target Buffer (BTB) has been exploited to leak cryptographical secrets [AKS07]. BTB mispredictions create a delay of a few cycles compared to an accurate prediction. This delay can be detected in the context of a cryptographic side-channel attack. Aciïçmez et al. propose several attack models. If the attacker and the victim achieve core co-residency, they share a common BTB. The attacker can then constantly clear the BTB, and cause a BTB miss during the execution of the target branch. Therefore, there will be a misprediction in the victim process when it takes the target branch. The attacker can then detect the delay caused by the misprediction to detect the branch taken and infer secret data. The BTB can also be exploited in a trace-driven approach. The attacker regularly executes branches BTB-congruent with the targeted branch. When the victim takes the targeted branch, one of the attacker branches is evicted from the BTB set resulting in a misprediction. By creating a trace with the execution times of all its branches, the attacker can detect mispredictions and create a real-time trace of the victim branch accesses. Evtyushkin et al. [EPA16] exploited a BTB side-channel to de-randomize virtual addresses in ASLR. By creating BTB set collisions between the attacker process and user-controlled kernel calls, the attacker creates timing differences, then can infer information about the kernel-space addresses.

Still in the Branch Prediction Unit, Evtyushkin et al. [ERAP18] showed that even a protected BPU implementation with a hardened BTB can fall victim to microarchitectural
side channels. In a new attack called BranchScope, they force a directional branch predictor to switch to a simple 1-level mode, letting an attacker communicate across user space or even from an SGX enclave. In concept, the attack is similar to a Prime+Probe but targeting the Pattern History Table (PHT). In a first step, the attacker sets the PHT in a desired state by executing a handpicked set of instruction. Then, after a short stalling time, they execute more branches targeting the same PHT entry. Based on the prediction outcome, the attacker is able to determine if another process has accessed the PHT entry between the two phases.

Translation Lookaside Buffers were also exploitable in side-channel attacks. In particular, Gras et al. [GRBG18] showed how, despite recent countermeasures on cache attacks, cryp-tographic implementations could be targeted by leakage caused by the TLB. The attack is similar to a Prime+Probe cache attack. The attacker creates an eviction set for a specific TLB set. By iterating through it, the attacker fills the TLB set with its own values, effectively evicting other addresses in the set. Then, after a short waiting time, the attacker measures the access time to addresses in the eviction set. If it is slower, then some addresses have been evicted, meaning the victim has accessed addresses stored in the targeted TLB set. Using this attack, Gras et al. leaked EdDSA and RSA secret keys in a single-trace setting.

The DSB, or *μop* cache, has also been exploited to mount covert channels [RMT+21]. By filling specific rows in the *μop* cache, the attacker can communicate and create a 900 kbit/s covert channel through different logical cores on the same physical core.

Attacks based on on-core contention have also been implemented inside the JavaScript sandbox. In particular, Andrysco et al. [AKM+15] leveraged a side channel based on floating-point operations to leak values of pixels rendered in the browser. By applying SVG filters with subnormal values and timing the rendering time, they were able to extract pixel values from the victim's browser. Stolen pixels could lead to monitoring or history sniffing.

### 2.4.1.5. Automated discovery of side channels

Finding target components for microarchitectural attacks requires heavy reverse-engineering and fine tuning to detect the vulnerabilities. Academics have proposed directions for more systematic and automatic approaches.

In a blog post [Fog], Fogh presented Covert Shotgun. Its goal is to detect possible on-core contention between pairs of instructions of an ISA. The framework executes each instruction of the pair on two logical cores sitting on the same physical core and measures their execution time. Instruction pairs presenting a longer execution time than usual reveal contention. The source of this contention is not precisely identified by the method but can be leveraged to create side-channel attacks or covert channels. Covert Shotgun was only tested on a limited subset of the whole x86 ISA but automatically found hundreds of possible instruction pairs creating contention, i.e., as many potential covert channels.

ABSynthe [GGK+20] follows a similar blackbox approach. It also aims at identifying potential on-core contention but targets the whole x86_64 ISA. For a pair of instruction $(i_x, i_y)$, they automatically evaluate the impact of $i_y$ on $i_x$'s execution time. If $i_x$'s execution time is longer when $i_y$ is running on the same physical core, this reveals on-core contention. By repeating this experiment on all possible pairs of instructions, they create *leakage maps* of a specific microarchitecture, i.e., an exhaustive representation of contention caused by instruction interferences. Each pair of instructions creating contention represents a possible side channel, but the congested component is not identified. The authors then present ABSynthe, an automated side-channel synthesis. Given a specific microarchitecture (and its
leakage map) and a target process, it automatically creates a spy process that can, after a synchronized preprocessing phase, leak the target process's secrets without synchronization. ABSynthe is illustrated on an EdDSA implementation, effectively recovering 256-bit private keys in a single trace.

Osiris [WIN$^+$21] expands side-channel synthesis to a larger scope than resource contention. They model side channels as a triplet of sequences:

**Reset sequence:** The attacker brings the targeted microarchitectural component into a known state, e.g., Prime of Prime$+$Probe or Flush from Flush$+$Reload.

**Trigger sequence:** The victim changes the microarchitectural state, e.g., modifying the cache state based on a secret.

**Measurement sequence:** With a timing measurement, the attacker determines if the target component is still in the reset state or has been modified in the trigger sequence. This is where the information leaks. Probe from Prime$+$Probe is an example of this phase.

For each triplet of instructions from a specific ISA, they determine if the measurements are similar with or without the trigger phase. Such triplets are potential side channels. Osiris rediscovered previously existing side channels, e.g., Flush$+$Reload, and also discovered 4 new side channels, enabling powerful attacks.

### 2.4.2. Microarchitectural Fault Attacks

Attackers can actively induce faults in the microarchitecture and exploit their effects to mount attacks. Hardware fault attacks have been largely studied, where faults are induced by changing the physical conditions, e.g., high or low temperature, laser, or radiation. Errors can also be entirely induced by software. In this subsection, we present an overview of software-based microarchitectural fault attacks.

#### 2.4.2.1. RowHammer

In 2014, Kim et al. [KDK$^+$14] proposed RowHammer, the first software-based microarchitectural fault attack. This attack exploits bugs in the DRAM. Reading values in the DRAM have side effects on physically adjacent rows. In particular, when a DRAM row is *hammered*, i.e., repeatedly accessed within a DRAM refresh window, the charge of physically adjacent rows may drop under a threshold, causing a bitflip from 1 to 0. This means an attacker can potentially modify bits in unauthorized memory areas. The rate of bitflip can increase if both rows adjacent to the victim are *hammered*.

Seaborn and Dullien [SD15] presented two different exploits leveraging RowHammer. The first is a sandbox escape from the Native Client (NaCl) sandbox. By causing bit flips in dynamically generated indirect jumps, the attacker can gain control over these jumps and access addresses outside of the sandbox. The second allows an unprivileged user to escalate to kernel privileges. Leveraging RowHammer, the attacker induces a specific bit in the Page Table Entry (PTE) to flip, making the PTE point to an attacker-controlled page. The attacker now has read/write access to its own page table, which effectively grants them control over the whole physical memory. Since these exploits, many RowHammer-based attacks have been developed, against cryptographic implementations [BM18, PSS$^+$18], attacking or gaining access to cloud co-resident VMs [RGB$^+$16, XZZT16], or triggering bitflips remotely [LAS$^+$18, TKA$^+$18].
<figure><img src="image_22.png" /><figcaption>**Figure 2.13.** – Illustration of the RowHammer fault attack</figcaption></figure>

Gruss et al. [GMM16] presented a RowHammer implementation running entirely in the JavaScript sandbox, allowing to remotely mount attacks from the victim's browser. Implementing RowHammer in JavaScript came with significant challenges. First, JavaScript is oblivious to virtual or physical addresses. The attacker does not know which address it is reading and thus does not know which DRAM row it is hammering. However, the attacker can exploit the 2 MB memory pages used by the browser. By iterating through a large array and measuring access latency, the attacker can detect page faults when the access time is significantly longer. This leaks information about the first address of the page. The attacker now knows that the following 2 MB represent 16 DRAM rows. It can cause bit flips by hammering rows at the edge of this page to cause bit flips in an adjacent, potentially sensitive page. Second, the attacker has no access to the `clflush` instruction. If the attacker repeatedly accesses the hammered address, it is served from the cache, not from the DRAM, thus preventing bit flips. The attacker has to ensure that the address does not sit in the cache without `clflush`. The authors propose eviction-based RowHammer. Similarly to Prime+ Probe, the attacker composes an eviction set for the targeted address. By iterating through this set, they can ensure the eviction of the targeted address, which is then served from the DRAM. However, this eviction and DRAM load must have a high frequency to cause bit flips. The author proposed strategies to empirically determine optimal eviction strategies to maximize the eviction rate and minimize the eviction time.

### 2.4.2.2. DVFS-based fault attacks

Dynamic Voltage and Frequency Scaling (DVFS) is the energy management system of processors. It allows to dynamically lower processing speed to save energy. For instance, when the workload on a core is low, the DVFS reduces the core's frequency to reduce its energy consumption. When the workload increases, DVFS can raise the frequency to boost performances. DVFS can be controlled from the software, for instance with Linux's `CPUfreq` scaling governor.
<figure><img src="image_23.png" /><figcaption>**Figure 2.14.** – Illustration of the workflow of transient execution attacks.</figcaption></figure>

CLKSCREW [TSS17] is a software-based microarchitectural fault attack exploiting DVFS. Flip-flops are memory components that change their value on the rising edge of the core clock. To locally propagate their output to adjacent flip-flops, the duration of the interval between two rising clock edges must be greater than a time interval defined by flip-flops' hardware properties. By overclocking i.e., over-extending the frequency at a specific time, a software-based attacker can induce bitflips by exploiting flip-flops' minimal timing constraint. The authors leveraged CLKSCREW to extract secret keys or privilege-escalate in Trustzone in ARM/Android smartphones.

Plundervolt [MOG$^+$20] is another software-based fault attack leveraging DVFS. They exploit the side effects of briefly lowering the voltage on the transistors, possibly creating faults. As opposed to CLKSCREW, their attack focuses on x86 microarchitectures. They show the first occurrence of fault attacks on an Intel SGX enclave. Notably, an attacker leveraging Plundervolt can cause faults in the execution of hardware AES-NI computations in SGX, effectively allowing them to extract secret keys from the enclave in a few minutes.

### 2.4.3. Transient Execution Attacks

Transient instructions are instructions that often result from unauthorized computations, e.g., mispredictions or page faults. When detected, these faults cause a full pipeline flush and a roll-back of all operations. This flush resets the architecture at the previous state. However, the microarchitectural state may be modified by transient instructions' side effects. Mispredictions in speculative execution are a source of transient instructions. For instance, when the dependency is finally resolved and the wrong branch was taken, all µops from the mispredicted branch are purged from the ROB. Nevertheless, speculative execution is not the only source of transient instructions. Faults can also cause transient execution. When a page fault occurs, subsequent operations can still be processed in transient execution. They are later flushed but may also impact the microarchitectural state.

Transient execution attacks follow an execution pattern illustrated in Figure 2.14:

1. The attacker sets the microarchitecture in a specific state to prepare the transient execution and data extraction.

2. The attacker triggers transient execution, for instance by forcing a misprediction or forcing an instruction fault.

3. The transient instructions access the victim's sensitive data.
4. The attacker needs to extract the private data. As the data is flushed from the microarchitecture, the attacker cannot simply read it. Most transient execution attacks use a microarchitectural covert channel, where the transient instructions are the sending component.

5. Finally, transient instructions are flushed and the architectural state is reset, but the attacker can read the sensitive data from the microarchitectural covert channel.

Although the workflow is similar, transient execution attacks vary by the trigger phase, e.g., speculative execution or fault, on which address space the read happens, or the covert channel to extract data. Canella et al. [CBS<sup>+</sup>19] propose a classification of transient execution into two major classes: Spectre-type and Meltdown-type.

### 2.4.3.1. Spectre attack class

Spectre-type attacks exploit the transient window created by a control or data flow misprediction in speculative execution. Since the initial Spectre attack [KHF<sup>+</sup>19], many variants have been proposed, targeting different predictive components, poisoning strategies, or extraction covert channels. Canella et al. [CBS<sup>+</sup>19] propose a first classification based on the exploited victim component:

**Spectre-PHT** [KW18, KHF<sup>+</sup>19] targets the Pattern History Table (PHT), which predicts the results of conditional branches.

**Spectre-BTB** [KHF<sup>+</sup>19] exploits the Branch Target Buffer, which predicts the branch target for indirect branches.

**Spectre-RSB** [KKSA18, MR18] exploits the Return Stack Buffer responsible for return address predictions.

**Spectre-STL** [Hor18] exploits Store To Load data dependencies, where the memory disambiguator speculates on loads before all store operations on the same location have completed.

Another essential property of Spectre-like attacks is the poisoning method, or how the attacker "trains" the branch predictor to induce the misprediction in a particular branch in the victim execution. In particular, an attacker can exploit the virtual address indexation of branch prediction units to poison a branch predictor on a different address space in a cross-process setting, or directly train the branch predictor in the victim address space in a same-process setting. In native code on Intel processors, Spectre-PHT, Spectre-BTB, and Spectre-RSB all work both in same-process and cross-process settings, whereas Spectre-STL is only executable in same-process settings [CBS<sup>+</sup>19].

As cache side channels are the most studied in the literature, most Spectre-like attacks use cache-based covert channels to extract stolen data. In particular, the original Spectre and most of the following attacks leveraged Flush+Reload as their covert channel. However, Bhattacharya et al. [BSN<sup>+</sup>19] presented a Spectre-like attack exploiting port contention as a covert channel. By achieving core co-residency with the victim, the attacker can monitor its port usage with a high spatial resolution. This variant broadens the scope of potentially vulnerable code gadgets, as a single instruction can be leveraged to create detectable port contention.
Spectre attacks can be implemented in the JavaScript sandbox. In the original Spectre paper [KHF+19], Kocher et al. propose a JavaScript implementation of Spectre-PHT in the same-process setting. It allowed an attacker in Chrome 62 to read private memory in the browser's process by training the Pattern History Table to always predict a particular branch by passing it in-bound values. Then, on the last conditional branch, the attacker passes an out-of-bound value, resulting in a transient read to private data. The main difference with native Spectre attacks is the covert channel. As Flush+Reload is not implementable in the JavaScript sandbox, the authors leveraged Prime+Probe to extract sensitive values. Maisuradze and Rossow [MR18] presented ret2spec, a Spectre-RSB type attack that can also be implemented in WebAssembly, entirely running in the browser's sandbox. It also allows an attacker to arbitrarily read memory in the same-process setting. In 2021, Google's security team released a proof of concept of a same-process Spectre-PHT [Teab] in recent versions of Chrome, supposedly hardened against Spectre attacks.

### 2.4.3.2. Meltdown attack class

Meltdown-type attacks rely on the transient execution window after a CPU exception. These exceptions are handled by the architecture when the faulting instruction is retired, i.e., all of its µops are executed and previous instructions have been retired. When the faulting instruction is retired, the pipeline is flushed and the architectural state is set in a safe setting. However, between the faulting instruction emission and its retirement, an attacker can exploit the transient window where instructions following the faulting instructions are transiently executed on unauthorized data. These instructions will never be committed to the architecture, but an attacker can retrieve the information from the side-effects left on the microarchitecture by these transient instructions with a covert channel.

The Meltdown class has a wide variety of exploits and variants. Canella et al. [CBS+19] introduced a classification of Meltdown-like attacks based on the type of fault creating the transient execution. Many Meltdown variants exploit page faults [LSG+20, KW18, BMW+18] where the attacker tries to read unauthorized memory. In that setting, the system throws a page fault, but the data from the read is transiently forwarded to the following instructions in the pipeline, letting an attacker extract them. Applications to Meltdown-PF include leaking the entire kernel memory or reading values out of Intel SGX and generic hypervisors. Canella et al. [CSH+20] presented a page-fault-based Meltdown attacks running entirely in the JavaScript sandbox on 32-bits x86 Linux. Other exceptions were leveraged to mount Meltdown attacks, such as bound-range-exceed, where the attacker triggers the fault by accessing out-of-bound array indexes [CBS+19], general protection faults for unauthorized reads to privileged registers [Teaa], or lazy context switches in the FPU [SP18].

Van Schaik et al. [vSMÖ+19] presented RIDL, a Meltdown-type attack allowing to leak in-flight data from the Line Fill Buffer (LFB). The attacker first sets private values in the LFB, for instance with kernel calls. Then, by loading a new page, it forces the CPU to speculatively load a value from memory, which can be uncached in-flight private values loaded from the Line Fill Buffer. This incorrect load causes a fault, but the transient load is forwarded to transient instructions as in other Meltdown-type attacks. As the attack does not leverage physical or virtual addresses, it allows an address-oblivious attacker to leak privileged information. Such attacks are often referred to as Microarchitectural Data Sampling (MDS). RIDL attacks can leverage different faults to create the transient window, but always exploit the LFB as a source of leakage. RIDL was also implemented entirely in the JavaScript sandbox, where
the attacker is completely oblivious to physical or virtual memory addresses. It allowed an attacker in WebAssembly to leak victim data from a process running on the same physical core.

Schwarz et al. [SLM+19] presented ZombieLoad, another MDS attack targeting in-flight data, allowing to leak data from the LFB even if it is cached in the L1. ZombieLoad works on systems hardened against Meltdown attacks, such as Intel's CascadeLake generation, which is not affected by RIDL. The authors present various application scenarios for ZombieLoad. Notably, they showed how an unprivileged user can steal 128-bits AES-NI keys in under 10s in a cross-process attack.

## 2.5. Side-Channel Attacks on Software and Browser Resources

This section presents an overview of side-channels attacks targeting software resources. These resources include *system-level resources*, managed by the operating system, and *application-level resources*, managed by the various programs. In particular, we take a strong interest in browser resources, as they are a core component of the contributions of this manuscript.

### 2.5.1. Attacks on system resources

Suzaki et al. [SIYA11] introduced an attack targetting memory deduplication. By creating a page in their address space with the exact same content as a victim memory page, the attacker possibly triggers the OS's memory deduplication. Then, the attacker writes on their own page. If the page was deduplicated, this triggers a page fault, resulting in a slower write. If the page was not deduplicated, the write access is fast. This difference in timing lets the attacker know whether or not their page was duplicated, i.e., that another process had a memory page with the exact same content. Suzaki leveraged this exploit to detect the presence of sshd or apache2 on the machine from inside a KVM virtualized environment. They also apply memory-deduplication attacks on images, showing how the same attacker, inside a virtual machine, can detect a file downloaded by a browser. Owens and Wang [OW11] showed how a virtualized attacker could exploit memory deduplication to fingerprint the OS. By reproducing OS specific memory pages, the attacker can detect when deduplication occurs, leaking information on which OS is installed from the virtual machine. Xiao et al. [XXHW13] used deduplication as a physical layer to create a 90 bit/s covert channel between two virtual machines running in the same system. Barresi et al. [BRPG15] introduced an exploit to fully reverse ASLR directly from a virtual machine. Schwarzl et al. [SKLG21] mounted fully remote memory-deduplication attacks based on HTTP requests, allowing to fingerprint server information or reverse KASLR over the internet.

Memory-deduplication attacks were also implemented in the JavaScript sandbox. Gruss et al. [GBM15] showed how a JavaScript attacker could detect running applications as well as monitor websites opened in other tabs in the same browser. The main challenge of a JavaScript-based memory-deduplication attack is to create exact copies of the victim's memory pages. To do so, they leverage JavaScript engine's implementations of `malloc`, creating page-aligned arrays. This allows an attacker to target page-aligned elements, e.g., CSS style sheets or images in browsers. Bosman et al. [BRBG16] showed how a JavaScript-based attacker could leverage page deduplication and the RowHammer bug to gain arbitrary read and write access in Microsoft Edge in a same-address-space setting.
Jana and Shmatikov [JS12] described a new memory-based side channel. By identifying processes' footprints on memory, an attacker, here a malicious application on a Android phone, can infer private information. Their attacker can infer which websites are being browsed by the victim in real-time, as well as the state of the browsing session. The authors also show how context switch timings can leak information on the user's keystrokes. A Linux-based attacker can monitor the `/proc/<pid>/schedstat` system file to retrieve the timing between two keystrokes. Even on Android systems, where this file is not accessible by unprivileged applications, the attacker can leak context switch information from the `/proc/<pid>/status` file instead.

Schwarz et al. [SLG+18b] showed how even an unprivileged sandboxed attacker can mount keystroke timing attacks by exploiting timing differences caused by system interrupts. The attacker repeatedly calls a high-resolution timer, e.g., `rdtsc`. If the OS throws an interrupt, the measurement process is interrupted, resulting in a significant cycle difference between two subsequent timestamps. As I/O operations, including keystrokes, cause system interruptions, an attacker can measure the time between these peaks to retrieve timing information on the user's keystrokes. This information can then be used, along with machine-learning algorithms, to reconstruct typed information such as a PIN codes or URLs [SWT01, ZW09]. Lipp et al. [LGS+17] implemented this attack entirely in the JavaScript sandbox. The main challenge of porting this particular attack to JavaScript is the lack of high-resolution timers. Instead of repeatedly calling a timestamp, the attacker measures the number of increments between two clock edges, similarly to clock interpolation. A drop in the increment count indicates the measurement has been interrupted. The attacker can use these drops to extract timing information about the victim's keystrokes.

### 2.5.2. Attacks on browser resources

Modern browsers are considerably large software that include many optimizations. In particular, components may be shared between origins, tabs, or contexts for optimization and browsing performance reasons. Sharing a component between the victim and the attacker can leak information about the victim's secrets or behavior.

Mowery et al. [MBYS11] showed how a JavaScript attacker can retrieve useful fingerprinting information about the browser vendor, version, or JIT engine by measuring the execution time of specific computations. They also show how an attacker can take advantage of the NoScript extension. NoScript is a browser extension that lets users block JavaScript from unknown origins. It is based on a whitelist system, i.e., the user defines which domains can run JavaScript code in the browser. By including many sources of codes from different origins on its own website, the attacker can identify which are whitelisted and which are not. This information can be used as a fingerprint of the user as the whitelist is custom.

Sanchez-Rola et al. [SSB17a] presented how differences in timings caused by access-control policies leak information about which extensions are installed. Browsers keep an access-control list to prevent third-parties, e.g., a JavaScript attacker, from accessing unauthorized extension resources. When a JavaScript attacker tries to access an extension resource, the browser typically first checks if the extension is installed, then checks the access-control list to see if the third party can access the extension. When they cannot access an extension's resource, a JavaScript attacker can measure this access request time to determine whether or not it was declined because of access control or because the extension is not installed. An attacker can then create a browser fingerprint by inferring which extensions are installed, even with no
permissions.

Van Goethem et al. [vGJN15] introduced several timing attacks against shared browser resources. In particular, they target the loading time of external resources, the parsing time of multimedia resources, the application cache, or the Service Worker Cache API to leak personal information in a cross-origin setting.

Vila and Köpf [VK17] exploited Chrome's shared event loop to monitor users, infer browsed websites, or create a covert channel. By creating contention on JavaScript's main event loop, the attacker can measure delay introduced by other processes using performance-heavy JavaScript computation. If the attacker fingerprints the impact of a specific website on the event loop, they can use it to recognize that website in an online setting. Similarly, monitoring the event loop lets the attacker infer the user's actions and idle times.

## 2.6. Countermeasures to Side Channels

This section presents an overview of side-channel countermeasures. Researchers have proposed a wide array of countermeasures, either specific to a particular vulnerability or more generic approaches. We present three primary paradigms of countermeasures: preventing the leak, preventing the measurement, and detecting the attack. As microarchitectural vulnerabilities are at the frontier of software and hardware, mitigations can be implemented at several levels: hardware, system, or application level, particularly in the browser.

### 2.6.1. Preventing the data leak

The most naive approach to preventing side channels is probably to fix the source of the data leakage. In particular, most side channels exploit the sharing of a component between the victim and the attacker to leak information.

#### 2.6.1.1. Hardware Level

Architecture researchers have proposed several hardware-level mitigations to microarchitectural data leaks. Most of these solutions rely on resource partitioning, i.e., that the resources are not shared between the attacker and the victim.

Cache partitioning has been a widely studied mitigation to cache side channels. Static sharing [Pag05] is probably the most straightforward cache partition: the cache is split into multiple partitions, each assigned to a specific process. That way, data in a set cannot be evicted or flushed by malicious processes. Similar static sharing has been proposed at the logical thread level [OST06]. However, statically sharing the cache comes at a dramatic performance cost, as it reduces the cache capacity for each process and loses the shared memory optimizations brought by VIPT addressing. Dynamic partitioning introduces less performance overhead than static partitioning. Wang and Lee [WL07] proposed a Partition-Locked Cache, where users could activate a protection bit on cache lines, preventing them from being evicted by other processes. Vanguard [SLCO18] uses a different approach to cache partitioning: instead of assigning lines to a specific process, processes can only replace their own data in a cache set. This replacement policy modification prevents an attacker from evicting or flushing victim data while keeping the advantages of a shared cache. Other approaches have been proposed, e.g., partitioning at the set level [DJL+12], or runtime approaches based on hit/miss rates [WC14, WFZ+16].
<table><caption>**Table 2.1.** – Overview of side channels countermeasures.</caption><thead><tr><th></th><th>Preventing leakage</th><th>Preventing measurement</th><th>Detecting attack</th></tr></thead><tbody><tr><td>Browser</td><td><ul><li>Same-Origin Policy [Cong]</li><li>Site Isolation [RMO19]</li><li>COOP/COEP [AJ]</li></ul></td><td><ul><li>Removing high-resolution timers</li><li>Deterministic browsers [CCLW17]</li></ul></td><td></td></tr><tr><td>Application</td><td><ul><li>Secret-independent Code</li></ul></td><td><ul><li>Blinding</li></ul></td><td></td></tr><tr><td>System</td><td><ul><li>Memory partitioning [KPM12, Inte]</li><li>Thread partitioning</li><li>Temporal partitioning [GZ13, VRS14, ZR13]</li></ul></td><td><ul><li>Fuzzy Timers [VDS11]</li></ul></td><td></td></tr><tr><td>Hardware</td><td><ul><li>Cache partitioning [Pag05, OST06, WL07, DJL<sup>+</sup>12, WC14, WFZ<sup>+</sup>16]</li><li>Dynamic SMT [TP19, TRVT22]</li></ul></td><td><ul><li>Randomized Cache [WL07, LL14, Qur18, THAC18]</li></ul></td><td><ul><li>HPC-based detection [CSY16, AYQ<sup>+</sup>16, PIO19, LG18, For18, WSS<sup>+</sup>20]</li></ul></td></tr></tbody></table>

Sharing components is also at the root of SMT-based attacks such as port contention. The most naive mitigation to these attacks is to simply disable SMT. However, this proposition represents a considerable performance degradation of up to 15% [BSN<sup>+</sup>19], as SMT allows for a highly efficient use of hardware resources. Townley and Ponomarev [TP19] proposed SMT-COP, a hybrid approach based on statically partitioning the use of resources between threads. This partitioning could be either temporal, with each thread accessing the resource after the other, or spatial, with each thread having its execution units. Their approach must be supported by the hardware and introduces a performance overhead of 8% compared to standard SMT, while preventing most contention-based side channels on the execution units or ports. More recently, Taram et al. proposed SecSMT [TRVT22], focusing on more secured shared resources against contention-based side channels. Their approach introduces, at the hardware level, different ways to share resources. In static partitioning, the resources are statically shared between logical cores. In dynamic partitioning, the partition of resources evolves according to the workload of logical cores to enhance parallelization. However, the
resources are never used by both cores simultaneously. More interestingly, asymmetric partitioning relies on different levels of trust. This model gains even more performance by letting a low-level security thread leak information to a high-security thread, but not letting high-security information leak to other threads. This is particularly interesting in a browser-based scenario. It is unsafe to leak information to the sandbox, whereas leaking sandboxed information to other threads presents fewer threats. Their asymmetric partitioning presents almost no overhead compared to traditional SMT.

Spectre-specific countermeasures were also proposed. Intel proposed to use logic fences in sensitive computations to prevent speculation [Inta]. The instruction sequence Return Trampoline (retpoline) was proposed by Google [Tur18] to mitigate Spectre-RSB. Placed into a source code, it forces misspeculation in a specific branch containing a fence, preventing speculation.

### 2.6.1.2. System Level

The OS can enforce partitioning at the memory level. Such approaches have been proposed against cache attacks in a cloud scenario. Kim et al. [KPM12] proposed to create stealth memory, i.e., areas of memory that are not set congruent with data from other processes. As the mapping of addresses to cache sets is deterministic, the system has to ensure that all pages congruent to a specific set are either locked or assigned to a single process. Processes can then set sensible data in stealth memory and ensure no data leak from the cache. Cache Allocation Technology [Inte], is an Intel software interface controlling the cache space allocation. It can operate at several granularities, restricting the cache space allowed to a thread, process, or VM.

The OS can use the scheduler to enforce spatial partitioning of resources with a thread granularity. For instance, allowing highly sensitive operations, such as computations depending on a secret, to run on a different physical core than other applications could reduce the risk of leaking private information in a side-channel attack depending on SMT such as port contention. Similarly, only sharing hardware resources between processes owned by the same user could provide more isolation, especially in cloud environments.

Temporal partitioning can also be enforced by the operating system. For instance, by flushing the cache [GZ13, VRS14] or internal buffers, e.g., the BTB [ZR13] at each context switch reduces the sharing of private data, thus reducing the risks of microarchitectural side channels.

### 2.6.1.3. Application Level

A generic application-level mitigation to microarchitectural attack is to ensure that the microarchitecture state is not dependent on secrets. In particular, cryptographic libraries try to ensure constant-time and cache-independent execution of computations using the secret key. Port-independent code has also been suggested [ABuH$^+$19]. If the port usage does not vary according to the secret information, then port-contention-based side-channel attacks are ineffective. However, such a solution requires detecting and correcting all sensitive code in existing sensible implementations, and for all vulnerable microarchitectural components, and is not effective against covert channels.
### 2.6.1.4. Browser Level

Isolation is a staple of browser security. Separating resources and communication between contexts drastically reduces the threat surface of timing side channels.

The same-origin policy [Cong] is a central security feature of the web. An origin is defined by the tuple (Protocol, Port, Host). The same-origin policy restricts read access to resources loaded from a different origin. This means that data from one website, e.g., authentication cookies, are not accessible from another website loaded in another tab. This countermeasure aims at mitigating, among others, timing attacks using browser resources. However, the same-origin policy has no impact on other types of timing attacks. In particular, Spectre [KHF+19] was also implemented in JavaScript, therefore demonstrating the limitations of the same-origin policy.

In response to transient execution attacks, Chrome developed a new security measure called site isolation [RMO19], which forces each website, defined by the tuple (Protocol, Host), to run in a specific process, not shared with other websites. This prevents an attacker to access the mapped memory space of another website and mitigates the effect of process-space JavaScript-based transient execution attacks by preventing the access—including transient—to out-of-bound information. This feature was deployed in Chrome 67, and was deployed in Firefox 95 [Wika].

Site isolation was extended with the introduction of Cross-Origin Embedder Policy (COEP) and Cross-Ori origin Opener Policy (COOP) [AJ]. COOP ensures that a top-level window is isolated from other documents by putting them in a different browsing context group. For instance, if a website opens a pop-up whose origin is different from the website, the browser under COOP puts the pop-up in a separate process, similarly to site isolation, and prevents direct interaction with the main document. COEP complements COOP by forcing the browser to only load trusted resources. If a resource has no explicit permission to be loaded, the browser does nothing if COEP is enabled. Both COOP and COEP rely on policies defined through HTTP headers and they were both added in Firefox 79 and Chrome 83 [Conb, Cona]. When they are enabled on a document, they guarantee a unique context group for the site and a safe loading of trusted resources.

## 2.6.2. Preventing the measurement

Instead of fixing the data leakage at the source, academics have proposed to obfuscate the leakage by preventing attackers from detecting or measuring it. This can be either by removing access to measurement tools or randomizing execution to prevent monitoring.

### 2.6.2.1. Hardware Level

Many cache attacks exploit the deterministic nature of cache indexing, e.g., the usage of an eviction set in Prime+Probe. To prevent attacks, researchers have proposed randomized caches, obfuscating the leakage by randomizing the mapping from memory addresses to cache sets. Wang and Lee [WL07] proposed to use a dynamic random permutation table for each process, rendering other processes' cache access unpredictable. Liu and Lee [LL14] proposed random-fill caches, where cache misses fill the cache with randomized values in addresses next to the accessed address. However, both approaches require hardware-based lookup tables and are not scalable to large last-level caches. Computation-based approaches have been developed for large randomized caches. CEASER [Qur18] is a secured cache using
low-latency block ciphers as an indexation function. This address-set mapping is frequently
refreshed by changing the cipher key. Trilla et al. [THAC18] propose another approach to
cache randomization. Instead of randomizing the address-to-set mapping, they inject random
timings to the cache behavior, lowering the risk of timing leakage. Purnal et al. [PGGV21]
proposed a systematic approach to randomized caches, listing most major contributions.

2.6.2.2. System Level

The operating system can prevent data leakage by preventing the timing measurement.
For cache attacks, if an attacker cannot distinguish cache hits from misses, the attack is
not possible. Vattikonda et al. [VDS11] modify the native rdtsc instruction on virtualized
systems. They reduce the timer by adding significant jitter, i.e., a random component to
timing measurement. Similarly, StopWatch [LGR14] is a cloud-based architecture aiming at
preventing co-residency timing side channels. Each VM is replicated, and I/O timings are
computed on the median of all replicated VMs' timings.

2.6.2.3. Application Level

Applications can enforce randomization in their execution to prevent attackers from retrieving
the full secrets. In particular, applications using sensitive secrets, e.g., keys in cryptographic
computation, use blinding techniques to prevent timing information from being directly
related to the key. Instead of using the key for operations such as scalar multiplication, the
application modifies the key, generally with a xor operation, with a random factor. Then,
after the computation, the result is returned to its original value. If the attacker retrieves
information with a side-channel attack, they obtain information about the xorred key and
not the actual key. This random component is computed at runtime and changes for each
computation, preventing the attacker from reverting the changes.

2.6.2.4. Browser Level

After the publication of microarchitectural side-channel attacks in the JavaScript sand-
box [OKSK15], browser vendors have enforced timing-based countermeasures in client-side
scripting languages. By removing access to high-resolution timers, browser vendors aim at
reducing data leakage in generic JavaScript applications.

**Mitigations on performance.now()** Such changes have been applied to performance.now(), JavaScript high-resolution timer implementation. After the first JavaScript timing attack [OKSK15] in 2015, the W3C advised that the resolution should be reduced to 5 µs to mitigate timing attacks, and browsers followed [Ore15, K15].

However, in 2017 Schwarz et al. [SMGM17] demonstrated that it was still possible to
recover high-resolution timers with the clamped resolution by using interpolation. This
allowed an attacker to reimplement most timing attacks. In particular, they presented a clock
interpolation-based timer with a resolution of 500 ns.

After the disclosure of Spectre [KHF+19], browser vendors went to greater lengths to
mitigate timing attacks. Mozilla first clamped performance.now() to a resolution of 20 µs in
Firefox 57.0.4 [Wag18] then furthermore to 2 ms in Firefox 59 [Bug18a].
Browser vendors then introduced jitter to `performance.now()`: Firefox 60 set the resolution to 1 ms and added a jitter of 1 ms range [Bug18b, Con20b], while Chrome 64 set the resolution to 100 µs with a jitter of 100 µs [Kyö18].

<figure><img src="image_24.png" /><figcaption>(a) The time between two clock edges is longer: (b) The time between two clock edges is shorter: the interpolated time is 9 ticks. the interpolated time is 5 ticks.</figcaption></figure>

**Figure 2.15.** – Impact of `performance.now()`'s jitter on clock interpolation: for the same event, the interpolated time changes between two measurements

The goal of this jitter is to prevent clock interpolation as presented by Schwarz et al. [SMGM17] and described in Section 2.3.3. Figure 2.15 presents the impact of jitter on interpolation. Clock interpolation does not measure the duration of an event *per se*, but rather the duration between the end of the event and the next clock edge. If the duration of a clock period is constant, this is equivalent to measuring the duration of the event. However, if the duration of a clock period is not constant, measuring twice the same event can yield different timings, rendering the measurement non-deterministic. If the jitter is higher than the needed resolution for timing attacks, i.e., sufficient to distinguish the two leaking timings, attackers cannot mount their attack.

However, such drastic countermeasures also impacted web development [Bug18c]. JavaScript-based animation or video games often require sub-millisecond timers, which were no longer available. These countermeasures were however temporary, until other countermeasures were developed. Indeed, because of the security added by site isolation, most browsers have re-allowed high-resolution timers under certain conditions.

Since Chrome 91, `performance.now()` has a resolution of 5 µs with a jitter of 5 µs under COOP/COEP [Sta], whereas, since version 79, Firefox has a resolution of 20 ± 20 µs without jitter, only when COOP/COEP is activated [Moz20b, Bug20].

The implementation of jitter differs across browsers. On Firefox 81, the jitter is computed using the SHA-256 hash function as follows [Moz20b]: the high-resolution timestamp is clamped to the lowest millisecond. The browser then uses SHA-256 on a tuple composed of the clamped time, a context-dependent seed, and a secret seed. It returns a random midpoint between the clamped time and the next millisecond. Depending on whether the precise timestamp is under or above this midpoint, the returned timestamp will be the lowest or highest millisecond, uniformly distributing clock edges between 0 and 2 ms. On Chromium, the jittered value is computed similarly, using murmur3 as the hash function [Good].

**Mitigations on SharedArrayBuffer**

Due to the powerful threat this SharedArrayBuffer-based clock create, SharedArrayBuffer were disabled by default after the publication of Spectre [KHF+19] in Firefox 57.0.4 and all Chrome versions from 60. Once again, this measure is very restrictive for web developers and was only meant to be temporary. With the introduction of COOP/COEP and site isolation, browsers have re-enabled SharedArrayBuffer, claiming that access to a high-resolution timer is not a major threat in a strict isolation context. Both Chrome and Firefox current versions support SharedArrayBuffer only when COOP/COEP is
enabled [Gooc, Conf].

**Other approaches** Due to the complexity of completely removing timers in the browser, researchers have proposed more systematic solutions. One approach is to try and make the browser deterministic as prototyped by Cao et al. with DeterFox [CCLW17]. By transforming a physical clock into a logical one, they change the behavior of known timers so that they do not return the time a request takes but return the number of operations that are being executed. However, a deterministic browser would require significant architectural changes and would represent a significative performance overhead. Some programs need to access the actual physical clock of the system to function properly and having a deterministic clock would present a lot of hurdles for developers. Schwarz et al. [SLG18a] propose a fine-grained permission system to prevent JavaScript side-channel attacks. It allow to control the fuzziness of timers, as well as other software-based mitigations such as array index randomization, slower SharedArrayBuffer or disabling them, or removing JavaScript multithreading.

### 2.6.3. Detecting the attacks

Additionally to preventing the attack at its source, researchers have proposed methods to detect – and stop attacks at runtime.

#### 2.6.3.1. Hardware and System Level

Initially a debugging and optimization tool, Hardware Performance Counters can be used to detect side-channel attacks during execution. While performance counters are purely microarchitectural features, the detection is generally handled by system handles on HPCs. Foreman [For18] classify performance-counter-based detection in three categories.

Signature-based detection focuses on detecting well-known threats in an unadaptative scenario. Chiappetta et al. [CSY16] proposed to detect Flush+Reload attacks based on the L3 cache usage. In a standard execution, a process should benefit from the cache with cache hits. However, when they fall victim to Flush+Reload-based attacks, due to the constant flushes, processes emit significantly more L3 cache misses than usual. Chiappetta et al. use this difference to detect attacks in real-time by comparing the evolution of L3 misses with performance counters between a signature of a standard, unattacked execution and the signature of the runtime execution. If the current execution presents more cache misses, it means that the process is under attack. In their paper presenting Flush+Flush [GMWM16], Gruss et al. show how these performance-counters-based detection methods are ineffective against Flush+Flush, as this attack primitive does not cause cache misses. This signature-based approach has the advantage of being simple to implement as only standard signatures are needed, but is not highly adaptative to new attacks or attacks on new implementations.

Heuristic-based approaches try to provide a more generalistic approach. They monitor malicious behaviors by setting thresholds on specific performance counters. When one or several thresholds are crossed, they detect possible attack threats. CacheShield is a heuristic-based approach, monitoring the quantity of cache misses with performance counters. When a threshold is crossed, the tool reports it as a possible attack. Aweke et al. [AYQ+16] proposed a HPC-based protection leveraging last-level cache latest misses, Load Latency, and Precise Store counters to detect when a DRAM row is being hammered. When this detection occurs, the adjacent rows are refreshed. HexPADS [Pay16a] is an automated framework to detect
outstanding behavior and classify them as potential attacks. It is able to detect RowHammer attacks or cache side channels. Heuristic-based approaches allow for more genericity in the detection, but may yield a higher rate of false positives than other approaches as standard execution may accidentally cross thresholds.

Machine-learning-based detection is emerging as a compromise between the two previous approaches. It has been used by Chiappetta et al. [CSY16] to enhance detection using their signature-based approach. Prada et al. [PIO19] trains a model using L3 cache misses performance counters to detect Flush+Reload side channels against AES. By monitoring last-level cache events as well as branch mispredictions, Li and Gaudiot [LG18] propose a machine-learning-based approach to detect Spectre-PHT type attacks. Wang et al. [WSS+20] systematize the approach of machine learning on HPC-based detection to compare the efficiency, accuracy, and performance overhead of various sampling methods, training datasets, or models.

## 2.7. Browser Fingerprinting

A *browser fingerprint* is a collection of information allowing to identify, uniquely or not, an instance of a browser. Browser fingerprinting is closely related to side channels: an agent extracts legit information, e.g., the browser version or language for browser fingerprinting, to distort them into another usage. While side channels often aim at stealing secrets or monitoring a user, browser fingerprinting aim at circumventing the web's anonymity. This section provides a brief introduction to browser fingerprinting, necessary to comprehend the privacy implications of Chapter 5.

### 2.7.1. Usages of Browser Fingerprinting

The browser's profile created by browser fingerprinting allows agents to break the anonymity of the web. This fingerprint can be used for different purposes, legitimate or malicious:

**Web tracking:** Tracking is probably the most widespread fingerprint application. An agent can create a fingerprint for a user and recognize it on various websites, thus monitoring its activity. It allows, for instance, to provide targeted advertisement. This tracking fingerprinting is particularly interesting on the modern web, where other tracking methods, e.g., cookies, are regulated by laws.

**Authentication:** For more legitimate purposes, fingerprints can be used as a method to enhance authentication. In addition to a classic authentication, e.g., with a password, a website can retrieve the user's fingerprint and compare it with previous authentications. If the fingerprint changed, the website can ask for a more secure mean of authentication.

**Bot identification:** Users' fingerprints result from a set of parameters emerging from a normal utilization of a computer and browser. A computer-controlled browser can lack a coherent human-like fingerprint, and sites can use this difference to detect bots and reduce risks of denial of service attacks or web crawling.

**Vulnerability discovery:** Browser fingerprinting can reveal information about the system that is supposed to be hidden, e.g., the version number of software or OS. A malicious user can use this information to find specific exploits against that system, thus reinforcing the threat posed by attackers.
### 2.7.2. Attributes of Fingerprints

A fingerprint is often a set of various attributes ranging from browser values to hardware information. A fingerprint, or more precisely an attribute, can be more interesting than others with certain properties:

**Uniqueness:** The end goal of fingerprinting is uniquely identifying a user. To that extent, a unique fingerprint is necessary. Unique attributes are rare, and this uniqueness is generally obtained by collecting multiple attributes. However, an attribute can provide more uniqueness if it allows to reduce the scope of users on its own.

**Stability:** Any change in an attribute value changes the fingerprint and therefore breaks user identification. However, relying on software fingerprinting means that attributes are constantly changing (e.g., the browser version in the User Agent). Vastel et al. [VLRR18] showed that it is possible to link two fingerprints that are slightly different from each other through heuristics. Therefore, uniqueness is less critical than stability to link fingerprints for a single attribute.

### 2.7.3. Software Fingerprinting Techniques

Software attributes are by far the most studied in the literature. In 2009, Mayer [May09] demonstrated that information sent by the browser to the server for configuration purposes, e.g., screen size, could lead to the deanonymization of users. This finding was extended in 2010 [Eck10] in a larger-scale approach, considering HTTP headers, JavaScript active fingerprinting, and plugins such as Flash. This study showed that 95% of collected fingerprints were unique.

Since the publication of these original papers, the variety of software-based fingerprint attributes only grew. Fingerprints used various features such as the Canvas API [Sto13, AEE+14], fonts [FE15], battery status information [OACD15], WebGL [Sto13] or installed browser extensions [SVS17, SSB17b, SLKN19].

### 2.7.4. Hardware Fingerprinting Techniques

Instead of exploiting software-based information, an attacker can leverage side channels to infer hardware-related attributes. This fingerprinting method is typically active, i.e., the attributes are not directly given by the browser but rather inferred from computation, e.g., a JavaScript code. Nakibly et al. [NSY15] proposed various ideas to identify a user based on fingerprints of their audio system or GPU. Sanchez-Rola et al. [SSB18] leveraged imperfections in the crystal of processor clocks to identify users. More recently, Laor et al. [LMD+22] showed that the difference in execution time between different execution units in individual GPUs can serve as a unique and robust fingerprint.

As they are not static, hardware fingerprints can be harder to obtain than software fingerprints but are more stable as users rarely change hardware components and are less likely to be spoofed as a user cannot modify them as easily as an HTTP header.
High Resolution Timers in the Browser 3

JavaScript-based timing attacks have been greatly explored over the last few years. They rely on subtle timing differences to infer information that should not be available inside of the JavaScript sandbox. In reaction to these attacks, the W3C and browser vendors have implemented several countermeasures, with an important focus on JavaScript timers. In 2015, Oren et al. [OKSK15] implemented a fully JavaScript-based cache attack, running entirely in the browser. Based on Prime+Probe [LYG+15], it allows an attacker to track user behavior and recover information belonging to other processes running on the same system. This contribution opened the scope for more critical attacks, exploiting hardware components from the restricted JavaScript sandbox. To try and mitigate JavaScript-based timing attacks, browser vendors have developed countermeasures specifically targeting timers. Notably, they decreased timers’ resolution to make them less precise and introduced jitter to add noise to measurements. Other security features like site isolation [RMO19] were added to reinforce the browser’s security and act as a novel line of defense against timing attacks. Amid all these changes, it can be hard to keep track of all the different evolutions that browsers underwent: each browser vendor implements different countermeasures, and decisions can be taken in a hurry after the publication of critical vulnerabilities. For instance, in 2018, drastic timer-based countermeasures were taken only a few days after the publication of Spectre [KHF+19]. These patches can be hard to track, and the quantitative aspect of such countermeasures has not been appropriately studied.

In this chapter, we aim to provide a clear view of the vulnerability of browsers to timing
attacks. In the first part, we take a broad look at the research done in the area to systematize
it. We provide a taxonomy of attacks with their prerequisites and classify the countermeasures
based on their target resources. In the second part, our goal is to assess the actual efficiency
of timing-based countermeasures after seeing how much they changed over the years. In order
to gain proper insight, we implemented our own performance.rdtsc() high-precision timer
into Chromium and Firefox so that we can deconstruct studied timers into their most basic
blocks. With its help, we identified that recent countermeasures present significant advances
against timing attacks, but they also present noticeable steps back. For example, with the
introduction of COOP/COEP, Firefox 79 gained a robust resource isolation mechanism but
lost a lot with regard to timing attacks. Before, an attacker needed several minutes to build
an eviction set to conduct an attack. Now, with the resolution changed from 1 ms to 20 µs, we
show that it can be set up in just a single second. Another problem is the recent reintroduction
of SharedArrayBuffer after it was deactivated due to the disclosure of Spectre [KHF+19]. Its
presence introduces a real security risk because a malicious script can abuse it by creating
a very powerful timer that has an incredibly high resolution with very low overhead. By
lowering timing-based countermeasures, all the prerequisites for large classes of timing attacks
are met, meaning that these attacks can theoretically be implemented.
**Contributions** This chapter makes the following contributions:

* We provide a classification of prerequisites for timing attacks and present the most notable classes of timing attacks (Section 3.1).

* We classify countermeasures based on the resources they target (Section 3.2).

* We present tools to analyze timers and the threat they pose for timing attacks. Notably, we detail our custom timers and automatic tests that measure the resolution and measurement overhead of several built-in timers, which can easily be reproduced for other systems and future browser versions (Section 3.3).

* We present a longitudinal study of browsers' timing-based countermeasures. (Section 3.4).

**Update to the results of this contribution** This contribution, including the results and the conclusion and discussion, stem from an article written in 2020. Due to the rapid development of browsers and research in security, several results may have changed since. Although these changes do not drastically impact the conclusions of this article, Section 3.7 presents the latest changes and discusses their consequences.

## 3.1. Timing attacks in browsers

Timing attacks in browsers are a large class of attacks exploiting timing differences in computations in order to infer private information. As they are mainly based on JavaScript, the attacker code will, by design, always be executed on the victim's hardware. In this section, we aim at systematizing timing attacks in browsers, by classifying prerequisites for different classes of attacks.

### 3.1.1. Attack prerequisites

We systematize the major timing attacks prerequisites in order to classify them and better understand the outline of attacks. We have identified the following prerequisites:

* P1 High-resolution timers,

* P2 Shared hardware resources,

* P3 Transient execution,

* P4 Shared system resources,

* P5 Shared browser features.

**P1: High-resolution timers** To mount timing attacks, an attacker must be able to distinguish between data-leaking events with their timings. To do so, they need access to high-resolution timers. JavaScript offers several timers presented in Section 2.3, but this chapter focuses on the two timers with the best resolution: `performance.now()` interpolation and `SharedArrayBuffer`.
<table><caption>**Table 3.1.** – Comparison of attacks prerequisites. All attacks require P1, but with different ranges of resolutions.</caption><thead><tr><th></th><th>Required<br>resolution<br>(P1)</th><th>P2</th><th>P3</th><th>P4</th><th>P5</th></tr></thead><tbody><tr><td>Spy in the sandbox [OKSK15]</td><td>100 ns</td><td>●</td><td>○</td><td>○</td><td>○</td></tr><tr><td>Rowhammer.js [GMM16]</td><td>100 ns</td><td>●</td><td>○</td><td>○</td><td>○</td></tr><tr><td>Website fingerprinting [SKH<sup>+19</sup>]</td><td>10–100 ns</td><td>●</td><td>○</td><td>○</td><td>○</td></tr><tr><td>DRAM covert channel [SMGM17]</td><td>10 ns</td><td>●</td><td>○</td><td>○</td><td>○</td></tr><tr><td>Breaking ASLR [GRB<sup>+17</sup>]</td><td>100 ns</td><td>●</td><td>○</td><td>○</td><td>○</td></tr><tr><td>Spectre [KHF<sup>+19</sup>]</td><td>100 ns</td><td>●</td><td>●</td><td>●</td><td>○</td></tr><tr><td>ret2spec [MR18]<sup>1</sup></td><td>100 ns</td><td>●</td><td>●</td><td>●</td><td>○</td></tr><tr><td>RIDL [vSMÖ<sup>+19</sup>]</td><td>100 ns</td><td>●</td><td>●</td><td>●</td><td>○</td></tr><tr><td>Store-to-Leak [CGG<sup>+19</sup>]</td><td>100 ns</td><td>●</td><td>●</td><td>●</td><td>○</td></tr><tr><td>Memory deduplication [GBM15]</td><td>1 µs</td><td>○</td><td>○</td><td>●</td><td>○</td></tr><tr><td>Loophole [VK17]<sup>2</sup></td><td>100 µs</td><td>○</td><td>○</td><td>○</td><td>●</td></tr><tr><td>Extension side channel [vGJ17]</td><td>100 ns</td><td>○</td><td>○</td><td>○</td><td>●</td></tr></tbody></table>

**P2: Shared hardware resources** Modern CPUs present major microarchitectural optimizations in order to compute rapidly and efficiently, often solely created with performance in mind. Since hardware is situated below software security rings and components are often shared between processes, contention at the microarchitectural level can be the root cause of software-based side-channel attacks. By design, these attacks are also less fixable than software.

**P3: Transient execution** Transient instructions are instructions that are executed but never committed to the architecture. They can be produced by several factors, such as speculative execution or faulting memory accesses.

**P4: Shared system resources** In a browser, system resources are often shared between contexts. Memory in particular can be shared between all code running in the same browser process.

**P5: Shared browser features** A lot of purely software features are shared between contexts or tabs in browsers. This is the case for history, cookies, event loops, or renderers, for example. The difference in timings of such features' operation can leak information between tabs and inform about a user's behavior.

### 3.1.2. Attack classes

We now give an overview of the major classes of timing attacks in browsers, along with their major characteristics and prerequisites. Table 3.1 illustrates the prerequisites for a sample of state-of-the-art timing attacks in browsers.
**Hardware-based side-channel attacks** Hardware-based side-channel attacks exploit hardware components shared by all processes in the system. By measuring the computation time of specific operations, an attacker can infer the state of certain components. These attacks share two major prerequisites:

P1 High-resolution timers,

P2 Shared hardware components, e.g., cache, DRAM.

The resolution of required timers varies in function of the attacked hardware, but typically spans from 10–100 ns in order to potentially distinguish different states in the hardware.

**Transient execution attacks** With Spectre [KHF+19], Kocher et al. paved the way for a new class of attacks, exploiting transient execution to leak protected data. Some of these attacks can be implemented in browsers in JavaScript, including Spectre-PHT [CBS+19]. This class of attacks is very wide, but shares some prerequisites:

P1+P2 A hardware covert channel to extract leaked data,

P3 Transient execution,

P4 Shared system resources—finding secrets to leak.

P3 is a wide prerequisite: the Spectre attack alone possesses many variants, each targeting different optimizations [CBS+19]. Most crucially, different attacks target different secrets to leak (P4), i.e., in the same address space, or across address spaces.

**Attacks based on system resources** By exploiting shared system resources, an attacker can also retrieve information using a side-channel attack. These attacks share the following prerequisites:

P1 High-resolution timers,

P4 Shared system resources.

**Attacks based on browser resources** In order to have a uniform and efficient browsing experience, most browsers share information between tabs or processes. This includes, among others, browsing history, browser extensions, or event loops. However, this sharing of information, even if not directly reachable in JavaScript, can leak private information to a malicious site. This class of attacks shares two prerequisites:

P1 High-resolution timers,

P5 Shared browser resources.

<footnote n="1">¹ret2spec can still break ASLR with site isolation.</footnote>

<footnote n="2">²Since each process has its own event loop, the attack is not implementable on recent Chrome versions.</footnote>
### 3.2. Countermeasures in browsers

In this section, we systematize the different countermeasures proposed by academics or browser vendors. We have categorized such countermeasures into three classes:

**C1** Isolation-based countermeasures,

**C2** Timing-based countermeasures,

**C3** Browser resources based.

#### 3.2.1. C1: Isolation

Isolation has been established as a staple of browser security with measures such as site isolation or COOP/COEP, presented in Section 2.6.1.4.

While these countermeasures were mostly implemented to prevent transient execution attacks, especially Spectre-PHT [KHF+19], they do not prevent other timing attacks, e.g., microarchitectural side channels or attacks exploiting browser features as, by design, they are not meant to mitigate P1, P2, or P3. As site isolation isolates the process' attack space, they also do not mitigate P4 for transient execution attacks targeting cross-address-space data, such as RIDL [vSMÖ+19].

#### 3.2.2. C2: Timers

The common prerequisite for timing attacks is access to timers. By removing timers, or lowering their resolution, most timing attacks would theoretically be mitigated. While the needed resolution depends on the attack, most hardware and transient attacks require high-resolution timers (P1), around 10 to 100 ns. Removing such high-resolution timers would theoretically mitigate these attacks.

However, even by reducing timers' resolution, interpolation is still possible as long as attackers have access to timers with a constant time free-running operation. To mitigate clock interpolation in browsers, adding a random jitter to API timers was proposed [SMGM17, KS16]. Adding jitter to a measurement is equivalent to having clock periods of different times (with an average around the real clock period). This means that the interpolated time would vary significantly between clock periods for the same event, hence reducing the precision of clock interpolation.

Another widely-adopted countermeasure was to disable `SharedArrayBuffer` to prevent attackers from using them as high-resolution clocks.

Firefox 81's `performance.now()` has a resolution of 20 µs without jitter, only when COOP/COEP is activated [Moz20b, Bug20], and a resolution of 1 ms when COOP/COEP is not activated. It offers access to `SharedArrayBuffer` only when COOP/COEP is enabled. Since Chrome 72, `SharedArrayBuffer` is enabled and `performance.now()` has a resolution of 5 µs with a jitter of 5 µs under site isolation (which is present by default)<fnref n="3" />.

<footnote n="3">³Since the original publication of this contribution, newer Chrome versions changed the policy about timer security, and now these changes require COOP/COEP to be activated to recover `SharedArrayBuffer` and high-resolution timers. See Section 3.7 for a short update on later changes.</footnote>
### 3.2.3. C3: Browser resources

When some timing leakages in the browser are not fixed by a more general approach like C1, there is a need to issue patches that specifically target them. For example, the history sniffing attack detailed by Paul Stone in 2013 [Sto13] was only fixed in Firefox in 2020 by issuing repaints on both visited and unvisited elements [Moz20a, Moz19]. Regarding extension fingerprinting, the timing attacks detailed by Sanchez-Rola et al. [SSB17c] and Van Goethem and Joosen [vGJ17] were fixed by the Chromium team by changing how the checks for web-accessible resources were made to prevent early-out exits [Chr17a, Chr17b].

### 3.2.4. State of browser countermeasures

Figure 3.1 illustrates the evolution of browsers’ main countermeasures and significant state-of-the-art attacks. P2 and P3 are not mitigated in browsers. Site isolation and COOP/COEP are important security updates on browsers, but do not mitigate hardware side-channel attacks and transient execution attacks other than Spectre-PHT. Furthermore, V8 developers claim that software fixes for transient execution attacks are “an unsustainable path” [TS] as fixes on P3 are too performance consuming, and fixes on P4 only apply to specific attacks. Mitigation for P3 must be implemented at least at the OS level, if not at the hardware level.

The common prerequisite of timing attacks is P1, i.e., access to high-resolution timers. This means that C2, timing-based countermeasures, are the only generic defense for timing attacks, including future timing attacks.

Changes in timer-based countermeasures were motivated by a compromise between security—less effective timers mean less threatening attacks—and usability. Indeed, countermeasures had major implications on web development and were meant to be temporary. SharedArray Buffer are a powerful tool to build more complex websites, and access to high-resolution timers is necessary for some fields of web design, such as animation or monitoring performances. There is a clear trade-off between strengthening the timers for security, and weakening them for easier development. However, we have found no quantitative study of the impact of the changes in values, especially regarding API timers’ resolution and jitter. Finding when and why each countermeasure was deployed often requires a deep dive into browser bug trackers.

With all the changes brought to countermeasures, especially timing-based, it is not clear to what extent P1 is mitigated, i.e., to what extent browsers are vulnerable to most timing attacks, and whether an optimal value for resolution and jitter exists. In the following sections, we evaluate the efficiency of timing-based countermeasures.

## 3.3. Evaluation tools

In this section, we present our threat model, and the properties of timers we are interested in: their resolution and measurement overhead. We focus on three timers or variants: `performance.now()` interpolated, `performance.now()` interpolated and amplified, and `SharedArrayBuffer`.

### 3.3.1. Threat model

We evaluate two popular browsers that are Mozilla Firefox and Google Chrome, letting aside Safari, Edge, or Tor Browser. It should be noted that we study the desktop version of these browsers, as all timing attacks we look at were not performed on mobile devices.
<figure><img src="image_25.png" /><figcaption>**Figure 3.1.** – Timeline of timing attacks and browser countermeasures. Items in italics are significant attacks that caused the changes. Items preceded by **[C1]** are isolation-based countermeasures, and **[C2]** are timing-based countermeasures</figcaption></figure>

JavaScript-based timing attacks can be used in several threat models, each offering a different range of possibilities. First-party attacks, where a user visits a malicious website, offer the most possibilities to the attacker. As the attacker can setup `COOP/COEP` as she wishes, she can freely use the unrestricted timers, based either on `performance.now()` or `SharedArrayBuffer`. In this model with Firefox 81, an attacker has access to `performance.now()` with 20 µs resolution and no jitter and to `SharedArrayBuffer`. However, the attacker has to redirect users to her malicious website.

With third-party attacks, a user visits a legitimate website that has, e.g., a malicious advertisement controlled by the attacker. This allows the JavaScript code of the ad to be run on the user's machine. As opposed to the first-party model, the attacker does not control the top-level page. The attacker therefore cannot set up `COOP/COEP`. Without these flags, an attacker on Firefox 81 only has access to `performance.now()` clamped and jittered at 1 ms and no access to `SharedArrayBuffer`. This model presents a major threat surface, as it can be implemented on massively visited websites, e.g., Twitter or Facebook.

On Chrome 84, timer-based countermeasures are independent of `COOP/COEP`. This means that the first and third-party models offer the same timing possibilities, i.e., access to `SharedArrayBuffer` and `performance.now()` with a 5 µs resolution and jitter. However, Chrome 84 implements site isolation, which prevents certain transient execution attacks.

### 3.3.2. Measurement tools

One challenge is that measuring properties of high-resolution timers requires timers with an even higher resolution and precision, which are not available in standard browser releases. To solve this issue, we built a custom version of Firefox and Chromium that adds a new JavaScript method we call `performance.rdtsc()`, which executes the `rdtsc` instruction. The `rdtsc` instruction reads a CPU timestamp counter and returns it. As `rdtsc` is a cycle-accurate timer, we can use it to evaluate real-world auxiliary timers. Specifically, we use it to measure
the resolution of both `performance.now()` interpolation and `SharedArrayBuffer`, as well as their measurement overhead.

We implemented our custom `rdtsc` builds in Firefox 81 and Chromium 84, built from sources on the same system. We disabled debug flags to get a version as close to the release version as possible. We modified files of the `performance` API [Con20a] to add a new public method, `performance.rdtsc()`, which executes the native `rdtsc` and returns its value. However, `rdtsc` can be reordered by the out-of-order execution, therefore two calls to `performance.rdtsc()` would often be executed together, hence not timing an event. For instance, two calls to `performance.rdtsc()` with a call to `performance.now()` between would return the same timing difference as two subsequent calls to `performance.rdtsc()`. We fixed this by adding memory fences (mfence instructions) in our `performance.rdtsc()` method, before and after the call to `rdtsc`. Memory fences force the CPU to compute all operations preceding the fence before executing operations succeeding the fence. This prevents out-of-order execution of parts of code where the order of execution is critical. Appendix A.1 illustrates our implementation of `performance.rdtsc()` for Firefox 81.

The `mfence` instructions add a constant overhead to the returned timestamp. Overhead is also added by the browser handling of the native code. On our system, the total overhead is around 1000 cycles. In all our following measurements, we measured the cycle difference between two events, and then the cycle difference between two subsequent `performance.rdtsc()`. This allows us to estimate the overhead for the current state of the CPU, depending on noise or core frequency. By removing this overhead, we retrieve a more reliable estimation of the event time in cycles.

### 3.3.3. Resolution

The resolution of a timer is the smallest value that it can measure. The smaller the resolution, the more precise the timer is. However, precisely evaluating the resolution of a JavaScript timer is a challenge as this requires an already precise timer.

**performance.now() interpolation** To evaluate the resolution of `performance.now()`, we evaluate the maximum number of times we can increment a variable between two clock edges. By dividing the clock edge duration by this number of ticks, or measuring the time it takes to increment, we learn the shortest event that we can measure. However, because of the jitter, this value alone is not representative. It can vary significantly between measurements. Hence the importance of the standard deviation of the resolution.

It is also important to note that the duration of a "tick", an increment, can vary from one browser version to another. The main factor of this varying duration is due to the implementation of clock interpolation: after every increment, we check if `performance.now()` timestamp has changed. As the computation of the pseudo-random jitter often uses a hash function, it significantly increases the `performance.now()` computation time. Logically, the number of possible increments during the same duration changes according to this computation time.

**performance.now() amplification** As the resolution is downgraded by pseudo-random values, we also study the impact of amplification. By repeating the measurement, an attacker can average the results and reduce the impact of the jitter. This amplification allows an attacker to recover a higher resolution. As each repetition can increase the resolution, granting an
absolute resolution for an amplification clock is illogical. The attacker has to compromise
between the resolution and the number of repetitions she can afford.

In the following sections, we call error rate for cache hit/miss discrimination, the ratio of false hits, i.e., misses computed as hits, and false misses, i.e., hits computed as misses over the total number of experiments. This error rate allows to evaluate the efficiency of a timer in a real-world cache timing attack. We assume that a timer with a 5% error rate has a resolution sufficient to implement cache timing attacks, hence a resolution of around 10–100 ns. This rate will be used as a standard for amplification in the following sections. We compute this rate by causing ourselves cache hits by calling a variable repeatedly and cache misses by calling a variable after evicting it from the cache by browsing through a large array.

Amplifying the results by repeating measurements is, however, not adapted to all attacks. Specifically, it only works when the attacker controls the event that creates the timing difference. For instance, in the case of a covert channel, the attacker can recreate the conditions for the same measurement several times. Some monitoring attacks do not tolerate repetitions, as the attacker cannot recreate the same conditions for the measurement. For instance, RIDL [vSMÖ+19] steals in-flight data, and cannot select which data is in flight.

**SharedArrayBuffer** For SharedArrayBuffer, the resolution is the time of a shared increment. The faster the increment, the higher the resolution. Other factors can impact the resolution of the timer, such as the computation time of reading a value from the array or potential concurrent accesses. Multithreading is handled differently in different browsers and this can be a potential source of randomness on the timestamp.

3.3.4. Measurement overhead

While the resolution indicates how precise a timer can be, an overhead is always incurred
during a time measurement. In our study, we consider the following ones:

* Setting up a timer and handling it (starting it, stopping it, retrieving the results) always adds an overhead. For `performance.now()`, it is very low as we rely on a built-in API in the browser. For `SharedArrayBuffer`, it is also comparatively low as starting a worker is very fast and communication with it is immediate.

* The repetition of ticks in timer interpolation (see Figure 2.8) is an overhead. Indeed, even if we measure the time of a short event, we still need to wait for the next clock edge of the timer that we rely on to retrieve the results. This is why, for `performance.now()` interpolation, the resolution can be very low but the overhead can be large as we are bound by `performance.now()` resolution.

To evaluate the measurement overhead of a given method, we use our custom `performance.rdtsc()` to measure the time difference between the start and the end of the measurement, without any event in between. A high measurement overhead does not necessarily prevent the attack but it plays a major role in the attack severity.

**Ideal bit rate** A covert channel created with a high measurement overhead will yield a lower bit rate than one with a low measurement overhead, i.e., the time to receive one bit will be higher with a high measurement overhead. Assuming each measure of time corresponds to a bit of information, and that the time the operation takes $t_{op}$ is insignificant with respect to the overhead $t_{oh}$, we can define the ideal bit rate of such a timer to $\frac{1}{t_{oh}}$ bit/s.
<table><caption>**Table 3.2.** – Comparison of timers’ resolution, measurement overhead and ideal bit rate for an error rate of 5%. We used a frequency of 1.60 GHz and the resolution is displayed in ns. We can observe that the SharedArrayBuffer-based timer is by far the most efficient, as it offers a better resolution and a lower measurement overhead than timers based on performance.now() on all browsers. Timers based on performance.now() clocks are still highly effective in Chrome 84 and Firefox 81 with COOP/COEP.</caption><thead><tr><th>Browser</th><th>Timer</th><th>Resolution [cycles]</th><th>Converted resolution</th><th>Measurement overhead [cycles]</th><th>Ideal bit rate [bit/s]</th></tr></thead><tbody><tr><td rowspan="2">Chrome 84</td><td>SharedArrayBuffer</td><td>20</td><td>10 ns</td><td>40</td><td>1 × 10<sup>8</sup></td></tr><tr><td>performance.now() interpolation<sup>4</sup></td><td>100-1000</td><td>100 ns</td><td>7.2 × 10<sup>4</sup></td><td>22 × 10<sup>4</sup></td></tr><tr><td rowspan="3">Firefox 81</td><td>SharedArrayBuffer</td><td>20</td><td>10 ns</td><td>42</td><td>1 × 10<sup>8</sup></td></tr><tr><td>performance.now() interpolation</td><td>100-1000</td><td>100 ns</td><td>2.9 × 10<sup>7</sup></td><td>60</td></tr><tr><td>Interpolation with COOP/COEP</td><td>100-1000</td><td>100 ns</td><td>7 × 10<sup>4</sup></td><td>22 × 10<sup>4</sup></td></tr></tbody></table>

**Eviction set** Measurement time, and therefore measurement overhead, also impacts other attacks. For instance, building an eviction set in JavaScript [VKM19], has a complexity in time measurements of $O(C)$ where C is the size of the cache. Standard L3 caches have a size of several megabytes. In practice, on our system, an attacker must measure latency around 100 000 times in order to build an eviction set. We use the computation of an eviction set as a standard as it is a critical step of attacks based on Prime+Probe. A high measurement time therefore leads to huge eviction set computation times, which may not be available to an attacker in a real-world scenario where a user only spends a few seconds or minutes on a web page.

## 3.4. Results

In this section, we present the results of our longitudinal studies of `performance.now()` and `SharedArrayBuffer` timers over many browser versions, as well as the impact of changes in countermeasures on state-of-the-art attacks. Table 3.2 presents the results of our comparative study.

### 3.4.1. Experimental setup

We ran measurements on a machine with an Intel CPU i5-8365U (Whiskey Lake generation) with 1.60 GHz frequency, under Fedora 31.

We performed experiments using every major release versions of Firefox from 53 (2017)

<footnote n="4">⁴COOP/COEP has no impact on timers in Chrome 84.</footnote>