C/C++ knowledge base (ナレッジベース) リファレンス

C/C++ knowledge base (ナレッジベース) ファイルは、拡張子が .kb のテキストファイルです。Klocwork では、コードを解析するたびに、コード内で動作する各関数のレコードを含む knowledge base (ナレッジベース) が自動的に生成されます。Klocwork によるコードの解釈方法を変更するには、独自の knowledge base (ナレッジベース) レコードを作成して .kb ファイルに保存し、コードの Klocwork 解析にインポートすることができます。Klocwork 解析のチューニングのための knowledge base (ナレッジベース) レコードの作成方法例については、C/C++ 解析のチューニングを参照してください。

ナレッジベースの構文

.kb ファイルには、knowledge base (ナレッジベース) レコードに加えて、空白行とコメント (先頭に # を付加) を含めることができます。

knowledge base (ナレッジベース) レコードには、空白で区切られた次のフィールドが含まれています。

<function_name> <function_key> <record_kind> <specification>

フィールド

  • <function_name> は、この knowledge base (ナレッジベース) レコードに該当する関数またはクラスメソッドの完全修飾名です。クラス名または名前空間名にはワイルドカード (*) を使用できますが、メソッド名には使用できません。

    ワイルドカードを使用して、テンプレートおよびテンプレート関数の型を最大 1 レベルまで表すこともできます。例: std::vector<*>::insert (テンプレート) または mytemplatefunction<*> (テンプレート関数)

  • <function_key> は関数キーです。これは、関数引数 (シグネチャ) をベースにしており、これを使って、多重定義された C++ 関数を区別します。次のパターンもご利用いただけます。
    • すべての C 関数や、多重定義された C++ 関数では、"-" (ハイフン) を使用します
    • ノースロー演算子のための nothrow
    • 演算子 new および delete のための placementKlocwork アナライザーを混乱させないため、ナレッジベースを手動で編集する際に、"new" または "delete"では特別の関数キー (ダブルハイフン(--)) を使う必要があります。そうでない場合は、アナライザーはこれらの呼び出しを、組み込まれた C++ 演算子 (new および delete) への呼び出しであると解釈します。

    <function_key> フィールドのワイルドカードも使用できます。単一のワイルドカード (*) を使って、いずれかのシグネチャをマッチさせることができます。

    構文 "@args(N)" を使用して、関数キーの引数の数を指定できます。例:
    foo @args(2) RET 1 : $$ EQ(0)
    この KB レコードは、任意の型の 2 つの引数を持つグローバル名前空間の任意の関数 "foo" に一致します。

    # <function_name> <function_key> <record_kind> <specification>
    1. read_data - DMEM ,MRF,1
    2. MyClass::fail - NORET  
    3. MyClass::new nothrow RET env:EQ(0)
    4. MyClass::new placement DMEM ,MRF,2
    5. *::read * DMEM ,MRF,1

    明示的な関数のキーを使って知識ベース レコードを作成込む最も簡単な方法は、適切な関数やメソッド向けに自動生成されたナレッジベースレコードを取得し、それらの<record_kind>と<specification>フィールドを適宜変更することです。

  • <record_kind> は、ALLOCXMRF などの knowledge base (ナレッジベース) レコードの名前です。
  • <specification> は knowledge base (ナレッジベース) レコードに適用する特性を定義します。

<specification> フィールドはすべての knowledge base (ナレッジベース) レコードで異なり、ソケット式または条件付きソケット式として定義します。

<socket_expression> は通常次の形式で定義します。

$<arg_number> : <value> | $$ | [1->] .<field_name>

フィールド

  • <arg_number> は関数の中の引数の数、または、パラメーターです。 ゼロ (0) は、クラスメソッドのための「この」引数を指定します。
  • <value> は引数に割り当てる値で、単一値か角かっこ内に値の範囲を指定できます。一部のレコードには、範囲演算子の EQ、NE、GE、LE、および =、!=、==、<、>、<=、および >= というその他の記号を使用できます。たとえば、$1 EQ(0) $2 [4,16]は、引数 1 が 0 と等価である必要があること、および引数 2 が 4 から 16 までの範囲である必要があることを指定しています。ソケット式にも通常の論理演算子 (&&、|) および算術演算子 (+、-、*) を含めることができます。
  • $$ は関数の戻り値を指定します。
  • <field_name> は、クラスまたは構造のフィールドの、単純または修飾された名前で、-> はフィールド名のポインターを示します。たとえば、$1->x は、関数の最初の引数 (($1) によりポイントされた構造のフィールド x を指定します。

ソケット式には、プロパティを使用することもできます。

  • charlength - 文字単位の文字列長
  • arraysize - 配列要素のバッファの割り当てサイズ
  • bytesize - バッファの割り当てサイズ (バイト単位)

さらに、関数も使用できます。

  • if (a,b,c) - 論理式 a を評価し、a が true の場合は関数の結果が b、a が false の場合は結果が c になります。
  • min(a,b) - 2 つの値の最小値。
  • max(a,b) - 2 つの値の最大値。
  • formatted_printf(a, b) - 書式文字列が引数 a の場合、printf のような関数により作成される文字列の長さを返し、表示される引数は引数 b から始まります。

条件付きソケットは、次の形式で表現します。

<precondition> : <socket_expression> : <postcondition>

フィールド

  • <precondition> はソケット式により示される演算に対して満たす必要がある条件、または事前条件がなく、演算が常に実行されることを示す 1 です。
  • <postcondition> は演算が成功した場合に満たす条件です。

仮想メソッドに KB を使用する

仮想メソッドでは、KB レコードを基本クラスとそのすべての子クラスに適用できますが、指定された基本クラスのインターフェイスを介してのみ適用できます。たとえば、KB レコードを適用する場合:

Base::func * RET 1 : $$ EQ(5)

次のコードに:

class Base
{
public: 
    virtual int func();
};
 
class Child : public Base
{
public: 
    virtual int func();
};
 
void foo()
{
    Child *c = new Child();
    Base *b = c;
 
    int x = b->func(); // Since this is a pointer of class Base, then the KB will be applied here even if the object is of type Child.Klocwork will know that x == 5 after the call.
    int y = c->func(); // Since this is a pointer of class Child, then the KB will NOT be applied here even if the class derives from Base.So, Klocwork will not know anything about the value of y.
}

KB は基本インターフェイスにのみ適用され、子インターフェイスには適用されないことに注意してください。子クラス (仮想メソッド) 専用の新しい KB を追加して、同じ動作 (または異なる動作) を指定できます。これにより子クラスの KB を指定できます。ただし、子クラスオブジェクトにのみ適用されます。

レコードの種類の指定フィールド

ACQUIRE レコードおよび RELEASE レコード

ACQUIRE レコードおよび RELEASE レコードはリソース処理の規則を指定します。ACQUIRE レコードは、関数が記述子により識別される種類のリソースオブジェクトを取得 (割り当て) できることを指定します。RELEASE レコードは、関数が識別されたリソースオブジェクトを解放できることを指定します。

指定フィールドの構文

<resource_kind> ':'<conditional_socket> | 'ignore'

フィールド

  • <resource_kind> は、FILE や pthread_mutex など、一致させるリソース処理の種類を示します。
  • <conditional_socket> は、取得したリソースのリソース記述子として使用する値を特定します。
  • ignore は、コード内の ACQUIRE 関数または RELEASE 関数をスキップするために使用できます。

ACQUIRE 関数と RELEASE 関数のペア

RH.LEAK チェッカーは、次の関数のペアを ACQUIRE/RELEASE のペアとして検索します。

pthread_attr_init / pthread_attr_destroy pthread_mutexattr_init / pthread_mutexattr_destroy pthread_condattr_init / pthread_condattr_destroy pthread_barrierattr_init / pthread_barrierattr_destroy pthread_rwlockattr_init / pthread_rwlockattr_destroy posix_trace_attr_init / posix_trace_attr_destroy

これらの関数は主に pthread ライブラリ (pthread.h) に関連し、thread、mutex、barrier などの対応するオブジェクトを初期化するために使用される異なる種類の attribute オブジェクトを操作します。

例 1

次の抜粋の RH.LEAK チェッカーは通常 37 行目で 2 つの指摘を報告します。

1   #include <sys/types.h>
2   #include <sys/stat.h>
3   #include <sys/mman.h>
4   #include <fcntl.h>
5   #include <pthread.h>
6  
7   struct semaphore {
8   pthread_mutex_t lock;
10  unsigned count;
11  };
12  typedef struct semaphore semaphore_t;
13 
14  semaphore_t *
15  semaphore_create(char *semaphore_name)
16  {
17  int fd;
18  semaphore_t *semap;
19  pthread_mutexattr_t psharedm;
20  pthread_condattr_t psharedc;
21  
22  fd = open(semaphore_name, O_RDWR | O_CREAT | O_EXCL, 0666);
23  if (fd < 0)
24  return NULL;
25  ftruncate(fd, sizeof(semaphore_t));
26  pthread_mutexattr_init(&psharedm);
27  pthread_mutexattr_setpshared(&psharedm, PTHREAD_PROCESS_SHARED);
28  pthread_condattr_init(&psharedc);
29  pthread_condattr_setpshared(&psharedc, PTHREAD_PROCESS_SHARED);
30  semap = (semaphore_t *) mmap(NULL, sizeof(semaphore_t),
31  PROT_READ | PROT_WRITE, MAP_SHARED,
32  fd, 0);
33  close(fd);
34  pthread_mutex_init(&semap->lock, &psharedm);
35  pthread_cond_init(&semap->nonzero, &psharedc);
36  semap->count = 0;
37  return semap;
38 }
37:Resource acquired to 'psharedm' at line 26 may be lost here.
37:Resource acquired to 'psharedc' at line 28 may be lost here.

Klocwork 解析でこれらの指摘を抑制するには、knowledge base (ナレッジベース) に次のレコードを追加できます。

   pthread_mutexattr_init - ACQUIRE ignore
   pthread_mutexattr_destroy - RELEASE ignore
   pthread_condattr_init - ACQUIRE ignore
   pthread_condattr_destroy - RELEASE ignore

例 2

標準 C ライブラリ (stdio.h) からの fopen 関数と fclose 関数によるファイルストリーム操作は、次のレコードにより記述できます。

   fopen - ACQUIRE FILE : 1 : $$ : $$ NE (0)
   fclose - RELEASE FILE : 1 : $1 : 1

この例の最初のレコードは、'fopen' の呼び出しにより返される値がリソース記述子、'FILE' を指定することを示します。この関数は、事前条件はなくても (1 に示されるように)、戻り値が null でない場合にのみ、ファイルを開くというアクションを実行します。2 番目のレコードは、'fclose' 関数がその記述子を最初の引数として渡すことで常にファイルを閉じることを示します。

例 3

次のレコードは、pthread.h ライブラリからの 'pthread_mutex_init' 関数と 'pthread_mutex_destroy' 関数による pthread mutex 操作を記述するために使用できます。

   pthread_mutex_init - ACQUIRE pthread_mutex : 1 : *$1 : $$ EQ(0)
   pthread_mutex_destroy - RELEASE pthread_mutex : 1 : *$1 : 1

これらのレコードは、戻り値が 0 の場合に 'pthread_mutex_init' 関数が最初の引数によってポイントされるミューテックスオブジェクトを初期化し、'pthread_mutex_destroy' 関数がその記述子のポインターによりミューテックスを常に破壊することを指定します。

ALLOC レコード

ALLOC レコードは、パラメーターに対する一部の条件 (事前条件) でのみ関数がメモリを割り当て、メモリ割り当てが成功した場合に (事後条件)、返された結果を特定の値に設定します。Klocwork 解析のチューニングのための ALLOC レコードの作成方法例については、C/C++ 解析のチューニングを参照してください。

指定フィールドの構文

   <group> ':'<conditional_socket> | ignore

フィールド

  • <group> はメモリ関数グループを指定します。
  • ignore は、コード内の割り当て関数をスキップするために使用できます。

例 1

次の knowledge base (ナレッジベース) レコードは:

   search_data - ALLOC stdc : $1 GE(0), $2 GE(0) : *$4 : $$ GE(0)

最初と 2 番目のパラメーターが 0 以上の場合に、関数 'search_data' が 'stdc' 割り当てグループに属するメモリを割り当てることを示します。割り当てられたメモリは逆参照される 4 番目のパラメーターを通して渡され、割り当てが成功した場合に、関数の戻り値が 0 以上になります。

例 2

次の抜粋では、Klocwork で関数 'use_precond' の変数 'p' の誤検知メモリリークが通常は報告されます。

1   void alloc_precond(int a, void** p)
2   {
3   if (a>0) 
4   *p = malloc(10);
5   }
6
7   void use_precond(int input_data)
8   {
9   void* p;
10  if (input_data==0) {
11  alloc_precond(input_data, &p);    // process it

この誤検知を回避するために、関数 'alloc_precond' に対して自動的に生成される ALLOC レコードは次のようになります。

    alloc_precond - ALLOC stdc : $1 GE(1) : *$2 : 1

このレコードは、パラメーター 'a' が 1 以上である場合に、新しいメモリが割り当てられることを意味しています。関数 'alloc_precond' が 'use_precond' の例で呼び出されると、'input_data' の引数の値は 0 となり、メモリは割り当てられません。

例 3

次の抜粋では、Klocwork で関数 'use_postcond' の変数 'ptr' のメモリリークが通常は報告されます。

1   int alloc_postcond(int** p)
2   {
3   int* q = malloc(sizeof(int));
4
5   if (q) {
6   *p = q;
7   return 0; // success
8   }
9   else {
10  return -1; // fail
11  }
12}
13
14  int* use_postcond()
15  {
16  int* ptr;
17  int res = alloc_postcond(&ptr);
18
19  if (res == 0)
20  return ptr; // return for further processing
21  
22  return 0;
22  }

関数 'alloc_postcond' に対して自動的に生成される ALLOC レコードは次のようになります。

   alloc_postcond - ALLOC stdc : 1 : *$1 : *$1 NE(0), $$ EQ(0)

このレコードは、'alloc_postcond' で新規メモリが割り当てられた場合に、この関数の戻り値が 0 になり、*p が 0 以外になることを示します。この条件が満たされた場合 ('res == 0')、新規に割り当てられたメモリが追加処理のために返され、'use_postcond' でメモリリークが発生しません。

BAA レコードおよび IAA レコード

配列アクセスの境界 (BAA) レコードは、渡されるポインターを通して関数が配列にアクセスする方法 (読み取り、作成、読み取り/作成、アクセス間隔、およびアクセス単位サイズ) を記述します。内部配列アクセス (IAA) レコードは、ローカル配列の名前とサイズだけでなく、関数がこれらの配列にアクセスする方法を記述します。

指定フィールドの構文

BAA:

   <ReadWrite> ':'<precondition> ':'<socket-expression> ':'<Interval>[','<UnitSize>] ':'<postcondition> 

IAA:

   <ReadWrite> ':'<precondition> ':'<ArrayName> ':'<ArraySize> ':'<Interval>[','<UnitSize>]

フィールド

  • <ReadWrite> は R、W、または RW です。
  • <Interval> は '['<boundary specification>','<boundary_specification>']' です。
  • <boundary_specification> は配列境界を指定するソケット式です。
  • <UnitSize> は正の数です。
  • <ArrayName> は識別子または配列です。
  • <ArraySize> は正の数です。

例 1

strdup - BAA R:1:$1:[0,charlength($1)]:1

このレコードは、strdup が最初の引数を 0 から最初の引数の文字列長まで読み取ることを示します。基本的に、このレコードは strdup が最初の引数として 0 で終わる文字列を予期することを指定しています。

例 2

send_data - BAA R:1:$1:[0,if($2==0,charlength($1),$3+1)]:1

このレコードは、関数 send_data が最初の引数によってポイントされるバッファからいくつかのバイトを読み取ることを示します。バイト数は、2 番目の引数が 0 でない限り、2 番目の引数から取得されます。2 番目の引数が 0 の場合は、最初の引数が文字列として処理され、サイズが 3 番目の引数に 1 を足した文字列長として自動的に評価されます。

例 3

store_and_eval - IAA W:1:temp_buffer:4:[0,$1]

このレコードは、関数 store_and_eval が temp_buffer という名前の内部バッファにアクセスし、このアクセス範囲は 0 から最初の引数の値までになることを示します。

例 4

sprintf - BAA W:1:$1:[0,formatted_printf(2,3)]

このレコードは、書式設定された出力規則に従って、sprintf が引数を表示するために必要なバイト数を最初の引数によってポイントされたバッファに書き込むことを示します。

バイトオーダーレコード

バイトオーダーレコードは、変数により入力データを受信するか、出力データを返すホストからネットワークへのバイトオーダー変換関数またはネットワークからホストへのバイトオーダー変換関数を指定します。各型の関数を記述するバイトオーダーレコードは次のとおりです。

  • BO.HTON.I- (I) でのホストからネットワークへの変換
  • BO.HTON.O- (O) でのホストからネットワークへの変換
  • BO.NTOH.I- (I) でのネットワークからホストへの変換
  • BO.NTOH.O- (O) でのネットワークからホストへの変換
  • BO.READ- ファイルの読み取り
  • BO.RECV- ネットワーク受信
  • BO.SEND- ネットワーク送信
  • BO.WRITE- ファイルの書き込み

指定フィールドの構文

<conditional_socket>

フィールド、<conditional socket> は入力データまたは出力データを渡すために使用される変数を記述します

例 1

hton - BO.HTON.I 1 : $1 : 1

このレコードは、変換関数 hton が変換する入力データとして最初の引数を取得すること、およびホストのバイトオーダーを予期することを示します。

例 2

hton - BO.HTON.O 1 : $$ : 1

このレコードは、変換関数 hton がネットワークバイトオーダーで変換された値を返すことを示します。

例 3

read - BO.READ 1 : *$2 : 1

このレコードは、関数 read が 2 番目の引数によってポイントされた変数によりファイルから読み取った値を返すことを示します。

BPS レコード

バッファプロパティ設定 (BPS) レコードは、関数がバッファプロパティを変更する方法を記述します。

指定フィールドの構文

   <property name>'='<boundary specification>

例 1

strdup - BPS charlength($$)=charlength($1)
strdup - BPS bytesize($$)=charlength($1)+1

これらの文字列複製関数のレコードは以下を示します。

  • 新規に割り当てられたチャンクのサイズが、最初の引数として渡された文字列長と等しい。
  • 新規に割り当てられたバッファが専有するスペースは、最初の引数の長さに 1 を足したものになる (0 バイト終端)。

例 2

strcpy - BPS charlength($1)=charlength($2)

このレコードは、最初の引数として strcpy に渡されたバッファが、2 番目の引数として渡された文字列と同じ長さの文字列を持つことを示します。

CONC レコードおよび LOCK レコード

CONC レコードおよび LOCK レコードは、特定の条件で変数、スレッド、ミューテックス、およびハンドラーをロックおよびロック解除する関数に関係しています。CONC レコードおよび LOCK レコードは次のとおりです。

  • CONC.CONDSIGNAL- 関数が条件変数でロックされたスレッドをロック解除することを指定します。
  • CONC.CONDWAIT- 関数が条件変数で呼び出しスレッドをロックし、ロックされているミューテックスを解放することを指定します。
  • CONC.LOCK- 関数がハンドラーをロックすることを指定します。
  • CONC.LOCK.TRY- 関数がハンドラーのロックを試行することを指定します。
  • CONC.UNLOCK- 関数がハンドラーをロック解除することを指定します。
  • CREATETHREAD- スレッドを作成する関数を指定します。たとえば、Linux では pthread_create であり、Windows では CreateThread です。

  • LOCK- 関数が変数をロックすることを指定します。
  • LOCK_START- 現在のスレッドで単一のロックに対応する変数を関数がロックすることを指定します

  • LOCK_UNLOCK- 関数が変数をロックおよびロック解除することを指定します

  • UNLOCK- 関数が変数をロック解除することを指定します。
  • UNLOCK_START- スレッドを作成する関数に対して、単一のロック解除に対応する変数を関数がロック解除することを指定します

指定フィールドの構文

CONC.CONDSIGNAL、CONC.LOCK、CONC.LOCK.TRY、CONC.UNLOCK、LOCK、UNLOCK

   <conditional_socket>

フィールド、<conditional_socket> はロックまたはロック解除の条件を指定します

CONC.CONDWAIT

   <precondition> ':'<socket_expression> ':'<socket_expression> : <postcondition>

フィールド

  • <precondition> および <postcondition> は演算の条件を指定します。
  • <socket_expression> フィールドはロック解除する条件変数とミューテックスを定義します。

例 1

pthread_cond_signal - CONC.CONDSIGNAL 1 : *$1 : 1

このレコードは、関数 pthread_cond_signal が最初の引数によってポイントされる指定条件変数でブロックされたスレッドの 1 つ以上を常にロック解除し、この演算に成功した場合に 0 を返すことを示します。

例 2

pthread_cond_wait - CONC.CONDWAIT 1 : *$1 : *$2 : 1

このレコードは、関数が 2 番目の引数によってポイントされたミューテックスを常に解放し、呼び出しスレッドにより最初の引数によってポイントされた条件変数をロックすることを示します。

例 3

pthread_mutex_lock - CONC.LOCK 1 : *$1 : $$ EQ(0)

このレコードは、関数 pthread_mutex_lock が最初の引数によってポイントされるオブジェクトのロックを常に試行し、この演算に成功した場合に 0 を返すことを示します。ミューテックスが既にロックされている場合、ミューテックスが使用できるようになるまで、呼び出しスレッドによりブロックされます。

DBZ レコード

DBZ レコードは、ゼロ定数値を返すか、引数によって指し示された変数にゼロ定数値を書き込むか、入力引数により除算を行うかのいずれかの方法でゼロによる除算を行う事がありうる関数に関係しています。DBZ レコードは次のとおりです。
  • DBZ.SRC - ゼロによる除算を行うためにソースとして使用する関数呼び出しを指定します。つまり、この関数は出力引数を介して直接的または間接的にゼロ定数値を返すことがあります
  • xDBZ - ゼロ定数値を確認せずに、除算の除数として引数を使う関数を特定します。

指定フィールドの構文

DBZ.SRC

<conditional_socket>

フィールド

  • <conditional_socket> はゼロ定数値に割り当てることがありうる変数と割り当て条件を特定します。

xDBZ

<conditional_socket>

フィールド

  • <conditional_socket> はゼロによる除算の条件を特定します。

例 1

foo - DBZ.SRC $1 EQ(0) : $$ : 1
bar - DBZ.SRC $1 LE(-1) : *$2 : 1

この例の最初のレコードは、最初の引数が 0 に等しい場合、関数 'foo' がその戻り値としてゼロ定数値を返していることを示しています。

2 番目のレコードは、1 番目の引数が -1 より大きい値でない場合、関数 'bar' が ゼロ定数値を 2 番目の引数により参照されるメモリーに書き込んでいることを示しています。

この抜粋で:

int dbz_01(int total) {
    int x = 0;
    int count = foo(x);
    return total / count;
}

Klocwork は変数 'count' を除数として使うとゼロによる除算を検出します。これは、関数 'foo' の呼び出しの際に最初のパラメータとして渡されるとき、'x' がゼロと等しくなるためです。関数 'foo' はゼロ定数値を返し、これが変数 'count' に割り当てられます。

この抜粋で:

int dbz_02(int x) {
      int may_be_zero;
      bar(x, &may_be_zero);
      if (x > 0) {
           return x / may_be_zero;
      }
      return (-x) / may_be_zero;
}

Klocwork は変数 'may_be_zero' が最後に使用されるためにゼロによる除算を検出します。これは、変数 'x' がゼロ未満の場合にこれに到達し、'x' がゼロ未満の場合は関数 'bar' の呼び出しが変数 'may_be_zero' にゼロ定数値を書き込むためです。

例 2

blah - xDBZ $3 GE(1): $1 : 1

このレコードは、3 番目の引数が 1 以上の場合に、関数 'blah' がゼロ定数値を確認せずに、除算またはモジュロ演算の除数として最初の引数を使うことを示します。

FREE レコードおよび SAFE_FREE レコード

FREE レコードおよび SAFE_FREE レコードはメモリを解放する関数に関係しています。メモリの割り当てとその後の解放に、C と C++ のメモリ管理関数の混合使用やスカラーとベクターのメモリ管理関数の混合使用など、異なるグループの関数を使用した場合には、通常 Klocwork でレポートが作成されます。FREE レコードは特定の割り当てと解放動作を定義するために使用されます。

指定フィールドの構文

   <alloc_group> <expression> [post: <postcondition>]

フィールド

  • <alloc_group> はメモリ関数グループを特定します。
  • <expression> は解放する引数を指定します。

例 1

realloc - FREE stdc $1 post: $$ NE(0)

このレコードは、realloc が最初の引数で渡されるメモリを解放し、メモリが解放されたときに非 null の結果を返す stdc 関数であることを示します。

例 2

hsplit - SAFE_FREE stdc $1->tbl_array

このレコードは、関数 hsplit の最初の引数が構造をポイントすることを示します。この構造のフィールド tbl_array により参照されるメモリが解放され、新しい値がこのフィールドに割り当てられます。

ハッシュソルト レコード

以下は、ハッシュのキー値を計算する関数、またはソルトを引数に取るハッシュから派生したキー値を計算する関数に関するハッシュソルト レコードです。これらは、RCA.HASH.SALT.EMPTY チェッカーを拡張するために使用できます。

  • RCA.HASH.SALT レコードは、これらの関数のソルト引数を指定します。
  • RCA.HASH.SALT.SIZE レコードは、これらの関数のソルト引数のサイズまたは長さを指定します。

指定フィールドの構文

RCA.HASH.SALT

<conditional_socket>

フィールド

  • <conditional_socket> は、ソルトを関数に渡すための引数を特定します。

RCA.HASH.SALT.SIZE

<conditional_socket>

フィールド

  • <conditional_socket> は、ソルトのサイズと長さを関数に渡すための引数を特定します。

generateHash - RCA.HASH.SALT 1 : $2 : 1
generateHashWithSaltSize - RCA.HASH.SALT 1 : $2 : 1
generateHashWithSaltSize - RCA.HASH.SALT.SIZE 1 : $3 : 1

これらのレコードは、generateHash 関数および generateHashWithSaltSize 関数の第 2 引数をハッシュ計算のソルトとして使用し、generateHashWithSaltSize 関数の第 3 引数を、ソルトとして渡す配列または文字列のサイズの決定に使用することを指定します。

NNTS.SRC レコード

NNTS.SRC レコードは、非 null 終了文字列問題の検出に関係し、非 null 終了文字列を返せる関数を指定します。

指定フィールドの構文

   <conditional_socket> ':'<subtype> ':'<size> ':'<src_expression>

フィールド

  • <conditional_socket> は非 null で終了できる変数とその条件を特定します。
  • <subtype> は、コピー元の場所 (strncpy、memcpy) からメモリバッファをコピーする関数の ncpy、または読み取りタイプの関数 (read、fread) の size です。
  • size サブタイプの場合、<size> は可能な非 null 終了バッファの新しいサイズを指定する <range_condition> です。
  • ncpy サブタイプの場合、<src_expression> はコピー元バッファとして使用する変数を特定するソケット式です。

strncpy - NNTS.SRC 1 : $1 : 1 : ncpy : [$3] : $2
read - NNTS.SRC 1 : $2 : 1 : size : [$3]

この例の最初のレコードは、3 番目のパラメーターの長さが 2 番目のパラメーターの長さ以下の場合に、関数 'strncpy' が最初のパラメーターとして非 null 終了文字列を返せることを示します。2 番目のレコードは、関数 'read' が 2 番目のパラメーターとして非 null 終了文字列を返せることを示します。バッファのサイズは 3 番目の引数で渡されます。

NPD レコード

NPD レコードは、null 値を返すか、引数によってポイントされた変数に null 値を書き込むかのいずれかの方法で null ポインター逆参照を行うことができる関数に関係しています。NPD レコードは次のとおりです。

  • NPD- 引数の null チェックをせずに、引数を逆参照する関数を指定します (関数に null が渡される場合はランタイムエラーが発生します)。
  • NPD.SRC- ソースとして使用する関数呼び出しを指定します。
  • xNPD- シンクとして使用する関数呼び出しを指定します。

指定フィールドの構文

NPD

   <arg_number>

フィールド <arg_number> は逆参照するパラメーター番号です。

NPD.SRC

   <conditional_socket>

フィールド <conditional_socket> は null 値に割り当てることができる変数と割り当て条件を特定します

xNPD

   <range_condition> ':'<arg_number>

フィールド、<range_condition> は逆参照条件を指定します

例 1

myElemCopy - NPD 1
myElemCopy - NPD 2

この例のレコードは、関数 myElemCopy が null についてチェックしないで最初の 2 つの引数を逆参照することを示します。

このコードで:

tElem *bar(tElem *e1, tElem *e2)
{
  if (!e1 && !e2) return NULL;
  if (!e1) e1 = createElem();
  myElemCopy(e1,e2);
  return e1;
}

e2 が null で e1 が null でない場合に myElemCopy に渡される 2 番目の引数が null になり、アプリケーションが失敗するため、Klocwork により、myElemCopy の呼び出しに対する null ポインター逆参照の可能性が検出されます。

例 2

foo - NPD.SRC $1 EQ(0) : $$ : 1
xff - NPD.SRC $2 LE(-1) : *$1 : 1

この例の最初のレコードは、最初の引数が 0 に等しい場合、関数 'foo' がその戻り値として null ポインターを返していることを示しています。2 番目のレコードは、2 番目の引数が -1 より大きい値でない場合、関数 'xff' が null ポインター値を最初の引数により参照されるメモリに書き込んでいることを示しています。

この抜粋で:

int npd_01(int t) {
      if (!t) { 
            char *s = foo(t);
            return *s != '\0';
      }
      return 0;
}

't' が関数 foo の呼び出しで最初のパラメーターとして渡されるときに 0 になるため、Klocwork により、変数 's' の逆参照の null ポインター逆参照が検出されます。関数 foo は null を返し、これが変数 's' に割り当てられます。

この抜粋で:

int npd_02(int w) {
      int *p;
      xff(&p, w);
      if (w > 0) {
           return *p;
      }
      return -*p;
}

変数 'w' が 0 未満の場合にこれに到達し、'w' が 0 未満の場合は関数 xff の呼び出しが変数 'p' に null 値を書き込むため、Klocwork により、変数 'p' の 2 番目の逆参照の null ポインター逆参照が検出されます。

例 3

bcopy - xNPD $3 GE(1): 1

このレコードは、3 番目の引数が 1 以上の場合に、関数 bcopy が null についてチェックしないで最初の引数を逆参照することを示します。

PWD_INPUT レコード

PWD_INPUT レコードは、パスワードフィールドを識別する関数、およびユーザーからの入力としてパスワードを受け取る関数に関連しています。

  • PWD_INPUT.SRC レコードは、パスワードフィールドを識別する関数を指定します。

  • PWD_INPUT.SINK レコードは、ユーザーからの入力としてパスワードを受け入れる関数を指定します。

指定フィールドの構文

<socket-expression>

ここで、<socket_expression> は、ソース関数 PWD_INPUT.SRC またはシンク関数 PWD_INPUT.SINK を定義します。

例 1:

QLineEdit::setEchoMode  QLineEdit*,QLineEdit::EchoMode, PWD_INPUT.SRC $1[1,3] : *$0 :1
QLineEdit::text const\ QLineEdit*, PWD_INPUT.SINK 1: *$0 :1

これらのレコードは、QT フレームワーク関数を指定します。setEchoMode の引数 1 は列挙型であり、パスワードフィールドであるために 1 から 3 の値でなければなりません。そのため、PWD_INPUT.SRC KB での前提条件として追加されます。

例 2:

gtk_entry_set_visibility -         PWD_INPUT.SRC $2 EQ(0): *$1 :1
gtk_entry_set_input_purpose -      PWD_INPUT.SRC $2[8,9] : *$1 :1
gtk_entry_get_text -               PWD_INPUT.SINK 1: *$1 :1

これらのレコードは、gtk フレームワーク関数を指定します。gtk_entry_set_visibility の引数 1 は、パスワードフィールドであるために 0 の値でなければなりません。そのため、PWD_INPUT.SRC KB での前提条件として追加されます。 同様に、gtk_entry_set_input_purpose の引数 1 は列挙型であり、パスワードフィールドであるために 8 または 9 のいずれかの値でなければなりません。そのため、PWD_INPUT.SRC KB での前提条件として追加されます。

例 3:

wxTextCtrl::\#constructor   *      PWD_INPUT.SRC $6 EQ(2048) : *$0 : 1
wxTextCtrl::GetValue *             PWD_INPUT.SINK 1: *$0 :1

これらのレコードは、wxwidgets フレームワーク関数を指定します。constructor の引数 6 は、パスワードフィールドであるために 2048 の 10 進数値でなければなりません。そのため、PWD_INPUT.SRC KB での前提条件として追加されます。

R レコードおよび W レコード

R レコードは、関数が引数か引数の一部のメモリ、または引数か引数の一部によってポイントされるメモリを読み取ることを指定します。W レコードは、関数がメモリに書き込むことを指定します。

指定フィールドの構文

   <simple_condition> ':'<socket_expression> | 'dummy'

フィールド

  • <simple_condition> は、関数が必ず値を読み取るか書き込むこと ('1')、または読み取るか書き込む可能性があること ('env') を示します。
  • <socket_expression> には、読み取る値または書き込む値を指定します。
  • 'dummy' は、呼び出された関数が渡された値、またはこれらの値により到達可能なメモリを読み取らないことを示します。

例 1

この抜粋に示されたように、関数 point_getXY は最初の引数によってポイントされる構造の x フィールドと y フィールドを読み取ります。

struct Point { int x, y; };
int point_getXY(struct Point * p) {
    return p->x * p->y;
}

以下には、いくつかの該当する knowledge base (ナレッジベース) レコードを示しています。

point_getXY - R 1:$1
point_getXY - R 1:$1->Point::x
point_getXY - R 1:$1->Point::y

例 2

この抜粋で、関数 check_buf はダミー関数です。

int check_buf(char * buf) {
    return 1;
}

以下には、該当する knowledge base (ナレッジベース) レコードを示しています。

check_buf - R dummy

RET レコード

すべての RET レコードは、関数の戻り値に関係する特性を指定します。RET レコードには以下が含まれています。

  • RET - 指定された事前条件が揃うと、直接的 (戻り値により) または間接的 (引数として渡されるポインターにより) に関数によって戻される値と象徴的な条件を特定します。
  • CHECKRET- 値を返す前に null について結果をチェックした回数を指定します。
  • CONDNORET- パラメーターの値に基づいて、プロセスまたはスレッドの実行を終了する関数を指定します。
  • NORET- exit 関数や abort 関数など、戻り値のない関数を指定します。
  • RETARG- 引数が常に関数の戻り値を返す場合、または別の引数から返された場合に、引数の値を返す関数を指定します。
  • xRET- 関数が引数によって変更する値と戻り値の間の依存関係を指定します。

Klocwork 解析のチューニングに NORET および CONDNORET を使用する例については、C/C++ 解析のチューニングを参照してください。

指定フィールドの構文

RET

     <precondition> : <postcondition>  

CHECKRET

   <checked> ',' <total>

CONDNORET

   <socket_expression>

RETARG

   'RETARG' '1' ':'<socket_expression> '=' <socket_expression>

xRET

   <range_value-1> ':'<socket_expression> <range_value-2>

フィールド

  • <range_value-1> は戻り値を指定します。
  • <range_value-2> はリファレンスにより返される値を指定します。

例 1

foo - RET 1: $$ NE(0)  
foo - RET $1 GE(1), $2 EQ(0) : $$ GE(1), $$==$1, *$3 EQ (-1)  

各 RET レコードは、関数が <precondition>と一致するパラメータを受信した場合に、<postcondition> が指定する値と条件を返すことを宣言します。最初のレコードは、どの任意の入力においても、この関数がゼロ以外の値を (直接) 返すことを示します。2 番目のレコードは、最初の引数が 1 以上で、2 番目の引数が 0 の場合、関数 ‘foo’ が 最初の引数と等しい値(1 以上)の値を返し、関数 ‘foo’ が 3 番目の引数によって指し示された変数の値を設定することを示します。

例 2

kwapi_cfgparam_getParameterValue - CHECKRET 31,43

この CHECKRET レコードは、null について結果をチェックする場合、関数の呼び出しの数が 31 で、呼び出しの総数が 43 であることを示します。

例 3

check_status - CONDNORET ($1!=0)

この CONDNORET レコードは、最初の引数が 0 でない場合に check_status がプログラムの実行を停止することを示します。

例 4

Klocwork では、一部の呼び出しから制御フローが返されないことを把握することが重要になります。たとえば、この抜粋で:

if (p==NULL) myAssertFunction("p == NULL");
strcpy(p,"Some string");

Klocwork で myAssertFunction の戻り値がないことを把握している場合、strcpy の呼び出し後に最初の引数として NULL が渡されないことを把握します。そうでない場合は、Klocwork で null ポインター逆参照の可能性に関する警告が出されます。

myAssertFunction - NORET

この NORET レコードは、myAssertFunction を戻り値のない関数として特定します。

例 5

RETARG レコードは関数が引数の値を返すことを指定します。次の両方のシナリオがサポートされています。

  • 引数から渡される値が常に関数の戻り値として返される場合
  • 値が別の引数から返される場合 (ポイントされたメモリまたは参照されるメモリに書き込まれる)

次のコードには、最初のタイプの戻り値のシンプルな例を示しています。たとえば、次の定義の場合:

1   typedef struct Point { int x, y, z; } Point;
2   int point_getX(Point* p_p) {
3   return p_p->x;
4   }
5   Point p;

次のステートメントでは:

   y = point_getX(&p);

p.x の値が y に割り当てられます。

point_getX - RETARG 1:$$=$1-->Point::x
reassign_filestream_1 - RETARG 1:*$1=*$2

この RETARG 例の最初のレコードは、point_getX が最初の引数のフィールド x を返すことを示します。2 番目のレコードは reassign_filestream_1 が 2 番目の引数によってポイントされる変数の値を最初の引数によってポイントされる変数に書き込むことを示します。

例 6

acpi_ex_get_object_reference - XRET  EQ(0): *$2 NE(0)

この例の xRET レコードは、逆参照された 2 番目の変数を 0 以外の値に設定する場合は、acip_ex_get_object_reference が 0 を返すことを示します。

SETZERO レコード

SETZERO レコードは、関数が引数か引数の一部によってポイントされるメモリに null バイトを書き込むことを指定します。

指定フィールドの構文

   <socket_expression> ':'<Interval>

フィールド

  • <socket_expression> は、関数によって書き込まれるメモリを特定します。
  • <Interval> は '['<boundary specification>','<boundary_specification>']' です。
  • <boundary_specification> は、null バイトで満たされる書き込みメモリの範囲を指定するソケット式です。

ResetName - SETZERO $1 : [0,$2]
initNode - SETZERO $1-><id>Node::name</id> : [160,160]

この例の最初のレコードは、関数 'ResetName' が最初のパラメーターによってポイントされたメモリ領域の最初のバイトを null バイトで満たすことを示します。書き込まれるバイト数は 2 番目の引数として渡されます。2 番目のレコードは、関数 'initNode' が定数 160 をインデックスとして使用して、1 null バイトをバッファ 'name' (最初の引数で渡される構造のメンバー) に書き込むことを示します。

SLEEP レコード

SLEEP レコードは、関数が長時間に渡ってプログラムの実行をブロックできることを指定します。

指定フィールドの構文

   <socket_expression> ':'<Interval>

フィールド

  • <precondition> は、ブロック操作に対して満たす必要がある条件か、または事前条件がなく、ブロックが常に実行されることを示す 1 です。

read - SLEEP 1
WaitForSingleObject - SLEEP $2 NE(0)

最初のレコードは、関数 'read' が一定期間に渡ってプロセスの実行を一時停止できることを示します。2 番目のレコードは、その 2 番目の引数が 0 に等しくない場合、関数 'WaitForSingleObject' がプロセス実行をブロックする可能性があることを示します。

SQL インジェクションデータレコード

SQL インジェクションデータレコードは、SQL を実行する関数や、SQL コマンドを示すステートメントオブジェクトを準備する関数に関連しています。このデータレコードは次のとおりです。

  • SQLExec は、SQL クエリを実行する関数を指定します。

  • SQLProp は、1 つのパラメーターから別のパラメーター (文字列、またはプリペアド SQL ステートメントオブジェクト) に SQL クエリを伝播する関数を指定します。

  • SQLValidate は、SQL クエリをチェックする関数を指定します。

SQLExec と SQLValidate の指定フィールドの構文

  <socket_expression>

フィールド

<socket_expression> は、SQL クエリの特性を定義します。

以下の例は、SQLite C/C++ API を使用し、データレコードの使用法を示しています。

例 1

  sqlite3_exec - SQLExec $2

このレコードは、sqlite3_exec に渡される 2 つ目の引数に SQL コマンドが指定され、この SQL コマンドが sqlite3_exec の呼び出しで実行されることを示しています。

例 2

sqlite3_reset - SQLValidate $1

このレコードは、sqlite3_reset に渡される最初の引数に SQL コマンドが指定され、この SQL コマンドが sqlite3_reset の呼び出しで検証されることを示しています。この検証が成功すると、最初の引数で示された SQL コマンドは (sqlite3_exec などを使用して) 実行しても安全であることが保証されます。

SQLProp の指定フィールドの構文

  <socket-expression> : <socket-expression>

フィールド

<socket_expression> には、SQL クエリと SQL クエリが伝播されるオブジェクトの特性を定義します。

以下の例は、SQLite C/C++ API を使用し、このデータレコードの使用法を示しています。

例 3

  sqlite3_prepare_v2 - SQLProp $2 : *$4

このレコードは、sqlite3_prepare_v2 の 2 つ目の引数に、4 つ目の引数が指すオブジェクトの作成に使用される SQL コマンドが指定されることを示しています。実際には、2 つ目の引数に格納される SQL コマンドが、4 つ目の引数が指すオブジェクトに伝播されます。

汚染データレコードおよび安全でないデータレコード

汚染データレコードは、未検証データを返したり使用したりする可能性がある関数に関係しています。汚染データレコードは次のとおりです。

  • TaintedIntData は整数を返す関数を指定します。
  • TSCheckPT は文字列をチェックする機能を指定します。
  • TSCheckXSS はクエリ文字列を消去する関数を指定します。このレコードは、欠陥を報告せずに分析を中止する必要がある KB 内の関数をリストアップするために使用される、1 つのソケット式を使用します。

  • TSFMTsink は書式文字列を返す関数を指定します。
  • TSprop および TSsrc は文字列を返す関数を指定します。
  • TSSinkXSS は標準出力ストリームに作成込むことができる関数を指定します。このレコードは 2 つのソケット式を使用しており、最初のソケットは汚染された文字列を渡すことができる関数パラメーターのインデックスを与え、2 番目 (オプション) のソケットは FILE* タイプの入力を受け入れるパラメーターのインデックスを与えます。$0 (オブジェクト) または $$ (戻り値変数) は使用できません。式にソケットが 1 つある場合 (TSSinkXSS $3 など)、サブ選択条件は存在しません。式に 2 つのソケットがある場合 (TSSinkXSS $3 : $1 等)、関数「stdout」「_iob[1]」「&_iob[1]」「__acrt_iob_func(1)」「&__iob_func()[1]」「__iob_func()[1]」「(__getreent( ))->_stdout") の KB は作成できません。

  • UnsafeAllocSizeAccepter はメモリ割り当てサイズのデータを使用する関数を指定します。
  • UnsafeArrayIndexAccepter は配列インデックスとしてデータを使用する関数を指定します。
  • UnsafeBinopAccepter は算術二項演算でデータを使用する関数を指定します。
  • UnsafeLoopBoundAccepter はループ境界としてデータを使用する関数を指定します。

指定フィールドの構文

   <socket-expression>

フィールド、<socket_expression> は汚染データの特性を定義します

例 1

win32_fread - TaintedIntData *$1

このレコードは、win32_fread の最初の引数によってポイントされる変数に、呼び出し後に汚染値が含まれることを示します。

例 2

<function name> - TSCheckPT 1 : $<parameter number> : 1

このレコードで、<function name> はファイルパスを中立化させるために使用される関数、また、<parameter number> は中立化が必要なファイルパスを受け取るパラメーターの数です。

例 3

strcat - TSprop $1 : $1 , $2

このレコードは、最初または 2 番目の引数が汚染データを含むバッファをポイントする場合に、最初の引数として strcat に渡されるバッファが呼び出しから返された後に汚染されていることを指定します。

例 4

str_new - UnsafeAllocSizeAccepter $1

このレコードは、関数 str_new が最初の引数を使用して割り当てに必要なメモリのサイズを計算しても、この値が有効かどうかをチェックしないことを示します。

例 5

puts - TSSinkXSS $1

puts() 関数では、最初のパラメーターに汚染された文字列を割り当てることができます。FILE* タイプのパラメーターはありません。

例 6

fprintf - TSSinkXSS $3 : $1

fprintf() 関数では、第三のパラメーターに汚染された文字列を割り当てることができます。最初のパラメーターは FILE* タイプであり、標準出力に対してチェックする必要があります。

例 7

func - TSCheckXSS $1

汚染された文字列が func() 関数の最初のパラメーターに渡された場合、欠陥を報告せずに分析を中止する必要があります。

ユーザー名およびパスワードのレコード

ユーザー名およびパスワードのレコードは、何らかの認証を実行し、特定されたデータを入力パラメータとして取る関数に関係しています。これらのレコードは HCC チェッカーを拡張します。

  • HCC.SINK.USER レコードは、こうした関数のユーザー名用の引数を指定します。
  • HCC.SINK.PWD レコードは、こうした関数のパスワード用の引数を指定します。

指定フィールドの構文

   <socket-expression>

フィールド、 <socket_expression> はユーサー名関数の引数 HCC.SINK.USER またはパスワード関数の引数 HCC.SINK.PWD を定義します。

例 1

db_connect - HCC.SINK.USER 1 : $2 : 1
db_connect - HCC.SINK.PWD 1 : $3 : 1 

これらのレコードは、db_connect 関数の 2 番目の引数をユーザー名に使用し、3 番目の引数を認証パスワードに使用することを指定します。

xERRNO レコード

xERRNO は、関数が ERRNO 値を設定するかどうかを指定します。C で使用されるグローバル変数 ERRNO は、errno.h ヘッダーファイルで定義されます。

ERRNO の値は、C の一部の関数呼び出しに対して自動的に設定されます。ERRNO 変数の値を使用して、どのエラーが発生したかを識別できます。

指定フィールドの構文

   <flag>

フィールド

  • <flag> は、関数が ERRNO の値を設定するかどうかを示す値を定義します。

  • <flag> 値 0 は、C 標準で文書化されているように、関数呼び出しで ERRNO の値が設定されないことを意味します。

  • <flag> 値 1 は、C 標準で文書化されているように、関数呼び出しで ERRNO の値が設定されることを意味します。

例 1

fopen - xERRNO 0
ftell - xERRNO 0

これらのレコードは、ERRNO の値を設定しない C 関数を指定します。したがって、フラグ値 0 が使用されます。

例 2

fgetwc - xERRNO 1
strtoull - xERRNO 1

これらのレコードは、ERRNO の値を設定する C 関数を指定します。したがって、フラグ値 1 が使用されます。

XMRF レコード

XMRF レコードは、渡されたポインターにより割り当てられるメモリのオーナーシップを関数が保持するか移管するかを指定します。Klocwork 解析のチューニングのための XMRF レコードの作成方法例については、C/C++ 解析のチューニングを参照してください。

後方互換性に関する注意

以前は、オーナーシップの移管を指定する knowledge base (ナレッジベース) レコードが DMEM MRF/NMRF でした。現在、このレコードは XMRF と呼ばれ、レコードの構文も DMEM 構文から変更されています。ただし、Klocwork では引き続き既存の DMEM MRF/NMRF レコードもサポートされます。

指定フィールドの構文

   <socket-expression> ':'<retention_flag>

フィールド、<retention_flag> は呼び出し側がオーナーシップを保持しないことを示す 0 か、呼び出し側がオーナーシップを保持することを示す 1 のいずれかです

f_act - XMRF $3 : 1
f_test - XMRF $2 : 0

これらのレコードは、関数呼び出し f_act が 3 番目の引数のオーナーシップを保持し、関数呼び出し f_test が 2 番目の引数のオーナーシップを f_test に移管することを示します。