8
votes

Utilisation de la mémoire non allouée sans erreur?

Pourquoi cela fonctionne-t-il?

#include <iostream>
using namespace std;

int main() {
    float* tab = new float[3];

    cout << tab[7] << endl;
    tab[7] = 6.87;
    cout << tab[7] << endl;

    delete[] tab;
}


0 commentaires

5 Réponses :


5
votes

Les deux d'entre eux font accès à des limites d'accès - vous disposez d'un tableau de 3 pointeurs flottants et vous accédez à la 8ème matrice. Ceci est lié à un crash.

Cependant, contrairement à Java ou à d'autres langues gérées, il n'existe aucune vérification des limites explicites pour chaque accès de matrice (puisque le coût de performance de celui-ci est prohibitif). Donc, la seule vérification des limites que vous avez est votre MMU. Si vous avez fini par accéder à la mémoire qui n'appartient pas à votre application, vous vous écraserez. Si vous appuyez sur la mémoire qui n'est pas allouée, mais il arrive toujours de faire partie de votre processus (pourrait être un mot de garde, par exemple), il réussira silencieusement. C'est une grande recette pour les insectes très difficiles à traquer.

La vérification des limites est la clé. Faites-le quand vous le pouvez.


0 commentaires

1
votes

Dans le premier exemple, onglet [2] a une valeur qui pointe vers la mémoire valide. L'onglet [2] +7 n'est pas alloué, mais cela pourrait être. Pas de faute SEG.

Dans la seconde, onglet [7] n'a pas de valeur ... il s'agit de bits aléatoires (éventuellement de zéros ou de 0xDeadbeef ou de toute valeur était la dernière valeur). Cela ne signifie presque certainement pas que la mémoire est valable pour cette application pour accéder. Ainsi: BOOM.


0 commentaires

0
votes

La protection d'accès à la mémoire n'est pas très fin. Lorsque vous allouez une mémoire, vous obtenez une page complète de mémoire allouée à votre programme. Lorsque vous essayez d'accéder à cette mémoire supplémentaire, il risque de réussir, mais vous êtes également susceptible de courir sur une autre mémoire allouée à votre programme.

C'est pourquoi les dépassements de tampon fonctionnent comme une attaque. Dans de nombreux cas, il est prévisible de ce que cette mémoire supplémentaire après l'utilisation de votre matrice est utilisée. Si je peux contrôler ce que vous avez mis là-bas, je peux écraser les données que vous ne voulez pas de m'écrouiller. Si je peux écraser votre pile d'appels, je peux exécuter n'importe quel code que je veux dans votre contexte de processus. S'il s'agit d'un service exécutant en tant qu'utilisateur administrateur, j'ai une escalade locale de privilège. S'il s'agit d'un service sur Internet de quelque sorte, j'ai une attaque d'exécution à distance.

Votre meilleur pari est de travailler avec des structures plus robustes comme STD :: Vector Sauf si vous avez un but spécifique d'utilisation des tableaux. (Et même alors, vous pourrez peut-être Éloignez-vous avec des vecteurs ).


5 commentaires

Même alors STD :: Vecteur uniquement des chèques de débogage des bâtiments de débogage (heureusement!)


Sauf si vous utilisez vecteur :: at () ou travaillez avec des itérateurs plutôt que des index.


En fait, celui-ci est faux un peu aussi. Tout d'abord, il n'y a pas de "pages" dans la norme et toutes les implémentations ne fonctionneront pas comme si vous prétendez qu'ils font à cet égard. Deuxièmement, vous ne pouvez écraser que la pile (sur des systèmes qui en utilisent même une) lorsque la matrice est située dessus. La mémoire allouée à partir du magasin libre n'est pas sur la pile. Le type d'attaque dont vous parlez se produit lorsque vous faites quelque chose comme void f () {char buf [sz]; obtient (buf); } , qui n'est pas le problème de l'OP.


@Noah: Il demande pourquoi cela fonctionne comme ça. Il a évidemment frappé le comportement indéfini et tout ce qui se passe, arrive. Mais dans la plupart (tous) GCC sur Linux ou VC sur des implémentations de Windows, il va s'agir d'attribuer des pages lorsque vous appelez nouveau ().


@Noah: Oui, il ne va pas frapper la pile dans ce cas particulier, mais c'est un résultat possible de sortir des limites dans d'autres cas. Les débordements de tampon dans le tas peuvent être aussi dangereux, car tout à fait à faire est de remplacer un pointeur de fonction unique (ou même un fourble).



9
votes

L'utilisation de la mémoire non allouée entraîne un comportement non défini. Vous ne pouvez avoir aucune attente de ce qui se passera lorsque vous le ferez même sur le même système et le même compilateur, sans parler de différentes combinaisons de matériel et de compilateur.

Le programme pourrait s'écraser immédiatement, cela pourrait fonctionner pendant un moment, puis échouer plus tard, il pourrait même sembler de fonctionner parfaitement.

Accéder à la mémoire que vous ne possédez pas est toujours une erreur de programmation, cependant. Ne pensez pas à l'apparition d'une opération correcte comme «cela fonctionne parfois», pensez-y comme «je suis vraiment malchanceux et mon bogue ne s'affiche pas rapidement».


0 commentaires

7
votes

tandis que les autres réponses, à l'exception de Mark's, ne sont pas faux, ce n'est pas vraiment correct non plus. Ce que vous faites en accédant à des données au-delà de la fin de ce que vous avez explicitement attribué dans votre programme provoque un "comportement indéfini". Il peut faire n'importe quoi, y compris "travail".

La réponse de Steve n'existait pas quand j'ai commencé à écrire ceci.


1 commentaires

+1 La publication simultanée entraîne également un comportement non défini, FYI