Masalah Menggunakan Fungsi NtQuerySystemInformation dengan Argumen yang Tidak Didokumentasikan

Pagi hari itu dimulai dengan fakta bahwa "seandainya" pecah. Ungkapan ini pernah diciptakan oleh salah satu kolega saya yang mendemonstrasikan bagaimana debuggernya masuk ke blok if ketika melangkah melalui kode, sementara kondisi bahwa jika diperiksa benar-benar salah. Masalahnya pada saat itu ternyata sepele - ia menggunakan rilis yang dioptimalkan membangun, dan dalam skenario ini, kepercayaan pada debugging langkah-demi-langkah, tentu saja, tidak mungkin. Tetapi ungkapan “seandainya bangkrut” telah berakar dan telah digunakan di sini sejak saat itu untuk menunjukkan situasi di mana sesuatu yang begitu mendasar berhenti bekerja sehingga hampir tidak mempercayainya.

Jadi, hari itu, fungsi NtQuerySystemInformation mogok - salah satu fungsi terpenting dari OS Windows, yang mengembalikan informasi tentang proses, utas, deskriptor sistem, dll. Mengenai manfaat menggunakan fitur ini, saya pernah menulis artikel ini . Tetapi ternyata bahkan landasan suatu sistem kadang-kadang bisa gagal.

Jadi apa yang terjadi?

Untuk waktu yang cukup lama (untuk beberapa tahun sudah) kami menggunakan panggilan ke fungsi NtQuerySystemInformation dengan argumen SystemHandleInformation untuk mendapatkan informasi tentang semua deskriptor dalam sistem. Ya, argumen ini secara formal merujuk pada yang tidak berdokumen, tetapi jika Anda mulai mencari informasi tentang cara membuat daftar semua deskriptor di semua aplikasi Windows yang sedang berjalan, kombinasi NtQuerySystemInformation + SystemHandleInformation akan menjadi opsi yang paling sering diusulkan. Dan itu benar-benar berfungsi, pada semua OS yang dimulai dengan Windows NT.

Mengapa Anda perlu mencari deskriptor di semua proses? Ya, karena berbagai alasan. Utilitas seperti Process Hacker hanya menunjukkannya untuk tujuan informasi. Ada program yang melakukan ini untuk mencari sumber daya yang saat ini diblokir oleh seseorang (misalnya, file). Dan Anda dapat, misalnya, menemukan dalam proses asing sebuah mutex yang digunakan untuk mengizinkan peluncuran hanya satu salinan program, tutup dan biarkan dua contoh aplikasi semacam itu diluncurkan. Atau daftar deskriptor untuk menduplikasi mereka untuk mengatur kotak pasir. Secara umum, ada banyak tugas.

Saya tidak akan memberikan deskripsi lengkap tentang penghitungan deskriptor di sini, saya hanya akan mengatakan bahwa itu pada umumnya mirip dengan contoh umum, seperti ini :

while ((status = NtQuerySystemInformation( SystemHandleInformation, handleInfo, handleInfoSize, NULL )) == STATUS_INFO_LENGTH_MISMATCH) handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2); // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH. if (!NT_SUCCESS(status)) { printf("NtQuerySystemInformation failed!\n"); return 1; } for (i = 0; i < handleInfo->HandleCount; i++) { ... } 

Tapi kemudian saya meluncurkan aplikasi kita - dan tiba-tiba ternyata deskriptor yang saya butuhkan (dan saya tahu pasti itu ada!) Tidak ada dalam daftar yang dikembalikan oleh fungsi NtQuerySystemInformation (). Itu saja, mereka datang - "jika bangkrut".

Mencoba mereproduksi masalah pada komputer lain di kantor. Pada beberapa itu direproduksi, sebagian besar - tidak. Kami mencoba memahami bagaimana mereka yang direproduksi berbeda dari mereka yang semuanya baik. Versi Windows adalah sama di mana-mana, pembaruan, pembuatan program kami - semuanya identik. Tiba-tiba, seseorang memperhatikan bahwa semua laptop yang mereproduksi masalahnya berasal dari model yang sama. Ketidakcocokan perangkat keras? Tapi kenapa tiba-tiba sekarang, dulu bekerja ... Selain itu, ada laptop lain dari model yang sama di kantor yang bekerja sekarang. Bahkan versi driver perangkat dibandingkan - semuanya tampak sama. Tetapi semuanya bekerja pada beberapa laptop, tetapi tidak pada yang lain.

Menarik rambut di kepala berlangsung sekitar setengah hari, sampai saya tidak sengaja memperhatikan dua hal:

  1. Proses PID, yang biasanya nomor tiga, empat, atau lima digit di komputer saya karena alasan tertentu menjadi enam digit. Cukup aneh melihat PID tipe 780936. Saya belum pernah memperhatikan ini sebelumnya. Selain itu, jumlah proses yang berjalan cukup memadai (hingga seratus).
  2. Manajer tugas pada tab CPU menunjukkan jumlah total deskriptor dalam sistem - dan itu sangat besar, lebih dari 800.000.

Untuk aplikasi normal, adalah normal untuk membuka seratus atau dua deskriptor. Ya, seribu. Dengan penggunaan aktif, chrome dapat membuka sekitar 2000, Visual Studio dapat membuka 3.000 pada proyek-proyek besar. Tetapi siapa yang membuka 800.000? Untungnya, Process Hacker yang disebutkan sebelumnya memungkinkan Anda untuk menunjukkan jumlah deskriptor untuk setiap proses dan bahkan mengurutkan daftar proses berdasarkan jumlah deskriptor yang digunakan.

Dan apa yang kita lihat? Dan kita melihat sesuatu seperti gambar ini:



Saya harus mengatakan bahwa saya baru saja membuat tangkapan layar di atas, jadi yang pertama dalam daftar proses memiliki "total" sekitar 20.000 deskriptor. Dan kemudian, ketika saya melihat masalah untuk pertama kalinya, ada sekitar 650.000 di sana Dan siapa pahlawan kita? Bingo! Ini adalah proses SynTPEnhService.exe.

Dan kemudian seluruh teka-teki berkembang di kepalaku. SynTPEnhService.exe adalah bagian dari driver touchpad Synaptics. Itu diinstal hanya pada laptop model tertentu di kantor kami, di mana masalah terjadi. Pengamatan singkat menunjukkan bahwa setiap 5 detik proses ini memulai proses anak SynTPEnh.exe, yang ditutup setelah 1-2 detik. Pada saat yang sama, proses induk terus memegang deskriptor dari proses anak, yang mengarah pada kebocoran deskriptor. Satu per satu setiap 5 detik. Ini adalah 17.280 penjelas per hari. Biarkan komputer menyala selama seminggu dan sekarang Anda memiliki lebih dari seratus ribu deskriptor hang. Komputer pribadi saya tidak hidup kembali selama lebih dari sebulan - karenanya PID proses baru dengan angka di atas setengah juta. Ini juga menjelaskan mengapa masalah tersebut terjadi pada beberapa laptop di kantor kami, tetapi itu tidak terjadi pada laptop lain: beberapa rekan saya me-reboot PC mereka setiap hari, dan seseorang, seperti saya, membiarkannya dihidupkan untuk malam itu. .

Ngomong-ngomong, di tempat ini saya ingat bahwa saya sudah membaca tentang beberapa masalah dengan driver touchpad Synaptics. Setelah menggali sedikit, saya menemukan artikel ini ditulis oleh Bruce Dawson (banyak terjemahan dari artikelnya diterbitkan pada waktu yang berbeda di Habré, tetapi bukan yang spesifik ini). Di sana ia menjelaskan masalah kebocoran kehabisan memori karena restart tanpa henti dari proses SynTPEnh.exe, tetapi tidak mengatakan apa-apa tentang masalah kebocoran pegangan, jadi temuan saya masih berbeda dari itu.

Pemecahan masalah


Jadi, pengemudi touchpad "memakan" ratusan ribu deskriptor - dan jadi apa? Dan fakta bahwa fungsi NtQuerySystemInformation (SystemHandleInformation, ...) ditulis kembali pada zaman Windows NT memiliki (dan memiliki) beberapa buffer internal yang sangat terbatas. Saya tidak dapat menemukan indikasi ukurannya di mana pun, tetapi, jelas, itu tidak dirancang untuk sejuta deskriptor. Akibatnya, fungsi mengembalikan mereka "sebanyak yang mereka bisa", yang berarti bahwa di antara mereka mungkin ada atau tidak ada yang diinginkan.

Apa yang harus dilakukan Seperti yang dikatakan Rick dari serial animasi “Rick and Morty”: “Ketika Anda menciptakan teleportasi, Anda segera menemukan hal yang tidak menyenangkan: Anda adalah orang terakhir di alam semesta yang menciptakannya.” Ternyata, Microsoft menyadari masalah ini dengan buffer terbatas di NtQuerySystemInformation ketika memanggilnya dengan argumen SystemHandleInformation sudah 20 tahun yang lalu, dan karena itu, mulai dengan WindowsXP, mereka menambahkan fungsi NtQuerySystemInformation lain (dan juga tidak berdokumen) argumen SystemExtendedHandleInformation. Ketika Anda memanggil NtQuerySystemInformation (SystemExtendedHandleInformation, ...), semua deskriptor dalam sistem akan dikembalikan kepada Anda, tidak peduli berapa banyak yang ada. Ya, atau lebih tepatnya, saya tidak tahu ini dengan pasti, mungkin ada beberapa batasan untuk argumen ini, tetapi sudah pasti bahwa ia dapat mengembalikan 800.000 deskriptor dalam keadaan.

Di internet, Anda dapat menemukan contoh menggunakan SystemExtendedHandleInformation, misalnya, yang ini . Secara umum, semuanya serupa di sana, struktur lain hanya digunakan, dan itu saja.

Itu adalah cerita instruktif tentang penggunaan argumen tidak berdokumen dari OS Widnows, yang bisa sangat berguna, tetapi membutuhkan pengujian dan kesiapan yang cermat untuk masalah yang tidak standar.

Source: https://habr.com/ru/post/id433906/


All Articles