已开启
fix(cve): 修复 CVE-2026-114605 在 python-dns 中的漏洞 #71
infra_team创建于 12 天前
fix(cve): 修复 CVE-2026-114605 在 python-dns 中的漏洞 #71
已开启
共 2 个文件变更+107-1
| @@ -0,0 +1,99 @@ | |||
| 1 | +From 3c1991fea116bb0e905c93efa98782399611ef3a Mon Sep 17 00:00:00 2001 | ||
E | |||
| 2 | +From: Bob Halley <halley@dnspython.org> | ||
| 3 | +Date: Sat, 26 Sep 2026 13:38:07 -0700 | ||
| 4 | +Subject: [PATCH] Limit decompression to a chain length of at most 16 pointers. | ||
| 5 | + | ||
| 6 | +A maliciously crafted DNS message may cause dns.name.from_wire_parser() to use excessive CPU for DNS name decompression. Applications which process untrusted DNS wire form messages with dnspython may be degraded or DoS'd by the extra CPU use required. | ||
| 7 | + | ||
| 8 | +Credit: | ||
| 9 | + | ||
| 10 | +Evgenios Gkritsis, Department of Informatics, University of Piraeus & Athena Research Center, Greece | ||
| 11 | +Constantinos Patsakis, Department of Informatics, University of Piraeus & Athena Research Center, Greece | ||
| 12 | +George Stergiopoulos, Department of Informatics, Athens University of Economics and Business, Greece | ||
| 13 | + | ||
| 14 | +Upstream-commit: https://github.com/rthalley/dnspython/commit/b485e460c266540a1a09ca6d8d1d7b685b1d3023 | ||
| 15 | +Signed-off-by: infra_team <zhaiwenjie1@huawei.com> | ||
| 16 | +--- | ||
| 17 | + dns/name.py | 11 +++++++++++ | ||
| 18 | + tests/test_name.py | 22 ++++++++++++++++++++++ | ||
| 19 | + 2 files changed, 33 insertions(+) | ||
| 20 | + | ||
| 21 | +diff --git a/dns/name.py b/dns/name.py | ||
| 22 | +index f452bfe..8d701ab 100644 | ||
| 23 | +--- a/dns/name.py | ||
| 24 | ++++ b/dns/name.py | ||
| 25 | +@@ -84,6 +84,10 @@ class BadPointer(dns.exception.FormError): | ||
| 26 | + """A DNS compression pointer points forward instead of backward.""" | ||
| 27 | + | ||
| 28 | + | ||
| 29 | ++class PointerChainTooLong(dns.exception.FormError): | ||
| 30 | ++ """A DNS compression pointer chain was too long.""" | ||
| 31 | ++ | ||
| 32 | ++ | ||
| 33 | + class BadLabelType(dns.exception.FormError): | ||
| 34 | + """The label type in DNS name wire format is unknown.""" | ||
| 35 | + | ||
| 36 | +@@ -1022,6 +1026,9 @@ def from_text( | ||
| 37 | + return Name(labels) | ||
| 38 | + | ||
| 39 | + | ||
| 40 | ++MAX_COMPRESSION_POINTER_CHAIN = 16 | ||
| 41 | ++ | ||
| 42 | ++ | ||
| 43 | + # we need 'dns.wire.Parser' quoted as dns.name and dns.wire depend on each other. | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +@@ -1040,6 +1047,7 @@ def from_wire_parser(parser: "dns.wire.Parser") -> Name: | ||
| 47 | + | ||
| 48 | + labels = [] | ||
| 49 | + biggest_pointer = parser.current | ||
| 50 | ++ hops = 0 | ||
| 51 | + with parser.restore_furthest(): | ||
| 52 | + count = parser.get_uint8() | ||
| 53 | + while count != 0: | ||
| 54 | +@@ -1050,6 +1058,9 @@ def from_wire_parser(parser: "dns.wire.Parser") -> Name: | ||
| 55 | + if current >= biggest_pointer: | ||
| 56 | + raise BadPointer | ||
| 57 | + biggest_pointer = current | ||
| 58 | ++ hops += 1 | ||
| 59 | ++ if hops > MAX_COMPRESSION_POINTER_CHAIN: | ||
| 60 | ++ raise PointerChainTooLong | ||
| 61 | + parser.seek(current) | ||
| 62 | + else: | ||
| 63 | + raise BadLabelType | ||
| 64 | +diff --git a/tests/test_name.py b/tests/test_name.py | ||
| 65 | +index 1d18ce3..e9303ae 100644 | ||
| 66 | +--- a/tests/test_name.py | ||
| 67 | ++++ b/tests/test_name.py | ||
| 68 | +@@ -693,6 +693,28 @@ class NameTestCase(unittest.TestCase): | ||
| 69 | + | ||
| 70 | + self.assertRaises(dns.name.BadLabelType, bad) | ||
| 71 | + | ||
| 72 | ++ def testCompressionChainLength(self): | ||
| 73 | ++ # We're going to decode some wire starting two bytes from the end. At this | ||
| 74 | ++ # position is a pointer which points to four bytes from the end, and it points | ||
| 75 | ++ # to one 6 bytes from the end, etc., until finally the 16th pointer points at | ||
| 76 | ++ # offset zero, which is the root label followed by a byte of padding. After | ||
| 77 | ++ # all of this useless pointer hopping, we should get the root name. | ||
| 78 | ++ w = b"\x00\x00\xc0\x00\xc0\x02\xc0\x04\xc0\x06\xc0\x08\xc0\x0a\xc0\x0c\xc0\x0e\xc0\x10\xc0\x12\xc0\x14\xc0\x16\xc0\x18\xc0\x1a\xc0\x1c\xc0\x1e" | ||
| 79 | ++ self.assertEqual(len(w), 34) | ||
| 80 | ++ starts = [c for c in w if c == 0xC0] | ||
| 81 | ++ self.assertEqual(len(starts), dns.name.MAX_COMPRESSION_POINTER_CHAIN) | ||
| 82 | ++ | ||
| 83 | ++ def bad(): | ||
| 84 | ++ # Now we make the wire one pointer longer, which is too long! | ||
| 85 | ++ w1 = w + b"\xc0\x20" | ||
| 86 | ++ self.assertEqual(len(w1), 36) | ||
| 87 | ++ starts = [c for c in w1 if c == 0xC0] | ||
| 88 | ++ self.assertEqual(len(starts), dns.name.MAX_COMPRESSION_POINTER_CHAIN + 1) | ||
| 89 | ++ dns.name.from_wire(w1, 34) | ||
| 90 | ++ | ||
| 91 | ++ self.assertEqual(dns.name.from_wire(w, 32)[0], dns.name.from_text(".")) | ||
| 92 | ++ self.assertRaises(dns.name.PointerChainTooLong, bad) | ||
| 93 | ++ | ||
| 94 | + def testParent1(self): | ||
| 95 | + n = dns.name.from_text("foo.bar.") | ||
| 96 | + self.assertEqual(n.parent(), dns.name.from_text("bar.")) | ||
| 97 | +-- | ||
| 98 | +2.39.5 | ||
| 99 | + | ||
| @@ -14,7 +14,7 @@ messages, names, and records. | |||
| 14 | Name: python-dns | 14 | Name: python-dns |
| 15 | Summary: %{sum} | 15 | Summary: %{sum} |
| 16 | Version: 2.4.2 | 16 | Version: 2.4.2 |
| 17 | -Release: 4 | 17 | +Release: 5 |
| 18 | License: ISC and MIT | 18 | License: ISC and MIT |
| 19 | URL: http://www.dnspython.org/ | 19 | URL: http://www.dnspython.org/ |
| 20 | Source0: https://github.com/rthalley/dnspython/archive/v%{version}/dnspython-%{version}.tar.gz | 20 | Source0: https://github.com/rthalley/dnspython/archive/v%{version}/dnspython-%{version}.tar.gz |
| @@ -31,6 +31,9 @@ Patch0007: backport-Retry-on-test-timeout.patch | |||
| 31 | Patch0008: backport-add-more-wrapping-accidentally-omitted-from-the-PR.patch | 31 | Patch0008: backport-add-more-wrapping-accidentally-omitted-from-the-PR.patch |
| 32 | Patch0009: fix-to-skip-unsupport-testcases.patch | 32 | Patch0009: fix-to-skip-unsupport-testcases.patch |
| 33 | 33 | ||
| 34 | +# fix CVE-2026-114605 | ||
| 35 | +Patch0010: 0010-Limit-decompression-to-a-chain-length-of-at-most-16-.patch | ||
| 36 | + | ||
| 34 | BuildArch: noarch | 37 | BuildArch: noarch |
| 35 | 38 | ||
| 36 | BuildRequires: python3-devel python3-setuptools python3-cryptography | 39 | BuildRequires: python3-devel python3-setuptools python3-cryptography |
| @@ -83,6 +86,10 @@ fi | |||
| 83 | %doc examples | 86 | %doc examples |
| 84 | 87 | ||
| 85 | %changelog | 88 | %changelog |
| 89 | +* Sat Oct 24 2026 infra_team <zhaiwenjie1@huawei.com> - 2.4.2-5 | ||
| 90 | +- fix CVE-2026-114605 | ||
| 91 | +- Limit DNS name decompression to a chain length of at most 16 pointers | ||
| 92 | + | ||
| 86 | * Wed Oct 29 2025 gaihuiying <eaglegai@163.com> - 2.4.2-4 | 93 | * Wed Oct 29 2025 gaihuiying <eaglegai@163.com> - 2.4.2-4 |
| 87 | - sync to skip testResolveTCP on loongarch64 | 94 | - sync to skip testResolveTCP on loongarch64 |
| 88 | 95 | ||
@infra_team commit id建议跟社区保持一致