CWE Rule 401
説明
ルールの説明
The software does not sufficiently track and release allocated memory after it has been used, which slowly consumes remaining memory.
Polyspace 実装
ルール チェッカーは以下の問題をチェックします。
メモリ リーク
スレッド固有のメモリ リーク
例
この問題は、malloc、calloc、realloc、または new を介して割り当てられたメモリのブロックが解放されなかった場合に発生します。メモリが関数内で割り当てられる場合、以下の条件を満たせば欠陥は生じません。
関数内で、
freeまたはdeleteを使用してメモリが解放される。関数が
malloc、calloc、reallocまたはnewによって割り当てられたポインターを返す。関数がグローバル変数またはパラメーターにポインターを格納する。
malloc などの動的メモリ割り当て関数はヒープ上のメモリを割り当てます。使用後にメモリを解放しないと、別の割り当てに使用できるメモリの量が減少します。メモリに制限がある組み込みシステムでは、プログラムの実行中でも、使用可能なヒープ メモリを使い果たす可能性があります。
動的に割り当てられたメモリへのアクセスがあるスコープを確認します。このスコープの最後でメモリ ブロックを解放します。
メモリのブロックを解放するには、メモリの割り当て時に使用したポインターに対して関数 free を使用します。次に例を示します。
ptr = (int*)malloc(sizeof(int));
...
free(ptr);同じモジュール内のメモリは同じ抽象化レベルで割り当てと解放を行うことをお勧めします。次の例の場合、func はメモリの割り当てと解放を同じレベルで行っていますが、func2 はそうではありません。
void func() {
ptr = (int*)malloc(sizeof(int));
{
...
}
free(ptr);
}
void func2() {
{
ptr = (int*)malloc(sizeof(int));
...
}
free(ptr);
}#include<stdlib.h>
#include<stdio.h>
void assign_memory(void)
{
int* pi = (int*)malloc(sizeof(int));
if (pi == NULL)
{
printf("Memory allocation failed");
return;
}
*pi = 42;
/* Defect: pi is not freed */
} //Noncompliantこの例では、malloc によって pi が動的に割り当てられています。関数 assign_memory はメモリを解放せず、pi も返しません。
1 つの修正方法として、pi が参照するメモリを関数 free を使用して解放することができます。関数 free は関数 assign_memory が終了する前に呼び出さなければなりません。
#include<stdlib.h>
#include<stdio.h>
void assign_memory(void)
{
int* pi = (int*)malloc(sizeof(int));
if (pi == NULL)
{
printf("Memory allocation failed");
return;
}
*pi = 42;
/* Fix: Free the pointer pi*/
free(pi);
}もう 1 つの修正方法は、ポインター pi を返すことです。pi を返すことにより、assign_memory を呼び出している関数が pi を使用してメモリ ブロックを解放できるようになります。
#include<stdlib.h>
#include<stdio.h>
int* assign_memory(void)
{
int* pi = (int*)malloc(sizeof(int));
if (pi == NULL)
{
printf("Memory allocation failed");
return(pi);
}
*pi = 42;
/* Fix: Return the pointer pi*/
return(pi);
}
#define NULL '\0'
void initialize_arr1(void)
{
int *p_scalar = new int(5);
} //Noncompliant
void initialize_arr2(void)
{
int *p_array = new int[5];
} //Noncompliant
この例では、関数により 2 つの変数 p_scalar および p_array が、キーワード new を使用して作成されています。しかしこれらの関数は、これらのポインターについてメモリをクリーン アップせずに終了しています。これらの関数はこれらの変数の作成に new を使用したので、そのメモリは、各関数の最後で delete を呼び出してクリーン アップしなければなりません。
このエラーを修正するため、delete ステートメントを new の初期化ごとに追加します。大かっこ [] を使用して変数をインスタンス化した場合は、delete の呼び出しにも大かっこを使用しなければなりません。
#define NULL '\0'
void initialize_arrs(void)
{
int *p_scalar = new int(5);
int *p_array = new int[5];
delete p_scalar;
p_scalar = NULL;
delete[] p_array;
p_scalar = NULL;
}このチェッカーは、既定の Polyspace® as You Code 解析では非アクティブにされます。Polyspace as You Code 解析で非アクティブにされるチェッカー (Polyspace Access)を参照してください。
この問題は、動的に割り当てられたスレッド固有のメモリをスレッドの終了前に開放しなかった場合に発生します。
スレッド固有のストレージを作成するには、通常、以下の手順を行います。
スレッド固有のストレージのキーを作成する。
スレッドを作成する。
各スレッドで、ストレージを動的に割り当ててから、このストレージとキーを関連付ける。
関連付けたら、格納されたデータはキーを使用して後から読み取ることができます。
スレッドの終了前に、キーを使用してスレッド固有のメモリを解放する。
チェッカーは、最後の手順が不足しているスレッドの実行パスにフラグを設定します。
チェッカーは、以下の関数のファミリに対して機能します。
tss_getおよびtss_set(C11)pthread_getspecificおよびpthread_setspecific(POSIX)
メモリに格納されたデータは、スレッドの終了後も他のプロセスから利用できます (メモリ リーク)。メモリ リークはセキュリティの脆弱性になるだけでなく、使用可能なメモリの量を縮小し、パフォーマンスを低下させる可能性があります。
スレッドの終了前に動的に割り当てられたメモリを解放します。
動的に割り当てられたメモリは、free などの関数を使用して明示的に開放できます。
または、キーを作成するときにデストラクター関数とキーを関連付けることができます。デストラクター関数は、キー値を引数に指定してスレッドの最後に呼び出されます。デストラクター関数の本体で、キーに関連付けられたメモリを解放することができます。この方法を使用している場合でも、Bug Finder は欠陥にフラグを設定します。適切なコメントを使用してこの欠陥は無視します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
#include <threads.h>
#include <stdlib.h>
/* Global key to the thread-specific storage */
tss_t key;
enum { MAX_THREADS = 3 };
int add_data(void) {
int *data = (int *)malloc(2 * sizeof(int));
if (data == NULL) {
return -1; /* Report error */
}
data[0] = 0;
data[1] = 1;
if (thrd_success != tss_set(key, (void *)data)) {
/* Handle error */
}
return 0;
}
void print_data(void) {
/* Get this thread's global data from key */
int *data = tss_get(key);
if (data != NULL) {
/* Print data */
}
}
int func(void *dummy) {
if (add_data() != 0) {
return -1; //Noncompliant
}
print_data();
return 0; //Noncompliant
}
int main(void) {
thrd_t thread_id[MAX_THREADS];
/* Create the key before creating the threads */
if (thrd_success != tss_create(&key, NULL)) {
/* Handle error */
}
/* Create threads that would store specific storage */
for (size_t i = 0; i < MAX_THREADS; i++) {
if (thrd_success != thrd_create(&thread_id[i], func, NULL)) {
/* Handle error */
}
}
for (size_t i = 0; i < MAX_THREADS; i++) {
if (thrd_success != thrd_join(thread_id[i], NULL)) {
/* Handle error */
}
}
tss_delete(key);
return 0;
}この例では、各スレッドの開始関数 func で次の 2 つの関数を呼び出しています。
add_data: この関数はストレージを動的に割り当て、関数tss_setを使用してそのストレージとキーを関連付けます。print_data: この関数は関数tss_getを使用して、格納されているデータを読み取ります。
func が戻った時点では、動的に割り当てられたストレージが解放されていません。
1 つの修正方法として、スレッドの開始関数が終わる前に、動的に割り当てられたメモリを明示的に開放します。訂正されたバージョンでは変更が強調表示されています。
この訂正されたバージョンでも、func のエラー処理セクションの return ステートメントには相変わらず欠陥が表示されます。エラー処理セクションに入るのは動的なメモリ割り当てが失敗した場合に限られるため、この欠陥は実際には発生しません。適切なコメントを使用してこの残りの欠陥は無視します。詳細は、以下を参照してください。
Polyspace ユーザー インターフェイスでのバグ修正または正当化による結果への対処 (Polyspace ユーザー インターフェイスで結果をレビューする場合)。
Polyspace Access でのバグ修正または正当化による結果への対処 (Polyspace Access) (Web ブラウザーで結果をレビューする場合)。
コードへの注釈付けと既知の結果または許容可能な結果の非表示 (IDE で結果をレビューする場合)
#include <threads.h>
#include <stdlib.h>
/* Global key to the thread-specific storage */
tss_t key;
enum { MAX_THREADS = 3 };
int add_data(void) {
int *data = (int *)malloc(2 * sizeof(int));
if (data == NULL) {
return -1; /* Report error */
}
data[0] = 0;
data[1] = 1;
if (thrd_success != tss_set(key, (void *)data)) {
/* Handle error */
}
return 0;
}
void print_data(void) {
/* Get this thread's global data from key */
int *data = tss_get(key);
if (data != NULL) {
/* Print data */
}
}
int func(void *dummy) {
if (add_data() != 0) {
return -1; //Noncompliant
}
print_data();
free(tss_get(key));
return 0;
}
int main(void) {
thrd_t thread_id[MAX_THREADS];
/* Create the key before creating the threads */
if (thrd_success != tss_create(&key, NULL)) {
/* Handle error */
}
/* Create threads that would store specific storage */
for (size_t i = 0; i < MAX_THREADS; i++) {
if (thrd_success != thrd_create(&thread_id[i], func, NULL)) {
/* Handle error */
}
}
for (size_t i = 0; i < MAX_THREADS; i++) {
if (thrd_success != thrd_join(thread_id[i], NULL)) {
/* Handle error */
}
}
tss_delete(key);
return 0;
}チェック情報
| カテゴリ: その他 |
バージョン履歴
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)