Skip to content

Commit 3208246

Browse files
committed
Merge remote-tracking branch 'upstream/main' into gh-119132-remove
2 parents 75308de + 60181f4 commit 3208246

8 files changed

Lines changed: 141 additions & 60 deletions

File tree

Doc/library/email.header.rst

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,36 @@ The :mod:`email.header` module also provides the following convenient functions.
178178
Decode a message header value without converting the character set. The header
179179
value is in *header*.
180180

181-
This function returns a list of ``(decoded_string, charset)`` pairs containing
182-
each of the decoded parts of the header. *charset* is ``None`` for non-encoded
183-
parts of the header, otherwise a lower case string containing the name of the
184-
character set specified in the encoded string.
181+
For historical reasons, this function may return either:
185182

186-
Here's an example::
183+
1. A list of pairs containing each of the decoded parts of the header,
184+
``(decoded_bytes, charset)``, where *decoded_bytes* is always an instance of
185+
:class:`bytes`, and *charset* is either:
186+
187+
- A lower case string containing the name of the character set specified.
188+
189+
- ``None`` for non-encoded parts of the header.
190+
191+
2. A list of length 1 containing a pair ``(string, None)``, where
192+
*string* is always an instance of :class:`str`.
193+
194+
An :exc:`email.errors.HeaderParseError` may be raised when certain decoding
195+
errors occur (e.g. a base64 decoding exception).
196+
197+
Here are examples:
187198

188199
>>> from email.header import decode_header
189200
>>> decode_header('=?iso-8859-1?q?p=F6stal?=')
190201
[(b'p\xf6stal', 'iso-8859-1')]
202+
>>> decode_header('unencoded_string')
203+
[('unencoded_string', None)]
204+
>>> decode_header('bar =?utf-8?B?ZsOzbw==?=')
205+
[(b'bar ', None), (b'f\xc3\xb3o', 'utf-8')]
206+
207+
.. note::
208+
209+
This function exists for for backwards compatibility only. For
210+
new code, we recommend using :class:`email.headerregistry.HeaderRegistry`.
191211

192212

193213
.. function:: make_header(decoded_seq, maxlinelen=None, header_name=None, continuation_ws=' ')
@@ -203,3 +223,7 @@ The :mod:`email.header` module also provides the following convenient functions.
203223
:class:`Header` instance. Optional *maxlinelen*, *header_name*, and
204224
*continuation_ws* are as in the :class:`Header` constructor.
205225

226+
.. note::
227+
228+
This function exists for for backwards compatibility only, and is
229+
not recommended for use in new code.

Doc/library/sys.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,13 @@ always available. Unless explicitly noted otherwise, all variables are read-only
19331933
interpreter is pre-release (alpha, beta, or release candidate) then the
19341934
local and remote interpreters must be the same exact version.
19351935

1936+
.. audit-event:: remote_debugger_script script_path
1937+
1938+
When the script is executed in the remote process, an
1939+
:ref:`auditing event <auditing>`
1940+
``sys.remote_debugger_script`` is raised
1941+
with the path in the remote process.
1942+
19361943
.. availability:: Unix, Windows.
19371944
.. versionadded:: 3.14
19381945

Doc/whatsnew/3.14.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,14 @@ concurrent.futures
12591259
buffer.
12601260
(Contributed by Enzo Bonnal and Josh Rosenberg in :gh:`74028`.)
12611261

1262+
configparser
1263+
------------
1264+
1265+
* Security fix: will no longer write config files it cannot read. Attempting
1266+
to :meth:`configparser.ConfigParser.write` keys containing delimiters or
1267+
beginning with the section header pattern will raise a
1268+
:class:`configparser.InvalidWriteError`.
1269+
(Contributed by Jacob Lincoln in :gh:`129270`)
12621270

12631271
contextvars
12641272
-----------

Lib/configparser.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,11 +1218,14 @@ def _convert_to_boolean(self, value):
12181218

12191219
def _validate_key_contents(self, key):
12201220
"""Raises an InvalidWriteError for any keys containing
1221-
delimiters or that match the section header pattern"""
1221+
delimiters or that begins with the section header pattern"""
12221222
if re.match(self.SECTCRE, key):
1223-
raise InvalidWriteError("Cannot write keys matching section pattern")
1224-
if any(delim in key for delim in self._delimiters):
1225-
raise InvalidWriteError("Cannot write key that contains delimiters")
1223+
raise InvalidWriteError(
1224+
f"Cannot write key {key}; begins with section pattern")
1225+
for delim in self._delimiters:
1226+
if delim in key:
1227+
raise InvalidWriteError(
1228+
f"Cannot write key {key}; contains delimiter {delim}")
12261229

12271230
def _validate_value_types(self, *, section="", option="", value=""):
12281231
"""Raises a TypeError for illegal non-string values.

Lib/email/header.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,22 @@
5959
def decode_header(header):
6060
"""Decode a message header value without converting charset.
6161
62-
Returns a list of (string, charset) pairs containing each of the decoded
63-
parts of the header. Charset is None for non-encoded parts of the header,
64-
otherwise a lower-case string containing the name of the character set
65-
specified in the encoded string.
62+
For historical reasons, this function may return either:
63+
64+
1. A list of length 1 containing a pair (str, None).
65+
2. A list of (bytes, charset) pairs containing each of the decoded
66+
parts of the header. Charset is None for non-encoded parts of the header,
67+
otherwise a lower-case string containing the name of the character set
68+
specified in the encoded string.
6669
6770
header may be a string that may or may not contain RFC2047 encoded words,
6871
or it may be a Header object.
6972
7073
An email.errors.HeaderParseError may be raised when certain decoding error
7174
occurs (e.g. a base64 decoding exception).
75+
76+
This function exists for backwards compatibility only. For new code, we
77+
recommend using email.headerregistry.HeaderRegistry instead.
7278
"""
7379
# If it is a Header object, we can just return the encoded chunks.
7480
if hasattr(header, '_chunks'):
@@ -161,6 +167,9 @@ def make_header(decoded_seq, maxlinelen=None, header_name=None,
161167
This function takes one of those sequence of pairs and returns a Header
162168
instance. Optional maxlinelen, header_name, and continuation_ws are as in
163169
the Header constructor.
170+
171+
This function exists for backwards compatibility only, and is not
172+
recommended for use in new code.
164173
"""
165174
h = Header(maxlinelen=maxlinelen, header_name=header_name,
166175
continuation_ws=continuation_ws)

Lib/test/test_email/test_email.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2568,6 +2568,18 @@ def test_multiline_header(self):
25682568
self.assertEqual(str(make_header(decode_header(s))),
25692569
'"Müller T" <T.Mueller@xxx.com>')
25702570

2571+
def test_unencoded_ascii(self):
2572+
# bpo-22833/gh-67022: returns [(str, None)] rather than [(bytes, None)]
2573+
s = 'header without encoded words'
2574+
self.assertEqual(decode_header(s),
2575+
[('header without encoded words', None)])
2576+
2577+
def test_unencoded_utf8(self):
2578+
# bpo-22833/gh-67022: returns [(str, None)] rather than [(bytes, None)]
2579+
s = 'header with unexpected non ASCII caract\xe8res'
2580+
self.assertEqual(decode_header(s),
2581+
[('header with unexpected non ASCII caract\xe8res', None)])
2582+
25712583

25722584
# Test the MIMEMessage class
25732585
class TestMIMEMessage(TestEmailBase):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:class:`configparser`'s error message when attempting to write an invalid key is now more helpful.

Modules/_remote_debugging_module.c

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
* ============================================================================ */
4040

4141
#define GET_MEMBER(type, obj, offset) (*(type*)((char*)(obj) + (offset)))
42+
#define CLEAR_PTR_TAG(ptr) (((uintptr_t)(ptr) & ~Py_TAG_BITS))
43+
#define GET_MEMBER_NO_TAG(type, obj, offset) (type)(CLEAR_PTR_TAG(*(type*)((char*)(obj) + (offset))))
4244

4345
/* Size macros for opaque buffers */
4446
#define SIZEOF_BYTES_OBJ sizeof(PyBytesObject)
@@ -212,6 +214,8 @@ typedef struct {
212214
#endif
213215
} RemoteUnwinderObject;
214216

217+
#define RemoteUnwinder_CAST(op) ((RemoteUnwinderObject *)(op))
218+
215219
typedef struct
216220
{
217221
int lineno;
@@ -243,6 +247,13 @@ module _remote_debugging
243247
* FORWARD DECLARATIONS
244248
* ============================================================================ */
245249

250+
static inline int
251+
is_frame_valid(
252+
RemoteUnwinderObject *unwinder,
253+
uintptr_t frame_addr,
254+
uintptr_t code_object_addr
255+
);
256+
246257
static int
247258
parse_tasks_in_set(
248259
RemoteUnwinderObject *unwinder,
@@ -734,8 +745,7 @@ parse_task_name(
734745
return NULL;
735746
}
736747

737-
uintptr_t task_name_addr = GET_MEMBER(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_name);
738-
task_name_addr &= ~Py_TAG_BITS;
748+
uintptr_t task_name_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_name);
739749

740750
// The task name can be a long or a string so we need to check the type
741751
char task_name_obj[SIZEOF_PYOBJECT];
@@ -798,8 +808,7 @@ static int parse_task_awaited_by(
798808
return -1;
799809
}
800810

801-
uintptr_t task_ab_addr = GET_MEMBER(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_awaited_by);
802-
task_ab_addr &= ~Py_TAG_BITS;
811+
uintptr_t task_ab_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_awaited_by);
803812

804813
if ((void*)task_ab_addr == NULL) {
805814
return 0;
@@ -849,8 +858,7 @@ handle_yield_from_frame(
849858
return -1;
850859
}
851860

852-
uintptr_t stackpointer_addr = GET_MEMBER(uintptr_t, iframe, unwinder->debug_offsets.interpreter_frame.stackpointer);
853-
stackpointer_addr &= ~Py_TAG_BITS;
861+
uintptr_t stackpointer_addr = GET_MEMBER_NO_TAG(uintptr_t, iframe, unwinder->debug_offsets.interpreter_frame.stackpointer);
854862

855863
if ((void*)stackpointer_addr != NULL) {
856864
uintptr_t gi_await_addr;
@@ -917,6 +925,11 @@ parse_coro_chain(
917925
return -1;
918926
}
919927

928+
int8_t frame_state = GET_MEMBER(int8_t, gen_object, unwinder->debug_offsets.gen_object.gi_frame_state);
929+
if (frame_state == FRAME_CLEARED) {
930+
return 0;
931+
}
932+
920933
uintptr_t gen_type_addr = GET_MEMBER(uintptr_t, gen_object, unwinder->debug_offsets.pyobject.ob_type);
921934

922935
PyObject* name = NULL;
@@ -936,7 +949,7 @@ parse_coro_chain(
936949
}
937950
Py_DECREF(name);
938951

939-
if (GET_MEMBER(int8_t, gen_object, unwinder->debug_offsets.gen_object.gi_frame_state) == FRAME_SUSPENDED_YIELD_FROM) {
952+
if (frame_state == FRAME_SUSPENDED_YIELD_FROM) {
940953
return handle_yield_from_frame(unwinder, gi_iframe_addr, gen_type_addr, render_to);
941954
}
942955

@@ -981,8 +994,7 @@ create_task_result(
981994
goto error;
982995
}
983996

984-
coro_addr = GET_MEMBER(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_coro);
985-
coro_addr &= ~Py_TAG_BITS;
997+
coro_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_coro);
986998

987999
if ((void*)coro_addr != NULL) {
9881000
if (parse_coro_chain(unwinder, coro_addr, call_stack) < 0) {
@@ -1816,10 +1828,10 @@ parse_frame_from_chunks(
18161828

18171829
char *frame = (char *)frame_ptr;
18181830
*previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous);
1819-
1820-
if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) >= FRAME_OWNED_BY_INTERPRETER ||
1821-
!GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable)) {
1822-
return 0;
1831+
uintptr_t code_object = GET_MEMBER_NO_TAG(uintptr_t, frame_ptr, unwinder->debug_offsets.interpreter_frame.executable);
1832+
int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, code_object);
1833+
if (frame_valid != 1) {
1834+
return frame_valid;
18231835
}
18241836

18251837
uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr);
@@ -1832,9 +1844,7 @@ parse_frame_from_chunks(
18321844
}
18331845
#endif
18341846

1835-
return parse_code_object(
1836-
unwinder, result, GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable),
1837-
instruction_pointer, previous_frame, tlbc_index);
1847+
return parse_code_object(unwinder, result, code_object, instruction_pointer, previous_frame, tlbc_index);
18381848
}
18391849

18401850
/* ============================================================================
@@ -2077,6 +2087,33 @@ find_running_task_and_coro(
20772087
* FRAME PARSING FUNCTIONS
20782088
* ============================================================================ */
20792089

2090+
static inline int
2091+
is_frame_valid(
2092+
RemoteUnwinderObject *unwinder,
2093+
uintptr_t frame_addr,
2094+
uintptr_t code_object_addr
2095+
) {
2096+
if ((void*)code_object_addr == NULL) {
2097+
return 0;
2098+
}
2099+
2100+
void* frame = (void*)frame_addr;
2101+
2102+
if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_CSTACK ||
2103+
GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_INTERPRETER) {
2104+
return 0; // C frame
2105+
}
2106+
2107+
if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_GENERATOR
2108+
&& GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_THREAD) {
2109+
PyErr_Format(PyExc_RuntimeError, "Unhandled frame owner %d.\n",
2110+
GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner));
2111+
set_exception_cause(unwinder, PyExc_RuntimeError, "Unhandled frame owner type in async frame");
2112+
return -1;
2113+
}
2114+
return 1;
2115+
}
2116+
20802117
static int
20812118
parse_frame_object(
20822119
RemoteUnwinderObject *unwinder,
@@ -2098,13 +2135,10 @@ parse_frame_object(
20982135
}
20992136

21002137
*previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous);
2101-
2102-
if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) >= FRAME_OWNED_BY_INTERPRETER) {
2103-
return 0;
2104-
}
2105-
2106-
if ((void*)GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable) == NULL) {
2107-
return 0;
2138+
uintptr_t code_object = GET_MEMBER_NO_TAG(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable);
2139+
int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, code_object);
2140+
if (frame_valid != 1) {
2141+
return frame_valid;
21082142
}
21092143

21102144
uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr);
@@ -2117,9 +2151,7 @@ parse_frame_object(
21172151
}
21182152
#endif
21192153

2120-
return parse_code_object(
2121-
unwinder, result, GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable),
2122-
instruction_pointer, previous_frame, tlbc_index);
2154+
return parse_code_object(unwinder, result, code_object,instruction_pointer, previous_frame, tlbc_index);
21232155
}
21242156

21252157
static int
@@ -2144,26 +2176,10 @@ parse_async_frame_object(
21442176
}
21452177

21462178
*previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous);
2147-
2148-
*code_object = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable);
2149-
// Strip tag bits for consistent comparison
2150-
*code_object &= ~Py_TAG_BITS;
2151-
assert(code_object != NULL);
2152-
if ((void*)*code_object == NULL) {
2153-
return 0;
2154-
}
2155-
2156-
if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_CSTACK ||
2157-
GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_INTERPRETER) {
2158-
return 0; // C frame
2159-
}
2160-
2161-
if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_GENERATOR
2162-
&& GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_THREAD) {
2163-
PyErr_Format(PyExc_RuntimeError, "Unhandled frame owner %d.\n",
2164-
GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner));
2165-
set_exception_cause(unwinder, PyExc_RuntimeError, "Unhandled frame owner type in async frame");
2166-
return -1;
2179+
*code_object = GET_MEMBER_NO_TAG(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable);
2180+
int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, *code_object);
2181+
if (frame_valid != 1) {
2182+
return frame_valid;
21672183
}
21682184

21692185
uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr);
@@ -2899,8 +2915,9 @@ static PyMethodDef RemoteUnwinder_methods[] = {
28992915
};
29002916

29012917
static void
2902-
RemoteUnwinder_dealloc(RemoteUnwinderObject *self)
2918+
RemoteUnwinder_dealloc(PyObject *op)
29032919
{
2920+
RemoteUnwinderObject *self = RemoteUnwinder_CAST(op);
29042921
PyTypeObject *tp = Py_TYPE(self);
29052922
if (self->code_object_cache) {
29062923
_Py_hashtable_destroy(self->code_object_cache);

0 commit comments

Comments
 (0)