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

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のデュアルライセンスね