メインコンテンツ

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

CERT C: Rule EXP47-C

無効な型の引数を使用して va_arg を呼び出さない

説明

ルール定義

無効な型の引数を使用して va_arg を呼び出さないようにします。1

Polyspace 実装

ルール チェッカーは以下の問題をチェックします。

  • 無効なデータ型が va_arg に渡されました

  • 現在の引数リストに対する va_arg 呼び出しが多すぎます

すべて展開する

問題

[無効なデータ型が va_arg に渡されました] は、va_arg 呼び出しのデータ型が、va_arg で読み取る可変個引数関数の引数のデータ型と一致しない場合に発生します。

たとえば、unsigned char 引数を可変個引数関数 func に渡します。既定の引数プロモーションにより、この引数は int にプロモートされます。unsigned char 引数を読み取る va_arg 呼び出しを使用すると、型の不一致が発生します。

void func (int n, ...) {
   //...   
   va_list args;
   va_arg(args, unsigned char);
   //...   
}

void main(void) {
   unsigned char c;
   func(1,c);
}

リスク

可変個引数関数 (可変個の引数をもつ関数) では、va_arg を使用して、可変引数リスト (va_list) から各引数を読み取ります。va_arg を使用しても、読み取る引数が実際に存在していたり、その引数のデータ型が va_arg 呼び出しのデータ型と一致することは保証されません。両方の条件が満たされていることは自分で確認する必要があります。

va_arg 呼び出しで正しくない型を読み取ると、未定義の動作が発生する可能性があります。関数の引数がスタック上に存在することが原因で、スタックの望ましくない領域にアクセスする場合があります。

修正方法

可変個引数関数に渡される引数のデータ型が、va_arg 呼び出しのデータ型と一致していることを確認します。

可変個引数関数の引数に対して既定の引数プロモーションが行われます。可変個引数関数の引数のデータ型をプロトタイプから決定することはできません。このような関数の引数では既定の引数プロモーションが実行されます (C99 規格の節 6.5.2.2 および節 7.15.1.1 を参照してください)。整数の引数に対して整数プロモーションが行われ、float 型の引数は double にプロモートされます。整数の引数では、charshort など、データ型を int で表すことができる場合、そのデータ型は int にプロモートされます。そうでない場合は unsigned int にプロモートされます。他のすべての引数ではプロモーションが行われません。

未定義の動作や処理系定義の動作を回避するには、可変個引数関数の使用を最小限にします。可変個引数関数の使用を検出するには、MISRA C:2012 Rule 17.1 または MISRA C++:2008 Rule 8-4-1 のチェッカーを使用します。

例 - 関数の引数タイプおよび va_arg の引数として使用される char
#include <stdarg.h>
#include <stdio.h>

unsigned char func(size_t count, ...) {
    va_list ap;
    unsigned char result = 0;
    va_start(ap, count);
    if (count > 0) {
        result = va_arg(ap, unsigned char); //Noncompliant
    }
    va_end(ap);
    return result;
}

void func_caller(void) {
    unsigned char c = 0x12;
    (void)func(1, c);
}

この例では、funcunsigned char 引数を受け取ります。この引数に対して int への既定の引数プロモーションが行われます。va_arg 呼び出しのデータ型は依然として unsigned char であり、これは int 引数タイプと一致しません。

修正 — intva_arg の引数として使用

1 つの修正方法として、va_arg を使用して int 引数を読み取ります。

#include <stdarg.h>
#include <stdio.h>

unsigned char func(size_t count, ...) {
    va_list ap;
    unsigned char result = 0;
    va_start(ap, count);
    if (count > 0) {
        result = va_arg(ap, int); 
    }
    va_end(ap);
    return result;
}

void func_caller(void) {
    unsigned char c = 0x12;
    (void)func(1, c); 
}
問題

[現在の引数リストに対する va_arg 呼び出しが多すぎます] は、va_arg の呼び出しの数が、対応する可変個引数関数に渡される引数の数を超えている場合に発生します。解析では、可変個引数関数が呼び出される場合にのみ欠陥が報告されます。

[現在の引数リストに対する va_arg 呼び出しが多すぎます] では、次の場合には欠陥を報告しません。

  • 可変個引数関数内の va_arg の呼び出しの数が不確定である。たとえば、呼び出しが外部ソースからのものである場合です。

  • va_arg で使用される va_list が無効である。

リスク

va_arg を呼び出すとき、va_list 内に使用可能な次の引数がないと、動作が未定義になります。va_arg の呼び出しによって、データが破損したり、予期しない結果が返されたりする可能性があります。

修正方法

適切な数の引数を可変個引数関数に渡すようにします。

例 - va_arg を呼び出すときに使用可能な引数がない
#include <stdarg.h>
#include <stddef.h>
#include <math.h>

/* variadic function defined with
* one named argument 'count'
*/
int variadic_func(int count, ...) {
    int result = -1;
    va_list ap;
    va_start(ap, count);
    if (count > 0) {
        result = va_arg(ap, int);
        count --;
        if (count > 0) {
/* No further argument available 
* in va_list when calling va_arg
*/	

            result += va_arg(ap, int); //Noncompliant
        }
    }
    va_end(ap);
    return result;
}

void func(void) {

    (void)variadic_func(2, 100); 

}

この例では、func() 内で呼び出されたときに、名前付き引数と可変個引数が 1 つだけ variadic_func() に渡されます。va_arg の 2 番目の呼び出しでは、ap で使用可能な可変個引数がそれ以上ないため、動作が未定義になります。

修正 — 適切な数の引数を可変個引数関数に渡す

1 つの修正方法として、適切な数の引数を可変個引数関数に渡すようにします。

#include <stdarg.h>
#include <stddef.h>
#include <math.h>

/* variadic function defined with
* one named argument 'count'
*/

int variadic_func(int count, ...) {
    int result = -1;
    va_list ap;
    va_start(ap, count);
    if (count > 0) {
        result = va_arg(ap, int);
        count --;
        if (count > 0) {

/* The correct number of arguments is
* passed to va_list when variadic_func()
* is called inside func()
*/			
            result += va_arg(ap, int); 
        }
    }
    va_end(ap);
    return result;
}

void func(void) {

    (void)variadic_func(2, 100, 200); 

} 

チェック情報

グループ: Rule 03.式 (EXP)

バージョン履歴

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.