Est-il sécuritaire d’parsingr un fichier / proc /?

Je veux parsingr /proc/net/tcp/ , mais est-ce sûr?

Comment dois-je ouvrir et lire des fichiers depuis /proc/ et ne pas avoir peur, qu’un autre processus (ou le système d’exploitation lui-même) le modifiera en même temps?

En général, non. (Donc, la plupart des réponses sont fausses.) Cela peut être sûr, selon la propriété que vous voulez. Mais il est facile de se retrouver avec des bogues dans votre code si vous en assumez trop sur la cohérence d’un fichier dans /proc . Par exemple, consultez ce bogue qui supposait que /proc/mounts était un instantané cohérent .

Par exemple:

  • /proc/uptime est totalement atomique , comme quelqu’un l’a mentionné dans une autre réponse – mais seulement depuis Linux 2.6.30 , qui a moins de deux ans. Donc, même ce minuscule fichier sortingvial était soumis à des conditions de course jusque-là et se trouve encore dans la plupart des kernelx d’entreprise. Voir fs/proc/uptime.c pour la source actuelle ou la validation qui l’a rendue atomique . Sur un kernel antérieur à la 2.6.30, vous pouvez open le fichier, en read un peu, puis si vous revenez plus tard et read , le morceau que vous obtenez sera incompatible avec le premier morceau. (Je viens de le démontrer, essayez-le vous-même pour vous amuser.)

  • /proc/mounts est atomique dans un seul appel système en read . Donc, si vous read tout le fichier en une seule fois, vous obtenez un instantané cohérent des points de assembly du système. Toutefois, si vous utilisez plusieurs appels système en read – et si le fichier est volumineux, c’est exactement ce qui se produira si vous utilisez des bibliothèques d’E / S normales et ne prêtez pas attention à ce problème – vous serez soumis à une condition de course. Non seulement vous n’obtiendrez pas un instantané cohérent, mais les points de assembly qui étaient présents avant de commencer et qui n’ont jamais cessé d’être présents risquent de disparaître dans ce que vous voyez. Pour voir qu’il est atomique pour un read() , regardez m_start() dans fs/namespace.c et voyez-le saisir un sémaphore qui garde la liste des points de assembly, qu’il garde jusqu’à m_stop() que m_stop() , appelé lors de la read() est terminé. Pour voir ce qui peut mal tourner, voyez ce bogue de l’année dernière (le même que celui que j’ai lié ci-dessus) dans un logiciel de haute qualité qui lisait allègrement /proc/mounts .

  • /proc/net/tcp , qui est en fait votre question, est encore moins cohérent. C’est atomique seulement dans chaque ligne de la table . Pour voir cela, regardez listening_get_next() dans net/ipv4/tcp_ipv4.c et net/ipv4/tcp_ipv4.c established_get_next() juste en dessous dans le même fichier et voyez les verrous qu’ils sortent à chaque entrée. Je n’ai pas de code de reproches à scope de main pour démontrer le manque de cohérence d’une ligne à l’autre, mais il n’y a pas de verrou (ou quelque chose d’autre) qui le rendrait cohérent. Ce qui est logique si vous y réfléchissez – la mise en réseau est souvent une partie très occupée du système, il ne vaut donc pas la peine de présenter une vue cohérente dans cet outil de diagnostic.

L’autre élément qui garde atomique /proc/net/tcp dans chaque ligne est la mise en mémoire tampon dans seq_read() , que vous pouvez lire dans fs/seq_file.c . Cela garantit qu’une fois que vous read() partie d’une ligne, le texte de la ligne entière est conservé dans un tampon afin que le prochain read() obtienne le rest de cette ligne avant de commencer une nouvelle. Le même mécanisme est utilisé dans /proc/mounts pour garder chaque ligne atomique même si vous faites plusieurs appels read() , et c’est aussi le mécanisme utilisé par /proc/mounts /proc/uptime dans les nouveaux kernelx pour restr atomique. Ce mécanisme ne met pas en mémoire tampon l’intégralité du fichier, car le kernel est prudent quant à l’utilisation de la mémoire.

La plupart des fichiers de /proc seront au moins aussi cohérents que /proc/net/tcp , chaque ligne étant une image cohérente d’une entrée dans les informations fournies, car la plupart utilisent la même abstraction de seq_file . Comme l’illustre l’exemple /proc/uptime , certains fichiers étaient en cours de migration pour utiliser seq_file en 2009; Je parie qu’il y en a encore qui utilisent des mécanismes plus anciens et qui n’ont même pas ce niveau d’atomicité. Ces mises en garde sont rarement documentées. Pour un fichier donné, votre seule garantie est de lire le source.

Dans le cas de /proc/net/tcp , vous pouvez le lire et parsingr chaque ligne sans crainte. Mais si vous essayez de tirer des conclusions de plusieurs lignes à la fois – méfiez-vous, d’autres processus et le kernel le modifient pendant que vous le lisez, et vous créez probablement un bogue.

Bien que les fichiers dans /proc apparaissent comme des fichiers réguliers dans l’espace utilisateur, ils ne sont pas vraiment des fichiers mais plutôt des entités prenant en charge les opérations de fichier standard à partir de l’espace utilisateur ( open , read , close ). Notez que ceci est très différent de celui d’avoir un fichier ordinaire sur le disque qui est modifié par le kernel.

Tout ce que fait le kernel est d’imprimer son état interne dans sa propre mémoire en utilisant une fonction de type sprintf, et cette mémoire est copiée dans l’espace utilisateur chaque fois que vous lancez un appel système read(2) .

Le kernel gère ces appels de manière totalement différente de celle des fichiers normaux, ce qui pourrait signifier que l’instantané complet des données que vous allez lire pourrait être prêt au moment où vous l’ open(2) , tandis que le kernel s’assure que les appels simultanés sont cohérent et atomique. Je n’ai pas lu ça ailleurs, mais ça n’a pas vraiment de sens d’être autrement.

Mon conseil est de jeter un oeil à l’implémentation d’un fichier proc dans votre version d’Unix. Ceci est vraiment un problème d’implémentation (comme le format et le contenu de la sortie) qui n’est pas régi par une norme.

L’exemple le plus simple serait l’implémentation du fichier proc de uptime dans Linux. Notez comment la totalité du tampon est produite dans la fonction de rappel fournie à single_open .

/ proc est un système de fichiers virtuel: en fait, il donne simplement une vue pratique des composants du kernel. Il est sûr de le lire (c’est pourquoi il est ici), mais il est risqué à long terme, car l’intérieur de ces fichiers virtuels peut évoluer avec une nouvelle version du kernel.

MODIFIER

Plus d’informations disponibles dans la documentation de proc sur les documents du kernel Linux , chapitre 1.4 Réseau Je ne trouve pas si les informations sur la manière dont les informations évoluent au fil du temps. Je pensais que c’était gelé à découvert, mais je ne peux pas avoir de réponse définitive.

EDIT2

Selon Sco doc (pas linux, mais je suis presque sûr que toutes les saveurs de * nix se comportent comme ça)

Bien que l’état du processus et par conséquent le contenu des fichiers / proc puissent changer d’instant en instant, il est garanti qu’une seule lecture (2) d’un fichier / proc renverra une représentation «saine» de l’état, c’est-à-dire que la lecture sera un instantané atomique de l’état du processus. Aucune garantie de ce type ne s’applique aux lectures successives appliquées à un fichier / proc pour un processus en cours d’exécution. De plus, l’atomicité n’est spécifiquement pas garantie pour toute E / S appliquée au fichier as (adresse-espace); le contenu de l’espace d’adressage d’un processus peut être modifié simultanément par un LWP de ce processus ou par tout autre processus du système.

L’API procfs du kernel Linux fournit une interface pour s’assurer que les lectures renvoient des données cohérentes. Lisez les commentaires dans __proc_file_read . L’élément 1) du grand bloc de commentaires explique cette interface.

Cela étant dit, il est bien entendu nécessaire d’utiliser un fichier proc spécifique pour utiliser cette interface correctement afin de s’assurer que les données renvoyées sont cohérentes. Donc, pour répondre à votre question: non, le kernel ne garantit pas la cohérence des fichiers proc lors d’une lecture, mais il fournit les moyens pour les implémentations de ces fichiers d’assurer la cohérence.

J’ai la source de Linux 2.6.27.8 à scope de main car je fais actuellement du développement de pilotes sur une cible ARM intégrée.

Le fichier … linux-2.6.27.8-lpc32xx/net/ipv4/raw.c à la ligne 934 contient par exemple

  seq_printf(seq, "%4d: %08X:%04X %08X:%04X" " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n", i, src, srcp, dest, destp, sp->sk_state, atomic_read(&sp->sk_wmem_alloc), atomic_read(&sp->sk_rmem_alloc), 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops)); 

quelles sorties

 [wally@zenetfedora ~]$ cat /proc/net/tcp sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode 0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 15160 1 f552de00 299 1: 00000000:C775 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 13237 1 f552ca00 299 ... 

dans la fonction raw_sock_seq_show() qui fait partie d’une hiérarchie de fonctions de traitement procfs . Le texte n’est pas généré tant qu’une demande read() n’est pas faite avec le /proc/net/tcp , un mécanisme raisonnable puisque les lectures de procfs sont certainement beaucoup moins courantes que la mise à jour des informations.

Certains pilotes (comme le mien) implémentent la fonction proc_read avec un seul sprintf() . La complication supplémentaire dans l’implémentation des pilotes de base est de gérer une sortie potentiellement très longue qui peut ne pas tenir dans le tampon intermédiaire de l’espace kernel lors d’une lecture unique.

Je l’ai testé avec un programme utilisant un tampon de lecture de 64 Ko, mais il en résulte une mémoire tampon de 3072 octets dans le système pour que proc_read renvoie les données. Plusieurs appels avec des pointeurs évolutifs sont nécessaires pour obtenir plus que la quantité de texte renvoyée. Je ne sais pas quelle est la bonne façon de rendre les données renvoyées cohérentes lorsque plusieurs E / S sont nécessaires. Certes, chaque entrée dans /proc/net/tcp est cohérente. Il est probable que les lignes juxtaposées soient instantanées à des moments différents.

En l’absence de bogues inconnus, il n’y a pas de conditions de /proc dans /proc qui entraîneraient la lecture de données corrompues ou un mélange de données anciennes et nouvelles. En ce sens, c’est sûr. Cependant, il existe toujours la condition de concurrence selon laquelle une grande partie des données que vous lisez à partir de /proc est potentiellement obsolète dès qu’elle est générée, et même plus au moment où vous obtenez la lecture / traitement. Par exemple, les processus peuvent mourir à tout moment et un nouveau processus peut être affecté au même pid; les seuls identifiants de processus que vous pouvez utiliser sans conditions de concurrence sont vos propres processus enfants ». Même chose pour les informations réseau (ports ouverts, etc.) et la plupart des informations contenues dans /proc . Je considérerais comme une pratique mauvaise et dangereuse de compter sur des données de /proc exactes, à l’exception des données sur votre propre processus et potentiellement sur ses processus enfants. Bien sûr, il peut être utile de présenter d’autres informations à partir de /proc à l’utilisateur / admin pour obtenir des informations / journalisation / etc. fins.

Lorsque vous lisez à partir d’un fichier / proc, le kernel appelle une fonction qui a été enregistrée à l’avance pour être la fonction “read” pour ce fichier proc. Voir la fonction __proc_file_read dans fs / proc / generic.c.

Par conséquent, la sécurité de proc read est aussi sûre que la fonction que le kernel appelle pour satisfaire la demande de lecture. Si cette fonction verrouille correctement toutes les données qu’elle touche et vous retourne dans un tampon, il est alors tout à fait sûr de les lire en utilisant cette fonction. Comme les fichiers proc comme celui utilisé pour satisfaire les demandes de lecture de / proc / net / tcp existent depuis un certain temps et ont été scrupuleusement révisés, ils sont à peu près aussi sûrs que vous pourriez le demander. En fait, de nombreux utilitaires Linux courants reposent sur la lecture depuis le système de fichiers proc et le formatage de la sortie d’une manière différente. (Sur le dessus de ma tête, je pense que «ps» et «netstat» le font).

Comme toujours, vous n’avez pas à me croire sur parole; vous pouvez regarder la source pour calmer vos peurs. La documentation suivante de proc_net_tcp.txt vous indique où les fonctions “read” pour / proc / net / tcp live, vous pouvez donc regarder le code qui est exécuté lorsque vous lisez à partir de ce fichier proc et vérifiez qu’il n’y a pas de dangers de locking.

Ce document décrit les interfaces / proc / net / tcp et / proc / net / tcp6.
Notez que ces interfaces sont déconseillées en faveur de tcp_diag. Ces interfaces / proc fournissent des informations sur les connexions TCP actuellement actives et sont implémentées par tcp4_seq_show () dans net / ipv4 / tcp_ipv4.c et tcp6_seq_show () dans net / ipv6 / tcp_ipv6.c, respectivement.