PagedOut Issue 2 : What if - Infinite Malloc

Table des matières :

Cet article a été publié dans PagedOut dans le Tome 2. Ceci est la version web de cet article.

Et si - Nous essayons de malloc infiniment?

Vous êtes-vous déjà demandé ce qui pourrait arriver si vous essayiez d’allouer une mémoire infinie avec malloc en C sous Linux? Découvrons-le.

ATTENTION: Cette expérience peut être dangereuse pour votre système. Exécutez-la uniquement dans une machine virtuelle ou sur un ordinateur dédié aux tests!

Preuve de Concept

Afin d’étudier notre idée, voici une simple boucle infinie while(1), allouant une nouvelle zone mémoire à chaque tour. Il est nécessaire de définir le premier caractère de notre nouvelle zone mémoire allouée, pour être sûr qu’elle est conservée telle quelle et qu’elle est vraiment réservée à notre programme.

#include <stdlib.h>
#include <stdio.h>

//gcc -Wall infmalloc.c -o infmalloc

int main() {
  long long int k = 0;
  while (1) {
    // Allocates new memory
    char * mem = malloc(1000000);
    k += 1;
    // Use the allocated memory
    // to prevent optimization
    // of the page
    mem[0] = '\0';
    printf("\rAllocated %lld", k);
  }
  return 0;
}

Nous pouvons maintenant le compiler et l’exécuter. La première fois que j’ai exécuté ceci, mon ordinateur s’est écrasé. Je l’ai exécuté une deuxième fois avec htop fonctionnant sur la même machine, afin de suivre la quantité de mémoire virtuelle que nous avons pu allouer:

Infinite malloc, allocated virtual memory

Wow, 621 Go de mémoire virtuelle! C’est plus que la somme des capacités de ma RAM et de mon disque dur! Que se passe-t-il?

Que se passe t’il?

Dans un premier temps, nos nouvelles pages de mémoire allouées sont créées directement dans la RAM tant qu’il y a suffisamment d’espace. À un moment donné, nous manquerons d’espace dans la RAM, de sorte que les dernières pages récemment utilisées (algorithme LRU) seront déplacées vers le swap, situé sur le disque dur afin de pouvoir écrire les nouvelles pages allouées dans la RAM. Notre mémoire virtuelle allouée est maintenant plus grande que la RAM, c’est ce qu’on appelle [mémoire overcommit] (https://www.win.tue.nl/~aeb/linux/lk/lk-9.html#ss9.6). Cela pose deux problèmes:

  • Premièrement, notre programme crée des pages à une vitesse extrêmement rapide dans l’espace d’adressage de la mémoire virtuelle.

  • Deuxièmement, écrire quelque chose sur le disque dur est extrêmement lent par rapport à l’écriture dans la RAM. Les nouvelles pages à écrire sur le disque sont placées dans une file d’attente asynchrone en attendant que le disque les écrive.

Voici un schéma de la configuration de blocage:

Blocking configuration

Après quelques secondes, il y a tellement de pages à déplacer sur le disque que le système d’exploitation se fige en attendant que le disque les écrive. Cela crée un déni de service!

Protections

Heureusement, il existe des moyens de prévenir ce type d’attaques/bugs. Vous pouvez utiliser ulimits ou cgroups pour définir la quantité maximale de mémoire virtuelle qu’un processus peut allouer.

Vous pouvez afficher la limite actuellement définie avec ulimit -a (sur la plupart des systèmes, elle est illimitée par défaut).

Vous pouvez définir la quantité maximale de mémoire virtuelle avec ulimit -v. La commande ulimit -v prend une valeur en Kio, tandis que malloc() la prend en octets. Faites attention à ce que vous faites cependant, si vous faites un ulimit -v 1, beaucoup de choses vont casser en raison d’échecs d’allocations de mémoire (comme sudo, ulimit, …)!

Conclusions

Nous avons vu qu’une boucle infinie de «malloc» peut créer un déni de service en gelant l’ordinateur. Afin de protéger un système contre de telles attaques ou bogues de programme, on peut définir la quantité maximale de mémoire virtuelle via ulimit -v VALUE ou cgroups.

References