Add cache coherency
This commit is contained in:
parent
370af6ee59
commit
2f7790f2bc
165
main.tex
165
main.tex
@ -26,7 +26,7 @@
|
||||
Laboratoire \textsc{Irisa}\\
|
||||
Université de \textsc{Rennes} 1}
|
||||
|
||||
\date{3 Juin 2024 - 12 Juillet 2024}
|
||||
\date{3 juin 2024 - 12 juillet 2024}
|
||||
|
||||
% quelques macros
|
||||
\newcommand{\TODO}[1]{{\color{red}#1}}
|
||||
@ -59,10 +59,10 @@ Université de \textsc{Rennes} 1}
|
||||
|
||||
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 une mémoire SRAM, plus petite mais plus rapide. Stocker en cache les éléments accédés
|
||||
le plus fréquemment permet donc de réduire le nomre d'appels à la mémoire et donc le temps d'exécution.
|
||||
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 processeur possède, au premier niveau, un L1i (cache des instructions) et
|
||||
Chaque coeur possède, au premier niveau, un L1i (cache des instructions) et
|
||||
un L1d (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
|
||||
@ -70,12 +70,12 @@ 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}\footnote{\TODO{c'est plus compliqué sur les nouveaux proc}}.
|
||||
é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{broadwelldieshot}}
|
||||
\caption{Broadwell Deca-Core die shot by Intel - annotated by Wikichip~\cite{broadwelldieshot}}
|
||||
% L'espace vide l'est bien sur le die-shot, donc sûrement "Intel being stupid"
|
||||
\end{figure}
|
||||
|
||||
@ -83,25 +83,70 @@ Lorsqu'un coeur accède à une donnée qui n'est pas encore dans son cache, c'es
|
||||
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.
|
||||
Différents travaux~\cite{slice-reverse, practicalTiming} ont permis de déterminer cette fonction.
|
||||
|
||||
\subsection{\TODO{Protocoles de cohérence de cache}}
|
||||
\subsection{Protocoles de cohérence de cache}
|
||||
|
||||
\TODO{
|
||||
\begin{enumerate}
|
||||
\item Problème à résoudre
|
||||
\item Expliquer la solution par "directory"
|
||||
\item Expliquer le snooping
|
||||
\end{enumerate}
|
||||
}
|
||||
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 cache 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}
|
||||
|
||||
\TODO{Expliquer le concept général}
|
||||
\subsubsection{Mémoire partagée}
|
||||
|
||||
\TODO{était bien formulé dans le 2.2 de~\cite{flushflush}
|
||||
donc reprend largement les mêmes idées, ce n'est pas une citation et ces infos
|
||||
ne sont pas non plus particulières à ce papier.
|
||||
Comment dire que ça en est inspiré, faut-il le faire ?}
|
||||
|
||||
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 \TODO{\ang{sandbox}és}
|
||||
ou 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}:
|
||||
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
|
||||
@ -111,7 +156,7 @@ D'après le manuel Intel\cite{intel-man-vol1}:
|
||||
\end{displayquote}
|
||||
|
||||
Lorsque l'instruction \texttt{clflush} est exécutée, l'adresse et la ligne de cache associée sont
|
||||
évincés de tous les caches L1, L2 et L3 où elles se trouvaient possiblement, et cela dans tous les
|
||||
é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.
|
||||
|
||||
@ -120,13 +165,11 @@ les adresses mémoires auxquelles il a accès.
|
||||
|
||||
\subsubsection{Flush+Reload}
|
||||
|
||||
\TODO{2.2 \& 2.3 of \cite{flushflush}}
|
||||
|
||||
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:
|
||||
Flush+Reload~\cite{flushreload} propose donc la méthode suivante:
|
||||
|
||||
\begin{algorithm}[ht]
|
||||
\caption{Flush+Reload}\label{alg:flushreload}
|
||||
@ -150,7 +193,7 @@ et ne permet pas une haute fréquence d'observation.
|
||||
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 :
|
||||
Flush+Flush~\cite{flushflush} propose la méthode suivante :
|
||||
|
||||
\begin{algorithm}[ht]
|
||||
\caption{Flush+Flush}\label{alg:flushflush}
|
||||
@ -173,9 +216,6 @@ Les avantages de cette méthode par rapport à Flush+Reload sont multiples :
|
||||
de données qui peuvent être extraites est bien plus élevé :
|
||||
$496$KB/s contre $298$KB/s pour Flush+Reload
|
||||
|
||||
\TODO{what are packets in \cite{flushflush} ?
|
||||
$\rightarrow$ j'ai eu une réponse il faut que je regarde}
|
||||
|
||||
\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
|
||||
@ -184,10 +224,10 @@ Les avantages de cette méthode par rapport à Flush+Reload sont multiples :
|
||||
\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és,
|
||||
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
|
||||
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
|
||||
@ -198,12 +238,14 @@ les pages accédées dans la librairie \textsc{Gtk} \texttt{libgdk.so} y est ég
|
||||
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.
|
||||
De bons résultats~\cite{flushflush} ont toutefois été obtenus en appliquant un seuil global.
|
||||
|
||||
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}.
|
||||
|
||||
Guillaume \textsc{Didier} et Clémentine \textsc{Maurice}~\cite{calibrationdoneright}
|
||||
proposent une autre approche : comprendre les messages échangés en fonction de l'état de cohérence
|
||||
du cache ainsi que les autres sources de variabilité pour bien étalonner sur
|
||||
l'ensemble des combinaisons pertinentes et choisir les bons seuils en fonction.
|
||||
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}.
|
||||
@ -217,12 +259,12 @@ par processeur répondant aux critères suivants:
|
||||
\item Processeur Intel ;
|
||||
\item Exactement 2 \ang{socket} ;
|
||||
\item Nombre de coeurs par \ang{socket} est une puissance de
|
||||
deux \footnote{\TODO{l'expliquer en amont et le rappeler ici}} ;
|
||||
deux \footnote{\TODO{l'expliquer en amont et le rappeler ici, voir Intro}} ;
|
||||
\item Micro-architecture antérieure à SkyLake\footnote{Le L3 n'est plus inclusif
|
||||
sur les processeurs serveur à partir de SkyLake}
|
||||
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}
|
||||
Les machines suivantes ont donc été utilisées~\cite{g5k-nodes}
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{|c||c|c|c|}
|
||||
@ -246,7 +288,7 @@ Le \ang{NUMA balancing} a également été désactivé (à l'échelle du systèm
|
||||
ce qui peut se faire pour le processus courant sans
|
||||
privilèges\footnote{\TODO{à vérifier ! voir les "À faire"}}.
|
||||
|
||||
Les fichiers de résultats bruts sont accessibles en ligne\cite{g5k-results}.
|
||||
Les fichiers de résultats bruts sont accessibles en ligne~\cite{g5k-results}.
|
||||
|
||||
\section{Analyse des résultats}
|
||||
|
||||
@ -254,7 +296,7 @@ Les fichiers de résultats bruts sont accessibles en ligne\cite{g5k-results}.
|
||||
\begin{figure}[ht]
|
||||
\centering
|
||||
\includegraphics[width=0.5\textwidth]{low-core-count}
|
||||
\caption{Topologie LCC Haswell EP d'après \cite{tuningXeon} \TODO{schéma à déplacer}}
|
||||
\caption{Topologie LCC Haswell EP d'après~\cite{tuningXeon} \TODO{schéma à déplacer}}
|
||||
\end{figure}
|
||||
|
||||
Les schémas de présentation d'Intel suggèrent une topologie en anneau, avec un CPU divisé en deux grandes parties.
|
||||
@ -274,7 +316,7 @@ du QPI et du \ang{Home Agent}}:
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
Pour simplifier les interprétations, nous les avons renumérotés de la sorte,
|
||||
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}
|
||||
@ -290,18 +332,17 @@ c'est la numérotation que nous utiliserons pour la suite:
|
||||
|
||||
Pour trouver la numérotation des \ang{slices}, deux méthodes sont possibles:
|
||||
\begin{itemize}
|
||||
\item Utiliser les compteurs de performance pour déterminer la \ang{slice} d'une adresse. Cela nécessite
|
||||
\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 ;
|
||||
\item Utiliser la fonction de hachage (linéaire)\cite{slice-reverse} pour
|
||||
déterminer la \ang{slice} de chaque adresse. Comme la
|
||||
fonction est appliquée sur l'adresse virtuelle et non pas physique, l'ordre des \ang{slices} n'est pas le
|
||||
bon mais deux adresses sont envoyées dans la même \ang{slice} uniquement si elles le sont réellement.
|
||||
\TODO{clarifier, le côté math est mal expliqué}
|
||||
|
||||
Il serait envisageable de donner une méthode permettant de réordonner automatiquement les \ang{slices},
|
||||
et donc d'exploiter ceci depuis un utilisateur non privilégié
|
||||
\TODO{à réécrire aussi}
|
||||
\item Utiliser la fonction de hachage~\cite{slice-reverse} pour
|
||||
déterminer la \ang{slice} de chaque adresse. Comme la fonction est appliquée sur
|
||||
l'adresse virtuelle et non pas physique, celle-ci est déterminée à permutation
|
||||
près.\footnote{\TODO{référence à l'explication du nb de coeur = puissance de 2 en intro}}
|
||||
Il serait envisageable de donner une méthode permettant de
|
||||
réordonner automatiquement les \ang{slices}, et donc de déterminer l'information de la slice
|
||||
depuis un utilisateur non privilégié.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
@ -309,10 +350,7 @@ Pour trouver la numérotation des \ang{slices}, deux méthodes sont possibles:
|
||||
|
||||
\TODO{graphiques comparaison prédiction/réel}
|
||||
|
||||
\TODO{Je ne sais plus, en enlevant $S_R$,
|
||||
est-ce que le modèle marchait bien avec socket(A) = socket(V) ?}
|
||||
|
||||
Les résultats obtenus quand le \ang{socket} attaquant et victime diffèrent suggèrent l'échange des messages suivant lors d'un \texttt{clflush} qui provoque un \ang{cache miss}:
|
||||
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.
|
||||
@ -344,6 +382,11 @@ passer par lui.
|
||||
\subsection{\TODO{Topologie Hit}}
|
||||
|
||||
|
||||
\section{\TODO{Ouverture}}
|
||||
\TODO{Extension au Xeon SP, L3 non inclusif+topologie différente
|
||||
|
||||
Extension aux nouveaux proc avec slice != 1/coeur}
|
||||
|
||||
\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} ).
|
||||
@ -354,16 +397,22 @@ supported by a scientific interest group hosted by Inria and including \textsc{C
|
||||
|
||||
\section{À faire pour continuer le stage}
|
||||
|
||||
\TODO{rédiger mieux qu'une liste}
|
||||
Plusieurs pistes se proposent pour continuer le travail présenté dans ce rapport :
|
||||
\begin{itemize}
|
||||
\item vérifier que \texttt{numactl} permet de verrouiller les pages "partagées"
|
||||
\item modèle hit : coût des sauts $A \rightarrow S$ cohérent avec $S_L \rightarrow S_R$ ?
|
||||
\item \TODO{en plottant A=S=V pour toutes les valeurs possibles, ça ne montrait pas de preuve
|
||||
du trajet \ang{slice}\_l-\ang{slice}\_r}
|
||||
\item Clarifier les hypothèses avancées dans ce rapport :
|
||||
sur la cohérence des modèles proposés avec la réalité et effectuer certaines vérifications :
|
||||
\texttt{numactl} permet-il bien de verrouiller les pages partagées ? Peut-on proposer Une
|
||||
renumérotation automatique des slices ?
|
||||
\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{Faire un passage sur le code : qu'est-ce que j'ai apporté,
|
||||
qu'est-ce qui était déjà fait. Où le trouver}
|
||||
\TODO{Question à poser pour les systèmes à 10 sockets : si l'on désactive un coeur, la slice correspondante est-elle désactivée ? dans ce cas, qu'en est-il de la fonction de hachage ?}
|
||||
|
||||
|
||||
\TODO{Faire un passage sur le code : ce que j'ai apporté,
|
||||
ce qui était déjà fait. Où le trouver}
|
||||
|
||||
\TODO{Passage sur le labo, l'équipe, l'ambiance}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user