ISO/IEC TS 17961 [fileclose]
Failing to close files or free dynamic memory when they are no longer needed
説明
ルール定義
不要になったファイルを閉じない、または不要になった動的メモリを解放しない。1
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 */
}
この例では、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);
}
"リソース リーク" が発生するのは、FILE
ポインターを使用してファイル ストリームを開いているが、それを閉じる前に以下の処理を行った場合です。
ポインターのスコープの終了。
別のストリームへのポインターの割り当て。
ファイル ハンドルを明示的に、できるだけ早期に解放しないと、リソースが使い果たされることによりエラーが発生することがあります。
FILE
ポインターを、そのスコープの終了前か、そのポインターを別のストリームに割り当てる前に閉じます。
FILE
ポインターがスコープの終了前に解放されない#include <stdio.h>
void func1( void ) {
FILE *fp1;
fp1 = fopen ( "data1.txt", "w" );
fprintf ( fp1, "*" );
fp1 = fopen ( "data2.txt", "w" );
fprintf ( fp1, "!" );
fclose ( fp1 );
}
この例では、ファイル ポインター fp1
がファイル data1.txt
を指しています。fp1
は、data1.txt
のファイル ストリームから明示的に分離される前に、別のファイル data2.txt
へのアクセスに使用されています。
FILE
ポインターを解放1 つの修正方法として、fp1
を data1.txt
のファイル ストリームから明示的に分離します。
#include <stdio.h>
void func1( void ) {
FILE *fp1;
fp1 = fopen ( "data1.txt", "w" );
fprintf ( fp1, "*" );
fclose(fp1);
fp1 = fopen ( "data2.txt", "w" );
fprintf ( fp1, "!" );
fclose ( fp1 );
}
スレッド固有のメモリ リークは、動的に割り当てられたスレッド固有のメモリをスレッドの終了前に開放していない場合に発生します。
スレッド固有のストレージを作成するには、通常、以下の手順を行います。
スレッド固有のストレージのキーを作成する。
スレッドを作成する。
各スレッドで、ストレージを動的に割り当ててから、このストレージとキーを関連付ける。
関連付けたら、格納されたデータはキーを使用して後から読み取ることができます。
スレッドの終了前に、キーを使用してスレッド固有のメモリを解放する。
チェッカーは、最後の手順が不足しているスレッドの実行パスにフラグを設定します。
チェッカーは、以下の関数のファミリに対して機能します。
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; /* Report error */
}
print_data();
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;
}
この例では、各スレッドの開始関数 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; /* Report error */
}
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;
}
チェック情報
決定可能性:決定不可能 |
バージョン履歴
R2019a で導入
1 Extracts from the standard "ISO/IEC TS 17961 Technical Specification - 2013-11-15" are reproduced with the agreement of AFNOR. Only the original and complete text of the standard, as published by AFNOR Editions - accessible via the website www.boutique.afnor.org - has normative value.
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)