Skip to content

Commit bec62dc

Browse files
committed
Merge branch 'main' into windows-add-sys-abiflags
2 parents 1b8439b + c432d01 commit bec62dc

File tree

10 files changed

+93
-16
lines changed

10 files changed

+93
-16
lines changed

Lib/email/_policybase.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""
55

66
import abc
7+
import re
78
from email import header
89
from email import charset as _charset
910
from email.utils import _has_surrogates
@@ -14,6 +15,14 @@
1415
'compat32',
1516
]
1617

18+
# validation regex from RFC 5322, equivalent to pattern re.compile("[!-9;-~]+$")
19+
valid_header_name_re = re.compile("[\041-\071\073-\176]+$")
20+
21+
def validate_header_name(name):
22+
# Validate header name according to RFC 5322
23+
if not valid_header_name_re.match(name):
24+
raise ValueError(
25+
f"Header field name contains invalid characters: {name!r}")
1726

1827
class _PolicyBase:
1928

@@ -314,6 +323,7 @@ def header_store_parse(self, name, value):
314323
"""+
315324
The name and value are returned unmodified.
316325
"""
326+
validate_header_name(name)
317327
return (name, value)
318328

319329
def header_fetch_parse(self, name, value):

Lib/email/policy.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44

55
import re
66
import sys
7-
from email._policybase import Policy, Compat32, compat32, _extend_docstrings
7+
from email._policybase import (
8+
Compat32,
9+
Policy,
10+
_extend_docstrings,
11+
compat32,
12+
validate_header_name
13+
)
814
from email.utils import _has_surrogates
915
from email.headerregistry import HeaderRegistry as HeaderRegistry
1016
from email.contentmanager import raw_data_manager
@@ -138,6 +144,7 @@ def header_store_parse(self, name, value):
138144
CR or LF characters.
139145
140146
"""
147+
validate_header_name(name)
141148
if hasattr(value, 'name') and value.name.lower() == name.lower():
142149
return (name, value)
143150
if isinstance(value, str) and len(value.splitlines())>1:

Lib/test/test_ctypes/test_dlerror.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def test_null_dlsym(self):
120120
# Assert that the IFUNC was called
121121
self.assertEqual(os.read(pipe_r, 2), b'OK')
122122

123-
123+
@test.support.thread_unsafe('setlocale is not thread-safe')
124124
@unittest.skipUnless(os.name != 'nt', 'test requires dlerror() calls')
125125
class TestLocalization(unittest.TestCase):
126126

Lib/test/test_email/test_email.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,31 @@ def test_nonascii_add_header_with_tspecial(self):
728728
"attachment; filename*=utf-8''Fu%C3%9Fballer%20%5Bfilename%5D.ppt",
729729
msg['Content-Disposition'])
730730

731+
def test_invalid_header_names(self):
732+
invalid_headers = [
733+
('Invalid Header', 'contains space'),
734+
('Tab\tHeader', 'contains tab'),
735+
('Colon:Header', 'contains colon'),
736+
('', 'Empty name'),
737+
(' LeadingSpace', 'starts with space'),
738+
('TrailingSpace ', 'ends with space'),
739+
('Header\x7F', 'Non-ASCII character'),
740+
('Header\x80', 'Extended ASCII'),
741+
]
742+
for policy in (email.policy.default, email.policy.compat32):
743+
for setter in (Message.__setitem__, Message.add_header):
744+
for name, value in invalid_headers:
745+
self.do_test_invalid_header_names(
746+
policy, setter,name, value)
747+
748+
def do_test_invalid_header_names(self, policy, setter, name, value):
749+
with self.subTest(policy=policy, setter=setter, name=name, value=value):
750+
message = Message(policy=policy)
751+
pattern = r'(?i)(?=.*invalid)(?=.*header)(?=.*name)'
752+
with self.assertRaisesRegex(ValueError, pattern) as cm:
753+
setter(message, name, value)
754+
self.assertIn(f"{name!r}", str(cm.exception))
755+
731756
def test_binary_quopri_payload(self):
732757
for charset in ('latin-1', 'ascii'):
733758
msg = Message()

Lib/test/test_email/test_message.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,30 @@ def test_folding_with_long_nospace_http_policy_1(self):
10041004
parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default)
10051005
self.assertEqual(parsed_msg['Message-ID'], m['Message-ID'])
10061006

1007+
def test_invalid_header_names(self):
1008+
invalid_headers = [
1009+
('Invalid Header', 'contains space'),
1010+
('Tab\tHeader', 'contains tab'),
1011+
('Colon:Header', 'contains colon'),
1012+
('', 'Empty name'),
1013+
(' LeadingSpace', 'starts with space'),
1014+
('TrailingSpace ', 'ends with space'),
1015+
('Header\x7F', 'Non-ASCII character'),
1016+
('Header\x80', 'Extended ASCII'),
1017+
]
1018+
for email_policy in (policy.default, policy.compat32):
1019+
for setter in (EmailMessage.__setitem__, EmailMessage.add_header):
1020+
for name, value in invalid_headers:
1021+
self.do_test_invalid_header_names(email_policy, setter, name, value)
1022+
1023+
def do_test_invalid_header_names(self, policy, setter, name, value):
1024+
with self.subTest(policy=policy, setter=setter, name=name, value=value):
1025+
message = EmailMessage(policy=policy)
1026+
pattern = r'(?i)(?=.*invalid)(?=.*header)(?=.*name)'
1027+
with self.assertRaisesRegex(ValueError, pattern) as cm:
1028+
setter(message, name, value)
1029+
self.assertIn(f"{name!r}", str(cm.exception))
1030+
10071031
def test_get_body_malformed(self):
10081032
"""test for bpo-42892"""
10091033
msg = textwrap.dedent("""\

Lib/test/test_timeit.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,7 @@ def test_main_negative_reps(self):
297297
@unittest.skipIf(sys.flags.optimize >= 2, "need __doc__")
298298
def test_main_help(self):
299299
s = self.run_main(switches=['-h'])
300-
# Note: It's not clear that the trailing space was intended as part of
301-
# the help text, but since it's there, check for it.
302-
self.assertEqual(s, timeit.__doc__ + ' ')
300+
self.assertEqual(s, timeit.__doc__)
303301

304302
def test_main_verbose(self):
305303
s = self.run_main(switches=['-v'])

Lib/timeit.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
timeit(string, string) -> float
4545
repeat(string, string) -> list
4646
default_timer() -> float
47-
4847
"""
4948

5049
import gc
@@ -302,7 +301,7 @@ def main(args=None, *, _wrap_timer=None):
302301
precision += 1
303302
verbose += 1
304303
if o in ("-h", "--help"):
305-
print(__doc__, end=' ')
304+
print(__doc__, end="")
306305
return 0
307306
setup = "\n".join(setup) or "pass"
308307

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
When headers are added to :class:`email.message.Message` objects, either through
2+
:meth:`email.message.Message.__setitem__` or :meth:`email.message.Message.add_header`,
3+
the field name is now validated according to :rfc:`RFC 5322, Section 2.2 <5322#section-2.2>`
4+
and a :exc:`ValueError` is raised if the field name contains any invalid characters.

Modules/_ctypes/_ctypes.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4406,7 +4406,7 @@ _build_result(PyObject *result, PyObject *callargs,
44064406
}
44074407

44084408
static PyObject *
4409-
PyCFuncPtr_call(PyObject *op, PyObject *inargs, PyObject *kwds)
4409+
PyCFuncPtr_call_lock_held(PyObject *op, PyObject *inargs, PyObject *kwds)
44104410
{
44114411
PyObject *restype;
44124412
PyObject *converters;
@@ -4544,6 +4544,16 @@ PyCFuncPtr_call(PyObject *op, PyObject *inargs, PyObject *kwds)
45444544
outmask, inoutmask, numretvals);
45454545
}
45464546

4547+
static PyObject *
4548+
PyCFuncPtr_call(PyObject *op, PyObject *inargs, PyObject *kwds)
4549+
{
4550+
PyObject *result;
4551+
Py_BEGIN_CRITICAL_SECTION(op);
4552+
result = PyCFuncPtr_call_lock_held(op, inargs, kwds);
4553+
Py_END_CRITICAL_SECTION();
4554+
return result;
4555+
}
4556+
45474557
static int
45484558
PyCFuncPtr_traverse(PyObject *op, visitproc visit, void *arg)
45494559
{
@@ -6093,14 +6103,19 @@ _ctypes_mod_exec(PyObject *mod)
60936103
}
60946104

60956105
#ifdef WORDS_BIGENDIAN
6096-
st->swapped_suffix = PyUnicode_InternFromString("_le");
6106+
st->swapped_suffix = PyUnicode_InternFromString("_le");
60976107
#else
6098-
st->swapped_suffix = PyUnicode_InternFromString("_be");
6108+
st->swapped_suffix = PyUnicode_InternFromString("_be");
60996109
#endif
61006110
if (st->swapped_suffix == NULL) {
61016111
return -1;
61026112
}
61036113

6114+
st->error_object_name = PyUnicode_InternFromString("ctypes.error_object");
6115+
if (st->error_object_name == NULL) {
6116+
return -1;
6117+
}
6118+
61046119
if (_ctypes_add_types(mod) < 0) {
61056120
return -1;
61066121
}

Modules/_ctypes/callproc.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,7 @@ _ctypes_get_errobj(ctypes_state *st, int **pspace)
164164
"cannot get thread state");
165165
return NULL;
166166
}
167-
if (st->error_object_name == NULL) {
168-
st->error_object_name = PyUnicode_InternFromString("ctypes.error_object");
169-
if (st->error_object_name == NULL) {
170-
return NULL;
171-
}
172-
}
167+
assert(st->error_object_name != NULL);
173168
if (PyDict_GetItemRef(dict, st->error_object_name, &errobj) < 0) {
174169
return NULL;
175170
}

0 commit comments

Comments
 (0)