M2Crypto BIO.readlines 挂起, python 2.7

0 投票
2 回答
560 浏览
提问于 2025-04-17 13:01

我们正在把一个基于C++和OpenSSL的项目转换成使用Python和M2Crypto的版本,但在使用M2Crypto的BIO功能时遇到了一个比较奇怪的问题。具体来说,调用BIO.readlines()时会一直卡住,无法继续。

我们尝试了一个简单的例子:

f = open('test.txt','w')
f.write('hello world\n')
f.close()

import M2Crypto.BIO
bio = M2Crypto.BIO.openfile('test.txt','r')
lines = bio.readlines()
# the above call hangs forever

为了确认我们的OpenSSL安装没有什么大问题,我们写了一个小测试程序来读取我们刚创建的test.txt文件。

#include <openssl/bio.h>
#include <openssl/err.h>
int main() {
    const int maxrd = 4096;
    char line[maxrd];
    int rd;
    BIO* bio = BIO_new_file("test.txt","r");
    while((rd = BIO_gets(bio, line, maxrd)) > 0) {
        printf("%s",line);
        }
    if (rd == -1) {
        printf("BIO error %ld\n", ERR_get_error());
        }
    }

结果没有问题。

我们研究了M2Crypto-0.21.1/SWIG/_bio.i这个包装文件,觉得可能找到了问题的根源。第109行测试了BIO_gets()的返回值。

if (r < 0) {
    // return Py_None
    }

但是,BIO_gets()的手册上说它可能返回0或-1来表示流的结束。

我认为应该是这样:

if (r < 1) {
    // return Py_None
    }

但我想看看其他人是否也遇到过这个问题,或者我们对BIO_gets()的理解是否有误。

--- 详细信息 --- Python 2.7 M2Crypto 0.21.1 OpenSSL 0.9.8q-fips 2010年12月2日 FreeBSD 8.2-RELEASE-p4

2 个回答

0

这个问题和Debian Linux中的一个错误有关,具体是错误 #717675

在Fedora 21上这个问题无法重现,而且我没有找到任何会修改BIO.py_bio.i的Fedora补丁。

下面是为Debian发布的补丁:

--- /usr/lib64/python2.7/site-packages/M2Crypto/BIO.py  2011-01-15 20:10:05.000000000 +0100
+++ BIO.py  2015-05-20 09:24:46.600582999 +0200
@@ -73,6 +73,8 @@
             buf=m2.bio_gets(self.bio, 4096)
             if buf is None:
                 break
+       if len(buf)==0:
+       break
             lines.append(buf)
         return lines
0

如果将来有人遇到这个问题,我想分享一下我们的修复方案。

--- M2Crypto-0.21.1.orig/SWIG/_bio.i  2011-01-15 14:10:06.000000000 -0500
+++ M2Crypto-0.21.1/SWIG/_bio.i   2012-02-14 11:34:15.000000000 -0500
@@ -106,7 +106,7 @@
     Py_BEGIN_ALLOW_THREADS
     r = BIO_gets(bio, buf, num);
     Py_END_ALLOW_THREADS
-    if (r < 0) {
+    if (r < 1) {
         PyMem_Free(buf);
         if (ERR_peek_error()) {
             PyErr_SetString(_bio_err, ERR_reason_error_string(ERR_get_error()));

注意:对于那些了解M2Crypto内部工作原理的人来说,这个问题有三种解决方案。第一种就是上面提到的修复方案。我们认为这个方案符合BIO_gets()的手册说明,所以我们选择了这个方案。

第二种解决方案是修复M2Crypto/BIO.py。具体来说,就是修改实现BIO.readlines()的代码,检查m2.bio.gets()的返回值,如果是None或者缓冲区长度为0,就把这两种情况都当作流的结束。

第三种解决方案则是直接不调用BIO.readlines(),而只调用BIO.readline()(注意,单数的readline和复数的readlines是有区别的),并检查BIO.readline()的返回值是否为None或者缓冲区长度为0。

第三种方案看起来可能不太像是一个真正的解决办法,更像是一种规避。但如果你担心在一个可能没有修复M2Crypto的环境中部署应用程序,这种方法绝对是最兼容的。

我们已经把我们的修复方案提交给了负责的开发者Heikki,但他还没有机会审查我们的建议。在官方给出明确答复之前,我想先分享一下我们的想法。

撰写回答