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:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user