Repository: secdev/scapy

Issue:
ECU_State bug
              Hi @polybassa, sorry for bothering. If you have the time there's a small bug

```
Traceback (most recent call last):
  File "/opt/hostedtoolcache/PyPy/3.9.16/x64/lib/pypy3.9/threading.py", line 980, in _bootstrap_inner
    self.run()
  File "/opt/hostedtoolcache/PyPy/3.9.16/x64/lib/pypy3.9/threading.py", line 917, in run
    self._target(*self._args, **self._kwargs)
  File "<input>", line 25, in answering_machine_thread
  File "/home/runner/work/scapy/scapy/scapy/ansmachine.py", line 210, in __call__
    self.sniff()
  File "/home/runner/work/scapy/scapy/scapy/ansmachine.py", line 216, in sniff
    sniff(**self.optsniff)
  File "/home/runner/work/scapy/scapy/scapy/sendrecv.py", line 1310, in sniff
    sniffer._run(*args, **kwargs)
  File "/home/runner/work/scapy/scapy/scapy/sendrecv.py", line 1253, in _run
    session.on_packet_received(p)
  File "/home/runner/work/scapy/scapy/scapy/sessions.py", line 109, in on_packet_received
    result = self.prn(pkt)
  File "/home/runner/work/scapy/scapy/scapy/ansmachine.py", line 172, in reply
    reply = self.make_reply(pkt)
  File "/home/runner/work/scapy/scapy/scapy/contrib/automotive/ecu.py", line 681, in make_reply
    if not resp.supports_state(self._ecu_state):
  File "/home/runner/work/scapy/scapy/scapy/contrib/automotive/ecu.py", line 544, in supports_state
    return any(s == state or state in s for s in self.__states)
  File "/home/runner/work/scapy/scapy/scapy/contrib/automotive/ecu.py", line 544, in <genexpr>
    return any(s == state or state in s for s in self.__states)
  File "/home/runner/work/scapy/scapy/scapy/contrib/automotive/ecu.py", line 119, in __contains__
    return all(s in self._expand() for s in item._expand())
  File "/home/runner/work/scapy/scapy/scapy/contrib/automotive/ecu.py", line 54, in _expand
    *[self._flatten(v) for v in self.__dict__.values()]):
  File "/home/runner/work/scapy/scapy/scapy/contrib/automotive/ecu.py", line 54, in <listcomp>
    *[self._flatten(v) for v in self.__dict__.values()]):
RuntimeError: dictionary changed size during iteration
Transition for edge (session1, session2tp1) failed
Error entering path [session1, session2tp1, security_level2session2tp1]
```

_Originally posted by @gpotter2 in https://github.com/secdev/scapy/issues/3869#issuecomment-1431042595_
            


Discussion:

Resolving patch:
diff --git a/scapy/contrib/automotive/ecu.py b/scapy/contrib/automotive/ecu.py
--- a/scapy/contrib/automotive/ecu.py
+++ b/scapy/contrib/automotive/ecu.py
@@ -16,7 +16,7 @@
 from threading import Lock
 
 from scapy.compat import Any, Union, Iterable, Callable, List, Optional, \
-    Tuple, Type, cast, Dict, orb, ValuesView
+    Tuple, Type, cast, Dict, orb
 from scapy.packet import Raw, Packet
 from scapy.plist import PacketList
 from scapy.sessions import DefaultSession
@@ -39,7 +39,7 @@ class EcuState(object):
 
     def __init__(self, **kwargs):
         # type: (Any) -> None
-        self.__cache__ = None  # type: Optional[Tuple[List[EcuState], ValuesView[Any]]]  # noqa: E501
+        self.__cache__ = None  # type: Optional[Tuple[List[EcuState], List[Any]]]  # noqa: E501
         for k, v in kwargs.items():
             if isinstance(v, GeneratorType):
                 v = list(v)
@@ -47,18 +47,18 @@ def __init__(self, **kwargs):
 
     def _expand(self):
         # type: () -> List[EcuState]
-        if self.__cache__ is None or \
-                self.__cache__[1] != self.__dict__.values():
+        values = list(self.__dict__.values())
+        keys = list(self.__dict__.keys())
+        if self.__cache__ is None or self.__cache__[1] != values:
             expanded = list()
-            for x in itertools.product(
-                    *[self._flatten(v) for v in self.__dict__.values()]):
+            for x in itertools.product(*[self._flatten(v) for v in values]):
                 kwargs = {}
-                for i, k in enumerate(self.__dict__.keys()):
+                for i, k in enumerate(keys):
                     if x[i] is None:
                         continue
                     kwargs[k] = x[i]
                 expanded.append(EcuState(**kwargs))
-            self.__cache__ = (expanded, self.__dict__.values())
+            self.__cache__ = (expanded, values)
         return self.__cache__[0]
 
     @staticmethod


Test patch: