Czy analizowanie pliku / proc / jest bezpieczne?


152

Chcę przeanalizować /proc/net/tcp/, ale czy jest to bezpieczne?

Jak mam otwierać i czytać pliki z /proc/i nie bać się, że jakiś inny proces (lub sam system operacyjny) zmieni go w tym samym czasie?


29
+1. To cholernie dobre pytanie. Żałuję tylko, że nie mam odpowiedzi, ale nie mogę się doczekać, aby się dowiedzieć, ponieważ robiłem tego rodzaju rzeczy już sporo wcześniej.
paxdiablo

1
Jestem prawie pewien, że samo przeczytanie tego da ci listę połączeń, a także UID, który jest właścicielem każdego z nich, tak jak wtedy, gdy je otwierałeś . Nie mogę jednak znaleźć tego udokumentowanego, więc na razie komentuję.
Tim Post

3
Prosta odpowiedź brzmi oczywiście tak, ponieważ nie jest to plik - czytanie go powinno być zawsze bezpieczne. Odpowiedzi mogą nie być spójne po przeczytaniu, ale będzie to bezpieczne.
Rory Alsop

Dlatego zamiast tego powinieneś używać sysctl. (to również mniej wywołań systemowych)
Good Person

@GoodPerson - w jaki sposób może sysctlmi to pomóc /proc/net/tcp/na przykład przeanalizować plik?
Kiril Kirov

Odpowiedzi:


111

Ogólnie nie. (Więc większość odpowiedzi jest błędnych.) To może być bezpieczne, w zależności od tego, jaką nieruchomość chcesz. Ale łatwo jest skończyć z błędami w swoim kodzie, jeśli za dużo zakładasz na temat spójności pliku w /proc. Na przykład zobacz ten błąd, który wynika z założenia, że /proc/mountsjest to spójna migawka .

Na przykład:

  • /proc/uptimejest całkowicie atomowy , jak ktoś wspomniał w innej odpowiedzi - ale tylko od Linuksa 2.6.30 , który ma mniej niż dwa lata. Tak więc nawet ten mały, trywialny plik do tej pory podlegał warunkom wyścigu i nadal występuje w większości jąder korporacyjnych. Zobacz fs/proc/uptime.cbieżące źródło lub zatwierdzenie, które uczyniło je atomowym . Na jądrze starszym niż 2.6.30 możesz openplik, readkawałek, a jeśli później wrócisz i readznowu, otrzymany kawałek będzie niezgodny z pierwszym kawałkiem. (Właśnie to zademonstrowałem - spróbuj sam dla zabawy.)

  • /proc/mountsjest atomowy w ramach pojedynczego readwywołania systemowego. Więc jeśli zbierzesz readcały plik naraz, otrzymasz jedną spójną migawkę punktów montowania w systemie. Jeśli jednak używasz kilku readwywołań systemowych - i jeśli plik jest duży, dokładnie to się stanie, jeśli użyjesz normalnych bibliotek I / O i nie zwrócisz szczególnej uwagi na ten problem - zostaniesz poddany wyścigowi stan: schorzenie. Nie tylko nie uzyskasz spójnej migawki, ale punkty montowania, które były obecne przed rozpoczęciem i nigdy nie przestały być obecne, mogą zniknąć w tym, co widzisz. Aby zobaczyć, że to atomowy dla jednego read(), spojrzenie m_start()nafs/namespace.c i zobaczyć go chwycić semafora że Osłony listy strumieni, które utrzymuje aż m_stop(), która jest wywoływana, gdyread()skończone. Aby zobaczyć, co może pójść nie tak, zobacz ten błąd z zeszłego roku (ten sam, do którego dołączyłem powyżej) w oprogramowaniu wysokiej jakości, które beztrosko czyta /proc/mounts.

  • /proc/net/tcp, o którą tak naprawdę pytasz, jest jeszcze mniej spójne. Jest niepodzielna tylko w każdym wierszu tabeli . Aby to zobaczyć, przyjrzeć listening_get_next()sięnet/ipv4/tcp_ipv4.c i established_get_next()tuż poniżej w tym samym pliku i zobaczyć zamki biorą na każdej pozycji po kolei. Nie mam pod ręką kodu repro, aby zademonstrować brak spójności między wierszami, ale nie ma tam żadnych blokad (ani niczego innego), które sprawiłyby, że byłby spójny. Co ma sens, jeśli się nad tym zastanowić - praca w sieci jest często bardzo zajętą ​​częścią systemu, więc nie warto przedstawiać spójnego obrazu w tym narzędziu diagnostycznym.

Drugi kawałek, który utrzymuje /proc/net/tcpatomowej w zasięgu każdego wiersza jest buforowanie w seq_read(), co można przeczytać wfs/seq_file.c . Zapewnia to, że gdy już read()podzielisz jeden wiersz, tekst całego wiersza zostanie zachowany w buforze, dzięki czemu następny read()otrzyma resztę tego wiersza przed rozpoczęciem nowego. Ten sam mechanizm jest używany /proc/mountsdo zachowania atomowości każdego wiersza, nawet jeśli wykonujesz wiele read()wywołań, a także jest to mechanizm /proc/uptimeużywany w nowszych jądrach, aby zachować atomowość. Mechanizm ten nie buforuje całego pliku, ponieważ jądro jest ostrożne w kwestii wykorzystania pamięci.

Większość plików /procbędzie co najmniej tak spójna, jak /proc/net/tcpw każdym wierszu spójny obraz jednego wpisu we wszelkich dostarczanych informacjach, ponieważ większość z nich używa tej samej seq_fileabstrakcji. Jak /proc/uptimepokazuje przykład, niektóre pliki były nadal migrowane do użytku seq_filedopiero w 2009 roku; Założę się, że wciąż są takie, które wykorzystują starsze mechanizmy i nie mają nawet takiego poziomu atomowości. Te zastrzeżenia są rzadko udokumentowane. W przypadku danego pliku jedyną gwarancją jest odczytanie źródła.

W przypadku /proc/net/tcp, możesz go przeczytać i przeanalizować każdy wiersz bez obaw. Ale jeśli starają się wyciągać wnioski z wielu linii na raz - uważaj, inne procesy i jądro zmieniając go podczas jej czytania, a ty pewnie tworząc błąd.


1
co z atomowością readdir? jak czytanie / proc / self / fd? czy to jest bezpieczne?
socketpair,

Nie to, że odpowiedź na pytanie, ale aby dodać temat jak sprawdzić czas pracy można korzystać clock_gettime(2)z CLOCK_MONOTONIC(choć może jest Informacja techniczna jestem nieświadomy tutaj, ale ja osobiście nie widziałem go tylko od chwili rozruchu). W przypadku Linuksa masz również opcję sysinfo(2).
Pryftan

44

Chociaż pliki /procwyświetlane jako zwykłe pliki w przestrzeni użytkownika, nie są one naprawdę pliki, ale raczej podmioty, które obsługują standardowe operacje na plikach z przestrzeni użytkownika ( open, read, close). Zauważ, że jest to zupełnie inne niż posiadanie zwykłego pliku na dysku, który jest zmieniany przez jądro.

Wszystko, co robi jądro, to wypisuje swój stan wewnętrzny do swojej własnej pamięci za pomocą sprintffunkcji podobnej do tej, która jest kopiowana do przestrzeni użytkownika za każdym razem, gdy read(2)wywołujesz wywołanie systemowe.

Jądro obsługuje te wywołania w zupełnie inny sposób niż w przypadku zwykłych plików, co może oznaczać, że cała migawka danych, które przeczytasz, może być gotowa w momencie open(2), gdy to zrobisz , podczas gdy jądro zapewnia, że ​​jednoczesne wywołania są spójne i niepodzielne. Nigdzie tego nie czytałem, ale tak naprawdę nie ma sensu być inaczej.

Moja rada jest taka, aby przyjrzeć się implementacji pliku proc w swoim specyficznym dla Uniksa stylu. W rzeczywistości jest to kwestia implementacji (podobnie jak format i zawartość wyniku), która nie jest regulowana przez standard.

Najprostszym przykładem może być implementacja uptimepliku proc w Linuksie. Zwróć uwagę, jak cały bufor jest tworzony w funkcji wywołania zwrotnego dostarczonej do single_open.


3
@Ignacio: Po prostu kieruję OP w tym kierunku, ponieważ odniosłem wrażenie, że on myśli, że procpliki są zwykłymi plikami otwieranymi do zapisu przez jądro.
Blagovest Buyukliev

4
Twoja rada dotycząca implementacji konkretnego pliku jest dobra. Niestety przypuszczenie, że wszystko to jest migawkowe, open()jest błędne w przypadku wielu plików, w szczególności tych /proc/net/tcp, którymi zajmuje się OP. Ma to sens, jeśli myślisz o kosztach zapewnienia tej semantyki - musiałbyś zrobić coś takiego, jak zablokowanie wewnętrznych struktur danych, które rejestrują wszystkie te połączenia TCP, co w obciążonym systemie jest katastrofą, nawet jeśli trzymasz ją tylko długo wystarczy, aby przejrzeć i sformatować dane do bufora. Zobacz moją odpowiedź, aby dowiedzieć się, co się właściwie dzieje.
Greg Price

16

/ proc to wirtualny system plików: w rzeczywistości daje wygodny wgląd w wewnętrzne elementy jądra. Przeczytanie go na pewno jest bezpieczne (dlatego jest tutaj), ale na dłuższą metę jest to ryzykowne, ponieważ wewnętrzne pliki wirtualnych plików mogą ewoluować wraz z nowszą wersją jądra.

EDYTOWAĆ

Więcej informacji znajduje się w dokumentacji proc w dokumencie jądra Linuksa , rozdział 1.4 Sieć Nie mogę znaleźć informacji, jak te informacje ewoluują w czasie. Myślałem, że jest zamrożony, ale nie mam jednoznacznej odpowiedzi.

EDYCJA2

Według Sco doc (nie linux, ale jestem prawie pewien, że wszystkie smaki * nix zachowują się tak)

Chociaż stan procesu i w konsekwencji zawartość plików / proc mogą zmieniać się z natychmiastowego na natychmiastowy, pojedyncze odczytanie (2) pliku / proc gwarantuje, że zwróci `` rozsądną '' reprezentację stanu, to znaczy odczyt będzie atomowa migawka stanu procesu. Żadna taka gwarancja nie ma zastosowania do kolejnych odczytów zastosowanych do pliku / proc dla działającego procesu. Ponadto atomowość nie jest w szczególności gwarantowana dla żadnego I / O zastosowanego do pliku as (przestrzeń adresowa); zawartość przestrzeni adresowej dowolnego procesu może być jednocześnie modyfikowana przez LWP tego procesu lub jakikolwiek inny proces w systemie.


3
"Myślę" ? Byłoby miło mieć ostateczną odpowiedź :)
static_rtti

Biorąc pod uwagę implementację / proc w jądrze, dotyczy to również Linuksa. Jeśli czytasz plik procfs w pojedynczym wywołaniu odczytu, jest to spójne - oczywiście zakładając, że czytany plik proc został poprawnie zaimplementowany po stronie jądra.
Erik

8
Nie sądzę, żebyś mógł wymyślić gorsze możliwe źródło informacji niż SCO i próbować traktować proctak, jakby zachowywało się podobnie w różnych jądrach (lub nawet zakładając, że istnieje - nie musi tego robić w systemie Unix) ) przyniesie Ci świat bólu.
Nicholas Knight

1
@Nicholas: cóż, nie mogłem znaleźć jakiejś ostatecznej odpowiedzi w dokumentacji jądra, możesz ją wskazać, jeśli ją znasz.
Bruce

2
Ciekawe, że doktorzy SCO tak mówią. Niestety nie zawsze jest to prawdą w Linuksie, aw szczególności nie jest to prawdą /proc/net/tcp, co jest głównym zmartwieniem OP. Raczej tylko każdy pojedynczy wiersz w danych wyjściowych jest niepodzielny. Zobacz moją odpowiedź po szczegóły.
Greg Price

14

API procfs w jądrze Linuksa zapewnia interfejs zapewniający, że odczyty zwracają spójne dane. Przeczytaj komentarze w __proc_file_read. Punkt 1) w dużym bloku komentarzy wyjaśnia ten interfejs.

To powiedziawszy, to oczywiście zależy od implementacji konkretnego pliku proc, aby poprawnie używać tego interfejsu, aby upewnić się, że zwracane dane są spójne. A więc odpowiadając na twoje pytanie: nie, jądro nie gwarantuje spójności plików proc podczas odczytu, ale zapewnia środki do implementacji tych plików w celu zapewnienia spójności.


4
Niestety, wiele plików /procw rzeczywistości nie zapewnia spójności. Zobacz moją odpowiedź po szczegóły.
Greg Price

3
Ponadto __proc_file_read()jest przestarzały na korzyść seq_file. Zobacz raczej zirytowany, brzmiący komentarz (napisany przez Linusa) tuż nad długim komentarzem blokowym.
Greg Price

6

Mam przydatne źródło dla Linuksa 2.6.27.8, ponieważ w tej chwili pracuję nad rozwojem sterowników w osadzonym celu ARM.

Na przykład plik ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.cw linii 934 zawiera

    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));

które wyjścia

[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
...

w funkcji, raw_sock_seq_show()która jest częścią hierarchii funkcji obsługujących procfs . Tekst nie jest generowany, dopóki plik nie read()zostanie złożony /proc/net/tcp, co jest rozsądnym mechanizmem, ponieważ odczyty procfs są z pewnością znacznie mniej powszechne niż aktualizowanie informacji.

Niektóre sterowniki (takie jak moje) implementują funkcję proc_read z pojedynczym plikiem sprintf(). Dodatkową komplikacją związaną z implementacją podstawowych sterowników jest obsługa potencjalnie bardzo długich danych wyjściowych, które mogą nie zmieścić się w pośrednim buforze przestrzeni jądra podczas pojedynczego odczytu.

Przetestowałem to z programem używającym bufora odczytu 64K, ale w moim systemie pojawił się bufor przestrzeni jądra o wielkości 3072 bajtów, aby proc_read mógł zwrócić dane. Aby otrzymać więcej niż tyle zwróconego tekstu, potrzeba wielu wywołań z zaawansowanymi wskaźnikami. Nie wiem, jaki jest właściwy sposób, aby zwracane dane były spójne, gdy potrzebne jest więcej niż jedno wejście / wyjście. Z pewnością każdy wpis /proc/net/tcpjest spójny. Istnieje pewne prawdopodobieństwo, że linie obok siebie są migawkami w różnym czasie.


Naprawdę przepraszam, nie rozumiałem dużo. Więc masz na myśli, że jeśli użyję ifstream, będzie to niebezpieczne, ale jeśli użyję, readbędzie bezpieczne? Lub ifstreamużywa wewnętrznie read? I co proponujesz?
Kiril Kirov

@Kiril: Przepraszam za zamieszanie. To jest wyjaśnienie, w jaki sposób dane /proc/net/tcpsą sformatowane i jest całkowicie niezależne od tego, jak ktoś je czyta.
wallyk

1
Tak! Zgadujesz, że różne linie (in /proc/net/tcp) nie pochodzą z tej samej migawki. Zobacz moją odpowiedź, aby uzyskać wyjaśnienie.
Greg Price

3

Oprócz nieznanych błędów, nie ma warunków wyścigu /proc, które prowadziłyby do odczytu uszkodzonych danych lub mieszania starych i nowych danych. W tym sensie jest to bezpieczne. Jednak nadal istnieje warunek wyścigu, że wiele danych, z których czytasz, /procjest potencjalnie nieaktualnych, gdy tylko zostaną wygenerowane, a nawet bardziej, zanim zaczniesz je odczytywać / przetwarzać. Na przykład procesy mogą umrzeć w dowolnym momencie, a nowemu procesowi można przypisać ten sam pid; jedynymi identyfikatorami procesów, których możesz kiedykolwiek użyć bez warunków rasowych, są procesy własnego dziecka ”. To samo dotyczy informacji o sieci (otwarte porty itp.) I tak naprawdę większość informacji w formacie /proc. Uznałbym za złą i niebezpieczną praktykę poleganie na jakichkolwiek danych w formacie/procdokładność, z wyjątkiem danych dotyczących własnego procesu i potencjalnie jego procesów podrzędnych. Oczywiście nadal może być przydatne przedstawienie innych informacji od /procużytkownika / administratora do celów informacyjnych / logowania / itp. cele.


Robię to, aby uzyskać i wykorzystać pewne informacje dla mojego własnego procesu (dla mojego PID, używając getpid()). Więc to musi być bezpieczne.
Kiril Kirov

1
Tak, uważam to za całkowicie bezpieczne.
R .. GitHub STOP HELPING ICE

Nie zgadzam się, że procesy potomne byłyby lepiej zachowane niż jakikolwiek inny proces. Jeśli chodzi o /procinterfejs, wszystkie mają te same słabe i mocne strony. W każdym razie OP pyta o informacje związane ze sterownikiem urządzenia, a nie o procesy.
wallyk

1
Jeśli pid Njest twoim procesem potomnym, możesz upewnić się, że pid Nnadal odnosi się do tego samego (prawdopodobnie zakończonego) procesu, dopóki nie wywołasz na waitnim funkcji -family. Gwarantuje to, że nie ma wyścigów.
R .. GitHub STOP HELPING ICE

O co chodzi z zalewem -1 i bez wyjaśnienia?
R .. GitHub ZATRZYMAJ SIĘ NA LODZIE

2

Kiedy czytasz z pliku / proc, jądro wywołuje funkcję, która została wcześniej zarejestrowana jako funkcja "odczytu" dla tego pliku proc. Zobacz __proc_file_readfunkcję w fs / proc / generic.c.

Dlatego bezpieczeństwo odczytu proc jest tak bezpieczne, jak funkcja wywoływana przez jądro w celu spełnienia żądania odczytu. Jeśli ta funkcja prawidłowo blokuje wszystkie dane, których dotyka i wraca do Ciebie w buforze, odczyt przy użyciu tej funkcji jest całkowicie bezpieczny. Ponieważ pliki proc, takie jak ten używany do spełniania żądań odczytu do / proc / net / tcp, istnieją już od jakiegoś czasu i przeszły skrupulatną recenzję, są tak bezpieczne, o jakie można prosić. W rzeczywistości wiele popularnych narzędzi Linuksa polega na czytaniu z systemu plików proc i formatowaniu danych wyjściowych w inny sposób. (Myślę, że „ps” i „netstat” to robią).

Jak zawsze, nie musisz wierzyć mi na słowo; możesz spojrzeć na źródło, aby uspokoić swoje obawy. Poniższa dokumentacja z proc_net_tcp.txt mówi ci, gdzie znajdują się funkcje "read" dla / proc / net / tcp, więc możesz spojrzeć na rzeczywisty kod, który jest uruchamiany, kiedy czytasz z tego pliku proc i sprawdzić samemu, że nie ma zagrożenia blokujące.

Ten dokument opisuje interfejsy / proc / net / tcp i / proc / net / tcp6.
Zauważ, że te interfejsy są przestarzałe na rzecz tcp_diag. Te interfejsy / proc dostarczają informacji o aktualnie aktywnych połączeniach TCP i są implementowane przez tcp4_seq_show () odpowiednio w net / ipv4 / tcp_ipv4.c i tcp6_seq_show () w net / ipv6 / tcp_ipv6.c.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.