À quel point est-il dangereux d'accéder à un tableau en dehors des limites?

221

Dans quelle mesure est-il dangereux d'accéder à un tableau en dehors de ses limites (en C)? Il peut parfois arriver que je lise à l'extérieur du tableau (je comprends maintenant que j'accède ensuite à la mémoire utilisée par d'autres parties de mon programme ou même au-delà) ou que j'essaie de définir une valeur pour un index en dehors du tableau. Le programme se bloque parfois, mais parfois il s'exécute, ne donnant que des résultats inattendus.

Maintenant, ce que je voudrais savoir, c'est à quel point est-ce vraiment dangereux? Si cela endommage mon programme, ce n'est pas si mal. Si d'un autre côté, cela casse quelque chose en dehors de mon programme, parce que j'ai réussi à accéder à une mémoire totalement indépendante, alors c'est très mauvais, j'imagine. J'ai lu beaucoup de "tout peut arriver", "la segmentation pourrait être le moindre problème" , "votre disque dur pourrait devenir rose et les licornes pourraient chanter sous votre fenêtre", ce qui est bien, mais quel est vraiment le danger?

Mes questions:

  1. La lecture de valeurs de l'extérieur du tableau peut-elle endommager autre chose que mon programme? J'imagine que le simple fait de regarder les choses ne change rien, ou changerait-il par exemple l'attribut «dernière ouverture» d'un fichier que j'ai atteint?
  2. La définition de valeurs en dehors du tableau peut-elle endommager autre chose que mon programme? De cette question de débordement de pile, je suppose qu'il est possible d'accéder à n'importe quel emplacement de mémoire, qu'il n'y a aucune garantie de sécurité.
  3. J'exécute maintenant mes petits programmes à partir de XCode. Cela fournit-il une protection supplémentaire autour de mon programme lorsqu'il ne peut pas atteindre en dehors de sa propre mémoire? Peut-il nuire à XCode?
  4. Des recommandations sur la façon d'exécuter mon code intrinsèquement buggé en toute sécurité?

J'utilise OSX 10.7, Xcode 4.6.

ChrisD
la source
En général, le système d'exploitation se protégera et protégera les autres processus de vos malversations. Ce n'est pas quelque chose sur lequel vous devez nécessairement compter fortement, cependant.
Hot Licks
7
En outre, vous n'arriverez jamais à atteindre un fichier sur votre disque dur lorsque vous accédez à un index de tableau hors limites (dans votre RAM).
DrummerB
1
je crois que vous posez des questions sur le tableau C, non? donc cela n'a rien à voir avec ObjC et ne se rapporte vraiment à aucun IDE.
Bryan Chen
17
Voici mon exemple préféré de résultats étranges (il traite de la pile, mais je l'ai trouvé vraiment instructif ...).
phipsgabler
11
xkcd.com/371
Dan se

Réponses:

125

En ce qui concerne la norme ISO C (la définition officielle du langage), l'accès à un tableau en dehors de ses limites a " comportement indéfini ". La signification littérale de ceci est:

comportement, lors de l'utilisation d'une construction de programme non portable ou erronée ou de données erronées, pour laquelle la présente Norme internationale n'impose aucune exigence

Une note non normative développe ce point:

Les comportements indéfinis possibles vont de l'ignorance complète de la situation avec des résultats imprévisibles, au comportement pendant la traduction ou l'exécution du programme d'une manière documentée caractéristique de l'environnement (avec ou sans l'émission d'un message de diagnostic), à la fin d'une traduction ou de l'exécution (avec l'émission d'un message de diagnostic).

Voilà donc la théorie. Quelle est la réalité?

Dans le "meilleur" cas, vous accéderez à une partie de la mémoire appartenant à votre programme en cours d'exécution (ce qui pourrait entraîner un mauvais comportement de votre programme), ou ce n'est pas le cas. à votre programme en cours d'exécution (ce qui entraînera probablement votre programme à planter avec quelque chose comme un défaut de segmentation). Ou vous pouvez essayer d'écrire dans la mémoire que votre programme possède, mais qui est marqué en lecture seule; cela entraînera probablement également le plantage de votre programme.

Cela suppose que votre programme s'exécute sous un système d'exploitation qui tente de protéger les processus simultanément. Si votre code s'exécute sur le "bare metal", par exemple s'il fait partie d'un noyau de système d'exploitation ou d'un système embarqué, alors il n'y a pas une telle protection; votre code de mauvaise conduite était censé fournir cette protection. Dans ce cas, les possibilités de dommages sont considérablement plus importantes, y compris, dans certains cas, les dommages physiques au matériel (ou aux choses ou aux personnes à proximité).

Même dans un environnement de système d'exploitation protégé, les protections ne sont pas toujours à 100%. Il existe des bogues du système d'exploitation qui permettent aux programmes non privilégiés d'obtenir un accès root (administratif), par exemple. Même avec des privilèges utilisateur ordinaires, un programme défectueux peut consommer des ressources excessives (CPU, mémoire, disque), entraînant éventuellement la panne de l'ensemble du système. De nombreux logiciels malveillants (virus, etc.) exploitent les dépassements de mémoire tampon pour obtenir un accès non autorisé au système.

(Un exemple historique: j'ai entendu dire que sur certains anciens systèmes avec mémoire de base , l'accès répétitif à un seul emplacement de mémoire dans une boucle étroite pouvait littéralement faire fondre ce morceau de mémoire. D'autres possibilités incluent la destruction d'un écran CRT et le déplacement de la lecture / tête d'écriture d'un lecteur de disque avec la fréquence harmonique de l'armoire du lecteur, ce qui le fait marcher sur une table et tomber sur le sol.)

Et Skynet doit toujours s'inquiéter.

L'essentiel est le suivant: si vous pouviez écrire un programme pour faire quelque chose de mal délibérément , il est au moins théoriquement possible qu'un programme buggy puisse faire la même chose accidentellement .

Dans la pratique, il est très peu probable que votre programme de buggy fonctionnant sur un système MacOS X fasse quelque chose de plus grave qu'un crash. Mais il n'est pas possible d' empêcher complètement le code buggé de faire de très mauvaises choses.

Keith Thompson
la source
1
merci, je comprends parfaitement cela. Mais cela déclenche immédiatement une question de suivi: que peut faire un programmeur débutant pour protéger son ordinateur de ses propres créations peut-être horribles? Après avoir testé un programme à fond, je peux le lancer dans le monde. Mais le premier essai est forcément un programme incorrect. Comment gardez-vous vos systèmes à l'abri de vous-même?
ChrisD
6
@ChrisD: Nous avons tendance à avoir de la chance. 8-)} Sérieusement, la protection au niveau du système d'exploitation est plutôt bonne de nos jours. Le pire des cas, si j'écris une bombe à fourche accidentelle , je devrai peut-être redémarrer pour récupérer. Mais les dommages réels au système ne valent probablement pas la peine d'être inquiétés, tant que votre programme n'essaie pas de faire quelque chose au bord de la dangerosité. Si vous êtes vraiment inquiet, l'exécution du programme sur une machine virtuelle n'est peut-être pas une mauvaise idée.
Keith Thompson
1
D'un autre côté, j'ai vu beaucoup de choses étranges se produire sur les ordinateurs que j'ai utilisés (fichiers corrompus, erreurs système irrécupérables, etc.), et je n'ai aucune idée du nombre d'entre elles qui auraient pu être causées par un programme C présentant le comportement indéfini redouté. (Jusqu'à présent, aucun démon réel n'est sorti de mon nez.)
Keith Thompson
1
merci de m'avoir enseigné les bombes à fourche - j'ai fait des choses proches de cela, en essayant de saisir la récursivité :)
ChrisD
2
scientificamerican.com/article/… donc le feu est toujours possible avec l'électronique moderne.
Mooing Duck
25

En général, les systèmes d'exploitation d'aujourd'hui (les plus populaires de toute façon) exécutent toutes les applications dans les régions de mémoire protégées à l'aide d'un gestionnaire de mémoire virtuelle. Il s'avère qu'il n'est pas terriblement FACILE (disons) de simplement lire ou écrire dans un emplacement qui existe dans de VRAIS espaces en dehors des régions qui ont été attribuées / allouées à votre processus.

Réponses directes:

1) La lecture n'endommagera presque jamais directement un autre processus, mais elle peut indirectement endommager un processus si vous lisez une valeur KEY utilisée pour chiffrer, déchiffrer ou valider un programme / processus. La lecture hors limites peut avoir des effets quelque peu négatifs / inattendus sur votre code si vous prenez des décisions en fonction des données que vous lisez

2) La seule façon dont vous pourriez réellement ENDOMMAGER quelque chose en écrivant dans un emplacement accessible par une adresse mémoire est si cette adresse mémoire sur laquelle vous écrivez est en fait un registre matériel (un emplacement qui n'est en fait pas pour le stockage de données mais pour contrôler une pièce) du matériel) pas un emplacement RAM. En fait, vous n'endommagerez toujours normalement pas quelque chose, sauf si vous écrivez un emplacement programmable unique qui n'est pas réinscriptible (ou quelque chose de cette nature).

3) Généralement exécuté à partir du débogueur exécute le code en mode débogage. L'exécution en mode débogage a tendance à (mais pas toujours) à arrêter votre code plus rapidement lorsque vous avez fait quelque chose de hors d'usage ou carrément illégal.

4) N'utilisez jamais de macros, utilisez des structures de données qui ont déjà des limites d'index de tableau intégrées, etc ...

SUPPLÉMENTAIRE Je dois ajouter que les informations ci-dessus ne concernent vraiment que les systèmes utilisant un système d'exploitation avec des fenêtres de protection de la mémoire. Si vous écrivez du code pour un système embarqué ou même un système utilisant un système d'exploitation (en temps réel ou autre) qui n'a pas de fenêtres de protection de la mémoire (ou des fenêtres adressées virtuelles), il faut faire preuve de beaucoup plus de prudence lors de la lecture et de l'écriture dans la mémoire. Dans ces cas également, des pratiques de codage SAFE et SECURE doivent toujours être utilisées pour éviter les problèmes de sécurité.

trompettes
la source
4
Des pratiques de codage sûres et sécurisées doivent toujours être utilisées.
Nik Bougalis
3
Je suggérerais de ne PAS utiliser try / catch pour le code bogué, sauf si vous interceptez des exceptions très spécifiques et savez comment en récupérer. Catch (...) est la pire chose que vous puissiez ajouter à un code buggy.
Eugene
1
@NikBougalis - Je suis entièrement d'accord, mais c'est ENCORE PLUS IMPORTANT si le système d'exploitation n'inclut pas de protection de la mémoire / des espaces d'adressage virtuels, ou s'il y a un manque de système d'exploitation :-)
trumpetlicks
@Eugene - Je n'ai jamais remarqué que c'était un problème pour moi, mais je suis d'accord avec vous, est-ce que je l'ai édité :-)
trumpetlicks
1) vous voulez dire des dommages parce que je révélerais quelque chose qui aurait dû rester secret? 2) Je ne suis pas sûr de comprendre ce que vous voulez dire, mais je suppose que je n'accède à la RAM qu'en essayant d'accéder à des emplacements en dehors des limites du tableau?
ChrisD
9

Ne pas vérifier les limites peut entraîner des effets secondaires laids, y compris des failles de sécurité. L'une des plus laides est l'exécution de code arbitraire . Dans l'exemple classique: si vous avez un tableau de taille fixe et que vous utilisez strcpy()pour y placer une chaîne fournie par l'utilisateur, l'utilisateur peut vous donner une chaîne qui déborde le tampon et écrase d'autres emplacements de mémoire, y compris l'adresse de code où le CPU doit retourner lorsque votre fonction finitions.

Ce qui signifie que votre utilisateur peut vous envoyer une chaîne qui fera essentiellement appeler votre programme exec("/bin/sh"), ce qui le transformera en shell, exécutant tout ce qu'il veut sur votre système, y compris la récolte de toutes vos données et la transformation de votre machine en nœud de botnet.

Voir Briser la pile pour le plaisir et le profit pour plus de détails sur la façon dont cela peut être fait.

che
la source
Je sais que je ne devrais pas accéder aux éléments du tableau au-delà des limites, merci d'avoir renforcé ce point. Mais la question est, en plus de faire toutes sortes de dommages à mon programme, puis-je par inadvertance aller au-delà de la mémoire de mon programme? Et je veux dire sur OSX.
ChrisD
@ChrisD: OS X est un système d'exploitation moderne, il vous fournira donc une protection complète de la mémoire. Par exemple, vous ne devriez pas être limité à ce que votre programme est autorisé à faire. Cela ne devrait pas inclure de jouer avec d'autres processus (sauf si vous exécutez avec les privilèges root).
che
Je préfère dire sous les privilèges du ring 0, pas ceux de root.
Ruslan
Plus intéressant est que les compilateurs hyper-moderne peut décider que si le code tente de lire à foo[0]travers foo[len-1]après avoir déjà utilisé un chèque de lencontre la longueur du tableau soit exécuter ou sauter un morceau de code, le compilateur doit se sentir libre d'exécuter cette autre code sans condition même si l'application possède le stockage passé le tableau et les effets de la lecture, il aurait été bénin, mais l'effet de l'invocation de l'autre code ne le serait pas.
supercat
8

Vous écrivez:

J'ai lu beaucoup de "tout peut arriver", "la segmentation pourrait être le moindre problème", "votre disque dur pourrait devenir rose et les licornes pourraient chanter sous votre fenêtre", ce qui est bien, mais quel est vraiment le danger?

Disons-le de cette façon: charger un pistolet. Pointez-le à l'extérieur de la fenêtre sans but ni tir particuliers. Quel est le danger?

Le problème est que vous ne savez pas. Si votre code écrase quelque chose qui bloque votre programme, tout va bien car il l'arrêtera dans un état défini. Cependant, s'il ne plante pas, les problèmes commencent à se poser. Quelles ressources sont sous le contrôle de votre programme et que pourrait-il leur apporter? Quelles ressources pourraient être contrôlées par votre programme et que pourraient-elles leur apporter? Je connais au moins un problème majeur causé par un tel débordement. Le problème était dans une fonction de statistiques apparemment dénuée de sens qui a gâché une table de conversion non liée pour une base de données de production. Le résultat a été un nettoyage très coûteux par la suite. En fait, il aurait été beaucoup moins cher et plus facile à gérer si ce problème avait formaté les disques durs ... avec d'autres mots: les licornes roses pourraient être votre moindre problème.

L'idée que votre système d'exploitation vous protégera est optimiste. Si possible, essayez d'éviter d'écrire hors des limites.

Udo Klein
la source
ok, c'est exactement ce dont j'avais peur. Je vais «essayer d'éviter d'écrire hors des limites» mais, vu ce que j'ai fait ces derniers mois, je vais sûrement le faire encore beaucoup. Comment êtes-vous devenu si bon en programmation sans un moyen sûr de pratiquer?
ChrisD
3
Qui a dit que tout était en sécurité;)
Udo Klein
7

Ne pas exécuter votre programme en tant que root ou tout autre utilisateur privilégié ne nuira à aucun de votre système, donc généralement cela peut être une bonne idée.

En écrivant des données dans un emplacement de mémoire aléatoire, vous n'endommagerez directement aucun autre programme en cours d'exécution sur votre ordinateur car chaque processus s'exécute dans son propre espace mémoire.

Si vous essayez d'accéder à une mémoire non allouée à votre processus, le système d'exploitation arrêtera l'exécution de votre programme avec une erreur de segmentation.

Donc, directement (sans exécuter en tant que root et accéder directement à des fichiers comme / dev / mem), il n'y a aucun danger que votre programme interfère avec tout autre programme exécuté sur votre système d'exploitation.

Néanmoins - et c'est probablement ce dont vous avez entendu parler en termes de danger - en écrivant aveuglément des données aléatoires dans des emplacements de mémoire aléatoires par accident, vous pouvez certainement endommager tout ce que vous pouvez endommager.

Par exemple, votre programme peut vouloir supprimer un fichier spécifique donné par un nom de fichier stocké quelque part dans votre programme. Si, par accident, vous écrasez simplement l'emplacement où le nom de fichier est stocké, vous pouvez supprimer un fichier très différent à la place.

mikyra
la source
1
Si vous êtes en cours d' exécution en tant que root (ou un autre utilisateur privilégié), cependant, attention. Les dépassements de mémoire tampon et de baie sont un exploit de malware courant.
John Bode
en fait, le compte que j'utilise pour tous mes calculs quotidiens n'est pas un compte administrateur (j'utilise la terminologie OSX puisque c'est mon système). Voulez-vous me dire que je ne peux pas endommager quelque chose en essayant de définir n'importe quel emplacement de mémoire? Ce sont en fait d'excellentes nouvelles!
ChrisD
Comme déjà mentionné précédemment, le pire dommage que vous pouvez faire par accident est le pire que vous puissiez faire en tant qu'utilisateur. Si vous voulez être sûr à 100% de ne détruire aucune de vos données, vous voudrez probablement ajouter un compte différent à votre ordinateur et expérimenter cela.
mikyra
1
@mikyra: Cela n'est vrai que si les mécanismes de protection du système sont efficaces à 100%. L'existence de logiciels malveillants suggère que vous ne pouvez pas toujours compter sur cela. (Je ne veux pas suggérer que cela vaut nécessairement la peine de s'inquiéter; il est possible, mais peu probable, qu'un programme puisse accidentellement exploiter les mêmes failles de sécurité exploitées par les logiciels malveillants.)
Keith Thompson
1
La liste ici comprend: Exécution de code à partir de sources non fiables. Il suffit de cliquer sur le bouton OK dans une fenêtre contextuelle du pare-feu sans même lire de quoi il s'agit ni de l'éteindre complètement si la connexion réseau souhaitée ne peut pas être établie. Correctif des binaires avec le dernier hack de sources douteuses. Ce n'est pas la faute de la voûte si le propriétaire invitera volontairement tout cambrioleur avec les deux bras et des portes fortifiées extra fortes grandes ouvertes.
mikyra
4

NSArrays dans Objective-C se voient attribuer un bloc de mémoire spécifique. Le dépassement des limites du tableau signifie que vous accéderez à la mémoire qui n'est pas affectée au tableau. Ça signifie:

  1. Cette mémoire peut avoir n'importe quelle valeur. Il n'y a aucun moyen de savoir si les données sont valides en fonction de votre type de données.
  2. Cette mémoire peut contenir des informations sensibles telles que des clés privées ou d'autres informations d'identification d'utilisateur.
  3. L'adresse mémoire peut être invalide ou protégée.
  4. La mémoire peut avoir une valeur changeante car elle est accessible par un autre programme ou thread.
  5. D'autres choses utilisent l'espace d'adressage de la mémoire, comme les ports mappés en mémoire.
  6. L'écriture de données sur une adresse mémoire inconnue peut planter votre programme, écraser l'espace mémoire du système d'exploitation et entraîner généralement l'implosion du soleil.

De l'aspect de votre programme, vous voulez toujours savoir quand votre code dépasse les limites d'un tableau. Cela peut entraîner le renvoi de valeurs inconnues, provoquant le plantage de votre application ou la fourniture de données non valides.

Richard Brown
la source
NSArraysont des exceptions hors limites. Et cette question semble concerner le tableau C.
DrummerB
Je voulais en effet dire des tableaux C. Je sais qu'il y a NSArray, mais pour l'instant la plupart de mes exercices sont en C
ChrisD
4

Vous voudrez peut-être essayer d'utiliser l' memcheckoutil dans Valgrind lorsque vous testez votre code - il ne détectera pas les violations de limites de tableau individuelles dans un cadre de pile, mais il devrait détecter de nombreux autres types de problèmes de mémoire, y compris ceux qui causeraient des subtilités plus larges problèmes hors du cadre d'une seule fonction.

Du manuel:

Memcheck est un détecteur d'erreur de mémoire. Il peut détecter les problèmes suivants, courants dans les programmes C et C ++.

  • Vous ne devez pas accéder à la mémoire, par exemple, en dépassant et en sous-exécutant des blocs de tas, en dépassant le haut de la pile et en accédant à la mémoire après sa libération.
  • Utiliser des valeurs non définies, c'est-à-dire des valeurs qui n'ont pas été initialisées, ou qui ont été dérivées d'autres valeurs non définies.
  • Libération incorrecte de la mémoire de segment de mémoire, telle que la libération de blocs de segment de mémoire double ou une utilisation incorrecte de malloc / new / new [] par rapport à free / delete / delete []
  • Chevauchement des pointeurs src et dst dans memcpy et les fonctions connexes.
  • Fuites de mémoire.

ETA: Cependant, comme le dit la réponse de Kaz, ce n'est pas une panacée et ne donne pas toujours les résultats les plus utiles, surtout lorsque vous utilisez des modèles d'accès passionnants .

Aesin
la source
Je soupçonne que l'analyseur de XCode trouverait la plupart de cela? et ma question n'est pas tant de savoir comment trouver ces bogues, mais si l'exécution d'un programme qui a encore ces bogues est dangereuse pour la mémoire non allouée à mon programme. Je vais devoir exécuter le programme pour voir les bugs se produire
ChrisD
3

Si vous faites de la programmation au niveau des systèmes ou de la programmation de systèmes intégrés, de très mauvaises choses peuvent se produire si vous écrivez dans des emplacements de mémoire aléatoires. Les systèmes plus anciens et de nombreux microcontrôleurs utilisent des E / S mappées en mémoire, de sorte que l'écriture dans un emplacement mémoire mappé sur un registre périphérique peut faire des ravages, surtout si elle est effectuée de manière asynchrone.

Un exemple est la programmation de la mémoire flash. Le mode de programmation sur les puces de mémoire est activé en écrivant une séquence spécifique de valeurs à des emplacements spécifiques à l'intérieur de la plage d'adresses de la puce. Si un autre processus devait écrire dans un autre emplacement de la puce pendant ce temps, cela entraînerait l'échec du cycle de programmation.

Dans certains cas, le matériel encapsulera les adresses (les bits / octets d'adresse les plus importants sont ignorés), donc l'écriture dans une adresse au-delà de la fin de l'espace d'adressage physique entraînera en fait l'écriture des données en plein milieu des choses.

Et enfin, les processeurs plus anciens comme le MC68000 peuvent être verrouillés au point que seule une réinitialisation matérielle peut les remettre en marche. Je n'y ai pas travaillé depuis quelques décennies, mais je pense que c'est quand il a rencontré une erreur de bus (mémoire inexistante) en essayant de gérer une exception, il s'arrêterait simplement jusqu'à ce que la réinitialisation matérielle soit confirmée.

Ma plus grande recommandation est une prise flagrante pour un produit, mais je n'y ai aucun intérêt personnel et je ne suis aucunement affilié à eux - mais basé sur quelques décennies de programmation C et de systèmes embarqués où la fiabilité était critique, le PC de Gimpel Lint ne détectera pas seulement ce genre d'erreurs, il fera de vous un meilleur programmeur C / C ++ en constamment harcelant au sujet des mauvaises habitudes.

Je recommanderais également de lire la norme de codage MISRA C, si vous pouvez attraper une copie de quelqu'un. Je n'en ai pas vu de récents, mais dans le temps, ils m'ont expliqué pourquoi vous devriez / ne devriez pas faire les choses qu'ils couvrent.

Je ne sais pas pour vous, mais à propos de la 2e ou de la 3e fois que je reçois un coredump ou un raccrochage de n'importe quelle application, mon opinion sur la société qui l'a produite diminue de moitié. La 4e ou la 5e fois et quel que soit le paquet devient étagère et je conduis un piquet en bois au centre du paquet / disque dans lequel il est entré juste pour m'assurer qu'il ne revienne jamais me hanter.

Dan Haynes
la source
Selon le système, les lectures hors plage peuvent également déclencher un comportement imprévisible, ou elles peuvent être bénignes, bien qu'un comportement matériel bénin sur des charges hors plage n'implique pas un comportement bénin du compilateur.
supercat
2

Je travaille avec un compilateur pour une puce DSP qui génère délibérément du code qui accède à un après la fin d'un tableau en code C qui ne le fait pas!

Cela est dû au fait que les boucles sont structurées de sorte que la fin d'une itération prélève certaines données pour l'itération suivante. Ainsi, la donnée extraite à la fin de la dernière itération n'est jamais réellement utilisée.

Écrire du code C comme ça invoque un comportement indéfini, mais ce n'est qu'une formalité d'un document de standards qui se préoccupe d'une portabilité maximale.

Plus souvent qu'autrement, un programme qui accède hors des limites n'est pas habilement optimisé. C'est tout simplement buggé. Le code récupère une certaine valeur de déchets et, contrairement aux boucles optimisées du compilateur susmentionné, le code utilise ensuite la valeur dans les calculs suivants, les corrompant ainsi.

Cela vaut la peine d'attraper des bogues comme ça, et il vaut donc la peine de rendre le comportement non défini, même pour cette seule raison: pour que l'exécution puisse produire un message de diagnostic comme "dépassement de la ligne à la ligne 42 de main.c".

Sur les systèmes avec mémoire virtuelle, il se peut qu'un tableau soit alloué de telle sorte que l'adresse qui suit se trouve dans une zone non mappée de mémoire virtuelle. L'accès bombardera alors le programme.

Soit dit en passant, notez qu'en C, nous sommes autorisés à créer un pointeur qui dépasse la fin d'un tableau. Et ce pointeur doit être plus grand que n'importe quel pointeur à l'intérieur d'un tableau. Cela signifie qu'une implémentation C ne peut pas placer un tableau juste à la fin de la mémoire, où l'adresse un plus serait bouclée et aurait l'air plus petite que les autres adresses du tableau.

Néanmoins, l'accès à des valeurs non initialisées ou hors limites est parfois une technique d'optimisation valide, même s'il n'est pas portable au maximum. C'est par exemple pourquoi l'outil Valgrind ne signale pas les accès aux données non initialisées lorsque ces accès se produisent, mais uniquement lorsque la valeur est utilisée ultérieurement d'une manière qui pourrait affecter le résultat du programme. Vous obtenez un diagnostic comme «branche conditionnelle dans xxx: nnn dépend de la valeur non initialisée» et il peut parfois être difficile de localiser son origine. Si tous ces accès étaient immédiatement bloqués, il y aurait beaucoup de faux positifs provenant du code optimisé par le compilateur ainsi que du code correctement optimisé à la main.

En parlant de cela, je travaillais avec un codec d'un fournisseur qui dégageait ces erreurs lorsqu'il était porté sur Linux et exécuté sous Valgrind. Mais le vendeur m'a convaincu que seulement plusieurs bitsde la valeur utilisée provenait en fait de la mémoire non initialisée, et ces bits ont été soigneusement évités par la logique. Seuls les bons bits de la valeur étaient utilisés et Valgrind n'a pas la possibilité de retrouver le bit individuel. Le matériel non initialisé provient de la lecture d'un mot après la fin d'un flux binaire de données codées, mais le code sait combien de bits se trouvent dans le flux et n'utilisera pas plus de bits qu'il n'y en a réellement. Étant donné que l'accès au-delà de la fin de la matrice de flux binaires ne cause aucun dommage à l'architecture DSP (il n'y a pas de mémoire virtuelle après la matrice, pas de ports mappés en mémoire et l'adresse ne se termine pas), c'est une technique d'optimisation valide.

"Comportement indéfini" ne signifie pas vraiment grand-chose, car selon ISO C, simplement inclure un en-tête qui n'est pas défini dans la norme C, ou appeler une fonction qui n'est pas définie dans le programme lui-même ou la norme C, sont des exemples d'indéfini comportement. Un comportement indéfini ne signifie pas "non défini par quiconque sur la planète" simplement "non défini par la norme ISO C". Mais bien sûr, un comportement parfois indéfini n'est vraiment absolument pas défini par quiconque.

Kaz
la source
De plus, à condition qu'il existe au moins un programme qu'une implémentation particulière traite correctement même si elle taxe nominalement toutes les limites d'implémentation données dans la norme, cette implémentation pourrait se comporter de manière arbitraire lorsqu'elle est alimentée par tout autre programme exempt de violations de contraintes et toujours " conforme ". Par conséquent, 99,999% des programmes C (autre chose que le «programme unique» d'une plate-forme) reposent sur des comportements où la norme n'impose aucune exigence.
supercat
1

Outre votre propre programme, je ne pense pas que vous casserez quoi que ce soit, dans le pire des cas, vous essayerez de lire ou d'écrire à partir d'une adresse mémoire qui correspond à une page que le noyau n'a pas assignée à vos processus, générant l'exception appropriée et d'être tué (je veux dire, votre processus).

jbgs
la source
3
..Quoi? Que diriez-vous d'écraser la mémoire dans votre propre processus utilisé pour stocker une variable utilisée plus tard ... qui a maintenant mystérieusement changé sa valeur! Ces bugs sont très amusants à retrouver, je vous assure. Une ségrégation serait le meilleur résultat. -1
Ed S.
2
Je veux dire qu'il ne "cassera" pas d'autres processus, à part son propre programme;)
jbgs
En effet, je m'en fiche si je romps mon propre programme. J'apprends juste, le programme est évidemment de toute façon erroné si j'accède à quelque chose hors de ma gamme. Je m'inquiète de plus en plus des risques de casser quelque chose d'autre pendant le débogage de mes créations
ChrisD
La chose est: puis-je être certain si j'essaie d'accéder à la mémoire qui ne m'est pas affectée, que mon processus sera tué? (étant sur OSX)
ChrisD
3
Il y a des années, j'étais un programmeur C maladroit. J'ai accédé à des tableaux en dehors de leurs limites des centaines de fois. En plus de mon processus tué par le système d'exploitation, rien ne s'est jamais produit.
jbgs