【问题标题】:M2Crypto BIO.readlines hangs, python 2.7M2Crypto BIO.readlines 挂起,python 2.7
【发布时间】:2012-03-06 00:53:33
【问题描述】:

我们正在将基于 C++ openssl 的项目转换为带有 M2Crypto 的 python,并且在使用 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() 系统的理解是否有误。

--- 详情 --- Pythong 2.7 M2加密 0.21.1 OpenSSL 0.9.8q-fips 2010 年 12 月 2 日 FreeBSD 8.2-RELEASE-p4

【问题讨论】:

    标签: python swig m2crypto


    【解决方案1】:

    这个问题与 Debian Linux 中的 bug #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
    

    【讨论】:

    • 好吧,我明白为什么在 Fedora 中该错误无法重现。存在一个补丁“m2crypto-0.21.1-timeouts.patch”,它只是重写了 Connection.py 中的函数“makefile”。在这个新函数中,来自 python socket.py 的经典 _fileobject 类被调用。这有点奇怪,因为它会导致避免 BIO.py 中定义的所有机制。意味着 open_https 返回的对象不再是 BIO 对象......因此我认为 m2crypto 的部分好处丢失了......
    • 是的,它更像是一个评论而不是一个答案。
    【解决方案2】:

    如果将来其他人偶然发现此问题,我想分享我们的补丁。

    --- 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 或 len(buf) == 0,并将两者都视为流结束。

    第三种解决方案是简单地避免调用 BIO.readlines(),并限制自己调用 BIO.readline()(注意 -- singluar readline vs readlines),并测试 BIO.readline() 的返回值无或 len(buf) == 0。

    第三种解决方案似乎不是一个很好的选择——更像是回避。但是,如果您担心将应用程序部署到可能无法修补 M2Crypto 的环境中,那么这种方法保证是最兼容的。

    我们已将补丁提交给唱片的开发者 Heikki,但他还没有机会查看我们的建议。在以一种或另一种方式给出官方答案之前,我想分享我们的想法。

    【讨论】:

    • 顺便说一句——我不认识开发者 Heikki……但他的项目 M2Crypto 绝对很棒。它不仅是 Python/OpenSSL 包装器的特殊实现,而且是创建 SWIG 代码的正确方法的一个示例——干净、简单、彻底等等……干得好,先生。
    猜你喜欢
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 2012-01-19
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多