#34294: File locking fails if argtypes redefined
------------------------------+--------------------------------------
Reporter: Grub4K | Owner: nobody
Type: Bug | Status: new
Component: Core (Other) | Version: dev
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------
Description changed by Grub4K:
Old description:
> `django.core.files.locks` uses `ctypes.windll.kernel32` which is a global
> instance. If other code redefines `windll.kernel32.LockFileEx.argtypes`
> the function fails with an error.
> I am not sure how to write a test since its global
> {{{#!python
> import ctypes
> import ctypes.wintypes
>
> from django.core.files import locks
>
> class OVERLAPPED(ctypes.Structure):
> _fields_ = [
> ("Internal", ctypes.wintypes.LPVOID),
> ("InternalHigh", ctypes.wintypes.LPVOID),
> ("Offset", ctypes.wintypes.DWORD),
> ("OffsetHigh", ctypes.wintypes.DWORD),
> ("hEvent", ctypes.wintypes.HANDLE),
> ]
>
> ctypes.windll.kernel32.LockFileEx.argtypes = [
> ctypes.wintypes.HANDLE,
> ctypes.wintypes.DWORD,
> ctypes.wintypes.DWORD,
> ctypes.wintypes.DWORD,
> ctypes.wintypes.DWORD,
> ctypes.wintypes.DWORD,
> ]
>
> with open("./file", "w") as f:
> locks.lock(f, locks.LOCK_EX)
> }}}
> Possible patch:
> {{{#!diff
> diff --git a/django/core/files/locks.py b/django/core/files/locks.py
> index 5752b08a69..c0f471f87d 100644
> --- a/django/core/files/locks.py
> +++ b/django/core/files/locks.py
> @@ -32,12 +32,12 @@ if os.name == "nt":
> POINTER,
> Structure,
> Union,
> + WinDLL,
> byref,
> c_int64,
> c_ulong,
> c_void_p,
> sizeof,
> - windll,
> )
> from ctypes.wintypes import BOOL, DWORD, HANDLE
>
> @@ -73,10 +73,11 @@ if os.name == "nt":
> LPOVERLAPPED = POINTER(OVERLAPPED)
>
> # --- Define function prototypes for extra safety ---
> - LockFileEx = windll.kernel32.LockFileEx
> + kernel32 = WinDLL("kernel32")
> + LockFileEx = kernel32.LockFileEx
> LockFileEx.restype = BOOL
> LockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, DWORD,
> LPOVERLAPPED]
> - UnlockFileEx = windll.kernel32.UnlockFileEx
> + UnlockFileEx = kernel32.UnlockFileEx
> UnlockFileEx.restype = BOOL
> UnlockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, LPOVERLAPPED]
>
> diff --git a/tests/files/tests.py b/tests/files/tests.py
> index b3478d2732..d0e633cb7a 100644
> --- a/tests/files/tests.py
> +++ b/tests/files/tests.py
> @@ -200,6 +200,32 @@ class FileTests(unittest.TestCase):
> self.assertIs(locks.unlock(f1), True)
> self.assertIs(locks.unlock(f2), True)
>
> + @unittest.skipIf(os.name != "nt", "File locking usees WinDLL on
> windows only")
> + def test_local_windll(self):
> + import ctypes
> + import ctypes.wintypes
> +
> + old_argtypes = ctypes.windll.kernel32.LockFileEx.argtypes
> + try:
> + ctypes.windll.kernel32.LockFileEx.argtypes = [
> + ctypes.wintypes.HANDLE,
> + ctypes.wintypes.DWORD,
> + ctypes.wintypes.DWORD,
> + ctypes.wintypes.DWORD,
> + ctypes.wintypes.DWORD,
> + ctypes.wintypes.DWORD,
> + ]
> + file_path = Path(__file__).parent / "test.png"
> + with open(file_path) as file:
> + try:
> + locks.lock(file, locks.LOCK_EX)
> + except Exception:
> + self.fail("Locking raised exception with changed
> argtypes")
> + else:
> + locks.unlock(file)
> + finally:
> + ctypes.windll.kernel32.LockFileEx.argtypes = old_argtypes
> +
>
> class NoNameFileTestCase(unittest.TestCase):
> """
> }}}
New description:
`django.core.files.locks` uses `ctypes.windll.kernel32` which is a global
instance. If other code redefines `windll.kernel32.LockFileEx.argtypes`
the function fails with an error.
{{{#!python
import ctypes
import ctypes.wintypes
from django.core.files import locks
class OVERLAPPED(ctypes.Structure):
_fields_ = [
("Internal", ctypes.wintypes.LPVOID),
("InternalHigh", ctypes.wintypes.LPVOID),
("Offset", ctypes.wintypes.DWORD),
("OffsetHigh", ctypes.wintypes.DWORD),
("hEvent", ctypes.wintypes.HANDLE),
]
ctypes.windll.kernel32.LockFileEx.argtypes = [
ctypes.wintypes.HANDLE,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD,
]
with open("./file", "w") as f:
locks.lock(f, locks.LOCK_EX)
}}}
Possible patch:
{{{#!diff
diff --git a/django/core/files/locks.py b/django/core/files/locks.py
index 5752b08a69..c0f471f87d 100644
--- a/django/core/files/locks.py
+++ b/django/core/files/locks.py
@@ -32,12 +32,12 @@ if os.name == "nt":
POINTER,
Structure,
Union,
+ WinDLL,
byref,
c_int64,
c_ulong,
c_void_p,
sizeof,
- windll,
)
from ctypes.wintypes import BOOL, DWORD, HANDLE
@@ -73,10 +73,11 @@ if os.name == "nt":
LPOVERLAPPED = POINTER(OVERLAPPED)
# --- Define function prototypes for extra safety ---
- LockFileEx = windll.kernel32.LockFileEx
+ kernel32 = WinDLL("kernel32")
+ LockFileEx = kernel32.LockFileEx
LockFileEx.restype = BOOL
LockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, DWORD,
LPOVERLAPPED]
- UnlockFileEx = windll.kernel32.UnlockFileEx
+ UnlockFileEx = kernel32.UnlockFileEx
UnlockFileEx.restype = BOOL
UnlockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, LPOVERLAPPED]
diff --git a/tests/files/tests.py b/tests/files/tests.py
index b3478d2732..d0e633cb7a 100644
--- a/tests/files/tests.py
+++ b/tests/files/tests.py
@@ -200,6 +200,32 @@ class FileTests(unittest.TestCase):
self.assertIs(locks.unlock(f1), True)
self.assertIs(locks.unlock(f2), True)
+ @unittest.skipIf(os.name != "nt", "File locking usees WinDLL on
windows only")
+ def test_local_windll(self):
+ import ctypes
+ import ctypes.wintypes
+
+ old_argtypes = ctypes.windll.kernel32.LockFileEx.argtypes
+ try:
+ ctypes.windll.kernel32.LockFileEx.argtypes = [
+ ctypes.wintypes.HANDLE,
+ ctypes.wintypes.DWORD,
+ ctypes.wintypes.DWORD,
+ ctypes.wintypes.DWORD,
+ ctypes.wintypes.DWORD,
+ ctypes.wintypes.DWORD,
+ ]
+ file_path = Path(__file__).parent / "test.png"
+ with open(file_path) as file:
+ try:
+ locks.lock(file, locks.LOCK_EX)
+ except Exception:
+ self.fail("Locking raised exception with changed
argtypes")
+ else:
+ locks.unlock(file)
+ finally:
+ ctypes.windll.kernel32.LockFileEx.argtypes = old_argtypes
+
class NoNameFileTestCase(unittest.TestCase):
"""
}}}
--
--
Ticket URL: <https://code.djangoproject.com/ticket/34294#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/01070185ea967075-e0594b78-f155-4fae-be6c-4db8e74c5140-000000%40eu-central-1.amazonses.com.