Est-il possible de lire la mémoire d'un autre programme en allouant tout l'espace vide sur un système?

26

Théoriquement, si je construisais un programme qui allouait toute la mémoire inutilisée sur un système et continuait à demander de plus en plus de mémoire alors que d'autres applications libéraient de la mémoire dont elles n'avaient plus besoin, serait-il possible de lire la mémoire récemment libérée d'une autre application ? Ou est-ce en quelque sorte protégé par un système d'exploitation moderne?

Je n'ai aucune application pratique pour cela, je suis juste curieux. Je me rends compte qu'il y a des problèmes avec l'allocation de "toute la mémoire disponible" dans la vie réelle.

Edit: Pour clarifier, je pose des questions spécifiques sur la mémoire «libérée», et non sur l'accès à la mémoire actuellement allouée par une autre application.

ConditionRacer
la source

Réponses:

23

Non, car un bon noyau efface le contenu de la mémoire avant de l'envoyer à un processus de protection contre exactement le type d'attaque que vous proposez.

Sur les systèmes Unixy, la mémoire est allouée aux processus en étendant ce qu'on appelle l' interruption du programme , qui est la limite de l'espace virtuellement adressable qu'un processus peut utiliser. Un processus indique au noyau qu'il souhaite étendre son espace adressable, et le noyau l'autorisera si la mémoire est disponible ou l'appel échouera dans le cas contraire. (Le nom de l' brk()appel système provient de ce concept.)

Dans la pratique, les gros blocs de mémoire libérée ne se heurtent pas souvent à l'interruption du programme, ce qui serait nécessaire pour qu'un processus retourne de la mémoire au noyau en réduisant l'interruption du programme. Tout dépend, bien entendu, de la mise en œuvre de malloc()et de votre système free(). Si vous avez des sources disponibles, elles vous diront si la mémoire est retournée ou non.

Il n'y a pas d'implications de sécurité pour malloc()ne pas initialiser la mémoire car tout ce qu'il a obtenu via brk()a été nettoyé et tout ce qui était précédemment free()d a été écrit par le même processus.

Blrfl
la source
19

Oui, il est théoriquement possible de lire la mémoire libérée d'un autre processus. C'était la source d'un certain nombre d'attaques par élévation de privilèges à l'époque. De ce fait, les systèmes d'exploitation de nos jours mettent à zéro la mémoire si elle était précédemment allouée par un autre processus. La raison pour laquelle vous ne voyez pas toujours la mémoire mise à zéro est qu'il est plus efficace de ne pas mettre à zéro la mémoire si elle était précédemment allouée par le même processus. Le système d'exploitation essaie de restituer des pages de mémoire au même processus s'il le peut.

Karl Bielefeldt
la source
1
"Oui mais non" est "non". @Blrfl a raison.
Ross Patterson
4
@ RossPatterson: Sur le point théorique, Karl a en fait plus raison que moi. La réalité pratique est que les systèmes d'exploitation traditionnels ont fermé ce trou il y a des années.
Blrfl
@Blrfl Compris. Mais «il y a des années», c'était à la fin des années 1960, lorsque les systèmes de pagination et la mémoire virtuelle ont été introduits pour la première fois. Certainement au moment de Multics, VM / 370 et OS / VS. En l'absence de bogues, cela n'a pas été possible dans la mémoire de la plupart des programmeurs pratiquants.
Ross Patterson
The reason you don't always see zeroed out memory is because it is more efficient not to zero out the memory if it was previously allocated by the same process Je vois une certaine incohérence ici. Voulez-vous dire "même exécutable"? Comment vérifie-t-on s'il ne faut pas mettre à zéro - par le chemin du disque?
jakub.g
1
Je pense que je manque quelque chose. Pourquoi alors, quand je compile et exécute un programme C ++ avec, disons, des entiers non initialisés, ils ne sont pas égaux à 0 lorsque je lis ces variables?
jakub.g
2

Plusieurs couches impliquées ici affectent la réponse.

Si vous supposez un système d'exploitation de mémoire virtuelle moderne, vous ne pourrez pas voir les restes des données d'un autre processus dans les pages que vous allouez.

Lorsqu'un processus est chargé pour la première fois, la table de pages est chargée et potentiellement des cadres de mémoire réelle sont alloués à ces pages. Au minimum, la table de pages ou sa table supplémentaire contiendra une carte de toute la mémoire que le processus peut allouer. C'est également là que la pause initiale du processus, mentionnée ci-dessus, est définie.

Alors que malloc () peut, si le processus est autorisé, entraîner une modification de la pause du processus, en ajoutant plus de pages à une table de page de processus (page supplémentaire) pour satisfaire la demande, l'endroit où un processus peut "obtenir un autre" données de processus est à la couche de mémoire réelle inférieure.

Dans ces deux scénarios, un système d'exploitation moderne qui utilise la pagination de la demande ou l'allocation différée n'alloue pas encore de mémoire physique (trames). Le système d'exploitation ne fait que "prendre des notes" sur la mémoire virtuelle de ce processus considérée comme valide. La mémoire réelle n'est attribuée qu'en cas de besoin.

La mémoire physique ou les trames sont allouées à un processus lorsque la page virtuelle est réalisée et mappée dans une table de pages de processus C'est là que le potentiel d'exposition des données existe. Cela se produit lors d'une erreur de page. L'exposition est due au fait qu'un processus précédent utilisait peut-être cette même trame et que ses données ont été abandonnées ou échangées, pour faire de la place pour la demande de mémoire physique actuelle. Le système d'exploitation doit veiller à ce que les données du processus demandeur soient correctement échangées ou que la trame soit effacée (mise à zéro) avant de reprendre le processus. Ceci est également mentionné ci-dessus comme un problème "ancien mais résolu".

Cela rend quelque peu inutile si la mémoire des autres processus a été «libérée» ou non. Un autre processus "libéré" de la mémoire réside toujours dans les pages affectées à ce processus et ne sont généralement pas mappés jusqu'à la fin du processus car ils seront simplement échangés lorsque la mémoire sera faible ou qu'ils seront autrement expulsés. malloc () et free () gèrent la mémoire virtuelle affectée au processus au niveau (utilisateur).

Dans votre question, votre processus, continue de demander de plus en plus de mémoire, en théorie, poussant tous les autres processus hors de la mémoire. En réalité, il existe des stratégies d'allocation de trames - mondiales et locales - qui peuvent également affecter la réponse. Il est tout aussi probable que le processus force ses propres pages à manquer de mémoire avant de pouvoir dépasser le système d'exploitation et tous les autres processus. Bien que cela dépasse votre question initiale.

Tout cela est théorique dans un système comme MS-DOS. MS-DOS (et d'autres systèmes plus simples) n'utilisent pas de mémoire virtuelle (par eux-mêmes) et vous pouvez facilement piquer et produire d'autres données de "processus".

Quelques bonnes références, qui peuvent être plus faciles à comprendre que le code source Linux, seraient un bon manuel de systèmes d'exploitation, Operating Systems Concepts par Silberscatz, Gavin et Gange, ou Operating Systems Design par Andrew Tanenbaum. Quelque chose comme Nachos de Berkeley ou Pintos de Stanford sont de petits systèmes d'exploitation conçus pour l'apprentissage et qui contiennent ces mêmes idées.

0xACE
la source
0

J'ai essayé cela sur Ubuntu il y a 16.04 mois. Tout comme 0xACE l'a dit, le système d'exploitation moderne alloue une page virtuelle entièrement nulle une fois que vous avez appelé malloc (). Mais, si vous n'écrivez rien dans le tampon alloué, il ne sera pas mappé dans la mémoire physique (c'est-à-dire le principe de copie sur écriture), ainsi vous lirez toujours les zéros d'un bloc "non initialisé". Il existe peut-être des systèmes d'exploitation intégrés compilés avec l'option "CONFIG_MMAP_ALLOW_UNITIALIZED" pour de meilleures performances, dans ce cas, vous pourriez obtenir ce que vous avez prévu.

weir007
la source
-1

Non, cela ne permettra pas à un autre programme de lire la mémoire d'un autre grâce à la magie de la pagination . De cette façon, l'utilisation totale de la mémoire peut dépasser le ram physique en déchargeant des parties de celui-ci sur le disque dur.

De plus, la mémoire maximale qu'un processus peut allouer est arbitrairement limitée par le système d'exploitation (jusqu'à 4 concerts pour une architecture 32 bits), après quoi l' allocappel suivant renverra une erreur de mémoire insuffisante.

monstre à cliquet
la source
N'y a-t-il pas des API spécifiques à la plate-forme qui peuvent contourner cela? Honnêtement, je ne sais pas, mais je ne serais pas surpris (par exemple, Linux permet d'empêcher le système d'exploitation de déplacer une page hors de la mémoire physique, via mlock).
S'il y a 4 Go de RAM et que la pagination est limitée à 8 Go, que faire si l'application demande 12 Go (sur un x64)?
Arseni Mourzenko
alors les appels système devraient renvoyer une erreur quand il reste trop peu de mémoire libre, cela ou l'ordinateur s'arrêtera simplement quand il n'y en aura plus ...
Ratchet Freak
4
Il ne demande pas de lire la mémoire d'un autre, mais plutôt de lire sa mémoire LIBÉRÉE. Cette section de RAM est actuellement gratuite, et ... Je ne pense pas ... que les schémas de pagination mettent à zéro la mémoire après sa libération. Ainsi, le programme allouerait un bloc de mémoire et analyserait les données non initialisées qui sont déjà là.
Philip
@philip correct, je pose une question spécifique sur la mémoire libérée.
ConditionRacer