マルチスレッドのDouble CloseとSystem Call Hook
同僚が修正したweston関連のマルチスレッドコードを手に入れたところ、double closeが疑われた.私が確定してくれることを望んでいます.
修正したwestonでは、executive elfファイルが1つしかありません.source codeはありません.このようなhook要件には、一般的に3つの方法がありますが、それぞれ異なります. kernelのclose system callを変更する:カーネル をコンパイルできることが要求される wrap libcのclose():glibcコードを直接変更したり、ldの「--wrap=symbol」オプションを使用したりすることができます.少なくともコンパイルされたobjectが必要です.linkのときにwrap が必要です. LD_を使用PRELOAD、新しいダイナミックライブラリをhook/wrap closeに作成し、カーネルとlibcを変更する必要はなく、ソースコード も必要ありません.
カーネルでのSystem Callの変更
カーネルソースがあるので、第1の方法が第一選択になりました.double closeは、通常2回目のcloseではエラーが発生しますが、他のスレッドのfdを誤って閉じる場合がありますので、エラーを閉じるときにこのスレッドの名前、pidを印刷するだけでいいです.close system callのインプリメンテーションコードに行を追加します(ファイルはfs/file.cにあります):
ユーザ状態Hook
プログラムのコンパイルから実行への状態遷移に基づいて、以下の方法が考えられる. Linkのときwrap 実行前に、ローダldはLD_を行うPRELOAD
linkの場合ldの--wrap=symbolオプションを使用し、リンク時にすべてのsymbolを__に置き換えます.wrap_Symbol,libcにおける実際のsymbolは,_になる.real_Symbolですので、定義する必要があります.wrap_Symbol関数は、中で呼び出す必要がある場合があります.real_symbol.
LD_の場合PRELAAD、これはローダldの機能で、ldにはLD_などのデバッグ用の他の機能オプションもあります.DEBUG、これらはすべてman 8 ldを使用することができる.soで表示するか、ドキュメントThe LD_を表示します.DEBUG environment variable.
ユーザ状態Hookの注意点
SystemCallの表示方法の注意
LD_の使用にかかわらずPRELOADはやはりリンクを使う--wrap=symbol方式で、System CallがHookに住んでいない場合がありがちですが、このような場合はstraceを使ってプログラムのsystem callを見るのが一般的で、正しい方法はlstraceを使うべきです.これについては,2を参照した記事,および記事のコメントを見ることができる.
静的リンクのSystem Call
静的リンクの実行可能ファイルは、他のsoでシンボルを解析したり検索する必要がないため、ldを必要とせずに直接実行する.so(ローダ)なので、これらの方法は適用されません.StackOverFlowで回答したCommnet:How to hook ALL linux system calls during a binary executionを参照
hookを使用した参照項目
また、valgrind wrapやmalloc/free、socksify wrapのconnect呼び出しなど、ユーザ状態のHookを使用してユーザ状態プログラムの動作をデバッグし、監視するプログラムもたくさんあります.そのため、これらのコードは参考に値します.
なぜdouble closeはマルチスレッドで問題があるのですか?
なぜなら、同じプロセスのすべてのスレッドがfdを共有しているからであり、すなわちその範囲は同じであり、みんながopenのfile/socketで得たfdの値の範囲は同じ範囲内であり、使用済みのfdはcloseの後(reference count==0)に回収され、再利用されるからである.
linuxでは、ulimit-aを使用すると、userspaceで使用できるfdの範囲が表示されます.
したがって、同時に最も多く開くことができるファイル/sockectの数は1024であり、この値には2つの方法があります.
①コマンドラインでulimit-n 4096(ここでは4096は設定後の使用可能なハンドル数)を使用して、1つのプロセスで使用可能な最大ハンドル数を構成できます.②プログラムでsetrlimitというSystem Callを呼び出して、プロセスで使用できる最大ハンドル数を構成することができます.参照コードは以下の通りです.valgrindのtestコードも参照できます.
double closeを回避する方法
ソースコードがある場合は、参照リンク1の推奨に従って、closeが呼び出された場所に戻り値判断をコードに加算する必要があります.man closeでは、次のように表示されます.
closeは呼び出しのたびに成功することを保証することはできません.これはfreeとは違います.
リファレンス Not so obvious multi-thread programming specific bugs How to Wrap a System Call (Libc Function) in Linux
コードが不完全な場合は、次の手順に従います.http://www.hexiongjun.com/?p=283
修正したwestonでは、executive elfファイルが1つしかありません.source codeはありません.このようなhook要件には、一般的に3つの方法がありますが、それぞれ異なります.
カーネルでのSystem Callの変更
カーネルソースがあるので、第1の方法が第一選択になりました.double closeは、通常2回目のcloseではエラーが発生しますが、他のスレッドのfdを誤って閉じる場合がありますので、エラーを閉じるときにこのスレッドの名前、pidを印刷するだけでいいです.close system callのインプリメンテーションコードに行を追加します(ファイルはfs/file.cにあります):
int __close_fd(struct files_struct *files, unsigned fd)
{
struct file *file;
struct fdtable *fdt;
spin_lock(&files->file_lock);
fdt = files_fdtable(files);
if (fd >= fdt->max_fds)
goto out_unlock;
file = fdt->fd[fd];
if (!file)
goto out_unlock;
rcu_assign_pointer(fdt->fd[fd], NULL);
__clear_close_on_exec(fd, fdt);
__put_unused_fd(files, fd);
spin_unlock(&files->file_lock);
return filp_close(file, files);
out_unlock:
spin_unlock(&files->file_lock);
printk(KERN_ERR "---process id/pid[%d] name=%s, close fd[%d], failed with EBADF
", current->pid,current->comm,fd);
return -EBADF;
}
ユーザ状態Hook
プログラムのコンパイルから実行への状態遷移に基づいて、以下の方法が考えられる.
linkの場合ldの--wrap=symbolオプションを使用し、リンク時にすべてのsymbolを__に置き換えます.wrap_Symbol,libcにおける実際のsymbolは,_になる.real_Symbolですので、定義する必要があります.wrap_Symbol関数は、中で呼び出す必要がある場合があります.real_symbol.
LD_の場合PRELAAD、これはローダldの機能で、ldにはLD_などのデバッグ用の他の機能オプションもあります.DEBUG、これらはすべてman 8 ldを使用することができる.soで表示するか、ドキュメントThe LD_を表示します.DEBUG environment variable.
ユーザ状態Hookの注意点
SystemCallの表示方法の注意
LD_の使用にかかわらずPRELOADはやはりリンクを使う--wrap=symbol方式で、System CallがHookに住んでいない場合がありがちですが、このような場合はstraceを使ってプログラムのsystem callを見るのが一般的で、正しい方法はlstraceを使うべきです.これについては,2を参照した記事,および記事のコメントを見ることができる.
静的リンクのSystem Call
静的リンクの実行可能ファイルは、他のsoでシンボルを解析したり検索する必要がないため、ldを必要とせずに直接実行する.so(ローダ)なので、これらの方法は適用されません.StackOverFlowで回答したCommnet:How to hook ALL linux system calls during a binary executionを参照
hookを使用した参照項目
また、valgrind wrapやmalloc/free、socksify wrapのconnect呼び出しなど、ユーザ状態のHookを使用してユーザ状態プログラムの動作をデバッグし、監視するプログラムもたくさんあります.そのため、これらのコードは参考に値します.
なぜdouble closeはマルチスレッドで問題があるのですか?
なぜなら、同じプロセスのすべてのスレッドがfdを共有しているからであり、すなわちその範囲は同じであり、みんながopenのfile/socketで得たfdの値の範囲は同じ範囲内であり、使用済みのfdはcloseの後(reference count==0)に回収され、再利用されるからである.
linuxでは、ulimit-aを使用すると、userspaceで使用できるfdの範囲が表示されます.
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 94963
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 94963
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
したがって、同時に最も多く開くことができるファイル/sockectの数は1024であり、この値には2つの方法があります.
①コマンドラインでulimit-n 4096(ここでは4096は設定後の使用可能なハンドル数)を使用して、1つのプロセスで使用可能な最大ハンドル数を構成できます.②プログラムでsetrlimitというSystem Callを呼び出して、プロセスで使用できる最大ハンドル数を構成することができます.参照コードは以下の通りです.valgrindのtestコードも参照できます.
#include <sys/time.h>
#include <sys/resource.h>
struct rlimit rlp;
rlp.rlim_cur = 4096;
setrlimit(RLIMIT_NOFILE, &rlp);
double closeを回避する方法
ソースコードがある場合は、参照リンク1の推奨に従って、closeが呼び出された場所に戻り値判断をコードに加算する必要があります.man closeでは、次のように表示されます.
ERRORS
EBADF fd isn't a valid open file descriptor.
EINTR The close() call was interrupted by a signal; see signal(7).
EIO An I/O error occurred.
closeは呼び出しのたびに成功することを保証することはできません.これはfreeとは違います.
リファレンス
コードが不完全な場合は、次の手順に従います.http://www.hexiongjun.com/?p=283