Not only is the Internet dead, it's starting to smell really bad.:2019年07月分

2019/07/03(Wed)

[Security] FreeBSD-SA-19:09.iconv - iconv buffer overflow

言及したとたんに またFreeBSDやらかしたんか(クソデカため息)、もちろんCitrus由来のバグではなくいつもの愚かな地球人が異星人の古代遺跡をわけもわからずに弄りまわして例のアレが発動して人類全滅因果地平の彼方へってやつ。

本来CitrusはCSI実装なのでcitrus_noneモジュールはあくまでシングルバイトを扱うだけのもんなんだが、なぜかFreeBSDではcitrus_noneでwchar_t=UCS4を仮定してたり本来はcitrus_utf8モジュールが受け持つUTF-8のエンコード/デコードまでやってたりで意味がわからん、多分もうCitrusとは名ばかりのUnicode Hard"weird"な実装に成り果ててんじゃねえの。

そんでそのどう考えても要らない処理だよねってところでバグってSAってほんとHARAKIRIモノだよな。

なんか以前Fの方でCitrus iconv遅いとかいう文句が聞こえてきたことあったけど、そのためにwchar_t=UCS4化してるとかだったら完全にお門違いなんだよなぁ。 性能問題については全く別の理由があるってプロファイル取ってみりゃすぐわかるんだけどね。ちなみにオレオレN6ではそのボトルネックを解消するべく新API/ABIへの移行を視野に各エンコーディングモジュールのコードを整理中だったんだけど、ここずーっとコード書くのに集中できる環境が無い状態が続いとるので完成まで辿り着くかは知らん、何よりいろんなStateful encodingの仕様を今更思い出すのがつらい。

あと遅いとか文句言ってる連中はどうせこの Issueに添付したようなUTF-{8,32}だけ速くなるアドホックなモジュール追加で満足するんじゃねえかなという気はするんだが、そもそもCitrus iconvの構造を理解してないだろうから無意味やな。

2019/07/04(Thu)

[Security] オレオレN6ツリーのOpenSSLを最新にアップデートする

外部向けに開いてるサービスはpkgsrcからビルドしたものに置換えてはいるんだけど、いいかげんbaseの方もサポート切れた1.0.1系から1.0.2系に更新し(さすがに1.1.xにはしない)、ついでにせっかくgit使ってるんだしsubtree化して今後のアプデの手間を減らすよう作業しようかと。

オレオレN6リポジトリの中にOpenSSLのgit repoをsubtreeとして登録するには

$ rm -rf crypto/external/bsd/openssl/dist
$ git remote add openssl https://github.com/openssl/openssl.git
$ git subtree add --prefix=crypto/external/bsd/openssl/dist openssl OpenSSL_1_0_2k

とすればいい、チェックアウト対象として「OpenSSL_1_0_2k」とリリースタグ指定したけども「OpenSSL_1_0_2-stable」とstable枝を指定してもよい、だがワイはOpenSSLの「安定」を信用する気がないので以下略

それと作業ベースとしてまず最新リリースでなくN8にマージされてるちょいと古いバージョンを選んだってのもある、こいつでビルドが通ってから最新の1.0.2sにまでアップデートする予定。

そんでN独自で適用されてるローカルパッチのマージ作業、これがちょいと大変。

いくつかCVE出されとるセキュリティホールの修正の差分は、gitのメリットを生かして

$ git cherry-pick -Xsubtree=crypto/external/bsd/openssl/dist ハッシュ

とOpenSSL_1_0_2-stable枝からgit cherry-pickでつまみ喰いしてくることでマージ、今後git subtree pullした時にconflictしてもCVSでやってた頃よりはよっぽど楽である。未だにCVS使ってる人って前世でどんな悪事を働いたのか。

そんでそれ以外のローカルパッチのマージなんだけど、これがいまどきCVSとかいうクソザコナメクジSCMを使ってる負債でとても大変なのだ。 そもそもCVSにおいてcommitはアトミックではないという欠点があるので、複数ファイルにわたる変更について差分を全部探すことがクッソ難しいとかいっても若者には判らんよね…

ちなみにNの場合、CVSを改造して同一commitにはユニークな番号を付加する「commitid」という機能を追加したはずなんだが、実装途中で飽きたのか打ち捨てられリポジトリには記録されてるけど検索する方法はどこにも無いという中途半端な状態のまま。 なのでGitやSubversionなんかで空気を吸うよりも簡単に出せるいわゆるチェンジセットと同等のことをNのCVS repositoryでやろうとすると

というクソめんどくさい労力が必要になる *1、さすがの限界集落である。

そんで更に困ったことにopensslはツリーにインポートされてから何度も置き場が変更されていて、移動やコピーのできないCVSでは更新履歴が途切れてしまうのでcvs annotateじゃ追い切れないんですな、やはりCVSなんて使ってるのはカルマ以下略

つーかそもそもの話、コミットメッセージを確認するまでも無くdiffで差分チラ見しただけでもこれ意味ねーんじゃねーかなと怪しまれるパッチだらけなんだよな、本当に必要なのか検証せんとならん。

問題なさそうなとこから挙げていくと、まず1.0.1系と1.0.2系のAPI非互換でOpenSSHなんかの依存アプリ/ライブラリのビルドが通らなくなるので、互換レイヤを各ヘッダ中のinline関数として提供してる部分。

$ grep -r OPENSSL_API_COMPAT dist
dist/crypto/asn1/asn1.h:#if OPENSSL_API_COMPAT >= 0x10100000L
dist/crypto/dh/dh.h:#if OPENSSL_API_COMPAT >= 0x10100000L
dist/crypto/dsa/dsa.h:#if OPENSSL_API_COMPAT >= 0x10100000L
dist/crypto/ecdsa/ecdsa.h:#if OPENSSL_API_COMPAT >= 0x10100000L
dist/crypto/evp/evp.h:#if OPENSSL_API_COMPAT >= 0x10100000L
dist/crypto/hmac/hmac.h:#if OPENSSL_API_COMPAT >= 0x10100000L
dist/crypto/rsa/rsa.h:#if OPENSSL_API_COMPAT >= 0x10100000L
dist/crypto/x509/x509_vfy.h:#if OPENSSL_API_COMPAT >= 0x10100000L
dist/crypto/x509v3/x509v3.h:#if OPENSSL_API_COMPAT >= 0x10100000L

こういうのは新規にコードを書く人がN以外ではコンパイルの通らないコードを気づかず書いてしまう可能性があるけど、わざわざ-DOPENSSL_API_COMPAT=0x10100000Lしない限りは不可視で無害だからまぁええか。 というかこんなinline関数で埋められるものならOpenSSL本家はなんで互換性壊したって話ではある、頭OpenSSLだからか…

そんで古いDESのAPIをビルドしないよう、des.hの中で

が定義されてるのをifdefで無効化してる、どうせOpenSSL本家も将来的には消すでーと書いてあるし早漏でも困らん上にそもそもDES自体以下略なのでこれもええわ。

そんでデフォルトではビルドされないいくつかの暗号アルゴリズムをopensslconf.hで

というマクロをifdefして消してビルドされるようにしてるんだが、そもそもopensslconf.hってのはConfigureから自動生成するものなので

./Configure enable-md2 enable-rc5 experimental-store BSD-generic64

あるいはターゲットを指定せずに検出にまかせて

./config  enable-md2 enable-rc5 experimental-store

と叩いて再生成したものをcommitすべきかなと、そうすりゃそもそもこの定義自体がopensslconf.hには現れてこないからifdefする必要無いのだ。

ただConfigureはPerlで書かれてるのでいちいちインストールするのはめんどくさい人もいるだろう、そしてopensslconf.hはアーキテクチャ依存で吐かれるからMDのものをMIに直す作業は必要だ。 それに一緒に吐かれるMakefileやテストデータとコードは不要ともくれば、まぁデフォルトのdistターゲット

./Configure dist

を叩いて生成されているopensslconf.h(tarballに付属するのはこれ)いじってる現状もまあ仕方ないのかもしれん、ただ可読性の悪くなるifdef使わずに単純に該当行消せなんだな。

あとは誰もがご存じの通りOpenSSLのコードはキングオブクソの塊で、移植性のあるコード *2を書くときに必須となるintN_t型なんかのstdint.h/inttypes.hにある機能を使ってない、その代わりにさっき登場したopensslconf.hの中に

#define IDEA_INT unsigned int

#define MD2_INT unsigned int

#define RC2_INT unsigned int

#define RC4_INT unsigned int
#define RC4_CHUNK unsigned long

#define DES_LONG unsigned int

といった整数型に関するマクロ、そして書式文字列や最小・最大値などを切りかえる

/* Only one for the following should be defined */
#undef SIXTY_FOUR_BIT_LONG
#undef SIXTY_FOUR_BIT
#define THIRTY_TWO_BIT
#endif

というマクロがあるんだけど、Configureが生成した値はターゲットとして指定したアーキテクチャ依存(MD)のものなので、手作業で非依存(MI)に修正せんとアカンのだが、現状の値が正しいか確認しにソースを読むのがつらい。 まーこんな行き当たりばったりのマクロを各モジュールで参照してはifdef書いてたら、そりゃ不運(ハードラック)と踊(ダンス)っちまってバグの温床どころか寒天培地にしてセキュリティーホールの穀倉地帯と言われますわ。

なおコメントに

/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a
 * %20 speed up (longs are 8 bytes, int's are 4). */

なんて書いてあってuintN_tを使うと性能ガーといいだす無知な子が中にいそうな予感するのだが、ならint_fastN_tとかあるよね?って話で、時代遅れのコードを書く輩は存在自体が有害といっていい。 そりゃさーこんなコードauditしようにもソースエディタで開いて○秒でfork決意しますわ、 LibreSSLとか BoringSSLとか。

あとそれ以外にも整数型の移植性に関連するNローカルパッチが以下の通り存在するのだが、どれも?な修正ばかりである。

とまあ、Nローカルでいじってる部分は別にこれ取り込まなくてもビルドは通るよね(そもそもpkgsrc版のopensslにはこれらの変更は適用されとらんし)、それどころか微妙に壊れてる箇所すらある。 というか本家にフィードバック(push)する意思の無いやっつけパッチはその後のマージ(pull)の手間が増えるだけで有害としかいいようがない。

んで最も意図が判らんかったのがこちら

diff -uNrBw openssl.orig/dist/crypto/des/des.h openssl/dist/crypto/des/des.h
--- openssl.orig/dist/crypto/des/des.h       2019-07-03 17:11:05.000000000 +0900
+++ openssl/dist/crypto/des/des.h  2019-07-03 15:46:39.000000000 +0900
@@ -92,6 +93,7 @@
     } ks[16];
 } DES_key_schedule;

+#ifndef __NetBSD__
 # ifndef OPENSSL_DISABLE_OLD_DES_SUPPORT
 #  ifndef OPENSSL_ENABLE_OLD_DES_SUPPORT
 #   define OPENSSL_ENABLE_OLD_DES_SUPPORT
@@ -104,6 +106,12 @@

 # define DES_KEY_SZ      (sizeof(DES_cblock))
 # define DES_SCHEDULE_SZ (sizeof(DES_key_schedule))
+#else
+# include <sys/types.h>
+# define DES_KEY_SZ    8 /* (sizeof(DES_cblock)) */
+# define DES_SCHEDULE_SZ 128 /* (sizeof(DES_key_schedule)) */
+# define OPENSSL_DISABLE_OLD_DES_SUPPORT
+#endif

 # define DES_ENCRYPT     1
 # define DES_DECRYPT     0

構造体のsizeofだったマクロが謎のハードコード値に書き換えられている、この変更に関する コミットログも特定したけど、必要性がさっぱりなのである、変更者の名前からしてもはや真意を尋ねることも叶わん。

そんでopensslconf.hに関してもうひとつ、ちゃんとコード読んでねえなぁと感じるのがOpenSSLではWindowsなどとのソース互換のために

# include OPENSSL_UNISTD

と直接unistd.hをインクルードせず、opensslconf.hの中で

#undef OPENSSL_UNISTD
#define OPENSSL_UNISTD <unistd.h>

とマクロを定義するしきたりなんだが、これを無視して直接

diff -uNrBw openssl/dist/apps/s_time.c openssl.orig/dist/apps/s_time.c
--- openssl/dist/apps/s_time.c  2019-07-03 17:11:05.000000000 +0900
+++ openssl.orig/dist/apps/s_time.c     2019-07-03 15:47:02.000000000 +0900
@@ -82,7 +82,7 @@
 # include "wintext.h"
 #endif
 #if !defined(OPENSSL_SYS_MSDOS)
-# include OPENSSL_UNISTD
+# include <unistd.h>
 #endif

 #undef PROG

と書換えたりする、これマージのたびにトラブル引き起こす可能性があるからちゃんとしきたり通りにすべきよね…

そしてもういっちょなんでこんな変更が入ったのかが謎なのが、ソースコード中のfree_funcという名称をfreefuncに一律に書換えてる。

diff -uNrBw openssl/dist/crypto/asn1/asn1.h openssl.orig/dist/crypto/asn1/asn1.h
--- openssl/dist/crypto/asn1/asn1.h     2019-07-03 17:11:05.000000000 +0900
+++ openssl.orig/dist/crypto/asn1/asn1.h        2019-07-03 15:47:02.000000000 +0900
@@ -906,7 +906,7 @@
 STACK_OF(OPENSSL_BLOCK) *d2i_ASN1_SET(STACK_OF(OPENSSL_BLOCK) **a,
                                       const unsigned char **pp,
                                       long length, d2i_of_void *d2i,
-                                      void (*free_func) (OPENSSL_BLOCK),
+                                      void (*freefunc) (OPENSSL_BLOCK),
                                       int ex_tag, int ex_class);

 # ifndef OPENSSL_NO_BIO

なぜかこういう差分が大量にあるのだ。

これおそらくはzlib.hに

typedef void   (*free_func)  OF((voidpf opaque, voidpf address));

というtyedefが存在するのでzlibとopensslを同時に使う場合にconflictするケースがあったんかなという気がする、でもこれ構造体のフィールド名まで変更しとってソース互換壊しとるし、どう考えても正しい対応ではないよなぁ。

それとapps/s_client.cおよびapps/s_server.cでfileno_stdin/fileno_stdoutの呼出箇所を一律修正してる部分、ぱっと見エラーチェックが増えてて信頼性向上目的かなと思ったけど、そもそも「標準入出力」のファイルディスクリプタ番号をチェックするのは無意味よね…

diff -uNrBw openssl.orig/dist/apps/s_server.c openssl/dist/apps/s_server.c
--- openssl.orig/dist/apps/s_server.c        2019-07-03 17:11:05.000000000 +0900
+++ openssl/dist/apps/s_server.c   2019-07-03 15:47:02.000000000 +0900
@@ -2169,7 +2169,7 @@
     char *buf = NULL;
     fd_set readfds;
     int ret = 1, width;
-    int k, i;
+    int k, i, fdin;
     unsigned long l;
     SSL *con = NULL;
     BIO *sbio;
@@ -2314,9 +2314,14 @@
         read_from_sslcon = SSL_pending(con);

         if (!read_from_sslcon) {
+            fdin = fileno_stdin();
+            if (fdin < 0) {
+                BIO_printf(bio_err,"Bad fileno for stdin\n");
+                goto err;
+            }
             FD_ZERO(&readfds);
 #if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_NETWARE) && !defined(OPENSSL_SYS_BEOS_R5)
-            openssl_fdset(fileno_stdin(), &readfds);
+            openssl_fdset(fdin, &readfds);
 #endif
             openssl_fdset(s, &readfds);
             /*

あとはopenssl2netbsdという$Id$といったRCSIDを取り除いて意図しないキーワード置換が行われないようにするスクリプトがMakefileを生成するPerlスクリプトの変数名破壊してて、それがそのままcommitされてるのも実害は無いとはいえうーんという。

diff -uNrBw openssl.orig/dist/util/pl/BC-32.pl openssl/dist/util/pl/BC-32.pl
--- openssl.orig/dist/util/pl/BC-32.pl       2019-07-03 17:11:06.000000000 +0900
+++ openssl/dist/util/pl/BC-32.pl  2019-07-03 15:46:39.000000000 +0900
@@ -100,7 +100,7 @@
        local($ret,$Name);

        $taget =~ s/\//$o/g if $o ne '/';
-       ($Name=$name) =~ tr/a-z/A-Z/;
+       (Name=name) =~ tr/a-z/A-Z/;

 #      $target="\$(LIB_D)$o$target";
        $ret.="$target: $objs\n";

というか今時RCSIDが許されるのは小学生までだよねキャハハキモーイなのでワイのリポジトリからは消滅いただいた。

これ以外は細かなバグ修正っぽいのが数件ある、でもpkgsrc版にも反映されてないし本家に報告もされとらんだろうから、本当に必要なのかもっとコード読み込まないと不明。

*1:ちなみにdeveloperであらせられる ryo(rsh)氏の作られた Commit Viewerを使うことで手間だいぶ省けるけどね…
*2:ただし メインフレーマーは隅っこで腕立て伏せでもしてろ

2019/07/05(Fri)

[Security] 続・OpenSSL

ついでにN8ではなくN HEADも差分を出して確認したんだが、こっちだと1.1.x系と1.0.2系両方がビルドされ後者はcrypto/external/bsd/openssl.old以下に置かれるように変更になったんだけど、CVSの悲しさでまたまっさらな状態から作業やり直ししとってせめてFみたいにrepocopy(リポジトリ内にある*,vファイルを直接コピーし過去の履歴を引継ぐ)すればいいのに感ある。

そんでまっさらな状態からやり直した時に、N8には適用されてるOpenSSL-1.0.2-stable枝からの以下の脆弱性対策パッチ

すべてドロップされててデグレードしとるのな、それ以外のNローカル修正は全部適用されてるし滝川先生はこれ意図的にやっとる?いやどんな意図といわれても答えに困るが…退屈な日常の中で皆にちょっとしたスリルを感じて生の喜びを感じて欲しかったからとかかな…

[チラシの裏] RSS壊れてた

ちょいとコード弄った際にRSSの事忘れててここ一日半くらいエラー吐いてた模様、失礼しました。

[Security] 続々・OpenSSL

つーことでNローカルパッチを検証していくよ、とりあえず最初は このコミットから。

スタックトレース見る限りではssl3_finish_mac()の中でhandshake_dgstが

   ∧_∧
  ( ´∀`)< ぬるぽ


   ( ・∀・)   | | ガッ
  と    )    | |
    Y /ノ    人
     / )    <  >__Λ∩
   _/し' //. V`Д´)/ ←>>1
  (_フ彡        

というしごく単純なお話で、Nのローカルパッチではチェックを追加してる。

--- src/crypto/external/bsd/openssl.orig/dist/ssl/s3_enc.c      2019-07-05 15:47:05.000000000 +0900
+++ src/crypto/external/bsd/openssl/dist/ssl/s3_enc.c 2019-07-03 15:47:02.000000000 +0900
@@ -586,7 +586,7 @@ void ssl3_finish_mac(SSL *s, const unsig
     if (s->s3->handshake_buffer
         && !(s->s3->flags & TLS1_FLAGS_KEEP_HANDSHAKE)) {
         BIO_write(s->s3->handshake_buffer, (void *)buf, len);
-    } else {
+    } else if (s->s3->handshake_dgst != NULL) {
         int i;
         for (i = 0; i < SSL_MAX_DIGEST; i++) {
             if (s->s3->handshake_dgst[i] != NULL)

ここ以外にもいくつかヌルポチェック追加されているけど省略。

この問題はすぐさま本家OpenSSLの旧課題管理に チケット#2214として報告済なんだけど、Nのコードは古いスナップショットなので最新のコードを試してくれという至極最もなリクエストに対し、めんどくせー最新のコード読んだだけだけど該当箇所に変更は無いはずだし直せやという微妙な返答をしてしまったせいで、放置プレイくらっとりますな。

そんでSSLv3は POODLE(CVE-2014-3566)でプロトコルレベルで脆弱性があると死刑宣告されとるので、そのタイミング「古いバージョンに対する古い報告」との理由で対応無しでクローズされてしまってる。

しかしその2年後、GithubのIssue Trackへまったく同じ問題である crash in ssl3_finish_mac #2113が登録され、今度はコミュニケーション不全は起きずに正しい分析がなされ、修正はOpenSSL-1.0.2-stable枝へ マージリクエストも認められとる。

この教訓として学ぶべきは「重大にはみえないバグが直るか直らないかは報告者の態度次第ってところもある」ってとこですね(ぉ

実際の 修正内容

ってやつ、Nでのローカル修正みたいにモグラ叩き的なヌルポチェック入れるよか正しい修正ですわね。

ということでオレオレN6ではこいつをcherry-pickしてこの件はおしまい。

2019/07/06(Sat)

[Security] 続々々・OpenSSL

@ssl/s3_pkt.c

お次は この変更でまたしてもヌルポ→ガッ問題で FreeBSD-SA-14:10.opensslの修正を取り込んだもの。

OpenSSL側の旧課題管理をみると #3321: NULL pointer dereference with SSL_MODE_RELEASE_BUFFERS flagとして報告されているんだけど、Fから送られてきた修正は採用せず、master枝の この修正を1.0.2-stableにバックポートしようぜという結論になっとる。

そんでそのバックポートが このコミットなので、リリースでもとっくに直ってるからローカルパッチは不要って事ね。

@crypto/rsa/rsa_eay.c

そして この変更のうち rsa_eay.cに対するCVE-2006-2940の変更の一部だけが残ってる件、これも本家の修正は これなので、さっきのやつと同様に暫定対応と本対応が違ってて前者の残骸が残ってしまったパターンやな、よってこいつも不要。

@engines/e_padlock.c

さらにお次は この修正、SSPのためにallocaを使わずmalloc/freeするぜという変更だがgcc -fstack-protectorとallocaの組合わせで何か問題ってあったっけ、alloca(3)はそもそも使うべきではないなら禿同だし、事実スタック踏み抜いてstack smashing detectedで死にやがったってなら話は別なんだけど…

そんで同件として これこれの変更もある、そんでbn_exp.cの対応は不十分でallocaそのまま使ってる箇所あるね、まぁSPARC T4の場合のパスなのでそもそもN動かねえだろという気がするので無視でええか…

そもそもあちこちasmで最適化しとるコードにalloca相当の処理がありそうなので(もちろんstack protectorは踏み抜こうが検知しない)、こんなん適用しても焼け石に水かなぁという気が、ええいやはりOpenSSLは捨てるべき。

@crypto/engine/eng_cryptodev.c

これは OpenBSD Cryptographic Frameworkこと opencrypto(9)に対応した暗号デバイスをエンジンにするコードをNでも動くようにしとる差分なんだが、ご本家たるOpenBSDからして彼らのOpenSSLのforkであるlibresslで

Abandon the auto-ENGINE /dev/crypto interface.  VIA 3des cbc receives
collateral damage.

The syncronous nature of this mechanism has hampered performance for
symmetric crypto relative to brute-force cpu. The assymetric crypto
support never really materialized in drivers.

So abandon the complexity.
ok tedu beck mikeb
some disagrement from djm but if he wants to test /dev/crypto ciphers
he should do it without this this gigantic API in the way

とまぁサポートすんの諦めて削除してしまっとるのよね、うーん現状まともに動作してるんですかねこれ…対応デバイスにしたって

# PCI cryptographic devices
hifn*   at pci? dev ? function ?        # Hifn 7755/7811/795x
ubsec*  at pci? dev ? function ?        # Broadcom 5501/5601/580x/582x

とかそもそも今更入手できるシロモノなのかよく判らんしな(しろめ)、Intelの AES-NI命令セット対応あたりはeng_aesni.cと専用エンジンあるのでopencrypto(9)経由で使う必要もなさそうだしなぁ(そもそも対応してるかすらわからん、Fは使えると マニュアルにあるけど)。

ということでこいつもさくっとdropしてしまって良さそうね、どうせpkgsrcでは無効になってるし。

@次回

まだまだ差分は残ってるけど続きはまた後日。

それにしてもNetBSDがいまだにCVSなので履歴追うのが大変なのもアレだけど、OpenSSLの方はせっかくGit使ってようが定期的にインデントやら改行コードをぶっ壊す開発者がおって定期的にスクリプト流して揃え直してるせいで、git annotateで追跡できなくなるのはすげームカつくわね、普通はそういうのpre commit hookで拒否するなり強制フォーマットしてからcommitするよね…その手のトラブルはWinCVSあたりで卒業してるはずなのにね普通のプロジェクトなら…

2019/07/07(Sun)

[オレオレN6] 続々々々・OpenSSL

まったく関係ない話だけど、某オク眺めてたら出品者の名前がどう考えてもOpenBSD由来で出品地もあの魔境「愛知県」だったのでウォッチリスト入れることなくスルーしたゾ、こわいですね。

はいどうでもいい話はおいといてOpenSSLのNローカルパッチのAudit続けるよ、ここまでほとんど無意味なパッチしか無くてあまりのマージ担当者の脳死っぷりに戦慄してるゾ。

@crypto/rand/rand_unix.c

現状のifdefでは一般的なUNIXと同様に

  • /dev/urandom
  • /dev/random
  • /dev/srandom

の順で探し、最初にみつかった乱数生成擬似デバイスからエントロピーを得るというコードになってる。んでLinux向けのifdefにNも追加してselect(2)でなくpoll(2)を使う差分がローカルで追加されてるやね。

diff -uNrBw openssl.orig/dist/crypto/rand/rand_unix.c openssl/dist/crypto/rand/rand_unix.c
--- openssl.orig/dist/crypto/rand/rand_unix.c     2019-07-06 18:47:53.000000000 +0900
+++ openssl/dist/crypto/rand/rand_unix.c        2019-07-03 15:47:02.000000000 +0900
@@ -125,8 +125,8 @@
 # include <fcntl.h>
 # include <unistd.h>
 # include <time.h>
-# if defined(OPENSSL_SYS_LINUX) /* should actually be available virtually
-                                 * everywhere */
+# if defined(OPENSSL_SYS_LINUX) || defined(__NetBSD__) /* should actually
+                                 * be available virtually everywhere */
 #  include <poll.h>
 # endif
 # include <limits.h>
@@ -314,7 +314,7 @@
                  */
                 try_read = 1;

-#   elif defined(OPENSSL_SYS_LINUX)
+#   elif defined(OPENSSL_SYS_LINUX) || defined(__NetBSD__)
                 /* use poll() */
                 struct pollfd pset;

でもなぁ、ここの部分はifdef Linuxよりもifdef OpenBSDの方にN追加しarc4random(3)使うようにした方がpoll(2)やselect(2)でデバイスファイルと格闘する必要ないっすよね…

# elif defined __OpenBSD__
int RAND_poll(void)
{
    u_int32_t rnd = 0, i;
    unsigned char buf[ENTROPY_NEEDED];

    for (i = 0; i < sizeof(buf); i++) {
        if (i % 4 == 0)
            rnd = arc4random();
        buf[i] = rnd;
        rnd >>= 8;
    }
    RAND_add(buf, sizeof(buf), ENTROPY_NEEDED);
    OPENSSL_cleanse(buf, sizeof(buf));

    return 1;
}
# else                          /* !defined(__OpenBSD__) */

まぁNにarc4random(3)マージされたの最近だしと擁護すべく履歴確認したら17年前であった、あーワイ生まれる前ですわー(しろめ)

@crypto/rand/randfile.c

ここはRAND_file_name()に注目、.rndというPRNG(Pseudo Random Number Generator)のSeedファイルを環境変数などから探してパスを返すコードなんだが、どこにも見つからんかったら代わりに/dev/arandomを返すというコードがOpenBSD方面からの要求で追加されとる。

そんでローカルパッチではこのifdef OpenBSDのパスにNも追加し、Nには無い/dev/arandomの代わりに/dev/urandomを返すように変更されとるんだな。

うーんこれ普通は.rndが見つからなければ作成するので、そもそもOpenBSDの変更は意味あるの?という希ガス、そもそもOpenSSLのRAND_load_file()のコメントに

/*
 * Note that these functions are intended for seed files only. Entropy
 * devices and EGD sockets are handled in rand_unix.c
 */

と注意書きもあるわけでな…

ちなみにこの差分、OpenSSLが1.1.0になる時ライセンスをApache License 2.0へ変更することになったのだけど、Theoはそれに 同意しなかったので、彼がcontributeした部分のコードは すべてrevert commitされてしまった。よってN HEADで使われてる1.1.1系のRAND_load_fileでは/dev/urandomは使われないのだよね。それで問題ないんだからローカルパッチも無用よね…

ちなみにlibresslにおいてRAND_file_nameは

const char *
RAND_file_name(char * buf, size_t size)
{
        if (strlcpy(buf, "/dev/urandom", size) >= size)
                return (NULL);
        return buf;
}

と絶対に/dev/urandomを返すマンになってる上に、RAND_load_file()には

/* Note that these functions should not be used. */

と「使うんじゃねえクソが」というコメントが入ってるので、もうgdgdとしかいいようが無い。

@crypto/armcap.c

こいつはARMv7以降か否かでifdef入ってる。

diff -uNrBw openssl/dist/crypto/armcap.c openssl.orig/dist/crypto/armcap.c
--- openssl/dist/crypto/armcap.c        2019-07-03 17:11:05.000000000 +0900
+++ openssl.orig/dist/crypto/armcap.c   2019-07-03 15:46:39.000000000 +0900
@@ -31,18 +31,22 @@
  * Following subroutines could have been inlined, but it's not all
  * ARM compilers support inline assembler...
  */
+#if __ARM_MAX_ARCH__>=7
 void _armv7_neon_probe(void);
 void _armv8_aes_probe(void);
 void _armv8_sha1_probe(void);
 void _armv8_sha256_probe(void);
 void _armv8_pmull_probe(void);
 unsigned long _armv7_tick(void);
+#endif

 unsigned long OPENSSL_rdtsc(void)
 {
+#if __ARM_MAX_ARCH__>=7
     if (OPENSSL_armcap_P & ARMV7_TICK)
         return _armv7_tick();
     else
+#endif
         return 0;
 }

しかしこれさ、本家の方で別のifdef入れて対応済だから意味ないんだよね…

#if __ARM_MAX_ARCH__<7
void OPENSSL_cpuid_setup(void)
{
}

unsigned long OPENSSL_rdtsc(void)
{
    return 0;
}
#else
static sigset_t all_masked;

static sigjmp_buf ill_jmp;
static void ill_handler(int sig)
{
    siglongjmp(ill_jmp, sig);
}

/*
 * Following subroutines could have been inlined, but it's not all
 * ARM compilers support inline assembler...
 */
#if __ARM_MAX_ARCH__>=7
void _armv7_neon_probe(void);
void _armv8_aes_probe(void);
void _armv8_sha1_probe(void);
void _armv8_sha256_probe(void);
void _armv8_pmull_probe(void);
unsigned long _armv7_tick(void);
#endif

はい、ご覧の通りNローカルパッチは完全に無意味なんですわ、ほんとマージ作業担当者ええかげんにせーよ(怒)。

@crypto/ppccap.c

こいつはDONT_USE_SHA{256,512}_FROM_LIBCという謎のifdefがあって、 1.0.2jのマージ時に混入したようなんだが当然のようにオリジナルには無い上にどこにも定義されてないので#if 0と同義。

そんでどうもlibcrypto/arch/powerpc64でasmコードをregenし忘れたままビルドがコケるからよくわからんけどコメントにした疑惑がある、翌日にこのifdefでコメントになってるasmコードが commitされてるしな。

ということでこれも不要な差分と思われる、あのさぁ…

@crypto/evp/m_sha1.c

差分の一部をとりあげると

diff -uNrBw openssl.orig/dist/crypto/evp/m_sha1.c openssl/dist/crypto/evp/m_sha1.c
--- openssl.orig/dist/crypto/evp/m_sha1.c    2019-07-03 17:11:05.000000000 +0900
+++ openssl/dist/crypto/evp/m_sha1.c       2019-07-03 15:46:39.000000000 +0900
@@ -136,8 +141,8 @@
     SHA224_DIGEST_LENGTH,
     EVP_MD_FLAG_PKEY_METHOD_SIGNATURE | EVP_MD_FLAG_DIGALGID_ABSENT,
     init224,
-    update256,
-    final256,
+    update224,
+    final224,
     NULL,
     NULL,
     EVP_PKEY_RSA_method,
@@ -199,8 +213,8 @@
     SHA384_DIGEST_LENGTH,
     EVP_MD_FLAG_PKEY_METHOD_SIGNATURE | EVP_MD_FLAG_DIGALGID_ABSENT,
     init384,
-    update512,
-    final512,
+    update384,
+    final384,
     NULL,
     NULL,
     EVP_PKEY_RSA_method,

ちゅーかんじで、SHA256のかわりにSHA224そしてSHA512のかわりにSHA384を使うように変更されてる、これはローカルパッチ適用前のOpenSSL側のコメントを読むと

/*
 * Even though there're separate SHA224_[Update|Final], we call
 * SHA256 functions even in SHA224 context. This is what happens
 * there anyway, so we can spare few CPU cycles:-)
 */

/* See comment in SHA224/256 section */

とあって「SHA224/384の場合でも計算中はSHA256/512を使うよその方が性能いいし」ってことらしい、まぁ最終的に計算結果切り詰めりゃ同じだしな。

ということで何でNでは性能落としてまで愚直に計算するように変更したのか、これcvs annotateで履歴追いきれずに追求を断念した。ということでこいつも不要パッチかなぁという気分。

@次回

今日はこのくらいで、あと残ってるのはあまり意味の無いstr{cpy,cat}→strl{cpy,cat}やsprintf→snprintfの書き換えと、ワイのあまり知識のないCPU arch向けの差分くらいではあるけど、まだまだ続くよ。

2019/07/08(Mon)

[オレオレN6] 続々々々々・OpenSSL

過去回の補足など。

まずfileno_std{in,out}にチェック条件を追加してる理由は、gcc-4.5のfalse alarm消しが目的だと判った、4.5.3では問題なさげなので無駄な差分には消えていただこう。

そしてcrypto/des/des.hでsizeof使わずハードコードに書き換えた理由も多分わかった、これは

ちゅーことだ、うーんこの。

それとSHA384/224のUpdateにSHA512/256使って性能アップ狙うコードはそもそもバグってるちゅー報告が PR/42881としてあがってて、これはNのlibcのSHA512/256実装だとおきる問題?なので、本家OpenSSLには未報告ってことっぽい。

そんでOPENSSL_UNISTDマクロを無意味に<unistd.h>に置換えてるのも、結局これらopensslのコードをlibdesやらlibcやらに引っ張ってきた時にローカルで適用したパッチの成れの果てなんやな。

さらにfree_func→freefuncへの置換はやっぱzlibとのconflict回避目的とコミットログから判明。

そんではちょっとだけ続き。

@crypto/bio/b_sock.c

この部分はgetaddrinfo(3)を呼びだす前に、ai_familyにOPENSSL_USE_IPV6の有無でAF_INET/AF_INET6をいちいち切替しとるのだけど(AF_UNSPECでええやろ…)るのだけど、この修正はAF_INETに強制的に上書きしてる。

diff -upNr --exclude=CVS --exclude=test openssl.orig/dist/crypto/bio/b_sock.c openssl/dist/crypto/bio/b_sock.c
--- openssl.orig/dist/crypto/bio/b_sock.c    2019-07-08 00:21:13.000000000 +0900
+++ openssl/dist/crypto/bio/b_sock.c       2019-07-03 15:47:02.000000000 +0900
@@ -690,6 +690,9 @@ int BIO_get_accept_socket(char *host, in
             }
         }

+        /* XXX: below we cast to sockaddr_in! */
+        hint.ai_family = AF_INET;
+
         if ((*p_getaddrinfo.f) (h, p, &hint, &res))
             break;

コミットログを読むと

This code is really broken. It allocates struct sockaddr on the stack
and expects to work with IPV6. Tell the hints that we only want IPV4
for now, so that we don't try to bind to an IPV6 address as returned
by getaddrinfo, and then we bash in V4 in the family!
jeez

とある、要するに本家の このコミットによる6対応コードが壊れてるので4だけ使うよみたいな話っぽい、確かにV4/6のコードが混在してて変な感じだがちゃんと読まないと影響範囲判らんなこれ。

なお最新版では名前解決周りは2016年頃に大幅にリファクタリングされてるので、そっちで修正があったとしてもcherry-pickは難しい感じだし、このBIO_get_accept_socket自体がdeprecated扱いとなってる、あのさぁ…

2019/07/09(Tue)

[オレオレN6] 続々々々々々・OpenSSL

いい加減めんどくさくなってきたのだけど、続きやるよ。

@dist/crypto/ppccpuid.pl

この部分やね。

diff --exclude=CVS -upNr openssl-1.0.2k/crypto/ppccpuid.pl src/crypto/external/bsd/openssl/dist/crypto/ppccpuid.pl
--- openssl-1.0.2k/crypto/ppccpuid.pl   2017-01-26 22:22:03.000000000 +0900
+++ src/crypto/external/bsd/openssl/dist/crypto/ppccpuid.pl  2016-10-15 01:23:17.000000000 +0900
@@ -102,8 +102,12 @@ Ladd:      lwarx   r5,0,r3
 .globl .OPENSSL_rdtsc
 .align 4
 .OPENSSL_rdtsc:
-       mftb    r3
+Lrdtsc:
+       mftb    r5
        mftbu   r4
+       mftb    r3
+       cmplw   r3,r5
+       bne     Lrdtsc
        blr
        .long   0
        .byte   0,12,0x14,0,0,0,0,0

このコミットのうちのOPENSSL_rdtscの差分だけが残ったもよう。

要するにOPENSSL_rdtsc(ReaD Time Stamp Counter)の処理でタイムベースレジスタから値を読む際に、32bit archでは64bitの読出しがアトミックでないので

  • TBL(Time Base Lower)を読む
  • TBU(Time Base Upper)を読む
  • 再度TBLを読み、前回の値と比較する
  • 一致しなければ捨ててやり直し

と無限ループしろだそうだ、古事記にもそう書かれている。はえーそうすると本家OpenSSLは正しい値とれてませんねぇ…

ちなみにmasterではこの コミットの一部で修正されて、32bit/64bitでそれぞれ最適化されてる。 まぁ1.0.2だとGCM128でしか使ってないし、masterでもrand_unix.cでエントロピープール注ぎ込むノイズとしての用途だけっぽい、まぁ値は正しくなくても
[can't get size: /home/www/public_html/https:/imgs.xkcd.com/comics/random_number.png]
ではないから脆弱性とはいえないし1.0.2-stableへのプルリクはしないちゅーことですかねこれ。

…と思ったら最新版だと この変更が更に追加されてる、 Issue Invalid instruction exception in OPENSSL_rdtsc @ppc e500v2 #8012というバグレポが上がってて、mftbではなくmfspr 268を使わんとならない石もある事を考慮しての差分がほとんどなので判りづらいけど、こっそりと「TBLだけ読んTBUは読まない」という変更入ってるなこれ。

ん?と思って確認したところ、確かにC言語でのプロトタイプは

uint32_t OPENSSL_rdtsc(void);

なのでどっちみちTBU捨てられるのだ、その前提だと教科書通りに無限ループしてTBL/TBU読むまでもないよね…

ということで捨てられるんだしこのパッチ無くて何の問題も無いよね、まーた不要パッチっすねこれ。

つーかe500v2はN/evbppcでもサポートされとるのでそっちの対応が必要よね、これまで障害報告が無いのはNのユーザーランドで必要なのはlogin(1)しかない説の補強として以下略。 まぁHEADにはOpenSSL 1.1.1系入ってるからみんなそっちを使ってるってことにしておこう。

@crypto/evp/e_aes.c

これにもpowerpc向けのifdefがある。

diff -upNr --exclude=CVS --exclude=test openssl.orig/dist/crypto/evp/e_aes.c openssl/dist/crypto/evp/e_aes.c
--- openssl.orig/dist/crypto/evp/e_aes.c     2019-07-08 00:21:13.000000000 +0900
+++ openssl/dist/crypto/evp/e_aes.c        2019-07-03 15:47:02.000000000 +0900
@@ -168,6 +168,7 @@ void AES_xts_decrypt(const unsigned char
 #  ifdef VPAES_ASM
 #   define VPAES_CAPABLE (OPENSSL_ppccap_P & PPC_ALTIVEC)
 #  endif
+# ifdef GOT_PPC_ASM_AES_WORKING
 #  define HWAES_CAPABLE  (OPENSSL_ppccap_P & PPC_CRYPTO207)
 #  define HWAES_set_encrypt_key aes_p8_set_encrypt_key
 #  define HWAES_set_decrypt_key aes_p8_set_decrypt_key
@@ -176,6 +177,7 @@ void AES_xts_decrypt(const unsigned char
 #  define HWAES_cbc_encrypt aes_p8_cbc_encrypt
 #  define HWAES_ctr32_encrypt_blocks aes_p8_ctr32_encrypt_blocks
 # endif
+# endif

 # if     defined(AES_ASM) && !defined(I386_ONLY) &&      (  \
         ((defined(__i386)       || defined(__i386__)    || \

これも前回のDONT_USE_SHA{256,512}_FROM_LIBCと同じでasmの再生成忘れなのか、AESアクセラレータ積んだPOWER8絡みなのかさっぱりわからん。

更に

@@ -878,7 +880,7 @@ const EVP_CIPHER *EVP_aes_##keylen##_##m
 { return &aes_##keylen##_##mode; }
 # endif

-# if defined(OPENSSL_CPUID_OBJ) && (defined(__arm__) || defined(__arm) || defined(__aarch64__))
+# if defined(OPENSSL_CPUID_OBJ) && (defined(__arm__) || defined(__arm) || defined(__aarch64__)) && defined(AES_ASM)
 #  include "arm_arch.h"
 #  if __ARM_MAX_ARCH__>=7
 #   if defined(BSAES_ASM)

とあるのは、Makefileが読み込むarch/*/aes.incという各アーキテクチャ向けのインクルードファイルの中で、-DAES_ASMで有効/無効を切り替えられるようにですな。

@crypto/sparccpuid.S

機能的変更は無いんだけど、 このコミットで「.」ではじまるラベルを「_」ではじまるように書換えてる、asでなんか問題あったんかなこれ。

@次回

これでだいたい解析終わりか、あとは 課題管理にバラした差分と解析結果そして必要なものは正しい修正方法をコメントしてリポジトリに反映してく作業やね…アーメンド。

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なので俺が困らないなら誰も困らないのである。

*1:リンクしてるだけならwpa_supplicantもなんだが、実はすべてlibcryptoのDES_*を使ってて全く無意味というおいゴルァ免許持ってんのか案件だった…

[オレオレ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ような変更をブチ込むという阿鼻叫喚、はーやめたらもう。

*1:構造体のフィールド名のfree_funcまで書換えちゃってるからね…

[オレオレN6] debugging lint(1)

ちなみにOはもう7年前ほど前にlint(1)消したのだけど、Fも今の12-CURRENTでlint(1)消したんだな、そりゃ正常な判断力がありゃ百害あって一利無しと判断するわな。

最小ケース作るべく検証。

これでだいたい絞れたね。つまりは

というトリガーで発生するわけだ。

これがtypedefでなくマクロってんならプリプロセッサが先に置換するので

と古事記とセキュアコーディングにそう書かれてるくらいのダサい事故が起きる可能性もあるけど、typedefじゃそうはならんので単純にlint(1)のバグよね予約語じゃあるまいし。

@次回

とりあえずusr.bin/xlintの下のコード読んでみますかね、いつも言ってるけどyacc/lex嫌いなんだよなぁ…

2019/07/11(Thu)

[オレオレN] debugging lint(1) - その2

前回の続き、OpenSSLの話からすっかり脱線してワイはどこまで流れていくのだろうか、流れ着く先は無とトルストイにもそう書かれている。

@lint(1)を読み解くために必要となる知識

ワイがlint(1)のコード読むのはOにXPG4DLを移植するという何の生産性も無い行為をやっとった頃、Nの__RENAMEも一緒に移植するためにlint(1)対応を入れるのに眺めた時以来だなぁ。 基本lint(1)絡みのエラーを発見したらほとんどの開発者は即if defined(__lint__)を入れて回避するのでな。わざわざlint(1)の修正を試みるのは栗ストス先生くらいの上にあの人の修正も大概にアドホックだからなぁ…

なおソース読んでて今回発見したバグとは関係無いけど変なmbtowc(3)の使い方をしとるのを目撃しまたお前か感ある、L接頭子対応をI18N知らん人間が実装すりゃまぁこうなるよな。 まじめに考えるのであればCの仕様における「ソース文字集合」を解釈するためにgccの--input-charsetオプション相当を実装することになるし、さらにlibcがサポートしてるかは無関係に__STDC_ISO_10646__も解釈できないとツールとして微妙。 もちろんI18N玄人だったらそんなの実装する気なんぞ湧いてだろうしなぁ、そもそもPOSIX localeのsingle binary I18NにおいてL接頭子は Portable Character Set≒US-ASCIIに収まってる前提なので、mbtowc(3)すら呼ばずにwchar_tへのキャストで十分だからな…つーかyacc/lexで書かれてる時点でI18Nもクソも無いのだ。

そう、lint(1)の心臓部たる構文解析と字句解析はyacc/lex(あるいはbison/flex)で書かれてるのである、ソースでいうと

  • src/usr.bin/xlint/lint1/cgram.y … yaccで書かれたC文法の構文解析器
  • src/usr.bin/xlint/lint1/scan.l … lexで書かれた〃の字句解析器

がそれにあたる。

若者がいまさらこの令和の時代にyacc/lexを学ぼうとすると入門書の入手すら難しいかもしれんけど、特に進歩もしてないので古本屋かアマゾンマケプレで古文書を買えば十分だと古事記にもそう書かれている。

まずyacc、こいつはBNFっぽい記法で文法を書けば(LA)LR法な構文解析器のCソースを吐いてくれる便利 *1ツール。 そしてlexは正規表現を書けば同様に字句解析器のCソースを以下同文。

そんであとはCの構文規則の知識も必要となる、プログラミング言語Cはシンプルなどと未だにいう人がおるけど、最新版の ISO/IEC 9899:2018(C18)を見てお前の知ってるCか確認しろと小一時間問い詰めたくなるよね。いろいろと複雑化しとるせいでLR法でパースするのは難儀とはきく。

ちなみに他人の努力の成果に乗っかる人間の鑑ことフリーライダーであれば、yaccの定義を C11 Parserから貰ってくるという手もある、3-clause BSDLだし。この作者の方はLR法だけで実装するテクニックについて

という論文も書いてる、来世で読むこととしよう。

とはいえlint(1)はコンパイラの拡張も扱う必要があるので、本気でlint(1)でC18対応をしようと思ったらgccやClang/LLVMの仕様も抑えないとならん。 なおこいつらのようなモダンなコンパイラはとっくの昔にyaccでの構文解析は諦めている、おそらく唯一yacc/lexで頑張ってたpccもたかだかC99対応の半ばにして諦めて開発停止状態 *2。 なもんでコンパイラのコード読むのにyacc/lexのお勉強なんて必要も無い時代なのだ、そりゃ書籍も売ってませんわ。

@エラーメッセージから

前回の再放送になるけど、このバグの引金は

  • 関数ポインタの引数名が
  • typedefで追加したユーザー定義型名と被る

と発動することが最小ケース作ったことで判明してる。

あとはエラーメッセージからもうちょっと情報を得たいところであるけど、表示される

syntax error [249]

というメッセージとコードは構文解析器がパース失敗した時にデフォルトで呼ばれる

/* ARGSUSED */
int
yyerror(const char *msg)
{
        error(249, yytext);
        if (++sytxerr >= 5)
                norecover();
        return (0);
}

という関数が出してるものなので、このメッセージから原因箇所を特定するのは困難で、grepひとつで原因箇所特定みたいな甘い話は転がっていない。

しかしもうちょっと注意深くエラーメッセージを眺めると

unko.c(2): syntax error 'foo' [249]
unko.c(2): syntax error ')' [249]

と2回のエラーが記録されてるのがお判りいただけるだろうか。

この部分に注目すると、これはlexによる字句解析で

  • void … 型
  • (*foo) … 引数名(関数ポインタ)
  • (void) …関数ポインタの引数リスト

と解析すべきところを

  • void … 型
  • (* … 引数名(もちろん正しくない)
  • foo … 予期しないトークン
  • ) … 予期しないトークン
  • (void) … どうなってるか不明

と間違った解析がされてしまったのでは?という仮説が浮かぶよね浮かばない?

ではなぜtypedefの無い時は発動しないのかが謎ではあるけど、そこいらへんは実際にコード読まないと判らんやね。

@次回

まったくやる気でないわー、もうオレオレN6からはlint(1)削除すると決意したのでこのバグ修正する意味なぞ存在しないんだけど、えっバグ修正を!? 出来らあっ!という気分になる時もあるのだ、お察しください。

*1:1/4世紀前までは便利だったかもしれんけど、国際化とかスレッド安全が求められる現代プログラミングにには不向きだし、今の時代そもそもプログラミング言語Cを選ぶこと自体が以下略
*2:ragge氏の息子が引継ぐのを待つしかないねまだ小学生だろうか、C18の実装が終る頃には最新はC5Xくらいまで進んでそう。

2019/07/12(Fri)

[オレオレN6] debugging lex(1) - その3

どうでもいい昔話、仕事中にlex的なもので実装するのが普通じゃないすかねーいう話をしたら、相手はなぜか REXXと受け取り、まったく話が噛み合わないまま時間だけ過ぎていったことがあった…LとRの発音って難しいですね(そうじゃない)

まぁワイもいきなりREXXの話を振られたら、 安達祐実…? マーク・ボラン…?としかいいようがないのだけどな。

@字句解析側のコードを追う

とりあえずscan.lを読んでみますか、左辺の正規表現にマッチすると右辺のreturnが呼出される(そのまま戻り値を返してもいいし、関数呼出してその処理結果にまかせるでもいい)。


L	[_A-Za-z]
D	[0-9]
...

{L}({L}|{D})*	return (name());
...

"*"				return (operator(T_MULT, MULT));
...
";"				return (T_SEMI);
"{"				return (T_LBRACE);
"}"				return (T_RBRACE);
...
"("				return (T_LPARN);
")"				return (T_RPARN);

ご覧の通り字句解析の時点では[_A-Za-z][_A-Za-z0-9]にマッチすれば、typedefといった予約語だろうが型名だろうが関数ポインタだろうがノンケだろうがお構いなしにバラしてしまっとる。

なので以下のコードは

typedef int foo;
int bar(void (*foo)(int))
{
}

こんな感じでバラされてるわけ。

typedef		-> name()
int		-> name()
foo		-> name()
;		-> T_SEMI
int		-> name()
bar		-> name()
(		-> T_LPARN
void		-> name()
(		-> T_LPARN
*		-> operator(T_MULT, MULT)
foo		-> name()
)		-> T_RPARN
(		-> T_LPARN
int		-> name()
)		-> T_RPARN
)		-> T_RPARN
{		-> T_LBRACE
}		-> T_RBRACE

そんでこれ以上の事はすべて構文解析側でのお仕事となっとる。

またtypedefの有無で挙動が変わる件について、name()関数の中をチラ見すると

static int
name(void)
{
	char    *s;
	sbuf_t  *sb;
	sym_t   *sym;
	int     tok;

	sb = allocsb();
	sb->sb_name = yytext;
	sb->sb_len = yyleng;
	sb->sb_hash = hash(yytext);
	if ((sym = search(sb)) != NULL && sym->s_keyw) {
		freesb(sb);
		return (keyw(sym));
	}

字句解析で正規表現にマッチすると、その文字列はyytext、長さはyylengとして参照できるのだけど、それをsbuf_tに詰めたものを引数としてsearch()関数を呼出している。

このsearch関数はシンボルテーブル(symtab)をから名前の一致するものを検索して返す、typedefされたユーザー定義型はこのsymtabに入ってるっぽい。

/* Symbol table */
static  sym_t   *symtab[HSHSIZ1];
...
/* Typ of next expected symbol */
symt_t  symtyp;
...
static sym_t *
search(sbuf_t *sb)
{
	sym_t   *sym;

	for (sym = symtab[sb->sb_hash]; sym != NULL; sym = sym->s_link) {
		if (strcmp(sym->s_name, sb->sb_name) == 0) {
			if (sym->s_keyw || sym->s_kind == symtyp)
				return (sym);
		}
	}

	return (NULL);
}

つーことで、関数ポインタの方のfooの解析でname()関数が呼ばれた時、ここで誤ってtypedeされた型名に置き換えられてしまってるっぽいね。 なもんでname()関数の戻り値も変わってしまうので、構文解析の方でマッチする規則が存在せずsyntax errorとなるちゅーこと。 予想とはちょっと違うけどもまぁ字句解析の問題ってのは当たってるからまぁええわ。

ということで今日のやる気は数分で切れたのでここまで。

こっからは余談だけど、大昔にusenetで公開されてたANSI-Cパーサ( yaccによる構文解析lexによる字句解析) や昨日触れた C18対応パーサでは、typedefなどの予約語は

"typedef"		{ count(); return(TYPEDEF); }

字句解析側で専用の戻り値を用意してるので読みやすい、構文解析側もTYPEDEFで探せば苦労なく

storage_class_specifier
	: TYPEDEF
	| EXTERN
	| STATIC
	| AUTO
	| REGISTER
	;

declaration_specifiers
	: storage_class_specifier
	| storage_class_specifier declaration_specifiers
	| type_specifier
	| type_specifier declaration_specifiers
	| type_qualifier
	| type_qualifier declaration_specifiers
	;

declaration
	: declaration_specifiers ';'
	| declaration_specifiers init_declarator_list ';'
	;

external_declaration
	: function_definition
	| declaration
	;

translation_unit
	: external_declaration
	| translation_unit external_declaration
	;

と最上位のCソースファイル(翻訳単位=translation unit)まで辿りつける。

しかしlint(1)の字句解析はさっきも書いた通りに、型名だろうが予約語だろうが関数名だろうが変数名だろうがクソミソでname()関数が呼び出される作りだから、構文解析側の処理が追いづらくて非常にめんどい。

なんでこんな作りかというと、lint(1)つまりpccのソースでは字句解析に昔はlex使ってなかったのよね、UNIX V7の scan.c参照。

struct lxrdope {
	/* dope for reserved, in alphabetical order */

	char *lxrch;	/* name of reserved word */
	short lxract;	/* reserved word action */
	short lxrval;	/* value to be returned */
	} lxrdope[] = {
	...
	"typedef",	AR_CL,	TYPEDEF,
	...
	"",		0,	0,	/* to stop the search */
	};

lxres() {
	/* check to see of yytext is reserved; if so,
	/* do the appropriate action and return */
	/* otherwise, return -1 */

	…
	for( p= lxrdope+c; p->lxrch[0] == ch; ++p ){
		if( !strcmp( yytext, p->lxrch ) ){ /* match */

と全面的にCで書かれてた。

これをどっかの時点で無理矢理lex化したことで

/*
 * Keywords.
 * During initialisation they are written to the symbol table.
 */
static  struct  kwtab {
	const	char *kw_name;	/* keyword */
	int	kw_token;	/* token returned by yylex() */
	scl_t	kw_scl;		/* storage class if kw_token T_SCLASS */
	tspec_t	kw_tspec;	/* type spec. if kw_token T_TYPE or T_SOU */
	...
} kwtab[] = {
	...
	{ "typedef",	T_SCLASS,	TYPEDEF, 0,	0,	   0, 0, 0 },
	...
}

とやっぱりCで予約語テーブルを定義して比較する字句解析になってて、あまりlex使ってる意味ねーな感ある。

@次回

あーもうめんどくせえ、lint(1)捨てて終わりにしてえ。

2019/07/13(Sat)

[WWW] RSS配信の不具合

前にも書いたけどinoreaderで既読記事が新着に上がってきたり逆に更新された記事が取得されないトラブルは今日の修正で直ったはず、ご迷惑をおかけいたしました。

原因はRSS出力時だけアンカー中の記事番号がぶっ壊れるバグがあって、配信の度にパーマネントリンク(整髪料8000円)が別になってたのが問題で、このチラシの裏と相性の悪いinoreaderだと新規記事扱いになってた模様。

これなぁ、通常の最新5日分のHTMLもRSSも実は完全に同じロジックが流れててテンプレートだけが違うだけなのに、前者は正常にアンカー吐かれるのに後者だけなぜか壊れるという心霊現象で特定まで時間かかってしまった、やっぱり他人の書いたPerlを迂闊に弄ってはならないというやつ。

2019/07/15(Mon)

[写真] EPSON GT-X970買った

EPSON GT-X750の中古を 買ったのを皮切りに、ごく短期間で GT-X820GT-X800と入手してさすがにもう買うこともねーわと思ってたんだが、ジャンク屋で最上位機種だったGT-X970が売ってたのでつい買ってしまった。 相場だと20k超えるくらいだから貧乏人には縁ねーよと思ってたのだけど、電源アダプタ欠品につき動作未確認ジャンクで5kだったので仕方ないね。

とはいえ5kという大金払って故障品だと死ねるので、家から同種のアダプタ(GT-X820のやつ)を持込んで動作確認をしてから購入したった。 なお電源入れたら初期化エラーの赤ランプが点灯したので一度はそっと棚に戻したんだがこれは単に輸送用ロック解除忘れであった、気づいたのは一度購入見送って店を出た後であった。

一点だけ残念なのは付属品、フィルムアダプタは全部揃ってたけけどドライバ関連が欠品だった。バンドルのX-rite i1Profilerというカラーキャリブレーションソフトはちゃんと買うと高いんだよなぁ。

ということで中判フィルムスキャンの画質比較でMinolta DiMAGE Scan MultiとGT-X820の比較記事を書こうと思ってたのを、GT-X970との比較に変更なんやな。

[音楽] Taylor McFerrin/Love's Last Chance

かつてHerbie HancockがHip-Hopに挑戦し壮絶に討ち死にして以来すっかり死に体であったJazzも、その仇をとったRobert Glasper ExperimentやKamasi WashingtonそしてThundercatなんかの新世代の登場で息を吹き返した感あるけど、個人的にはこのTaylor McFerrenが一番好き。

そんで8月に待望の新譜が出るとアナウンスあったんだが、父Bobby McFerrin譲りのボーカル披露でR&B/Soul色が強くなっとるね。

前作「Easy Riser」は今は亡き横浜レコファンの店内放送で流れててすげー気になって買った記憶、マジ名盤なので買って。

前回の来日公演はとっくに終わってて悲しみに溢れたので、今回は行きたいのうまぁブルーノートで諭吉コースだと財政的に厳しいけど。

2019/07/16(Tue)

[オレオレN6] NなんとかBSD OpenSSL コード監査 続き

もうひとつおかしな差分めっけたのでメモ。

diff --exclude=CVS -upNr openssl-1.0.2k/apps/ocsp.c src/crypto/external/bsd/openssl/dist/apps/ocsp.c
--- openssl-1.0.2k/apps/ocsp.c  2017-01-26 22:22:03.000000000 +0900
+++ src/crypto/external/bsd/openssl/dist/apps/ocsp.c    2016-10-15 01:23:17.000000000 +0900
@@ -230,7 +230,7 @@ int MAIN(int argc, char **argv)
         } else if (!strcmp(*args, "-port")) {
             if (args[1]) {
                 args++;
-                port = *args;
+                port = BUF_strdup(*args);
             } else
                 badarg = 1;
         } else if (!strcmp(*args, "-header")) {

ちなみにコミットログは こちら

don't try to free a buffer that came from the arguments, make a copy instead.
This can happen if we specify --port

これはコマンドライン引数の解析で-portと-urlそれぞれでportというローカル変数を共用してるんだけど

という違いがあり、前者の場合不正なfreeとなるので入れた対策とある、まぁ実際この時点では問題があったのはこのコミットの時点のコードを読んだら判った。

しかしだな、現状のコードを読んでみると

205         } else if (!strcmp(*args, "-url")) {
...
208             if (tport)
209                 OPENSSL_free(tport);
...
212             thost = tport = tpath = NULL;
213             if (args[1]) {
214                 args++;
215                 if (!OCSP_parse_url(*args, &host, &port, &path, &use_ssl)) {
216                     BIO_printf(bio_err, "Error parsing URL\n");
217                     badarg = 1;
218                 }
...
220                 tport = port;
...
230         } else if (!strcmp(*args, "-port")) {
231             if (args[1]) {
232                 args++;
233                 port = *args;
...
891     if (tport)
892         OPENSSL_free(tport);

となっており、-urlの時だけtportというローカル変数にportをコピーし、tportの方をfreeすることで-portの時に不正なfreeが実施されない対策がちゃんと入ってるのよね。

ということでこれもマージの際に差分をちゃんと読んでないせいで、逆に今ではメモリリークしてしまっとるデグレードパッチなのだ。

やっぱりNのリリースエンジニアリングは機能しとらんな、ナンマイダナンマイダ。

2019/07/17(Wed)

[オレオレN6] NなんとかBSD OpenSSL コード監査 続きの続き

さらにもういっちょ酷い差分をみた、これはOpenSSLに含まれるテストコードに対する差分。

diff --exclude=CVS -upNr openssl-1.0.2k/crypto/ripemd/rmdtest.c src/crypto/external/bsd/openssl/dist/crypto/ripemd/rmdtest.c
--- openssl-1.0.2k/crypto/ripemd/rmdtest.c      2017-01-26 22:22:03.000000000 +0900
+++ src/crypto/external/bsd/openssl/dist/crypto/ripemd/rmdtest.c        2015-03-23 19:22:48.000000000 +0900
@@ -76,7 +76,7 @@ int main(int argc, char *argv[])
 #  include <openssl/ebcdic.h>
 # endif

-static char *test[] = {
+static const char *test[] = {
     "",
     "a",
     "abc",
@@ -88,7 +88,7 @@ static char *test[] = {
     NULL,
 };

-static char *ret[] = {
+static const char *ret[] = {
     "9c1185a5c5e9fc54612808977ee8f548b2258d31",
     "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe",
     "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc",
@@ -103,7 +103,7 @@ static char *pt(unsigned char *md);
 int main(int argc, char *argv[])
 {
     int i, err = 0;
-    char **P, **R;
+    const char **P, **R;
     char *p;
     unsigned char md[RIPEMD160_DIGEST_LENGTH];

@@ -112,12 +112,12 @@ int main(int argc, char *argv[])
     i = 1;
     while (*P != NULL) {
 # ifdef CHARSET_EBCDIC
-        ebcdic2ascii((char *)*P, (char *)*P, strlen((char *)*P));
+        ebcdic2ascii((const char *)*P, (char *)*P, strlen((char *)*P));
 # endif
-        EVP_Digest(&(P[0][0]), strlen((char *)*P), md, NULL, EVP_ripemd160(),
+        EVP_Digest(&(P[0][0]), strlen((const char *)*P), md, NULL, EVP_ripemd160(),
                    NULL);
         p = pt(md);
-        if (strcmp(p, (char *)*R) != 0) {
+        if (strcmp(p, (const char *)*R) != 0) {
             printf("error calculating RIPEMD160 on '%s'\n", *P);
             printf("got %s instead of %s\n", p, *R);
             err++;

これどういう差分かというと

ので、このままコンパイルすると

error: initialization discards qualifiers from pointer target type

とエラーとなってので、testとretにconst修飾子をつけて警告出ないように修正したぜ!ってことですな。

でもですね、少しでも真面目にコード読んだらのならこの修正はありえんのだ、なぜなら

はずのコードなのよね、警告潰すだけでコードまともに読まない悪癖の典型すなぁ、他のテストコードに対する差分も同様の問題のある修正がいくつかあるなぁ。

ということで本当に必要な修正はこっちなんだよな。

diff --git a/tests/crypto/libcrypto/ripemd/Makefile b/tests/crypto/libcrypto/ripemd/Makefile
index dd55aa90a7..f219f3a9d4 100644
--- a/tests/crypto/libcrypto/ripemd/Makefile
+++ b/tests/crypto/libcrypto/ripemd/Makefile
@@ -3,5 +3,6 @@
 HELPER_NAME=   ripemdtest
 HELPER_DIR=    ripemd
 HELPER_SRCS=   rmdtest.c
+COPTS.${HELPER_SRCS}+=-Wno-write-strings

 .include <bsd.init.mk>

まぁEBCDICの場合はstrdup(3)するとかもっと手間のかかるコード書きたい人はどうぞ。

2019/07/19(Fri)

[オレオレN6] NなんとかBSD OpenSSL コード監査 続きの続きの続き

あといくつか残ってる差分、本来であればopensslconf.h中で定義すべきマクロをMakefileから渡せるようにするkludgeがほとんどだな。 ローカルで追加されてるifdef NEED_CPUID_SETUPあたりがそれね。

なぜこんなのが必要なのかというと、opensslconf.hはNローカルパッチではifdef __LP64__なんかを使って無理矢理MI(Machine Independent)に改造されてるからなんだな、本来はMD(Machine Dependent)で吐かれるはずのものなのでこいつがOPENSSL_CPUID_OBJを定義すりゃいいだけなんだけど。

なので置き場所もcrypto/external/bsd/openssl/dist/crypto/ではなくてcrypto/external/bsd/openssl/libcrypto/arch/*/の方が利に適ってるんだよな。 そうすりゃ単純に実機でconfig実行して自動生成したファイルを対応するCPUの下に放り込めばおkになるわけで。

ということでcrypto/external/bsd/openssl/lib/libcrypto/Makefileを

diff --git a/crypto/external/bsd/openssl/lib/libcrypto/Makefile b/crypto/external/bsd/openssl/lib/libcrypto/Makefile
index 2d05e00bd1..a1dcdb2d48 100644
--- a/crypto/external/bsd/openssl/lib/libcrypto/Makefile
+++ b/crypto/external/bsd/openssl/lib/libcrypto/Makefile
@@ -38,6 +38,7 @@ CRYPTODIST=   ${NETBSDSRCDIR}/crypto
 .PATH: ${OPENSSLSRC}

 .include "srcs.inc"
+.PATH: ${.CURDIR}/arch/${CRYPTO_MACHINE_CPU}

 AFLAGS+=-DELF
 LIBDPLIBS+= crypt ${NETBSDSRCDIR}/lib/libcrypt

のようにちょいと変更し、opensslconf.hはcrypto/external/bsd/openssl/libcrypto/arch/*に配置してみた。

ところがこれだとビルドこけちゃうのよね、原因は簡単な話で

ということ、インクルードパスをいじって${.CURDIR}/arch/${CRYPTO_MACHINE_CPU}/opensslconf.hを先に見にいくようにすればビルドは通るんだろうけど。

でもその解決法だとgcc -m32で/usr/lib/i386/libcrypto.soをリンクするアプリを作るときに32bit版のopensslconf.hがシステムに存在しない問題は解決しない。 結局のところopensslのconfigスクリプトが自動生成したファイルはそのまま使えず、結局今のようにifdefを大量に入れて無理矢理MIに改造せんとアカン、これは美しくないよなー。

ちなOpenBSDのLibreSSLではMDなopensslconf.hを用意してるんだけど、gcc -m32なんぞ一切考慮してないってことでええんかな *1

そもそもgcc -m32おかまいなく32/64bitどちらも/usr/includeを使うようになってるNのディレクトリ構成が無駄に話をややこしくしてる感はある。 例えばCygwinを例に挙げると、32bit版のヘッダファイルは/usr/i686-pc-cygwin/sys-root/usr/includeとして完全に分離されてるわけで、インクルードパスにさえ気をつければいいだけなのだ。

これがNだと単純に/usr/include/x86_64/openssl/opensslconf.hと/usr/include/i386/openssl/opensslconf.hを用意するだけではダメだからなぁ、opensslconf.hをインクルードしてる側のコードをいじる必要がでてきちゃうので、はっきりいって現実的でないのよ。

ということで設計ミスちゅーか理想が高くて現実世界に対応できないパターンやな、オレオレN6ではいずれなんらかの対応を考えていきたい。

*1:つーかそもそも32bit版ライブラリってどうしてるんだっけ…

[オレオレN6] OpenSSL code audit は続くよどこまでも

とりあえずNローカルパッチの大半は不要と判断ついたので、必要なパッチをマージの上opensslconf.hをとりあえずx86_64とi386用だけ作成してビルドの確認中。

だったのだけど、うーん?

dependall ===> tests/crypto/libcrypto/rc4
#      link  rc4/h_rc4test
/usr/tooldir/bin/i486--netbsdelf-gcc    --sysroot=/usr/src/obj.i386/destdir.i386     -o h_rc4test  rc4test.o -lcrypto -lcrypt       -Wl,-rpath-link,/usr/src/obj.i386/destdir.i386/lib  -L=/lib
rc4test.o: In function `main':
/usr/src/crypto/external/bsd/openssl/dist/crypto/rc4/rc4test.c:127: undefined reference to `OPENSSL_cpuid_setup'
collect2: ld returned 1 exit status

*** Failed target:  h_rc4test
*** Failed command: /usr/tooldir/bin/i486--netbsdelf-gcc --sysroot=/usr/src/obj.i386/destdir.i386 -o h_rc4test rc4test.o -lcrypto -lcrypt -Wl,-rpath-link,/usr/src/obj.i386/destdir.i386/lib -L=/lib
*** Error code 1

Stop.
nbmake: stopped in /usr/src/tests/crypto/libcrypto/rc4

これOPENSSL_cpuid_setupってそもそも.hidden属性だから不可視のはずなのに、なんでrc4test.cは直接呼びだそうとしとるんやろうな。 そもそもこいつは.initで呼ばれるものなのでユーザーが呼ぶ関数じゃないんだよな…

.section        .init
        PIC_PROLOGUE
        call    PIC_PLT(OPENSSL_cpuid_setup)
        PIC_EPILOGUE

そんでrc4test.cをのぞいてみると

int main(int argc, char *argv[])
{
    int i, err = 0;
    int j;
    unsigned char *p;
    RC4_KEY key;
    unsigned char obuf[512];

# if !defined(OPENSSL_PIC)
    void OPENSSL_cpuid_setup(void);

    OPENSSL_cpuid_setup();
# endif

んあーそうかlibcrypto.aを静的リンクするようなケースだとOPENSSL_cpuid_setup呼ばれねえ!ってやつか、でもそれ-Wl,--whole-archive libcrypto.aするべきなんじゃねーのという気が…

つーかそもそも今rc4testは動的リンクで作られとるのでOPENSSL_PICが定義されてないのが変なんやな、オレオレN6とN8でgcc/binutilsのバージョン違うのでどこかで事前定義マクロが働いてない可能性か。

それとOPENSSL_cpuid_setupでソース検索したら、x86_64の場合NのローカルパッチというかMake regenターゲットの中でx86_64cpu.plで自動生成するx86_64cpu.Sに対して、OPENSSL_cpuid_setupを.hiddenから.globlに書換えてるんだよな。

regen:
        for i in $$(find ${OPENSSLSRC} -name \*${MACHINE_ARCH}.pl) \
                $$(find ${OPENSSLSRC}/crypto/bn/asm -name ${MACHINE_ARCH}-\*.pl) \
                ${OPENSSLSRC}/crypto/bn/asm/rsaz-avx2.pl \
                ${OPENSSLSRC}/crypto/${MACHINE_ARCH}cpuid.pl ; do \
                (set +x; echo "#include <machine/asm.h>"; CC=${CC:Q} perl $$i elf | sed \
                    -e 's/.hidden       OPENSSL_cpuid_setup/.globl      OPENSSL_cpuid_setup/' \
                    -e 's/call  OPENSSL_cpuid_setup/call        PIC_PLT(OPENSSL_cpuid_setup)/' \
                    -e 's/rdseedq/rdseed/') \
                > $$(basename $$i .pl).S; \
        done

これおそらく過去rc4testのビルドがコケる問題がTNFの方でも問題になってて、よくわかんねーしOPENSSL_cpuid_setupを呼ぶんだから呼べるように.globlにしたれって対応入れた感じなんだろうか。

ただまぁ、OPENSSL_cpuid_setupそのものはx86_64とx86以外の例えばarmであればarmcap.cで

void OPENSSL_cpuid_setup(void)
{

とCの関数として定義されててバリバリ可視属性なので、x86_64とx86だけわざわざ不可視属性にする意味無いよね…という気はするのでこの対策でも悪くは無いのか。

2019/07/20(Sat)

[GCC] よくあるしっぱい

コードななめ読みでifdef追加して__sparcv9と__sparc_v9__の挙動の違いにハマって1時間ほど潰れた気分はどうだ?

まぁ知ってる人は知ってるだろうけど、初心者Sun向けに解説書いておくと、__sparcv9ってのはSUNWpro *1というコンパイラの事前定義マクロ。

そんで__sparc_v9__ってのはgccの事前定義マクロなんだけどこいつはご丁寧にSUNWpro互換の事前定義マクロも吐くので__sparcv9もプリプロセッサは処理する。

なのでgcc環境では名前がよく似ているので両者を混同してしまいがちなんだが、じつはこいつら意味は違うので迂闊に書き間違えると死ぬ。

$ echo __sparcv9 | /usr/tooldir/bin/sparc64--netbsd-gcc -E  -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"
1

$ echo __sparc_v9__ | /usr/tooldir/bin/sparc64--netbsd-gcc -E  -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"
1

と64bitコード吐いてる間は同じなんだけど、-m32スイッチつけて32bitコード吐かせようとすると

$ echo __sparcv9 | /usr/tooldir/bin/sparc64--netbsd-gcc -m32 -E  -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"
__sparcv9

$ echo __sparc_v9__ | /usr/tooldir/bin/sparc64--netbsd-gcc -m32 -E  -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"
1

はい、今死んだ今死んだよ君!

ということでgccでsparc or sparc64のifdef切るには__sparc_v9__ではなく__sparc__ + __arch64__を使えというお話。

$ echo __arch64__ | /usr/tooldir/bin/sparc64--netbsd-gcc -E  -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"
1

$ echo __arch64__ | /usr/tooldir/bin/sparc64--netbsd-gcc -m32 -E  -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"
__arch64__
*1:商品名だとアホみたいにブランド名変え続けてたので(Oracle Developer Studio←Sun Studio←Sun ONE Studio←Forte Developer←Sun Workshop←SunSoft Workshop←SPARCworks)、混乱しないように最後まで変わらなかったSVR4パッケージ名でみんな呼んでるはず…だよね?

2019/07/21(Sun)

[オープンソース] git cherry-pickでいともたやすく行われるライセンス汚染

先日からこのチラシの裏で実況しとる通りオレオレN6のOpenSSLメンテを楽にするためgit subtree化しつつ

という作業をしたんだが、これまじめに考えると

という問題をはらんでることに気づいてしまった。

これOpenSSLの開発者自身が1.0.2枝にプルアップしのであればパッチ貢献者がライセンス変更に同意したと見なしてもいいんだろうけど、ワイ個人が勝手に持ってくるってパターンではアウトだよなぁ、まさかパッチだけはデュアルライセンスなんて都合のいい解釈は通らねえし。

ワイ自身はOpenBSDみたいにAPL2.0断固反対ではないけど、置き場所をcrypto/external/bsdからcrypto/expernal/apache2に移さんとならんのがアレ。

[オレオレN6] まだOpenSSLいじってる

前記事でOpenSSL1.1.0以降はAPL2.0と書いてしまったが、まだリリースされてない3.0からAPL2.0に切替わるので、今リリースされてるブツはまだBSDLであった *1

それと作業も残り少ない段階になって残念なことに気づいてしまったんだが、OpenSSL-1.0.2のEOLは2019/12/31なので半年後にはまた1.1.1化の作業が必要になるのだ、そんなら最初っから1.1.1にしておけばよかったぞはやく教えてよ…

まぁ現在のソースツリー上でのビルドプロセスを理解したしNローカルパッチの要不要の切り分けができてるから、1.1.1化も今回ほど手間はかからんだろうからええけどな。

@MACHINE_CPU=vaxでビルドがこける

なるほど、bn_exp.cのコードでalloca使わないように変更してた件は実行時に-fstack-protectorのガードが働いて死ぬからでなく、警告オプションの-Wstack-protectorにひっかかって-Werrorとのコンボでビルドエラーになるのか。

--- bn_exp.o ---
#   compile  libcrypto/bn_exp.o
/usr/tooldir/bin/vax--netbsdelf-gcc -O1 -fgcse -fstrength-reduce -fgcse-after-reload -std=gnu99  -Werror   -fstack-protector -Wstack-protector   --param ssp-buffer-size=1   --sysroot=/usr/src/obj.vax/destdir.vax -Dlibcrypto -I. -I/usr/src/crypto/external/bsd/openssl/dist/crypto -I/usr/src/crypto/external/bsd/openssl/dist -I/usr/src/crypto/external/bsd/openssl/dist/crypto/asn1 -I/usr/src/crypto/external/bsd/openssl/dist/crypto/evp -I/usr/src/crypto/external/bsd/openssl/dist/crypto/modes -DDSO_DLFCN -DHAVE_DLFCN_H  -D_FORTIFY_SOURCE=2 -c   -I/usr/src/crypto/external/bsd/openssl/dist/crypto/bn /usr/src/crypto/external/bsd/openssl/dist/crypto/bn/bn_exp.c -o bn_exp.o
cc1: warnings being treated as errors
/usr/src/crypto/external/bsd/openssl/dist/crypto/bn/bn_exp.c: In function 'BN_mod_exp_mont_consttime':
/usr/src/crypto/external/bsd/openssl/dist/crypto/bn/bn_exp.c:698: warning: not protecting local variables: variable length buffer
*** [bn_exp.o] Error code 1
nbmake: stopped in /usr/src/crypto/external/bsd/openssl/lib/libcrypto

これNだとamd64なんかではエラーにならんので意識したことなかったな、__builtin_allocaだと抑制されるわけでもなさそうなんだがなーんでエラー出ないんですかねぇ…

該当箇所のコードはこんなん。

#ifdef alloca
    if (powerbufLen < 3072)
        powerbufFree =
            alloca(powerbufLen + MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH);
    else
#endif
        if ((powerbufFree =
             (unsigned char *)OPENSSL_malloc(powerbufLen +
                                             MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH))
            == NULL)
        goto err;

ちょいと頭のいいコンパイラならpowerbufLenのとりうる値は最大でわずか3072+64でその程度でスタック踏み抜く可能性はほぼ皆無と判断できそうな気もするけど、問答無用で-Wstack-protectorが発動するもよう。

つーかそもそもこれallocaだったら定数でも出るんだな。

#include <stdio.h>

int
main(void)
{
        alloca(1);
}
$ LANG=C gcc -fstack-protector -Wstack-protector unko.c -o unko
unkox.c: In function 'main':
unkox.c:6:1: warning: stack protector not protecting local variables: variable length buffer [-Wstack-protector]
 main(void)

もうこうちょっと慈悲はないんですかねぇ…

むーgccのコード確認したら、alloca呼んだら無条件でこの警告出るもよう。

  /* Honor stack protection warnings.  */
  if (warn_stack_protect)
    {
      if (cfun->calls_alloca)
        warning (OPT_Wstack_protector,
                 "not protecting local variables: variable length buffer");
      if (has_short_buffer && !crtl->stack_protect_guard)
        warning (OPT_Wstack_protector,
                 "not protecting function: no buffer at least %d bytes long",
                 (int) PARAM_VALUE (PARAM_SSP_BUFFER_SIZE));
    }

まぁチェック条件などで最大値が決まってるとしても実際のスタックサイズはわからんから一律禁止ってことか、vmparam.hのDFLSSIZ/MAXSSIZあたりでなんとかならんのか。

んでコード監査の結果としては、OpenSSLでalloca呼んでるとこはスタック踏み抜くほどのサイズ要求にならんのは前後のコード読めば確定的なので、警告抑制でいいかもう。

diff --git a/crypto/external/bsd/openssl/lib/libcrypto/bn.inc b/crypto/external/bsd/openssl/lib/libcrypto/bn.inc
index 51bda18c54..37fad137ae 100644
--- a/crypto/external/bsd/openssl/lib/libcrypto/bn.inc
+++ b/crypto/external/bsd/openssl/lib/libcrypto/bn.inc
@@ -21,3 +21,5 @@ SRCS += ${BN_SRCS}
 .for cryptosrc in ${BN_SRCS}
 CPPFLAGS.${cryptosrc} = -I${OPENSSLSRC}/crypto/bn
 .endfor
+
+COPTS.bn_exp.c=-Wno-stack-protector
diff --git a/crypto/external/bsd/openssl/lib/libcrypto/engine.inc b/crypto/external/bsd/openssl/lib/libcrypto/engine.inc
index de4d66052e..3718b447f9 100644
--- a/crypto/external/bsd/openssl/lib/libcrypto/engine.inc
+++ b/crypto/external/bsd/openssl/lib/libcrypto/engine.inc
@@ -39,3 +39,5 @@ CRYPTOCPPFLAGS+=-DNEED_CPUID_SETUP
 .for cryptosrc in ${ENGINE_SRCS}
 CPPFLAGS.${cryptosrc} = -I${OPENSSLSRC}/crypto/engine ${CRYPTOCPPFLAGS}
 .endfor
+
+COPTS.e_padlock.c=-Wno-stack-protector

ってもNローカルパッチのようにalloca → malloc+freeしたところでたいした差分ではないんだけども。

@MACHINE_CPU=m68kでビルドがこける

うーんm68kのAESのencrypt/decryptはNだとアセンブラで書かれてるんだけど、N8のソースのままだとビルド通らんな。

#      link  bin/openssl
/usr/tooldir/bin/m68k--netbsdelf-gcc    --sysroot=/usr/src/obj.hp300/destdir.hp300     -o openssl  openssl.o apps.o ecparam.o ec.o pkeyparam.o genpkey.o pkey.o pkeyutl.o ts.o cms.o s_cb.o s_socket.o app_rand.o verify.o asn1pars.o req.o dgst.o dh.o dhparam.o enc.o passwd.o gendh.o errstr.o ca.o pkcs7.o crl2p7.o crl.o rsa.o rsautl.o dsa.o dsaparam.o x509.o genrsa.o gendsa.o prime.o s_server.o s_client.o speed.o s_time.o version.o sess_id.o srp.o ciphers.o nseq.o pkcs12.o pkcs8.o spkac.o smime.o rand.o engine.o ocsp.o -lssl -lcrypto -lcrypt       -Wl,-rpath-link,/usr/src/obj.hp300/destdir.hp300/lib  -L=/lib
/usr/tooldir/lib/gcc/m68k--netbsdelf/4.5.3/../../../../m68k--netbsdelf/bin/ld: warning: type and size of dynamic symbol `AES_encrypt' are not defined
/usr/tooldir/lib/gcc/m68k--netbsdelf/4.5.3/../../../../m68k--netbsdelf/bin/ld: dynamic variable `AES_encrypt' is zero size
/usr/tooldir/lib/gcc/m68k--netbsdelf/4.5.3/../../../../m68k--netbsdelf/bin/ld: speed.o(.text+0x2972): unresolvable R_68K_32 relocation against symbol `AES_encrypt'
/usr/tooldir/lib/gcc/m68k--netbsdelf/4.5.3/../../../../m68k--netbsdelf/bin/ld: final link failed: Nonrepresentable section on output
collect2: ld returned 1 exit status

エラーログだと

  • AES_encryptに.typeと.sizeが定義されてねえぜ
  • AES_encryptのサイズがゼロだぜ
  • AES_encryptが解決できねえから再配置できねえぜ
  • 存在しないセクションあるからリンク失敗だぜ

ちゅーことでGNU asの.typeと.size疑似命令で明示しろってだけなんだが、N8はこのままリリースされとるってことはbinutilsのバージョンでエラーにはならんのかな。 そんでもHEADにimportされてる1.1.1(crypto/external/bsd/openssl)の方では修正はいってたりよくわからん。

ちなみにその修正もコピペミスでバグってるからオレオレN6ではリンクエラーなのだ、正しくは↓やね。

$ diff -u aes-m68k.S.orig aes-m68k.S
--- aes-m68k.S.orig     2018-03-07 15:55:01.000000000 +0900
+++ aes-m68k.S  2019-07-21 15:12:12.869653900 +0900
@@ -828,7 +828,7 @@

        moveml  %sp@+,%d2-%d7/%a2-%a5
        rts
-       .size   AES_set_decrypt_key, .-AES_set_decrypt_key
+       .size   AES_encrypt, .-AES_encrypt

 | void
 | AES_decrypt(const unsigned char *in,

@MACHINE_CPU=armわからん

うーんarm_arch.hの定義を眺めてるけど 全部同じじゃないですかとしかいいようがないぞ…

# if !defined(__ARM_ARCH__)
#  if defined(__CC_ARM)
#   define __ARM_ARCH__ __TARGET_ARCH_ARM
#   if defined(__BIG_ENDIAN)
#    define __ARMEB__
#   else
#    define __ARMEL__
#   endif
#  elif defined(__GNUC__)
#   if   defined(__aarch64__)
#    define __ARM_ARCH__ 8
#    if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
#     define __ARMEB__
#    else
#     define __ARMEL__
#    endif
  /*
   * Why doesn't gcc define __ARM_ARCH__? Instead it defines
   * bunch of below macros. See all_architectires[] table in
   * gcc/config/arm/arm.c. On a side note it defines
   * __ARMEL__/__ARMEB__ for little-/big-endian.
   */
#   elif defined(__ARM_ARCH)
#    define __ARM_ARCH__ __ARM_ARCH
#   elif defined(__ARM_ARCH_8A__)
#    define __ARM_ARCH__ 8
#   elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__)     || \
        defined(__ARM_ARCH_7R__)|| defined(__ARM_ARCH_7M__)     || \
        defined(__ARM_ARCH_7EM__)
#    define __ARM_ARCH__ 7
#   elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__)     || \
        defined(__ARM_ARCH_6K__)|| defined(__ARM_ARCH_6M__)     || \
        defined(__ARM_ARCH_6Z__)|| defined(__ARM_ARCH_6ZK__)    || \
        defined(__ARM_ARCH_6T2__)
#    define __ARM_ARCH__ 6
#   elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__)     || \
        defined(__ARM_ARCH_5E__)|| defined(__ARM_ARCH_5TE__)    || \
        defined(__ARM_ARCH_5TEJ__)
#    define __ARM_ARCH__ 5
#   elif defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)
#    define __ARM_ARCH__ 4
#   else
#    error "unsupported ARM architecture"
#   endif
#  endif
# endif

ゲシュタルト崩壊してきたぞ…

そんでワイも持ってるARMはhpcarmくらいなんだが、こいつは

  • __ARM_ARCH_4__(StrongARM SA-1100/1110)
  • __ARM_ARCH_5__(XScale PXA270)

で最低でもARMv4のはずなんだが、クロスのgccは

$ echo __ARM_ARCH_3__ | /usr/tooldir/bin/arm--netbsdelf-gcc -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"
1

とOpenSSLのARM用のアセンブラコードがサポートしとらん__ARM_ARCH_3__を自称するしもうわからん。

さらにはarch/arm/crypto.incみると

.PATH.S: ${.PARSEDIR}
CPUID_SRCS = armv4cpuid.S armcap.c armv4-mont.S armv4-gf2m.S
CPUID = yes
CPPFLAGS += -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_GF2m
.if !empty(MACHINE_ARCH:Mearmv4*) || ${MACHINE_ARCH} == "armeb"
CPPFLAGS += -D__ARM_MAX_ARCH__=4
.elif ${MACHINE_ARCH} == "earmeb" || ${MACHINE_ARCH} == "earmhfeb"
CPPFLAGS += -D__ARM_MAX_ARCH__=5
.elif !empty(MACHINE_ARCH:Mearmv6*eb)
CPPFLAGS += -D__ARM_MAX_ARCH__=6
.else
CPPFLAGS += -D__ARM_MAX_ARCH__=8
.endif
.include "../../crypto.inc"

となってて強制的に__ARM_MAX_ARCH__を指定しててカオスもいいところなのだ。

*1:正確には4-clause BSDL(SSLeay License)とAPL1.0のデュアルライセンスね

2019/07/22(Mon)

[近況] RFIDゲートが欲しい

我が家の認知症老人、ここしばらくは物盗られ妄想からカプグラ症候群にステップアップしたようで、家族は全員別人にすりかわり自分はどこか知らない家に拉致監禁されてるというロールプレイをはじめた。 なもんで隙あらば窓から叫んだり外飛び出して通行人に助けを求めたり深夜徘徊して自分の家と思い込んだ隣家に押入ろうとしたりするので、四六時中気の休まる時間が無いですわ。

こうなるともう中華監視カメラとかの類は力不足で、非接触のICタグかなんかで火元や出入口にアンテナ置いて夜間に近づいたら即アラートとかでもないと無理なんだろうけど、工作で何とかなるレベルじゃなさそうよね。

2019/07/23(Tue)

[SCM] git-subtreeの落とし穴

以前はcontribだったけど現在は本体に取り込まれたgit-subtree、こいつは3rd partyなソースの管理に便利なんだけどrebaseが絡むとややこしいのよな。毎回忘れるのでメモ残しておく。

まずgit subtree addした後、それより以前のcommitに間違いを発見してrebaseを実行するようなケース、簡単な図にすると

	A (master) edit master 1
	|
	B (master) subtree add onto master
	|
	C (master) edit master 2

という履歴があった場合、Aを改変すべく

$ git rebase -i A

を実行すると、なぜかBの時点で実行した

$ git subtree add --prefix=foo --squash remote master

の--prefixが最適用の際に無視されてしまい、ディレクトリ構成が変になるわAのソースが上書きされCの差分適用でconflictが発生するので、お前は全身の穴から血を吹いて死ぬ。

例えばワイのオレオレN6ではOpenSSLをsubtree化してるのだけど、subtree add以前のcommitからrebaseを実行するとこうなる。

$ git checkout tnozaki-openssl
$ git rebase a25e71bd8d516744e119cf08eaf89ed9c3a11ec2
CONFLICT (add/add): Merge conflict in tools/Makefile
Auto-merging tools/Makefile
CONFLICT (add/add): Merge conflict in .gitignore
Auto-merging .gitignore
error: could not apply 44cfd356c... Squashed 'crypto/external/bsd/openssl/dist/' content from commit 76599d516c
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 44cfd356c... Squashed 'crypto/external/bsd/openssl/dist/' content from commit 76599d516c

前述の通りsubtree addで指定した--prefix=src/crypto/external/bsd/openssl/distが無視されてしまい

と本来はprefix以下に置かれるべきファイルがrepository rootに存在するファイルを上書きしてしまい、conflictが発生してしまうのだ。

わざわざ実装読みたくねえし原因調べる気もないのでこれが正解かは知らんけど、git rebaseに-r/--rebase-mergesオプションをつけて実行してmerge commit情報を残すといいみたい。

$ git rebase --rebase-merges a25e71bd8d516744e119cf08eaf89ed9c3a11ec2
Rebasing (2/59)
Checking out files: 100% (2278/2278), done.
Rebasing (5/59)
Checking out files: 100% (2199/2199), done.
Checking out files: 100% (2278/2278), done.
Checking out files: 100% (569/569), done.
Checking out files: 100% (24/24), done.
Successfully rebased and updated refs/heads/tnozaki-openssl.

はい--prefixが維持されてsubtree addが適用されるので正常にrebaseできまんた。

そんでお次、今度は

というケース、これも言葉じゃ伝わりにくいので図にすると

	A (master) edit master 1
	|\
	| B (branch) edit branch 1
	| |
	| C (branch) subtree add onto branch
	| |
	| D (branch) edit branch 2
	|
	E (master) edit master 2

という状態から

$ git rebase master

を実行して

	A (master) edit master 1
	|
	E (master) edit master 2
	 \
	  B (branch) edit branch 1
	  |
	  C (branch) subtree add onto branch
	  |
	  D (branch) edit branch 2

としたい場合やね、でもこれもまたエラーになってしまう。

さっきと同様にオレオレN6でやってみた場合の失敗ログはこちら。

$ git rebase master
First, rewinding head to replay your work on top of it...
Generating patches: 100% (54/54), done.
Applying: ENHANCEMENT: temporary remove crypto/external/bsd/openssl/dist for git subtree.
Applying: Squashed 'crypto/external/bsd/openssl/dist/' content from commit 76599d516c
Using index info to reconstruct a base tree...
.git/rebase-apply/patch:1285: trailing whitespace.
     the certificate can be used for (if anything). Set valid_flags field
.git/rebase-apply/patch:1328: trailing whitespace.
  *) Initial experimental support for explicitly trusted non-root CAs.
.git/rebase-apply/patch:1379: trailing whitespace.
  *) New ctrls to retrieve supported signature algorithms and
.git/rebase-apply/patch:1584: trailing whitespace.

.git/rebase-apply/patch:1754: trailing whitespace.
  *) Fix for TLS record tampering bug. A carefully crafted invalid
warning: squelched 7650 whitespace errors
warning: 7655 lines add whitespace errors.
Falling back to patching base and 3-way merge...
CONFLICT (add/add): Merge conflict in tools/Makefile
Auto-merging tools/Makefile
CONFLICT (add/add): Merge conflict in .gitignore
Auto-merging .gitignore
error: Failed to merge in the changes.
Patch failed at 0002 Squashed 'crypto/external/bsd/openssl/dist/' content from commit 76599d516c
hint: Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".

やっぱり.gitignoreとtools/Makefileのconflictなので、subtree addの--prefixが失われたことが原因と思われる。

これもやっぱり-r/--rebase-mergesオプションをつければおk。

$ git rebase --rebase-merges master
Checking out files: 100% (2278/2278), done.
Checking out files: 100% (2199/2199), done.
Checking out files: 100% (2278/2278), done.
Checking out files: 100% (569/569), done.
Checking out files: 100% (3/3), done.
Checking out files: 100% (11/11), done.
Successfully rebased and updated refs/heads/tnozaki-openssl.

しかしBitbucketの運営元である暮らし安心 Altassianのブログには

あなたは sub-tree の含まれるリポジトリをどうやってリベースしていますか? この Stack Overflow に関する議論から分かることは、銀の弾丸は存在しないということです。

有効な手順は、手動でリベースを行うために単純に rebase --interactive を実行し、git subtree add のコミットを削除して rebase --continue を実行し、リベースが完了したら git subtree add コマンドを再実行することだと思います。

などと書いてある、Stackoverflowがソースってのがアレやけどまぁワイより何十倍もgitに詳しいだろうから、--rebase-mergesではダメなケースもあるのかもしれない。 つーか古いバージョンの話をしているのだろうか…

そして最後にgit subtree addする際には必ず--squashを忘れないこと、でないとrebaseをする時に

という落とし穴があるのだ。

結論、やっぱりgit捨てたい。

2019/07/24(Wed)

[オレオレN6] OpenSSL + ARM arch

前回の続き、オレオレN6のOpenSSLがhpcarmでビルド止まるのはクロス用のgccが__ARM_ARCH_3__なのでarm_arch.hの中で未サポートのCPUと判定されるからなんだが、N8で問題が起きないのは__ARM_ARCH_5TEJ__を返すからちゅーことか。

$ echo __ARM_ARCH_5TEJ__ | /usr/tooldir/bin/arm--netbsdelf-eabi-gcc -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"
1

ということで、今のMakefile中では

.if !empty(MACHINE_ARCH:Mearmv4*) || ${MACHINE_ARCH} == "armeb"
CPPFLAGS += -D__ARM_MAX_ARCH__=4
.elif ${MACHINE_ARCH} == "earmeb" || ${MACHINE_ARCH} == "earmhfeb"
CPPFLAGS += -D__ARM_MAX_ARCH__=5
.elif !empty(MACHINE_ARCH:Mearmv6*eb)
CPPFLAGS += -D__ARM_MAX_ARCH__=6
.else
CPPFLAGS += -D__ARM_MAX_ARCH__=8
.endif

としてMACHINE_ARCHで条件分岐してOpenSSLローカルの定義である__ARM_MAX_ARCH__をセットしとるんだけどこれは間違いであり

diff --git a/crypto/external/bsd/openssl/lib/libcrypto/arch/arm/crypto.inc b/crypto/external/bsd/openssl/lib/libcrypto/arch/arm/crypto.inc
index e11de9e953..699ee2de0e 100644
--- a/crypto/external/bsd/openssl/lib/libcrypto/arch/arm/crypto.inc
+++ b/crypto/external/bsd/openssl/lib/libcrypto/arch/arm/crypto.inc
@@ -3,13 +3,13 @@ CPUID_SRCS = armv4cpuid.S armcap.c armv4-mont.S armv4-gf2m.S
 CPUID = yes
 CPPFLAGS += -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_GF2m
 .if !empty(MACHINE_ARCH:Mearmv4*) || ${MACHINE_ARCH} == "armeb"
-CPPFLAGS += -D__ARM_MAX_ARCH__=4
+CPPFLAGS += -D__ARM_ARCH__=4
 .elif ${MACHINE_ARCH} == "earmeb" || ${MACHINE_ARCH} == "earmhfeb"
-CPPFLAGS += -D__ARM_MAX_ARCH__=5
+CPPFLAGS += -D__ARM_ARCH__=5
 .elif !empty(MACHINE_ARCH:Mearmv6*eb)
-CPPFLAGS += -D__ARM_MAX_ARCH__=6
+CPPFLAGS += -D__ARM_ARCH__=6
 .else
-CPPFLAGS += -D__ARM_MAX_ARCH__=8
+CPPFLAGS += -D__ARM_ARCH__=8
 .endif
 .include "../../crypto.inc"

として本来はコンパイラが正しい値をセットするはずの(現状はしていない)__ARM_ARCH__を指定して、arm_arch.hの条件分岐を機能するようにしろってことかね。

あとワイはevb*とか基板剥きだしのオタクプロダクツには近寄らん主義なのでさっぱり判らんのだけど、/etc/mk.confに

USR_OBJMACHINE=yes
OBJMACHINE=yes

を設定しただけではMACHINE_ARCHが異なる場合もobjdirが同じで困るんだが、これ明示的に別のディレクトリにするにはわざわざbuild.shの引数で設定しないとダメなんすかね、めんどい。

[オレオレN6] OpenSSLアップデート作業もうそろそろ片付きそう

ようやくopensslconf.hのMD化を終えてN6でサポートされてるCPUはひととおりビルド通るのを確認、x86_64とsparc64は32/64bitでかっこわるいifdef入れてるけど、それぞれi386とsparcのopensslconf.hもインストールするようにして

#if defined(__x86_64__)
#include <openssl/x86_64/opensslconf.h>
#elif defined(__i386__)
#include <openssl/i386/opensslconf.h>
#endif

みたいなwrapperを用意した方がよさそうだけど、それは後回し。

そんでOpenSSLのコードには最適化のための黒魔術

# DES_PTR       use pointer lookup vs arrays in the DES in crypto/des/des_locl.h
# DES_RISC1     use different DES_ENCRYPT macro that helps reduce register
#               dependancies but needs to more registers, good for RISC CPU's
# DES_RISC2     A different RISC variant.
# DES_UNROLL    unroll the inner DES loop, sometimes helps, somtimes hinders.
# BN_LLONG      use the type 'long long' in crypto/bn/bn.h
# RC4_INDEX     define RC4_INDEX in crypto/rc4/rc4_locl.h.  This turns on
#               array lookups instead of pointer use.
# RC4_CHUNK     enables code that handles data aligned at long (natural CPU
#               word) boundary.
# BF_PTR        use 'pointer arithmatic' for Blowfish (unsafe on Alpha).
# BF_PTR2       intel specific version (generic version is more efficient).

というマクロが大量にあって、Configureを実行するとターゲットのCPUに合わせた設定をopensslconf.hに定義してくれるんだが

で定義される値がバラバラで、どれがちゃんとベンチ回して測定して決めた最適値なのか判らんのよな。 特に今opensslconf.hにではなくCPPFLAGSで渡してる値なんてそれこそSSLeay時代からの設定をそのまま秘伝のタレ状態で引継いでて、廃止されてるはずの指定あったりで信用性がちょっとね…

今はpkgsrcのパッチの値を採用してopensslconf.hを生成して放り込んだけど、それでもOpenBSD(LibreSSL)やLinuxの設定値と結構違いがあるのだ(CPU同じなら一緒やろ)、ちゃんとマシン用意してベンチ回して検証せんと駄目だわな。 とはいえしまいこんでる産廃ども(SUN Blade150, Mac Mini G4, Jornada 710, W-ZERO3複数)を引っ張り出して火入れてられるような家庭の状態じゃねえからな…

しゃーないのでgxemulとかQEMUでお茶濁すか、どうせ今更エミュ以外で動かす奴もおらんだろうし…

ということでmasterへのmergeのblockerになっとるlint(1)のfalse alarm問題以外はだいたい片付いたから、そっちの退治に戻りますかね…ああ読みたくねえ…

2019/07/25(Thu)

[オレオレN6] debugging lint(1) その4

前回はlex(1)による字句解析側を調べた結果

という事までは調べたので、今度はyacc(1)側の調査。

yacc(1)のソースは%%と%%で囲まれた中に、BNF記法によく似た書式で構文規則を記述していく。

%%
program:
          /* empty */ {
          	...
          }
        | translation_unit
        ;

translation_unit:
          ext_decl
        | translation_unit ext_decl
        ;
...
%%

ここでジェイコム1円61万株…?みずぽ大量誤発注…?という人は「バッカス・ナウア記法」でお勉強のお時間なんやな。 ナウアつっても日高義樹のワシントンリポートも無関係なのである。 ふつーのプログラマならすでにマスター済やろうからBNFを読むようにこのyaccの構文規則を読み進めていけばいいだけ…

…なんだがプログラミング言語Cの構文規則はそれなりに複雑なので、時間がかかり過ぎるのであまり現実的ではないやね。

かといって一般的なCのデバッガ(gdbなど)で障害部位の特定をしようにも、自動生成された有限オートマトン(日本に帰化したサッカー選手の名前っぽい)による大量の謎テーブルとスイッチ文のラベルでまず心が折れるのだ。

ということでprintfデバッグの時間だね(ニッコリ)、つーかいちいちprintfを入れずともyaccにはYYDEBUGというデバッグ支援機能があってトレースログを出力することが可能、こいつを有効にするには

という手順が必要になる、さいわいlint(1)のMakefileには

diff --git a/usr.bin/xlint/lint1/Makefile b/usr.bin/xlint/lint1/Makefile
index b02b7ffdf8..e28721ab78 100644
--- a/usr.bin/xlint/lint1/Makefile
+++ b/usr.bin/xlint/lint1/Makefile
@@ -7,9 +7,9 @@ SRCS=           cgram.y scan.l mem1.c mem.c err.c main1.c decl.c tree.c func.c \
                init.c emit.c emit1.c inittyp.c tyname.c print.c
 MAN=           lint.7
 YHEADER=
-#DBG=-g
-#CPPFLAGS+=-DYYDEBUG=1
-#YFLAGS+=-v
+DBG=-g
+CPPFLAGS+=-DYYDEBUG=1
+YFLAGS+=-v

 CPPFLAGS+=     -I${.CURDIR} -I. -DPASS=\"${PROG}.h\"

とすでに定義がされてるので、コメント外して有効にすればいい。

それでは前回作った問題のおきないコード

typedef void *foo;
void bar(void (*bar)(void))
{
}

と問題が発生する最小ケース

typedef void *foo;
void bar(void (*foo)(void))
{
}

をそれぞれYYDEBUGを有効にしたlint(1)に喰わせてその出力の差分をみてみよう。

--- OK.txt      2019-07-25 12:46:57.000000000 +0900
+++ NG.txt      2019-07-25 12:47:13.000000000 +0900
@@ -83,23 +83,29 @@
 yydebug: state 419, shifting to state 65
 yydebug: state 65, reducing by rule 171 (asterisk : T_MULT)
 yydebug: after reduction, shifting from state 419 to state 70
-yydebug: state 70, reading 316 (T_NAME)
+yydebug: state 70, reading 317 (T_TYPENAME)
 yydebug: state 70, reducing by rule 167 (pointer : asterisk)
 yydebug: after reduction, shifting from state 419 to state 413
-yydebug: state 413, shifting to state 412
-yydebug: state 412, reducing by rule 162 (direct_notype_param_decl : T_NAME)
-yydebug: after reduction, shifting from state 413 to state 488
-yydebug: state 488, reading 262 (T_RPARN)
-yydebug: state 488, reducing by rule 161 (notype_param_decl : pointer direct_notype_param_decl)
-yydebug: after reduction, shifting from state 419 to state 496
-yydebug: state 496, shifting to state 541
-yydebug: state 541, reducing by rule 156 (direct_param_decl : T_LPARN notype_param_decl T_RPARN)
-yydebug: after reduction, shifting from state 308 to state 423
-yydebug: state 423, reading 261 (T_LPARN)
-yydebug: state 423, shifting to state 106
+unko.c(2): syntax error 'foo' [249]
+yydebug: error recovery discarding state 413
+yydebug: error recovery discarding state 419
+yydebug: error recovery discarding state 308
+yydebug: error recovery discarding state 184
+yydebug: state 111, error recovery shifting to state 177
+yydebug: state 177, error recovery discards token 317 (T_TYPENAME)
+yydebug: state 177, reading 262 (T_RPARN)
+yydebug: state 177, shifting to state 304
+yydebug: state 304, reducing by rule 183 (abs_decl_param_list : abs_decl_lparn error T_RPARN)
+yydebug: after reduction, shifting from state 85 to state 109
+yydebug: state 109, reducing by rule 176 (param_list : abs_decl_param_list)
+yydebug: after reduction, shifting from state 85 to state 129
+yydebug: state 129, reducing by rule 151 (type_direct_decl : type_direct_decl param_list)
+yydebug: after reduction, shifting from state 46 to state 85
+yydebug: state 85, reading 261 (T_LPARN)
+yydebug: state 85, shifting to state 106
 yydebug: state 106, reading 289 (T_TYPE)
 yydebug: state 106, reducing by rule 184 (abs_decl_lparn : T_LPARN)
-yydebug: after reduction, shifting from state 423 to state 111
+yydebug: after reduction, shifting from state 85 to state 111
 yydebug: state 111, reducing by rule 46 (clrtyp :)
 yydebug: after reduction, shifting from state 111 to state 51
 yydebug: state 51, shifting to state 25
@@ -120,56 +126,30 @@
 yydebug: after reduction, shifting from state 111 to state 180
 yydebug: state 180, shifting to state 305
 yydebug: state 305, reducing by rule 182 (abs_decl_param_list : abs_decl_lparn vararg_parameter_type_list T_RPARN)
-yydebug: after reduction, shifting from state 423 to state 109
-yydebug: state 109, reducing by rule 176 (param_list : abs_decl_param_list)
-yydebug: after reduction, shifting from state 423 to state 499
-yydebug: state 499, reducing by rule 159 (direct_param_decl : direct_param_decl param_list)
-yydebug: after reduction, shifting from state 308 to state 423
-yydebug: state 423, reading 262 (T_RPARN)
-yydebug: state 423, reducing by rule 153 (param_decl : direct_param_decl)
-yydebug: after reduction, shifting from state 308 to state 422
-yydebug: state 422, reducing by rule 193 (parameter_declaration : declspecs deftyp param_decl)
-yydebug: after reduction, shifting from state 111 to state 182
-yydebug: state 182, reducing by rule 188 (parameter_type_list : parameter_declaration)
-yydebug: after reduction, shifting from state 111 to state 181
-yydebug: state 181, reducing by rule 185 (vararg_parameter_type_list : parameter_type_list)
-yydebug: after reduction, shifting from state 111 to state 180
-yydebug: state 180, shifting to state 305
-yydebug: state 305, reducing by rule 182 (abs_decl_param_list : abs_decl_lparn vararg_parameter_type_list T_RPARN)
 yydebug: after reduction, shifting from state 85 to state 109
 yydebug: state 109, reducing by rule 176 (param_list : abs_decl_param_list)
 yydebug: after reduction, shifting from state 85 to state 129
 yydebug: state 129, reducing by rule 151 (type_direct_decl : type_direct_decl param_list)
 yydebug: after reduction, shifting from state 46 to state 85
-yydebug: state 85, reading 257 (T_LBRACE)
+yydebug: state 85, reading 262 (T_RPARN)
 yydebug: state 85, reducing by rule 145 (type_decl : type_direct_decl)
 yydebug: after reduction, shifting from state 46 to state 83
-yydebug: state 83, reducing by rule 21 (func_decl : declspecs deftyp type_decl)
-yydebug: after reduction, shifting from state 8 to state 6
-yydebug: state 6, reducing by rule 16 ($$1 :)
-yydebug: after reduction, shifting from state 6 to state 22
-yydebug: state 22, reducing by rule 22 (opt_arg_declaration_list :)
-yydebug: after reduction, shifting from state 22 to state 54
-yydebug: state 54, reducing by rule 17 ($$2 :)
-yydebug: after reduction, shifting from state 54 to state 95
-yydebug: state 95, shifting to state 140
-yydebug: state 140, reducing by rule 251 (comp_stmnt_lbrace : T_LBRACE)
-yydebug: after reduction, shifting from state 95 to state 142
-yydebug: state 142, reading 258 (T_RBRACE)
-yydebug: state 142, shifting to state 210
-yydebug: state 210, reducing by rule 252 (comp_stmnt_rbrace : T_RBRACE)
-yydebug: after reduction, shifting from state 142 to state 240
-yydebug: state 240, reducing by rule 247 (comp_stmnt : comp_stmnt_lbrace comp_stmnt_rbrace)
-yydebug: after reduction, shifting from state 95 to state 141
-yydebug: state 141, reducing by rule 18 (func_def : func_decl $$1 opt_arg_declaration_list $$2 comp_stmnt)
-unko.c(2): warning: argument bar unused in function bar [231]
-yydebug: after reduction, shifting from state 8 to state 11
-yydebug: state 11, reducing by rule 6 (ext_decl : func_def)
+unko.c(2): syntax error ')' [249]
+yydebug: error recovery discarding state 83
+yydebug: error recovery discarding state 46
+yydebug: error recovery discarding state 15
+yydebug: state 8, error recovery shifting to state 1
+yydebug: state 1, error recovery discards token 262 (T_RPARN)
+yydebug: state 1, reading 257 (T_LBRACE)
+yydebug: state 1, error recovery discards token 257 (T_LBRACE)
+yydebug: state 1, reading 258 (T_RBRACE)
+yydebug: state 1, shifting to state 16
+yydebug: state 16, reducing by rule 15 (data_def : error T_RBRACE)
+yydebug: after reduction, shifting from state 8 to state 12
+yydebug: state 12, reducing by rule 7 (ext_decl : data_def)
 yydebug: after reduction, shifting from state 8 to state 23
 yydebug: state 23, reducing by rule 4 (translation_unit : translation_unit ext_decl)
 yydebug: after reduction, shifting from state 0 to state 8
 yydebug: state 8, reading 0 (end-of-file)
 yydebug: state 8, reducing by rule 2 (program : translation_unit)
 yydebug: after reduction, shifting from state 0 to state 5
-Lint pass2:
-bar defined( unko.c(2) ), but never used

この出力をパッと見て気づくのは

-yydebug: state 70, reading 316 (T_NAME)
+yydebug: state 70, reading 317 (T_TYPENAME)

がトリガーとなって後の構文解析エラーが起きてるってことで、これは その3で字句解析側のコード監査で登場したname関数

406 static int
407 name(void)
408 {
409         char    *s;
410         sbuf_t  *sb;
411         sym_t   *sym;
412         int     tok;
413
414         sb = allocsb();
415         sb->sb_name = yytext;
416         sb->sb_len = yyleng;
417         sb->sb_hash = hash(yytext);
418         if ((sym = search(sb)) != NULL && sym->s_keyw) {
419                 freesb(sb);
420                 return (keyw(sym));
421         }
422
423         sb->sb_sym = sym;
424
425         if (sym != NULL) {
426                 if (blklev < sym->s_blklev)
427                         LERROR("name()");
428                 sb->sb_name = sym->s_name;
429                 sb->sb_len = strlen(sym->s_name);
430                 tok = sym->s_scl == TYPEDEF ? T_TYPENAME : T_NAME;
431         } else {
432                 s = getblk(yyleng + 1);
433                 (void)memcpy(s, yytext, yyleng + 1);
434                 sb->sb_name = s;
435                 sb->sb_len = yyleng;
436                 tok = T_NAME;
437         }
438
439         yylval.y_sb = sb;
440         return (tok);
441 }

の中で

って処理が問題なのだという事が証明されたやね、そもそも関数の引数名にまでシンボルテーブルを検索してtypedefと置換する必要なんて無いわけでな。

ということでトークンが変数名ならこのシンボルテーブルの検索をスキップすりゃよさそうなんだが、このトークンは変数名って情報がどこにも無さそうでなぁ…

あと完全にスキップしてしまうとシンボルテーブルにtypedefだけでなくグローバル変数なんかもあったりすると、グローバル変数名をローカル変数名が隠してしまういわゆるシャドウパラメータの検知ができなくなったりしないかなぁという心配があるのでそこの検証もだな。

@次回

つーことでsym_tとsymtabの実装をもうちょい読み進めましょうかね…ハァ…

2019/07/26(Fri)

[オレオレN6] debugging lint(1) その5

ふーむ、どうもN8のlint(1)のソースの変更履歴ながめとったら このコミットで問題のopensslとzlibのconflictする定義を切り出したテストコードが放り込まれてるのに気づいた、そんで このコミットでこのfalse alarm問題は修正したぜということらしい。

後者のコミットの差分をN6でビルド通るように%expectの値 *1を修正したものがこちら。

diff --git a/usr.bin/xlint/lint1/cgram.y b/usr.bin/xlint/lint1/cgram.y
index 646e0a21bd..74f149e213 100644
--- a/usr.bin/xlint/lint1/cgram.y
+++ b/usr.bin/xlint/lint1/cgram.y
@@ -107,7 +107,7 @@ static inline void RESTORE(const char *file, size_t line)
 #endif
 %}

-%expect 5
+%expect 8

 %union {
        int     y_int;
@@ -982,7 +982,7 @@ notype_param_decl:
        ;

 direct_notype_param_decl:
-         T_NAME {
+         identifier {
                $$ = dname(getsym($1));
          }
        | T_LPARN notype_param_decl T_RPARN {

引数名について字句解析がT_NAMEを返してきた場合だけ構文解析にマッチするようにしてたのを

identifier:
          T_NAME {
                $$ = $1;
          }
        | T_TYPENAME {
                $$ = $1;
          }
        ;

と、T_NAMEだけでなくT_TYPENAMEどちらでもマッチするようにしたってこと。

とりあえずこの変更を取り込めば現在発生するfalse alarmは出なくなるのだけど、本質的にこのパッチは対症療法でしかないよね… *2

というのも本来はtypedefの影響を受けないはずの変数名がname()関数内で無理矢理痴漢されてT_TYPENAMEとして扱われてるって根本的なところが直ってないのだ。 例えば以下のコード

typedef int foo;
int bar(int foo)
{
}

に対してlint(1)を実行すると

$ lint -z /home/tnozaki/unko.c
unko.c:
unko.c(2): warning: argument foo unused in function bar [231]
Lint pass2:
bar defined( unko.c(2) ), but never used

とシンタックスエラーは出ないけど、YYDEBUGを有効にしてトレースを確認すると

yydebug: state 0, reading 288 (T_SCLASS) ← typedef
...
yydebug: state 14, reading 289 (T_TYPE) ← int
...
yydebug: state 15, reading 316 (T_NAME) ← foo
...
yydebug: state 8, reading 289 (T_TYPE) ← int
...
yydebug: state 15, reading 316 (T_NAME) ← bar
...
yydebug: state 106, reading 289 (T_TYPE) ← int
...
yydebug: state 184, reading 317 (T_TYPENAME) ← foo
...

という具合で、barの引数名であるfooがT_NAMEではなくT_TYPENAMEに化けてることがわかる。 まぁ化けてても現状では文法チェックに影響を与えてはいないのだが潜在バグだからねこれ…

ただtypedefへの置換をname()関数で行う限り、現在処理しているトークンが予約語なのか型名なのかシンボル名なのか変数名なのかはわからない。 どうあがいても構文解析側でしかわからんものを字句解析側でやろうとすること自体が頭おかしいコードなので、このへんでお茶濁すのもまぁ手かもしれない。

正しい修正についてはまた次回を待て、いや次回で直ってる気がしないんだが…

*1:シフト還元衝突の警告を抑制する魔法、yacc実行した時に「n shift/reduce conflicts」といわれた数字を入れとけばだいたいおk
*2:まぁcommitした人の名前をみればまたお前か案件なので以下略

2019/07/27(Sat)

Tycho/Fuji Rock 2019

さすがにTychoがTwitterトレンドとiTunes Storeのトップになっとるのは目を疑ったけど、初の日本単独公演の頃から妙に客層幅広いのが印象に残るバンドだったので、耳に届きさえすればの実力ですわね、いいぞもっと売れて老害ロックを駆逐しよう。

配信があったのありがたい、音小さいしエンコ重くてプチプチ切れるとか不満はあったけどね。

セトリは新作と前作が多め、やっぱフジロック(笑)だから時間短くて曲数がこれから盛り上がるのに~ってとこですな。

  1. A Walk (from "Dive")
  2. L (from "Awake")
  3. Weather (from "Weather")
  4. (M.C.)
  5. Japan (from "Weather")
  6. Easy (from "Weather")
  7. For How Long (from "Weather")
  8. Pink & Blue (from "Weaher")
  9. Awake (from "Awake")
  10. (M.C.)
  11. Horizon (from "Epoch")
  12. Epoch (from "Epoch")
  13. Division (from "Epoch")
  14. (M.C.)
  15. No Stress (from "Weather")

なのでBEATNIKがアルバム発売にあわせて単独公演ブックせずにこのままフジロックだけでお茶濁そうって気なら、これまでのPLANCHAに権利返してやってくれ感あるよな、あそこは普通の神経ならとばす名古屋入れるくらい本気だった。 なおPLANCHAは今日はいちファンとして客席でキャッキャしてオタク特有の早口のツイートしておった、さすがやな。

あと前ステージがTycho好きならToro y Moiも好きでしょといういかにもフジロック(笑)の音楽聴かずに文字情報だけでブックしてる感ほんときらい、なぜいっとき同じチルウェイブというジャンル扱いされたのか、ほんと理解に苦しむんだけどあれ…

2019/07/28(Sun)

[オレオレN6] まだOpenSSLいじってる

ローカルパッチの監査は終ったしlint(1)バグの緩和策もあったのでいよいよマージかけようかと思ったんだけど、Makefileから渡してるCPPFLAGSなんかもすげー怪しいのでこっちも監査せんとダメだわ。

つーかlibcrypto/(arch/*/)?*.incによるビルドシステムは破綻しとるのよね、元はというと

というコンセプトではじまったっぽいけど、そのperl scriptとやらがアレだったのかCVSから消され、その後はOpenSSLのバージョンアップの度に手作業で変更してるという賽の河原なのである、そもそも元のOpenSSLのビルドシステム自体が無茶苦茶だしそれに追従しようとしたら破綻するよなそら。

本当はOpenBSDみたいなMakefile wrapperで極力手間減らすビルドシステムがいいんだろうけど、Nはクロスビルド必須の上にベースシステムにperlが無いからまぁ難しい。

おかげで普通にtarball展開してビルドする時に定義されるCPPFLAGSがx86_64だけでもこれだけ漏れてるようでアレ。

ざっと見た限りでは

という感じ、こいつらも対応せんと性能的にpkgsrc使った方が高速みたいな本末転倒なことになってそうやな…

あとPA-RISCとMIPSでも高速化のためのアセンブラコードを吐くperl scriptがあるんだがNでは使われてない、ただまぁ

実機も無エ
需要も無エ
まったぐユーザー俺一人
エミュレーターあるけれど
動かし方を読む気が無エ
婆さんと爺さんはボケて毎日ぐーるぐる

なので対応する気はないです。

2019/07/29(Mon)

[オレオレN6] まだまだOpenSSLいじってる

とりあえずECP_NISTZ256は1.0.2kではコードは存在するけどデフォルトではビルドされてないのでいいのか、つーかx86_64以外はそもビルドできないっぽ。

あと過去記事にもあるとおりSHA256/512実装がOpenSSLのものでなくlibcにあるものが使われるように改造されとるんだが、そのせいでe_aes_cbc_hmac_sha256.cが-DAES_ASMすると無条件で-DSHA256_ASM前提になるのでビルドエラーになる。 これはOpenSSL側のifdefが悪いので対応をブチ込んでおく。

diff --git a/crypto/external/bsd/openssl/dist/crypto/evp/e_aes_cbc_hmac_sha256.c b/crypto/external/bsd/openssl/dist/crypto/evp/e_aes_cbc_hmac_sha256.c
index 46c9d03389..3548c63a10 100644
--- a/crypto/external/bsd/openssl/dist/crypto/evp/e_aes_cbc_hmac_sha256.c
+++ b/crypto/external/bsd/openssl/dist/crypto/evp/e_aes_cbc_hmac_sha256.c
@@ -90,7 +90,7 @@ typedef struct {

 # define NO_PAYLOAD_LENGTH       ((size_t)-1)

-# if     defined(AES_ASM) &&     ( \
+# if     defined(AES_ASM) && defined(SHA256_ASM) &&     ( \
         defined(__x86_64)       || defined(__x86_64__)  || \
         defined(_M_AMD64)       || defined(_M_X64)      || \
         defined(__INTEL__)      )

あとi386 portでアセンブラコードが軒並み無効になってるのはこれOpenSSLのx86 ASMがi586を最低限の動作環境としとるのに対し、Nはi486だからなんですかね。 それにしてはAESだけは586コード有効になっとるけど、AESだけはAESNIの絡みでCPUIDの結果で動作切替わるようになってたりするんだろうかね、よくわからん。

つーかi386を名乗ってるのにもうi386では動かんしi486も実機なぞ残ってないだろうからグッバイでええやろ…ということでユーザー1名のオレオレN6では全部有効にしておいた。 ただopenssl speedの結果を眺めるに性能は微妙かなぁという気がしないでもない。

んで他のCPUのベンチにQEMU環境を作ろうとしたらTERM無関係に矢印キー効かずsysinst途中から進められなくなるのでクソ、もうsysinst使わずにdisklanbel他でインストールする方法を思い出すほどの気力はねえですわ。