LoginSignup
1

C Secure Coding Rules(10)5.23 Including tainted or out-of-domain input in a format string [usrfmt]

Last updated at Posted at 2018-04-04

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にフィードバックするために用います。

ISO/IEC TS 17961:2013 C Secure Coding Rules(1)一覧

ISO/IEC TS 17961:2013 C Secure Coding Rules(1) All list(to be confirmed) - kaizen_nagoya @ Qiita
https://qiita.com/kaizen_nagoya/items/54e056195c4f11b850a1

一つの規則で複数回のコンパイルが必要な場合、別記事にしています。

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

作業予定

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

現状では、変な代入、奇異な操作が頻出します。コンパイルエラーが出ないようにするなるべく短い記述で済まそうという趣旨で、他意はありません。
意味のある出力があるよりよい記述に変更する予定です。

利用コンパイラ

Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.4.0
または
clang version 6.0.0 (tags/RELEASE_600/final)
Target: x86_64-apple-darwin17.4.0

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

環境(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

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

Example 1

In this noncompliant example, a diagnostic is required because a format string is read from an external catalog and passed as an argument to the vfprintf function.

usrfmt.c
// ISO/IEC JTC 1/SC 22/WG 14 N 1624 Date: 2012-06-26 ISO/IEC TS 17961, p.39
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1624.pdf
/// lines are added by Dr. Kiyoshi Ogawa, 2018
/// Compiled on 
///  Clang(LLVM) clang version 6.0.0 (tags/RELEASE_600/final) 
///  GCC(GNU) gcc-7 (Homebrew GCC 7.3.0_1) 7.3.0
/// hosted Environment macOS 10.13.3 or 10.12.9
// EXAMPLE 1 In this noncompliant example, a diagnostic is required because a format string is read from an external catalog and passed as an argument to the vfprintf function.

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

void format_error(const char *filename, ...) {
  FILE *fd = fopen(filename, "r");
  if (fd == NULL) {
    /* ... */
    printf("file:%s can not open.\n",filename);///
    return;
  }
  char fmt[BUFSIZ];
  if (fgets(fmt, BUFSIZ, fd) == NULL) {
    /* ... */
    printf("NULL text\n");///
  }
  va_list va;
  va_start(va, filename);
  vfprintf(stderr, fmt, va); // diagnostic required
  va_end(va);
  fclose(fd);
}
int main(void) {///
  char * usrfmt = "usrfmt.c";///
  format_error(usrfmt);///
  return EXIT_SUCCESS;///
}///
shell
$ ./gcc7ts.sh usrfmt
$ clang usrfmt.c
// EXAMPLE 1 In this noncompliant example, a diagnostic is required because a format string is read from an external catalog and passed as an argument to the vfprintf function.

$ gcc-7 usrfmt.c
// EXAMPLE 1 In this noncompliant example, a diagnostic is required because a format string is read from an external catalog and passed as an argument to the vfprintf function.

Example 2

In this compliant example, a diagnostic is not required because the format string that is read from an external catalog and passed as an argument to the vfprintf function is first sanitized.

usrfmt2.c
// ISO/IEC JTC 1/SC 22/WG 14 N 1624 Date: 2012-06-26 ISO/IEC TS 17961, p.40
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1624.pdf
/// lines are added by Dr. Kiyoshi Ogawa, 2018
/// Compiled on 
///  Clang(LLVM) clang version 6.0.0 (tags/RELEASE_600/final) 
///  GCC(GNU) gcc-7 (Homebrew GCC 7.3.0_1) 7.3.0
/// hosted Environment macOS 10.13.3 or 10.12.9
//EXAMPLE 2 In this compliant example, a diagnostic is not required because the format string that is read from an external catalog and passed as an argument to the vfprintf function is first sanitized.

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

void safe_format_error(const char *filename, ...) {
  FILE *fd = fopen(filename, "r");
  if (fd == NULL) {
    /* ... */
    printf("file:%s can not open.\n",filename);///
    return;///
  }
  char fmt[BUFSIZ];
  if (fgets(fmt, BUFSIZ, fd) == NULL) {
    /* ... */
    printf("NULL text\n");///
  }
  /* only allow %d in the format string: */
  const char *fc;
  for (fc = fmt; *fc != '\0'; ++fc) {
    if (*fc == '%' && (fc[1] != '%' && fc[1] != 'd')) {
          printf("char:%s\n",fc);///
      fclose(fd);
      return;
    }
  }
  va_list va;
  va_start(va, filename);
  vfprintf(stderr, fmt, va);
  va_end(va);
  fclose(fd);
  printf("closed\n");
}
int main(void) {///
  char * usrfmt = "usrfmt.c";///
  safe_format_error(usrfmt);///
  return EXIT_SUCCESS;///
}///
shell
$ ./gcc7ts.sh usrfmt2
$ clang usrfmt2.c
// EXAMPLE 1 In this noncompliant example, a diagnostic is required because a format string is read from an external catalog and passed as an argument to the vfprintf function.
closed

$ gcc-7 usrfmt2.c
// EXAMPLE 1 In this noncompliant example, a diagnostic is required because a format string is read from an external catalog and passed as an argument to the vfprintf function.
closed

Example 3

In this noncompliant example, a diagnostic is required because the string user may contain a tainted value.

usrfmt3.c
// 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
/// lines are added by Dr. Kiyoshi Ogawa, 2018
/// Compiled on 
///  Clang(LLVM) clang version 6.0.0 (tags/RELEASE_600/final) 
///  GCC(GNU) gcc-7 (Homebrew GCC 7.3.0_1) 7.3.0
/// hosted Environment macOS 10.13.3 or 10.12.9
//EXAMPLE 3 In this noncompliant example, a diagnostic is required because the string user may contain a tainted value.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS, free
#include <stdarg.h> /// for va_...
#include <string.h> /// for strlen 

#include "get.h"
int incorrect_password(void) {
  int ret;
  char user[BUFSIZ];
  GET_TAINTED_STRING(user, BUFSIZ);
  static const char MSG_FORMAT[] = "%s cannot be authenticated.\n";
  printf("get\n");///
  size_t size = strlen(user) + sizeof(MSG_FORMAT);
  char *msg = (char *)malloc(size);
  if (msg == NULL) {
    fprintf(stderr, "Could not malloc memmory in %s\n", __func__);
    return -1;
  } else {
    ret = snprintf(msg, size, MSG_FORMAT, user); // MSG_FORMAT is not tainted
    if (ret < 0) {
      fprintf(stderr, "snprintf encoding error occurred in %s\n", __func__);
    } else if (ret >= size) {
      printf("snprintf returned %d in %s\n", ret, __func__);
    }
    fprintf(stderr, "%s\n", msg); // diagnostic required
    free(msg);
  }
  printf("end\n");///
  return 0;
}

int main(void) {///
  printf("main\n");///
  printf("return:%d \n",incorrect_password());///
  return EXIT_SUCCESS;///
}///
shell
$ ./gcc7ts.sh usrfmt3
$ clang usrfmt3.c
end

$ gcc-7 usrfmt3.c
end

なにも出力がなかった。mainを出力させようとしたらendが出てきた。謎。

Example 4

In this compliant example, a diagnostic is not required because the argument fmt is constrained to be one of the elements of the formats array, which is not controlled by the user.

usrfmt4.c
// ISO/IEC JTC 1/SC 22/WG 14 N 1624 Date: 2012-06-26 ISO/IEC TS 17961, p40
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1624.pdf
/// lines are added by Dr. Kiyoshi Ogawa, 2018
/// Compiled on 
///  Clang(LLVM) clang version 6.0.0 (tags/RELEASE_600/final) 
///  GCC(GNU) gcc-7 (Homebrew GCC 7.3.0_1) 7.3.0
/// hosted Environment macOS 10.13.3 or 10.12.9
//EXAMPLE 4 In this compliant example, a diagnostic is not required because the argument fmt is constrained to be one of the elements of the formats array, which is not controlled by the user.

#include <stdio.h> /// for printf
#include <stdlib.h> /// for EXIT_SUCCESS, free
#include <stdarg.h> /// for va_...

enum int_tag { I_char, I_shrt, I_int, I_long, I_llong };
static const char *const formats[] = { "%hhi", "%hi", "%i", "%li", "%lli" };
static int fmtintv(enum int_tag tag, const char *fmt, va_list va) {
  return vfprintf(stdout, fmt, va);
}
int format_integer(enum int_tag tag, ...) {
  va_list va;
  int n;
  if (tag < I_char || I_llong < tag)
    return -1;
  va_start(va, tag);
  n = fmtintv(tag, formats[tag], va);
  va_end(va);
  return n;
}
int main(void) {///
  printf("\n return:%d \n",format_integer(I_int));///
  return EXIT_SUCCESS;///
}///
shell
$ ./gcc7ts.sh usrfmt4
$ clang usrfmt4.c
-496109672
 return:10 

$ gcc-7 usrfmt4.c
-394639464
 return:10 

usrfmt3.cと同様にmainの文字を出力するとreturn:3となった。
挙動全体は未確認#参考文献
コンパイル用shell script C版(clangとgcc)とC++版(clang++とg++)
https://qiita.com/kaizen_nagoya/items/74220c0577a512c2d7da

C言語(C++)に対する誤解、曲解、無理解、爽快。
https://qiita.com/kaizen_nagoya/items/3f3992c9722c1cee2e3a

MISRA C まとめ #include
https://qiita.com/kaizen_nagoya/items/f1a79a7cbd281607c7c9

どうやって MISRA C Example Suiteをコンパイルするか
https://qiita.com/kaizen_nagoya/items/fbdbff5ff696e2ca7f00

[C][C++]の国際規格案の例題をコンパイルするときの課題7つ。
https://qiita.com/kaizen_nagoya/items/5f4b155030259497c4de

文書履歴

ver. 0.10 初稿 20180404
ver. 0.11 gcc-7追記 Example節項目追記 20180408
ver. 0.12 ありがとう追記 20230413

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

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

Thank you very much for reading to the last sentence.

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

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
1