LoginSignup
4
4

More than 5 years have passed since last update.

「Cは超高級アセンブラ?」の「未定義動作」に対する逃げ道の探索

Posted at

概要

Cは超高級アセンブラ? - Qiita [キータ]

yohhoyさんのコメント

ここで挙げられているプログラムは、いわゆる「未定義動作」となります。
http://www.jpcert.or.jp/sc-rules/c-exp05-c.html

環境(コンパイラやOS等)によっては出力結果が20になったり18になったり、はたまた実行時クラッシュを引き起こしたりしますが、いずれもこのプログラムとしては「あり得る」実行結果です。

とあったので、逃げ道がないか探しました。

結論は、構造体の実体そのものにはconstをつけず、メンバにconstつければ逃げられるようです

探索

EXP05-C. const 修飾をキャストではずさない
には、以下のように記述されています。

変数の型にたいするconst修飾をキャストしてはずさないこと。const 修飾をキャストしてはずすと、プログラムが定数値を変更できることになり、未定義の動作につながるおそれがある。

C99 の脚注には以下のように記載されている。
処理系は、volatileでないconstオブジェクトを、読み取り専用記憶域に置いてもよい。さらに、処理系はそのアドレスが使われないならば、そのようなオブジェクトに記憶域を割り付けなくてもよい。
(略)

自動検出

  • LDRA tool suite V 7.6.0 はこのレコメンデーションの違反を検出できる。
  • GCC は -Wcast-qual フラグを使用することでこのレコメンデーションの違反を検出できる
  • Compass/ROSE はこのレコメンデーションの違反を検出できる。

(参考)
JIS X 3010:2003 プログラム言語C

要は・・・

  • 読み取り専用記憶域に置かれない
  • GCC の -Wcast-qual フラグを使用して検出されない

であれば、OKであろうと考えた。

テスト方法

  • 対象の変数(st_dest)が読み取り専用記憶域に置かれていないことをprintfによるアドレス 出力とobjdumpにより確認する。
  • GCC の-Wcast-qual フラグを使用して検出されない

objdumpの確認結果は、適当になってしまうところがあったので、省略(たぶんOK)。

逃げ道

test.c
#include <stdio.h>
#include <stdlib.h>
/* add - start */
#include <string.h>

#define PRINT_VALUE(a) printf(#a" = %d\n", a);

struct {
  const int age;
} st_src = {20}, st_dest = {18};
/* add - end */

int
main (
  int   argc,
  char *argv[ ]
  )
{
  const int age = 18;

  printf ( "%d\n", age );
  *( ( int* )( &age ) ) = 20; /* -Wcast-qualにより、ここは検出可能 */
  printf ( "%d\n", age );

  /* add - start */
  PRINT_VALUE(st_dest.age);

  /* 
     st_dest.age = 20; という代入は、 
      エラー: 読み取り専用メンバ 'age' への代入です
     となる。(constの期待動作をする。)
   */
  /* -Wcast-qualでも↓は、検出されない・・・ st_src と st_dest自体は「constオブジェクト」ではないので、読み取り専用領域におかれない。 */
  memcpy(&st_dest, &st_src, sizeof(st_dest));

  PRINT_VALUE(st_dest.age);
  /* add - end */
  return ( EXIT_SUCCESS );
}
/*
  MinGW + gcc- 4.7.2でコンパイル
   $ gcc -pedantic -Wall -Wcast-qual test.c
   test.c: 関数 'main' 内:
   test.c:22:6: 警告: cast discards '__attribute__((const))' qualifier from pointer target type [-Wcast-qual]

  実行結果
   $ ./a.exe
   18
   20
   st_dest.age = 18
   st_dest.age = 20
*/
4
4
21

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
4