|
3 | 3 | from typing import Union
|
4 | 4 |
|
5 | 5 | import pytest
|
6 |
| - |
7 | 6 | from multidict import (
|
8 | 7 | CIMultiDict,
|
9 | 8 | CIMultiDictProxy,
|
@@ -872,3 +871,47 @@ def lower(self) -> str:
|
872 | 871 | d.update(lst)
|
873 | 872 |
|
874 | 873 | assert [("a", "a2"), ("b", "b"), ("c", "c")] == list(d.items())
|
| 874 | + |
| 875 | + |
| 876 | +def test_multidict_shrink_regression() -> None: |
| 877 | + """ |
| 878 | + Regression test for _md_shrink pointer increment bug in 6.6.0. |
| 879 | +
|
| 880 | + The bug was introduced in PR #1200 which added _md_shrink to optimize |
| 881 | + memory usage. The bug occurs when new_ep == old_ep (first non-deleted |
| 882 | + entry), causing new_ep to not be incremented. This results in the first |
| 883 | + entry being overwritten and memory corruption. |
| 884 | +
|
| 885 | + See: https://github.com/aio-libs/multidict/issues/1221 |
| 886 | + """ |
| 887 | + # Test case that reproduces the corruption |
| 888 | + md: MultiDict[str] = MultiDict() |
| 889 | + |
| 890 | + # Create pattern: [kept, deleted, kept, kept, ...] |
| 891 | + # This triggers new_ep == old_ep on first iteration of _md_shrink |
| 892 | + for i in range(10): |
| 893 | + md[f"k{i}"] = f"v{i}" |
| 894 | + |
| 895 | + # Delete some entries but keep the first one |
| 896 | + # This creates the exact condition for the bug |
| 897 | + for i in range(1, 10, 2): |
| 898 | + del md[f"k{i}"] |
| 899 | + |
| 900 | + # Trigger shrink by adding many entries |
| 901 | + # When the internal array needs to resize, it will call _md_shrink |
| 902 | + # because md->used < md->keys->nentries |
| 903 | + for i in range(50): |
| 904 | + md[f"new{i}"] = f"val{i}" |
| 905 | + |
| 906 | + # The bug would cause k0 to be lost due to memory corruption! |
| 907 | + assert "k0" in md, "First entry k0 was lost due to memory corruption!" |
| 908 | + assert md["k0"] == "v0", "First entry value was corrupted!" |
| 909 | + |
| 910 | + # Verify all other kept entries survived |
| 911 | + for i in range(0, 10, 2): |
| 912 | + assert f"k{i}" in md, f"Entry k{i} missing!" |
| 913 | + assert md[f"k{i}"] == f"v{i}", f"Entry k{i} has wrong value!" |
| 914 | + |
| 915 | + # Verify new entries |
| 916 | + for i in range(50): |
| 917 | + assert md[f"new{i}"] == f"val{i}" |
0 commit comments