[python-daemon-Bugs][315147] Bad file descriptor using tempfile/os.urandom on Python 3.4

python-daemon-bugs at alioth.debian.org python-daemon-bugs at alioth.debian.org
Sat Aug 22 05:05:47 UTC 2015


python-daemon-Bugs item #315147 was changed at 2015-08-22 05:05 by Torfus Polymorphus
You can respond by visiting: 
https://alioth.debian.org/tracker/?func=detail&atid=413098&aid=315147&group_id=100328

Status: Open
Priority: 3
Submitted By: Torfus Polymorphus (torf-guest)
Assigned to: Nobody (None)
Summary: Bad file descriptor using tempfile/os.urandom on Python 3.4 


Initial Comment:
I'm trying to use Python's tempfile module inside DaemonContext. That works
fine in Python 2.7, however on Python 3.4 I'm running into problems related
to os.urandom.

Code to reproduce:

    #!/usr/bin/env python

    import sys
    import tempfile
    import traceback

    from daemon import DaemonContext

    with DaemonContext(stderr=sys.stderr):
        try:
            tempfile.TemporaryFile()
        except Exception as e:
            traceback.print_exc()

On Python 3.4, this prints the following traceback for me:

    Traceback (most recent call last):
      File "./sandbox.py", line 13, in <module>
        tempfile.TemporaryFile()
      File "/usr/lib/python3.4/tempfile.py", line 632, in TemporaryFile
        dir = gettempdir()
      File "/usr/lib/python3.4/tempfile.py", line 370, in gettempdir
        tempdir = _get_default_tempdir()
      File "/usr/lib/python3.4/tempfile.py", line 293, in _get_default_tempdir
        name = next(namer)
      File "/usr/lib/python3.4/tempfile.py", line 247, in __next__
        choose = self.rng.choice
      File "/usr/lib/python3.4/tempfile.py", line 238, in rng
        self._rng = _Random()
      File "/usr/lib/python3.4/random.py", line 90, in __init__
        self.seed(x)
      File "/usr/lib/python3.4/random.py", line 108, in seed
        a = int.from_bytes(_urandom(32), 'big')
    OSError: [Errno 9] Bad file descriptor

On Python 2.7 the code works without problems.

The problem seems to be that importing tempfile somehow automatically opens a
file descriptor that is used by os.urandom (the _urandom from the traceback
is just os.urandom). When the daemon context is entered, that file descriptor
goes bad. This can be seen by listing the open file descriptors before and
after importing tempfile, for example using the following code:

    #!/usr/bin/env python

    import os
    import os.path

    def print_open_fds():
        for root, dirnames, filenames in os.walk('/proc/self/fd'):
            for filename in filenames:
                fullname = os.path.join(root, filename)
                if not os.path.exists(fullname):
                    continue
                print(os.readlink(fullname))

    print("Open fds before importing tempfile:")
    print_open_fds()

    import tempfile

    print("Open fds before after tempfile:")
    print_open_fds()

On Python 3.4 this shows that a file descriptor for /dev/urandom is opened
when tempfile is imported (on Python 2.7 this does not happen). The same
behavior on both 3.4 and 2.7 can be seen if you replace "import tempfile"
in the code above with "os.urandom(32)". The problem also does not occur
if tempfile is only imported after the daemon context has been entered.

According to Python issue 18756 (https://bugs.python.org/issue18756) the
behavior was introduced on purpose to avoid performance problems with the
old code (which openend and closed the file descriptor for every call to
os.urandom). That change was also back-ported to Python 2.7, however it seems
that it's not in Python 2.7.6 (that's the 2.7 version I'm running).

The problem is that that file descriptor is neither documented nor publicly
accessible, as far as I can see. This means that it's hard to tell
DaemonContext to keep the file descriptor open via files_preserve. One
possibility is to manually find the file descriptor:

    import os
    import os.path
    import sys
    import tempfile
    import traceback

    from daemon import DaemonContext

    def get_fd(path):
        """
        Get an already open file descriptor for a file.
        """
        path = os.path.realpath(path)
        for root, dirnames, filenames in os.walk('/proc/self/fd'):
            for filename in filenames:
                fullname = os.path.join(root, filename)
                if not os.path.exists(fullname):
                    continue
                if os.path.realpath(fullname) == path:
                    return int(filename)
        raise RuntimeError('No open file descriptor for %r.' % path)

    fds = []
    try:
        fds.append(get_fd('/dev/urandom'))
    except RuntimeError:
        # No file descriptor for /dev/urandom, probably Python 2.7
        pass

    with DaemonContext(stderr=sys.stderr, files_preserve=fds):
        try:
            tempfile.TemporaryFile()
        except Exception as e:
            traceback.print_exc()

That works, but feels hackish.

I'd like to hear your opinion on this issue. I don't really think that this
is a problem with python-daemon, but it's an issue that users of
python-daemon are quite likely to encounter when they are working with
temporary files.


----------------------------------------------------------------------

Comment By: Torfus Polymorphus (torf-guest)
Date: 2015-08-22 05:05

Message:
It seems that others have had similar problems, cf. https://github.com/Pylons/pyramid/issues/1554. That thread mentions that this issue should be fixed in newer versions of Python 3.4 and in the upcoming Python 3.5, cf. Python issue 21207 (https://bugs.python.org/issue21207).

----------------------------------------------------------------------

Comment By: Torfus Polymorphus (torf-guest)
Date: 2015-08-22 04:55

Message:
Argh, is there any way to keep code formatted in a bug report?

----------------------------------------------------------------------

You can respond by visiting: 
https://alioth.debian.org/tracker/?func=detail&atid=413098&aid=315147&group_id=100328



More information about the python-daemon-bugs mailing list