576 lines
27 KiB
TeX
576 lines
27 KiB
TeX
\documentclass[10pt]{article}
|
||
|
||
% import des packages nécessaires
|
||
\usepackage[margin=1.2in]{geometry}
|
||
\usepackage[french]{babel}
|
||
\usepackage[T1]{fontenc}
|
||
\usepackage{algorithm2e}
|
||
\usepackage{csquotes}
|
||
\usepackage{hyperref}
|
||
\usepackage{footmisc}
|
||
\usepackage{graphicx}
|
||
\usepackage{amsmath}
|
||
\usepackage{xcolor}
|
||
\usepackage{syntax}
|
||
\usepackage{float}
|
||
|
||
% configuration des packages
|
||
\hypersetup{colorlinks=false}
|
||
\graphicspath{ {./figs/} }
|
||
\SetKwComment{Comment}{/* }{ */}
|
||
|
||
% titre
|
||
\title{Caractérisation de l’instruction \texttt{clflush} sur systèmes multi-\ang{socket}}
|
||
\author{Augustin \textsc{Lucas}\\ENS de Lyon\\ \\Encadré par :\\
|
||
Guillaume \textsc{Didier}, Angeliki \textsc{Kritikakou}\\
|
||
Équipe \textsc{Taran}\\
|
||
Laboratoire \textsc{Irisa}\\
|
||
Université de \textsc{Rennes} 1}
|
||
|
||
\date{3 juin 2024 - 12 juillet 2024}
|
||
|
||
% quelques macros
|
||
\newcommand{\TODO}[1]{{\color{red}#1}}
|
||
\newcommand{\ang}[1]{\emph{#1}} % texte anglais
|
||
|
||
% le document lui-même
|
||
\begin{document}
|
||
\maketitle
|
||
|
||
\begin{abstract}
|
||
Les attaques par canaux auxiliaires sont particulièrement intéressantes car elles
|
||
permettent un accès à des informations restreintes, par delà même les \ang{sandbox} ou
|
||
machines virtuelles. Nous nous intéresserons plus particulièrement aux attaques par
|
||
canaux auxiliaires sur le cache : Flush+Reload et Flush+Flush.
|
||
|
||
Ce stage cherche à renseigner la topologie des appels à l'instruction \texttt{clflush}
|
||
sur des systèmes \ang{Intel} à deux \ang{sockets} ; notamment pour mieux évaluer l'état d'une
|
||
ligne de cache en fonction du temps d'exécution de l'instruction \texttt{clflush} sur celle-ci.
|
||
\end{abstract}
|
||
|
||
\newpage
|
||
\tableofcontents
|
||
|
||
|
||
\section{Contexte}
|
||
|
||
\subsection{Hiérarchie de cache}
|
||
|
||
La mémoire DRAM d'un ordinateur est lente comparée à la fréquence du CPU. Le CPU dispose donc de caches,
|
||
basés sur des mémoires SRAM, plus petites mais plus rapides. Stocker en cache les éléments accédés
|
||
le plus fréquemment permet donc de réduire le nombre d'appels à la mémoire et donc le temps d'exécution.
|
||
|
||
Les processeurs que nous étudions disposent de 3 niveaux de cache : L1, L2, L3.
|
||
Chaque coeur possède, au premier niveau, un L1-I (cache des instructions) et
|
||
un L1-D (cache des données). Au second niveau, il possède un L2 qui contient potentiellement
|
||
des données et des instructions. Au dernier niveau, le L3 est partagé entre tous les coeurs de la
|
||
\ang{socket}, et celui-ci est inclusif de tous les caches de niveau inférieur : toute ligne
|
||
stockée dans le cache L1 ou L2 est également dans le L3.
|
||
|
||
Si le L3 est partagé, il n'est cependant pas situé en un seul endroit dans le CPU
|
||
mais est réparti en différentes \ang{slices} : des tranches de mémoire accolées chacune à un coeur.
|
||
Dans le modèle étudié, chaque coeur a exactement une \ang{slice}.
|
||
|
||
\begin{figure}[ht]
|
||
\centering
|
||
\includegraphics[width=0.4\textwidth]{broadwell-die-shot}
|
||
\caption{Broadwell Deca-Core die shot by Intel - annotated by Wikichip~\cite{broadwell-wikichip}}
|
||
% L'espace vide l'est bien sur le die-shot, donc sûrement "Intel being stupid"
|
||
\end{figure}
|
||
|
||
Lorsqu'un coeur accède à une donnée qui n'est pas encore dans son cache, c'est toute la ligne de mémoire:
|
||
les 64 octets environnants (généralement) qui sont chargés dans son L1 ou L2. Comme le L3 est inclusif,
|
||
la ligne y est chargée également. Une fonction de hachage non-documentée attribue à chaque adresse
|
||
physique une unique \ang{slice} dans laquelle elle peut être mise en cache.
|
||
Différents travaux~\cite{slice-reverse, practicalTiming} ont permis de déterminer cette fonction.
|
||
|
||
\label{poweroftwo} Si les fonctions initialement utilisées
|
||
étaient simplement basés sur certains bits de l'adresse,
|
||
la fonction de hachage utilisée à partir de la micro-architecture Sandy Bridge
|
||
(et au moins jusqu'à Tiger Lake) utilise le XOR de plusieurs bits de chaque adresse physique
|
||
pour générer chaque bit du numéro de \ang{slice}. Alors, la fonction de hachage est linéaire sur les
|
||
processeurs avec un nombre de coeurs qui est une puissance de 2, mais une composante non-linéaire
|
||
s'ajoute sur les autres processeurs.
|
||
|
||
\subsubsection{\ang{NUMA}}
|
||
|
||
\begin{figure}[ht]
|
||
\centering
|
||
\includegraphics[width=1.0\textwidth]{lstopo}
|
||
\caption{Topologie \ang{NUMA} de la machine \textit{paravance} extraite
|
||
avec l'outil \texttt{lstopo} de \texttt{hwloc}}
|
||
\end{figure}
|
||
|
||
Dans un système à plusieurs \ang{sockets}, chaque \ang{socket} a de la mémoire DRAM à laquelle il
|
||
peut accéder directement. Sur les machines Intel, le \ang{QPI} (\ang{QuickPath Interconnect})
|
||
permet à un processeur d'accéder aux données localisées dans la mémoire rattachée à un autre processeur.
|
||
|
||
Le principe \ang{NUMA} (\ang{Non-uniform memory access}) délimite alors des groupes contenant
|
||
un processeur et les adresses mémoire qui en sont rapprochées et les transmet au système d'exploitation.
|
||
Cela permet à ce dernier d'allouer la mémoire dans les adresses proche du processeurs qui en a besoin.
|
||
|
||
Sur Linux, si beaucoup d'accès mémoire sont réalisés d'un processeur
|
||
à des adresses qui lui sont éloignées, les pages correspondantes sont migrées.
|
||
|
||
\subsection{Protocoles de cohérence de cache}
|
||
|
||
Dans des systèmes à plusieurs coeurs, d'autant plus avec plusieurs processeurs, où chaque coeur a
|
||
un cache qui lui est propre, un problème de cohérence apparaît. Comment s'assurer qu'une ligne de données
|
||
ne soit pas réécrite de différentes manières en plusieurs caches du système ?
|
||
|
||
On peut commencer par définir des états dans lesquels
|
||
sont considérées les lignes de cache :
|
||
\begin{itemize}
|
||
\item \emph{M} Modifié : la ligne est stockée modifiée dans un unique cache
|
||
\item \emph{E} Exclusif : la ligne est stockée intacte dans un unique cache
|
||
\item \emph{S} Partagé : la ligne est stockée intacte dans plusieurs caches
|
||
(plusieurs caches disjoints, des L1 de coeurs différents par exemple)
|
||
\item \emph{I} Invalide : la ligne a été invalidée dans ce cache,
|
||
car modifiée dans un autre cache par exemple
|
||
\end{itemize}
|
||
D'autres sont parfois ajoutés à cette liste comme \ang{Forward}.
|
||
|
||
Une première solution au problème de cohérence de cache,
|
||
dite par annuaire (\ang{directory}), consiste à avoir à côté des différents
|
||
caches un \ang{directory} qui contient pour chaque ligne de mémoire en cache
|
||
son état dans les différents endroits où elle est stockée.
|
||
Lorsqu'une donnée partagée est modifiée, le \ang{directory} est chargé d'envoyer
|
||
aux autres caches une requête invalidant la ligne modifiée
|
||
|
||
Dans l'autre solution principalement utilisée, dite par \ang{snooping},
|
||
chaque cache surveille de son côté les lignes qu'il a en mémoire.
|
||
|
||
Dans le système étudié, la cohérence se gère par \ang{directory} au sein d'une
|
||
même \ang{socket}, mais par \ang{snooping} entre les deux \ang{sockets}.
|
||
|
||
\subsection{Attaques par canaux auxiliaires}
|
||
|
||
\subsubsection{Mémoire partagée}
|
||
|
||
Les systèmes d'exploitation utilisent le principe de mémoire partagée pour
|
||
réduire l'utilisation totale de mémoire physique. C'est-à-dire que différentes
|
||
pages de mémoire virtuelle correspondent à une même page de mémoire physique,
|
||
partagée potentiellement entre plusieurs processus.
|
||
|
||
Par exemple, les bibliothèques utilisées par plusieurs programmes ne sont chargées
|
||
qu'une seule fois en mémoire pour tous les programmes les utilisant.
|
||
|
||
De la même manière, lorsque un processus est dupliqué (via \ang{fork})
|
||
ou lancé deux fois, les données qu'ils ont en commun
|
||
(le code du programme par exemple) sont partagées entre
|
||
les différentes instances du programme.
|
||
|
||
Une autre forme de déduplication consiste à regarder
|
||
les pages de mémoire contenant les mêmes données et à les combiner.
|
||
Cela peut amener différents processus \ang{sandbox}és
|
||
même de machines virtuelles différentes à partager des données en commun.
|
||
|
||
\subsubsection{L'instruction \texttt{clflush}}
|
||
|
||
D'après le manuel Intel~\cite{intel-man-vol1}:
|
||
\begin{displayquote}
|
||
\sffamily \emph{
|
||
CLFLUSH (flush cache line) instruction writes and invalidates the cache line associated
|
||
with a specified linear address. The invalidation is for all levels of the processor’s cache
|
||
hierarchy, and it is broadcast throughout the cache coherency domain.
|
||
}
|
||
\end{displayquote}
|
||
|
||
Lorsque l'instruction \texttt{clflush} est exécutée, l'adresse et la ligne de cache associée sont
|
||
évincées de tous les caches L1, L2 et L3 où elles se trouvaient possiblement, et cela dans tous les
|
||
\ang{sockets} du système. Si des modifications avaient eu lieu,
|
||
les modifications sont réécrites dans la mémoire DRAM.
|
||
|
||
L'instruction \texttt{clflush} est accessible à tout utilisateur non privilégié sur
|
||
les adresses mémoires auxquelles il a accès.
|
||
|
||
\subsubsection{Flush+Reload}
|
||
|
||
Le temps de chargement d'une donnée est largement influencé par sa présence en cache.
|
||
Mesurer le temps de chargement d'une adresse permet donc de déterminer aisément si la ligne de
|
||
cache associée était déjà présente en cache.
|
||
|
||
Flush+Reload~\cite{flushreload} propose donc la méthode suivante:
|
||
|
||
\begin{algorithm}[ht]
|
||
\caption{Flush+Reload}\label{alg:flushreload}
|
||
\KwData{$x$ : addresse à surveiller}
|
||
\KwResult{Y a t-il eu un accès à $x$ ?}
|
||
$clflush(x)$ \Comment*[l]{$x$ n'est plus en cache}
|
||
$sleep(n)$ \Comment*[l]{Si un coeur accède à $x$, $x$ est à nouveau dans le L3}
|
||
$t \gets rdtsc()$\;
|
||
$read(x)$\;
|
||
$total\_time \gets rdtsc() - t$\;
|
||
\end{algorithm}
|
||
|
||
La différence entre le temps de lecture depuis le cache et depuis la DRAM étant conséquent,
|
||
cette méthode permet de déterminer avec un faible taux d'erreur si un accès a été fait à une adresse,
|
||
le grand nombre de \ang{reload} effectués par les \texttt{clflush} et \texttt{read} successifs est
|
||
cependant visible via des compteurs de performance et donc détectable,
|
||
et ne permet pas une haute fréquence d'observation.
|
||
|
||
\subsubsection{Flush+Flush}
|
||
|
||
Le temps d'exécution de l'instruction \texttt{clflush} dépendant de l'état de cohérence de la ligne
|
||
de cache concernée, la connaissance de son temps d'exécution permet de la même manière
|
||
de déterminer dans quel état était la ligne.
|
||
Flush+Flush~\cite{flushflush} propose la méthode suivante :
|
||
|
||
\begin{algorithm}[ht]
|
||
\caption{Flush+Flush}\label{alg:flushflush}
|
||
\KwData{$x$ : addresse à surveiller}
|
||
\KwResult{Y a t-il eu un accès à $x$ ?}
|
||
$clflush(x)$ \Comment*[l]{$x$ est dans l'état $I$}
|
||
$sleep(n)$ \Comment*[l]{$x$ passe dans l'état $E$ si un unique coeur y accède en lecture}
|
||
$t \gets rdtsc()$\;
|
||
$clflush(x)$\;
|
||
$total\_time \gets rdtsc() - t$\;
|
||
\end{algorithm}
|
||
|
||
Les avantages de cette méthode par rapport à Flush+Reload sont multiples :
|
||
\begin{itemize}
|
||
\item Aucun accès mémoire n'est réalisé pour surveiller l'adresse, ce qui rend les méthodes de
|
||
détection qui comptent le nombre de \ang{cache miss} inefficaces.
|
||
\cite{flushflush} propose des solutions de détection alternatives mais montre qu'elles
|
||
auraient toutes un coût bien trop élevé.
|
||
\item Comme aucun accès mémoire n'est réalisé, la vitesse de traitement et le débit
|
||
de données qui peuvent être extraites est bien plus élevé :
|
||
$496$KB/s contre $298$KB/s pour Flush+Reload
|
||
|
||
\item Comme l'opération mesurée agit sur tous les caches du système et pas seulement sur ceux
|
||
utilisés par l'attaquant, Flush+Flush peut opérer dans un système avec une
|
||
hiérarchie de cache non inclusive. Ce qui est le cas des systèmes à deux
|
||
\ang{sockets} notamment, alors que Flush+Reload nécessite de partager un cache en commun
|
||
(le L3 par exemple).
|
||
\end{itemize}
|
||
|
||
\
|
||
|
||
Similairement à ces autres méthodes, Flush+Flush peut extraire des données du fonctionnement des
|
||
autres processus en regardant les accès mémoires faits dans les bibliothèques partagées,
|
||
qui occupent les mêmes zones de la mémoire physique pour différents processus.
|
||
|
||
Daniel Gruss et al.~\cite{cachetemplateattacks} proposent par exemple de récupérer
|
||
le nonce d'une clé OpenSSL avec Flush+Reload
|
||
en regardant les zones mémoire accédées pendant le chiffrement de données.
|
||
Un enregistreur de frappe (\ang{keylogger}) basé sur
|
||
les pages accédées dans la librairie \textsc{Gtk} \texttt{libgdk.so} y est également mis en oeuvre.
|
||
|
||
\
|
||
|
||
Là où Flush+Reload choisit de mesurer le temps pour charger à nouveau une adresse en mémoire,
|
||
Flush+Flush choisit de mesurer le temps nécessaire pour l'évincer : la différence entre
|
||
un \ang{cache hit} et un \ang{cache miss} est alors beaucoup moins perceptible (moins de 12 cycles de CPU).
|
||
|
||
De bons résultats~\cite{flushflush} ont toutefois été obtenus en appliquant un seuil global.
|
||
Mais la connaissance des coeurs attaquant et victime et de la \ang{slice} de la ligne surveillée
|
||
permettent potentiellement une meilleure précision.
|
||
|
||
\label{nonprivilegedinfo} Dans un scénario réel,
|
||
l'attaquant peut choisir le coeur sur lequel il s'exécute,
|
||
a accès au coeur sur lequel le processus victime s'exécute via \texttt{/proc/pid}.
|
||
La \ang{slice} peut-être trouvée en utilisant l'adresse physique mais cette information demande
|
||
généralement des privilèges qu'un attaquant n'aura pas. En revanche, si la fonction de hachage
|
||
est linéaire -- ce qui est le cas si le nombre de coeurs
|
||
est une puissance de 2 (\ref{poweroftwo}) --
|
||
il est possible de définir une classe d'équivalence des adresses des pages qui appartiennent aux
|
||
mêmes \ang{slices}. Une connaissance fine d'éléments dépendants du numéro de \ang{slice} -- comme
|
||
le temps d'exécution de \texttt{clflush} dans certaines conditions -- permettrait alors
|
||
de déterminer la \ang{slice}.
|
||
|
||
\section{Motivation}
|
||
|
||
Guillaume \textsc{Didier} et Clémentine
|
||
\textsc{Maurice}~\cite{calibrationdoneright} proposent une rétro-ingénierie des
|
||
messages échangés en fonction de l'état de cohérence des lignes
|
||
évincées du cache. Cela passe par l'étude des sources de variabilité et permet de
|
||
mieux choisir un seuil propre à chaque combinaison attaquant/victime/\ang{slice}.
|
||
|
||
Ce travail s'était intéressé à certains processeurs Intel de micro-architectures
|
||
\ang{Coffee Lake} et
|
||
\ang{Haswell} à une seule \ang{socket}, mais a révélé que les résultats seraient bien plus complexes
|
||
sur des systèmes à plusieurs \ang{sockets}.
|
||
|
||
\section{Organisation des expériences}
|
||
|
||
Les expériences ont été faites sur 6 machines différentes,
|
||
via la plateforme \href{https://www.grid5000.fr/}{Grid'5000} afin d'échantillonner au moins une machine
|
||
par processeur répondant aux critères suivants:
|
||
\begin{itemize}
|
||
\item Processeur Intel ;
|
||
\item Exactement 2 \ang{socket} ;
|
||
\item Nombre de coeurs par \ang{socket} est une puissance de deux
|
||
\item Micro-architecture antérieure à SkyLake\footnote{Le L3 n'est plus inclusif
|
||
sur les processeurs serveur à partir de SkyLake et la topologie devient plus complexe}
|
||
\end{itemize}
|
||
|
||
Les machines suivantes ont donc été utilisées~\cite{g5k-nodes}
|
||
|
||
\begin{center}
|
||
\begin{tabular}{|c||c|c|c|}
|
||
\hline
|
||
Nom & Nombre de coeurs & Processeur & Micro-architecture \\
|
||
\hline
|
||
rennes/roazhon11 & 16 & Xeon E5-2660 & Sandy Bridge \\
|
||
rennes/roazhon12 & 16 & Xeon E5-2660 & Sandy Bridge \\
|
||
rennes/parasilo & 16 & Xeon E5-2630 v3 & Haswell \\
|
||
rennes/paravance & 16 & Xeon E5-2630 v3 & Haswell \\
|
||
lyon/nova & 16 & Xeon E5-2620 v4 & Broadwell \\
|
||
rennes/abacus2 & 16 & Xeon E5-2609 v4 & Broadwell \\
|
||
\hline
|
||
\end{tabular}
|
||
\end{center}
|
||
|
||
La prise de mesure consistait à relever pour un lot d'adresses
|
||
(chacune correspondant à une \ang{slice})
|
||
les temps d'exécution de l'instruction \texttt{clflush} pour toutes les paires de coeurs
|
||
et tous les états de cohérence possibles.
|
||
|
||
Initialement, le turbo était désactivé sur toutes les machines, la fréquence des coeurs était fixée
|
||
à la fréquence maximale en activant le mode "performance" via \texttt{cpufreq}.
|
||
|
||
Nous avons trouvé que:
|
||
\begin{itemize}
|
||
\item Contrairement aux machine \ang{Intel Core}, le mode performance ne permet pas
|
||
de fixer la fréquence de tous les coeurs à la fréquence maximale possible.
|
||
Nous avons donc fixé la fréquence des coeurs à la fréquence minimale (généralement 400MHz) ;
|
||
\item L'architecture \ang{NUMA} impliquant de déplacer les pages exploitées principalement par un
|
||
processeur distant, les adresses physiques changeaient durant l'expérience, ce qui rendait
|
||
tous les résultats complètement incohérents.
|
||
Nous avons donc désactivé le \ang{NUMA balancing} via \texttt{numactl},
|
||
ce qui peut se faire pour le processus courant sans privilèges\footnote{
|
||
Il faudrait vérifier que cela fonctionne aussi pour les pages
|
||
partagées comme les bibliothèques
|
||
}.
|
||
\item Nous avons également fixé la fréquence de l'\ang{uncore} via un MSR, mais cela n'a
|
||
pas eu de grand impact sur la clarté des résultats.
|
||
\end{itemize}
|
||
|
||
Les fichiers de résultats bruts sont accessibles en ligne~\cite{g5k-results}.
|
||
|
||
\section{Analyse des résultats}
|
||
|
||
\begin{figure}[ht]
|
||
\centering
|
||
\includegraphics[width=0.5\textwidth]{low-core-count}
|
||
\caption{Topologie LCC Haswell EP d'après~\cite{tuningXeon}}
|
||
\end{figure}
|
||
|
||
Les schémas de présentation d'Intel suggèrent une topologie en anneau, avec un CPU divisé en deux grandes parties.
|
||
|
||
\subsection{Questions génériques}
|
||
|
||
\subsubsection{Numérotation des coeurs}
|
||
|
||
Les premiers tests semblent révéler une numérotation
|
||
suivant ce schéma\footnote{Il est possible que la numérotation soit en fait tournée à 180°. Comme nous
|
||
avons appliqué la même rotation aux \ang{slice}, cela n'affecterait que les positions respectives
|
||
du QPI et du \ang{Home Agent}}:
|
||
|
||
\begin{center}
|
||
\begin{tabular}{|c|c|}
|
||
\hline
|
||
0 & 1 \\ \hline
|
||
2 & 3 \\ \hline
|
||
4 & 5 \\ \hline
|
||
6 & 7 \\ \hline
|
||
\end{tabular}
|
||
\end{center}
|
||
|
||
Pour simplifier les interprétations, nous les avons renumérotés de la façon suivante,
|
||
c'est la numérotation que nous utiliserons pour la suite:
|
||
|
||
\begin{center}
|
||
\begin{tabular}{|c|c|}
|
||
\hline
|
||
0 & 7 \\ \hline
|
||
1 & 6 \\ \hline
|
||
2 & 5 \\ \hline
|
||
3 & 4 \\ \hline
|
||
\end{tabular}
|
||
\end{center}
|
||
|
||
|
||
Pour trouver la numérotation des \ang{slices}, deux méthodes sont possibles:
|
||
\begin{itemize}
|
||
\item Utiliser la méthode proposée pour un attaquant non-privilégié (\ref{nonprivilegedinfo}).
|
||
Cela nécessite une connaissance préalable de la topologie ;
|
||
\item Utiliser les compteurs de performance pour déterminer la \ang{slice} d'une adresse.
|
||
Cela nécessite
|
||
de lire les MSR correspondants donc d'avoir un accès \ang{root}. Nous avons suivi cette méthode
|
||
afin d'être sûr que les numéros de \ang{slice} correspondent aux numéros de coeurs.
|
||
\end{itemize}
|
||
|
||
\subsubsection{Coeurs fantômes}
|
||
|
||
Pour répondre à des demandes variées, \ang{Intel} base ses processeurs serveurs sur
|
||
trois tailles à compter de la micro-architecture Haswell:
|
||
\begin{itemize}
|
||
\item LCC (\ang{Low Core Count}) : de 4 à 8 coeurs physiques pour
|
||
la micro-architecture Haswell (jusqu'à 10 pour Broadwell)
|
||
\item MCC (\ang{Medium Core Count}) : de 10 à 12 coeurs pour Haswell (12 à 14 pour Broadwell)
|
||
\item HCC (\ang{High Core Count}) : de 14 à 18 coeurs pour Haswell (16 à 24 pour Broadwell)
|
||
\end{itemize}
|
||
|
||
Les machines que nous utiliserons sont uniquement basées sur la \ang{die} LCC, mais certaines
|
||
(les Broadwell à 8 coeurs par exemple) sont alors livrées avec des coeurs désactivés. La \ang{slice}
|
||
de cache L3 associée est alors désactivée également. Cela pose tout de même question sur la possibilité
|
||
de sauts supplémentaires dûs à ces coeurs manquants: quel est le coeur désactivé ? Est-ce le même
|
||
sur tous les processeurs du même modèle ? Quel est alors l'impact sur le temps d'exécution ?
|
||
Par la suite, nous avons étudié principalement les résultats de la machine \textit{paravance}
|
||
pour éviter ces questions et établir plus facilement un premier modèle.
|
||
|
||
\subsection{Topologie \ang{miss}}
|
||
|
||
\begin{figure}[ht]
|
||
\centering
|
||
\includegraphics[width=1\textwidth]{predicted-miss}
|
||
\caption{durée prédite et réelle d'un cache \ang{miss} selon
|
||
les différents paramètres (\textit{paravance})}
|
||
\end{figure}
|
||
|
||
|
||
Les résultats obtenus quand le \ang{socket} attaquant et victime diffèrent
|
||
suggèrent l'échange des messages suivants lors d'un \texttt{clflush} qui provoque un \ang{cache miss}:
|
||
\begin{enumerate}
|
||
\item Le coeur attaquant contacte la \ang{slice} locale suivant \ref{figs:topology-miss}.
|
||
\item La \ang{slice} locale contacte la \ang{slice} distante en passant par le QPI.
|
||
Le trajet de la \ang{slice} locale au QPI se fait dans le sens horaire,
|
||
celui du QPI à la \ang{slice} distante dans le sens anti-horaire.
|
||
\item Si le \ang{Home Agent} distant doit être contacté, cela se fait à ce moment, en faisant
|
||
un tour complet de la \ang{socket} pour revenir à la \ang{slice} distante.
|
||
Nous n'avons effectivement pas trouvé d'élément permettant de montrer un trajet différent.
|
||
\item Le chemin est parcouru à l'envers pour repasser par la \ang{slice} locale jusqu'au coeur attaquant
|
||
\end{enumerate}
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.7\textwidth]{pattern}
|
||
\caption{trajet coeur attaquant $\rightarrow$ \ang{slice} locale}
|
||
\label{figs:topology-miss}
|
||
\end{figure}
|
||
|
||
Ainsi, le chemin privilégié pour le trajet coeur attaquant $\rightarrow$ \ang{slice} locale
|
||
serait assez proche du plus court, mais tenterait de limiter les passages
|
||
par le \ang{Home Agent} possiblement pour éviter de ralentir les opérations qui ont besoin de
|
||
passer par lui.
|
||
|
||
\subsection{Topologie \ang{hit} exclusif}
|
||
|
||
On peut diviser le \ang{hit} en deux grands cas :
|
||
\begin{itemize}
|
||
\item L'attaquant et la victime sont dans la même \ang{socket}
|
||
\item L'attaquant et la victime ne sont pas dans la même \ang{socket}
|
||
\end{itemize}
|
||
|
||
Initialement, le trajet de l'information est le même que dans le cas d'un \ang{miss},
|
||
et ce n'est qu'au moment où le message parvient au niveau de la \ang{slice} contenant
|
||
la ligne en cache que les choses changent. La \ang{slice} contacte alors le coeur victime,
|
||
et le message fait le chemin retour une fois la ligne de cache évincée.
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.4\textwidth]{miss-v-hit}
|
||
\caption{Nombre de cache \ang{hit} et cache \ang{miss} selon
|
||
la durée d'exécution de l'instruction \texttt{clflush}}
|
||
\end{figure}
|
||
|
||
Alors, on note une différence de coût significative entre un \ang{hit} local et un \ang{hit}
|
||
distant, dû au passage par le \ang{QPI}. Dans le cas où l'attaquant et la victime partagent la
|
||
même \ang{socket}, le coût d'un \ang{hit} devient alors généralement beaucoup
|
||
moins élevé que celui d'un \ang{miss} ; contrairement au comportement sur
|
||
des systèmes à une seule \ang{socket}.
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.79\textwidth]{predicted-remote-hit}
|
||
\caption{durée prédite et réelle d'un cache \ang{hit} selon les différents paramètres
|
||
(\textit{paravance})}
|
||
\end{figure}
|
||
|
||
Si ce modèle est assez proche des données, on comprend que celui-ci est loin d'être parfait.
|
||
La majeure difficulté dans l'établissement d'un modèle dans le cas d'un \ang{cache hit}
|
||
est la grande quantité de données : on ajoute une dépendance en une nouvelle variable, le
|
||
coeur victime, qui n'était pas prise en compte pour un \ang{miss}.
|
||
De plus, les différents facteurs sont ici difficiles à décorréler,
|
||
ce qui a rendu l'analyse plus chronophage.
|
||
|
||
\section{Conclusion}
|
||
|
||
Si nous n'avons pas pu trouver un modèle parfaitement cohérents
|
||
avec les résultats analysés pour une question de temps,
|
||
nous avons désormais une méthode pour prendre les mesures,
|
||
tout en maîtrisant les principales sources de bruit.
|
||
|
||
Plusieurs pistes se proposent alors pour continuer le travail présenté ici :
|
||
\begin{itemize}
|
||
\item Affiner le modèle, qui n'est pas parfait sur les \ang{cache miss} et ne colle pas
|
||
bien aux \ang{cache hit}. On pourrait ensuite voir comment celui-ci s'étend aux autres
|
||
états de cohérrence de cache ;
|
||
\item Clarifier les hypothèses avancées dans ce rapport en effectuant certaines vérifications :
|
||
\texttt{numactl} permet-il bien de verrouiller les pages partagées ? Peut-on proposer une
|
||
renumérotation automatique des slices ? Cela permettra de créer un modèle crédible d'attaquant ;
|
||
\item Proposer une méthode de réassignation automatique des slices pour savoir depuis un
|
||
utilisateur non privilégié la \ang{slice} d'une adresse virtuelle ;
|
||
\item Réaliser l'attaque Flush+Flush sur des systèmes à 2 \ang{sockets}
|
||
\end{itemize}
|
||
|
||
%\TODO{Extension au Xeon SP, L3 non inclusif+topologie différente
|
||
|
||
%Extension aux nouveaux proc avec slice != 1/coeur}
|
||
|
||
|
||
\newpage{}
|
||
%\nocite{*}
|
||
\bibliographystyle{plain}
|
||
\bibliography{refs}
|
||
|
||
|
||
\newpage{}
|
||
\section*{Annexe}
|
||
|
||
\subsection*{Contexte du stage}
|
||
|
||
Ce stage a été effectué dans l'université de \textsc{Rennes} 1,
|
||
dans le laboratoire de l'\textsc{Irisa} (Institut de Recherche en Informatique
|
||
et Systèmes Aléatoires), l'un des plus grands laboratoires de recherche français dans le domaine
|
||
de l'informatique avec plus de 850 personnes.
|
||
|
||
Il accueille 7 départements et une quarantaine d'équipes, j'ai été encadré par deux chercheurs de
|
||
l'équipe \textsc{Taran} (Architectures matérielles spécialisées pour l’ère post loi-de-Moore ) : Guillaume \textsc{Didier} et Angeliki \textsc{Kritikakou}.
|
||
En discutant avec les autres membres de l'équipe, j'ai pu découvrir de loin des problématiques
|
||
liées aux FPGA, l'ISA RISC-V et l'injection de fautes notamment.
|
||
|
||
J'ai beaucoup apprécié l'ambiance générale et tout ce que j'ai dû comprendre pour avancer sur
|
||
ce sujet de recherche, mais j'ai regretté qu'il soit trop spécifique pour pouvoir demander des conseils
|
||
facilement autour d'une pause café.
|
||
|
||
\subsection*{Contributions de code}
|
||
|
||
Le code utilisé pour prendre les mesures a principalement été
|
||
réutilisé de travaux existants~\cite{dendrobates}.
|
||
J'y ai contribué en résolvant quelques problèmes liés aux machines utilisées, et en ajoutant un
|
||
exécutable limitant la fréquence de l'\ang{uncore}.
|
||
|
||
Le code que j'ai écrit ou auquel j'ai apporté des modifications consiste principalement en:
|
||
|
||
\begin{itemize}
|
||
\item Toute la logique d'analyse des résultats, afin d'afficher les données qui m'intéressaient et de comprendre les relations. Il est disponible dans~\cite{dendrobates} \texttt{cache\_utils/*.py}
|
||
\item Un environnement et un script pour déployer l'expérience automatiquement sur des noeuds de Grid'5000~\cite{g5k-results}
|
||
\end{itemize}
|
||
|
||
\subsection*{Remerciements}
|
||
|
||
Je remercie toute l'équipe \textsc{Taran}, Guillaume \textsc{Didier} et Angeliki
|
||
\textsc{Kritikakou} tout particulièrement pour leur accueil chaleureux au sein du
|
||
laboratoire de l'\textsc{Irisa}.
|
||
Je suis très reconnaissant pour toutes les connaissances et l'expérience acquises durant ce stage.
|
||
|
||
\
|
||
|
||
\textbf{Aknowledgements} Experiments presented in this paper were carried out using the Grid'5000 testbed,
|
||
supported by a scientific interest group hosted by Inria and including \textsc{Cnrs},
|
||
\textsc{Renater} and several Universities as well as other organizations (see \url{https://www.grid5000.fr} ).
|
||
|
||
\end{document}
|