マルチスレッドの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にあります):
    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のとき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の範囲が表示されます.
    $ 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とは違います.
    リファレンス
  • Not so obvious multi-thread programming specific bugs
  • How to Wrap a System Call (Libc Function) in Linux


  • コードが不完全な場合は、次の手順に従います.http://www.hexiongjun.com/?p=283