--- /dev/null
+Fix a potential crash in :func:`compile`, :func:`exec`, :func:`eval` and
+:func:`ast.parse` when an allocation fails: the parser or compiler could
+return without setting an exception.
{
void *res = _PyPegen_parse(p);
assert(p->level == 0);
+ if (res != NULL && PyErr_Occurred()) {
+ // Discard a result returned with an exception still pending
+ // (e.g. a MemoryError from a recovered-from allocation failure).
+ return NULL;
+ }
if (res == NULL) {
if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) && _is_end_of_source(p)) {
PyErr_Clear();
if (tok == NULL) {
if (PyErr_Occurred()) {
_PyTokenizer_raise_init_error(filename_ob);
- return NULL;
+ }
+ else {
+ // The only silent tokenizer init failure is a failed allocation.
+ PyErr_NoMemory();
}
return NULL;
}
if (PyErr_Occurred()) {
_PyTokenizer_raise_init_error(filename_ob);
}
+ else {
+ // The only silent tokenizer init failure is a failed allocation.
+ PyErr_NoMemory();
+ }
return NULL;
}
// This transfers the ownership to the tokenizer
{
compiler *c = PyMem_Calloc(1, sizeof(compiler));
if (c == NULL) {
+ PyErr_NoMemory();
return NULL;
}
if (compiler_setup(c, mod, filename, pflags, optimize, arena, module) < 0) {