Doxygenを使ったドキュメント生成ではじめにやること。

はじめに

 Doxygenを使って、ドキュメントを生成するときに、はじめにやることを記載しておく。

概要

  1. 設定ファイルを生成する。
  2. 設定ファイルを編集する。
  3. ソースファイルにドキュメント付けをする。
  4. doxygenを実行する。
  5. 生成されたドキュメントを確認する。

設定ファイルを生成する

ソースがあるカレントディレクトリで、以下のコマンドを実行する。
実行後、「Doxygen」ファイルがカレントディレクトリに生成されるはずである。

 $ doxygen -g

設定ファイルを編集する

「EXTRACT_ALL」をYESにする。

EXTRACT_ALL            = YES  

ソースファイルにドキュメント付けをする

簡単に、ドキュメント付けしたものを以下に示す。

/*!  @brief  main本体.
 *   @param  [in]    argc
 *   @param  [in]    *argv
 *   @return 成否を返す.
 *   @retval 0   正常終了.
 *   @retval 1   異常終了.
 */
int
main(   int     argc,
        char    *argv )
{

    usage( argc, argv );

    // cat 本体
    _cat( argc, argv );

    return 0;
}

doxygenを実行する

以下のコマンドを実行する。

 $ doxygen Doxygen

生成されたドキュメントを確認する

カレントディレクトリに作成された「html」ディレクトリ内の「index.html」ファイルを
ブラウザを起動して、確認する。

file://xxx/html/index.html  ★ ブラウザにURLを入力

なぜか、「コマンドが見つからない」とエラーが出た、そのときは。。。

はじめに

本記事は、シェルスクリプトに関する防備録である。
こんなミスをするとは。。。

問題

シェルスクリプト内で、seqコマンドが使えなくなった。
(コマンドが見つからないと言われた。)

原因

シェルスクリプト内で環境変数PATHを編集していた。

解決策

PATH以外の変数名に変える。

Makefile.amでのソースファイル指定時の注意点

概要

Makefile.am(Autotools)を使って、バイナリを生成する場合、1文字違いで想定と違う動きをする。

Makefile.amでのソースファイル指定

Makefile.amには、生成バイナリ名とそれを構成するソースファイルを以下のように指定する。

bin_PROGRAMS=bin_name
bin_name_SOURCES=xx.c yy.c

xx.c と yy.cの2つのファイルから、bin_nameを作る。

しかしここで、
bin_name_SOURCES
bin_name_SOURCE にしたらどうなるか。
(↑SOURCES を複数形ではなく、単数形にする)

結果、xx.c から bin_name を作ろうとする。
( yy.c は、バイナリ生成に使われない。)

まとめ

Makefile.amにおいて、ソースを記述するときは、xx_SOURCES のように複数形にする。

自作の関数に対して、簡易コールグラフを出力する。

はじめに

本記事は、自作の関数(システムコールや、ライブラリ関数ではない)を呼び出した際に、その関数名を出力できないか調べてみた結果を記載したものである。
今回作成したものは、こちらから。

結論

GCCにおけるオプションの一つに「-finstrument-functions」というものがあり、これと+αでやりたいことができる。

GCCの-finstrument-functionsオプションとは何か

各関数で、呼び出された直後とreturn前 に自作の関数(ここでは、関数名を出力する関数)が呼べるようになる。
(一つ一つの関数に追加するのは面倒だから便利ということ。)

使い方

まだ、使い始めということもあり、簡単なサンプルしか示せないが、載せる。

  • __cyg_profile_func_enter()、__cyg_profile_func_exit() 関数を作成する。

   この関数名は固定である。
   今回やりたいことは、関数名の出力だが、その出力関数をこの2つの関数に記述する。
   (下記にtrace.cというファイルを載せる。詳細はそこで。)

  • 共有ライブラリ化する

   上で作成したソース(trace.c)を共有ライブラリ化する。

  • LD_PRELOAD で実行する。

   関数名を出力したい実行ファイルは、もうすでに作成済みであるとして、LD_PRELOADを使って、実行する。
    実行例:
     $ LD_PRELOAD=./libtrace.so ./main(実行ファイル)

trace.c

__cyg_profile_func_enter()、__cyg_profile_func_exit()の部分を載せる。

andre@andre-VirtualBox:~/work/ftrace$ cat trace.c
#define _GNU_SOURCE
#include<dlfcn.h>
#include<stdio.h>
#include<string.h>
#define MAX_SIZE 256
const char* addr2name               (   void* address,
                                        char* funcname      );
void        __cyg_profile_func_enter(   void* func_address,
                                        void* call_site     );
void        __cyg_profile_func_exit (   void* func_address,
                                        void* call_site     );
void        common_func             (   void* func_address,
                                        const char *fmt     );

/* ### 関数の呼び出し直後に実行される ### */
void __cyg_profile_func_enter(   void* func_address,
                                 void* call_site     )
{
    common_func( func_address, "enter =>" );
}

/* ### 関数の return 直前に実行される ### */
void __cyg_profile_func_exit (  void* func_address,
                                void* call_site     )
{
    common_func( func_address, "<= exit" );
}

void common_func( void* func_address, const char *fmt )
{
    char        _funcname[MAX_SIZE];
    const char  *_ret = NULL;

    memset( _funcname, 0x00, sizeof( _funcname ) );

    _ret = addr2name( func_address, _funcname );
    if( NULL != _ret )
    {
        printf( "%s _funcname = %s.\n", fmt,  _funcname );
    }
}

/* ### 関数ポインタのアドレスを(人が読める)関数名へ変換する ### */
const char*
addr2name( void* address, char* funcname )
{
    Dl_info dli;
    memset( &dli, 0x00, sizeof( dli ) );

    /* ### この dladdr() で関数名へ変換している ### */
    if( 0 != dladdr( address, &dli ) )
    {
        strncpy( funcname, dli.dli_sname, MAX_SIZE );
        return dli.dli_sname;
    }
    return NULL;
}

実行ファイルのソース

hello!とだけ出力する実行ファイルである。

andre@andre-VirtualBox:~/work/ftrace$ cat main.c
#include<stdio.h>

/* Proto Type */
void hello_world( void );

void
hello_world(void)
{
    printf( "hello!\n" );
}

int
main(void)
{
    hello_world();
    return 0;
}

ビルド&実行

今マイブームであるSConsを使って、ビルドしてみる。
SConstructという名前のファイルに、実行ファイルと共有ライブラリの2つを作成する命令を記述し、[scons]とだけ入力し、コマンド実行する。
http://www25.atwiki.jp/ovlivion-air/ が簡潔で、わかりやすい。
SConstruct

andre@andre-VirtualBox:~/work/ftrace$ cat SConstruct
Program('main.c', CFLAGS='-fPIC -finstrument-functions')
SharedLibrary('trace', Split('trace.c'), LIBS='dl', CFLAGS='-fPIC -DMACRO=_GNU_SOURCE')

scons

andre@andre-VirtualBox:~/work/ftrace$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o trace.os -c -fPIC -DMACRO=_GNU_SOURCE -fPIC trace.c
scons: done building targets.

実行。。。

andre@andre-VirtualBox:~/work/ftrace$ LD_PRELOAD=./libtrace.so ./main
enter => _funcname = main.
enter => _funcname = hello_world.
hello!
<= exit _funcname = hello_world.
<= exit _funcname = main.

その他

記事を書いている途中に発見した、同じことをやっているページを発見。こちらのほうがちょっと詳しく書いてある。
http://alohakun.blog7.fc2.com/blog-entry-758.html

参考サイト

http://d.hatena.ne.jp/torutk/20090126/p1
→やりたいことをC++でやっていたページ
http://www.nxmnpg.com/ja/3/dladdr
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/dlsym.3.html
→dladdr()について
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/feature_test_macros.7.html
→「#define _GNU_SOURCE」を一番上に書くべきであることをなんとなくわからせてくれたページ。

Flawfinderを試す。〜セキュアコーディングを目指して〜

はじめに

コーディング中に、バッファオーバフローをどうやって未然に防ぐか。
Flawfinderというツールがあるとわかったので、試してみた。

インストール

sudo apt-get install flawfinder

サンプルソース

本家にあるサンプルソースに対し、ツールを実行してみた。

andre@andre-VirtualBox:~/work/flawfinder$ cat -n test.c
     1	/* Test flawfinder.  This program won't compile or run; that's not necessary
     2	   for this to be a useful test. */
     3	
     4	#include <stdio.h>
     5	#define hello(x) goodbye(x)
     6	#define WOKKA "stuff"
     7	
     8	main() {
     9	 printf("hello\n");
    10	}
    11	
    12	/* This is a strcpy test. */
    13	
    14	int demo(char *a, char *b) {
    15	 strcpy(a, "\n"); // Did this work?
    16	 strcpy(a, gettext("Hello there")); // Did this work?
    17	 strcpy(b, a);
    18	 sprintf(s, "\n");
    19	 sprintf(s, "hello");
    20	 sprintf(s, "hello %s", bug);
    21	 sprintf(s, gettext("hello %s"), bug);
    22	 sprintf(s, unknown, bug);
    23	 printf(bf, x);
    24	 scanf("%d", &x);
    25	 scanf("%s", s);
    26	 scanf("%10s", s);
    27	 scanf("%s", s);
    28	 gets(f); // Flawfinder: ignore
    29	 printf("\\");
    30	 /* Flawfinder: ignore */
    31	 gets(f);
    32	 gets(f);
    33	 /* These are okay, but flawfinder version < 0.20 incorrectly used
    34	    the first parameter as the parameter for the format string */
    35	 syslog(LOG_ERR,"cannot open config file (%s): %s",filename,strerror(errno))
    36	 syslog(LOG_CRIT,"malloc() failed");
    37	 /* But this one SHOULD trigger a warning. */
    38	 syslog(LOG_ERR, attacker_string);
    39	
    40	}
    41	
    42	
    43	
    44	demo2() {
    45	  char d[20];
    46	  char s[20];
    47	  int n;
    48	
    49	  _mbscpy(d,s); /* like strcpy, this doesn't check for buffer overflow */
    50	  memcpy(d,s);
    51	  CopyMemory(d,s);
    52	  lstrcat(d,s);
    53	  strncpy(d,s);
    54	  _tcsncpy(d,s);
    55	  strncat(d,s,10);
    56	  strncat(d,s,sizeof(d)); /* Misuse - this should be flagged as riskier. */
    57	  _tcsncat(d,s,sizeof(d)); /* Misuse - flag as riskier */
    58	  n = strlen(d);
    59	  /* This is wrong, and should be flagged as risky: */
    60	  MultiByteToWideChar(CP_ACP,0,szName,-1,wszUserName,sizeof(wszUserName));
    61	  /* This is also wrong, and should be flagged as risky: */
    62	  MultiByteToWideChar(CP_ACP,0,szName,-1,wszUserName,sizeof wszUserName);
    63	  /* This is much better: */
    64	  MultiByteToWideChar(CP_ACP,0,szName,-1,wszUserName,sizeof(wszUserName)/sizeof(wszUserName[0]));
    65	  /* This is much better: */
    66	  MultiByteToWideChar(CP_ACP,0,szName,-1,wszUserName,sizeof wszUserName /sizeof(wszUserName[0]));
    67	  /* This is an example of bad code - the third paramer is NULL, so it creates
    68	     a NULL ACL.  Note that Flawfinder can't detect when a
    69	     SECURITY_DESCRIPTOR structure is manually created with a NULL value
    70	     as the ACL; doing so would require a tool that handles C/C++
    71	     and knows about types more that flawfinder currently does.
    72	     Anyway, this needs to be detected: */
    73	  SetSecurityDescriptorDacl(&sd,TRUE,NULL,FALSE);
    74	  /* This one is a bad idea - first param shouldn't be NULL */
    75	  CreateProcess(NULL, "C:\\Program Files\\GoodGuy\\GoodGuy.exe -x", "");
    76	  /* Test interaction of quote characters */
    77	  printf("%c\n", 'x');
    78	  printf("%c\n", '"');
    79	  printf("%c\n", '\"');
    80	  printf("%c\n", '\'');
    81	  printf("%c\n", '\177');
    82	  printf("%c\n", '\xfe');
    83	  printf("%c\n", '\xd');
    84	  printf("%c\n", '\n');
    85	  printf("%c\n", '\\');
    86	  printf("%c\n", "'");
    87	}
    88	
    89	
    90	int getopt_example(int argc,char *argv[]) {
    91	    while ((optc = getopt_long (argc, argv, "a",longopts, NULL )) != EOF) {
    92	    }
    93	}
    94	
    95	int testfile() {
    96	  FILE *f;
    97	  f = fopen("/etc/passwd", "r"); 
    98	  fclose(f);
    99	}
   100	
   101	/* Regression test: handle \\\n after end of string */
   102	
   103	#define assert(x) {\
   104	 if (!(x)) {\
   105	 fprintf(stderr,"Assertion failed.\n"\
   106	 "File: %s\nLine: %d\n"\
   107	 "Assertion: %s\n\n"\
   108	 ,__FILE__,__LINE__,#x);\
   109	 exit(1);\
   110	 };\
   111	 }
   112	
   113	int accesstest() {
   114	  int access = 0; /* Not a function call.  Should be caught by the
   115	                     false positive test, and NOT labelled as a problem. */
   116	}

実行結果

andre@andre-VirtualBox:~/work/flawfinder$ cat flawfinder_out.txt
Flawfinder version 1.27, (C) 2001-2004 David A. Wheeler.
Number of dangerous functions in C/C++ ruleset: 160
Examining test.c
test.c:32:  [5] (buffer) gets:
  Does not check for buffer overflows. Use fgets() instead.
   ★↑バッファオーバフローのためのチェックしていない。gets()->fgets()へ置き換えるべき。
test.c:56:  [5] (buffer) strncat:
  Easily used incorrectly (e.g., incorrectly computing the correct
  maximum size to add). Consider strlcat or automatically resizing strings.
   ★↑誤って使用されやすい。
   ★↑strncat()ではなく、strlcat()もしくは、自動的にリサイズする文字列型?を考えるべき。
  Risk is high; the length parameter appears to be a constant, instead of
  computing the number of characters left.
test.c:57:  [5] (buffer) _tcsncat:
  Easily used incorrectly (e.g., incorrectly computing the correct
  maximum size to add). Consider strlcat or automatically resizing strings.
  Risk is high; the length parameter appears to be a constant, instead of
  computing the number of characters left.
test.c:60:  [5] (buffer) MultiByteToWideChar:
  Requires maximum length in CHARACTERS, not bytes. Risk is high, it
  appears that the size is given as bytes, but the function requires size as
  characters.
    ★↑バイト数ではなく、文字の最大長が引数に要求されている。高リスクであり、バイト数を与えるべき。
test.c:62:  [5] (buffer) MultiByteToWideChar:
  Requires maximum length in CHARACTERS, not bytes. Risk is high, it
  appears that the size is given as bytes, but the function requires size as
  characters.
test.c:73:  [5] (misc) SetSecurityDescriptorDacl:
  Never create NULL ACLs; an attacker can set it to Everyone (Deny All
  Access), which would even forbid administrator access. 
test.c:73:  [5] (misc) SetSecurityDescriptorDacl:
  Never create NULL ACLs; an attacker can set it to Everyone (Deny All
  Access), which would even forbid administrator access. 
test.c:17:  [4] (buffer) strcpy:
  Does not check for buffer overflows when copying to destination.
    ★↑連結元へのコピー時にバッファオーバフローが考慮されていない。
  Consider using strncpy or strlcpy (warning, strncpy is easily misused).
    ★strncpy か strlcpyへの代替を考慮すべき。( 警告:strncpyは誤使用しやすい) 
test.c:20:  [4] (buffer) sprintf:
  Does not check for buffer overflows. Use snprintf or vsnprintf. 
    ★バッファオーバフローが考慮されていない。snprintf か vsnprintfを使うべき。
test.c:21:  [4] (buffer) sprintf:
  Does not check for buffer overflows. Use snprintf or vsnprintf. 
test.c:22:  [4] (format) sprintf:
  Potential format string problem. Make format string constant. 
    ★書式部分が文字列ではない可能性あり。文字列定数であるべき。
test.c:23:  [4] (format) printf:
  If format strings can be influenced by an attacker, they can be
  exploited. Use a constant for the format specification. 
    ★書式部分は、クラッカーによって悪用されることがしばしばある。そのため、書式部分は文字定数を使うべき。
test.c:25:  [4] (buffer) scanf:
  The scanf() family's %s operation, without a limit specification,
  permits buffer overflows. Specify a limit to %s, or use a different input
  function. 
    ★制限のない%s書式は、バッファオーバフローを許してしまう。上限制限(%5s等)をつけるか、ほかの関数を使用するべき。
test.c:27:  [4] (buffer) scanf:
  The scanf() family's %s operation, without a limit specification,
  permits buffer overflows. Specify a limit to %s, or use a different input
  function. 
test.c:38:  [4] (format) syslog:
  If syslog's format strings can be influenced by an attacker, they can
  be exploited. Use a constant format string for syslog. 
test.c:49:  [4] (buffer) _mbscpy:
  Does not check for buffer overflows when copying to destination.
  Consider using a function version that stops copying at the end of the
  buffer. 
    ★コピー先へのコピー時、バッファオーバフローが考慮されていない。
    ★バッファの終端でコピーを止める関数を使うことを考えるべき。
test.c:52:  [4] (buffer) lstrcat:
  Does not check for buffer overflows when concatenating to destination.
    ★結合先への文字列結合時、バッファオーバフローが考慮されていない。 
test.c:75:  [3] (shell) CreateProcess:
  This causes a new process to execute and is difficult to use safely.
  Specify the application path in the first argument, NOT as part of the
  second, or embedded spaces could allow an attacker to force a different
  program to run. 
test.c:75:  [3] (shell) CreateProcess:
  This causes a new process to execute and is difficult to use safely.
  Specify the application path in the first argument, NOT as part of the
  second, or embedded spaces could allow an attacker to force a different
  program to run. 
test.c:91:  [3] (buffer) getopt_long:
  Some older implementations do not protect against internal buffer
  overflows . Check implementation on installation, or limit the size of all
  string inputs. 
    ★関数内部でのバッファオーバフローに対して、保護されていない。
    ★インストール時に実装を確認するか、入力文字列すべてのサイズに制限をかけるべき。
test.c:16:  [2] (buffer) strcpy:
  Does not check for buffer overflows when copying to destination.
  Consider using strncpy or strlcpy (warning, strncpy is easily misused). Risk
  is low because the source is a constant string.
    ★コピー先へのコピー時、バッファオーバフローが考慮されていない。
    ★strncpy か strlcpy の使用を考えるべき(警告:strncpyは誤使用されやすい)
    ★コピー元が文字列定数であるため、低リスクである。
test.c:19:  [2] (buffer) sprintf:
  Does not check for buffer overflows. Use snprintf or vsnprintf. Risk
  is low because the source has a constant maximum length.
    ★バッファオーバフローを考慮すべきである。snprintf か vsnprintfを使うべきである。
    ★文字列定数が指定されており、最大長が決まっているため、低リスクである。
test.c:45:  [2] (buffer) char:
  Statically-sized arrays can be overflowed. Perform bounds checking,
  use functions that limit length, or ensure that the size is larger than
  the maximum possible length.
     ★静的サイズの配列がオーバーフローすることができます。
     ★境界チェックを実行し、長さを制限する機能を使用するか、
     ★またはサイズが使用可能な最大長より大きいことを確認してください。( by google翻訳 )
test.c:46:  [2] (buffer) char:
  Statically-sized arrays can be overflowed. Perform bounds checking,
  use functions that limit length, or ensure that the size is larger than
  the maximum possible length. 
test.c:50:  [2] (buffer) memcpy:
  Does not check for buffer overflows when copying to destination. Make
  sure destination can always hold the source data. 
     ★コピー先へのコピー時、バッファオーバフローを考慮すべき。
     ★コピー元のデータが、コピー先へ格納できるかいつも確認することが必要である。
test.c:51:  [2] (buffer) CopyMemory:
  Does not check for buffer overflows when copying to destination. Make
  sure destination can always hold the source data. 
test.c:97:  [2] (misc) fopen:
  Check when opening files - can an attacker redirect it (via symlinks),
  force the opening of special file type (e.g., device files), move
  things around to create a race condition, control its ancestors, or change
  its contents?. 
test.c:15:  [1] (buffer) strcpy:
  Does not check for buffer overflows when copying to destination.
  Consider using strncpy or strlcpy (warning, strncpy is easily misused). Risk
  is low because the source is a constant character.
test.c:18:  [1] (buffer) sprintf:
  Does not check for buffer overflows. Use snprintf or vsnprintf. Risk
  is low because the source is a constant character.
test.c:26:  [1] (buffer) scanf:
  it's unclear if the %s limit in the format string is small enough.
  Check that the limit is sufficiently small, or use a different input
  function. 
test.c:53:  [1] (buffer) strncpy:
  Easily used incorrectly; doesn't always \0-terminate or check for
  invalid pointers. 
test.c:54:  [1] (buffer) _tcsncpy:
  Easily used incorrectly; doesn't always \0-terminate or check for
  invalid pointers. 
test.c:55:  [1] (buffer) strncat:
  Easily used incorrectly (e.g., incorrectly computing the correct
  maximum size to add). Consider strlcat or automatically resizing strings. 
test.c:58:  [1] (buffer) strlen:
  Does not handle strings that are not \0-terminated (it could cause a
  crash if unprotected). 
    ★NULL終端がない文字列かもしれない。クラッシュするかも…
test.c:64:  [1] (buffer) MultiByteToWideChar:
  Requires maximum length in CHARACTERS, not bytes. Risk is very low,
  the length appears to be in characters not bytes.
test.c:66:  [1] (buffer) MultiByteToWideChar:
  Requires maximum length in CHARACTERS, not bytes. Risk is very low,
  the length appears to be in characters not bytes.

Hits = 36
Lines analyzed = 116 in 0.54 seconds (3259 lines/second)
Physical Source Lines of Code (SLOC) = 80
Hits@level = [0]   0 [1]   9 [2]   7 [3]   3 [4]  10 [5]   7
Hits@level+ = [0+]  36 [1+]  36 [2+]  27 [3+]  20 [4+]  17 [5+]   7
Hits/KSLOC@level+ = [0+] 450 [1+] 450 [2+] 337.5 [3+] 250 [4+] 212.5 [5+] 87.5
Suppressed hits = 2 (use --neverignore to show them)
Minimum risk level = 1
Not every hit is necessarily a security vulnerability.
There may be other security vulnerabilities; review your code!
(※ 訳しづらいものorできなかったものは、訳していない)

考察

独自の関数(lstrcat等)についても警告を出すようだ。関数の文字列から、そこらへんは判断しているみたい。
strncpyやstrncatではなく、もっと安全なstrlcpyやstrlcatがあるとは知らなかった。

Helgrindのサンプルを試す

はじめに

マルチスレッドでプログラムを動かすことが多くある。性能向上のために。Helgrindは、マルチスレッドでのバグを発見するために使用するツールである。今回は、このツールを動かす。簡単なサンプルを使用して。

今回使用したサンプルはこちら

なお、valgrindのバージョンは3.8.1である。

サンプルソース

下のサンプルソースは、本家からコピーしたもの。
通常、複数のスレッドが同一リソースを使用する場合、排他ロックするべきだが、していない。
したがって、不具合を生む可能性がある。

andre@andre-VirtualBox:~/work/helgrind$ cat main.c
#include <pthread.h>

int var = 0;

void* child_fn ( void* arg ) {
    var++; /* Unprotected relative to parent */ /* this is line 6 */   ★リソースvarが競合している。
    return NULL;
}

int main ( void ) {
    pthread_t child;
    pthread_create(&child, NULL, child_fn, NULL);
    var++; /* Unprotected relative to child */ /* this is line 13 */   ★リソースvarが競合している。
    pthread_join(child, NULL);
    return 0;
}

Helgrindを動かす。

Helgrindを動かした結果を下記に示す。

andre@andre-VirtualBox:~/work/helgrind$ valgrind --tool=helgrind ./test_main > no_history.txt 2>&1
andre@andre-VirtualBox:~/work/helgrind$ cat no_history.txt 
==11250== Helgrind, a thread error detector
==11250== Copyright (C) 2007-2012, and GNU GPL'd, by OpenWorks LLP et al.
==11250== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11250== Command: ./test_main
==11250== 
==11250== ---Thread-Announcement------------------------------------------
==11250== 
==11250== Thread #1 is the program's root thread            ★#1 はmainスレッド
==11250== 
==11250== ---Thread-Announcement------------------------------------------
==11250== 
==11250== Thread #2 was created
==11250==    at 0x41340B8: clone (clone.S:111)              ★#2 はchild_fnスレッド(生成されたスレッドである)
==11250== 
==11250== ----------------------------------------------------------------
==11250== 
==11250== Possible data race during read of size 4 at 0x804A020 by thread #1       ★#1 は4バイトの競合リソースを読み込んだ。
==11250== Locks held: none                                                         ★ ロック状態:ロックなし
==11250==    at 0x8048419: main (main.c:13)
==11250== 
==11250== This conflicts with a previous write of size 4 by thread #2              ★#2 は4バイトの競合リソースを読み込んだ。
==11250== Locks held: none                                                         ★ ロック状態:ロックなし
==11250==    at 0x80484E0: child_fn (main.c:6)
==11250==    by 0x404CD30: start_thread (pthread_create.c:304)
==11250==    by 0x41340CD: clone (clone.S:130)
==11250== 
==11250== ----------------------------------------------------------------
==11250== 
==11250== Possible data race during write of size 4 at 0x804A020 by thread #1      ★#1 は4バイトの競合リソースに書き込んだ。
==11250== Locks held: none                                                         ★ ロック状態:ロックなし
==11250==    at 0x8048419: main (main.c:13)                                        ★ 13行目が競合している
==11250== 
==11250== This conflicts with a previous write of size 4 by thread #2              ★#2 は4バイトの競合リソースに書き込んだ。
==11250== Locks held: none                                                         ★ ロック状態:ロックなし
==11250==    at 0x80484E0: child_fn (main.c:6)                                     ★ 6行目が競合している
==11250==    by 0x404CD30: start_thread (pthread_create.c:304)
==11250==    by 0x41340CD: clone (clone.S:130)
==11250== 
==11250== 
==11250== For counts of detected and suppressed errors, rerun with: -v
==11250== Use --history-level=approx or =none to gain increased speed, at
==11250== the cost of reduced accuracy of conflicting-access information
==11250== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

その他のオプションを試す。

上記Helgrind実行結果の最後4行にあるオプションを試す。

==11250== For counts of detected and suppressed errors, rerun with: ★-v★
==11250== Use ★--history-level=approx or =none★ to gain increased speed, at
==11250== the cost of reduced accuracy of conflicting-access information
==11250== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
--history-level=approx を試す。

approx は、approximateの略語みたいだ。下記の実行結果を見ると、もう少し詳細な動き(リソースを触った順番)がわかるようだ。

approximate:おおよその,ほぼ正確な

andre@andre-VirtualBox:~/work/helgrind$ valgrind --tool=helgrind --history-level=approx ./test_main >history_approx.txt 2>&1
andre@andre-VirtualBox:~/work/helgrind$ cat history_approx.txt 
==11283== Helgrind, a thread error detector
==11283== Copyright (C) 2007-2012, and GNU GPL'd, by OpenWorks LLP et al.
==11283== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11283== Command: ./test_main
==11283== 
==11283== ---Thread-Announcement------------------------------------------
==11283== 
==11283== Thread #1 is the program's root thread         ★#1 はmainスレッド
==11283== 
==11283== ---Thread-Announcement------------------------------------------
==11283== 
==11283== Thread #2 was created
==11283==    at 0x41340B8: clone (clone.S:111)           ★#2 はchild_fnスレッド
==11283== 
==11283== ----------------------------------------------------------------
==11283== 
==11283== Possible data race during read of size 4 at 0x804A020 by thread #1
==11283== Locks held: none
==11283==    at 0x8048419: main (main.c:13)
==11283==  This conflicts with a previous access by thread #2, after    ★リソースを触った順番(#2 -> #1)が書かれているようだ。
==11283==    (the start of the thread)
==11283==  but before
==11283==    at 0x4130779: madvise (syscall-template.S:82)
==11283==    by 0x41340CD: clone (clone.S:130)
==11283== 
==11283== ----------------------------------------------------------------
==11283== 
==11283== Possible data race during write of size 4 at 0x804A020 by thread #1
==11283== Locks held: none
==11283==    at 0x8048419: main (main.c:13)
==11283==  This conflicts with a previous access by thread #2, after    ★リソースを触った順番(#2 -> #1)が書かれているようだ。
==11283==    (the start of the thread)
==11283==  but before
==11283==    at 0x4130779: madvise (syscall-template.S:82)
==11283==    by 0x41340CD: clone (clone.S:130)
==11283== 
==11283== 
==11283== For counts of detected and suppressed errors, rerun with: -v
==11283== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
--history-level=none

よくわからない。

andre@andre-VirtualBox:~/work/helgrind$ valgrind --tool=helgrind --history-level=none ./test_main >history_none.txt 2>&1
andre@andre-VirtualBox:~/work/helgrind$ cat history_none.txt 
==11299== Helgrind, a thread error detector
==11299== Copyright (C) 2007-2012, and GNU GPL'd, by OpenWorks LLP et al.
==11299== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11299== Command: ./test_main
==11299== 
==11299== ---Thread-Announcement------------------------------------------
==11299== 
==11299== Thread #1 is the program's root thread
==11299== 
==11299== ----------------------------------------------------------------
==11299== 
==11299== Possible data race during read of size 4 at 0x804A020 by thread #1
==11299== Locks held: none
==11299==    at 0x8048419: main (main.c:13)
==11299== 
==11299== ----------------------------------------------------------------
==11299== 
==11299== Possible data race during write of size 4 at 0x804A020 by thread #1
==11299== Locks held: none
==11299==    at 0x8048419: main (main.c:13)
==11299== 
==11299== 
==11299== For counts of detected and suppressed errors, rerun with: -v
==11299== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
-v を試す。

いろいろな情報が盛り込まれたようだ。OSやgcc のバージョンまで表示されている。

andre@andre-VirtualBox:~/work/helgrind$ valgrind --tool=helgrind -v ./test_main >v.txt 2>&1
andre@andre-VirtualBox:~/work/helgrind$ cat v.txt
==11313== Helgrind, a thread error detector
==11313== Copyright (C) 2007-2012, and GNU GPL'd, by OpenWorks LLP et al.
==11313== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11313== Command: ./test_main
==11313== 
--11313-- Valgrind options:
--11313--    --tool=helgrind
--11313--    -v
--11313-- Contents of /proc/version:
--11313--   Linux version 3.0.0-24-generic (buildd@akateko) (gcc version 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3) ) #40-Ubuntu SMP Tue Jul 24 15:36:59 UTC 2012
--11313-- Arch and hwcaps: X86, x86-sse1-sse2
--11313-- Page sizes: currently 4096, max supported 4096
--11313-- Valgrind library directory: /usr/local/lib/valgrind
--11313-- Reading syms from /lib/i386-linux-gnu/ld-2.13.so
--11313--   Considering /lib/i386-linux-gnu/ld-2.13.so ..
--11313--   .. CRC mismatch (computed 1a2ed160 wanted fece6135)
--11313--   Considering /usr/lib/debug/lib/i386-linux-gnu/ld-2.13.so ..
--11313--   .. CRC is valid
--11313-- Reading syms from /home/andre/work/helgrind/test_main
--11313-- Reading syms from /usr/local/lib/valgrind/helgrind-x86-linux
--11313--    object doesn't have a dynamic symbol table
--11313-- Scheduler: using generic scheduler lock implementation.
--11313-- Reading suppressions file: /usr/local/lib/valgrind/default.supp
==11313== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-11313-by-andre-on-???
==11313== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-11313-by-andre-on-???
==11313== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-11313-by-andre-on-???
==11313== 
==11313== TO CONTROL THIS PROCESS USING vgdb (which you probably
==11313== don't want to do, unless you know exactly what you're doing,
==11313== or are doing some strange experiment):
==11313==   /usr/local/lib/valgrind/../../bin/vgdb --pid=11313 ...command...
==11313== 
==11313== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==11313==   /path/to/gdb ./test_main
==11313== and then give GDB the following command
==11313==   target remote | /usr/local/lib/valgrind/../../bin/vgdb --pid=11313
==11313== --pid is optional if only one valgrind process is running
==11313== 
--11313-- Reading syms from /usr/local/lib/valgrind/vgpreload_core-x86-linux.so
--11313-- Reading syms from /usr/local/lib/valgrind/vgpreload_helgrind-x86-linux.so
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04016b80 (index               ) R-> (0000.0) 0x0402d2dd index
--11313--     new: 0x04016b80 (index               ) R-> (0000.0) 0x0402d2a0 strchr
--11313-- REDIR: 0x4016b80 (index) redirected to 0x402d2dd (index)
--11313-- REDIR: 0x4016d40 (strlen) redirected to 0x402d3af (strlen)
--11313-- Reading syms from /lib/i386-linux-gnu/libpthread-2.13.so
--11313--   Considering /lib/i386-linux-gnu/libpthread-2.13.so ..
--11313--   .. CRC mismatch (computed 0b26b884 wanted 21b4acb3)
--11313--   Considering /usr/lib/debug/lib/i386-linux-gnu/libpthread-2.13.so ..
--11313--   .. CRC is valid
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04051660 (pthread_spin_init   ) W-> (0000.0) 0x0402c64a pthread_spin_init
--11313--     new: 0x04051660 (pthread_spin_init   ) W-> (0000.0) 0x0402c65a pthread_spin_unlock
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04052cf0 (sem_wait@@GLIBC_2.1 ) W-> (0000.0) 0x0402ca8a sem_wait@*
--11313--     new: 0x04052cf0 (sem_wait@@GLIBC_2.1 ) W-> (0000.0) 0x0402ca7d sem_wait
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04052d90 (sem_wait@GLIBC_2.0  ) W-> (0000.0) 0x0402ca8a sem_wait@*
--11313--     new: 0x04052d90 (sem_wait@GLIBC_2.0  ) W-> (0000.0) 0x0402ca7d sem_wait
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04052f50 (sem_post@@GLIBC_2.1 ) W-> (0000.0) 0x0402caa4 sem_post@*
--11313--     new: 0x04052f50 (sem_post@@GLIBC_2.1 ) W-> (0000.0) 0x0402ca97 sem_post
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04052fd4 (sem_post@GLIBC_2.0  ) W-> (0000.0) 0x0402caa4 sem_post@*
--11313--     new: 0x04052fd4 (sem_post@GLIBC_2.0  ) W-> (0000.0) 0x0402ca97 sem_post
--11313-- Reading syms from /lib/i386-linux-gnu/libc-2.13.so
--11313--   Considering /lib/i386-linux-gnu/libc-2.13.so ..
--11313--   .. CRC mismatch (computed 365f96ac wanted ff3422fe)
--11313--   Considering /usr/lib/debug/lib/i386-linux-gnu/libc-2.13.so ..
--11313--   .. CRC is valid
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x040d7b40 (index               ) R-> (0000.0) 0x0402d263 index
--11313--     new: 0x040d7b40 (index               ) R-> (0000.0) 0x0402d226 strchr
--11313-- REDIR: 0x40d7b40 (index) redirected to 0x402d263 (index)
--11313-- REDIR: 0x404cfd0 (pthread_create@@GLIBC_2.1) redirected to 0x402bb75 (pthread_create@*)
--11313-- REDIR: 0x40d5420 (calloc) redirected to 0x40281f7 (calloc)
--11313-- REDIR: 0x40d48e0 (free) redirected to 0x4028e78 (free)
==11313== ---Thread-Announcement------------------------------------------
==11313== 
==11313== Thread #1 is the program's root thread
==11313== 
==11313== ---Thread-Announcement------------------------------------------
==11313== 
==11313== Thread #2 was created
==11313==    at 0x41340B8: clone (clone.S:111)
==11313== 
==11313== ----------------------------------------------------------------
==11313== 
==11313== Possible data race during read of size 4 at 0x804A020 by thread #1
==11313== Locks held: none
==11313==    at 0x8048419: main (main.c:13)
==11313== 
==11313== This conflicts with a previous write of size 4 by thread #2
==11313== Locks held: none
==11313==    at 0x80484E0: child_fn (main.c:6)
==11313==    by 0x404CD30: start_thread (pthread_create.c:304)
==11313==    by 0x41340CD: clone (clone.S:130)
==11313== 
==11313== ----------------------------------------------------------------
==11313== 
==11313== Possible data race during write of size 4 at 0x804A020 by thread #1
==11313== Locks held: none
==11313==    at 0x8048419: main (main.c:13)
==11313== 
==11313== This conflicts with a previous write of size 4 by thread #2
==11313== Locks held: none
==11313==    at 0x80484E0: child_fn (main.c:6)
==11313==    by 0x404CD30: start_thread (pthread_create.c:304)
==11313==    by 0x41340CD: clone (clone.S:130)
==11313== 
--11313-- REDIR: 0x404dd70 (pthread_join) redirected to 0x402bb95 (pthread_join)
--11313-- REDIR: 0x404ed70 (pthread_mutex_lock) redirected to 0x402bdd4 (pthread_mutex_lock)
--11313-- REDIR: 0x404fdf0 (pthread_mutex_unlock) redirected to 0x402c1ae (pthread_mutex_unlock)
==11313== 
==11313== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==11313== 
==11313== 1 errors in context 1 of 2:
==11313== ----------------------------------------------------------------
==11313== 
==11313== Possible data race during write of size 4 at 0x804A020 by thread #1
==11313== Locks held: none
==11313==    at 0x8048419: main (main.c:13)
==11313== 
==11313== This conflicts with a previous write of size 4 by thread #2
==11313== Locks held: none
==11313==    at 0x80484E0: child_fn (main.c:6)
==11313==    by 0x404CD30: start_thread (pthread_create.c:304)
==11313==    by 0x41340CD: clone (clone.S:130)
==11313== 
==11313== 
==11313== 1 errors in context 2 of 2:
==11313== ----------------------------------------------------------------
==11313== 
==11313== Possible data race during read of size 4 at 0x804A020 by thread #1
==11313== Locks held: none
==11313==    at 0x8048419: main (main.c:13)
==11313== 
==11313== This conflicts with a previous write of size 4 by thread #2
==11313== Locks held: none
==11313==    at 0x80484E0: child_fn (main.c:6)
==11313==    by 0x404CD30: start_thread (pthread_create.c:304)
==11313==    by 0x41340CD: clone (clone.S:130)
==11313== 
==11313== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

FlawFinderを試す。〜セキュアコーディングを目指して〜

はじめに

コーディング中に、バッファオーバフローをどうやって未然に防ぐか。
Flawfinderというツールがあるとわかったので、試してみた。

インストール

sudo apt-get install flawfinder

サンプルソース

本家にあるサンプルソースに対し、ツールを実行してみた。

andre@andre-VirtualBox:~/work/flawfinder$ cat -n test.c
     1	/* Test flawfinder.  This program won't compile or run; that's not necessary
     2	   for this to be a useful test. */
     3	
     4	#include <stdio.h>
     5	#define hello(x) goodbye(x)
     6	#define WOKKA "stuff"
     7	
     8	main() {
     9	 printf("hello\n");
    10	}
    11	
    12	/* This is a strcpy test. */
    13	
    14	int demo(char *a, char *b) {
    15	 strcpy(a, "\n"); // Did this work?
    16	 strcpy(a, gettext("Hello there")); // Did this work?
    17	 strcpy(b, a);
    18	 sprintf(s, "\n");
    19	 sprintf(s, "hello");
    20	 sprintf(s, "hello %s", bug);
    21	 sprintf(s, gettext("hello %s"), bug);
    22	 sprintf(s, unknown, bug);
    23	 printf(bf, x);
    24	 scanf("%d", &x);
    25	 scanf("%s", s);
    26	 scanf("%10s", s);
    27	 scanf("%s", s);
    28	 gets(f); // Flawfinder: ignore
    29	 printf("\\");
    30	 /* Flawfinder: ignore */
    31	 gets(f);
    32	 gets(f);
    33	 /* These are okay, but flawfinder version < 0.20 incorrectly used
    34	    the first parameter as the parameter for the format string */
    35	 syslog(LOG_ERR,"cannot open config file (%s): %s",filename,strerror(errno))
    36	 syslog(LOG_CRIT,"malloc() failed");
    37	 /* But this one SHOULD trigger a warning. */
    38	 syslog(LOG_ERR, attacker_string);
    39	
    40	}
    41	
    42	
    43	
    44	demo2() {
    45	  char d[20];
    46	  char s[20];
    47	  int n;
    48	
    49	  _mbscpy(d,s); /* like strcpy, this doesn't check for buffer overflow */
    50	  memcpy(d,s);
    51	  CopyMemory(d,s);
    52	  lstrcat(d,s);
    53	  strncpy(d,s);
    54	  _tcsncpy(d,s);
    55	  strncat(d,s,10);
    56	  strncat(d,s,sizeof(d)); /* Misuse - this should be flagged as riskier. */
    57	  _tcsncat(d,s,sizeof(d)); /* Misuse - flag as riskier */
    58	  n = strlen(d);
    59	  /* This is wrong, and should be flagged as risky: */
    60	  MultiByteToWideChar(CP_ACP,0,szName,-1,wszUserName,sizeof(wszUserName));
    61	  /* This is also wrong, and should be flagged as risky: */
    62	  MultiByteToWideChar(CP_ACP,0,szName,-1,wszUserName,sizeof wszUserName);
    63	  /* This is much better: */
    64	  MultiByteToWideChar(CP_ACP,0,szName,-1,wszUserName,sizeof(wszUserName)/sizeof(wszUserName[0]));
    65	  /* This is much better: */
    66	  MultiByteToWideChar(CP_ACP,0,szName,-1,wszUserName,sizeof wszUserName /sizeof(wszUserName[0]));
    67	  /* This is an example of bad code - the third paramer is NULL, so it creates
    68	     a NULL ACL.  Note that Flawfinder can't detect when a
    69	     SECURITY_DESCRIPTOR structure is manually created with a NULL value
    70	     as the ACL; doing so would require a tool that handles C/C++
    71	     and knows about types more that flawfinder currently does.
    72	     Anyway, this needs to be detected: */
    73	  SetSecurityDescriptorDacl(&sd,TRUE,NULL,FALSE);
    74	  /* This one is a bad idea - first param shouldn't be NULL */
    75	  CreateProcess(NULL, "C:\\Program Files\\GoodGuy\\GoodGuy.exe -x", "");
    76	  /* Test interaction of quote characters */
    77	  printf("%c\n", 'x');
    78	  printf("%c\n", '"');
    79	  printf("%c\n", '\"');
    80	  printf("%c\n", '\'');
    81	  printf("%c\n", '\177');
    82	  printf("%c\n", '\xfe');
    83	  printf("%c\n", '\xd');
    84	  printf("%c\n", '\n');
    85	  printf("%c\n", '\\');
    86	  printf("%c\n", "'");
    87	}
    88	
    89	
    90	int getopt_example(int argc,char *argv[]) {
    91	    while ((optc = getopt_long (argc, argv, "a",longopts, NULL )) != EOF) {
    92	    }
    93	}
    94	
    95	int testfile() {
    96	  FILE *f;
    97	  f = fopen("/etc/passwd", "r"); 
    98	  fclose(f);
    99	}
   100	
   101	/* Regression test: handle \\\n after end of string */
   102	
   103	#define assert(x) {\
   104	 if (!(x)) {\
   105	 fprintf(stderr,"Assertion failed.\n"\
   106	 "File: %s\nLine: %d\n"\
   107	 "Assertion: %s\n\n"\
   108	 ,__FILE__,__LINE__,#x);\
   109	 exit(1);\
   110	 };\
   111	 }
   112	
   113	int accesstest() {
   114	  int access = 0; /* Not a function call.  Should be caught by the
   115	                     false positive test, and NOT labelled as a problem. */
   116	}

実行結果

andre@andre-VirtualBox:~/work/flawfinder$ cat flawfinder_out.txt
Flawfinder version 1.27, (C) 2001-2004 David A. Wheeler.
Number of dangerous functions in C/C++ ruleset: 160
Examining test.c
test.c:32:  [5] (buffer) gets:
  Does not check for buffer overflows. Use fgets() instead.
   ★↑バッファオーバフローのためのチェックしていない。gets()->fgets()へ置き換えるべき。
test.c:56:  [5] (buffer) strncat:
  Easily used incorrectly (e.g., incorrectly computing the correct
  maximum size to add). Consider strlcat or automatically resizing strings.
   ★↑誤って使用されやすい。
   ★↑strncat()ではなく、strlcat()もしくは、自動的にリサイズする文字列型?を考えるべき。
  Risk is high; the length parameter appears to be a constant, instead of
  computing the number of characters left.
test.c:57:  [5] (buffer) _tcsncat:
  Easily used incorrectly (e.g., incorrectly computing the correct
  maximum size to add). Consider strlcat or automatically resizing strings.
  Risk is high; the length parameter appears to be a constant, instead of
  computing the number of characters left.
test.c:60:  [5] (buffer) MultiByteToWideChar:
  Requires maximum length in CHARACTERS, not bytes. Risk is high, it
  appears that the size is given as bytes, but the function requires size as
  characters.
    ★↑バイト数ではなく、文字の最大長が引数に要求されている。高リスクであり、バイト数を与えるべき。
test.c:62:  [5] (buffer) MultiByteToWideChar:
  Requires maximum length in CHARACTERS, not bytes. Risk is high, it
  appears that the size is given as bytes, but the function requires size as
  characters.
test.c:73:  [5] (misc) SetSecurityDescriptorDacl:
  Never create NULL ACLs; an attacker can set it to Everyone (Deny All
  Access), which would even forbid administrator access. 
test.c:73:  [5] (misc) SetSecurityDescriptorDacl:
  Never create NULL ACLs; an attacker can set it to Everyone (Deny All
  Access), which would even forbid administrator access. 
test.c:17:  [4] (buffer) strcpy:
  Does not check for buffer overflows when copying to destination.
    ★↑連結元へのコピー時にバッファオーバフローが考慮されていない。
  Consider using strncpy or strlcpy (warning, strncpy is easily misused).
    ★strncpy か strlcpyへの代替を考慮すべき。( 警告:strncpyは誤使用しやすい) 
test.c:20:  [4] (buffer) sprintf:
  Does not check for buffer overflows. Use snprintf or vsnprintf. 
    ★バッファオーバフローが考慮されていない。snprintf か vsnprintfを使うべき。
test.c:21:  [4] (buffer) sprintf:
  Does not check for buffer overflows. Use snprintf or vsnprintf. 
test.c:22:  [4] (format) sprintf:
  Potential format string problem. Make format string constant. 
    ★書式部分が文字列ではない可能性あり。文字列定数であるべき。
test.c:23:  [4] (format) printf:
  If format strings can be influenced by an attacker, they can be
  exploited. Use a constant for the format specification. 
    ★書式部分は、クラッカーによって悪用されることがしばしばある。そのため、書式部分は文字定数を使うべき。
test.c:25:  [4] (buffer) scanf:
  The scanf() family's %s operation, without a limit specification,
  permits buffer overflows. Specify a limit to %s, or use a different input
  function. 
    ★制限のない%s書式は、バッファオーバフローを許してしまう。上限制限(%5s等)をつけるか、ほかの関数を使用するべき。
test.c:27:  [4] (buffer) scanf:
  The scanf() family's %s operation, without a limit specification,
  permits buffer overflows. Specify a limit to %s, or use a different input
  function. 
test.c:38:  [4] (format) syslog:
  If syslog's format strings can be influenced by an attacker, they can
  be exploited. Use a constant format string for syslog. 
test.c:49:  [4] (buffer) _mbscpy:
  Does not check for buffer overflows when copying to destination.
  Consider using a function version that stops copying at the end of the
  buffer. 
    ★コピー先へのコピー時、バッファオーバフローが考慮されていない。
    ★バッファの終端でコピーを止める関数を使うことを考えるべき。
test.c:52:  [4] (buffer) lstrcat:
  Does not check for buffer overflows when concatenating to destination.
    ★結合先への文字列結合時、バッファオーバフローが考慮されていない。 
test.c:75:  [3] (shell) CreateProcess:
  This causes a new process to execute and is difficult to use safely.
  Specify the application path in the first argument, NOT as part of the
  second, or embedded spaces could allow an attacker to force a different
  program to run. 
test.c:75:  [3] (shell) CreateProcess:
  This causes a new process to execute and is difficult to use safely.
  Specify the application path in the first argument, NOT as part of the
  second, or embedded spaces could allow an attacker to force a different
  program to run. 
test.c:91:  [3] (buffer) getopt_long:
  Some older implementations do not protect against internal buffer
  overflows . Check implementation on installation, or limit the size of all
  string inputs. 
    ★関数内部でのバッファオーバフローに対して、保護されていない。
    ★インストール時に実装を確認するか、入力文字列すべてのサイズに制限をかけるべき。
test.c:16:  [2] (buffer) strcpy:
  Does not check for buffer overflows when copying to destination.
  Consider using strncpy or strlcpy (warning, strncpy is easily misused). Risk
  is low because the source is a constant string.
    ★コピー先へのコピー時、バッファオーバフローが考慮されていない。
    ★strncpy か strlcpy の使用を考えるべき(警告:strncpyは誤使用されやすい)
    ★コピー元が文字列定数であるため、低リスクである。
test.c:19:  [2] (buffer) sprintf:
  Does not check for buffer overflows. Use snprintf or vsnprintf. Risk
  is low because the source has a constant maximum length.
    ★バッファオーバフローを考慮すべきである。snprintf か vsnprintfを使うべきである。
    ★文字列定数が指定されており、最大長が決まっているため、低リスクである。
test.c:45:  [2] (buffer) char:
  Statically-sized arrays can be overflowed. Perform bounds checking,
  use functions that limit length, or ensure that the size is larger than
  the maximum possible length.
     ★静的サイズの配列がオーバーフローすることができます。
     ★境界チェックを実行し、長さを制限する機能を使用するか、
     ★またはサイズが使用可能な最大長より大きいことを確認してください。( by google翻訳 )
test.c:46:  [2] (buffer) char:
  Statically-sized arrays can be overflowed. Perform bounds checking,
  use functions that limit length, or ensure that the size is larger than
  the maximum possible length. 
test.c:50:  [2] (buffer) memcpy:
  Does not check for buffer overflows when copying to destination. Make
  sure destination can always hold the source data. 
     ★コピー先へのコピー時、バッファオーバフローを考慮すべき。
     ★コピー元のデータが、コピー先へ格納できるかいつも確認することが必要である。
test.c:51:  [2] (buffer) CopyMemory:
  Does not check for buffer overflows when copying to destination. Make
  sure destination can always hold the source data. 
test.c:97:  [2] (misc) fopen:
  Check when opening files - can an attacker redirect it (via symlinks),
  force the opening of special file type (e.g., device files), move
  things around to create a race condition, control its ancestors, or change
  its contents?. 
test.c:15:  [1] (buffer) strcpy:
  Does not check for buffer overflows when copying to destination.
  Consider using strncpy or strlcpy (warning, strncpy is easily misused). Risk
  is low because the source is a constant character.
test.c:18:  [1] (buffer) sprintf:
  Does not check for buffer overflows. Use snprintf or vsnprintf. Risk
  is low because the source is a constant character.
test.c:26:  [1] (buffer) scanf:
  it's unclear if the %s limit in the format string is small enough.
  Check that the limit is sufficiently small, or use a different input
  function. 
test.c:53:  [1] (buffer) strncpy:
  Easily used incorrectly; doesn't always \0-terminate or check for
  invalid pointers. 
test.c:54:  [1] (buffer) _tcsncpy:
  Easily used incorrectly; doesn't always \0-terminate or check for
  invalid pointers. 
test.c:55:  [1] (buffer) strncat:
  Easily used incorrectly (e.g., incorrectly computing the correct
  maximum size to add). Consider strlcat or automatically resizing strings. 
test.c:58:  [1] (buffer) strlen:
  Does not handle strings that are not \0-terminated (it could cause a
  crash if unprotected). 
    ★NULL終端がない文字列かもしれない。クラッシュするかも…
test.c:64:  [1] (buffer) MultiByteToWideChar:
  Requires maximum length in CHARACTERS, not bytes. Risk is very low,
  the length appears to be in characters not bytes.
test.c:66:  [1] (buffer) MultiByteToWideChar:
  Requires maximum length in CHARACTERS, not bytes. Risk is very low,
  the length appears to be in characters not bytes.

Hits = 36
Lines analyzed = 116 in 0.54 seconds (3259 lines/second)
Physical Source Lines of Code (SLOC) = 80
Hits@level = [0]   0 [1]   9 [2]   7 [3]   3 [4]  10 [5]   7
Hits@level+ = [0+]  36 [1+]  36 [2+]  27 [3+]  20 [4+]  17 [5+]   7
Hits/KSLOC@level+ = [0+] 450 [1+] 450 [2+] 337.5 [3+] 250 [4+] 212.5 [5+] 87.5
Suppressed hits = 2 (use --neverignore to show them)
Minimum risk level = 1
Not every hit is necessarily a security vulnerability.
There may be other security vulnerabilities; review your code!
(※ 訳しづらいものorできなかったものは、訳していない)

考察

独自の関数(lstrcat等)についても警告を出すようだ。関数の文字列から、そこらへんは判断しているみたい。
strncpyやstrncatではなく、もっと安全なstrlcpyやstrlcatがあるとは知らなかった。

はじめに

マルチスレッドでプログラムを動かすことが多くある。性能向上のために。Helgrindは、マルチスレッドでのバグを発見するために使用するツールである。今回は、このツールを動かす。簡単なサンプルを使用して。

今回使用したサンプルはこちら。

なお、valgrindのバージョンは3.8.1である。

サンプルソース

下のサンプルソースは、本家からコピーしたもの。
通常、複数のスレッドが同一リソースを使用する場合、排他ロックするべきだが、していない。
したがって、不具合を生む可能性がある。

andre@andre-VirtualBox:~/work/helgrind$ cat main.c
#include <pthread.h>

int var = 0;

void* child_fn ( void* arg ) {
    var++; /* Unprotected relative to parent */ /* this is line 6 */   ★リソースvarが競合している。
    return NULL;
}

int main ( void ) {
    pthread_t child;
    pthread_create(&child, NULL, child_fn, NULL);
    var++; /* Unprotected relative to child */ /* this is line 13 */   ★リソースvarが競合している。
    pthread_join(child, NULL);
    return 0;
}

Helgrindを動かす。

Helgrindを動かした結果を下記に示す。

andre@andre-VirtualBox:~/work/helgrind$ valgrind --tool=helgrind ./test_main > no_history.txt 2>&1
andre@andre-VirtualBox:~/work/helgrind$ cat no_history.txt 
==11250== Helgrind, a thread error detector
==11250== Copyright (C) 2007-2012, and GNU GPL'd, by OpenWorks LLP et al.
==11250== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11250== Command: ./test_main
==11250== 
==11250== ---Thread-Announcement------------------------------------------
==11250== 
==11250== Thread #1 is the program's root thread            ★#1 はmainスレッド
==11250== 
==11250== ---Thread-Announcement------------------------------------------
==11250== 
==11250== Thread #2 was created
==11250==    at 0x41340B8: clone (clone.S:111)              ★#2 はchild_fnスレッド(生成されたスレッドである)
==11250== 
==11250== ----------------------------------------------------------------
==11250== 
==11250== Possible data race during read of size 4 at 0x804A020 by thread #1       ★#1 は4バイトの競合リソースを読み込んだ。
==11250== Locks held: none                                                         ★ ロック状態:ロックなし
==11250==    at 0x8048419: main (main.c:13)
==11250== 
==11250== This conflicts with a previous write of size 4 by thread #2              ★#2 は4バイトの競合リソースを読み込んだ。
==11250== Locks held: none                                                         ★ ロック状態:ロックなし
==11250==    at 0x80484E0: child_fn (main.c:6)
==11250==    by 0x404CD30: start_thread (pthread_create.c:304)
==11250==    by 0x41340CD: clone (clone.S:130)
==11250== 
==11250== ----------------------------------------------------------------
==11250== 
==11250== Possible data race during write of size 4 at 0x804A020 by thread #1      ★#1 は4バイトの競合リソースに書き込んだ。
==11250== Locks held: none                                                         ★ ロック状態:ロックなし
==11250==    at 0x8048419: main (main.c:13)                                        ★ 13行目が競合している
==11250== 
==11250== This conflicts with a previous write of size 4 by thread #2              ★#2 は4バイトの競合リソースに書き込んだ。
==11250== Locks held: none                                                         ★ ロック状態:ロックなし
==11250==    at 0x80484E0: child_fn (main.c:6)                                     ★ 6行目が競合している
==11250==    by 0x404CD30: start_thread (pthread_create.c:304)
==11250==    by 0x41340CD: clone (clone.S:130)
==11250== 
==11250== 
==11250== For counts of detected and suppressed errors, rerun with: -v
==11250== Use --history-level=approx or =none to gain increased speed, at
==11250== the cost of reduced accuracy of conflicting-access information
==11250== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

その他のオプションを試す。

上記Helgrind実行結果の最後4行にあるオプションを試す。

==11250== For counts of detected and suppressed errors, rerun with: ★-v★
==11250== Use ★--history-level=approx or =none★ to gain increased speed, at
==11250== the cost of reduced accuracy of conflicting-access information
==11250== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
--history-level=approx を試す。

approx は、approximateの略語みたいだ。下記の実行結果を見ると、もう少し詳細な動き(リソースを触った順番)がわかるようだ。

approximate:おおよその,ほぼ正確な

andre@andre-VirtualBox:~/work/helgrind$ valgrind --tool=helgrind --history-level=approx ./test_main >history_approx.txt 2>&1
andre@andre-VirtualBox:~/work/helgrind$ cat history_approx.txt 
==11283== Helgrind, a thread error detector
==11283== Copyright (C) 2007-2012, and GNU GPL'd, by OpenWorks LLP et al.
==11283== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11283== Command: ./test_main
==11283== 
==11283== ---Thread-Announcement------------------------------------------
==11283== 
==11283== Thread #1 is the program's root thread         ★#1 はmainスレッド
==11283== 
==11283== ---Thread-Announcement------------------------------------------
==11283== 
==11283== Thread #2 was created
==11283==    at 0x41340B8: clone (clone.S:111)           ★#2 はchild_fnスレッド
==11283== 
==11283== ----------------------------------------------------------------
==11283== 
==11283== Possible data race during read of size 4 at 0x804A020 by thread #1
==11283== Locks held: none
==11283==    at 0x8048419: main (main.c:13)
==11283==  This conflicts with a previous access by thread #2, after    ★リソースを触った順番(#2 -> #1)が書かれているようだ。
==11283==    (the start of the thread)
==11283==  but before
==11283==    at 0x4130779: madvise (syscall-template.S:82)
==11283==    by 0x41340CD: clone (clone.S:130)
==11283== 
==11283== ----------------------------------------------------------------
==11283== 
==11283== Possible data race during write of size 4 at 0x804A020 by thread #1
==11283== Locks held: none
==11283==    at 0x8048419: main (main.c:13)
==11283==  This conflicts with a previous access by thread #2, after    ★リソースを触った順番(#2 -> #1)が書かれているようだ。
==11283==    (the start of the thread)
==11283==  but before
==11283==    at 0x4130779: madvise (syscall-template.S:82)
==11283==    by 0x41340CD: clone (clone.S:130)
==11283== 
==11283== 
==11283== For counts of detected and suppressed errors, rerun with: -v
==11283== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
--history-level=none

よくわからない。

andre@andre-VirtualBox:~/work/helgrind$ valgrind --tool=helgrind --history-level=none ./test_main >history_none.txt 2>&1
andre@andre-VirtualBox:~/work/helgrind$ cat history_none.txt 
==11299== Helgrind, a thread error detector
==11299== Copyright (C) 2007-2012, and GNU GPL'd, by OpenWorks LLP et al.
==11299== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11299== Command: ./test_main
==11299== 
==11299== ---Thread-Announcement------------------------------------------
==11299== 
==11299== Thread #1 is the program's root thread
==11299== 
==11299== ----------------------------------------------------------------
==11299== 
==11299== Possible data race during read of size 4 at 0x804A020 by thread #1
==11299== Locks held: none
==11299==    at 0x8048419: main (main.c:13)
==11299== 
==11299== ----------------------------------------------------------------
==11299== 
==11299== Possible data race during write of size 4 at 0x804A020 by thread #1
==11299== Locks held: none
==11299==    at 0x8048419: main (main.c:13)
==11299== 
==11299== 
==11299== For counts of detected and suppressed errors, rerun with: -v
==11299== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
-v を試す。

いろいろな情報が盛り込まれたようだ。OSやgcc のバージョンまで表示されている。

andre@andre-VirtualBox:~/work/helgrind$ valgrind --tool=helgrind -v ./test_main >v.txt 2>&1
andre@andre-VirtualBox:~/work/helgrind$ cat v.txt
==11313== Helgrind, a thread error detector
==11313== Copyright (C) 2007-2012, and GNU GPL'd, by OpenWorks LLP et al.
==11313== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11313== Command: ./test_main
==11313== 
--11313-- Valgrind options:
--11313--    --tool=helgrind
--11313--    -v
--11313-- Contents of /proc/version:
--11313--   Linux version 3.0.0-24-generic (buildd@akateko) (gcc version 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3) ) #40-Ubuntu SMP Tue Jul 24 15:36:59 UTC 2012
--11313-- Arch and hwcaps: X86, x86-sse1-sse2
--11313-- Page sizes: currently 4096, max supported 4096
--11313-- Valgrind library directory: /usr/local/lib/valgrind
--11313-- Reading syms from /lib/i386-linux-gnu/ld-2.13.so
--11313--   Considering /lib/i386-linux-gnu/ld-2.13.so ..
--11313--   .. CRC mismatch (computed 1a2ed160 wanted fece6135)
--11313--   Considering /usr/lib/debug/lib/i386-linux-gnu/ld-2.13.so ..
--11313--   .. CRC is valid
--11313-- Reading syms from /home/andre/work/helgrind/test_main
--11313-- Reading syms from /usr/local/lib/valgrind/helgrind-x86-linux
--11313--    object doesn't have a dynamic symbol table
--11313-- Scheduler: using generic scheduler lock implementation.
--11313-- Reading suppressions file: /usr/local/lib/valgrind/default.supp
==11313== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-11313-by-andre-on-???
==11313== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-11313-by-andre-on-???
==11313== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-11313-by-andre-on-???
==11313== 
==11313== TO CONTROL THIS PROCESS USING vgdb (which you probably
==11313== don't want to do, unless you know exactly what you're doing,
==11313== or are doing some strange experiment):
==11313==   /usr/local/lib/valgrind/../../bin/vgdb --pid=11313 ...command...
==11313== 
==11313== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==11313==   /path/to/gdb ./test_main
==11313== and then give GDB the following command
==11313==   target remote | /usr/local/lib/valgrind/../../bin/vgdb --pid=11313
==11313== --pid is optional if only one valgrind process is running
==11313== 
--11313-- Reading syms from /usr/local/lib/valgrind/vgpreload_core-x86-linux.so
--11313-- Reading syms from /usr/local/lib/valgrind/vgpreload_helgrind-x86-linux.so
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04016b80 (index               ) R-> (0000.0) 0x0402d2dd index
--11313--     new: 0x04016b80 (index               ) R-> (0000.0) 0x0402d2a0 strchr
--11313-- REDIR: 0x4016b80 (index) redirected to 0x402d2dd (index)
--11313-- REDIR: 0x4016d40 (strlen) redirected to 0x402d3af (strlen)
--11313-- Reading syms from /lib/i386-linux-gnu/libpthread-2.13.so
--11313--   Considering /lib/i386-linux-gnu/libpthread-2.13.so ..
--11313--   .. CRC mismatch (computed 0b26b884 wanted 21b4acb3)
--11313--   Considering /usr/lib/debug/lib/i386-linux-gnu/libpthread-2.13.so ..
--11313--   .. CRC is valid
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04051660 (pthread_spin_init   ) W-> (0000.0) 0x0402c64a pthread_spin_init
--11313--     new: 0x04051660 (pthread_spin_init   ) W-> (0000.0) 0x0402c65a pthread_spin_unlock
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04052cf0 (sem_wait@@GLIBC_2.1 ) W-> (0000.0) 0x0402ca8a sem_wait@*
--11313--     new: 0x04052cf0 (sem_wait@@GLIBC_2.1 ) W-> (0000.0) 0x0402ca7d sem_wait
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04052d90 (sem_wait@GLIBC_2.0  ) W-> (0000.0) 0x0402ca8a sem_wait@*
--11313--     new: 0x04052d90 (sem_wait@GLIBC_2.0  ) W-> (0000.0) 0x0402ca7d sem_wait
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04052f50 (sem_post@@GLIBC_2.1 ) W-> (0000.0) 0x0402caa4 sem_post@*
--11313--     new: 0x04052f50 (sem_post@@GLIBC_2.1 ) W-> (0000.0) 0x0402ca97 sem_post
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x04052fd4 (sem_post@GLIBC_2.0  ) W-> (0000.0) 0x0402caa4 sem_post@*
--11313--     new: 0x04052fd4 (sem_post@GLIBC_2.0  ) W-> (0000.0) 0x0402ca97 sem_post
--11313-- Reading syms from /lib/i386-linux-gnu/libc-2.13.so
--11313--   Considering /lib/i386-linux-gnu/libc-2.13.so ..
--11313--   .. CRC mismatch (computed 365f96ac wanted ff3422fe)
--11313--   Considering /usr/lib/debug/lib/i386-linux-gnu/libc-2.13.so ..
--11313--   .. CRC is valid
==11313== WARNING: new redirection conflicts with existing -- ignoring it
--11313--     old: 0x040d7b40 (index               ) R-> (0000.0) 0x0402d263 index
--11313--     new: 0x040d7b40 (index               ) R-> (0000.0) 0x0402d226 strchr
--11313-- REDIR: 0x40d7b40 (index) redirected to 0x402d263 (index)
--11313-- REDIR: 0x404cfd0 (pthread_create@@GLIBC_2.1) redirected to 0x402bb75 (pthread_create@*)
--11313-- REDIR: 0x40d5420 (calloc) redirected to 0x40281f7 (calloc)
--11313-- REDIR: 0x40d48e0 (free) redirected to 0x4028e78 (free)
==11313== ---Thread-Announcement------------------------------------------
==11313== 
==11313== Thread #1 is the program's root thread
==11313== 
==11313== ---Thread-Announcement------------------------------------------
==11313== 
==11313== Thread #2 was created
==11313==    at 0x41340B8: clone (clone.S:111)
==11313== 
==11313== ----------------------------------------------------------------
==11313== 
==11313== Possible data race during read of size 4 at 0x804A020 by thread #1
==11313== Locks held: none
==11313==    at 0x8048419: main (main.c:13)
==11313== 
==11313== This conflicts with a previous write of size 4 by thread #2
==11313== Locks held: none
==11313==    at 0x80484E0: child_fn (main.c:6)
==11313==    by 0x404CD30: start_thread (pthread_create.c:304)
==11313==    by 0x41340CD: clone (clone.S:130)
==11313== 
==11313== ----------------------------------------------------------------
==11313== 
==11313== Possible data race during write of size 4 at 0x804A020 by thread #1
==11313== Locks held: none
==11313==    at 0x8048419: main (main.c:13)
==11313== 
==11313== This conflicts with a previous write of size 4 by thread #2
==11313== Locks held: none
==11313==    at 0x80484E0: child_fn (main.c:6)
==11313==    by 0x404CD30: start_thread (pthread_create.c:304)
==11313==    by 0x41340CD: clone (clone.S:130)
==11313== 
--11313-- REDIR: 0x404dd70 (pthread_join) redirected to 0x402bb95 (pthread_join)
--11313-- REDIR: 0x404ed70 (pthread_mutex_lock) redirected to 0x402bdd4 (pthread_mutex_lock)
--11313-- REDIR: 0x404fdf0 (pthread_mutex_unlock) redirected to 0x402c1ae (pthread_mutex_unlock)
==11313== 
==11313== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==11313== 
==11313== 1 errors in context 1 of 2:
==11313== ----------------------------------------------------------------
==11313== 
==11313== Possible data race during write of size 4 at 0x804A020 by thread #1
==11313== Locks held: none
==11313==    at 0x8048419: main (main.c:13)
==11313== 
==11313== This conflicts with a previous write of size 4 by thread #2
==11313== Locks held: none
==11313==    at 0x80484E0: child_fn (main.c:6)
==11313==    by 0x404CD30: start_thread (pthread_create.c:304)
==11313==    by 0x41340CD: clone (clone.S:130)
==11313== 
==11313== 
==11313== 1 errors in context 2 of 2:
==11313== ----------------------------------------------------------------
==11313== 
==11313== Possible data race during read of size 4 at 0x804A020 by thread #1
==11313== Locks held: none
==11313==    at 0x8048419: main (main.c:13)
==11313== 
==11313== This conflicts with a previous write of size 4 by thread #2
==11313== Locks held: none
==11313==    at 0x80484E0: child_fn (main.c:6)
==11313==    by 0x404CD30: start_thread (pthread_create.c:304)
==11313==    by 0x41340CD: clone (clone.S:130)
==11313== 
==11313== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)