python / python/cpython

Endless polling EOF socket

Đang mở
#148,950 9 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

stdlib type-bug
Ngôn ngữ chính
Python
Star
77.2k
Fork
35.9k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

Bug report

Bug description:

Hello,

I have this very simple code to attempt a TCP connection to a given host. The linux machine where I run this has tsocks library installed, to transparently redirect connections over socks server. If the target IP accessed through the socks server refuses the connection, the code will end in an endless unbreakable loop.

def tcp_connect(ip, port):
    """Attempt a TCP connection to the given IP and port."""
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        result = sock.connect_ex((ip, port))
        sock.close()

Timeout has no effect, ctrl+c on the console doesn't break it.

Connection attempt with some command line utility ends correctly.

$ ssh 1.1.1.1
ssh: connect to host 1.1.1.1 port 22: Transport endpoint is not connected
$ ldp telnet 1.1.1.1 22
Trying 1.1.1.1...
telnet: Unable to connect to remote host: Transport endpoint is not connected

I tried running strace to see what happens during that endless loop and this is the result :

getpeername(3, 0x7ffd6c12e3f0, [16])    = -1 ENOTCONN (Transport endpoint is not connected)
connect(3, {sa_family=AF_INET, sin_port=htons(2224), sin_addr=inet_addr("172.17.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=3, events=POLLOUT}], 1, 5000) = 1 ([{fd=3, revents=POLLOUT}])
connect(3, {sa_family=AF_INET, sin_port=htons(2224), sin_addr=inet_addr("172.17.0.1")}, 16) = 0
sendto(3, "\5\2\0\2", 4, 0, NULL, 0)    = 4
recvfrom(3, "\5\0", 2, 0, NULL, NULL)   = 2
sendto(3, "\5\1\0\1\225\203f\211\0\26", 10, 0, NULL, 0) = 10
recvfrom(3, 0x55944406ddd8, 10, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=3, events=POLLIN}], 1, 5000)  = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "", 10, 0, NULL, NULL)      = 0
poll([{fd=3, events=POLLIN}], 1, 5000)  = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "", 10, 0, NULL, NULL)      = 0
poll([{fd=3, events=POLLIN}], 1, 5000)  = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "", 10, 0, NULL, NULL)      = 0
poll([{fd=3, events=POLLIN}], 1, 5000)  = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "", 10, 0, NULL, NULL)      = 0
poll([{fd=3, events=POLLIN}], 1, 5000)  = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "", 10, 0, NULL, NULL)      = 0

<continues until sigkill while hogging 100% CPU>

I don't understand it that much, but LLM seems to believe, that the socket is getting polled despite EOF, which is supposedly an error.

Edit:
For comparison, this is how the strace on that telnet looks like

getpeername(3, 0x7ffd4d54e260, [16])    = -1 ENOTCONN (Transport endpoint is not connected)
connect(3, {sa_family=AF_INET, sin_port=htons(2224), sin_addr=inet_addr("172.17.0.1")}, 16) = 0
sendto(3, "\5\2\0\2", 4, 0, NULL, 0)    = 4
recvfrom(3, "\5\0", 2, 0, NULL, NULL)   = 2
sendto(3, "\5\1\0\1\225\203f\211\0\26", 10, 0, NULL, 0) = 10
recvfrom(3, "", 10, 0, NULL, NULL)      = 0
write(2, "telnet: Unable to connect to rem"..., 78telnet: Unable to connect to remote host: Transport endpoint is not connected
) = 78
close(3)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

Thank you for your attention.

CPython versions tested on:

3.10

Operating systems tested on:

Linux

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu với đoạn tcp_connect trong báo cáo và tái hiện nó trên Linux bằng Python 3.10 và tsocks, sau đó so sánh strace của nó với trường hợp telnet đang hoạt động. Theo dõi hành vi của socket và connect_ex xung quanh các lần đọc EOF lặp lại để xác định liệu vòng lặp nằm trong CPython hay trong lớp proxy trong suốt. Được xem là hoàn thành khi đã xác định được hành vi gây ra vấn đề và issue có một bài kiểm thử hồi quy có thể tái hiện hoặc một cách giải quyết rõ ràng.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
networking
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Cần làm rõ
Mức phù hợp với người mới
45/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.