diff --git a/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47180.patch b/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47180.patch
new file mode 100644
index 0000000000..7d27066d6f
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47180.patch
@@ -0,0 +1,110 @@
+From 7ffd3a90230cb99bcae4350fcb8498c48044f6a6 Mon Sep 17 00:00:00 2001
+From: "J. Nick Koston" <nick@koston.org>
+Date: Sun, 17 May 2026 19:48:44 -0700
+Subject: [PATCH] fix: bound DNS compression-pointer chain depth in DNSIncoming
+ (#1719)
+
+(cherry picked from commit f9e23592137f30fdf7ef710dba065da31c79b1cf)
+
+CVE: CVE-2026-47180
+Upstream-Status: Backport [https://github.com/python-zeroconf/python-zeroconf/commit/f9e23592137f30fdf7ef710dba065da31c79b1cf]
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/zeroconf/_protocol/incoming.pxd |  2 +-
+ src/zeroconf/_protocol/incoming.py  | 14 ++++++++++----
+ tests/test_protocol.py              | 22 ++++++++++++++++++++++
+ 3 files changed, 33 insertions(+), 5 deletions(-)
+
+diff --git a/src/zeroconf/_protocol/incoming.pxd b/src/zeroconf/_protocol/incoming.pxd
+index feaa2a0..eface8d 100644
+--- a/src/zeroconf/_protocol/incoming.pxd
++++ b/src/zeroconf/_protocol/incoming.pxd
+@@ -83,7 +83,7 @@ cdef class DNSIncoming:
+         link_py_int=object,
+         linked_labels=cython.list
+     )
+-    cdef unsigned int _decode_labels_at_offset(self, unsigned int off, cython.list labels, cython.set seen_pointers)
++    cdef unsigned int _decode_labels_at_offset(self, unsigned int off, cython.list labels, cython.set seen_pointers, unsigned int depth)
+ 
+     @cython.locals(offset="unsigned int")
+     cdef void _read_header(self)
+diff --git a/src/zeroconf/_protocol/incoming.py b/src/zeroconf/_protocol/incoming.py
+index 2d977b6..d772f47 100644
+--- a/src/zeroconf/_protocol/incoming.py
++++ b/src/zeroconf/_protocol/incoming.py
+@@ -60,7 +60,7 @@ DNS_COMPRESSION_POINTER_LEN = 2
+ MAX_DNS_LABELS = 128
+ MAX_NAME_LENGTH = 253
+ 
+-DECODE_EXCEPTIONS = (IndexError, struct.error, IncomingDecodeError)
++DECODE_EXCEPTIONS = (IndexError, struct.error, IncomingDecodeError, RecursionError)
+ 
+ 
+ _seen_logs: dict[str, int | tuple] = {}
+@@ -409,7 +409,7 @@ class DNSIncoming:
+         labels: list[str] = []
+         seen_pointers: set[int] = set()
+         original_offset = self.offset
+-        self.offset = self._decode_labels_at_offset(original_offset, labels, seen_pointers)
++        self.offset = self._decode_labels_at_offset(original_offset, labels, seen_pointers, 0)
+         self._name_cache[original_offset] = labels
+         name = ".".join(labels) + "."
+         if len(name) > MAX_NAME_LENGTH:
+@@ -418,8 +418,14 @@ class DNSIncoming:
+             )
+         return name
+ 
+-    def _decode_labels_at_offset(self, off: _int, labels: list[str], seen_pointers: set[int]) -> int:
++    def _decode_labels_at_offset(
++        self, off: _int, labels: list[str], seen_pointers: set[int], depth: _int
++    ) -> int:
+         # This is a tight loop that is called frequently, small optimizations can make a difference.
++        if depth > MAX_DNS_LABELS:
++            raise IncomingDecodeError(
++                f"DNS compression pointer chain exceeds {MAX_DNS_LABELS} at {off} from {self.source}"
++            )
+         view = self.view
+         while off < self._data_len:
+             length = view[off]
+@@ -457,7 +463,7 @@ class DNSIncoming:
+             if not linked_labels:
+                 linked_labels = []
+                 seen_pointers.add(link_py_int)
+-                self._decode_labels_at_offset(link, linked_labels, seen_pointers)
++                self._decode_labels_at_offset(link, linked_labels, seen_pointers, depth + 1)
+                 self._name_cache[link_py_int] = linked_labels
+             labels.extend(linked_labels)
+             if len(labels) > MAX_DNS_LABELS:
+diff --git a/tests/test_protocol.py b/tests/test_protocol.py
+index edd87c2..bac2b44 100644
+--- a/tests/test_protocol.py
++++ b/tests/test_protocol.py
+@@ -1011,6 +1011,28 @@ def test_label_compression_attack():
+     assert len(parsed.answers()) == 1
+ 
+ 
++def test_dns_compression_pointer_chain_depth_attack() -> None:
++    """Test our wire parser rejects deeply chained compression pointers without recursing."""
++    # Build a packet with one question whose name is a 1500-deep chain of forward
++    # compression pointers, ending in a root label. Each pointer is 2 bytes,
++    # so chain length easily exceeds CPython's default recursion limit.
++    header = b"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
++    # Question at offset 12: pointer to offset 18 (past the question's type/class).
++    question_name = bytes([0xC0, 18])
++    question_type_class = b"\x00\x01\x00\x01"
++    chain_depth = 1500
++    chain = bytearray()
++    for i in range(chain_depth):
++        target = 18 + 2 * (i + 1)
++        chain.append(0xC0 | (target >> 8))
++        chain.append(target & 0xFF)
++    chain.append(0x00)
++    packet = header + question_name + question_type_class + bytes(chain)
++    parsed = r.DNSIncoming(packet, ("1.2.3.4", 5353))
++    assert parsed.valid is False
++    assert parsed.questions == []
++
++
+ def test_dns_compression_loop_attack():
+     """Test our wire parser does not loop forever when dns compression is in a loop."""
+     packet = (
diff --git a/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb b/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
index fd083d6ee8..c405e83b8c 100644
--- a/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
+++ b/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
@@ -5,6 +5,8 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=9fe712b1bc27c5c4e9ecd7f31d208900"
 
 SRC_URI[sha256sum] = "03fcca123df3652e23d945112d683d2f605f313637611b7d4adf31056f681702"
 
+SRC_URI += "file://CVE-2026-47180.patch"
+
 inherit pypi python_poetry_core cython
 
 RDEPENDS:${PN} += " \
