メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

CERT C: Rule MSC40-C

説明

ルール定義

制約に違反しないようにします。1

Polyspace 実装

ルール チェッカーは、"インライン制約が守られていません" をチェックします。

すべて展開する

問題

[インライン制約が守られていません] は、非静的インライン関数内で、ファイル スコープの変更可能な静的変数を参照するか、ローカルの変更可能な静的変数を定義する場合に発生します。チェッカーは、変数が const 修飾子付きでない場合、その変数を変更可能と見なします。

たとえば、var は、inline 関数 func 内で定義された変更可能な static 変数です。g_step は、同じインライン関数内で参照先となるファイル スコープの変更可能な静的変数です。

static int g_step;
inline void func (void) {
   static int var = 0;
   var += g_step;
}

リスク

複数の関数呼び出しで静的変数を変更すると、各呼び出しで同じ変数が変更されると想定されます。たとえば、func を呼び出すたびに、var1 については同じインスタンスがインクリメントされますが、var2 については別のインスタンスがインクリメントされます。

void func(void) {
   static var1 = 0;
   var2 = 0;
   var1++;
   var2++;
}

関数にインラインと非インラインの定義が (個別のファイル内に) ある場合にその関数を呼び出すとき、C 標準では、インラインまたは非インライン形式のいずれかを使用することがコンパイラに許可されます (ISO®/IEC 9899:2011、節 6.7.4).コンパイラで、1 つの呼び出しではインライン定義を使用し、別の呼び出しでは非インラインを使用している場合、両方の呼び出しで同じ変数が変更されることはなくなります。この動作は、静的変数に対する想定に反します。

修正方法

次のいずれかの修正方法を使用します。

  • 変数を変更する意図がない場合は、変数を const として宣言する。

    変数を変更しなければ、予期しない変更の問題は起こりません。

  • 変数を非 static にする。宣言から static 修飾子を削除します。

    変数が関数内で定義されている場合、その変数は標準ローカル変数になります。ファイル スコープで定義されている場合は extern 変数になります。この動作の変更が意図したとおりであることを確認します。

  • 関数を static にする。static 修飾子を関数定義に追加します。

    関数を static にすれば、インライン定義のあるファイルでは関数を呼び出すときに常にインライン定義が使用されます。他のファイルでは関数の別の定義が使用されます。どの関数定義が使用されるかという問題が、コンパイラ任せでなくなります。

例 - インラインおよび外部定義での静的変数の使用
/* file1. c  : contains inline definition of get_random()*/

inline unsigned int get_random(void) 
{

    static unsigned int m_z = 0xdeadbeef;  //Noncompliant
    static unsigned int m_w = 0xbaddecaf;  //Noncompliant

    /* Compute next pseudorandom value and update seeds */
    m_z = 36969 * (m_z & 65535) + (m_z >> 16); 
    m_w = 18000 * (m_w & 65535) + (m_w >> 16); 
    return (m_z << 16) + m_w;   
}


int call_get_random(void)
{
    unsigned int rand_no;
    int ii;
    for (ii = 0; ii < 100; ii++) {
         rand_no = get_random();
    }
    rand_no = get_random();
    return 0;
}
/* file2. c  : contains external definition of get_random()*/

extern unsigned int get_random(void)
{
    /* Initialize seeds */
    static unsigned int m_z = 0xdeadbeef;
    static unsigned int m_w = 0xbaddecaf;
    
    /* Compute next pseudorandom value and update seeds */
    m_z = 36969 * (m_z & 65535) + (m_z >> 16);
    m_w = 18000 * (m_w & 65535) + (m_w >> 16);
    return (m_z << 16) + m_w;
}

この例では、get_random()file1.c にインライン定義があり、file2.c に外部定義があります。file1.cget_random が呼び出された場合、コンパイラでは自由にインライン定義を使用するか外部定義を使用するかを選択できます。

get_random() のインライン バージョンにおいて m_z および m_w のバージョンを変更するかどうかは、使用される定義に依存します。この動作は、静的変数に対する通常の想定に矛盾します。get_random() を呼び出す場合は、常に同じ m_z および m_w を変更すると想定します。

修正 — インライン関数を静的化

1 つの修正方法として、インライン化された get_random() を静的にします。コンパイラに関係なく、その後、file1.c での get_random() の呼び出しではインライン定義を使用します。他のファイルでの get_random() の呼び出しでは外部定義を使用します。この修正により、使用される定義と、その定義の静的変数が変更されるかどうかについて、あいまいさが解消されます。

/* file1. c  : contains inline definition of get_random()*/

static inline unsigned int get_random(void) 
{

    static unsigned int m_z = 0xdeadbeef; 
    static unsigned int m_w = 0xbaddecaf; 

    /* Compute next pseudorandom value and update seeds */
    m_z = 36969 * (m_z & 65535) + (m_z >> 16); 
    m_w = 18000 * (m_w & 65535) + (m_w >> 16); 
    return (m_z << 16) + m_w;   
}


int call_get_random(void)
{
    unsigned int rand_no;
    int ii;
    for (ii = 0; ii < 100; ii++) {
         rand_no = get_random();
    }
    rand_no = get_random();
    return 0;
}
/* file2. c  : contains external definition of get_random()*/

extern unsigned int get_random(void)
{
    /* Initialize seeds */
    static unsigned int m_z = 0xdeadbeef;
    static unsigned int m_w = 0xbaddecaf;
    
    /* Compute next pseudorandom value and update seeds */
    m_z = 36969 * (m_z & 65535) + (m_z >> 16);
    m_w = 18000 * (m_w & 65535) + (m_w >> 16);
    return (m_z << 16) + m_w;
}

チェック情報

グループ: Rule 48.その他 (MSC)

バージョン履歴

R2019a で導入


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.