LoginSignup
3
2

ISO/IEC TS 17961:2013
Information Technology — Programming languages, their environments and system software interfaces — C Secure Coding Rules
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1624.pdf

n1624は、ISO/IEC JTC1 SC22 WG14の作業文書(Working Draft)です。
公式のISO/IEC TS 17961:2013原本ではありません。

ISO/IEC JTC1 SC22 WG14では、可能な限り作業文書を公開し、幅広い意見を求めています。技術内容を検討し、ISO/IEC JTC1 SC22 WG14にフィードバックするために用います。

<この項は書きかけです。順次追記します。>

作業予定

下記規則の例をコンパイルする予定です。
1: コンパイルエラーが出ないようにする。
 accfree.cがこの段階です。
2: 実行時エラーが出ないようにする。
 ptrcomp.cがこの段階です。
3: 意味のある出力が出るようにする。
 検討中。

複数のコンパイルが必要な例のある規則は、別項目としリンクを記載する。

主な作業内容

1: Header fileをincludeする。
元資料にも記載があると良い。
コンパイラによっては、知らんぷりしてエラーだけ出す場合と、ヘッダファイル名を推奨する場合と、がある。
2: main関数を追記する。
main関数がある場合もある。main関数に関数呼び出しを追記する場合もある。

int main(int argc; char ** arg)
int main(void)

など複数の宣言方法をとることがある

3: 変数、定数を宣言する。
ライブラリヘッダファイル に見当たらない変数、定数を宣言する。
完全に調べ切れているわけではない。無駄な宣言があれば、ご指摘ください。

4: 処理がわかる出力を記述する。
if文のどちらを通ったかなど、既存の出力ではわからない場合に追記する。
計算結果を出力していない場合も記載することがある。

5:コンパイル・リンク・実行結果を記録する。
コンパイルエラー、警告、リンクエラー等を記録する。
コンパイルができた場合には実行結果を記録する。
処理系の都合、システム設定の都合でうまく出ない場合には、
別のコンパイラ、処理系で順次試す。

現在利用中のコンパイラ

Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.4.0 on macOS 10.13.3
または
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin16.7.0
on macOS 10.12.6

gcc-7 (Homebrew GCC 7.3.0_1) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.

gccは順次追記中です。その後、Visual Studioを予定しています。

環境(Environment)

hosted Environment, macOS 10.13.3 or 10.12.9

コンパイル用shell script

C版(clangとgcc)とC++版(clang++とg++)
https://qiita.com/kaizen_nagoya/items/74220c0577a512c2d7da

目的(purpose)

 この資料の目的は、国際規格・日本工業規格の内容の理解ではなく、処理系の振る舞いを理解し、結果としてプログラマの意図通りに、作ったプログラムが安全に動作するかどうかの確からしさを向上させることにある。

成果(outcome)

 複数のプログラムを複数の処理系で処理することにより、
プログラマの意図が、特定の処理系の範囲内での実現を目指しているか、
複数の処理系で動作する可搬性を目指しているかの区分を明確にできること。

 特定の処理系の範囲内での実現を目指したプログラムを、
誤って別の処理系で翻訳して、プログラマの意図しない動作をさせないこと。

任意のデータ(意図しないデータを含む)に対する処理が、プログラマの意図した範囲に収めることができるかどうかを確かめ、Security の基本機能を実現する。
 

マクロ

p.ixに2つのマクロの記載あり。
get.hとして利用。
5.16,5.23 でstring
5.25, 5.29, 5.36, 5.39, 5.45でinteger
を利用

get.h
/// ISO/IEC JTC 1/SC 22/WG 14 N 1624 Date: 2012-06-26 ISO/IEC TS 17961, p.ix
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1624.pdf


//Tainted source macros
//The function-like macros GET_TAINTED_STRING and //GET_TAINTED_INTEGER defined in this section are used in the examples in this Technical Specification to represent one possible method to obtain a tainted string and tainted integer.

#define GET_TAINTED_STRING(buf, buf_size) \
do { \
const char *taint = getenv("TAINT"); \
if (taint == 0) { \
exit(1); \
} \
\
size_t taint_size = strlen(taint) + 1; \
if (taint_size > buf_size) { \
exit(1); \
} \
\
strncpy(buf, taint, taint_size); \
} while (0)
#define GET_TAINTED_INTEGER(type, val) \
do { \
const char *taint = getenv("TAINT"); \
if (taint == 0) { \
exit(1); \
} \
\
errno = 0; \
long tmp = strtol(taint, 0, 10); \
if ((tmp == LONG_MIN || tmp == LONG_MAX) && \
errno == ERANGE) \
; /* retain LONG_MIN or LONG_MAX */ \
val = tmp & ~(type)0; \
} while (0)

#4.5 mutilated value(コード断片のある用語定義)

mutilated.c
/// ISO/IEC JTC 1/SC 22/WG 14 N 1624 Date: 2012-06-26 ISO/IEC TS 17961, p.4
//http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1624.pdf

//4.5 mutilated value:result of an operation performed on an untainted value that yields either an undefined result (such as the result of signed integer overflow), the result of right-shifting a negative number, implicit conversion to an integral type where the value cannot be represented in the destination type, or unsigned integer wrapping

#include <stdio.h>///
#include <stdlib.h>///
#include <limit.h>///

int j = INT_MAX + 1; // j is mutilated
char c = 1234; // c is mutilated if char is eight bits
unsigned int u = 0U - 1; // u is mutilated


int main(void){///
  printf("j=%d:%x c=%x u=%d:%x \n",j,j,c,u,u);///
  return EXIT_SUCCESS;///
}///
shell
$ cc mutilated.c 
mutilated.c:7:17: warning: overflow in expression; result is -2147483648 with type 'int' [-Winteger-overflow]
int j = INT_MAX + 1; // j is mutilated
                ^
mutilated.c:8:10: warning: implicit conversion from 'int' to 'char' changes value from 1234 to -46 [-Wconstant-conversion]
char c = 1234; // c is mutilated if char is eight bits
     ~   ^~~~
2 warnings generated.
$ ./a.out
j=-2147483648:80000000 c=ffffffd2 u=-1:ffffffff 

5. Rules

5.1. Accessing an object through a pointer to an incompatible type [ptrcomp]

example
/// // ISO/IEC JTC 1/SC 22/WG 14 N 1624 Date: 2012-06-26 ISO/IEC TS 17961, p.7
//http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1624.pdf

void f(void) {
  if (sizeof(int) == sizeof(float)) {
    float f = 0.0f;
    int *ip = (int *)&f;
    printf("float is %f\n", f);
    (*ip)++; // diagnostic required
    printf("float is %f\n", f);
  }
}

上記を含む実行可能なコードに追記し、コンパイル、実行。

ptrcomp.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv){
  f();
  return EXIT_SUCCESS;
}
shell
$ ./gcc7ts.sh ptrcomp
$ clang ptrcomp.c
float is 0.000000
float is 0.000000

$ gcc-7 ptrcomp.c
float is 0.000000
float is 0.000000

以下では、例と作り込んだ部分と資料の断片とを一つのファイルとしている。
作り込んだところは///を記載するように変更中である。

5.2. Accessing freed memory

C Secure Coding Rules(2) 5.2. Accessing freed memory - kaizen_nagoya @ Qiita
https://qiita.com/kaizen_nagoya/items/c8198a6ca3d43b274ef5

5.3. Accessing shared objects in signal handlers [accsig]

accsig.c
#include <stdio.h> ///
#include <stdlib.h> ///初めから書いて置いて欲しい。
#include <string.h> ///初めから書いて置いて欲しい。
#define MAX_MSG_SIZE 24
char *err_msg;
void handler(int signum) {
  if ((strcpy(err_msg, "SIGINT detected.")) == err_msg){      
    // diagnostic required
    /* ... */
  }
}
int main(void) {
  signal(SIGINT, handler);
  err_msg = (char *)malloc(MAX_MSG_SIZE);
  if (err_msg == NULL) {
    /* Handle error condition */
       printf("NULL \n");///
  }
  if ((strcpy(err_msg, "No errors yet.")) == err_msg) {
    /* ... */
      printf("%s\n",err_msg);///
  }
  /* Main code loop */
    printf("EXIT \n");///

  return EXIT_SUCCESS;
}
shell
$ cc accsig.c 
$ ./a.out
No errors yet.
EXIT 

5.4. No assignment in conditional expressions [boolasgn]

boolasgn.c
#include <stdio.h> ///
#include <stdlib.h>///

int foo(){
  return true;
}

int main(int argc, char* argv[]}{ ///
  int x = TRUE;///
  int y = FALSE;///
  int p; ///
  int q; ///
  int v; ///
  int w; ///
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the expression x = y is used as the controlling expression of the while statement.

  while ( x = y ) { /* ... */ } // diagnostic required

//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the expression x = y is used as the controlling expression of the while statement.

  do { /* ... */ } while ( foo(), x = y ) ; // diagnostic required

//EXAMPLE 3 In this compliant example, no diagnostic is required because the expression x = y is not used as the controlling expression of the while statement.

  do { /* ... */ } while ( x = y, p == q ) ; // no diagnostic required

//Exceptions
// EX1: Assignment is permitted where the result of the assignment is itself a parameter to a comparison expression (e.g., x == y or x != y) or relational expression and need not be diagnosed.
//EXAMPLE This example shows an acceptable use of this exception.

  if ( ( x = y ) != 0 ) { /* ... */ }

// EX2: Assignment is permitted where the expression consists of a single primary expression.
//EXAMPLE 1 This example shows an acceptable use of this exception.

  if ( ( x = y ) ) { /* ... */ }

//EXAMPLE 2 In this noncompliant example, a diagnostic is required because && is not a comparison or relational operator and the entire expression is not primary.

  if ( ( v = w ) && flag ) { /* ... */ } // diagnostic required
// EX3: Assignment is permitted in the above contexts where it occurs in a function argument or array index.
//EXAMPLE This example shows an acceptable use of this exception.
  if ( foo( x = y ) ) { /* ... */ }
  return EXIT_SUCCESS;///
}

fooの定義が不完全です。修正予定。

shell
$ cc boolasgn.c 
boolasgn.c:19:13: warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
  while ( x = y ) { /* ... */ } // diagnostic required
          ~~^~~
boolasgn.c:19:13: note: place parentheses around the assignment to silence this warning
  while ( x = y ) { /* ... */ } // diagnostic required
            ^
          (    )
boolasgn.c:19:13: note: use '==' to turn this assignment into an equality comparison
  while ( x = y ) { /* ... */ } // diagnostic required
            ^
            ==
boolasgn.c:45:19: warning: too many arguments in call to 'foo'
  if ( foo( x = y ) ) { /* ... */ }
       ~~~        ^
2 warnings generated.
$ ./a.out
// 無限ループかも。

5.5. Calling functions in the C Standard Library other than abort, _Exit, and signal from within a signal handler [asyncsig]

C Secure Coding Rules(3) 5.5 Calling functions in the C Standard Library other than abort, _Exit, and signal from within a signal handler - kaizen_nagoya @ Qiita
https://qiita.com/kaizen_nagoya/items/caba4fddb9088ee07a1e

5.6. Calling functions with incorrect arguments [argcomp]

C Secure Coding Rules(4) 5.6. Calling functions with incorrect arguments [argcomp] - kaizen_nagoya @ Qiita
https://qiita.com/kaizen_nagoya/items/3d33047a8cc59e0d2965

5.7. Calling signal from interruptible signal handlers [sigcall]

sigcall.c
#include <stdio.h> ///
#include <stdlib.h>///

void handler(int signum) {
  if (signal(signum, handler) == SIG_ERR) { // diagnostic required
    /* ... */
    printf("SIG_ERR: signumd\n");
  }
  /* ... */
}
void f(void) {
  if (signal(SIGUSR1, handler) == SIG_ERR) {
    /* ... */
  printf("SIG_ERR:SIGUSR1d\n");
  }
  /* ... */
}
int main(int argc, char* argv[]){
    printf("main\n");  
  return EXIT_SUCCESS;///
}
shell
$ cc sigcall.c 
$ ./a.out
main

5.8. Calling system [syscall]

syscall.c
#include <stdio.h> ///
#include <stdlib.h>///
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because a string consisting of any_cmd and the tainted value stored in input is copied into cmdbuf and then passed as an argument to the system function to execute.
void f(char *input) {
  char cmdbuf[512];
  int len_wanted = snprintf(
  cmdbuf, sizeof(cmdbuf), "any_cmd '%s'", input);
  if (len_wanted >= sizeof(cmdbuf)) {
    perror("Input too long");
  } else if (len_wanted < 0) {
    perror("Encoding error");
  } else if (system(cmdbuf) == -1) { // diagnostic required
    perror("Error executing input");
  }
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because system is used to remove the .config file in the user’s home directory.
void g(void) {
  system("rm ~/.config"); // diagnostic required
}
int main(int argc, char* argv[]){///
  f("Hello World!");///
  g();///
 return EXIT_SUCCESS;///
}///
shell
$ cc syscall.c 
$ ./a.out
sh: any_cmd: command not found
rm: /Users/administrator/.config: is a directory

5.9. Comparison of padding data [padcomp]

padcomp.c
#include <stdio.h> ///for printf
#include <stdlib.h>///for EXIT_SUCCESS
#include <string.h>/// for memcmp
struct my_buf {
  char buff_type;
  size_t size;
  char buffer[50];
};
unsigned int buf_compare(
const struct my_buf *s1,
const struct my_buf *s2)
  {
  if (!memcmp(s1, s2, sizeof(struct my_buf))) { // diagnostic required
    /* ... */
    printf("a: %s, $s \n",s1->buffer, s2->buffer);
  }
printf("b: %s, $s \n",s2->buffer, s1->buffer);
return 0;
}
int main(int argc, char* argv[]){///
  const struct my_buf b1 = {'a',13,"Hello World!n\n",14};///
  const struct my_buf b2 = {'b', 10,"hello world\n",11};///
  buf_compare(&b1,&b2);//

 return EXIT_SUCCESS;///
}///

shell
$ cc  padcomp.c
$ ./a.out
b: hello world, Hello World! 

5.10. Converting a pointer to integer or integer to pointer [intptrconv]

intptrconv.c
#include <stdio.h> ///for printf
#include <stdlib.h>///for EXIT_SUCCESS
#include <assert.h> /// for assert
//EXAMPLE 1 In this noncompliant example, a diagnostic is required on an implementation where pointers are 64-bits and unsigned integers are 32 bits because the pointer ptr is converted to an integer.
void f(void) {
  char *ptr;
  /* ... */
  unsigned int number = (unsigned int)ptr; // diagnostic required
  /* ... */
  printf("f:%s ",ptr);
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the integer literal 0xdeadbeef is converted to a pointer.
unsigned int *g(void) {
  unsigned int *ptr = (unsigned int *)0xdeadbeef; // diagnostic required
  /* ... */
  printf("g:%d ",*ptr);
  return ptr;
}
//Exceptions
// EX1: A null pointer can be converted to an integer; it takes on the value 0. Likewise, a 0 integer can be converted to a pointer; it becomes the null pointer.
// EX2: Any valid pointer to void can be converted to intptr_t or uintptr_t and back with no change in value. (This includes the underlying types if intptr_t and uintptr_t are typedefs and any typedefs that denote the same types as intptr_t and uintptr_t.)
// EXAMPLE
void h(void) {
  intptr_t i = (intptr_t)(void *)&i;
  uintptr_t j = (uintptr_t)(void *)&j;
  void *ip = (void *)i;
  void *jp = (void *)j;
  assert(ip == &i);
  assert(jp == &j);
  printf("h: %ld %ld",i,j);
}
int main(int argc, char* argv[]){///
  f();///
  printf("%d ",*g());///
  h();///
return EXIT_SUCCESS;///
}///
shell
$ cc intptrconv.c 
$ ./a.out
Segmentation fault: 11

5.11. Converting pointer values to more strictly aligned pointer types [alignconv]

C Secure Coding Rules(5) 5.11 Converting pointer values to more strictly aligned pointer types - kaizen_nagoya @ Qiita
https://qiita.com/kaizen_nagoya/items/7bcfbdde26d50a3438be

5.12. Copying a FILE object [filecpy]

filecpy.c
#include <stdio.h> ///for printf
#include <stdlib.h>///for EXIT_SUCCESS
int main(void) {
  FILE my_stdout = *(stdout); // diagnostic required
  if (fputs("Hello, World!\n", &my_stdout) == EOF) {
  /* ... */
  }
return EXIT_SUCCESS;
}
shell
$ cc filecpy.c 
$ ./a.out
Hello, World!

5.13. Declaring the same function or object in incompatible ways [funcdecl]

C Secure Coding Rules(6) 5.13. Declaring the same function or object in incompatible ways [funcdecl] - kaizen_nagoya @ Qiita
https://qiita.com/kaizen_nagoya/items/756d3bac5e763f42c643

5.14. Dereferencing an out-of-domain pointer [nullref]

nullref.c
//Example(s)
//EXAMPLE In this noncompliant example, a diagnostic is required because if malloc returns NULL, then the call to memcpy will dereference the null pointer c_str.
#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
void f(const char *input_str) {
  size_t size = strlen(input_str) + 1;
  char *c_str = (char *)malloc(size);
  if ((memcpy(c_str, input_str, size)) == c_str) { //   diagnostic required
    /* ... */
    printf("%s \n",c_srt);
  }
  /* ... */
  free(c_str);
  c_str = NULL;
}
int main(int argc, char** argv){ ///
  const char *input_str=argv[0];///
  f(input_str); ///
  return EXIT_SUCCESS;///
}//
```/

```shell_session:shell
cc addrescape.c 
$ ./a.out
./a.out 

5.15. Escaping of the address of an automatic object [addrescape]

C Secure Coding Rules(7) 5.15. Escaping of the address of an automatic object [addrescape] - kaizen_nagoya @ Qiita
https://qiita.com/kaizen_nagoya/items/cb337c06f75291074f48

5.16. Conversion of signed characters to wider integer types before a check for EOF [signconv]

signconv.c
//Example(s)
//EXAMPLE In this noncompliant example, a diagnostic is required because the character of type char pointed to by c_str is converted to int without being cast to unsigned char first.
#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include "get.h" /// for  GET_TAINTED_STRING

int yy_string_get(char *c_str) {
  int c = EOF;
  if (c_str && *c_str) {
    c = *c_str++; // if char is signed, a 0xFF char can be confused with EOF
    printf("yy\n");
  }
  printf("str\n");
  return c;
}
/* ... */
char string[BUFSIZ];
int main(int argc, char** argv){ ///
  GET_TAINTED_STRING(string, BUFSIZ);
  if (yy_string_get( string) == EOF) // diagnostic required
  {  
    printf("EOF\n");///
  }
  printf("return\n");///
  return EXIT_SUCCESS;///
}///

実行結果調整中

5.17. Use of an implied default in a switch statement [swtchdflt]

swtchdflt.c
//Example(s)
//EXAMPLE In this noncompliant example, a diagnostic is required because not all possible values of widget_type are checked for in the switch statement.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS

enum WidgetEnum { WE_W, WE_X, WE_Y, WE_Z };
void f(enum WidgetEnum widget_type) {
  switch (widget_type) { // diagnostic required
  case WE_X:
    /* ... */
    printf("WE_X:%d\n",widget_type);
    break;
  case WE_Y:
    /* ... */
    printf("WE_Y:%d\n",widget_type);
    break;
  case WE_Z:
    /* ... */
    printf("WE_Z:%d\n",widget_type);
    break;
  }
}

int main(int argc, char** argv){ ///
  f(WE_W);///
   printf("WE_W:%d\n",WE_W);///
  return EXIT_SUCCESS;///
}///

shell
$ cc swtchdflt.c 
swtchdflt.c:9:11: warning: enumeration value 'WE_W' not handled in switch [-Wswitch]
  switch (widget_type) { // diagnostic required
          ^
swtchdflt.c:9:11: note: add missing switch cases
  switch (widget_type) { // diagnostic required
          ^
1 warning generated.
$ ./a.out
WE_W:0

5.18. Failing to close files or free dynamic memory when they are no longer needed [fileclose]

C Secure Coding Rules(8) 5.18. Failing to close files or free dynamic memory when they are no longer needed [fileclose] - kaizen_nagoya @ Qiita
https://qiita.com/kaizen_nagoya/items/a45f1ef86281710b8eb0

5.19. Failing to detect and handle standard library errors [liberr]

libber.c
#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS

//Example(s)
//EXAMPLE In this noncompliant example, a diagnostic is required because the return value of fseek is not checked for an error condition.
void test_unchecked_return(FILE *file, long offset) {
  fseek(file, offset, SEEK_SET); // diagnostic required
}
//NOTE Return values from the following functions (Table 3) do not need to be checked because their historical use has overwhelmingly omitted error checking, and the consequences are not relevant to security.

//Exceptions
//EX1: The use of a void cast to signify programmer intent to ignore a return value from a function need not be diagnosed.
//EXAMPLE This example shows an acceptable use of this exception.
void foo(FILE *file) {
  (void)fputs("foo", file);
  /* ... */
}
//EX2: Ignoring the return value of a function that cannot fail or whose return value cannot signify an error condition need not be diagnosed. For example, strcpy is one such function.
int main(void){ ///
  long offset;
  FILE * fp=NULL;
  FILE my_stdout = *(stdout); // diagnostic required
  fp = &my_stdout;
  if (NULL != fp) {///
     test_unchecked_return(fp,offset);///
     printf("seek:%d\n",fseek(fp, offset, SEEK_SET) );///
     foo(fp);///
  } else {///
    return EXIT_FAILURE;///
  }///
  return EXIT_SUCCESS;///
}///
shell
$ cc  libber.c 
$ ./a.out
seek:0

foo関数の出力するように変更予定

5.20. Forming invalid pointers by library function [libptr]

5.20.1. Library functions that take a pointer and an integer
5.20.2. Library functions that take two pointers and an integer
5.20.3. Library functions that take a pointer and two integers
5.20.4. Standard memory allocation functions

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for memset, memcpy
#include <wchar.h> /// for wcslen

//Example(s)
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the size argument used in the memset() call is one longer than the size allocated for p.

void f1(size_t nchars) {
  char *p = (char *)malloc(nchars);
  const size_t n = nchars + 1;
  if (p) {
    memset(p, 0, n); // diagnostic required
    /* ... */ 
  }
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the size argument used in the memset() call is possibly miscalculated. There is no requirement that the size of an int is the same as the size of a float.

void f2(void) {
  float a[4];
  const size_t n = sizeof(int) * 4;
  void *p = a;
  memset(p, 0, n); // diagnostic required
  /* ... */
}
//EXAMPLE 3 In this noncompliant example, a diagnostic is required because the size argument used in the memcpy() call is possibly miscalculated. The size of an int does not need to be smaller than that of a double.

void f3(int *a) {
  double b = 3.14;
  const size_t n = sizeof(*a);
  void *p = a;
  void *q = &b;
  if ((memcpy(p, q, n)) == p) { // diagnostic required
    /* ... */
  }
  /* ... */
}
//EXAMPLE In this noncompliant example, a diagnostic is required because the value of n is not computed correctly, allowing a possible write past the end of the object referenced by p.

void f4(char p[], const char *q) {
  const size_t n = sizeof(p);
  if ((memcpy(p, q, n)) == p) { // diagnostic required
    /* ... */
  }
  /* ... */
}
//EXAMPLE 5 In this noncompliant example, a diagnostic is required because the value of n that is used in the malloc() call has been possibly miscalculated.

wchar_t *f5(void) {
  const wchar_t *p = L"Hello, World!";
  const size_t n = sizeof(p) * (wcslen(p) + 1);
  wchar_t *q = (wchar_t *)malloc(n); // diagnostic required
  /* ... */
  return q;
}
int main(int argc, char** argv){ ///
  size_t nchars=4;///
  char* p ="end of";///
  char* q ="object";///
  int b = 3;///
  int * a= &b;///
  f1(nchars);///
  f2();///
  f3(a);///
  f4(p,q);///
   printf("f5:%ls\n",f5());///
  return EXIT_SUCCESS;///
}//]/
shell
$ cc libptr.c 
libptr.c:41:26: warning: sizeof on array function parameter will return size of 'char *' instead of
      'char []' [-Wsizeof-array-argument]
  const size_t n = sizeof(p);
                         ^
libptr.c:40:14: note: declared here
void f4(char p[], const char *q) {
             ^
1 warning generated.
$ ./a.out
Bus error: 10

Bus errorの原因調査予定。

5.21. Forming or using out-of-bounds pointers or array subscripts [invptr]

5.22. Freeing memory multiple times [dblfree]

dblfree.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because x could be freed twice depending on the value of error_condition.
#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS, NULL
//#include <string.h> /// for memset, memcpy
//#include <wchar.h> /// for wcslen

void f(size_t num_elem) {
  int error_condition = 0;
  int *x = (int *)malloc(num_elem * sizeof(int));
  if (x == NULL) {
    /* ... */
    printf("x == NULL\n")
  }
  /* ... */
  if (error_condition == 1) {
    /* ... */    
    printf("error_condition == 1\n")

    free(x);
  }
  /* ... */
  free(x); // diagnostic required
  printf("free(x)\n")
  x = NULL;
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because realloc may free c_str1 when it returns NULL, resulting in c_str1 being freed twice.

void g(char *c_str1, size_t size) {
  char *c_str2 = (char *)realloc(c_str1, size);
  if (c_str2 == NULL) {
    free(c_str1); // diagnostic required
    return;
  }
  printf("str:%s\n",c_str2);///
}
int main(void){ ///
  size_t t = sizeof(int);
  char * s = "dblfree";
  f(t);///
  g(s,t);///
  return EXIT_SUCCESS;///
}///
shell
$ cc dblfree.c 
$ ./a.out
free(x)
a.out(71277,0x7fffa098d340) malloc: *** error for object 0x10743afa2: pointer being realloc'd was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

5.23. Including tainted or out-of-domain input in a format string [usrfmt]

5.24. Incorrectly setting and using errno [inverrno]

5.24.1. Library functions that set errno and return an in-band error indicator
5.24.2. Library functions that set errno and return an out-of-band error indicator
5.24.3. Library functions that occasionally set errno and return an out-of-band error indicator
5.24.4. Library functions that may or may not set errno
5.24.5. Library functions that do not explicitly set errno

Example 1

inverrno.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because errno is used for error checking and errno is not set to zero before the C Standard Library function strtoul is called.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS, NULL
#include <errno.h> /// for errno
#include <limits.h> /// for 'ULONG_MAX'
#include <locale.h> /// for LC_ALL

void f(const char *c_str) {
  char *endptr = NULL;
  unsigned long number = strtoul(c_str, &endptr, 0);
  if (endptr == c_str || (number == ULONG_MAX && errno == ERANGE)) { // diagnostic required
    /* ... */
    printf("endptr == c_str\n");///
  } else {
    /* ... */
    printf("endptr != c_str\n");///
  }
  /* ... */
}

//EXAMPLE 2 In this noncompliant example, a diagnostic is required because errno is used for error checking and the return value of the call to the C Standard Library function signal is not checked before checking errno.

void g(void) {
  errno = 0;
  signal(SIGINT, SIG_DFL);
  if (errno != 0) { // diagnostic required
    /* ... */
    printf("errno != 0\n");///
  }
}

//EXAMPLE 3 In this noncompliant example, a diagnostic is required because errno is used for error checking and errno is checked after the call to the C Standard Library function setlocale because setlocale does not explicitly set errno.

void h(void) {
  errno = 0;
  setlocale(LC_ALL, "");
  if (errno != 0) { // diagnostic required
    /* ... */
    printf("LC_ALL:errno != 0\n");///
  }
}

int main(void){ ///
  char* s="inverrno";
  f(s);///
  g();///
  h();///
  return EXIT_SUCCESS;///
}///
shell
$ cc inverrno.c 
$ ./a.out
endptr == c_str

5.25. Integer division errors [diverr]

5.26. Interleaving stream inputs and outputs without a flush or positioning call [ioileave]

ioileave.c
//EXAMPLE In this noncompliant example, a diagnostic is required because fread and fwrite are called on the same file without an intervening call to fflush, fseek, fsetpos, or rewind on the file.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS, NULL
#include <errno.h> /// for errno
#include <limits.h> /// for 'ULONG_MAX'
#include <locale.h> /// for LC_ALL

void f(const char *filename, char append_data[BUFSIZ]) {
  char data[BUFSIZ];
  FILE *file;
  file = fopen(filename, "a+");
  if (file == NULL) {
    /* ... */
    printf("NULL == file\n");///
    return;///
  }
  if (fwrite(append_data, sizeof(char), BUFSIZ, file) != BUFSIZ) {
    /* ... */
    printf("NULL == file\n");///
    return;///
  }
  if (fread(data, sizeof(char), BUFSIZ, file) != 0) { // diagnostic required
    /* ... */
    printf("data:%s\n",data);
  }
  fclose(file);
}
int main(void){ ///
  char* s="ioileave.c";///
  char data[BUFSIZ];///
  f(s, data)/// 
  return EXIT_SUCCESS;///
}///
shell
$ cc ioileave.c 
$ ./a.out

5.27. Modifying string literals [strmod]

strmod.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the string literal "string literal" is modified through the pointer p.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS, NULL
#include <string.h> /// for strrchr

void f1(void) {
  char *p = "string literal";
  p[0] = 'S'; // diagnostic required
  /* ... */
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the string literal "/tmp/edXXXXXX" is modified by the C Standard Library function tmpnam.

void f2(void) {
  if (tmpnam("/tmp/edXXXXXX")) { // diagnostic required
    /* ... */
  }
}
//EXAMPLE 3 In this noncompliant example, a diagnostic is required because the string literal "/tmp/filename" is modified through the pointer returned from the C Standard Library function strrchr.

void f3(void) {
  char *last_slash = strrchr("/tmp/filename", '/');
*last_slash = '\0'; // diagnostic required
  /* ... */
}
//EXAMPLE 4 In this noncompliant example, a diagnostic is required because the string literal "/tmp/filename" is modified through the pointer returned from the C Standard Library function strrchr.

void f4(void) {
  *strrchr("/tmp/filename", '/') = '\0'; // diagnostic required
  /* ... */
}
//EXAMPLE 5 In this noncompliant example, a diagnostic is required because the string literal "/tmp/filename" is modified.
void f5(void) {
  "/tmp/filename"[4] = '\0'; // diagnostic required
  /* ... */
}
//Exception
//No diagnostic need be issued if the analyzer can determine that the value of the pointer to non-const is never used to attempt to modify the characters of the string literal.

int main(void) {
  char *p = "abc";
  printf("%s\n", p);
  f1();///
  f2();///
  f3();///
  f4();///
  f5();///
  return EXIT_SUCCESS;
}
shell
$ cc strmod.c 
strmod.c:15:7: warning: 'tmpnam' is deprecated: This function is provided for compatibility reasons only. Due to
      security concerns inherent in the design of tmpnam(3), it is highly recommended that you use mkstemp(3)
      instead. [-Wdeprecated-declarations]
  if (tmpnam("/tmp/edXXXXXX")) { // diagnostic required
      ^
/usr/include/stdio.h:186:1: note: 'tmpnam' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns in...
^
/usr/include/sys/cdefs.h:180:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((deprecated(_msg)))
                                                      ^
1 warning generated.
$ ./a.out
abc
Bus error: 10

5.28. Modifying the string returned by getenv, localeconv, setlocale, and strerror [libmod]

libmod.c
//Rule
//Modifying the objects or strings returned by the library functions listed in getenv, localeconv, setlocale, and strerror shall be diagnosed.
//Rationale
//C identifies the following three instances of undefined behavior, which arise as a result of modifying the data structures or strings returned from getenv, localeconv, setlocale, and strerror:

//UB120
//The program modifies the string pointed to by the value returned by the setlocale function (7.11.1.1).
//UB121
//The program modifies the structure pointed to by the value returned by the localeconv function (7.11.2.1).
//UB184
//The string set up by the getenv or strerror function is modified by the program (7.22.4.6, 7.24.6.2).

```c:libmod.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the string returned from the C Standard Library function setlocale is modified.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS, NULL
#include <string.h> /// for strstr
#include <errno.h> /// for errno
#include <locale.h> /// for set locale
#define BUFFER_SIZE BUFSIZ

void f1(void) {
  char *locale = setlocale(LC_ALL, 0);
  if (locale != NULL) {
    char *cats[8];
    char *sep = locale;
    cats[0] = locale;
    int i;
    if (sep) {
      for (i = 0; (sep = strstr(sep, ";:")) && i < 8; ++i) {
        *sep = '\0'; // diagnostic required
        cats[i] = ++sep; 
      }
    }
  }
  /* ... */
  printf("f1:setlocale\n");///
}

//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the object returned from the C Standard Library function localeconv is modified.

void f2(void) {
  struct lconv *conv = localeconv();
  if ('\0' == conv->decimal_point[0]) {
    conv->decimal_point = "."; // diagnostic required
    printf("f2:decimal_point\n");///
  }
  if ('\0' == conv->thousands_sep[0]) {
    conv->thousands_sep = ","; // diagnostic required
    printf("f2:thousands_sep\n");///
  }
  /* ... */
  printf("f2:localconv\n");///
}

//EXAMPLE 3 In this noncompliant example, a diagnostic is required because the string returned from the C Standard Library function getenv is modified.

void f3(void) {
  char *shell_dir = getenv("SHELL");
  if (shell_dir != NULL) {
    char *slash = strrchr(shell_dir, '/');
    if (slash) {
      *slash = '\0'; // diagnostic required
      printf("f3:slash:%s\n", shell_dir);///
      return;///
    }
    /* use shell_dir */
    printf("f3:%s\n", shell_dir);///
    return;//
  }

}
//EXAMPLE 4 In this noncompliant example, a diagnostic is required because the string returned from the C Standard Library function strerror is modified.

const char *f4(int error) {
  char buf[BUFFER_SIZE];
  sprintf(buf, "(errno = %d)", error);
  char *error_str = strerror(error);
  strcat(error_str, buf); // diagnostic required
  return error_str;
}
int main(void) {
  int i = 1;
  f1();///
  f2();///
  f3();///
  printf("%s \n",f4(i));///
  return EXIT_SUCCESS;
}
shell
$ cc libmod.c 
$ ./a.out
f1:setlocale
f2:thousands_sep
f2:localconv
f3:slash:/bin
Bus error: 10

5.29. Overflowing signed integers [intoflow]

5.30. Passing a non-null-terminated string to a library function [nonnullstr]

nonnullstr.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the string str will not be null-terminated when passed as an argument to printf.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include <stddef.h> /// for size_t
#include <limits.h> /// for 'ULONG_MAX'

void f(){///
  char str[3] = "abc";
  printf("%s\n", str); // diagnostic required
}///

//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the wide string cur_msg will not be null-terminated when passed to wcslen. This will occur if lessen_memory_usage is invoked while cur_msg_size still has its initial value of 1024.

wchar_t *cur_msg = NULL;
size_t cur_msg_size = 1024;
size_t cur_msg_len = 0;

void lessen_memory_usage(void) {
  wchar_t *temp;
  size_t temp_size;
  /* ... */
  if (cur_msg != NULL) {
    temp_size = cur_msg_size / 2 + 1;
    temp = realloc(cur_msg, temp_size * sizeof(wchar_t));
    // temp & cur_msg might not be null-terminated
    if (temp == NULL) {
      /* ... */
    }
    cur_msg = temp;
    cur_msg_size = temp_size;
    cur_msg_len = wcslen(cur_msg); // diagnostic required
  }
}
//EXAMPLE 3 In this compliant example, a diagnostic is not required because cur_msg will always be null-terminated when passed to wcslen.
//wchar_t *cur_msg = NULL;
//size_t cur_msg_size = 1024;
//size_t cur_msg_len = 0;

void lessen_memory_usage2(void) {
  wchar_t *temp;
  size_t temp_size;
  /* ... */
  if (cur_msg != NULL) {
    temp_size = cur_msg_size / 2 + 1;
    temp = realloc(cur_msg, temp_size * sizeof(wchar_t));
    // temp & cur_msg might not be null-terminated
    if (temp == NULL) {
      /* ... */
    }
    cur_msg = temp;
    cur_msg[ temp_size - 1] = L'\0'; // cur_msg now properly null-terminated
    cur_msg_size = temp_size;
    cur_msg_len = wcslen(cur_msg); // diagnostic not required
  }
}
int main(void) {
  f();///
  lessen_memory_usage();///
  lessen_memory_usage2();///
  printf("nonnullstr \n");///
  return EXIT_SUCCESS;///
}///
shell
cc nonnullstr.c 
OgawaKiyoshi-no-MacBook-Pro:ts17961 ogawakiyoshi$ ./a.out
abc`?_??
nonnullstr 

5.31. Passing arguments to character-handling functions that are not representable as unsigned char [chrsgnext]

chrsgnext.c
//EXAMPLE In this noncompliant example, a diagnostic is required because the parameter to isspace, *t may not be representable as an unsigned char.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <ctype.h> /// for isspace
#include <string.h> /// for strlen

size_t count_preceding_whitespace(const char *s) {
  const char *t = s;
  size_t length = strlen(s) + 1;
  while (isspace(*t) && (t - s < length)) { // diagnostic required
    ++t;
  }
  return t - s;
}
int main(void) {
  char * s = "";
  printf("nonnullstr \n",  count_preceding_whitespace());///
  return EXIT_SUCCESS;///
}///
shell
$ cc chrsgnext.c 
$ ./a.out
0 

5.32. Passing pointers into the same object as arguments to different restrict-qualified parameters [restrict]

restrict.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the restrict-qualified pointer parameters to memcpy, ptr1, and ptr2 reference overlapping objects.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include <stddef.h> /// for size_t

void abcabc(void) {
  char c_str[]= "abc123edf";
  char *ptr1 = c_str;
  char *ptr2 = c_str + strlen("abc");
  memcpy(ptr2, ptr1, 6); // diagnostic required
  puts(c_str);
}

//EXAMPLE 2 In this noncompliant example, the src operand is used twice to refer to unmodified memory, which is allowed by the C Standard; the aliasing restrictions apply only when the object is modified. A diagnostic is required nontheless because the pointer src is twice a restrict-qualified pointer parameter to dual_memcpy, referencing overlapping objects.

void *dual_memcpy(void *restrict s1, const void *restrict s2, size_t n1, void *restrict s3, const void *restrict s4, size_t n2) {
  memcpy(s1, s2, n1);
  memcpy(s3, s4, n2);
 printf("%d %d %d %d \n",(int)s1,(int)s2,(int)s3,(int)s4);///
   return s1;
}

void f(void) {
  char dest1[10];
  char dest2[10];
  char src[] = "hello";
  dual_memcpy(dest1, src, sizeof(src),
  dest2, src, sizeof(src)); // diagnostic required
  puts(dest1);
  puts(dest2);
}
int main(void) {
  void abcabc(void);
  void *restrict s1;
  const void *restrict s2; 
  size_t n1;
  void *restrict s3;
  const void *restrict s4;
  size_t n2;
  dual_memcpy(s1, s2, n1, s3, s4, n2)
  f();
  return EXIT_SUCCESS;///
}///
shell
$ cc restrict.c 
$ ./a.out
-475007112 0 0 0 
-475007234 -475007250 -475007244 -475007250 
hello
hello

5.33. Reallocating or freeing memory that was not dynamically allocated [xfree]

xfree.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the pointer parameter to realloc, buf does not refer to dynamically allocated memory.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include <stddef.h> /// for size_t

#define BUFSIZE 256
void f(void) {
  char buf[BUFSIZE];
  char *p;
  /* ... */
  p = (char *)realloc(buf, 2 * BUFSIZE); // diagnostic required
  /* ... */
  printf("%s \n",p);
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the pointer parameter to free, c_str may not refer to dynamically allocated memory.

#define MAX_ALLOCATION 1000
int main(int argc, char *argv[]) {
  char *c_str = NULL;
  size_t len;
  f();///
  if (argc == 2) {
    len = strlen(argv[1]) + 1;
    if (len > MAX_ALLOCATION) {
      /* Handle error */
    }
    c_str = (char *)malloc(len);
    if (c_str == NULL) {
      /* Handle allocation error */
    }
    strcpy(c_str, argv[1]);
  } 
  else {
    c_str = "usage: $>a.exe[string]";
    printf("%s\n", c_str);
  }
  /* ... */
  free(c_str); // diagnostic required
  return EXIT_SUCCESS;
}
shell
$ cc xfree.c 
$ ./a.out
a.out(76770,0x7fffa098d340) malloc: *** error for object 0x7ffee9598610: pointer being realloc'd was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

## 5.34. Referencing uninitialized memory [uninitref] 

```c:uninitref.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the variable sign may be uninitialized when it is accessed in the return statement of the function is_negative.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <errno.h> /// for errno
#include <limits.h> /// for 'ULONG_MAX'

void g(double *a, size_t n) ;///
void f(size_t n);///

void get_sign(int number, int *sign) {
  if (sign == NULL) {
    /* ... */ 
  }
  if (number > 0) {
    *sign = 1;
  } else if (number < 0) {
    *sign = -1;
  } // If number == 0, sign is not changed.
}
int is_negative(int number) {
  int sign;
  get_sign(number, &sign);
  return (sign < 0); // diagnostic required
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the variable error_log is uninitialized when it is passed to sprintf.

int do_auth(void) {
  int result = -1;
  /* ... */
  return result;
}
void report_error(const char *msg) {
  const char *error_log;
  char buffer[24];
  sprintf(buffer, "Error: %s", error_log); // diagnostic required
  printf("%s\n", buffer);
}
int main(void) {
  double b = 2;
  double *a = &b;
  size_t n =sizeof(int);
  g(double *a, size_t n) ;///
  f(size_t n);///

  if (do_auth() == -1) {
    report_error("Unable to login");
  }
  return EXIT_SUCCESS;
}
//EXAMPLE 3 In this noncompliant example, a diagnostic is required because the elements of the array a are uninitialized when they are accessed in the for loop.

void f(size_t n) {
  int *a = (int *)malloc(n * sizeof(int));
  if (a != NULL) {
    for (size_t i = 0; i != n; ++i) {
      a[i] = a[i] ^ a[i]; // diagnostic required
    }
    /* ... */
    free(a);
  }
}
//EXAMPLE 4 In this noncompliant example, a diagnostic is required because the array elements a[n..2n] are uninitialized when they are accessed in the for loop.

void g(double *a, size_t n) {
  a = (double *)realloc(a, (n * 2 + 1) * sizeof(double));
  if (a != NULL) {
  for (size_t i = 0; i != n * 2 + 1; ++i) {
    if (a[i] < 0) {
      a[i] = -a[i]; // diagnostic required
    }
  }
  /* ... */
  free(a);
  }
}
shell
$ cc uninitref.c 
$ ./a.out
a.out(76835,0x7fffa098d340) malloc: *** error for object 0x7ffeea041750: pointer being realloc'd was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

5.35. Subtracting or comparing two pointers that do not refer to the same array [ptrobj]

ptrobj.c
//EXAMPLE In this noncompliant example, a diagnostic is required because the pointers c_str and (char **)next_num_ptr are subtracted and do not refer to the same array.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS

#define SIZE 256
void f(void) {
  int nums[SIZE];
  char *c_str[SIZE];
  int *next_num_ptr = nums;
  int free_bytes;
  /* ... */
  /* increment next_num_ptr as array fills */
  free_bytes = c_str - (char **)next_num_ptr; // diagnostic required
  printf("%d, %d, %s \n",*next_num_ptr,free_bytes, *c_str);
  /* ... */
}

int main(void) {
  f();
  return EXIT_SUCCESS;///
}///
shell
$ cc probj.c 
$ ./a.out
0, -256, (null) 

5.36. Tainted strings are passed to a string copying function [taintstrcpy]

taintstrcpy.c
//EXAMPLE In this noncompliant example, a diagnostic is required because the size of the string referenced by argv[0] might be greater than the size of the destination array pgm.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strcpy

void main(int argc, char *argv[]) {
char pgm[BUFSIZ];
  if (argc > 1) {
    strcpy(pgm, argv[0]); // diagnostic required
    printf("%s\n",pgm);
  }
  return EXIT_SUCCESS;///
}
shell
$ cc taintstrcpy.c 
$ ./a.out taintstrcpy
./a.out

5.37. Taking the size of a pointer to determine the size of the pointed-to type [sizeofptr]

sizeofptr.c
//EXAMPLE In this noncompliant example, a diagnostic is required because the sizeof operator is applied to the pointer parameter array.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS

void clear(int array[]) {
  for (size_t i = 0; i < sizeof(array) / sizeof(array[0]); // diagnostic required
    ++i) {
      printf("array[%d] =%d\n",(int)i,array[i]);
      array[i] = 0;
  }
}
int main(int argc, char *argv[]) {///
  int array[]={1,2,3,4};///
  clear(array);///
  return EXIT_SUCCESS;///
}///
shell
$ cc sizeofptr.c
sizeofptr.c:7:32: warning: sizeof on array function parameter will return size of 'int *' instead of 'int []' [-Wsizeof-array-argument]
  for (size_t i = 0; i < sizeof(array) / sizeof(array[0]); // diagnostic required
                               ^
sizeofptr.c:6:16: note: declared here
void clear(int array[]) {
               ^
1 warning generated.
$ ./a.out
array[0] =1
array[1] =2

5.38. Using a tainted value as an argument to an unprototyped function pointer [taintnoproto]

taintnoproto.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the tainted argument tainted is passed as an argument to a call through an unprototyped pointer to function pf. The initialization of pf and the definition of restricted_sink are informational and not necessary for this diagnosis.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <errno.h> /// for errno
#include <limits.h> /// for 'lONG_MAX'
#include "get.h" ///

void restricted_sink(int i) {
  int array[2];
  array[i] = 0;
  printf("%d %d\n",i,array[i]);
}
void (*pf)() = restricted_sink;
  void f(void) {
  int tainted;
  GET_TAINTED_INTEGER(int, tainted);
  printf("%d\n",tainted);
  (*pf)(tainted); // diagnostic required
}
//EXAMPLE 2 In this compliant example, a diagnostic is not required because the tainted argument tainted2 is passed as an argument to a call through a properly prototyped pointer to function pf2.
void (*pf2)(int);
void g(void) {
  int tainted2;
  GET_TAINTED_INTEGER(int, tainted2);
  printf("2:%d \n", tainted2);
  (*pf2)(tainted2);
}
int main(int argc, char *argv[]) {///
  f();///
  g();///
  return EXIT_SUCCESS;///
}///
shell
$ cc taintnoproto.c 
$ ./a.out

5.39. Using a tainted value to write to an object using a formatted input or output function [taintformatio]

taintformatio.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the call to fscanf can result in a write outside the character array buf.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include <errno.h> /// for errno
#include <limits.h> /// for 'lONG_MAX'
#include "get.h" /// for GET_TAINTED_INTEGER
#define BUF_LENGTH BUFSIZ

int main(void){ ///
  char buf[BUF_LENGTH];
  fscanf(stdin, "%s\n", buf); // diagnostic required
  printf("buf:%s\n", buf);///
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the sprintf function will write outside the bounds of the character array buf.
  int rc = 0;
  int x;
  GET_TAINTED_INTEGER(int, x);
  char buf2[sizeof("999")];
  rc = sprintf("buf2, "%d\n", x); // diagnostic required
  printf("buf2:%s", buf2);///
  if (rc == -1 || rc >= sizeof(buf2)) {
    /* handle error */
    printf("handle error\n");
  }
  return EXIT_SUCCESS;///
}
console
cc taintformatio.c
$ ./a.out
^C

5.40. Using a value for fsetpos other than a value returned from fgetpos [xfilepos]

xfilepos.c
//EXAMPLE In this noncompliant example, a diagnostic is required because an offset value other than one returned from fgetpos is used in a call to fsetpos.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include <errno.h> /// for errno
#include <limits.h> /// for 'lONG_MAX'

FILE *opener(const char *filename) {
  fpos_t offset;
  if (filename == NULL) {
    /* ... */
    printf("filename == NULL");
  }
  FILE *file = fopen(filename, "r");
  if (file == NULL) {
    /* ... */
    printf("file == NULL");
    return (FILE *)NULL;
  }
  memset(&offset, 0, sizeof(offset));
  if (fsetpos(file, &offset) != 0) { // diagnostic required
    /* ... */ 
    printf("%x",offset);
  }
  return file;
}
int main(int argc, char *argv[]) {///
  char * fn = "xfilepos.c"
  printf("%d",(int)opener(fn));///
  return EXIT_SUCCESS;///
}///
shell
$cc  xfilepos.c
$ ./a.out
-1600712656O
shell
$ cc taintformatio.c
taintformatio.c:20:30: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'const char *' [-Wint-conversion]
  rc = sprintf("buf2, %d\n", x); // diagnostic required
                             ^
/usr/include/secure/_stdio.h:47:56: note: expanded from macro 'sprintf'
  __builtin___sprintf_chk (str, 0, __darwin_obsz(str), __VA_ARGS__)
                                                       ^~~~~~~~~~~
taintformatio.c:20:30: warning: format string is not a string literal (potentially insecure) [-Wformat-security]
  rc = sprintf("buf2, %d\n", x); // diagnostic required
                             ^
/usr/include/secure/_stdio.h:47:56: note: expanded from macro 'sprintf'
  __builtin___sprintf_chk (str, 0, __darwin_obsz(str), __VA_ARGS__)
                                                       ^~~~~~~~~~~
taintformatio.c:20:30: note: treat the string as an argument to avoid this
  rc = sprintf("buf2, %d\n", x); // diagnostic required
                             ^
                             "%s", 
/usr/include/secure/_stdio.h:47:56: note: expanded from macro 'sprintf'
  __builtin___sprintf_chk (str, 0, __darwin_obsz(str), __VA_ARGS__)
                                                       ^
2 warnings generated.

##5.41. Using an object overwritten by getenv, localeconv, setlocale, and strerror [libuse]

libuse.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the string returned by the first call to the C Standard Library function getenv is accessed, after the second call to getenv, in the call to the C Standard Library function strcmp.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include <errno.h> /// for errno
#include <limits.h> /// for 'lONG_MAX'
#include <locale.h> /// for LC_ALL

int f(void) {
  char *tmpvar = getenv("TMP");
  char *tempvar = getenv("TEMP");
  if (!tmpvar || !tempvar) {
    /* ... */
  }
  return strcmp(tmpvar, tempvar) == 0; // diagnostic required
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the string returned by the first call to the C Standard Library function setlocale is accessed, after the second call to setlocale, in the third call to setlocale.

void g(const char *name) {
  const char *save = setlocale(LC_ALL, 0);
  if (setlocale(LC_ALL, name)) {
    /* ... */
  }
  setlocale(LC_ALL, save); // diagnostic required
}

//EXAMPLE 3 In this noncompliant example, a diagnostic is required because the pointer returned from the first call to the C Standard Library function strerror is accessed, in the call to fprintf, after the second call to strerror.

void h(const char *a, const char *b) {
  errno = 0;
  unsigned long x = strtoul(a, NULL, 0);
  int e1 = ULONG_MAX == x ? errno : 0;
  errno = 0;
  unsigned long y = strtoul(b, NULL, 0);
  int e2 = ULONG_MAX == y ? errno : 0;
  char* err1 = strerror(e1);
  char* err2 = strerror(e2);
  fprintf(stderr, "parsing results: %s, %s", err1, err2); // diagnostic required
}
int main(int argc, char *argv[]) {///
  char* st1 = "libuse";///
  char* st2 = "strerr";///
  f();///
  g(st1);///
  h(st1,st2);///
  return EXIT_SUCCESS;///
}///
shell
$ cc libuse.c
$ ./a.out
Segmentation fault: 11

5.42. Using character values that are indistinguishable from EOF [chreof]

chreof.c
//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the result of the call to the C Standard Library function getchar is stored into a variable of type char, c, and c is compared to EOF.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include <errno.h> /// for errno
#include <limits.h> /// for 'lONG_MAX'
#include <locale.h> /// for LC_ALL
#include <wchar.h> /// for wcslen

void f(void) {
  char buf[BUFSIZ];
  char c;
  size_t i = 0;
  while ((c = getchar()) != '\n' && c != EOF) { // diagnostic required
    if (i < BUFSIZ - 1) {
      buf[i++] = c;
    }
  }
  buf[i] = '\0';
  printf("%s\n", buf);
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the result of the call to the C Standard Library function getwc is stored into a variable of type wchar_t, wc, and wc is compared to WEOF.

void g(void) {
  wchar_t buf[BUFSIZ];
  wchar_t wc;
  size_t i = 0;
  while ((wc = getwc(stdin)) != '\n' && wc != WEOF) { // diagnostic required
    if (i < BUFSIZ - 1) {
      buf[i++] = wc;
    }
  }
  buf[i] = '\0';
  wprintf("%s\n", buf);
}
int main(int argc, char *argv[]) {///
  f();///
  g();///
  return EXIT_SUCCESS;///
}///
shell
cc chreof.c 
chreof.c:35:11: warning: incompatible pointer types passing 'char [4]' to parameter of type 'const wchar_t *' (aka 'const int *')
      [-Wincompatible-pointer-types]
  wprintf("%s\n", buf);
          ^~~~~~
/usr/include/wchar.h:155:39: note: passing argument to parameter here
int     wprintf(const wchar_t * __restrict, ...);
                                          ^
1 warning generated.
$ ./a.out
^C

5.43. Using identifiers that are reserved for the implementation [resident]

##5.44. Using invalid format strings [invfmtstr]

invfmtstr.c
//EXAMPLE In this noncompliant example, a diagnostic is required because the arguments to printf do not match the conversion specifiers in the supplied format string.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS

void f(void) {
  const char *error_msg = "Resource not available to user.";
  int error_type = 3;
/* ... */
  printf("Error (type %s): %d\n", error_type, error_msg); // diagnostic required
}

int main(int argc, char *argv[]) {///
  f();///
  return EXIT_SUCCESS;///
}///
shell
$ cc invfmtstr.c 
invfmtstr.c:10:35: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
  printf("Error (type %s): %d\n", error_type, error_msg); // diagnostic required
                      ~~          ^~~~~~~~~~
                      %d
invfmtstr.c:10:47: warning: format specifies type 'int' but the argument has type 'const char *' [-Wformat]
  printf("Error (type %s): %d\n", error_type, error_msg); // diagnostic required
                           ~~                 ^~~~~~~~~
                           %s
2 warnings generated.
$ ./a.out
Segmentation fault: 11

5.45. Tainted, potentially mutilated, or out-of-domain integer values are used in a restricted sink [taintsink]

taintsink.c
#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS
#include <string.h> /// for strlen
#include <errno.h> /// for errno
#include <limits.h> /// for 'lONG_MAX'
#include "get.h" /// for GET_TAINTED_INTEGER

//EXAMPLE 1 In this noncompliant example, a diagnostic is required because the tainted integer size is used to declare the size of the variable length array vla.

void f(const char *c_str) {
  size_t size;
  GET_TAINTED_INTEGER(size_t, size);
  char vla[size]; // diagnostic required
  strncpy(vla, c_str, size);
  vla[size - 1] = '\0';
  /* ... */
}
//EXAMPLE 2 In this noncompliant example, a diagnostic is required because the tainted integer color_index is used in pointer arithmetic to index into the array table.
  const char *table[] = { "black", "white", "blue", "green" };
const char *set_background_color(void) {
  int color_index;
  GET_TAINTED_INTEGER(int, color_index);
  printf("%d\n",color_index);
  const char *color = table[color_index]; // diagnostic required
/* ... */
  return color;
}
int main(int argc, char *argv[]) {///
  char * s = "taintsink.c";///
  f(s);///
  printf("back:%s\n", set_background_color());///
  return EXIT_SUCCESS;///
}///
shell
$ cc taintsink.c 
$ ./a.out

Annex A (informative) Intra- to Interprocedural Transformations

A.1 Function arguments and return values

FILE *fp = fopen(name, mode);
if (fp != NULL) /* checking for success */
/* ... */
void check_it(FILE *fp) {
if (fp != NULL) /* checking for success */
/* ... */
}
/* ... */
check_it(fopen(name, mode));
/* a wrapper around fopen */
FILE *xfopen(const char *name, const char *mode) {
return fopen(name, mode); /* return for checking elsewhere */
}
...
FILE *fp = xfopen(name, mode);
if (fp != NULL) /* checking for success */
/* ... */
/* trivial example */
FILE *identity(FILE *fp) {
return fp;
}
FILE *fp = identity(fopen(name, mode));
if (fp != NULL) /* checking for success */
/* ... */

A.2 Indirection

void check_indirect(FILE **pfp) {
if (*pfp != NULL) /* checking for success */
/* ... */
}
/* ... */
FILE *fp = fopen(name, mode);
check_indirect(&fp);
void return_result_thru_param(const char *name, const char *mode,
FILE **result) {
*result = fopen(name, mode);
}
/* ... */
FILE *fp;
return_result_thru_param(name, mode, &fp);
if (fp != NULL) /* checking for success */
/* ... */

int array[2];
int index = 2; /* part of violation */
array[index] = 0; /* violation */
Applying some of the interprocedural transformations yields
void indexer(int *array, int index) {
array[index] = 0; /* part of violation */
}
/* ... */
int array[2];
int index = 2; /* part of violation */
indexer(array, index); /* violation */
or
static int array[2];
int *get_array() {
return array; /* part of violation */
}
/* ... */
get_array()[2] = 0; /* violation */
or
struct array_params {
int *array;
int index;
};
void indexer(struct array_params *ap) {
ap->array[ap->index] = 0; /* part of violation */
}
/* ... */
int array[2];
struct array_params params;
params.array = array; /* part of violation */
params.index = 2; /* part of violation */
indexer(&params); /* violation */
These violations involve three steps: the array, the index, and the address arithmetic, where each could occur in a different function:
int *add(int *base, int offset) {
return base + offset; /* part of violation */
}
/* ... */
int array[2];
int index = 2; /* part of violation */
*add(array, index) = 0; /* violation */
Indirection may also be applied to a pointer being returned from a function:
const int *ptr_to_index() {
static int rv;
rv = 2; /* part of violation */
return &rv; /* part of violation */
}
/* ... */
int array[2];
array[*ptr_to_index()] = 0; /* violation */

A.3 Transformation involving standard library functions

const char* basename(const char *pathname) {
char *slash;
slash = strchr(pathname, '/');
if (slash) {
*slash++ = '\0'; /* violates EXP40-C. Do not modify constant values */
return slash;
}
return pathname;
}

A.4 Data flow through globals

int global_index;
void set_it() {
global_index = 2; /* part of violation */
}
/* ... */
int array[2];
set_it(); /* part of violation */
array[global_index] = 0; /* violation */

A.5 Data flow through the heap

struct some_record {
int index;
/* ... */
};
/* returns the heap-allocated record (created elsewhere)
* matching 'key'
*/
struct some_record *find_record(const char *key);
void set_it() {
struct some_record *record = find_record("xyz");
record->index = 2;
}
/* ... */
int array[2];
set_it(); /* part of violation */
struct some_record *record = find_record("xyz"); /* part of violation */
array[record->index] = 0; /* violation */
/* note that find_record() could even be called before set_it() */

A.6 Combined example

struct trouble {
int *array;
int index;
int *effective_address;
};
void set_array(struct trouble *t, int *array) {
t->array = array; /* part of violation *.
}
void set_index(struct trouble *t, int *index) {
t->index = *index; /* part of violation */
}
void compute_effective_address(struct trouble *t) {
t->effective_address = t->array + t->index; /* part of violation */
}
void store(struct trouble *t, int value) {
*t->effective_address = value; /* part of violation */
}
...
int array[2];
int index = 2; /* part of violation */
struct trouble t;
set_array(t, array); /* part of violation */
set_index(t, &index); /* part of violation */
compute_effective_address(&t); /* violation */
store(&t, 0); /* violation */

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

文書履歴

ver. 0.10 初稿 5.10まで 2018/04/03
ver. 0.11 4.5 mutilated value 及び 5.17まで追記 未完 2つ。2018/04/04
ver. 0.12 5.28まで追記 半分超え 2018/04/05
ver. 0.13 主な作業内容を追記 2018/04/05
ver. 0.14 5.45まで追記。実行結果不十分。2018/04/06
ver. 0.15 Annex A追記。コンパイルまだ 2018/06/16
ver. 0.16 加筆 20220103

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

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

Thank you very much for reading to the last sentence.

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

3
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
3
2