被php gnupg扩展的segfault错误折腾到半夜。
Contents
问题描述
开发机上明明可以正常运行的脚本部署到了线上机器就不能用了。而且还有一个奇怪的现象,命令行方式执行完全没有问题,可是一旦通过web方式访问,就段错误,浏览器提示服务器未返回数据:
$ curl -svv -d "id=52545" http://test.com.cn/tools/d * Adding handle: conn: 0x2a05f90 .... * Connected to test.com.cn (10.0.0.2) port 80 (#0) > POST /imtools/d HTTP/1.1 > User-Agent: curl/7.33.0 > Host: test.com.cn:80 > Accept: */* > Content-Length: 8 > Content-Type: application/x-www-form-urlencoded > } [data not shown] * upload completely sent off: 8 out of 8 bytes * Empty reply from server * Connection #0 to host test.com.cn left intact
服务器端日志显示Segmentation fault:
[Sun Apr 19 03:40:51 2015] [notice] child pid 2661 exit signal Segmentation fault (11) [Sun Apr 19 03:40:52 2015] [notice] child pid 2609 exit signal Segmentation fault (11) [Sun Apr 19 03:40:52 2015] [notice] child pid 2655 exit signal Segmentation fault (11) [Sun Apr 19 03:40:52 2015] [notice] child pid 2664 exit signal Segmentation fault (11) [Sun Apr 19 03:40:52 2015] [notice] child pid 2665 exit signal Segmentation fault (11)
排查经过
通过注释代码段倒也很快知道是gnupg扩展未能正常执行,执行gnupg_import()导入公钥时就segfault。
开发机上测试的时候曾经试过导入非法公钥文件,按照php官方文档,导入公钥失败会返回FALSE[1],但我这里重来都是直接段错误。于是判断是公钥文件的格式出了问题,怀疑是因为开发机和线上机器环境不同导致的。开发机是Centos 6.5, Nginx+PHP,线上机器是Centos 5.4,Apache+PHP。于是将公钥下载方式改为获取json格式的数据,但还是段错误。(后来想想公钥下载本来就只返回string类型,没必要转json)。然后又怀疑是gpgme版本问题,卸载了又重装了低版本的,重新编译gnupg,依然是段错误。
要抓狂了,感觉人在烦躁状态下智商都会下降,尽瞎猜,都忘了百度google了。google搜 “gnupg segfault” ,好多类似的。。gnupg web访问时会segfault。
oh well - my fault - as soon as I did
chmod -R 777
on my GNUPGHOME - it all workedthought it was happy with 666 (and CLI was happy with just rw) [2]
[2005-11-25 11:25 UTC] karl at karlaustin dot com
Just tried the new gnupg.c and that stops the segfaults, but it still doesn't actually make it work. It looks like the problem was that it was not looking in the default GPGHOME for the httpd user i.e. ~/.gnupg as if I use putenv() to set the GPGHOME it does now work.Thanks, [3]
第一个是stackoverflow,不得不说这个网站以及Google对技术人员很有用,很多疑难杂症百度搜不到国内网站也没有的这里都可以找到——虽然后来验证stackoverflow那个也不完全正确,权限不需要设置为777,但至少提供了思路。
排查线上机器,果然是因为www(apache用www用户运行)用户目录下没有.gnupg目录。问题清晰了,之所以可以命令行方式正常执行时因为命令行方式用的root用户执行的,而/root/.gnupg目录是正常的。至于为什么线上机器不能自动创建 .gnupg目录,大半夜的也不想探究了。直接切到其他用户,执行gpg,生成此目录,在复制到www家目录,改下属主。之后web方式访问,依然段错误,最后指定GNUPGHOME,终于正常了。
putenv("GNUPGHOME=/home/www/.gnupg/"); $res = gnupg_init(); $info = gnupg_import($res, "$keydata");
关于.gnupg目录
复制一些gpg man 关于 .gnupg目录的内容
--trustdb-name file Use file instead of the default trustdb. If file begins with a tilde and a slash, these are replaced by the $HOME directory. If the filename does not contain a slash, it is assumed to be in the GnuPG home directory (‘~/.gnupg’ if --homedir or $GNUPGHOME is not used). --homedir dir Set the name of the home directory to dir. If this option is not used, the home directory defaults to ‘~/.gnupg’. It is only recognized when given on the command line. It also overrides any home directory stated through the environment variable ‘GNUPGHOME’ or (on W32 systems) by means of the Registry entry HKCU\Software\GNU\GnuPG:HomeDir. ~/.gnupg/secring.gpg The secret keyring. You should backup this file. ~/.gnupg/secring.gpg.lock The lock file for the secret keyring. ~/.gnupg/pubring.gpg The public keyring. You should backup this file. ~/.gnupg/pubring.gpg.lock The lock file for the public keyring. ~/.gnupg/trustdb.gpg The trust database. There is no need to backup this file; it is better to backup the ownertrust values (see: [option --export-ownertrust]). ~/.gnupg/trustdb.gpg.lock The lock file for the trust database. ~/.gnupg/random_seed A file used to preserve the state of the internal random pool. GNUPGHOME If set directory used instead of "~/.gnupg".
参考资料
1. PHP手册 http://php.net/manual/zh/function.gnupg-import.php 2. PHP GnuPG segfaults in a webserver http://stackoverflow.com/questions/14323334/php-gnupg-segfaults-in-a-webserver 3. GNUPG Operations cause Apache to segfault https://bugs.php.net/bug.php?id=56674
发表回复