Apakah aman untuk mem-parsing file / proc /?

152

Saya ingin mengurai /proc/net/tcp/, tetapi apakah itu aman?

Bagaimana saya harus membuka dan membaca file dari /proc/dan tidak takut, bahwa beberapa proses lain (atau OS itu sendiri) akan mengubahnya dalam waktu yang bersamaan?

Kiril Kirov
sumber
29
+1. Itu pertanyaan yang sangat bagus. Saya hanya berharap saya punya jawabannya, tetapi saya berharap untuk mencari tahu karena saya sudah melakukan hal semacam itu sebelumnya.
paxdiablo
1
Saya cukup yakin hanya membacanya akan memberi Anda daftar koneksi, ditambah UID yang memiliki masing-masing, seperti ketika Anda membukanya . Saya tidak dapat menemukan yang didokumentasikan, jadi buatlah komentar untuk saat ini.
Tim Post
3
Jawaban sederhana jelas ya, karena ini bukan file - membacanya harus selalu aman. Jawaban mungkin tidak konsisten kali berikutnya Anda membacanya, tetapi itu akan aman.
Rory Alsop
Inilah mengapa Anda harus menggunakan sysctl sebagai gantinya. (itu juga syscalls lebih sedikit)
Good Person
@GoodPerson - bagaimana ini bisa sysctlmembantu saya mengurai /proc/net/tcp/file, misalnya?
Kiril Kirov

Jawaban:

111

Secara umum, tidak. (Jadi sebagian besar jawaban di sini salah.) Mungkin aman, tergantung pada properti apa yang Anda inginkan. Tetapi mudah untuk berakhir dengan bug dalam kode Anda jika Anda menganggap terlalu banyak tentang konsistensi file /proc. Misalnya, lihat bug ini yang berasal dari asumsi bahwa /proc/mountsitu adalah snapshot yang konsisten .

Sebagai contoh:

  • /proc/uptimebenar - benar atom , seperti seseorang yang disebutkan dalam jawaban lain - tetapi hanya sejak Linux 2.6.30 , yang kurang dari dua tahun. Jadi bahkan file mungil dan sepele ini tunduk pada kondisi balapan hingga saat itu, dan masih ada di sebagian besar kernel enterprise. Lihat fs/proc/uptime.csumber saat ini, atau komit yang membuatnya atomik . Pada kernel pra-2.6.30, Anda dapat openfile, readsedikit, kemudian jika Anda kembali dan readlagi, potongan yang Anda dapatkan akan tidak konsisten dengan bagian pertama. (Saya baru saja menunjukkan ini - cobalah sendiri untuk bersenang-senang.)

  • /proc/mountsadalah atom dalam readpanggilan sistem tunggal . Jadi jika Anda readseluruh file sekaligus, Anda mendapatkan snapshot konsisten tunggal poin mount pada sistem. Namun, jika Anda menggunakan beberapa readpanggilan sistem - dan jika file tersebut besar, ini adalah persis apa yang akan terjadi jika Anda menggunakan perpustakaan I / O normal dan tidak memberi perhatian khusus pada masalah ini - Anda akan dikenakan perlombaan kondisi. Anda tidak hanya akan mendapatkan snapshot yang konsisten, tetapi mount poin yang ada sebelum Anda mulai dan tidak pernah berhenti hadir mungkin hilang dalam apa yang Anda lihat. Untuk melihat bahwa itu atomik untuk satu read(), lihat ke m_start()dalamfs/namespace.c dan melihatnya mengambil semafor yang menjaga daftar mountpoints, yang disimpan sampai m_stop(), yang disebut ketikaread()dilakukan. Untuk melihat apa yang salah, lihat bug ini dari tahun lalu (sama dengan yang saya tautkan di atas) dalam perangkat lunak berkualitas tinggi yang membaca dengan gembira /proc/mounts.

  • /proc/net/tcp, yang sebenarnya Anda tanyakan, bahkan kurang konsisten dari itu. Itu hanya atom dalam setiap baris tabel . Untuk melihat ini, lihat ke listening_get_next()dalamnet/ipv4/tcp_ipv4.c dan established_get_next()tepat di bawah dalam file yang sama, dan lihat kunci yang mereka ambil pada setiap entri secara bergantian. Saya tidak memiliki kode repro berguna untuk menunjukkan kurangnya konsistensi dari baris ke baris, tetapi tidak ada kunci di sana (atau apa pun) yang akan membuatnya konsisten. Yang masuk akal jika Anda memikirkannya - jaringan sering kali merupakan bagian yang sangat sibuk dari sistem, jadi tidak sepadan dengan biaya overhead untuk menghadirkan tampilan yang konsisten dalam alat diagnostik ini.

Bagian lain yang membuat /proc/net/tcpatom dalam setiap baris adalah penyangga di seq_read(), yang dapat Anda baca difs/seq_file.c . Ini memastikan bahwa setelah Anda read()bagian dari satu baris, teks dari seluruh baris disimpan dalam buffer sehingga baris berikutnya read()akan mendapatkan sisa baris itu sebelum memulai yang baru. Mekanisme yang sama digunakan /proc/mountsuntuk menjaga setiap baris atom meskipun Anda melakukan banyak read()panggilan, dan juga mekanisme yang digunakan /proc/uptimepada kernel yang lebih baru untuk tetap atom. Mekanisme itu tidak buffer seluruh file, karena kernel berhati-hati tentang penggunaan memori.

Sebagian besar file dalam /procsetidaknya akan sama konsisten /proc/net/tcp, dengan setiap baris gambar yang konsisten dari satu entri dalam informasi apa pun yang mereka sediakan, karena kebanyakan dari mereka menggunakan seq_fileabstraksi yang sama . Seperti yang /proc/uptimediilustrasikan oleh contoh, beberapa file masih dimigrasi untuk digunakan seq_filebaru-baru ini pada tahun 2009; Saya yakin masih ada beberapa yang menggunakan mekanisme yang lebih tua dan bahkan tidak memiliki tingkat atomisitas seperti itu. Peringatan ini jarang didokumentasikan. Untuk file yang diberikan, satu-satunya jaminan Anda adalah membaca sumbernya.

Dalam hal ini /proc/net/tcp, Anda dapat membacanya dan mengurai setiap baris tanpa rasa takut. Tetapi jika Anda mencoba untuk menarik kesimpulan dari beberapa baris sekaligus - berhati-hatilah, proses lainnya dan kernel yang berubah saat Anda membacanya, dan Anda mungkin menciptakan bug.

Greg Price
sumber
1
bagaimana dengan atomicity readdir? suka membaca / proc / self / fd? apakah aman?
socketpair
Bukannya menjawab pertanyaan tetapi untuk menambahkan tentang cara memeriksa uptime yang dapat Anda gunakan clock_gettime(2)dengan CLOCK_MONOTONIC(meskipun mungkin ada teknis saya tidak tahu di sini tapi saya pribadi hanya melihatnya dengan sejak saat boot). Untuk Linux Anda juga memiliki opsi sysinfo(2).
Pryftan
44

Meskipun file dalam /procmuncul sebagai file biasa di userspace, mereka tidak benar-benar file melainkan entitas yang mendukung operasi file standar dari userspace ( open, read, close). Perhatikan bahwa ini sangat berbeda dari memiliki file biasa pada disk yang sedang diubah oleh kernel.

Semua yang dilakukan kernel adalah mencetak status internalnya ke dalam memorinya sendiri menggunakan sprintffungsi-like, dan memori tersebut disalin ke userspace setiap kali Anda mengeluarkan read(2)panggilan sistem.

Kernel menangani panggilan ini dengan cara yang sama sekali berbeda dari file biasa, yang dapat berarti bahwa seluruh snapshot dari data yang Anda baca dapat siap pada saat Anda open(2)melakukannya, sementara kernel memastikan bahwa panggilan bersamaan konsisten dan atom. Saya belum membacanya di mana pun, tetapi itu tidak masuk akal untuk menjadi sebaliknya.

Saran saya adalah untuk melihat penerapan file proc dalam rasa Unix khusus Anda. Ini benar-benar masalah implementasi (seperti format dan isi output) yang tidak diatur oleh standar.

Contoh paling sederhana adalah implementasi uptimefile proc di Linux. Perhatikan bagaimana seluruh buffer dihasilkan dalam fungsi callback yang disediakan single_open.

Buyuciev Blagovest
sumber
3
@Ignacio: Saya hanya menunjuk OP ke arah ini karena saya dibiarkan dengan kesan bahwa dia berpikir bahwa procfile adalah file biasa yang dibuka untuk ditulis oleh kernel.
Blagovest Buyukliev
4
Saran Anda untuk melihat implementasi file spesifik itu baik. Sayangnya dugaan bahwa itu semua snapshotted open()adalah salah untuk banyak file, dan khususnya untuk /proc/net/tcp, yang berkaitan dengan OP. Ini masuk akal jika Anda berpikir tentang biaya menyediakan semantik tersebut - Anda harus melakukan sesuatu seperti mengunci struktur data internal yang merekam semua koneksi TCP, yang pada sistem yang sibuk adalah bencana bahkan jika Anda hanya menahannya lama cukup untuk memindai dan memformat data ke buffer. Lihat jawaban saya untuk perincian tentang apa yang sebenarnya terjadi.
Harga Greg
16

/ proc adalah sistem file virtual: pada kenyataannya, itu hanya memberikan pandangan yang nyaman tentang kernel internal. Sudah pasti aman untuk membacanya (itu sebabnya ada di sini) tetapi berisiko dalam jangka panjang, karena internal dari file virtual ini dapat berevolusi dengan versi kernel yang lebih baru.

EDIT

Informasi lebih lanjut tersedia dalam dokumentasi proc di Linux kernel doc , bab 1.4 Jaringan Saya tidak dapat menemukan apakah informasi bagaimana informasi itu berkembang seiring waktu. Saya pikir itu dibekukan di tempat terbuka, tetapi tidak dapat memiliki jawaban yang pasti.

EDIT2

Menurut Sco doc (bukan linux, tapi saya cukup yakin semua rasa * nix berperilaku seperti itu)

Walaupun status proses dan akibatnya isi file / proc dapat berubah dari instan ke instan, pembacaan tunggal (2) dari file / proc dijamin untuk mengembalikan representasi negara yang `waras ', yaitu, pembacaan akan dilakukan snapshot atom dari keadaan proses. Tidak ada jaminan yang berlaku untuk pembacaan berurutan yang diterapkan pada file / proc untuk proses yang sedang berjalan. Selain itu, atomicity secara khusus tidak dijamin untuk I / O yang diterapkan pada file as (address-space); isi dari ruang alamat proses apa pun dapat secara bersamaan dimodifikasi oleh LWP dari proses itu atau proses lain dalam sistem.

Bruce
sumber
3
"Kupikir" ? Akan menyenangkan untuk memiliki jawaban yang pasti :)
static_rtti
Mengingat implementasi / proc di kernel, ini juga berlaku untuk linux. Jika Anda membaca file procfs dalam satu panggilan read, itu konsisten - tentu saja dengan asumsi file proc yang Anda baca telah diterapkan dengan benar kernelside.
Erik
8
Saya tidak berpikir Anda dapat menemukan sumber informasi yang lebih buruk daripada SCO, dan mencoba memperlakukannya procseolah-olah memiliki perilaku yang sama antara kernel yang berbeda (atau bahkan dengan asumsi itu ada - tidak harus dalam sistem Unix ) akan membuat Anda dunia yang terluka.
Nicholas Knight
1
@ Nicholas: yah, tidak bisa menemukan jawaban pasti di kernel kernel, silakan tunjukkan jika Anda mengetahuinya.
Bruce
2
Menarik bahwa dokumen SCO mengatakan itu. Sayangnya itu tidak selalu benar di Linux, dan khususnya itu tidak berlaku untuk /proc/net/tcp, yang merupakan perhatian utama OP. Sebaliknya, hanya setiap baris individu dalam output adalah atom. Lihat jawaban saya untuk detailnya.
Harga Greg
14

API procfs di kernel Linux menyediakan antarmuka untuk memastikan bahwa membaca mengembalikan data yang konsisten. Baca komentar di __proc_file_read. Butir 1) di blok komentar besar menjelaskan antarmuka ini.

Yang sedang berkata, tentu saja hingga implementasi file proc tertentu untuk menggunakan antarmuka ini dengan benar untuk memastikan data yang dikembalikan konsisten. Jadi, untuk menjawab pertanyaan Anda: tidak, kernel tidak menjamin konsistensi dari file proc selama membaca tetapi menyediakan sarana untuk implementasi file-file tersebut untuk memberikan konsistensi.

Pekerjaan
sumber
4
Sayangnya, banyak file /procdalam kenyataannya tidak memberikan konsistensi. Lihat jawaban saya untuk detailnya.
Harga Greg
3
Juga, __proc_file_read()sudah usang dalam mendukung seq_file. Lihat komentar terdengar agak jengkel (oleh Linus) tepat di atas komentar blok panjang.
Harga Greg
6

Saya memiliki sumber untuk Linux 2.6.27.8 berguna karena saya sedang melakukan pengembangan driver saat ini pada target ARM tertanam.

File ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.cdi baris 934 berisi, misalnya

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

output yang mana

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

dalam fungsi raw_sock_seq_show()yang merupakan bagian dari hierarki fungsi penanganan procfs . Teks tidak dihasilkan sampai read()permintaan dibuat dari /proc/net/tcpfile, suatu mekanisme yang masuk akal karena membaca procfs pasti jauh lebih jarang daripada memperbarui informasi.

Beberapa driver (seperti milik saya) mengimplementasikan fungsi proc_read dengan satu sprintf(). Komplikasi ekstra dalam implementasi driver inti adalah untuk menangani output yang berpotensi sangat panjang yang mungkin tidak sesuai dengan buffer ruang-menengah saat membaca tunggal.

Saya mengujinya dengan sebuah program menggunakan buffer baca 64K tetapi menghasilkan buffer ruang kernel 3072 byte di sistem saya untuk proc_read untuk mengembalikan data. Dibutuhkan banyak panggilan dengan pointer maju untuk mendapatkan lebih dari itu banyak teks yang dikembalikan. Saya tidak tahu apa cara yang benar untuk membuat data yang dikembalikan konsisten ketika lebih dari satu i / o diperlukan. Tentu saja setiap entri /proc/net/tcpkonsisten. Ada beberapa kemungkinan bahwa garis berdampingan adalah potret pada waktu yang berbeda.

wallyk
sumber
Maaf, saya tidak mengerti banyak. Jadi, maksud Anda, bahwa jika saya menggunakan ifstream, itu akan tidak aman, tetapi jika saya menggunakannya readakan aman? Atau ifstreammenggunakan internal read? Dan apa yang Anda sarankan?
Kiril Kirov
@ Kiril: Maaf atas kebingungannya. Ini adalah penjelasan tentang bagaimana data untuk /proc/net/tcpdiformat dan sepenuhnya independen dari bagaimana orang membacanya.
wallyk
1
Ya! Dan tebakan Anda benar bahwa garis yang berbeda (dalam /proc/net/tcp) tidak berasal dari snapshot yang sama. Lihat jawaban saya untuk penjelasan.
Harga Greg
3

Pendek bug yang tidak diketahui, tidak ada kondisi balapan di /procyang akan menyebabkan membaca data yang rusak atau campuran data lama dan baru. Dalam hal ini, aman. Namun masih ada kondisi ras yang sebagian besar data yang Anda baca /procberpotensi usang segera setelah itu dihasilkan, dan bahkan lebih pada saat Anda membaca / memprosesnya. Misalnya proses dapat mati kapan saja dan proses baru dapat diberikan pid yang sama; satu-satunya proses id yang dapat Anda gunakan tanpa kondisi balapan adalah proses anak Anda sendiri '. Hal yang sama berlaku untuk informasi jaringan (port terbuka, dll.) Dan benar-benar sebagian besar informasi dalam /proc. Saya akan menganggap itu praktik yang buruk dan berbahaya untuk mengandalkan data apa pun di/procmenjadi akurat, kecuali data tentang proses Anda sendiri dan kemungkinan proses anaknya. Tentu saja masih berguna untuk menyajikan informasi lain dari /procpengguna / admin untuk informasi / logging / dll. tujuan.

R .. GitHub BERHENTI MEMBANTU ES
sumber
Saya melakukan ini untuk mendapatkan dan menggunakan beberapa informasi untuk proses saya sendiri (untuk PID saya, menggunakan getpid()). Jadi, itu pasti aman.
Kiril Kirov
1
Ya, saya akan menganggap itu sepenuhnya aman.
R .. GitHub BERHENTI MEMBANTU ICE
Saya tidak setuju bahwa proses anak akan berperilaku lebih baik daripada proses lainnya. Sejauh menyangkut /procantarmuka, mereka semua memiliki kelemahan dan kekuatan yang sama. Bagaimanapun, OP bertanya tentang informasi terkait driver perangkat, bukan proses.
wallyk
1
Jika pid Nadalah proses anak Anda, Anda dapat memastikan bahwa pid Nmasih mengacu pada proses yang sama (mungkin diakhiri) sampai Anda memanggil waitfungsi-keluarga di atasnya. Ini memastikan bahwa tidak ada ras.
R .. GitHub BERHENTI MEMBANTU ICE
Ada apa dengan banjir -1 dan tidak ada penjelasan?
R .. GitHub BERHENTI MEMBANTU ICE
2

Ketika Anda membaca dari file / proc, kernel memanggil fungsi yang telah didaftarkan sebelumnya sebagai fungsi "baca" untuk file proc itu. Lihat __proc_file_readfungsi di fs / proc / generic.c.

Oleh karena itu, keamanan proc read hanya seaman fungsi yang dipanggil kernel untuk memenuhi permintaan read. Jika fungsi itu benar mengunci semua data yang disentuhnya dan kembali kepada Anda dalam buffer, maka itu benar-benar aman untuk dibaca menggunakan fungsi itu. Karena file proc seperti yang digunakan untuk memuaskan permintaan baca ke / proc / net / tcp telah ada untuk sementara waktu dan telah menjalani peninjauan yang teliti, mereka hampir seaman yang Anda bisa minta. Faktanya, banyak utilitas Linux yang umum mengandalkan pembacaan dari sistem file proc dan memformat output dengan cara yang berbeda. (Dari atas kepala saya, saya pikir 'ps' dan 'netstat' melakukan ini).

Seperti biasa, Anda tidak harus mengambil kata saya untuk itu; Anda dapat melihat sumbernya untuk menenangkan ketakutan Anda. Dokumentasi berikut dari proc_net_tcp.txt memberi tahu Anda tempat "baca" berfungsi untuk / proc / net / tcp, sehingga Anda dapat melihat kode aktual yang dijalankan ketika Anda membaca dari file proc itu dan memverifikasi sendiri bahwa tidak ada mengunci bahaya.

Dokumen ini menjelaskan antarmuka / proc / net / tcp dan / proc / net / tcp6.
Perhatikan bahwa antarmuka ini tidak digunakan lagi karena mendukung tcp_diag. Antarmuka / proc ini memberikan informasi tentang koneksi TCP yang sedang aktif, dan diimplementasikan oleh tcp4_seq_show () di net / ipv4 / tcp_ipv4.c dan tcp6_seq_show () di net / ipv6 / tcp_ipv6.c.

kesehatan
sumber