MagicStack / MagicStack/uvloop
Silently closes UDP socket
まだ誰も着手していません。
- 主要言語
- 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 にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
まず、uvloop と vanilla asyncio で issue の UDP 再現コードを実行し、宛先ポート 0 を指定した create_datagram_endpoint と transport.sendto に注目します。送信後のソケット状態と、その後の recv の動作を比較します。無効な UDP 宛先によって transport が暗黙的に閉じられず、その動作が回帰テストで文書化されれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- networking
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 35/100