2019/07/10(Wed)
○[オレオレN6] libdes removal
先日のOpenSSLの Nローカルパッチのaudit中に、DES_KEY_SZ/DES_SCHEDULE_SZマクロの定義がdes.hとopenssl/des.hで競合しないよう構造体のsizeof使わずにハードコードするというアレなコードがあったんだけど、もういい加減libdesには引退いただいた方がいいよねという結論に達した。
そもそも未だにbaseでlibdesなぞ使ってるアプリケーションって実はもうlib/libtelnetしか残って無さそうなんだよな *1。そんでこいつのlibcryptoの新DES API呼ぶよう書き直すのは非常に簡単で1分もかからんレベル、libdes消しても全く困らない感じ。
あとはp**srcでどんくらい影響がでるか、とはいえ現在libdesがあるから無効(OPENSSL_DISABLE_OLD_DES_SUPPORT)にされてるdes_old.hを有効にすればほとんどは救えるはず。
ただし以下の3つの関数だけは無いのでビルド通らないアプリは出てくるとは思う。
/* The following functions are not in the normal unix build or the
* SSLeay build. When using the SSLeay build, use RAND_seed()
* and RAND_bytes() instead. */
int des_new_random_key(des_cblock *);
void des_init_random_number_generator(des_cblock *);
void des_set_random_generator_seed(des_cblock *);
この件については、どうやら他のLinuxディス鳥なんかではlibcryptoにこいつらも追加したりして対応してるみたい。 それにNでもexternal/bsd/openssl/lib/libcrypto/rnd_keys.cに実装があるので、des_old.hに追加すればまず困ることは無いんじゃないかな。
まぁユーザー1名のオレオレN6なので俺が困らないなら誰も困らないのである。
○[オレオレN6] lint(1)捨てたい
なぜかopensshのビルドの途中でzlib.hとcrypto.hでfree_funcという名前が衝突するので、Nのローカルパッチは一律free_func→freefuncという書換をしてるという話を書いた。 でもソース読んで何で衝突するのかさっぱりだったんだが、実際にbuild流してみたら単にlint(1)がバグっててfalse alarmが上がってそこでエラーになってるだけみたいで大草原。
最小ケースはこちら、baseのは強引に対策済なのでpkgsrc版のOpenSSLで試してみて。
$ cat >unko.c
#include <zlib.h>
#include <openssl/crypto.h>
^D
そんでこのincludeしただけのCソースにlint(1)を実行すると
$ lint -z -I/usr/pkg/include unko.c
unko.c:
crypto.h(502): syntax error 'free_func' [249]
crypto.h(502): syntax error ')' [249]
crypto.h(507): syntax error 'free_func' [249]
crypto.h(507): syntax error ')' [249]
となる、zlib側の
typedef void (*free_func) OF((voidpf opaque, voidpf address));
というtypedefがなぜか
int CRYPTO_set_locked_mem_functions(void *(*m) (size_t),
void (*free_func) (void *));
のようなマクロの引数と、同名ってだけでCの仕様的には衝突するはずがないのにエラーになる。
以前より主張してるけど、lint(1)はコンパイラにpcc(1)を使ってた時代ならソースレベルで同じparse.yを共有してたから信用できたわけでな。
しかしコンパイラがgccに変更になりpccの開発も長期間途絶え、そして分離したlint(1)もろくにメンテされずに20年経ってもC99対応すら完成してない時点で有害でしかないのよね。 しかもfalse alarm対策にあちこちにif !defined(__lint__)みたいなクソkludge入れてる時間があったらさっさとビルドプロセスから消えて貰うべきなのだ。
そもそも今やlint(1)はgccの-Werrorなんかより有用な情報を吐くどころかfalse alarmばかりだし、今の時代オープンソースはCoverityなんかの静的解析ツールを貸してもらえるんだし、まったくもって惰性で使ってるだけなのだ。
そんで限界集落では脳死したオッサンどもがlint(1)のバグも直さずにOpenSSLのヘッダをソース互換が無くなる *1ような変更をブチ込むという阿鼻叫喚、はーやめたらもう。
○[オレオレN6] debugging lint(1)
ちなみにOはもう7年前ほど前にlint(1)消したのだけど、Fも今の12-CURRENTでlint(1)消したんだな、そりゃ正常な判断力がありゃ百害あって一利無しと判断するわな。
最小ケース作るべく検証。
- ただのtypedef
これはセーフtypedef void *foo; void bar(void *foo) { }
unko.c(2): warning: argument foo unused in function bar [231] Lint pass2: bar defined( unko.c(2) ), but never used
- typedefを関数ポインタに
これもセーフtypedef void (*foo)(void); void bar(void *foo) { }
unko.c: unko.c(2): warning: argument foo unused in function bar [231] Lint pass2: bar defined( unko.c(2) ), but never used
- typedefによるユーザ定義型の名前と同じ識別子を持つ関数ポインタの引数
ほいアウトtypedef void *foo; void bar(void (*foo)(void)) { }
unko.c: unko.c(2): syntax error 'foo' [249] unko.c(2): syntax error ')' [249]
これでだいたい絞れたね。つまりは
- 関数(あるいは関数プロトタイプ)の引数が関数ポインタである
- 関数ポインタな引数の識別子がtypedefによるユーザー定義型名と被る
というトリガーで発生するわけだ。
これがtypedefでなくマクロってんならプリプロセッサが先に置換するので
と古事記とセキュアコーディングにそう書かれてるくらいのダサい事故が起きる可能性もあるけど、typedefじゃそうはならんので単純にlint(1)のバグよね予約語じゃあるまいし。
@次回
とりあえずusr.bin/xlintの下のコード読んでみますかね、いつも言ってるけどyacc/lex嫌いなんだよなぁ…