********************************************************************** セッションs4a チュートリアル テーマ:[演習で学ぶ!]C言語に潜むリスクと,MISRA Cの有効性 講師:尾仲 洋和(サニー技研) 日時:2016/8/26 13:00-14:20 参加人数:前半 35人程度 後半 40人程度 ********************************************************************** MISRA Cの制約とその理由を考えることにより,より安全なプログラミングをすることを考える MISRA Cとは? 車載向けのC言語のコーディング規則であったが,クリティカルな組込み向けに対象を拡大したもの 最終問題:a = b = 0; のリスクは何か? を目標としつつ,様々な事例について考える. 以下,それぞれのコード事例問題は参加者に挙手をしてもらう. それらの人数と正解を記述する. コード事例1********** 下記の範囲は? A) char ①-128~127 ②0~255 ③その他 ①3人程度 ②3人程度 ③5人程度 正解:③ charは処理系により符号の有無が異なる. B) short ①-32768~32767 ②0~65535 ③その他 ①15人程度 ②0人 ③5人程度 正解:① C) unsigned int ①0~65535 ②0~4294967295 ③その他 ①0人 ②10人程度 ③15人程度 正解:③ 処理系によりサイズが異なる(2^16または2^32) MISRA Cでは,基本的な数値型に変わり,サイズと符号属性を示すtypedefを用いる int8_t,uint16_t,int16_t,uint16_t など ********** コード事例2********** aの値を2進数で表すと? int8_t a; a = -18; ①10010010 ②11101101 ③11101110 ①0人 ②0人 ③ 10人程度 正解:無し C言語の内部表現は処理系定義のため,必ずしも2の補数ではない. ただし,殆どは二の補数である. ********** コード事例3********** 型はuint_8 dの値は? a = 0x55; b = 0x0f; c = 1; d = a & b + c; ①0x06 ②0x10 ③その他 ①8人程度 ②2人 ③2人 正解:② &演算子よりも+演算子のほうが優先順位が高いため MISRA Cでは,式中における演算子の優先順位は明示すべきである. ********** コード事例4********** a,b,cのそれぞれの値は? a = 0; b = 0; c = a +++ b; ①aは0,bは1,cは1 ②aは1,bは0,cは0 ③aは1,bは0,cは1 ④その他 ①0人 ②5人程度 ③7人程度 ④0人 正解:② インクリメント,デクリメントは他の演算子と切り離して使用することが望ましい. a++と++aの返り値の違いがあるため. ********** コード事例5********** yの値は? 型はuint_8 x = 0; y = x/++x; ①0 ②1 ③その他 ①3人程度 ②10人程度 ③3人程度 正解:③ 分母と分母はどちらが先に評価されるかについて :C言語規格では評価の順番は決められていないので,未定義 ********** コード事例6********* xの値は? x = 1; if(x = 0) { x = 2; } ①0 ②1 ③2 ④その他 ①4人程度 ②2人程度 ③10人程度 ④1人 正解:① xに0が代入され,偽となる x=2はないのでx=0 コンパイルでは通ってしまうが,代入演算子の結果は用いるべきではない ********* (補題) x = 0; a[x] = a[x=1]; このように書くと左辺のxの値も未定義となり,推奨されない. コード事例7********* cの値は? uint16_t a, b; /* 2byte */ uint32_t c; /* 4byte */ a = 0xffff; b = 0x0002; c = a + b; ①0x00000001 ②0x00010001 ③その他 ①5人程度 ②3人程度 ③1人 正解:intサイズが16bitなら① intサイズが32bitなら② c=(uint16_t)(a + b); とすれば桁上がりを捨てることができる c=(uint32_t)(a + b); とすれば桁上がりを反映することができる ********* コード事例8********* バイトオーダーがリトルエンディアンの場合, どのアドレスにどの値が書き込まれるか? uint8_t *p; p = (uint8_t *)0x401; *(uint16_t *)p = 0x1234; ①401h番地が0x12,402h番地が0x34 ②401h番地が0x34,402h番地が0x12 ③その他 ①1人 ②6人程度 ③0人 正解:② であるが,特定のマイコンでは,奇数アドレスに配置された1バイト境界線のポインタを 2バイトの境界線にキャストした場合,意図通りにアクセスできない可能性がある. エンディアンについて知識がある人については,12人程度が手が上がった. ********* コード事例9********* このコードのリスクは何でしょうか? a = b = 0; 解答 :型がuint8_t の場合 a = 0,b = 0; となりリスクは存在しない :型がvolatile uint8_tの場合 a = b; と b = 0; が実行され,両者の順序は不定のため a = b; が先に実行されるとaは0ではなくなる. 代入演算子の結果は用いるべきではなく,リスク回避のため a = 0; b = 0; とするのが良い. ********* 以上のコード事例のような問題があるため, MISRA Cの規格に沿って安全なコードを記述し,リスクを防ぐのが良い. 質疑応答********* Q a = b = 0; は右結合であると考えたいが,本当に評価の順番は決まっていないのか? A 評価の順番は決まっていない. Q(K氏) MISRA-Cのチェックを行う静的解析ツールは存在するか? A マイコンメーカーが出しているコンパイラでは,オプションでチェックできるようになってきている. Q コード事例のように,実際に困ったことがある例はあるか? A if文の=と==の間違いは多く見られる. ********* ■まとめ クリティカルな用途向けの組込みプログラミングでは, MISRA Cに沿ったコーディングをすることにより,リスクを回避することができる. コード事例で一番正解が多かったのは,5問正解の人が1人であり, 参加者が知らない知識が多く勉強になった. 以上.