Partie 19 - Les small bins - mécanismes de protection (2/2)
Les small bins : mécanismes de protection (2/2)
Protections selon les versions
- 2.3.4 : Safe Unlink : mise en place d’une vérification de l’intégrité des liens lors du retrait d’un bloc libre (unlink). La glibc vérifie alors que les pointeurs du bloc ciblé (
victim) sont cohérents avec ceux de son prédécesseur et de son successeur ; - 2.26 : Contrôle de la taille du bloc
victimà retirer viaprev_sizelors de l’exécution deunlink.
Version 2.3.4 - Safe Unlink
❌ Message d’erreur associé : corrupted double-linked list.
Nous avons déjà vu précédemment que les protections appliquées à la version 2.3.4 datent de … 2004 ! Oui, ça fait déjà pas mal de temps 😅.
Ne pas confondre le Safe Unlink avec le Safe Linking ! Ce sont des protections totalement différentes qui n’agissent pas de la même manière.
Cette protection n’est pas propre aux small bins car elle est également utilisée avec les large bins. La vérification est présente dans la fonction unlink dont le contenu, sous forme de macro, est le suivant :
1
2
3
4
5
6
7
8
9
10
11
/* Take a chunk off a bin list */
#define unlink(P, BK, FD) { \
FD = P->fd; \
BK = P->bk; \
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P); \
else { \
FD->bk = BK; \
BK->fd = FD; \
} \
}
Pest le bloc libre à retirer de la corbeille. Il est aussi souvent appelévictim. Ne me demandez pas d’où ça vient, je n’en ai aucune idée 🙃.
La fonction unlink, nous en avons déjà brièvement parlé. Littéralement “défaire les liens”, cette fonction est appelée lorsqu’un bloc libre doit être retiré d’une corbeille. Sachant qu’en mémoire, les blocs libres dans une corbeille ne sont que des zones mémoire liées ou doublement liées, retirer un bloc revient à supprimer ces liens.
Unlink : retirer un bloc d’une corbeille
Avant de rentrer dans les détails du Safe Unlink, comprenons déjà comment fonctionne unlink lorsqu’un bloc victim est à retirer d’une corbeille. Cela peut arriver lorsque le bloc en question est adapté à une allocation demandée.
unlink n’est exécutée que pour deux types de corbeilles :
- les
small bins; - les
large bins.
Afin de vous éviter une migraine en essayant de comprendre ce vers quoi pointe une expression du genre FD->bk == P->fd->bk, voici un schéma qui met en exergue le fonctionnement de unlink :
Par souci de clarté, certains liens
fdetbkqui ne sont pas nécessaires à la compréhension du schéma ont été omis.
Avec ce schéma, vous ne devriez plus avoir trop de mal à comprendre ce qui est vérifié dans la condition FD->bk != P || BK->fd != P 😉.
Version 2.26 - Contrôle de la taille du bloc victim à retirer
❌ Message d’erreur associé : corrupted size vs. prev_size.
Une nouvelle vérification a été ajoutée dans la macro unlink lors de la version 2.26.
Evolution de la fonction unlink
Dans les premiers temps, unlink était une simple petite macro mais est désormais une fonction à part entière et contient bien plus de vérifications. Voici à quoi elle ressemble dans la version 2.41 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* Take a chunk off a bin list. */
static void
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");
fd->bk = bk;
bk->fd = fd;
if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if (p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr ("corrupted double-linked list (not small)");
if (fd->fd_nextsize == NULL)
{
if (p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}
La vérification qui nous intéresse est dans la première condition if.
Vous remarquerez qu’une troisième vérification est présente dans la fonction
unlink. Toutefois, cette vérification utilise les métadonnéesfd_nextsizeetbk_nextsizeque nous n’avons pas encore vues et qui sont utilisées par leslarge bins🔜.
Détails de la vérification
Je pense qu’avec le niveau de maîtrise du tas que vous avez désormais, il n’y a pas besoin d’épiloguer 😌 : la taille du bloc victim à retirer est comparée avec le champ prev_size du bloc situé immédiatement après victim (en se basant uniquement sur la taille du bloc victim).
