Commit b64c89bd authored by Brian Curtin's avatar Brian Curtin

Fix #2810 - handle the case where some registry calls return

ERROR_MORE_DATA, requiring another call to get the remaining data.

Patch by Daniel Stutzbach
parent 264acad7
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import os, sys import os, sys
import unittest import unittest
from test import test_support from test import test_support
threading = test_support.import_module("threading")
from platform import machine from platform import machine
# Do this first so test will be skipped if module doesn't exist # Do this first so test will be skipped if module doesn't exist
...@@ -231,6 +232,58 @@ class LocalWinregTests(BaseWinregTests): ...@@ -231,6 +232,58 @@ class LocalWinregTests(BaseWinregTests):
except WindowsError: except WindowsError:
self.assertEqual(h.handle, 0) self.assertEqual(h.handle, 0)
def test_changing_value(self):
# Issue2810: A race condition in 2.6 and 3.1 may cause
# EnumValue or QueryValue to throw "WindowsError: More data is
# available"
done = False
class VeryActiveThread(threading.Thread):
def run(self):
with CreateKey(HKEY_CURRENT_USER, test_key_name) as key:
use_short = True
long_string = 'x'*2000
while not done:
s = 'x' if use_short else long_string
use_short = not use_short
SetValue(key, 'changing_value', REG_SZ, s)
thread = VeryActiveThread()
thread.start()
try:
with CreateKey(HKEY_CURRENT_USER,
test_key_name+'\\changing_value') as key:
for _ in range(1000):
num_subkeys, num_values, t = QueryInfoKey(key)
for i in range(num_values):
name = EnumValue(key, i)
QueryValue(key, name[0])
finally:
done = True
thread.join()
DeleteKey(HKEY_CURRENT_USER, test_key_name+'\\changing_value')
DeleteKey(HKEY_CURRENT_USER, test_key_name)
def test_long_key(self):
# Issue2810, in 2.6 and 3.1 when the key name was exactly 256
# characters, EnumKey threw "WindowsError: More data is
# available"
name = 'x'*256
try:
with CreateKey(HKEY_CURRENT_USER, test_key_name) as key:
SetValue(key, name, REG_SZ, 'x')
num_subkeys, num_values, t = QueryInfoKey(key)
EnumKey(key, 0)
finally:
DeleteKey(HKEY_CURRENT_USER, '\\'.join((test_key_name, name)))
DeleteKey(HKEY_CURRENT_USER, test_key_name)
def test_dynamic_key(self):
# Issue2810, when the value is dynamically generated, these
# throw "WindowsError: More data is available" in 2.6 and 3.1
EnumValue(HKEY_PERFORMANCE_DATA, 0)
QueryValueEx(HKEY_PERFORMANCE_DATA, None)
# Reflection requires XP x64/Vista at a minimum. XP doesn't have this stuff # Reflection requires XP x64/Vista at a minimum. XP doesn't have this stuff
# or DeleteKeyEx so make sure their use raises NotImplementedError # or DeleteKeyEx so make sure their use raises NotImplementedError
@unittest.skipUnless(WIN_VER < (5, 2), "Requires Windows XP") @unittest.skipUnless(WIN_VER < (5, 2), "Requires Windows XP")
......
...@@ -63,6 +63,9 @@ Library ...@@ -63,6 +63,9 @@ Library
Extension Modules Extension Modules
----------------- -----------------
- Issue #2810: Fix cases where the Windows registry API returns
ERROR_MORE_DATA, requiring a re-try in order to get the complete result.
- Issue #8674: Fixed a number of incorrect or undefined-behaviour-inducing - Issue #8674: Fixed a number of incorrect or undefined-behaviour-inducing
overflow checks in the ``audioop`` module. overflow checks in the ``audioop`` module.
......
...@@ -1153,7 +1153,14 @@ PyEnumKey(PyObject *self, PyObject *args) ...@@ -1153,7 +1153,14 @@ PyEnumKey(PyObject *self, PyObject *args)
int index; int index;
long rc; long rc;
PyObject *retStr; PyObject *retStr;
char tmpbuf[256]; /* max key name length is 255 */
/* The Windows docs claim that the max key name length is 255
* characters, plus a terminating nul character. However,
* empirical testing demonstrates that it is possible to
* create a 256 character key that is missing the terminating
* nul. RegEnumKeyEx requires a 257 character buffer to
* retrieve such a key name. */
char tmpbuf[257];
DWORD len = sizeof(tmpbuf); /* includes NULL terminator */ DWORD len = sizeof(tmpbuf); /* includes NULL terminator */
if (!PyArg_ParseTuple(args, "Oi:EnumKey", &obKey, &index)) if (!PyArg_ParseTuple(args, "Oi:EnumKey", &obKey, &index))
...@@ -1180,8 +1187,8 @@ PyEnumValue(PyObject *self, PyObject *args) ...@@ -1180,8 +1187,8 @@ PyEnumValue(PyObject *self, PyObject *args)
long rc; long rc;
char *retValueBuf; char *retValueBuf;
char *retDataBuf; char *retDataBuf;
DWORD retValueSize; DWORD retValueSize, bufValueSize;
DWORD retDataSize; DWORD retDataSize, bufDataSize;
DWORD typ; DWORD typ;
PyObject *obData; PyObject *obData;
PyObject *retVal; PyObject *retVal;
...@@ -1199,6 +1206,8 @@ PyEnumValue(PyObject *self, PyObject *args) ...@@ -1199,6 +1206,8 @@ PyEnumValue(PyObject *self, PyObject *args)
"RegQueryInfoKey"); "RegQueryInfoKey");
++retValueSize; /* include null terminators */ ++retValueSize; /* include null terminators */
++retDataSize; ++retDataSize;
bufDataSize = retDataSize;
bufValueSize = retValueSize;
retValueBuf = (char *)PyMem_Malloc(retValueSize); retValueBuf = (char *)PyMem_Malloc(retValueSize);
if (retValueBuf == NULL) if (retValueBuf == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();
...@@ -1208,16 +1217,33 @@ PyEnumValue(PyObject *self, PyObject *args) ...@@ -1208,16 +1217,33 @@ PyEnumValue(PyObject *self, PyObject *args)
return PyErr_NoMemory(); return PyErr_NoMemory();
} }
Py_BEGIN_ALLOW_THREADS while (1) {
rc = RegEnumValue(hKey, char *tmp;
index, Py_BEGIN_ALLOW_THREADS
retValueBuf, rc = RegEnumValue(hKey,
&retValueSize, index,
NULL, retValueBuf,
&typ, &retValueSize,
(BYTE *)retDataBuf, NULL,
&retDataSize); &typ,
Py_END_ALLOW_THREADS (BYTE *)retDataBuf,
&retDataSize);
Py_END_ALLOW_THREADS
if (rc != ERROR_MORE_DATA)
break;
bufDataSize *= 2;
tmp = (char *)PyMem_Realloc(retDataBuf, bufDataSize);
if (tmp == NULL) {
PyErr_NoMemory();
retVal = NULL;
goto fail;
}
retDataBuf = tmp;
retDataSize = bufDataSize;
retValueSize = bufValueSize;
}
if (rc != ERROR_SUCCESS) { if (rc != ERROR_SUCCESS) {
retVal = PyErr_SetFromWindowsErrWithFunction(rc, retVal = PyErr_SetFromWindowsErrWithFunction(rc,
...@@ -1374,28 +1400,56 @@ PyQueryValue(PyObject *self, PyObject *args) ...@@ -1374,28 +1400,56 @@ PyQueryValue(PyObject *self, PyObject *args)
long rc; long rc;
PyObject *retStr; PyObject *retStr;
char *retBuf; char *retBuf;
long bufSize = 0; DWORD bufSize = 0;
DWORD retSize = 0;
char *tmp;
if (!PyArg_ParseTuple(args, "Oz:QueryValue", &obKey, &subKey)) if (!PyArg_ParseTuple(args, "Oz:QueryValue", &obKey, &subKey))
return NULL; return NULL;
if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE))
return NULL; return NULL;
if ((rc = RegQueryValue(hKey, subKey, NULL, &bufSize))
!= ERROR_SUCCESS) rc = RegQueryValue(hKey, subKey, NULL, &retSize);
if (rc == ERROR_MORE_DATA)
retSize = 256;
else if (rc != ERROR_SUCCESS)
return PyErr_SetFromWindowsErrWithFunction(rc, return PyErr_SetFromWindowsErrWithFunction(rc,
"RegQueryValue"); "RegQueryValue");
retStr = PyString_FromStringAndSize(NULL, bufSize);
if (retStr == NULL) bufSize = retSize;
return NULL; retBuf = (char *) PyMem_Malloc(bufSize);
retBuf = PyString_AS_STRING(retStr); if (retBuf == NULL)
if ((rc = RegQueryValue(hKey, subKey, retBuf, &bufSize)) return PyErr_NoMemory();
!= ERROR_SUCCESS) {
Py_DECREF(retStr); while (1) {
retSize = bufSize;
rc = RegQueryValue(hKey, subKey, retBuf, &retSize);
if (rc != ERROR_MORE_DATA)
break;
bufSize *= 2;
tmp = (char *) PyMem_Realloc(retBuf, bufSize);
if (tmp == NULL) {
PyMem_Free(retBuf);
return PyErr_NoMemory();
}
retBuf = tmp;
}
if (rc != ERROR_SUCCESS) {
PyMem_Free(retBuf);
return PyErr_SetFromWindowsErrWithFunction(rc, return PyErr_SetFromWindowsErrWithFunction(rc,
"RegQueryValue"); "RegQueryValue");
} }
_PyString_Resize(&retStr, strlen(retBuf));
if (retBuf[retSize-1] == '\x00')
retSize--;
retStr = PyString_FromStringAndSize(retBuf, retSize);
if (retStr == NULL) {
PyMem_Free(retBuf);
return NULL;
}
return retStr; return retStr;
} }
...@@ -1407,8 +1461,8 @@ PyQueryValueEx(PyObject *self, PyObject *args) ...@@ -1407,8 +1461,8 @@ PyQueryValueEx(PyObject *self, PyObject *args)
char *valueName; char *valueName;
long rc; long rc;
char *retBuf; char *retBuf, *tmp;
DWORD bufSize = 0; DWORD bufSize = 0, retSize;
DWORD typ; DWORD typ;
PyObject *obData; PyObject *obData;
PyObject *result; PyObject *result;
...@@ -1418,18 +1472,34 @@ PyQueryValueEx(PyObject *self, PyObject *args) ...@@ -1418,18 +1472,34 @@ PyQueryValueEx(PyObject *self, PyObject *args)
if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE))
return NULL; return NULL;
if ((rc = RegQueryValueEx(hKey, valueName,
NULL, NULL, NULL, rc = RegQueryValueEx(hKey, valueName, NULL, NULL, NULL, &bufSize);
&bufSize)) if (rc == ERROR_MORE_DATA)
!= ERROR_SUCCESS) bufSize = 256;
else if (rc != ERROR_SUCCESS)
return PyErr_SetFromWindowsErrWithFunction(rc, return PyErr_SetFromWindowsErrWithFunction(rc,
"RegQueryValueEx"); "RegQueryValueEx");
retBuf = (char *)PyMem_Malloc(bufSize); retBuf = (char *)PyMem_Malloc(bufSize);
if (retBuf == NULL) if (retBuf == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();
if ((rc = RegQueryValueEx(hKey, valueName, NULL,
&typ, (BYTE *)retBuf, &bufSize)) while (1) {
!= ERROR_SUCCESS) { retSize = bufSize;
rc = RegQueryValueEx(hKey, valueName, NULL, &typ,
(BYTE *)retBuf, &retSize);
if (rc != ERROR_MORE_DATA)
break;
bufSize *= 2;
tmp = (char *) PyMem_Realloc(retBuf, bufSize);
if (tmp == NULL) {
PyMem_Free(retBuf);
return PyErr_NoMemory();
}
retBuf = tmp;
}
if (rc != ERROR_SUCCESS) {
PyMem_Free(retBuf); PyMem_Free(retBuf);
return PyErr_SetFromWindowsErrWithFunction(rc, return PyErr_SetFromWindowsErrWithFunction(rc,
"RegQueryValueEx"); "RegQueryValueEx");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment