Add additional blocking operations to loop protection (#124017)

This commit is contained in:
J. Nick Koston 2024-08-16 09:03:24 -05:00 committed by GitHub
parent 14a3217d7e
commit cb8a6af12d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 143 additions and 0 deletions

View file

@ -8,6 +8,7 @@ import glob
from http.client import HTTPConnection from http.client import HTTPConnection
import importlib import importlib
import os import os
from pathlib import Path
from ssl import SSLContext from ssl import SSLContext
import sys import sys
import threading import threading
@ -162,6 +163,60 @@ _BLOCKING_CALLS: tuple[BlockingCall, ...] = (
strict_core=False, strict_core=False,
skip_for_tests=True, skip_for_tests=True,
), ),
BlockingCall(
original_func=SSLContext.load_cert_chain,
object=SSLContext,
function="load_cert_chain",
check_allowed=None,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.open,
object=Path,
function="open",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.read_text,
object=Path,
function="read_text",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.read_bytes,
object=Path,
function="read_bytes",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.write_text,
object=Path,
function="write_text",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.write_bytes,
object=Path,
function="write_bytes",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
) )

View file

@ -218,6 +218,17 @@ async def test_protect_loop_open(caplog: pytest.LogCaptureFixture) -> None:
assert "Detected blocking call to open with args" not in caplog.text assert "Detected blocking call to open with args" not in caplog.text
async def test_protect_loop_path_open(caplog: pytest.LogCaptureFixture) -> None:
"""Test opening a file in /proc is not reported."""
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/proc/does_not_exist").open(encoding="utf8"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to open with args" not in caplog.text
async def test_protect_open(caplog: pytest.LogCaptureFixture) -> None: async def test_protect_open(caplog: pytest.LogCaptureFixture) -> None:
"""Test opening a file in the event loop logs.""" """Test opening a file in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False): with patch.object(block_async_io, "_IN_TESTS", False):
@ -231,6 +242,71 @@ async def test_protect_open(caplog: pytest.LogCaptureFixture) -> None:
assert "Detected blocking call to open with args" in caplog.text assert "Detected blocking call to open with args" in caplog.text
async def test_protect_path_open(caplog: pytest.LogCaptureFixture) -> None:
"""Test opening a file in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data_not_exist").open(encoding="utf8"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to open with args" in caplog.text
async def test_protect_path_read_bytes(caplog: pytest.LogCaptureFixture) -> None:
"""Test reading file bytes in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data_not_exist").read_bytes(), # noqa: ASYNC230
):
pass
assert "Detected blocking call to read_bytes with args" in caplog.text
async def test_protect_path_read_text(caplog: pytest.LogCaptureFixture) -> None:
"""Test reading a file text in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data_not_exist").read_text(encoding="utf8"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to read_text with args" in caplog.text
async def test_protect_path_write_bytes(caplog: pytest.LogCaptureFixture) -> None:
"""Test writing file bytes in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data/not/exist").write_bytes(b"xxx"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to write_bytes with args" in caplog.text
async def test_protect_path_write_text(caplog: pytest.LogCaptureFixture) -> None:
"""Test writing file text in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data/not/exist").write_text("xxx", encoding="utf8"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to write_text with args" in caplog.text
async def test_enable_multiple_times(caplog: pytest.LogCaptureFixture) -> None: async def test_enable_multiple_times(caplog: pytest.LogCaptureFixture) -> None:
"""Test trying to enable multiple times.""" """Test trying to enable multiple times."""
with patch.object(block_async_io, "_IN_TESTS", False): with patch.object(block_async_io, "_IN_TESTS", False):
@ -354,6 +430,18 @@ async def test_protect_loop_load_verify_locations(
assert "Detected blocking call to load_verify_locations" in caplog.text assert "Detected blocking call to load_verify_locations" in caplog.text
async def test_protect_loop_load_cert_chain(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test SSLContext.load_cert_chain calls in the loop are logged."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
context = ssl.create_default_context()
with pytest.raises(OSError):
context.load_cert_chain("/dev/null")
assert "Detected blocking call to load_cert_chain" in caplog.text
async def test_open_calls_ignored_in_tests(caplog: pytest.LogCaptureFixture) -> None: async def test_open_calls_ignored_in_tests(caplog: pytest.LogCaptureFixture) -> None:
"""Test opening a file in tests is ignored.""" """Test opening a file in tests is ignored."""
assert block_async_io._IN_TESTS assert block_async_io._IN_TESTS