Repository: secdev/scapy

Issue:
Variable used before assignment 
There is a problem in error handling for restart() function in scapy/utils.py, where res_code may be undefined in the finally clause

https://github.com/secdev/scapy/blob/9a2a3819c2834aabcde87d80dd1ed1a707b95e6a/scapy/utils.py#L239-L250
Very trivial bug, probably no big deal, but still good to fix.

A similar issue exists in scapy/arch/linux.py, where the try block does not have a blanket except, which can cause similar problems with crash at line 143.
https://github.com/secdev/scapy/blob/9a2a3819c2834aabcde87d80dd1ed1a707b95e6a/scapy/arch/linux.py#L133-L150


Discussion:
Hi, thanks for the report. Do you have reproducible examples that can trigger these kind of bugs? (or are you using a tool to generate those reports).

I don't understand how they make sense. In the second case, if `open` triggers something else than an `IOError` (which is already super unlikely)... then the exception is raised? How can it crash at line 143?
In theory, the first report  (i.e., `res_code` is used before assignment) would be valid if `subprocess.call()` could fail with an unhandled exception.

https://github.com/secdev/scapy/blob/9a2a3819c2834aabcde87d80dd1ed1a707b95e6a/scapy/utils.py#L246

It is not obvious from static code analysis whether this can happen, but looking at the implementation of `suprocess.call()` it seems that this is not the case.

- [subprocess.call()](https://github.com/python/cpython/blob/main/Lib/subprocess.py#L381-L395)
- [subprocess.Popen.wait()](https://github.com/python/cpython/blob/main/Lib/subprocess.py#L1257-L1278)
- [subprocess.Popen._wait()](https://github.com/python/cpython/blob/main/Lib/subprocess.py#L1535-L1548)

I agree with @gpotter2 that the second report is a false positive: if the open() command fails the function exits either with an uncaught exception or returns in line  141. In either case, line 142 is unreached.
BTW: the code in `_get_if_list()` appears unnecessarily complicated. See #3859.
For the first case:
``` Python
try:
    res=subprocess.call("/bin/blobZ")
except KeyboardInterrupt:
    print("kbd")
finally:
    print(res)
```
results in NameError: name 'res' is not defined
which is clearly odd and not what was intended in the code=)

For the second case, python's open function actually raises OSError (as of python3.11)
https://docs.python.org/3/library/functions.html#open
It so happens that OSError is not an instance of IOError. 
```python
>>> isinstance(OSError, IOError)
False
```

As a result, the function in second case would simply crash instead of doing any cleanup whatsoever (i.e. the entire try/except thing is doing nothing).

These reports are both by-products of compilation with nuitka (I used it for performance more than for static analysis, just figured it would be nice to get these bugs addressed).  Naturally, I have validated both cases manually to be legitimate before posting. 
Do you mean
```
>>> issubclass(OSError, IOError)
True
```

> Naturally, I have validated both cases manually to be legitimate before posting.

Would you mind sharing your test cases?
> It so happens that OSError is not an instance of IOError.

Actually, I just learned that [IOError is identical with OSError](https://docs.python.org/3/library/exceptions.html?highlight=oserror#OSError) since Python version 3.3, and that the latter is the preferred name.
```
>>> IOError is OSError
True
```
@alexpyattaev I wasn't questioning the fact that the `subpocess.call()` could fail in general, just pointing out that this would be very unlikely to happen in the case of a restart, unless the exutable has been renamed or is some sort of resource exhaustion.
```
             res_code = subprocess.call([sys.executable] + sys.argv) 
```
However, I agree that for the sake of defensive programming (and to please static code analyzers) it would be sensible to initialize `res_code` before the try block.



Resolving patch:
diff --git a/scapy/utils.py b/scapy/utils.py
--- a/scapy/utils.py
+++ b/scapy/utils.py
@@ -242,10 +242,9 @@ def restart():
     if not conf.interactive or not os.path.isfile(sys.argv[0]):
         raise OSError("Scapy was not started from console")
     if WINDOWS:
+        res_code = 1
         try:
             res_code = subprocess.call([sys.executable] + sys.argv)
-        except KeyboardInterrupt:
-            res_code = 1
         finally:
             os._exit(res_code)
     os.execv(sys.executable, [sys.executable] + sys.argv)


Test patch: