This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author amoffat
Recipients amoffat, loewis, ned.deily
Date 2012-09-11.04:19:28
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1347337170.8.0.208818111589.issue15898@psf.upfronthosting.co.za>
In-reply-to
Content
Below I attached a script that reproduces the behavior of an assertion error on a specific fork condition for a process with a pty.  Here are the results

Xubuntu x64:
   Python 2.7, parent_close_slave_before_child_write = False: WORKS
   Python 2.7, parent_close_slave_before_child_write = True: WORKS
   Python 3.2, parent_close_slave_before_child_write = False: WORKS
   Python 3.2, parent_close_slave_before_child_write = True: WORKS
Mac OSX 10.7.2:
   Python 2.7, parent_close_slave_before_child_write = False: WORKS
   Python 2.7, parent_close_slave_before_child_write = True: WORKS
   Python 3.2, parent_close_slave_before_child_write = False: >> FAILS <<
   Python 3.2, parent_close_slave_before_child_write = True: WORKS



Here's dtruss -f for python 2.7 and 3.0 on Mac OSX:

2.7, parent_close_slave_before_child_write = False (WORKS)

    PID/THRD  SYSCALL(args)          = return
  356/0xc4a:  open_nocancel("/dev/ptmx\0", 0x20002, 0x0)         = 3 0
  356/0xc4a:  ioctl(0x3, 0x20007454, 0xFFFFFFFF)         = 0 0
  356/0xc4a:  ioctl(0x3, 0x20007452, 0x0)        = 0 0
  356/0xc4a:  ioctl(0x3, 0x40807453, 0x10041AD20)        = 0 0
  356/0xc4a:  stat64("/dev/ttys003\0", 0x7FFF5FBFF030, 0x0)      = 0 0
  356/0xc4a:  open_nocancel("/dev/ttys003\0", 0x20002, 0x0)      = 4 0
  356/0xc4a:  fork()         = 362 0
  362/0xc61:  fork()         = 0 0
  362/0xc61:  thread_selfid(0x100420BE0, 0x3, 0x1)       = 3169 0
  362/0xc61:  getpid(0x100420BE0, 0x3, 0x0)      = 362 0
  362/0xc61:  select(0x0, 0x0, 0x0, 0x0, 0x7FFF5FBFF3C0)         = 0 0
  362/0xc61:  setsid(0x0, 0x0, 0x1001A7B40)      = 362 0
  362/0xc61:  close(0x3)         = 0 0
  362/0xc61:  dup2(0x4, 0x0, 0x0)        = 0 0
  362/0xc61:  dup2(0x4, 0x1, 0x0)        = 1 0
  362/0xc61:  dup2(0x4, 0x2, 0x0)        = 2 0
  362/0xc61:  getrlimit(0x1008, 0x7FFF5FBFF3D0, 0x0)         = 0 0

  ... close range ...

  362/0xc61:  ioctl(0x1, 0x40487413, 0x7FFF5FBFF360)         = 0 0
  362/0xc61:  fstat64(0x1, 0x7FFF5FBFF2D0, 0x0)      = 0 0
  362/0xc61:  open_nocancel("/dev/\0", 0x100004, 0x0)        = 3 0
  362/0xc61:  fcntl_nocancel(0x3, 0x2, 0x1)      = 0 0
  362/0xc61:  fstatfs64(0x3, 0x7FFF5FBFE7B0, 0x0)        = 0 0
  362/0xc61:  getdirentries64(0x3, 0x1010AB600, 0x1000)      = 3080 0

  ... lots of lstat64 ...

  362/0xc61:  close_nocancel(0x3)        = 0 0
  362/0xc61:  open("/dev/ttys003\0", 0x2, 0x1FF)         = 3 0
  362/0xc61:  close(0x3)         = 0 0
  362/0xc61:  write(0x1, "testing\0", 0x7)       = 7 0
  362/0xc61:  close(0x1)         = 0 0
  356/0xc4a:  select(0x0, 0x0, 0x0, 0x0, 0x7FFF5FBFF3C0)         = 0 0
  356/0xc4a:  close(0x4)         = 0 0
  356/0xc4a:  read(0x3, "testing\0", 0x400)      = 7 0
  356/0xc4a:  write_nocancel(0x1, "'testing'\n\0", 0xA)      = 10 0
  356/0xc4a:  wait4(0x16A, 0x7FFF5FBFF3D4, 0x0)      = 362 0
  356/0xc4a:  sigaction(0x2, 0x7FFF5FBFF800, 0x7FFF5FBFF830)         = 0 0


3.0, parent_close_slave_before_child_write = False (FAILS)

    PID/THRD  SYSCALL(args)          = return
  385/0xe53:  open_nocancel("/dev/ptmx\0", 0x20002, 0xD15EB8)        = 3 0
  385/0xe53:  ioctl(0x3, 0x20007454, 0xBFFFF1BC)         = 0 0
  385/0xe53:  ioctl(0x3, 0x20007452, 0xBFFFF1BC)         = 0 0
  385/0xe53:  ioctl(0x3, 0x40807453, 0x64F220)       = 0 0
  385/0xe53:  stat64("/dev/ttys003\0", 0xBFFFF164, 0x64F220)         = 0 0
  385/0xe53:  open_nocancel("/dev/ttys003\0", 0x20002, 0x0)      = 4 0
  385/0xe53:  fork()         = 389 0
  389/0xe5c:  fork()         = 0 0
  389/0xe5c:  thread_selfid(0x0, 0x0, 0x0)       = 3676 0
  389/0xe5c:  getpid(0x0, 0x0, 0x0)      = 389 0
  389/0xe5c:  select_nocancel(0x0, 0x0, 0x0)         = 0 0
  389/0xe5c:  setsid(0x0, 0x0, 0x0)      = 389 0
  389/0xe5c:  close_nocancel(0x3)        = 0 0
  389/0xe5c:  dup2(0x4, 0x0, 0x0)        = 0 0
  389/0xe5c:  dup2(0x4, 0x1, 0x0)        = 1 0
  389/0xe5c:  dup2(0x4, 0x2, 0x0)        = 2 0
  389/0xe5c:  getrlimit(0x8, 0xBFFFF46C, 0x0)        = 0 0

  ... close range ...

  389/0xe5c:  ioctl(0x1, 0x402C7413, 0xBFFFF410)         = 0 0
  389/0xe5c:  fstat64(0x1, 0xBFFFF3A4, 0xBFFFF410)       = 0 0
  389/0xe5c:  open_nocancel("/dev/\0", 0x100004, 0xBFFFE968)         = 3 0
  389/0xe5c:  fcntl_nocancel(0x3, 0x2, 0x1)      = 0 0
  389/0xe5c:  fstatfs64(0x3, 0xBFFFE8D8, 0x1)        = 0 0
  389/0xe5c:  getdirentries64(0x3, 0x10DFC00, 0x1000)        = 3080 0

  ... lots of lstat64 ...

  389/0xe5c:  close_nocancel(0x3)        = 0 0
  389/0xe5c:  open_nocancel("/dev/ttys003\0", 0x20002, 0x0)      = 3 0
  389/0xe5c:  close_nocancel(0x3)        = 0 0
  389/0xe5c:  write_nocancel(0x1, "testing\0", 0x7)      = 7 0
  389/0xe5c:  close_nocancel(0x1)        = 0 0
  385/0xe53:  select_nocancel(0x0, 0x0, 0x0)         = 0 0
  385/0xe53:  close_nocancel(0x4)        = 0 0
  385/0xe53:  read_nocancel(0x3, "\0", 0x400)        = 0 0
  385/0xe53:  write_nocancel(0x1, "b''\n\0", 0x4)        = 4 0
  385/0xe53:  wait4_nocancel(0x185, 0xBFFFF474, 0x0)         = 389 0
  385/0xe53:  sigaction(0x2, 0xBFFFF800, 0xBFFFF838)         = 0 0
  385/0xe53:  madvise(0xDA0000, 0x20000, 0x9)        = 0 0
  385/0xe53:  madvise(0xDC0000, 0x20000, 0x9)        = 0 0
  385/0xe53:  madvise(0xD80000, 0x20000, 0x9)        = 0 0





2.7, parent_close_slave_before_child_write = True (WORkS)

    PID/THRD  SYSCALL(args)          = return
  363/0xcab:  open_nocancel("/dev/ptmx\0", 0x20002, 0x0)         = 3 0
  363/0xcab:  ioctl(0x3, 0x20007454, 0xFFFFFFFF)         = 0 0
  363/0xcab:  ioctl(0x3, 0x20007452, 0x0)        = 0 0
  363/0xcab:  ioctl(0x3, 0x40807453, 0x10041AD20)        = 0 0
  363/0xcab:  stat64("/dev/ttys003\0", 0x7FFF5FBFF030, 0x0)      = 0 0
  363/0xcab:  open_nocancel("/dev/ttys003\0", 0x20002, 0x0)      = 4 0
  363/0xcab:  fork()         = 368 0
  368/0xcb5:  fork()         = 0 0
  368/0xcb5:  thread_selfid(0x100420BE0, 0x3, 0x1)       = 3253 0
  368/0xcb5:  getpid(0x100420BE0, 0x3, 0x0)      = 368 0
  363/0xcab:  select(0x0, 0x0, 0x0, 0x0, 0x7FFF5FBFF3C0)         = 0 0
  363/0xcab:  close(0x4)         = 0 0
  368/0xcb5:  select(0x0, 0x0, 0x0, 0x0, 0x7FFF5FBFF3C0)         = 0 0
  368/0xcb5:  setsid(0x0, 0x0, 0x1001A7B40)      = 368 0
  368/0xcb5:  close(0x3)         = 0 0
  368/0xcb5:  dup2(0x4, 0x0, 0x0)        = 0 0
  368/0xcb5:  dup2(0x4, 0x1, 0x0)        = 1 0
  368/0xcb5:  dup2(0x4, 0x2, 0x0)        = 2 0
  368/0xcb5:  getrlimit(0x1008, 0x7FFF5FBFF3D0, 0x0)         = 0 0

  ... close range ...

  368/0xcb5:  ioctl(0x1, 0x40487413, 0x7FFF5FBFF360)         = 0 0
  368/0xcb5:  fstat64(0x1, 0x7FFF5FBFF2D0, 0x0)      = 0 0
  368/0xcb5:  open_nocancel("/dev/\0", 0x100004, 0x0)        = 3 0
  368/0xcb5:  fcntl_nocancel(0x3, 0x2, 0x1)      = 0 0
  368/0xcb5:  fstatfs64(0x3, 0x7FFF5FBFE7B0, 0x0)        = 0 0
  368/0xcb5:  getdirentries64(0x3, 0x1010AB600, 0x1000)      = 3080 0

  ... lots of lstat64 ...

  368/0xcb5:  close_nocancel(0x3)        = 0 0
  368/0xcb5:  open("/dev/ttys003\0", 0x2, 0x1FF)         = 3 0
  368/0xcb5:  close(0x3)         = 0 0
  368/0xcb5:  write(0x1, "testing\0", 0x7)       = 7 0
  368/0xcb5:  close(0x1)         = 0 0
  363/0xcab:  read(0x3, "testing\0", 0x400)      = 7 0
  363/0xcab:  write_nocancel(0x1, "'testing'\n\0", 0xA)      = 10 0
  363/0xcab:  wait4(0x170, 0x7FFF5FBFF3D4, 0x0)      = 368 0
  363/0xcab:  sigaction(0x2, 0x7FFF5FBFF800, 0x7FFF5FBFF830)         = 0 0




3.0, parent_close_slave_before_child_write = True (WORkS)

    PID/THRD  SYSCALL(args)          = return
  393/0xed3:  open_nocancel("/dev/ptmx\0", 0x20002, 0xD15EB8)        = 3 0
  393/0xed3:  ioctl(0x3, 0x20007454, 0xBFFFF1BC)         = 0 0
  393/0xed3:  ioctl(0x3, 0x20007452, 0xBFFFF1BC)         = 0 0
  393/0xed3:  ioctl(0x3, 0x40807453, 0x64F220)       = 0 0
  393/0xed3:  stat64("/dev/ttys003\0", 0xBFFFF164, 0x64F220)         = 0 0
  393/0xed3:  open_nocancel("/dev/ttys003\0", 0x20002, 0x0)      = 4 0
  393/0xed3:  fork()         = 399 0
  399/0xee3:  fork()         = 0 0
  399/0xee3:  thread_selfid(0x0, 0x0, 0x0)       = 3811 0
  399/0xee3:  getpid(0x0, 0x0, 0x0)      = 399 0
  393/0xed3:  select_nocancel(0x0, 0x0, 0x0)         = 0 0
  393/0xed3:  close_nocancel(0x4)        = 0 0
  399/0xee3:  select_nocancel(0x0, 0x0, 0x0)         = 0 0
  399/0xee3:  setsid(0x0, 0x0, 0x0)      = 399 0
  399/0xee3:  close_nocancel(0x3)        = 0 0
  399/0xee3:  dup2(0x4, 0x0, 0x0)        = 0 0
  399/0xee3:  dup2(0x4, 0x1, 0x0)        = 1 0
  399/0xee3:  dup2(0x4, 0x2, 0x0)        = 2 0
  399/0xee3:  getrlimit(0x8, 0xBFFFF46C, 0x0)        = 0 0

  ... close range ...

  399/0xee3:  ioctl(0x1, 0x402C7413, 0xBFFFF410)         = 0 0
  399/0xee3:  fstat64(0x1, 0xBFFFF3A4, 0xBFFFF410)       = 0 0
  399/0xee3:  open_nocancel("/dev/\0", 0x100004, 0xBFFFE968)         = 3 0
  399/0xee3:  fcntl_nocancel(0x3, 0x2, 0x1)      = 0 0
  399/0xee3:  fstatfs64(0x3, 0xBFFFE8D8, 0x1)        = 0 0
  399/0xee3:  getdirentries64(0x3, 0x10DFC00, 0x1000)        = 3080 0

  ... lots of lstat64 ...

  399/0xee3:  close_nocancel(0x3)        = 0 0
  399/0xee3:  open_nocancel("/dev/ttys003\0", 0x20002, 0x0)      = 3 0
  399/0xee3:  close_nocancel(0x3)        = 0 0
  399/0xee3:  write_nocancel(0x1, "testing\0", 0x7)      = 7 0
  399/0xee3:  close_nocancel(0x1)        = 0 0
  393/0xed3:  read_nocancel(0x3, "testing\0", 0x400)         = 7 0
  393/0xed3:  write_nocancel(0x1, "b'testing'\n\0", 0xB)         = 11 0
  393/0xed3:  wait4_nocancel(0x18F, 0xBFFFF474, 0x0)         = 399 0
  393/0xed3:  sigaction(0x2, 0xBFFFF800, 0xBFFFF838)         = 0 0
  393/0xed3:  madvise(0xDA0000, 0x20000, 0x9)        = 0 0
  393/0xed3:  madvise(0xDC0000, 0x20000, 0x9)        = 0 0
  393/0xed3:  madvise(0xD80000, 0x20000, 0x9)        = 0 0






-----------






import os
import pty
import resource
import signal
import tty
import time
import sys

PY3 = sys.version_info.major == 3
if PY3: raw_input = input


# change this to False for failure on py3
parent_close_slave_before_child_write = True


print("pid: %d" % os.getpid())
raw_input("hit enter when 'dtruss -f' is ready> ")

send_from_child = "testing123".encode()
master, slave = pty.openpty()
pid = os.fork()



# child process
if pid == 0:
    if parent_close_slave_before_child_write: time.sleep(2)
    else: time.sleep(1)
    os.setsid()
    os.close(master)

    os.dup2(slave, 0)
    os.dup2(slave, 1)
    os.dup2(slave, 2)

    max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
    os.closerange(3, max_fd)

    # make controlling terminal.  taken from pty.fork
    tmp_fd = os.open(os.ttyname(1), os.O_RDWR)
    os.close(tmp_fd)

    os.write(1, send_from_child)
    os.close(1)

    os._exit(255)

# parent process
else:
    if parent_close_slave_before_child_write: time.sleep(1)
    else: time.sleep(2)
    os.close(slave)

    actual_output = os.read(master, 1024)
    os.waitpid(pid, 0)

    assert(actual_output == send_from_child)
    print("correct output")
History
Date User Action Args
2012-09-11 04:19:31amoffatsetrecipients: + amoffat, loewis, ned.deily
2012-09-11 04:19:30amoffatsetmessageid: <1347337170.8.0.208818111589.issue15898@psf.upfronthosting.co.za>
2012-09-11 04:19:30amoffatlinkissue15898 messages
2012-09-11 04:19:28amoffatcreate