Fix #142909: SystemError when collections contain non UTF8 names

The Python API wasn't accounting for structs name properties using
path sub-types, which allow non UTF8 byte sequences.

This meant accessing the SpaceFileBrowser.recent_folders collection
could cause unhandled exceptions because
FileBrowserFSMenuEntry.name uses the PROP_FILENAME sub-type.

Resolve by adding a wrapper around the RNA struct name lookup
that handles path properties properly.
This commit is contained in:
Campbell Barton
2025-07-23 02:32:28 +00:00
parent 6571236b59
commit d3fd04c816
3 changed files with 82 additions and 50 deletions

View File

@@ -214,8 +214,12 @@ PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifi
FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier);
const ListBase *RNA_struct_type_functions(StructRNA *srna);
char *RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
ATTR_WARN_UNUSED_RESULT;
[[nodiscard]] char *RNA_struct_name_get_alloc_ex(
PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len, PropertyRNA **r_nameprop);
[[nodiscard]] char *RNA_struct_name_get_alloc(PointerRNA *ptr,
char *fixedbuf,
int fixedlen,
int *r_len);
/**
* Use when registering structs with the #STRUCT_PUBLIC_NAMESPACE flag.

View File

@@ -1116,14 +1116,25 @@ void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type)
srna->blender_type = blender_type;
}
char *RNA_struct_name_get_alloc_ex(
PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len, PropertyRNA **r_nameprop)
{
if (ptr->data) {
if (PropertyRNA *nameprop = RNA_struct_name_property(ptr->type)) {
*r_nameprop = nameprop;
return RNA_property_string_get_alloc(ptr, nameprop, fixedbuf, fixedlen, r_len);
}
}
return nullptr;
}
char *RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
{
PropertyRNA *nameprop;
if (ptr->data && (nameprop = RNA_struct_name_property(ptr->type))) {
return RNA_property_string_get_alloc(ptr, nameprop, fixedbuf, fixedlen, r_len);
if (ptr->data) {
if (PropertyRNA *nameprop = RNA_struct_name_property(ptr->type)) {
return RNA_property_string_get_alloc(ptr, nameprop, fixedbuf, fixedlen, r_len);
}
}
return nullptr;
}

View File

@@ -341,6 +341,46 @@ static void id_release_weakref(struct ID *id)
#endif /* USE_PYRNA_INVALIDATE_WEAKREF */
struct BPy_NamePropAsPyObject_Cache {
PyObject *(*nameprop_as_py_object_fn)(const char *, Py_ssize_t);
PropertyRNA *nameprop;
};
/**
* Wrapper for #RNA_struct_name_get_alloc_ex that handles non UTF8 names, see #142909.
*/
static PyObject *pyrna_struct_get_nameprop_as_pyobject(
PointerRNA *ptr, BPy_NamePropAsPyObject_Cache &nameprop_cache)
{
char fixedbuf[256];
int name_len;
PropertyRNA *nameprop;
char *name_ptr = RNA_struct_name_get_alloc_ex(
ptr, fixedbuf, sizeof(fixedbuf), &name_len, &nameprop);
if (LIKELY(name_ptr)) {
/* In most cases this only runs once. */
if (UNLIKELY(nameprop != nameprop_cache.nameprop)) {
nameprop_cache.nameprop = nameprop;
const PropertySubType subtype = RNA_property_subtype(nameprop);
if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME)) {
nameprop_cache.nameprop_as_py_object_fn = PyC_UnicodeFromBytesAndSize;
}
else {
nameprop_cache.nameprop_as_py_object_fn = PyUnicode_FromStringAndSize;
}
}
PyObject *result = nameprop_cache.nameprop_as_py_object_fn(name_ptr, name_len);
/* The string data may be corrupt if this asserts,
* or not using a file-path sub-type when it should.. */
BLI_assert(result != nullptr);
if (name_ptr != fixedbuf) {
MEM_freeN(name_ptr);
}
return result;
}
return nullptr;
}
void BPY_id_release(ID *id)
{
#ifdef USE_PYRNA_INVALIDATE_GC
@@ -4308,24 +4348,16 @@ static void pyrna_dir_members_rna(PyObject *list, PointerRNA *ptr)
/*
* Collect RNA attributes
*/
char name[256], *name_ptr;
int name_len;
iterprop = RNA_struct_iterator_property(ptr->type);
BPy_NamePropAsPyObject_Cache nameprop_cache = {nullptr};
RNA_PROP_BEGIN (ptr, itemptr, iterprop) {
/* Custom-properties are exposed using `__getitem__`, exclude from `__dir__`. */
if (RNA_property_is_idprop(static_cast<const PropertyRNA *>(itemptr.data))) {
continue;
}
name_ptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &name_len);
if (name_ptr) {
PyList_APPEND(list, PyUnicode_FromStringAndSize(name_ptr, name_len));
if (name != name_ptr) {
MEM_freeN(name_ptr);
}
if (PyObject *name_py = pyrna_struct_get_nameprop_as_pyobject(&itemptr, nameprop_cache)) {
PyList_APPEND(list, name_py);
}
}
RNA_PROP_END;
@@ -5179,18 +5211,11 @@ PyDoc_STRVAR(
static PyObject *pyrna_prop_collection_keys(BPy_PropertyRNA *self)
{
PyObject *ret = PyList_New(0);
char name[256], *name_ptr;
int name_len;
BPy_NamePropAsPyObject_Cache nameprop_cache = {nullptr};
RNA_PROP_BEGIN (&self->ptr.value(), itemptr, self->prop) {
name_ptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &name_len);
if (name_ptr) {
PyList_APPEND(ret, PyUnicode_FromStringAndSize(name_ptr, name_len));
if (name != name_ptr) {
MEM_freeN(name_ptr);
}
if (PyObject *name_py = pyrna_struct_get_nameprop_as_pyobject(&itemptr, nameprop_cache)) {
PyList_APPEND(ret, name_py);
}
}
RNA_PROP_END;
@@ -5211,32 +5236,24 @@ PyDoc_STRVAR(
static PyObject *pyrna_prop_collection_items(BPy_PropertyRNA *self)
{
PyObject *ret = PyList_New(0);
PyObject *item;
char name[256], *name_ptr;
int name_len;
int i = 0;
BPy_NamePropAsPyObject_Cache nameprop_cache = {nullptr};
RNA_PROP_BEGIN (&self->ptr.value(), itemptr, self->prop) {
if (itemptr.data) {
/* Add to Python list. */
item = PyTuple_New(2);
name_ptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &name_len);
if (name_ptr) {
PyTuple_SET_ITEM(item, 0, PyUnicode_FromStringAndSize(name_ptr, name_len));
if (name != name_ptr) {
MEM_freeN(name_ptr);
}
}
else {
/* A bit strange, but better than returning an empty list. */
PyTuple_SET_ITEM(item, 0, PyLong_FromLong(i));
}
PyTuple_SET_ITEM(item, 1, pyrna_struct_CreatePyObject(&itemptr));
PyList_APPEND(ret, item);
i++;
if (UNLIKELY(itemptr.data == nullptr)) {
continue;
}
/* Add to Python list. */
PyObject *item = PyTuple_New(2);
PyObject *name_py = pyrna_struct_get_nameprop_as_pyobject(&itemptr, nameprop_cache);
/* Strange to use an index `i` when `name_py` is null, better than excluding it's value.
* In practice this should not happen. */
PyTuple_SET_ITEM(item, 0, name_py ? name_py : PyLong_FromLong(i));
PyTuple_SET_ITEM(item, 1, pyrna_struct_CreatePyObject(&itemptr));
PyList_APPEND(ret, item);
i++;
}
RNA_PROP_END;