MagicStack / MagicStack/uvloop
Silently closes UDP socket
还没有人认领这个 Issue。
- 主要语言
- Cython
- 星标
- 11.9k
- 派生
- 616
- PR 合并指标
- 30 天内没有已合并 PR
描述
- uvloop version: 0.14
- Python version: 3.8.2
- Platform: Linux
- Can you reproduce the bug with
PYTHONASYNCIODEBUGin env?: Yes - Does uvloop behave differently from vanilla asyncio? How?: Yes. See below
uvloop silently closes UDP socket when sending data to incorrect destination.
Test program:
import asyncio
import uvloop
# uncomment this line to check it with uvloop
#uvloop.install()
async def main():
loop = asyncio.get_event_loop()
transport, proto = await loop.create_datagram_endpoint(
asyncio.DatagramProtocol,
local_addr=("192.168.0.2", 0),
)
print(transport.get_extra_info('sockname'))
print("Before sending the message with None destination")
transport.sendto(b"deadbeef")
print("After sending the message with None destination")
await asyncio.sleep(0.1) # let asyncio closes internal socket
print("Before sending the message with incorrect port destination")
transport.sendto(b"deadbeef", ("45.83.128.251", 0))
print("After sending the message with incorrect port destination")
transport.close()
asyncio.get_event_loop().run_until_complete(main())
asyncio output:
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "", line 1, in
File "/root/.pyenv/versions/3.8.2/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "", line 14, in main
File "/root/.pyenv/versions/3.8.2/lib/python3.8/asyncio/selector_events.py", line 1056, in sendto
self._fatal_error(
File "/root/.pyenv/versions/3.8.2/lib/python3.8/asyncio/selector_events.py", line 703, in _fatal_error
self._loop.call_exception_handler({
AttributeError: 'NoneType' object has no attribute 'call_exception_handler'
uvloop output:
Then we can cut off first block with sending to None destination. And here we have NEW ONE differece in handling ip:port.
async def main():
loop = asyncio.get_event_loop()
transport, proto = await loop.create_datagram_endpoint(
asyncio.DatagramProtocol,
local_addr=("192.168.0.2", 0),
)
print(transport.get_extra_info('sockname'))
print("Please use `netstat -au` to see this socket really listening. I will wait 10 sec")
await asyncio.sleep(10)
print("Before sending the message with incorrect port destination")
transport.sendto(b"deadbeef", ("45.83.128.251", 0))
print("After sending the message with incorrect port destination")
print("Please use `netstat -au` to see this socket STILL really listening. I will wait 10 sec")
await asyncio.sleep(10)
transport.close()
When i run it with asyncio event loop all looks fine, socket still present.
When i run it with uvloop it silently closes, and also i can go into infinity wait on recv() call
As i know RFC describe that case as (https://tools.ietf.org/html/rfc8085#section-5.1):
A UDP sender SHOULD NOT use a source port value of zero. A source
port number that cannot be easily determined from the address or
payload type provides protection at the receiver from data injection
attacks by off-path devices. A UDP receiver SHOULD NOT bind to port
zero.
But i got real messages from the internet with that port. I will drop it in my application, but i think uvloop should not silently closes socket. It's very unexpected and differ from asyncio event loop.
Proof from Sentry (https://github.com/spumer/source-query-proxy):
next iteration of recv packet was failed, cause in previous we send response to zero port

贡献指南
这个仓库没有索引到贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
首先,使用 uvloop 和 vanilla asyncio 运行 issue 的 UDP 复现程序,重点关注目标端口为 0 时的 create_datagram_endpoint 和 transport.sendto。比较发送后的 socket 状态以及后续的 recv 行为。完成的标准是:无效的 UDP 目标不会静默关闭 transport,并且该行为由回归测试记录。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- python
- 领域
- networking
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 停滞
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100