このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。
CWE Rule 690
説明
ルールの説明
The product does not check for an error after calling a function that can return with a NULL pointer if the function fails, which leads to a resultant NULL pointer dereference.
Polyspace 実装
ルール チェッカーは以下の問題をチェックします。
要注意の関数の戻り値がチェックされていません
汚染された NULL 文字列または非 NULL 終端文字列
保護されていない動的メモリ割り当て
例
この問題は、エラー発生の可能性に関する情報を返す要注意の標準関数を呼び出し、以下のいずれかを行った場合に発生します。
戻り値を無視。
戻り値を変数に単純に代入することも、戻り値を明示的に
void
にキャストすることもしていない。エラーの戻り値をテストすることなく関数の出力 (戻り値または引数の参照渡し) を使用。
以下のような理由で関数呼び出しで障害を発生する可能性が高い場合、チェッカーはその関数を要注意と見なします。
システム リソースが使い尽くされた状態 (リソースを割り当てるときなど)。
権限またはアクセス許可が変更された状態。
外部ソースからのデータを読み取り、書き込みまたは変換する際にソースが汚染された状態。
既存の API があってもサポートされない機能。
チェッカーは、関数がエラーなしで終了したかどうかを "戻り値" が示す関数のみを考慮します。
このような関数の一部では、以下のような重要なタスクを実行する可能性があります。
権限の設定 (
setuid
など)jail の作成 (
chroot
など)プロセスの作成 (
fork
など)スレッドの作成 (
pthread_create
など)ミューテックスのロックまたはロック解除 (
pthread_mutex_lock
など)メモリ セグメントのロックまたはロック解除 (
mlock
など)
要注意のタスクを実行する関数の戻り値をチェックせず、戻り値を通してエラー情報を示さない場合は、プログラムが予期せぬ動作をする可能性があります。これらの関数のエラーはプログラム全体に伝播し、不適切な出力、セキュリティの脆弱性およびシステム障害の原因となる可能性があります。
プログラムを続行する前に、"重要で要注意" の関数の戻り値をテストします。
要注意の関数ではない場合は、その関数を void
にキャストすることによって戻り値を明示的に無視することができます。Polyspace® は、要注意の関数が void にキャストされている場合はこの欠陥を発生させません。"重要で要注意の関数" の場合、さらに脆弱なタスクが実行されるため、この解決策は許容されません。
#include <pthread.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
void initialize() {
pthread_attr_t attr;
pthread_attr_init(&attr);//Noncompliant
}
int read_file(int argc, char *argv[])
{
FILE *in;
if (argc != 2) {
/* Handle error */
}
in = fmemopen (argv[1], strlen (argv[1]), "r");
return 0; //Noncompliant
}
この例は、要注意の POSIX 関数 pthread_attr_init
および fmemopen
の呼び出しを示しています。それらの戻り値が無視され、欠陥の原因になっています。
(void)
にキャスト考えられる 1 つの修正方法として、関数を void にキャストします。この修正では Polyspace およびレビュー担当者に、要注意の関数の戻り値を明示的に無視していることを伝えます。
#include <pthread.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
void initialize() {
pthread_attr_t attr;
(void)pthread_attr_init(&attr);//Compliant
}
int read_file(int argc, char *argv[])
{
FILE *in;
if (argc != 2) {
/* Handle error */
}
(void)fmemopen (argv[1], strlen (argv[1]), "r"); //Compliant
return 0;
}
1 つの修正方法として、pthread_attr_init
と fmemopen
の戻り値をテストして、エラーをチェックします。
#include <pthread.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
void initialize() {
pthread_attr_t attr;
int result = pthread_attr_init(&attr);//Compliant
if(result != 0){
//Handle fatal error
}
}
int read_file(int argc, char *argv[])
{
FILE *in;
if (argc != 2) {
/* Handle error */
}
in = fmemopen (argv[1], strlen (argv[1]), "r");
if (in==NULL){
// Handle error
}
return 0;//Compliant
}
#include <pthread.h>
extern void *start_routine(void *);
void returnnotchecked() {
pthread_t thread_id;
pthread_attr_t attr;
void *res;
(void)pthread_attr_init(&attr);
(void)pthread_create(&thread_id, &attr, &start_routine, ((void *)0)); //Noncompliant
pthread_join(thread_id, &res); //Noncompliant
}
この例では、2 つの重要な関数 pthread_create
および pthread_join
を呼び出しています。pthread_create
の戻り値は void にキャストすることで無視されますが、pthread_create
は (要注意の関数であるだけでなく) 重要な関数であるため、Polyspace ではこの "要注意の関数の戻り値がチェックされていません" という欠陥が無視されません。他の重要な関数 pthread_join
は暗黙的に無視される値を返します。pthread_join
は、pthread_create
の戻り値を使用しますが、この戻り値はチェックされていません。
この欠陥の修正として、これらの重要な関数の戻り値をチェックして、関数が想定どおりに実行されることを検証します。
#include <pthread.h>
#include <stdlib.h>
#define fatal_error() abort()
extern void *start_routine(void *);
void returnnotchecked() {
pthread_t thread_id;
pthread_attr_t attr;
void *res;
int result;
(void)pthread_attr_init(&attr);
result = pthread_create(&thread_id, &attr, &start_routine, NULL);
if (result != 0) {
/* Handle error */
fatal_error();
}
result = pthread_join(thread_id, &res);
if (result != 0) {
/* Handle error */
fatal_error();
}
}
この問題は、strcpy
や sprintf
などの文字列バッファーを暗黙的にデリファレンスする文字列操作ルーチンで、セキュリティで保護されていないソースからの文字列が使用された場合に発生します。
汚染された NULL 文字列または非 NULL 終端文字列では、scanf
ファミリの可変個引数関数の呼び出しから返された文字列に関する欠陥が報告されません。同様に、文字列と一緒に %s
指定子を printf
ファミリの可変個引数関数に渡した場合も欠陥は報告されません。
文字列がセキュリティで保護されないソースに由来している場合、攻撃者により文字列が操作されている可能性や、文字列ポインターが異なるメモリ位置に向けられている可能性があります。
文字列が NULL である場合、文字列ルーチンは文字列をデリファレンスできず、プログラムがクラッシュする原因となります。文字列が null で終了しない場合、文字列ルーチンでは文字列がいつ終了するかわからない可能性があります。このエラーは範囲外への書き込みの原因となり、バッファー オーバーフローを引き起こします。
文字列は、使用する前に検証します。以下についてチェックします。
文字列が NULL でない。
文字列が null で終了している。
文字列のサイズが、必要なサイズと一致している。
既定では、Polyspace は外部ソースからのデータは汚染されていると仮定します。Polyspace 解析での汚染のソースを参照してください。Polyspace 解析の現在のスコープ以外から発生したすべてのデータを汚染されたものと見なすには、コマンド ライン オプション [-consider-analysis-perimeter-as-trust-boundary]
を使用します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SIZE128 128
#define MAX 40
extern void print_str(const char*);
void warningMsg(void)
{
char userstr[MAX];
int n = read(0,userstr,MAX);
char str[SIZE128] = "Warning: ";
if (n != -1)
strncat(str, userstr, SIZE128-(strlen(str)+1));//Noncompliant
print_str(str);
}
この例では、文字列 str
は引数 userstr
と連結しています。userstr
の値は不明です。userstr
のサイズが使用可能なスペースより大きい場合、この連結はオーバーフローします。
1 つの修正方法として、strncat
で使用する前に、userstr
のサイズをチェックして、文字列が必ず null で終了するようにします。この例では、補助関数 sansitize_str
を使用して文字列を検証しています。欠陥はこの関数に集中しています。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SIZE128 128
#define MAX 40
extern void print_str(const char*);
int sanitize_str(char* s) {
int res = 0;
if (s && (strlen(s) > 0)) { // Noncompliant
// - string is not null
// - string has a positive and limited size
// - TAINTED_STRING on strlen used as a firewall
res = 1;
}
return res;
}
void warningMsg(void)
{
char userstr[MAX];
int n = read(0,userstr,MAX);
char str[SIZE128] = "Warning: ";
if (n != -1 && sanitize_str(userstr))
strncat(str, userstr, SIZE128-(strlen(str)+1));
print_str(str);
}
別の修正方法として、特定の文字列を含む関数 errorMsg
および warningMsg
を呼び出します。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SIZE128 128
extern void print_str(const char*);
void warningMsg(char* userstr)
{
char str[SIZE128] = "Warning: ";
strncat(str, userstr, SIZE128-(strlen(str)+1));
print_str(str);
}
void errorMsg(char* userstr)
{
char str[SIZE128] = "Error: ";
strncat(str, userstr, SIZE128-(strlen(str)+1));
print_str(str);
}
int manageSensorValue(int sensorValue) {
int ret = sensorValue;
if ( sensorValue < 0 ) {
errorMsg("sensor value should be positive");
exit(1);
} else if ( sensorValue > 50 ) {
warningMsg("sensor value greater than 50 (applying threshold)...");
sensorValue = 50;
}
return sensorValue;
}
この問題は、前回のメモリ割り当てが成功したかどうかを初めにチェックせずに、動的に割り当てられたメモリにアクセスしたときに発生します。
malloc
、calloc
または realloc
を使用してメモリを動的に割り当てる際、要求されたメモリが使用可能でない場合は値 NULL
が返されます。割り当ての後、コードでこの NULL
値をチェックせずにメモリ ブロックにアクセスした場合、このアクセスの成功は保証されません。
割り当てられたメモリ位置にアクセスする前に、malloc
、calloc
、または realloc
の戻り値が NULL かどうかをチェックします。
int *ptr = malloc(size * sizeof(int));
if(ptr) /* Check for NULL */
{
/* Memory access through ptr */
}
#include <stdlib.h>
void Assign_Value(void)
{
int* p = (int*)calloc(5, sizeof(int));
*p = 2; //Noncompliant
/* Defect: p is not checked for NULL value */
free(p);
}
メモリ割り当てが失敗した場合、関数 calloc
は NULL
を p
に返します。p
を使用してメモリにアクセスする前に、コードでは p
が NULL
かどうかのチェックを行っていません。
1 つの修正方法として、デリファレンスの前に p
の値が NULL
かどうかをチェックすることができます。
#include <stdlib.h>
void Assign_Value(void)
{
int* p = (int*)calloc(5, sizeof(int));
/* Fix: Check if p is NULL */
if(p!=NULL) *p = 2;
free(p);
}
#include <stdlib.h>
#include<string.h>
typedef struct recordType {
const char* id;
const char* data;
} RECORD;
RECORD* MakerecordType(const char *id,unsigned int size){
RECORD *rec = (RECORD *)calloc(1, sizeof(RECORD));
rec->id = strdup(id); //Noncompliant
const char *newData = (char *)calloc(1, size);
rec->data = newData;
return rec;
}
この例では、前回の動的メモリ割り当てからの NULL
値をチェックせずにポインター rec
がデリファレンスされたときに、チェッカーが欠陥を報告します。
同様の問題がポインター newData
でも発生します。ただし、ポインターはデリファレンスされませんが、rec->data
にコピーされるだけなので、欠陥は報告されません。null の可能性があるポインターにコピーするだけでは問題になりません。たとえば、関数 recordType_new
の呼び出し元がデリファレンスの前に rec->data
の NULL
値をチェックすれば、null ポインターのデリファレンスは回避されます。
チェック情報
カテゴリ: その他 |
バージョン履歴
R2023a で導入
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Web サイトの選択
Web サイトを選択すると、翻訳されたコンテンツにアクセスし、地域のイベントやサービスを確認できます。現在の位置情報に基づき、次のサイトの選択を推奨します:
また、以下のリストから Web サイトを選択することもできます。
最適なサイトパフォーマンスの取得方法
中国のサイト (中国語または英語) を選択することで、最適なサイトパフォーマンスが得られます。その他の国の MathWorks のサイトは、お客様の地域からのアクセスが最適化されていません。
南北アメリカ
- América Latina (Español)
- Canada (English)
- United States (English)
ヨーロッパ
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)