4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AUTOSAR CountdownAdvent Calendar 2022

Day 11

CERT C入門(1) Rule 01. Preprocessor (PRE)

Last updated at Posted at 2018-03-20

CERT C

CERT Cを道具としてうまく使いこなせるようになるまでの道を記述します。

CERT C
https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard
は、Security用のC言語コーディング標準です。
MISRA Cなどのコーディング標準、道具類、規格類をまとめて、実践的に対応しています。

CERT Cは、頻繁に構造が変わることがあリます。ここに記載したURLは、2018年3月22日(水)現在のものです。URLが繋がらなければ、見出しの文字で検索してください。

現在、17分類です。QiitaのURLは現在作業しているものdす。

Rule 01. Preprocessor (PRE)
https://qiita.com/kaizen_nagoya/items/8d2cb4158ed53d5a0a28
Rule 02. Declarations and Initialization (DCL)
https://qiita.com/kaizen_nagoya/items/8db2bf294bb66344d6df
Rule 03. Expressions (EXP)
Rule 04. Integers (INT)
Rule 05. Floating Point (FLP)
Rule 06. Arrays (ARR)
Rule 07. Characters and Strings (STR)
Rule 08. Memory Management (MEM)
Rule 09. Input Output (FIO)
Rule 10. Environment (ENV)
Rule 11. Signals (SIG)
Rule 12. Error Handling (ERR)
Rule 13. Application Programming Interfaces (API)
Rule 14. Concurrency (CON)
Rule 48. Miscellaneous (MSC)
Rule 50. POSIX (POS)
Rule 51. Microsoft Windows (WIN)

人生で影響を受けた本100冊。

CERT Cの利点

随時変更

最新の課題へ対応するように随時変更している。

コード

コンパイル可能そうなコードの断片を示している。

各種コーディング標準

関係を示している。

道具類

対応状況を示している。
CodeSonar
LDRA tool suite
Polyspace Bug Finder
... 規則によって対応できる道具の違いを示している。

文献

ISO/IEC 10646-2003
ISO/IEC 9899:2011
始め各種。

課題

構造の変化

Webで随時変更しており、時々構造を変更している。
どう追従するのがよいか思案中

コンパイル・実行による確認。

出力可能なコードになっておらず動作による確認のための作業が必要な場合がある。

参考文献のリンク切れ

重要度が低いと考えられているか、参考文献がリンク切れになっていたことがある。
https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography

2015年の時点のリンクの張りなおしは小川清が担当。

Rule 01. Preprocessor (PRE)

PRE30-C. Do not create a universal character name through concatenation

コンパイル

コンパイラ

LLVM

$ cc -v
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

gcc6

$ gcc-6 --version
gcc-6 (Homebrew GCC 6.4.0_1) 6.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Rule 01. Preprocessor (PRE)

PRE30-C Do not create a universal character name through concatenation

funcをmainに変更。実行するため。

pre30非適合例

pre30n.c
#define assign(uc1, uc2, val) uc1##uc2 = val
 
int main(void) {
  int \u0401;
  /* ... */
  assign(\u04, 01, 4);
  /* ... */
  return \u0401;
}
$ cc pre30n.c 
noncompliant.c:3:1: warning: return type of 'main' is not 'int'
      [-Wmain-return-type]
void main(void) {
^
noncompliant.c:3:1: note: change return type to 'int'
void main(void) {
^~~~
int
noncompliant.c:6:10: warning: incomplete universal character name; treating as
      '\' followed by identifier [-Wunicode]
  assign(\u04, 01, 4);
         ^
noncompliant.c:6:10: error: expected expression
2 warnings and 1 error generated.

$記号は入力促進記号(prompt)です。$の文字を入れる必要はありません。
main関数の引数、戻り値を調整。

pre30n.c
#include <stdio.h>
#define assign(uc1, uc2, val) uc1##uc2 = val
 
int main(int argc, char** argv) {
  int \u0401;
  /* ... */
  assign(\u04, 01, 4);
  /* ... */
  printf("%d \n",\u401);
  return argc;
}
./cert.sh pre30n
clang pre30n.c
pre30n.c:7:10: warning: incomplete universal character name; treating as '\' followed by identifier [-Wunicode]
  assign(\u04, 01, 4);
         ^
pre30n.c:7:10: error: expected expression
pre30n.c:9:18: warning: incomplete universal character name; treating as '\' followed by identifier [-Wunicode]
  printf("%d \n",\u401);
                 ^
pre30n.c:9:18: error: expected expression
2 warnings and 2 errors generated.
./cert.sh: line 4: ./pre30nl: No such file or directory
gcc-6 pre30n.c
pre30n.c: In function 'main':
pre30n.c:7:10: error: stray '\' in program
   assign(\u04, 01, 4);
          ^
pre30n.c:2:31: note: in definition of macro 'assign'
 #define assign(uc1, uc2, val) uc1##uc2 = val
                               ^~~
pre30n.c:7:11: error: 'u0401' undeclared (first use in this function)
   assign(\u04, 01, 4);
           ^
pre30n.c:2:31: note: in definition of macro 'assign'
 #define assign(uc1, uc2, val) uc1##uc2 = val
                               ^~~
pre30n.c:7:11: note: each undeclared identifier is reported only once for each function it appears in
   assign(\u04, 01, 4);
           ^
pre30n.c:2:31: note: in definition of macro 'assign'
 #define assign(uc1, uc2, val) uc1##uc2 = val
                               ^~~
pre30n.c:9:18: error: stray '\' in program
   printf("%d \n",\u401);
                  ^
pre30n.c:9:19: error: 'u401' undeclared (first use in this function)
   printf("%d \n",\u401);
                   ^~~~

pre30適合例

pre30c.c
#define assign(ucn, val) ucn = val 

void main(void) { 
  int \u0401; 
   /* ... */ 
  assign(\u0401, 4); 
   /* ... */ 
} 
$ cc pre30c.c
compliant.c:3:1: note: change return type to 'int'
void main(void) { 
^~~~
int
1 warning generated.

main関数の引数、戻り値を調整。

pre30c.c
#include <stdio.h>
#define assign(ucn, val) ucn = val 

int main(int argc, char **argv) { 
  int \u0401; 
   /* ... */ 
  assign(\u0401, 4); 
   /* ... */ 
  printf("%d \n", \u0401);
  return argc;
}
./cert.sh pre30c
clang pre30c.c
4 
gcc-6 pre30c.c
4 

PRE31-C. Avoid side effects in arguments to unsafe macros

pre31n.c
#include <stdio.h>
#define ABS(x) (((x) < 0) ? -(x) : (x))
  
int main(int n, char** argv) {
  /* Validate that n is within the desired range */
  int m = ABS(++n);
  /* ... */
  printf("%d %d\n", m,n);
  return n;
}
$ cc pre31n.c
$./a.out
3 3
$ rm a.out
$ gcc-6 pre31n.c
$./a.out
3 3

pre31適合例

pre31c.c
#include <stdio.h>
#define ABS(x) (((x) < 0) ? -(x) : (x))
  
int main(int n, char** argv) {
  /* Validate that n is within the desired range */
  int m = ABS(++n);
  /* ... */
  printf("%d %d\n", m,n);
  return n;
}
$ ./cert.sh pre31c
clang pre31c.c
2 2
gcc-6 pre31c.c
2 2

pre31適合例2

pre31c2.c
#include <stdio.h>
#include <complex.h>
#include <math.h>
  
static inline int iabs(int x) {
  return (((x) < 0) ? -(x) : (x));
}
  
void func(int n) {
  /* Validate that n is within the desired range */
 
  int m = iabs(++n);
  printf("%d \n",m);
  /* ... */
}

int main(int argc, char** argv){
  func(argc);
  return argc;
}
$ ./cert.sh pre31c2
clang pre31c2.c
2 
gcc-6 pre31c2.c
2 
$ ./cert.sh pre31c2 new
clang pre31c2.c
3 
gcc-6 pre31c2.c
3 

pre31適合例3

pre31c3.c
#include <stdio.h>
#include <complex.h>
#include <math.h>
  
static inline long long llabs(long long v) {
  return v < 0 ? -v : v;
}
static inline long labs(long v) {
  return v < 0 ? -v : v;
}
static inline int iabs(int v) {
  return v < 0 ? -v : v;
}
static inline int sabs(short v) {
  return v < 0 ? -v : v;
}
static inline int scabs(signed char v) {
  return v < 0 ? -v : v;
}
  
#define ABS(v)  _Generic(v, signed char : scabs, \
                            short : sabs, \
                            int : iabs, \
                            long : labs, \
                            long long : llabs, \
                            float : fabsf, \
                            double : fabs, \
                            long double : fabsl, \
                            double complex : cabs, \
                            float complex : cabsf, \
                            long double complex : cabsl)(v)
  
void func(int n) {
  /* Validate that n is within the desired range */
  int m = ABS(++n);
  /* ... */
  printf("%d \n",m);
  /* ... */
}

int main(int argc, char** argv){
  func(argc);
  return argc;
}
./cert.sh pre31c3
clang pre31c3.c
2 
gcc-6 pre31c3.c
2 

pre31適合例4

pre31c4.c
#include <stdio.h>

#include <complex.h>
#include <math.h>
  
#define ABS(x) __extension__ ({ __typeof (x) tmp = x; \
                    tmp < 0 ? -tmp : tmp; }) 
  
void func(int n) {
  /* Validate that n is within the desired range */
  int m = ABS(++n);
  /* ... */
  printf("%d \n",m);
  /* ... */
}

int main(int argc, char** argv){
  func(argc);
  return argc;
}
$ ./cert.sh pre31c4
clang pre31c4.c
2 
gcc-6 pre31c4.c
2 

pre31非適合例2

pre31n2.c
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
   
void process(size_t index) {
  assert(index++ > 0); /* Side effect */
  /* ... */
  printf("%d\n", index);
}
  
int main(int n, char** argv) {
  process(sizeof(argv));  
  /* ... */
  return n;
} 
./cert.sh pre31n2
clang pre31n2.c
pre31n2.c:8:18: warning: format specifies type 'int' but the argument has type 'size_t' (aka 'unsigned long') [-Wformat]
  printf("%d\n", index);
          ~~     ^~~~~
          %zu
1 warning generated.
9
gcc-6 pre31n2.c
9

本題とは関係ないがllvm/clangとgccで、既定値で警告を出す、出さないの違い。

pre31適合例5

pre31c5.c
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
   
void process(size_t index) {
  assert(index > 0); /* No side effect */
  ++index;
  /* ... */
  printf("%d\n", (int)index);
}
  
int main(int n, char** argv) {
  process(sizeof(argv));  
  /* ... */
  return n;
} 

警告が出ないようにcast。

$ ./cert.sh pre31c5
clang pre31c5.c
9
gcc-6 pre31c5.c
9

##PRE32-C. Do not use preprocessor directives in invocations of function-like macros
https://wiki.sei.cmu.edu/confluence/display/c/PRE32-C.+Do+not+use+preprocessor+directives+in+invocations+of+function-like+macros

pre32非適合例

pre32n.c
#include <stdio.h>
#include <string.h>
void func(const char *src) {
  /* Validate the source string; calculate size */
  char *dest;
  /* malloc() destination string */
  memcpy(dest, src,
    #ifdef PLATFORM1
      12
    #else
      24
    #endif
  );
  /* ... */
}
  
int main(int n, char** argv) {
 
  func(argv[1] );

  printf("%s \n",argv[1]);
  return n;
} 

mallocしていない。コンパイルして実行すると。

./cert.sh pre32n hello
clang pre32n.c
./cert.sh: line 4: 86208 Bus error: 10           ./$1l $2
gcc-6 pre32n.c
./cert.sh: line 7: 86217 Segmentation fault: 11  ./$1g $2

LLVM/clangとgccとでエラーになる行数が違うところに注目。
実行エラーにならないように書き換え。

pre32n.c
#include <stdio.h>
#include <string.h>
void func(const char *src) {
  /* Validate the source string; calculate size */
  char *dest[24];
  /* malloc() destination string */
  memcpy(dest, src,
    #ifdef PLATFORM1
      12
    #else
      24
    #endif
  );
  /* ... */
}

int main(int n, char** argv) {

  func(argv[1] );

  printf("%s \n",argv[1]);
  return n;
}

実行

./cert.sh pre32n helloworld20180322wednesday
clang pre32n.c
helloworld20180322wednesday 
gcc-6 pre32n.c
helloworld20180322wednesday 

なお、shellは下記のように第二引数を渡す書き換え

cert.sh
#!/bin/sh
echo "clang $1.c"
cc $1.c -o $1l
./$1l $2
echo "gcc-6 $1.c"
gcc-6 $1.c -o $1g
./$1g $2

pre32適合例

pre32c.c
#include <stdio.h>
#include <string.h>
 
void func(const char *src) {
  /* Validate the source string; calculate size */
  char *dest;
  /* malloc() destination string */ 
  #ifdef PLATFORM1
    memcpy(dest, src, 12);
  #else
    memcpy(dest, src, 24);
  #endif
  /* ... */
}
  
int  main(int n, char ** argv) {
  
  func(argv[1]);

  printf("%s\n", argv[1]);
  return n;
}

同様に実行時エラー。下記のように書き換え。

pre32c.c
#include <stdio.h>
#include <string.h>
 
void func(const char *src) {
  /* Validate the source string; calculate size */
  char dest[24];
  /* malloc() destination string */ 
  #ifdef PLATFORM1
    memcpy(dest, src, 12);
  #else
    memcpy(dest, src, 24);
  #endif
  /* ... */
}
  
int  main(int n, char ** argv) {
  
  func(argv[1]);

  printf("%s\n", argv[1]);
  return n;
}
./cert.sh pre32c helloworld20180322wednesday
clang pre32c.c
helloworld20180322wednesday
gcc-6 pre32c.c
helloworld20180322wednesday

これらの処理の自動化は
コンパイル用shell script
https://qiita.com/kaizen_nagoya/items/74220c0577a512c2d7da

<この記事は個人の過去の経験に基づく個人の感想です。現在所属する組織、業務とは関係がありません。>

文書履歴(document history)

ver. 0.01 初稿  20180321
ver. 0.02 ありがとう追記 20230513

最後までおよみいただきありがとうございました。

いいね 💚、フォローをお願いします。

Thank you very much for reading to the last sentence.

Please press the like icon 💚 and follow me for your happy life.

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?