2018/12/31

[c/c++][CERT]volatileを正しくコンパイルしないコンパイラ

C言語のセキュアなコーディング力を身につけるため、再履修するシリーズ。


DCL17-C. volatile 修飾された変数が間違ってコンパイルされることに注意
https://www.jpcert.or.jp/sc-rules/c-dcl17-c.html

うんうんvolatileは大切よね、くらいの気持ちで読み始めたのだが、思っていたのと全然違った。

しかし、"Volatiles are miscompiled, and what to do about it" [Eide and Regehr] が示したように、彼らがテストしたコンパイラのすべてが、volatile アクセスという点に関して何パーセントかの間違ったコードを生成した。

コンパイラのバグの話か!


私が新卒で入社した頃だが、組み込みのコンパイラでdoubleだったかfloatだったか、どっちか忘れたけど最適化とあわせると正しくない結果になるというコンパイラが使われていてね。
ふっとそんな昔のことを思い出してしまった。


記事では、IA32向けのGCC4.3.0+サイズ最適化オプションでの事例を紹介している。
そう、だいたいこういうのは最適化とセットで起きるのよね。
だから最適化禁止にしているプロジェクトもしばしばだ。

ここでは、引数をそのまま返す関数を用意して、変数参照ではなく関数呼び出しさせることでバグを回避する方法を紹介している。
が・・・バグがあると分かっていればやるだろうけど、まず初っぱなからこういう書き方はしないよなぁ。。。


コンパイラに頼れないので、情報だよりか、実経験だよりになる。
情報は、コンパイラのバグを知るしかないだろう。
そうなると、最新版ではなく、少し枯れたコンパイラを使う方が無難かもしれない。が、最新版だからこそバグが治っているということもあり、くぅ。。。。


実体験に頼る方も考えておいた方が良かろう。

しかし、コンパイラは常に進化し続けているため、クリティカルなコードについては、実配備を想定してコンパイルし、その結果のオブジェクトコードが正しいふるまいをするかどうかを確認すべきである。

問題はどうやって「正しいふるまいをするかどうかを確認」するかなのだが、アセンブラなんかいちいち確認したくない。
ほしいのは、このコードをコンパイルして、実行結果がこうなればOK、みたいなやつなのだが、CPUに依存してしまうとなると簡単ではないな。

実行して、volatile効果がすぐにわかるコードというと、私には並列動作くらいしか思いつかない。
スレッドでも割込みでも良いので、並列して動かして、片方はループでvolatile変数をチェック、もう片方はそのコンテキスト以外でvolatile変数を変更、というやつだ。
スレッドならまだしも、割り込みハンドラなんかを使うとなると、環境依存になってしまって難しいな。


じゃあ関数化するかというと、これも逃れられたのが確認できたというだけで、コンパイラのバグである以上、関数化すれば必ず大丈夫とは言いきれないだろう。


あれ、そうなると、volatile以外は大丈夫なのか? 関数の呼び出し順序なんかもある程度変更することが許容されているようなことが本に書かれていた気がするので、違う部分の最適化バグが出てくることもあろう。

とか考え出すときりが無い。
結局のところ、信頼せずに検証するしかないのかね。

2018/12/30

[c/c++][CERT]opaqueな型

C言語実装のセキュリティを高めるために(自分が)!の学習中。



DCL12-C. 抽象データ型は opaque な型を使って実装する
https://www.jpcert.or.jp/sc-rules/c-dcl12-c.html

なんでopaqueだけ英語なんだよ、というのが第一印象だ。
opaqueって、透過とかそういう意味だったよなー、と思ったのだが、逆で「不透明」とかそんな意味だった。
反対語はtransparentらしい。
つまり、中身を見せたくないようなデータ型を指している。


自分がこれをやるときはvoid*を引数にさせていたのだが、型がないとコンパイラでチェックできないのだ。
そう考えると、このやり方の方が賢いな。


これを使うと中身は隠蔽できるのだが、隠蔽するだけに呼び出し元で変数を作ることができない。
ポインタ型なら作ることができるので、呼び出した先で作ってもらうようなしくみがいるだろう。


01: struct xxx;
02: typedef struct xxx xxx_t;
03: 
04: void func(...)
05: {
06:    xxx_t *p_x = CreateXxx();


ちょっと嫌なのは、一時的に使いたいだけでもスタック変数として作ることができないという点だ。
何かしらのヒープ領域がいるし、それがグローバル変数でないのなら解放する処理もいる。

C++だとコンストラクタ/デストラクタで処理できて楽なのだけど、C言語だと、ねぇ。。。
それに、ヒープ領域を操作できるようにしてない環境だと、グローバル変数で予め確保しておくしかなかろう。


とはいっても、こういうことをしたいのは、OSとかライブラリのように、内部の動きを知られたくなかったり、内部の値を想定して実装されてもろくなことがないとか、だろう。


だから、これは使いたいときに使えばよい技だと判断した。
ポインタを渡している以上、それを破壊するのは相手の自由だし。

2018/12/29

[c/c++][CERT]errno_tという型があるのか

セキュアなCコードを書けるようになろう、ということで、再履修中。



DCL09-C. errno を返す関数は返り値を errno_t 型として定義する
https://www.jpcert.or.jp/sc-rules/c-dcl09-c.html

あれ・・・errnoってint型じゃなかったっけ??

ERRNO
http://linuxjm.osdn.jp/html/LDP_man-pages/man3/errno.3.html

そうそう、注意事項書かれているように、昔はerrno.hはなかったので、使うときは宣言してた。
errno.hができたというのは把握していたのだが、intでなくなったのは知らんかった。
が、ここのサンプルコードではintになってるな。


Ubuntu18.04の/usr/include/errno.hには、intをtypedefしてerrno_tに仕立てていた。
エラー値も#defineされていたし、そこまで心配はせんで良かろうが、int型に固執することもなかろう。
errnoをチェックするためにローカル保存するシーンはしばしばあるので、気をつけておかねば。

2018/12/27

[c/c++][CERT]ポインタのtypedefとconst

書いているソースコードを見て、セキュリティとかが甘いと言われた・・・。
く、屈辱だが、意識していないので反論どころか、何も言えるものが無い。


このままじゃまずいなー、と思って探していると、こちらのサイトが見つかった。

CERT C コーディングスタンダード
https://www.jpcert.or.jp/sc-rules/

英語版がすらすら読めるならよかったのだが、残念ながら私には無理だったので、日本語訳してあるのが非常にありがたいので、少しずつ読んでいこう。


読むだけだと身につかないので、気になったところだけブログの記事にしていく予定だ。
長いことC言語で書いているけど、自分の中の常識で固まっている可能性が高いので、初心に帰って読んでいこう。


DCL05-C. typedef による型定義ではポインタ型を避ける
https://www.jpcert.or.jp/sc-rules/c-dcl05-c.html


なんでも、ポインタ型をtypedefすると、それにconstしても思ったようなconstにならないらしい。
えー、ほんとかなぁ。

01: #include <stdio.h>
02: #include <string.h>
03: 
04: struct kuma {
05:     int     a;
06:     char    b[5];
07: };
08: 
09: typedef struct kuma kuma_t;
10: typedef struct kuma* kumaptr_t;
11: 
12: 
13: static void func(const kumaptr_t Kuma)
14: {
15:     Kuma->a = 20;
16:     strcpy(Kuma->b, "def");
17: }
18: 
19: 
20: int main(void)
21: {
22:     kuma_t      k;
23:     k.a = 10;
24:     strcpy(k.b, "abc");
25:     func(&k);
26:     printf("k.a=%d\n", k.a);
27:     printf("k.b=%s\n", k.b);
28:     return 0;
29: }

$ gcc -o tst -Wall -W typedeptr.c
$ ./tst
k.a=20
k.b=def


むう、コンパイラがエラーを出さない時点で書き込めるのはわかったのだが、やっぱりショックだ。

static void func(const kuma_t *Kuma)

引数をこうやると、コンパイルの時点で失敗する。
疑って悪かった。


でも、Win32 APIなんかはそういう定義がたくさんあったよなぁ、と思ったら、下の方に説明があった。
constまで付けたポインタ型をtypedefしたものも用意していて、そっちを使うのだ。
そうだ、確かにCがついた型もあった。そういう理由だったのか。

まあ、ポインタ型をtypedefしないようにしておく方が簡単だな。



あ、関数ポインタがあるやん。
私はあれが苦手で、typedefしてからじゃないと仮引数なんかにできないのだ。。

これも下の方に書いてあった。
関数ポインタ型は例外だ、と。
ありがたい。
いや、別に従わなくてもいいのだが、書いてあると安心するじゃないか。

2018/12/23

[virtualbox6]デスクトップに作ったショートカットから起動できない

VirtualBoxの6.0がリリースされた。

が、Windows10だけかもしれんが、デスクトップに作ったショートカットから起動するとエラーダイアログが表示される。

image

https://forums.virtualbox.org/viewtopic.php?f=6&t=90827&sid=09ae384d999a521bd8a7252db959f145


作られたショートカットは「VirtualBox.exe」だけど、それを「VirtualBoxVM.exe」にするとよいそうだ。
私のところは、それで動いた。
やれやれ。

2018/12/19

[c/c++]snprintfくらいしかあふれても\0付加してくれない

忘却とは忘れ去ることなり・・・。

文字列あふれなんか上位層でカットして、下位層はそのままスルーして使ってしまえ!と実装していたのだが、Linuxみたいにリソースが裕福な環境だと、チェックを減らしてROMやスタックの消費を抑えるよりも、きっちりチェックして安全を求めることが優先されるらしい。

ちっ。

以前調べた気もするが、記憶が無いので調べ直そう。


過去記事は、こちら。

[c/c++]strncatはあふれても\0付加しない
https://hiro99ma.blogspot.com/2016/12/ccstrncat0.html


どちらかといえば、snprintf()は\0を付加するが、それ以外はやってくれない、と覚えるのが良いのか。
でも、strncpy()なんかはメジャーな気がするから、やってくれるかもしれない(よくわからない期待)。


01: #include <stdio.h>
02: #include <string.h>
03: 
04: #define STRMAX      (10)
05: 
06: int main(void)
07: {
08:     char str1[STRMAX + 1];
09: 
10:     memset(str1, 'a', sizeof(str1));
11:     snprintf(str1, sizeof(str1), "12345678901234567890");
12:     printf("snprintf : [%s]\n", str1);
13: 
14:     memset(str1, 'a', sizeof(str1));
15:     strncpy(str1, "12345678901234567890", sizeof(str1));
16:     printf("strncpy  : [%s]\n", str1);
17: }


snprintf : [1234567890]
strncpy  : [12345678901]


一瞬、「おお、やってくれるやん」と思ったが、strncpy()では11文字出力されているので、STRMAX+1の後ろに\0がついていることになるな。


まあ、こういうのは、スタックの配置とアラインメントを考えて、こうするのがわかりやすかろう。


01: #include <stdio.h>
02: #include <string.h>
03: 
04: #define STRMAX      (15)
05: 
06: int main(void)
07: {
08:     char str0[STRMAX + 1];
09:     char str1[STRMAX + 1];
10:     char str2[STRMAX + 1];
11: 
12:     memset(str0, 'x', sizeof(str0));
13:     memset(str2, 'y', sizeof(str2));
14: 
15:     memset(str1, 'a', sizeof(str1));
16:     snprintf(str1, sizeof(str1), "12345678901234567890");
17:     printf("snprintf : [%s]\n", str1);
18: 
19:     memset(str1, 'a', sizeof(str1));
20:     strncpy(str1, "12345678901234567890", sizeof(str1));
21:     printf("strncpy  : [%s]\n", str1);
22: }


snprintf : [123456789012345]
strncpy  : [1234567890123456yyyyyyyyyyyyyyyy`d�D�]


メモリの配置がどっち側かわからなかったので前後に追加したが、上から順番だったようだ。
strncpy()はstr1に20文字中16文字をコピーし、そのまま\0は付加しなかった。
メモリの境界としてつながっていたので、printf()するとstr2の"yyy..."が出力され、こちらも\0がなかったのでさらに後ろのゴミが出力されて、\0が現れたので止まった、というところだろう。


strncpy()も\0付加してくれていいような気がしたのだけど、余計なことはしないということで付加はしないのだろう。
snprintf()は、そもそもフォーマットという余計なことをするので、\0の付加くらいは当たり前という立場なのか。

2018/12/08

[win10]なぜかGitHubのフォントが太い

うちでは、デスクトップPCとノートPCがあり、どっちもWindows10を使っている(バージョンは違うが)。

なぜか、ノートPCでGitHubを見たときのフォントが太いのだ。


たとえば、ここ。
https://github.com/hirokuma/leveldb-c-example


Firefox


デスクトップPC

image  


ノートPC

image



chrome


デスクトップPC

image


ノートPC

image


横に並べたいのだが、ブログエディタの設定がうまく行かん・・・。



chromeはあからさまに違うな。

firefoxは同じかも、という気がしたが、横に並べると違った。
というか、フォントの種類が違うな。

左がデスクトップPC、右がノートPCだが、右はドットフォントになっている。
私がドットフォントの方が好きなのでいいんだけど、同じ設定のつもりなのに違うというのが気にくわない。


image



デスクトップPCのバージョンは1803、ノートPCのほうは1809だ。
ノートPCの方を先にアップデートして様子を見ていたのだが、アップデート直後からそうなのか、しばらくして変わったのか、そもそも何か設定を変更してしまったのかがよくわからん。

フォントキャッシュを削除してみたが変わらんので、そういう問題ではないようだ。
フォントの種類に詳しければ何か分かったのかもしれんが、うん、私には分からん。


結論は何も出ないが、メモとして残しておこう。