Skip to content

Commit 2372da9

Browse files
committed
Merge in the main branch
2 parents b3e31c6 + 3908593 commit 2372da9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+317
-306
lines changed

Doc/library/http.server.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ instantiation, of which this module provides three different variants:
287287
specifying its value. Note that, after the send_header calls are done,
288288
:meth:`end_headers` MUST BE called in order to complete the operation.
289289

290+
This method does not reject input containing CRLF sequences.
291+
290292
.. versionchanged:: 3.2
291293
Headers are stored in an internal buffer.
292294

@@ -297,6 +299,8 @@ instantiation, of which this module provides three different variants:
297299
buffered and sent directly the output stream.If the *message* is not
298300
specified, the HTTP message corresponding the response *code* is sent.
299301

302+
This method does not reject *message* containing CRLF sequences.
303+
300304
.. versionadded:: 3.2
301305

302306
.. method:: end_headers()
@@ -555,6 +559,11 @@ Security considerations
555559
requests, this makes it possible for files outside of the specified directory
556560
to be served.
557561

562+
Methods :meth:`BaseHTTPRequestHandler.send_header` and
563+
:meth:`BaseHTTPRequestHandler.send_response_only` assume sanitized input
564+
and does not perform input validation such as checking for the presence of CRLF
565+
sequences. Untrusted input may result in HTTP Header injection attacks.
566+
558567
Earlier versions of Python did not scrub control characters from the
559568
log messages emitted to stderr from ``python -m http.server`` or the
560569
default :class:`BaseHTTPRequestHandler` ``.log_message``

Doc/library/timeit.rst

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,21 +143,24 @@ The module defines three convenience functions and a public class:
143143
timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()
144144

145145

146-
.. method:: Timer.autorange(callback=None)
146+
.. method:: Timer.autorange(callback=None, target_time=None)
147147

148148
Automatically determine how many times to call :meth:`.timeit`.
149149

150150
This is a convenience function that calls :meth:`.timeit` repeatedly
151-
so that the total time >= 0.2 second, returning the eventual
151+
so that the total time >= *Timer.target_time* seconds, returning the eventual
152152
(number of loops, time taken for that number of loops). It calls
153153
:meth:`.timeit` with increasing numbers from the sequence 1, 2, 5,
154-
10, 20, 50, ... until the time taken is at least 0.2 seconds.
154+
10, 20, 50, ... until the time taken is at least *target_time* seconds.
155155

156156
If *callback* is given and is not ``None``, it will be called after
157157
each trial with two arguments: ``callback(number, time_taken)``.
158158

159159
.. versionadded:: 3.6
160160

161+
.. versionchanged:: next
162+
The optional *target_time* parameter was added.
163+
161164

162165
.. method:: Timer.repeat(repeat=5, number=1000000)
163166

@@ -239,6 +242,13 @@ Where the following options are understood:
239242

240243
.. versionadded:: 3.5
241244

245+
.. option:: -t, --target-time=T
246+
247+
if :option:`--number` is 0, the code will run until it takes at
248+
least this many seconds (default: 0.2)
249+
250+
.. versionadded:: next
251+
242252
.. option:: -v, --verbose
243253

244254
print raw timing results; repeat for more digits precision
@@ -254,7 +264,7 @@ similarly.
254264

255265
If :option:`-n` is not given, a suitable number of loops is calculated by trying
256266
increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the total
257-
time is at least 0.2 seconds.
267+
time is at least :option:`--target-time` seconds (default: 0.2).
258268

259269
:func:`default_timer` measurements can be affected by other programs running on
260270
the same machine, so the best thing to do when accurate timing is necessary is

Doc/whatsnew/3.15.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,11 @@ timeit
11251125
:ref:`environment variables <using-on-controlling-color>`.
11261126
(Contributed by Yi Hong in :gh:`139374`.)
11271127

1128+
* Make the target time of :meth:`timeit.Timer.autorange` configurable
1129+
and add ``--target-time`` option to the command-line interface.
1130+
(Contributed by Alessandro Cucci and Miikka Koskinen in :gh:`140283`.)
1131+
1132+
11281133
tkinter
11291134
-------
11301135

@@ -1382,11 +1387,11 @@ Upgraded JIT compiler
13821387

13831388
Results from the `pyperformance <https://github.com/python/pyperformance>`__
13841389
benchmark suite report
1385-
`5-6% <https://doesjitgobrrr.com/run/2026-03-11>`__
1390+
`6-7% <https://www.doesjitgobrrr.com/run/2026-04-01>`__
13861391
geometric mean performance improvement for the JIT over the standard CPython
13871392
interpreter built with all optimizations enabled on x86-64 Linux. On AArch64
13881393
macOS, the JIT has a
1389-
`8-9% <https://doesjitgobrrr.com/run/2026-03-11>`__
1394+
`12-13% <https://www.doesjitgobrrr.com/run/2026-04-01>`__
13901395
speedup over the :ref:`tail calling interpreter <whatsnew314-tail-call-interpreter>`
13911396
with all optimizations enabled. The speedups for JIT
13921397
builds versus no JIT builds range from roughly 15% slowdown to over

Include/internal/pycore_ceval.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,16 +211,16 @@ extern void _PyEval_DeactivateOpCache(void);
211211

212212
/* --- _Py_EnterRecursiveCall() ----------------------------------------- */
213213

214-
static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
214+
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
215215
uintptr_t here_addr = _Py_get_machine_stack_pointer();
216216
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
217-
// Possible overflow if stack pointer is beyond the soft limit.
218-
// _Py_CheckRecursiveCall will check for corner cases and
219-
// report an error if there is an overflow.
217+
// Overflow if stack pointer is between soft limit and the base of the hardware stack.
218+
// If it is below the hardware stack base, assume that we have the wrong stack limits, and do nothing.
219+
// We could have the wrong stack limits because of limited platform support, or user-space threads.
220220
#if _Py_STACK_GROWS_DOWN
221-
return here_addr < _tstate->c_stack_soft_limit;
221+
return here_addr < _tstate->c_stack_soft_limit && here_addr >= _tstate->c_stack_soft_limit - 2 * _PyOS_STACK_MARGIN_BYTES;
222222
#else
223-
return here_addr > _tstate->c_stack_soft_limit;
223+
return here_addr > _tstate->c_stack_soft_limit && here_addr <= _tstate->c_stack_soft_limit + 2 * _PyOS_STACK_MARGIN_BYTES;
224224
#endif
225225
}
226226

@@ -235,7 +235,7 @@ PyAPI_FUNC(int) _Py_CheckRecursiveCallPy(
235235

236236
static inline int _Py_EnterRecursiveCallTstate(PyThreadState *tstate,
237237
const char *where) {
238-
return (_Py_ReachedRecursionLimit(tstate) && _Py_CheckRecursiveCall(tstate, where));
238+
return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where));
239239
}
240240

241241
static inline int _Py_EnterRecursiveCall(const char *where) {
@@ -249,6 +249,8 @@ static inline void _Py_LeaveRecursiveCallTstate(PyThreadState *tstate) {
249249

250250
PyAPI_FUNC(void) _Py_InitializeRecursionLimits(PyThreadState *tstate);
251251

252+
PyAPI_FUNC(int) _Py_ReachedRecursionLimit(PyThreadState *tstate);
253+
252254
// Export for test_peg_generator
253255
PyAPI_FUNC(int) _Py_ReachedRecursionLimitWithMargin(
254256
PyThreadState *tstate,

Include/internal/pycore_pystate.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -306,23 +306,23 @@ _Py_AssertHoldsTstateFunc(const char *func)
306306
#define _Py_AssertHoldsTstate()
307307
#endif
308308

309+
#if !_Py__has_builtin(__builtin_frame_address) && !defined(__GNUC__) && !defined(_MSC_VER)
310+
static uintptr_t return_pointer_as_int(char* p) {
311+
return (uintptr_t)p;
312+
}
313+
#endif
309314

310315
static inline uintptr_t
311316
_Py_get_machine_stack_pointer(void) {
312-
uintptr_t result;
313-
#if !defined(_MSC_VER) && defined(_M_ARM64)
314-
result = __getReg(31);
315-
#elif defined(_MSC_VER) && defined(_M_X64)
316-
result = (uintptr_t)_AddressOfReturnAddress();
317-
#elif defined(__aarch64__)
318-
__asm__ ("mov %0, sp" : "=r" (result));
319-
#elif defined(__x86_64__)
320-
__asm__("{movq %%rsp, %0" : "=r" (result));
317+
#if _Py__has_builtin(__builtin_frame_address) || defined(__GNUC__)
318+
return (uintptr_t)__builtin_frame_address(0);
319+
#elif defined(_MSC_VER)
320+
return (uintptr_t)_AddressOfReturnAddress();
321321
#else
322322
char here;
323-
result = (uintptr_t)&here;
323+
/* Avoid compiler warning about returning stack address */
324+
return return_pointer_as_int(&here);
324325
#endif
325-
return result;
326326
}
327327

328328
static inline intptr_t

Include/internal/pycore_pythonrun.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ extern PyObject * _Py_CompileStringObjectWithModule(
4646
* stack consumption of PyEval_EvalDefault */
4747
#if (defined(Py_DEBUG) \
4848
|| defined(_Py_ADDRESS_SANITIZER) \
49-
|| defined(_Py_THREAD_SANITIZER)) \
50-
|| defined(_Py_UNDEFINED_BEHAVIOR_SANITIZER)
49+
|| defined(_Py_THREAD_SANITIZER))
5150
# define _PyOS_LOG2_STACK_MARGIN 12
5251
#else
5352
# define _PyOS_LOG2_STACK_MARGIN 11

Include/pyport.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -598,11 +598,6 @@ extern "C" {
598598
# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
599599
# endif
600600
# endif
601-
# if __has_feature(undefined_behavior_sanitizer)
602-
# if !defined(_Py_UNDEFINED_BEHAVIOR_SANITIZER)
603-
# define _Py_UNDEFINED_BEHAVIOR_SANITIZER
604-
# endif
605-
# endif
606601
#elif defined(__GNUC__)
607602
# if defined(__SANITIZE_ADDRESS__)
608603
# define _Py_ADDRESS_SANITIZER

Lib/test/support/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
"BrokenIter",
7272
"in_systemd_nspawn_sync_suppressed",
7373
"run_no_yield_async_fn", "run_yielding_async_fn", "async_yield",
74-
"reset_code", "on_github_actions"
74+
"reset_code", "on_github_actions",
75+
"requires_root_user", "requires_non_root_user",
7576
]
7677

7778

@@ -3317,3 +3318,8 @@ def control_characters_c0() -> list[str]:
33173318
C0 control characters defined as the byte range 0x00-0x1F, and 0x7F.
33183319
"""
33193320
return [chr(c) for c in range(0x00, 0x20)] + ["\x7F"]
3321+
3322+
3323+
_ROOT_IN_POSIX = hasattr(os, 'geteuid') and os.geteuid() == 0
3324+
requires_root_user = unittest.skipUnless(_ROOT_IN_POSIX, "test needs root privilege")
3325+
requires_non_root_user = unittest.skipIf(_ROOT_IN_POSIX, "test needs non-root account")

Lib/test/test_itertools.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,38 @@ def keys():
754754
next(g)
755755
next(g) # must pass with address sanitizer
756756

757+
def test_grouper_reentrant_eq_does_not_crash(self):
758+
# regression test for gh-146613
759+
grouper_iter = None
760+
761+
class Key:
762+
__hash__ = None
763+
764+
def __init__(self, do_advance):
765+
self.do_advance = do_advance
766+
767+
def __eq__(self, other):
768+
nonlocal grouper_iter
769+
if self.do_advance:
770+
self.do_advance = False
771+
if grouper_iter is not None:
772+
try:
773+
next(grouper_iter)
774+
except StopIteration:
775+
pass
776+
return NotImplemented
777+
return True
778+
779+
def keyfunc(element):
780+
if element == 0:
781+
return Key(do_advance=True)
782+
return Key(do_advance=False)
783+
784+
g = itertools.groupby(range(4), keyfunc)
785+
key, grouper_iter = next(g)
786+
items = list(grouper_iter)
787+
self.assertEqual(len(items), 1)
788+
757789
def test_filter(self):
758790
self.assertEqual(list(filter(isEven, range(6))), [0,2,4])
759791
self.assertEqual(list(filter(None, [0,1,0,2,0])), [1,2])

Lib/test/test_mailbox.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from test.support import import_helper, warnings_helper
1212
from test.support import os_helper
1313
from test.support import refleak_helper
14+
from test.support import requires_root_user
1415
from test.support import socket_helper
1516
import unittest
1617
import textwrap
@@ -1086,6 +1087,7 @@ def test_permissions_after_flush(self):
10861087

10871088
self.assertEqual(os.stat(self._path).st_mode, mode)
10881089

1090+
@requires_root_user
10891091
@unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
10901092
def test_ownership_after_flush(self):
10911093
# See issue gh-117467
@@ -1108,10 +1110,7 @@ def test_ownership_after_flush(self):
11081110
else:
11091111
self.skipTest("test needs more than one group")
11101112

1111-
try:
1112-
os.chown(self._path, other_uid, other_gid)
1113-
except OSError:
1114-
self.skipTest('test needs root privilege')
1113+
os.chown(self._path, other_uid, other_gid)
11151114
# Change permissions as in test_permissions_after_flush.
11161115
mode = st.st_mode | 0o666
11171116
os.chmod(self._path, mode)

0 commit comments

Comments
 (0)