Skip to content

Commit 7824cbb

Browse files
Merge branch '3.13' into backport-2ccd6aa-3.13
2 parents c26de17 + 900dc2b commit 7824cbb

File tree

7 files changed

+193
-13
lines changed

7 files changed

+193
-13
lines changed

Lib/test/test_gettext.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
bmsgd2luayAoaW4gIm15IG90aGVyIGNvbnRleHQiKQB3aW5rIHdpbmsA
4040
'''
4141

42+
# .mo file with an invalid magic number
43+
GNU_MO_DATA_BAD_MAGIC_NUMBER = base64.b64encode(b'ABCD')
44+
4245
# This data contains an invalid major version number (5)
4346
# An unexpected major version number should be treated as an error when
4447
# parsing a .mo file
@@ -87,6 +90,32 @@
8790
ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA==
8891
'''
8992

93+
# Corrupt .mo file
94+
# Generated from
95+
#
96+
# msgid "foo"
97+
# msgstr "bar"
98+
#
99+
# with msgfmt --no-hash
100+
#
101+
# The translation offset is changed to 0xFFFFFFFF,
102+
# making it larger than the file size, which should
103+
# raise an error when parsing.
104+
GNU_MO_DATA_CORRUPT = base64.b64encode(bytes([
105+
0xDE, 0x12, 0x04, 0x95, # Magic
106+
0x00, 0x00, 0x00, 0x00, # Version
107+
0x01, 0x00, 0x00, 0x00, # Message count
108+
0x1C, 0x00, 0x00, 0x00, # Message offset
109+
0x24, 0x00, 0x00, 0x00, # Translation offset
110+
0x00, 0x00, 0x00, 0x00, # Hash table size
111+
0x2C, 0x00, 0x00, 0x00, # Hash table offset
112+
0x03, 0x00, 0x00, 0x00, # 1st message length
113+
0x2C, 0x00, 0x00, 0x00, # 1st message offset
114+
0x03, 0x00, 0x00, 0x00, # 1st trans length
115+
0xFF, 0xFF, 0xFF, 0xFF, # 1st trans offset (Modified to make it invalid)
116+
0x66, 0x6F, 0x6F, 0x00, # Message data
117+
0x62, 0x61, 0x72, 0x00, # Message data
118+
]))
90119

91120
UMO_DATA = b'''\
92121
3hIElQAAAAADAAAAHAAAADQAAAAAAAAAAAAAAAAAAABMAAAABAAAAE0AAAAQAAAAUgAAAA8BAABj
@@ -111,8 +140,10 @@
111140

112141
LOCALEDIR = os.path.join('xx', 'LC_MESSAGES')
113142
MOFILE = os.path.join(LOCALEDIR, 'gettext.mo')
143+
MOFILE_BAD_MAGIC_NUMBER = os.path.join(LOCALEDIR, 'gettext_bad_magic_number.mo')
114144
MOFILE_BAD_MAJOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_major_version.mo')
115145
MOFILE_BAD_MINOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_minor_version.mo')
146+
MOFILE_CORRUPT = os.path.join(LOCALEDIR, 'gettext_corrupt.mo')
116147
UMOFILE = os.path.join(LOCALEDIR, 'ugettext.mo')
117148
MMOFILE = os.path.join(LOCALEDIR, 'metadata.mo')
118149

@@ -131,10 +162,14 @@ def setUpClass(cls):
131162
os.makedirs(LOCALEDIR)
132163
with open(MOFILE, 'wb') as fp:
133164
fp.write(base64.decodebytes(GNU_MO_DATA))
165+
with open(MOFILE_BAD_MAGIC_NUMBER, 'wb') as fp:
166+
fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MAGIC_NUMBER))
134167
with open(MOFILE_BAD_MAJOR_VERSION, 'wb') as fp:
135168
fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MAJOR_VERSION))
136169
with open(MOFILE_BAD_MINOR_VERSION, 'wb') as fp:
137170
fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MINOR_VERSION))
171+
with open(MOFILE_CORRUPT, 'wb') as fp:
172+
fp.write(base64.decodebytes(GNU_MO_DATA_CORRUPT))
138173
with open(UMOFILE, 'wb') as fp:
139174
fp.write(base64.decodebytes(UMO_DATA))
140175
with open(MMOFILE, 'wb') as fp:
@@ -249,6 +284,16 @@ def test_bindtextdomain(self):
249284
def test_textdomain(self):
250285
self.assertEqual(gettext.textdomain(), 'gettext')
251286

287+
def test_bad_magic_number(self):
288+
with open(MOFILE_BAD_MAGIC_NUMBER, 'rb') as fp:
289+
with self.assertRaises(OSError) as cm:
290+
gettext.GNUTranslations(fp)
291+
292+
exception = cm.exception
293+
self.assertEqual(exception.errno, 0)
294+
self.assertEqual(exception.strerror, "Bad magic number")
295+
self.assertEqual(exception.filename, MOFILE_BAD_MAGIC_NUMBER)
296+
252297
def test_bad_major_version(self):
253298
with open(MOFILE_BAD_MAJOR_VERSION, 'rb') as fp:
254299
with self.assertRaises(OSError) as cm:
@@ -264,6 +309,16 @@ def test_bad_minor_version(self):
264309
# Check that no error is thrown with a bad minor version number
265310
gettext.GNUTranslations(fp)
266311

312+
def test_corrupt_file(self):
313+
with open(MOFILE_CORRUPT, 'rb') as fp:
314+
with self.assertRaises(OSError) as cm:
315+
gettext.GNUTranslations(fp)
316+
317+
exception = cm.exception
318+
self.assertEqual(exception.errno, 0)
319+
self.assertEqual(exception.strerror, "File is corrupt")
320+
self.assertEqual(exception.filename, MOFILE_CORRUPT)
321+
267322
def test_some_translations(self):
268323
eq = self.assertEqual
269324
# test some translations
@@ -768,6 +823,76 @@ def test_expand_lang(self):
768823
self.assertEqual(gettext._expand_lang(locale), expanded)
769824

770825

826+
class FindTestCase(unittest.TestCase):
827+
828+
def setUp(self):
829+
self.env = self.enterContext(os_helper.EnvironmentVarGuard())
830+
self.tempdir = self.enterContext(os_helper.temp_cwd())
831+
832+
for key in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
833+
self.env.unset(key)
834+
835+
def create_mo_file(self, lang):
836+
locale_dir = os.path.join(self.tempdir, "locale")
837+
mofile_dir = os.path.join(locale_dir, lang, "LC_MESSAGES")
838+
os.makedirs(mofile_dir)
839+
mo_file = os.path.join(mofile_dir, "mofile.mo")
840+
with open(mo_file, "wb") as f:
841+
f.write(GNU_MO_DATA)
842+
return mo_file
843+
844+
def test_find_with_env_vars(self):
845+
# test that find correctly finds the environment variables
846+
# when languages are not supplied
847+
mo_file = self.create_mo_file("ga_IE")
848+
for var in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
849+
self.env.set(var, 'ga_IE')
850+
result = gettext.find("mofile",
851+
localedir=os.path.join(self.tempdir, "locale"))
852+
self.assertEqual(result, mo_file)
853+
self.env.unset(var)
854+
855+
def test_find_with_languages(self):
856+
# test that passed languages are used
857+
self.env.set('LANGUAGE', 'pt_BR')
858+
mo_file = self.create_mo_file("ga_IE")
859+
860+
result = gettext.find("mofile",
861+
localedir=os.path.join(self.tempdir, "locale"),
862+
languages=['ga_IE'])
863+
self.assertEqual(result, mo_file)
864+
865+
@unittest.mock.patch('gettext._expand_lang')
866+
def test_find_with_no_lang(self, patch_expand_lang):
867+
# no language can be found
868+
gettext.find('foo')
869+
patch_expand_lang.assert_called_with('C')
870+
871+
@unittest.mock.patch('gettext._expand_lang')
872+
def test_find_with_c(self, patch_expand_lang):
873+
# 'C' is already in languages
874+
self.env.set('LANGUAGE', 'C')
875+
gettext.find('foo')
876+
patch_expand_lang.assert_called_with('C')
877+
878+
def test_find_all(self):
879+
# test that all are returned when all is set
880+
paths = []
881+
for lang in ["ga_IE", "es_ES"]:
882+
paths.append(self.create_mo_file(lang))
883+
result = gettext.find('mofile',
884+
localedir=os.path.join(self.tempdir, "locale"),
885+
languages=["ga_IE", "es_ES"], all=True)
886+
self.assertEqual(sorted(result), sorted(paths))
887+
888+
def test_find_deduplication(self):
889+
# test that find removes duplicate languages
890+
mo_file = [self.create_mo_file('ga_IE')]
891+
result = gettext.find("mofile", localedir=os.path.join(self.tempdir, "locale"),
892+
languages=['ga_IE', 'ga_IE'], all=True)
893+
self.assertEqual(result, mo_file)
894+
895+
771896
class MiscTestCase(unittest.TestCase):
772897
def test__all__(self):
773898
support.check__all__(self, gettext,

Lib/test/test_syntax.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,6 +2470,25 @@ def test_continuation_bad_indentation(self):
24702470

24712471
self.assertRaises(IndentationError, exec, code)
24722472

2473+
@support.cpython_only
2474+
def test_disallowed_type_param_names(self):
2475+
# See gh-128632
2476+
2477+
self._check_error(f"class A[__classdict__]: pass",
2478+
f"reserved name '__classdict__' cannot be used for type parameter")
2479+
self._check_error(f"def f[__classdict__](): pass",
2480+
f"reserved name '__classdict__' cannot be used for type parameter")
2481+
self._check_error(f"type T[__classdict__] = tuple[__classdict__]",
2482+
f"reserved name '__classdict__' cannot be used for type parameter")
2483+
2484+
# These compilations are here to make sure __class__, __classcell__ and __classdictcell__
2485+
# don't break in the future like __classdict__ did in this case.
2486+
for name in ('__class__', '__classcell__', '__classdictcell__'):
2487+
compile(f"""
2488+
class A:
2489+
class B[{name}]: pass
2490+
""", "<testcase>", mode="exec")
2491+
24732492
@support.cpython_only
24742493
def test_nested_named_except_blocks(self):
24752494
code = ""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Disallow ``__classdict__`` as the name of a type parameter. Using this
2+
name would previously crash the interpreter in some circumstances.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix possible use of :mod:`socket` address structures with uninitialized
2+
members. Now all structure members are initialized with zeroes by default.

Modules/socketmodule.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,6 +1779,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
17791779
assert(path.len >= 0);
17801780

17811781
struct sockaddr_un* addr = &addrbuf->un;
1782+
memset(addr, 0, sizeof(struct sockaddr_un));
17821783
#ifdef __linux__
17831784
if (path.len == 0 || *(const char *)path.buf == 0) {
17841785
/* Linux abstract namespace extension:
@@ -1822,6 +1823,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
18221823
{
18231824
int pid, groups;
18241825
struct sockaddr_nl* addr = &addrbuf->nl;
1826+
memset(addr, 0, sizeof(struct sockaddr_nl));
18251827
if (!PyTuple_Check(args)) {
18261828
PyErr_Format(
18271829
PyExc_TypeError,
@@ -1849,6 +1851,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
18491851
{
18501852
unsigned int node, port;
18511853
struct sockaddr_qrtr* addr = &addrbuf->sq;
1854+
memset(addr, 0, sizeof(struct sockaddr_qrtr));
18521855
if (!PyTuple_Check(args)) {
18531856
PyErr_Format(
18541857
PyExc_TypeError,
@@ -1926,6 +1929,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
19261929
return 0;
19271930
}
19281931
struct sockaddr_in* addr = &addrbuf->in;
1932+
memset(addr, 0, sizeof(struct sockaddr_in));
19291933
result = setipaddr(s->state, host.buf, (struct sockaddr *)addr,
19301934
sizeof(*addr), AF_INET);
19311935
idna_cleanup(&host);
@@ -1971,6 +1975,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
19711975
return 0;
19721976
}
19731977
struct sockaddr_in6* addr = &addrbuf->in6;
1978+
memset(addr, 0, sizeof(struct sockaddr_in6));
19741979
result = setipaddr(s->state, host.buf, (struct sockaddr *)addr,
19751980
sizeof(*addr), AF_INET6);
19761981
idna_cleanup(&host);
@@ -2028,6 +2033,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
20282033
{
20292034
const char *straddr;
20302035
struct sockaddr_rc *addr = &addrbuf->bt_rc;
2036+
memset(addr, 0, sizeof(struct sockaddr_rc));
20312037
_BT_RC_MEMB(addr, family) = AF_BLUETOOTH;
20322038
#ifdef MS_WINDOWS
20332039
unsigned long channel;
@@ -2054,6 +2060,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
20542060
case BTPROTO_HCI:
20552061
{
20562062
struct sockaddr_hci *addr = &addrbuf->bt_hci;
2063+
memset(addr, 0, sizeof(struct sockaddr_hci));
20572064
#if defined(__NetBSD__) || defined(__DragonFly__)
20582065
const char *straddr;
20592066
_BT_HCI_MEMB(addr, family) = AF_BLUETOOTH;
@@ -2105,6 +2112,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
21052112
const char *straddr;
21062113

21072114
struct sockaddr_sco *addr = &addrbuf->bt_sco;
2115+
memset(addr, 0, sizeof(struct sockaddr_sco));
21082116
_BT_SCO_MEMB(addr, family) = AF_BLUETOOTH;
21092117
if (!PyBytes_Check(args)) {
21102118
PyErr_Format(PyExc_OSError,
@@ -2182,6 +2190,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
21822190
return 0;
21832191
}
21842192
struct sockaddr_ll* addr = &addrbuf->ll;
2193+
memset(addr, 0, sizeof(struct sockaddr_ll));
21852194
addr->sll_family = AF_PACKET;
21862195
addr->sll_protocol = htons((short)protoNumber);
21872196
addr->sll_ifindex = ifr.ifr_ifindex;
@@ -2266,6 +2275,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
22662275
struct ifreq ifr;
22672276
Py_ssize_t len;
22682277
struct sockaddr_can *addr = &addrbuf->can;
2278+
memset(addr, 0, sizeof(struct sockaddr_can));
22692279

22702280
if (!PyTuple_Check(args)) {
22712281
PyErr_Format(PyExc_TypeError,
@@ -2318,6 +2328,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
23182328
unsigned long int rx_id, tx_id;
23192329

23202330
struct sockaddr_can *addr = &addrbuf->can;
2331+
memset(addr, 0, sizeof(struct sockaddr_can));
23212332

23222333
if (!PyArg_ParseTuple(args, "O&kk", PyUnicode_FSConverter,
23232334
&interfaceName,
@@ -2365,6 +2376,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
23652376
uint8_t j1939_addr;
23662377

23672378
struct sockaddr_can *addr = &addrbuf->can;
2379+
memset(addr, 0, sizeof(struct sockaddr_can));
23682380

23692381
if (!PyArg_ParseTuple(args, "O&KIB", PyUnicode_FSConverter,
23702382
&interfaceName,
@@ -2417,6 +2429,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
24172429
case SYSPROTO_CONTROL:
24182430
{
24192431
struct sockaddr_ctl *addr = &addrbuf->ctl;
2432+
memset(addr, 0, sizeof(struct sockaddr_ctl));
24202433
addr->sc_family = AF_SYSTEM;
24212434
addr->ss_sysaddr = AF_SYS_CONTROL;
24222435

Python/assemble.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd, int nlocalsplus,
503503
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
504504

505505
// This counter mirrors the fix done in fix_cell_offsets().
506-
int numdropped = 0;
506+
int numdropped = 0, cellvar_offset = -1;
507507
pos = 0;
508508
while (PyDict_Next(umd->u_cellvars, &pos, &k, &v)) {
509509
int has_name = PyDict_Contains(umd->u_varnames, k);
@@ -514,14 +514,14 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd, int nlocalsplus,
514514
continue;
515515
}
516516

517-
int offset = PyLong_AsInt(v);
518-
if (offset == -1 && PyErr_Occurred()) {
517+
cellvar_offset = PyLong_AsInt(v);
518+
if (cellvar_offset == -1 && PyErr_Occurred()) {
519519
return ERROR;
520520
}
521-
assert(offset >= 0);
522-
offset += nlocals - numdropped;
523-
assert(offset < nlocalsplus);
524-
_Py_set_localsplus_info(offset, k, CO_FAST_CELL, names, kinds);
521+
assert(cellvar_offset >= 0);
522+
cellvar_offset += nlocals - numdropped;
523+
assert(cellvar_offset < nlocalsplus);
524+
_Py_set_localsplus_info(cellvar_offset, k, CO_FAST_CELL, names, kinds);
525525
}
526526

527527
pos = 0;
@@ -533,6 +533,10 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd, int nlocalsplus,
533533
assert(offset >= 0);
534534
offset += nlocals - numdropped;
535535
assert(offset < nlocalsplus);
536+
/* XXX If the assertion below fails it is most likely because a freevar
537+
was added to u_freevars with the wrong index due to not taking into
538+
account cellvars already present, see gh-128632. */
539+
assert(offset > cellvar_offset);
536540
_Py_set_localsplus_info(offset, k, CO_FAST_FREE, names, kinds);
537541
}
538542
return SUCCESS;

0 commit comments

Comments
 (0)