Skip to content

Set: BUILD_SET opcode can be failed with segfault #101952

Closed
@Eclips4

Description

@Eclips4

TARGET(BUILD_SET) {
PyObject **values = &PEEK(oparg);
PyObject *set;
set = PySet_New(NULL);
int err = 0;
for (int i = 0; i < oparg; i++) {
PyObject *item = values[i];
if (err == 0)
err = PySet_Add(set, item);
Py_DECREF(item);
}
if (err != 0) {
Py_DECREF(set);
if (true) { STACK_SHRINK(oparg); goto error; }
}
STACK_SHRINK(oparg);
STACK_GROW(1);
POKE(1, set);
DISPATCH();
}

&

cpython/Python/bytecodes.c

Lines 1303 to 1316 in 36b139a

inst(BUILD_SET, (values[oparg] -- set)) {
set = PySet_New(NULL);
int err = 0;
for (int i = 0; i < oparg; i++) {
PyObject *item = values[i];
if (err == 0)
err = PySet_Add(set, item);
Py_DECREF(item);
}
if (err != 0) {
Py_DECREF(set);
ERROR_IF(true, error);
}
}

Doesn't take in account case, when PySet_New(NULL) returns NULL.
We are checking that PySet_Add doesn't return a non-zero(-1) value.
But, PySet_Add has a check, that first argument is a subclass of set. Which fails, if we will pass (PyObject *) NULL as first argument. Why?

#define PySet_Check(ob) \
    (Py_IS_TYPE((ob), &PySet_Type) || \
    PyType_IsSubtype(Py_TYPE(ob), &PySet_Type))

PySet_Add uses this macross. But, Py_TYPE will be failed with segfault when try to access ob_type of (PyObject *) NULL.

Implementation of Py_TYPE:

static inline PyTypeObject* Py_TYPE(PyObject *ob) {
    return ob->ob_type;
}
(gdb) call (PyObject *) NULL
$1 = (PyObject *) 0x0
(gdb) call $1->ob_type
Cannot access memory at address 0x8

So, we should add check, that value of PySet_New is not-null.

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions