Skip to content

Commit ec6ed66

Browse files
gh-94930: skipitem() in getargs.c should return non-NULL on error (GH-94931)
(cherry picked from commit 067f0da) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent a914fa9 commit ec6ed66

4 files changed

Lines changed: 37 additions & 8 deletions

File tree

Lib/test/test_getargs2.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -878,9 +878,19 @@ def test_s_hash(self):
878878
def test_s_hash_int(self):
879879
# "s#" without PY_SSIZE_T_CLEAN defined.
880880
from _testcapi import getargs_s_hash_int
881-
self.assertRaises(SystemError, getargs_s_hash_int, "abc")
882-
self.assertRaises(SystemError, getargs_s_hash_int, x=42)
883-
# getargs_s_hash_int() don't raise SystemError because skipitem() is not called.
881+
from _testcapi import getargs_s_hash_int2
882+
buf = bytearray([1, 2])
883+
self.assertRaises(SystemError, getargs_s_hash_int, buf, "abc")
884+
self.assertRaises(SystemError, getargs_s_hash_int, buf, x=42)
885+
self.assertRaises(SystemError, getargs_s_hash_int, buf, x="abc")
886+
self.assertRaises(SystemError, getargs_s_hash_int2, buf, ("abc",))
887+
self.assertRaises(SystemError, getargs_s_hash_int2, buf, x=42)
888+
self.assertRaises(SystemError, getargs_s_hash_int2, buf, x="abc")
889+
buf.append(3) # still mutable -- not locked by a buffer export
890+
# getargs_s_hash_int(buf) may not raise SystemError because skipitem()
891+
# is not called. But it is an implementation detail.
892+
# getargs_s_hash_int(buf)
893+
# getargs_s_hash_int2(buf)
884894

885895
def test_z(self):
886896
from _testcapi import getargs_z
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix ``SystemError`` raised when :c:func:`PyArg_ParseTupleAndKeywords` is
2+
used with ``#`` in ``(...)`` but without ``PY_SSIZE_T_CLEAN`` defined.

Modules/_testcapimodule.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6002,6 +6002,7 @@ settrace_to_record(PyObject *self, PyObject *list)
60026002
static PyObject *negative_dictoffset(PyObject *, PyObject *);
60036003
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
60046004
static PyObject *getargs_s_hash_int(PyObject *, PyObject *, PyObject*);
6005+
static PyObject *getargs_s_hash_int2(PyObject *, PyObject *, PyObject*);
60056006

60066007
static PyMethodDef TestMethods[] = {
60076008
{"raise_exception", raise_exception, METH_VARARGS},
@@ -6116,6 +6117,8 @@ static PyMethodDef TestMethods[] = {
61166117
{"getargs_s_hash", getargs_s_hash, METH_VARARGS},
61176118
{"getargs_s_hash_int", _PyCFunction_CAST(getargs_s_hash_int),
61186119
METH_VARARGS|METH_KEYWORDS},
6120+
{"getargs_s_hash_int2", _PyCFunction_CAST(getargs_s_hash_int2),
6121+
METH_VARARGS|METH_KEYWORDS},
61196122
{"getargs_z", getargs_z, METH_VARARGS},
61206123
{"getargs_z_star", getargs_z_star, METH_VARARGS},
61216124
{"getargs_z_hash", getargs_z_hash, METH_VARARGS},
@@ -7835,11 +7838,27 @@ PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
78357838
static PyObject *
78367839
getargs_s_hash_int(PyObject *self, PyObject *args, PyObject *kwargs)
78377840
{
7838-
static char *keywords[] = {"", "x", NULL};
7841+
static char *keywords[] = {"", "", "x", NULL};
7842+
Py_buffer buf = {NULL};
7843+
const char *s;
7844+
int len;
7845+
int i = 0;
7846+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "w*|s#i", keywords, &buf, &s, &len, &i))
7847+
return NULL;
7848+
PyBuffer_Release(&buf);
7849+
Py_RETURN_NONE;
7850+
}
7851+
7852+
static PyObject *
7853+
getargs_s_hash_int2(PyObject *self, PyObject *args, PyObject *kwargs)
7854+
{
7855+
static char *keywords[] = {"", "", "x", NULL};
7856+
Py_buffer buf = {NULL};
78397857
const char *s;
78407858
int len;
78417859
int i = 0;
7842-
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s#i", keywords, &s, &len, &i))
7860+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "w*|(s#)i", keywords, &buf, &s, &len, &i))
78437861
return NULL;
7862+
PyBuffer_Release(&buf);
78447863
Py_RETURN_NONE;
78457864
}

Python/getargs.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2695,9 +2695,7 @@ skipitem(const char **p_format, va_list *p_va, int flags)
26952695
if (*format == '#') {
26962696
if (p_va != NULL) {
26972697
if (!(flags & FLAG_SIZE_T)) {
2698-
PyErr_SetString(PyExc_SystemError,
2699-
"PY_SSIZE_T_CLEAN macro must be defined for '#' formats");
2700-
return NULL;
2698+
return "PY_SSIZE_T_CLEAN macro must be defined for '#' formats";
27012699
}
27022700
(void) va_arg(*p_va, Py_ssize_t *);
27032701
}

0 commit comments

Comments
 (0)