Core: Harden IDProps against too-high embedding depth.

IDProperties can be embedded into one-another with no theoritical depth
limit, however the code currently uses recursive patterns to process
them, which means beyond a certain depth it will run out of stack memory
and crash.

While we cannot really prevent creating such insanely high data depth,
this commit adds several safe-guards (readfile, writefile, foreach_id,
and freeing processes), skipping further processing beyond a certain
recursion depth (currently set at 1024, which should be both well
within safety margins of any modern OS, and more than enough for any
practical use-case).

Issue initially reported by Ray Molenkemp (@lazydodo), thanks.

This commit also contains a small refactor for the IDProperty freeing
code (and IDP array resize internal logic), essentially removing public
API to free IDProperties' content - all of its usages was immediately
after calling MEM_delete on the same root properties, so can as well
call directly IDP_FreeProperty.

Finally, it fixes a potential bug related to arrays of groups freeing,
where the 'do_id_user' option was not properly propagated through the
array resize logic. Probably not an issue in practice though (not sure
if arrays of groups are used anywhere currently?).

Pull Request: https://projects.blender.org/blender/blender/pulls/160274
This commit is contained in:
Bastien Montagne 2026-06-18 12:07:48 +02:00 • committed by Bastien Montagne
parent 3cb431d137
commit c8a6c96db2
11 changed files with 235 additions and 88 deletions

View file

@ -197,6 +197,12 @@ Note that these properties can only be assigned basic Python types:
- array of ints or floats
- dictionary (only string keys are supported, values must be basic types too)
.. note::
Using dictionaries allow to nest data into other data.
There is a hard limit of 1024 levels to the supported nesting depth.
Deeper nesting will lead to error messages and loss of data.
These properties are valid outside of Python. They can be animated by curves or used in driver paths.
For a list of types that support custom properties see:

View file

@ -81,7 +81,6 @@ void IDP_ResizeIDPArray(IDProperty *prop, int newlen);
* This function works for strings too!
*/
void IDP_ResizeArray(IDProperty *prop, int newlen);
void IDP_FreeArray(IDProperty *prop);
/* ---------- String Type ------------ */
/**
@ -288,8 +287,6 @@ IDProperty *IDP_New(char type,
* \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
* But it does not free the actual #IDProperty struct itself.
*/
void IDP_FreePropertyContent_ex(IDProperty *prop, bool do_id_user);
void IDP_FreePropertyContent(IDProperty *prop);
void IDP_FreeProperty_ex(IDProperty *prop, bool do_id_user);
void IDP_FreeProperty(IDProperty *prop);

View file

@ -64,6 +64,34 @@ static size_t idp_size_table[] = {
sizeof(int), /* #IDP_ENUM */
};
/**
* Maximum amount of supported depth in IDProperties (when putting e.g. groups inside groups
* inside groups etc.).
*
* Too many levels will lead to running out of stack memory and crashes. */
constexpr int MAX_IDPROP_DEPTH_LEVEL = 1026;
/**
* Write code uses one level less than runtime processing code, because it still has to write
* something when it detects the issue, to ensure references to the 'limit properties' remain
* valid.
* Limits overly noisy continous error messages in the console due to runtime processing and
* undo/redo. */
constexpr int MAX_IDPROP_DEPTH_LEVEL_FOR_WRITE = MAX_IDPROP_DEPTH_LEVEL - 1;
/**
* Read code uses two level less than runtime processing code, because it still has to read
* something when it detects the issue, to ensure references to the 'limit properties' remain
* valid.
* Limits overly noisy continous error messages in the console due to runtime processing and
* undo/redo. */
constexpr int MAX_IDPROP_DEPTH_LEVEL_FOR_READ = MAX_IDPROP_DEPTH_LEVEL - 2;
static void idp_free_property_content_recurse(IDProperty *prop,
const bool do_id_user,
const int recursion_depth);
static void idp_free_property_recurse(IDProperty *prop,
const bool do_id_user,
const int recursion_depth);
/* -------------------------------------------------------------------- */
/** \name Array Functions (IDP Array API)
* \{ */
@ -103,12 +131,12 @@ IDProperty *IDP_CopyIDPArray(const IDProperty *array, const int flag)
return narray;
}
static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user)
static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user, const int recursion_depth)
{
BLI_assert(prop->type == IDP_IDPARRAY);
for (int i = 0; i < prop->len; i++) {
IDP_FreePropertyContent_ex(GETPROP(prop, i), do_id_user);
idp_free_property_content_recurse(GETPROP(prop, i), do_id_user, recursion_depth + 1);
}
if (prop->data.pointer) {
@ -125,7 +153,7 @@ void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
IDProperty *old = GETPROP(prop, index);
if (item != old) {
IDP_FreePropertyContent(old);
idp_free_property_content_recurse(old, true, 0);
memcpy(old, item, sizeof(IDProperty));
}
@ -205,7 +233,8 @@ void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
}
/* ----------- Numerical Array Type ----------- */
static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
static void idp_resize_group_array(
IDProperty *prop, int newlen, void *newarr, const bool do_id_users, const int recursion_depth)
{
if (prop->subtype != IDP_GROUP) {
return;
@ -223,7 +252,7 @@ static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
IDProperty **array = static_cast<IDProperty **>(prop->data.pointer);
for (int a = newlen; a < prop->len; a++) {
IDP_FreeProperty(array[a]);
idp_free_property_recurse(array[a], do_id_users, recursion_depth + 1);
}
}
}
@ -234,31 +263,31 @@ void IDP_ResizeArray(IDProperty *prop, int newlen)
/* first check if the array buffer size has room */
if (newlen <= prop->totallen && prop->totallen - newlen < IDP_ARRAY_REALLOC_LIMIT) {
idp_resize_group_array(prop, newlen, prop->data.pointer);
idp_resize_group_array(prop, newlen, prop->data.pointer, true, 0);
prop->len = newlen;
return;
}
const int newsize = idp_resize_grow_size_calc(newlen);
if (is_grow == false) {
idp_resize_group_array(prop, newlen, prop->data.pointer);
idp_resize_group_array(prop, newlen, prop->data.pointer, true, 0);
}
prop->data.pointer = MEM_realloc_zeroed(prop->data.pointer,
idp_size_table[int(prop->subtype)] * size_t(newsize));
if (is_grow == true) {
idp_resize_group_array(prop, newlen, prop->data.pointer);
idp_resize_group_array(prop, newlen, prop->data.pointer, true, 0);
}
prop->len = newlen;
prop->totallen = newsize;
}
void IDP_FreeArray(IDProperty *prop)
static void IDP_FreeArray(IDProperty *prop, const bool do_id_user, const int recursion_depth)
{
if (prop->data.pointer) {
idp_resize_group_array(prop, 0, nullptr);
idp_resize_group_array(prop, 0, nullptr, do_id_user, recursion_depth);
MEM_delete_void(prop->data.pointer);
}
}
@ -810,13 +839,13 @@ IDProperty *IDP_GetPropertyTypeFromGroup(const IDProperty *prop,
* This is because all ID Property freeing functions free only direct data (not the ID Property
* struct itself), but for Groups the child properties *are* considered
* direct data. */
static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user, const int recursion_depth)
{
BLI_assert(prop->type == IDP_GROUP);
MEM_SAFE_DELETE(prop->data.children_map);
for (IDProperty &loop : prop->data.group) {
IDP_FreePropertyContent_ex(&loop, do_id_user);
idp_free_property_content_recurse(&loop, do_id_user, recursion_depth + 1);
}
prop->data.group.free_no_destruct();
}
@ -1303,8 +1332,20 @@ void IDP_ui_data_free(IDProperty *prop)
prop->ui_data = nullptr;
}
void IDP_FreePropertyContent_ex(IDProperty *prop, const bool do_id_user)
static void idp_free_property_content_recurse(IDProperty *prop,
const bool do_id_user,
const int recursion_depth)
{
if (recursion_depth > MAX_IDPROP_DEPTH_LEVEL) {
CLOG_ERROR(&LOG,
"Too deep level of IDProperties embedding detected (over %d levels), this is "
"likely caused by a buggy script or add-on. The data in property '%s' will not "
"be freed",
MAX_IDPROP_DEPTH_LEVEL,
prop->name);
return;
}
switch (prop->type) {
case IDP_INT:
case IDP_FLOAT:
@ -1312,16 +1353,16 @@ void IDP_FreePropertyContent_ex(IDProperty *prop, const bool do_id_user)
case IDP_BOOLEAN:
break;
case IDP_ARRAY:
IDP_FreeArray(prop);
IDP_FreeArray(prop, do_id_user, recursion_depth);
break;
case IDP_STRING:
IDP_FreeString(prop);
break;
case IDP_GROUP:
IDP_FreeGroup(prop, do_id_user);
IDP_FreeGroup(prop, do_id_user, recursion_depth);
break;
case IDP_IDPARRAY:
IDP_FreeIDPArray(prop, do_id_user);
IDP_FreeIDPArray(prop, do_id_user, recursion_depth);
break;
case IDP_ID:
if (do_id_user) {
@ -1335,26 +1376,27 @@ void IDP_FreePropertyContent_ex(IDProperty *prop, const bool do_id_user)
}
}
void IDP_FreePropertyContent(IDProperty *prop)
static void idp_free_property_recurse(IDProperty *prop,
const bool do_id_user,
const int recursion_depth)
{
IDP_FreePropertyContent_ex(prop, true);
idp_free_property_content_recurse(prop, do_id_user, recursion_depth);
MEM_delete(prop);
}
void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user)
{
IDP_FreePropertyContent_ex(prop, do_id_user);
MEM_delete(prop);
idp_free_property_recurse(prop, do_id_user, 0);
}
void IDP_FreeProperty(IDProperty *prop)
{
IDP_FreePropertyContent(prop);
MEM_delete(prop);
idp_free_property_recurse(prop, true, 0);
}
void IDP_ClearProperty(IDProperty *prop)
{
IDP_FreePropertyContent(prop);
idp_free_property_content_recurse(prop, true, 0);
prop->data.pointer = nullptr;
prop->len = prop->totallen = 0;
}
@ -1370,14 +1412,25 @@ void IDP_Reset(IDProperty *prop, const IDProperty *reference)
}
}
void IDP_foreach_property(IDProperty *id_property_root,
const int type_filter,
const FunctionRef<void(IDProperty *id_property)> callback)
static void idp_foreach_property_recurse(IDProperty *id_property_root,
const int type_filter,
const int recursion_depth,
const FunctionRef<void(IDProperty *id_property)> callback)
{
if (!id_property_root) {
return;
}
if (recursion_depth > MAX_IDPROP_DEPTH_LEVEL) {
CLOG_ERROR(&LOG,
"Too deep level of IDProperties embedding detected (over %d levels), this is "
"likely caused by a buggy script or add-on. The data in property '%s' will not "
"be processed further",
MAX_IDPROP_DEPTH_LEVEL,
id_property_root->name);
return;
}
if (type_filter == 0 || (1 << id_property_root->type) & type_filter) {
callback(id_property_root);
}
@ -1386,14 +1439,14 @@ void IDP_foreach_property(IDProperty *id_property_root,
switch (id_property_root->type) {
case IDP_GROUP: {
for (IDProperty &loop : id_property_root->data.group) {
IDP_foreach_property(&loop, type_filter, callback);
idp_foreach_property_recurse(&loop, type_filter, recursion_depth + 1, callback);
}
break;
}
case IDP_IDPARRAY: {
IDProperty *loop = IDP_property_array_get(id_property_root);
for (int i = 0; i < id_property_root->len; i++) {
IDP_foreach_property(&loop[i], type_filter, callback);
idp_foreach_property_recurse(&loop[i], type_filter, recursion_depth + 1, callback);
}
break;
}
@ -1402,7 +1455,19 @@ void IDP_foreach_property(IDProperty *id_property_root,
}
}
void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer);
void IDP_foreach_property(IDProperty *id_property_root,
const int type_filter,
const FunctionRef<void(IDProperty *id_property)> callback)
{
idp_foreach_property_recurse(id_property_root, type_filter, 0, callback);
}
static void idp_blend_write_recurse(BlendWriter *writer,
const IDProperty *prop,
const int recursion_depth);
static void IDP_WriteProperty_OnlyData(const IDProperty *prop,
BlendWriter *writer,
const int recursion_depth);
static void write_ui_data(const IDProperty *prop, BlendWriter *writer)
{
@ -1462,7 +1527,7 @@ static void write_ui_data(const IDProperty *prop, BlendWriter *writer)
}
}
static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer, const int recursion_depth)
{
/* Remember to set #IDProperty.totallen to len in the linking code! */
if (prop->data.pointer) {
@ -1474,7 +1539,7 @@ static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
IDProperty **array = static_cast<IDProperty **>(prop->data.pointer);
for (int i = 0; i < prop->len; i++) {
IDP_BlendWrite(writer, array[i]);
idp_blend_write_recurse(writer, array[i], recursion_depth + 1);
}
break;
}
@ -1500,16 +1565,47 @@ static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
}
}
static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer)
static void IDP_WriteIDPArray(const IDProperty *prop,
BlendWriter *writer,
const int recursion_depth)
{
/* Remember to set #IDProperty.totallen to len in the linking code! */
if (prop->data.pointer) {
const IDProperty *array = static_cast<const IDProperty *>(prop->data.pointer);
writer->write_struct_array(prop->len, array);
/* Recursion depth limit also needs to be handled here, as IDP arrays are written in a single
* call, without going through a call to `idp_blend_write_recurse`. */
if (recursion_depth > MAX_IDPROP_DEPTH_LEVEL_FOR_WRITE) {
CLOG_ERROR(&LOG,
"Too deep level of IDProperties embedding detected (over %d levels), this is "
"likely caused by a buggy script or add-on. The data in property '%s' will not "
"be written in the blendfile or memfile undo step",
MAX_IDPROP_DEPTH_LEVEL_FOR_WRITE,
prop->name);
IDProperty *empty_prop_idparray = IDP_NewIDPArray(prop->name);
IDP_ResizeIDPArray(empty_prop_idparray, prop->len);
IDProperty *empty_array = static_cast<IDProperty *>(empty_prop_idparray->data.pointer);
for (int a = 0; a < empty_prop_idparray->len; a++) {
empty_array[a].type = IDP_INT;
empty_array[a].subtype = 0;
IDP_int_set(&empty_array[a], 0);
STRNCPY(empty_array[a].name, array[a].name);
}
writer->write_struct_array_at_address(
empty_prop_idparray->len, prop->data.pointer, empty_array);
for (int a = 0; a < empty_prop_idparray->len; a++) {
IDP_WriteProperty_OnlyData(&empty_array[a], writer, recursion_depth + 1);
}
MEM_delete(empty_prop_idparray);
return;
}
writer->write_struct_array(prop->len, array);
for (int a = 0; a < prop->len; a++) {
IDP_WriteProperty_OnlyData(&array[a], writer);
IDP_WriteProperty_OnlyData(&array[a], writer, recursion_depth + 1);
}
}
}
@ -1522,15 +1618,17 @@ static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer)
writer->write_char_array(uint(prop->len), static_cast<char *>(prop->data.pointer));
}
static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer)
static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer, const int recursion_depth)
{
for (IDProperty &loop : prop->data.group) {
IDP_BlendWrite(writer, &loop);
idp_blend_write_recurse(writer, &loop, recursion_depth + 1);
}
}
/* Functions to read/write ID Properties */
void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
static void IDP_WriteProperty_OnlyData(const IDProperty *prop,
BlendWriter *writer,
const int recursion_depth)
{
switch (prop->type) {
case IDP_INT:
@ -1540,16 +1638,16 @@ void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
case IDP_ID:
break;
case IDP_GROUP:
IDP_WriteGroup(prop, writer);
IDP_WriteGroup(prop, writer, recursion_depth);
break;
case IDP_STRING:
IDP_WriteString(prop, writer);
break;
case IDP_ARRAY:
IDP_WriteArray(prop, writer);
IDP_WriteArray(prop, writer, recursion_depth);
break;
case IDP_IDPARRAY:
IDP_WriteIDPArray(prop, writer);
IDP_WriteIDPArray(prop, writer, recursion_depth);
break;
}
if (prop->ui_data != nullptr) {
@ -1557,13 +1655,39 @@ void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
}
}
void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
static void idp_blend_write_recurse(BlendWriter *writer,
const IDProperty *prop,
const int recursion_depth)
{
if (recursion_depth > MAX_IDPROP_DEPTH_LEVEL_FOR_WRITE) {
CLOG_ERROR(&LOG,
"Too deep level of IDProperties embedding detected (over %d levels), this is "
"likely caused by a buggy script or add-on. The data in property '%s' will not "
"be written in the blendfile or memfile undo step",
MAX_IDPROP_DEPTH_LEVEL_FOR_WRITE,
prop->name);
IDProperty empty_prop = {};
empty_prop.type = IDP_INT;
empty_prop.subtype = 0;
IDP_int_set(&empty_prop, 0);
STRNCPY(empty_prop.name, prop->name);
writer->write_struct_at_address(prop, &empty_prop);
IDP_WriteProperty_OnlyData(&empty_prop, writer, recursion_depth);
return;
}
writer->write_struct(prop);
IDP_WriteProperty_OnlyData(prop, writer);
IDP_WriteProperty_OnlyData(prop, writer, recursion_depth);
}
static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader);
void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
{
idp_blend_write_recurse(writer, prop, 0);
}
static void IDP_DirectLinkProperty(IDProperty *prop,
BlendDataReader *reader,
const int recursion_depth);
static void read_ui_data(IDProperty *prop, BlendDataReader *reader)
{
@ -1645,7 +1769,9 @@ static void read_ui_data(IDProperty *prop, BlendDataReader *reader)
}
}
static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader)
static void IDP_DirectLinkIDPArray(IDProperty *prop,
BlendDataReader *reader,
const int recursion_depth)
{
/* since we didn't save the extra buffer, set totallen to len */
prop->totallen = prop->len;
@ -1662,11 +1788,13 @@ static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader)
}
for (int i = 0; i < prop->len; i++) {
IDP_DirectLinkProperty(&array[i], reader);
IDP_DirectLinkProperty(&array[i], reader, recursion_depth + 1);
}
}
static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader)
static void IDP_DirectLinkArray(IDProperty *prop,
BlendDataReader *reader,
const int recursion_depth)
{
/* since we didn't save the extra buffer, set totallen to len */
prop->totallen = prop->len;
@ -1676,7 +1804,7 @@ static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader)
BLO_read_pointer_array_and_validate_size(reader, &prop->data.pointer, &prop->len);
IDProperty **array = static_cast<IDProperty **>(prop->data.pointer);
for (int i = 0; i < prop->len; i++) {
IDP_DirectLinkProperty(array[i], reader);
IDP_DirectLinkProperty(array[i], reader, recursion_depth + 1);
}
break;
}
@ -1718,7 +1846,9 @@ static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader)
prop->totallen = prop->len;
}
static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader)
static void IDP_DirectLinkGroup(IDProperty *prop,
BlendDataReader *reader,
const int recursion_depth)
{
ListBaseT<IDProperty> *lb = &prop->data.group;
prop->data.children_map = nullptr;
@ -1731,27 +1861,48 @@ static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader)
/* Link child id properties now. */
for (IDProperty &loop : prop->data.group) {
IDP_DirectLinkProperty(&loop, reader);
IDP_DirectLinkProperty(&loop, reader, recursion_depth + 1);
if (!prop->data.children_map->children.add(&loop)) {
CLOG_WARN(&LOG, "duplicate ID property '%s' in group", loop.name);
}
}
}
static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader)
static void IDP_DirectLinkProperty(IDProperty *prop,
BlendDataReader *reader,
const int recursion_depth)
{
auto reset_property = [](IDProperty *idprop) -> void {
idprop->type = IDP_INT;
idprop->subtype = 0;
IDP_int_set(idprop, 0);
idprop->ui_data = nullptr;
};
if (recursion_depth > MAX_IDPROP_DEPTH_LEVEL_FOR_READ) {
CLOG_ERROR(&LOG,
"Too deep level of IDProperties embedding detected (over %d levels), this is "
"likely caused by a buggy script or add-on. The data in property '%s' will not "
"be read from the blendfile",
MAX_IDPROP_DEPTH_LEVEL_FOR_READ,
prop->name);
/* NOTE: No attempt to free the property, as it may lead to further recursion. */
reset_property(prop);
return;
}
switch (prop->type) {
case IDP_GROUP:
IDP_DirectLinkGroup(prop, reader);
IDP_DirectLinkGroup(prop, reader, recursion_depth);
break;
case IDP_STRING:
IDP_DirectLinkString(prop, reader);
break;
case IDP_ARRAY:
IDP_DirectLinkArray(prop, reader);
IDP_DirectLinkArray(prop, reader, recursion_depth);
break;
case IDP_IDPARRAY:
IDP_DirectLinkIDPArray(prop, reader);
IDP_DirectLinkIDPArray(prop, reader, recursion_depth);
break;
case IDP_DOUBLE:
/* NOTE: this is endianness-sensitive. */
@ -1769,12 +1920,11 @@ static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader)
default:
/* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code,
* IDP are way too polymorphic to do it safely). */
printf(
"%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type);
CLOG_WARN(&LOG,
"Found unknown IDProperty type %d, reset to Integer one with null value",
prop->type);
/* NOTE: we do not attempt to free unknown prop, we have no way to know how to do that! */
prop->type = IDP_INT;
prop->subtype = 0;
IDP_int_set(prop, 0);
reset_property(prop);
}
if (prop->ui_data != nullptr) {
@ -1786,13 +1936,13 @@ void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const ch
{
if (*prop) {
if ((*prop)->type == IDP_GROUP) {
IDP_DirectLinkGroup(*prop, reader);
IDP_DirectLinkGroup(*prop, reader, 0);
}
else {
/* corrupt file! */
printf("%s: found non group data, freeing type %d!\n", caller_func_id, (*prop)->type);
/* don't risk id, data's likely corrupt. */
// IDP_FreePropertyContent(*prop);
CLOG_WARN(&LOG, "%s: found non group data, freeing type %d!", caller_func_id, (*prop)->type);
/* Don't risk it, data is likely corrupt. */
// idp_free_property_content_recurse(*prop, true, 0);
*prop = nullptr;
}
}

View file

@ -50,13 +50,11 @@ using namespace bke::id;
void BKE_libblock_free_data(ID *id, const bool do_id_user)
{
if (id->properties) {
IDP_FreePropertyContent_ex(id->properties, do_id_user);
MEM_delete(id->properties);
IDP_FreeProperty_ex(id->properties, do_id_user);
id->properties = nullptr;
}
if (id->system_properties) {
IDP_FreePropertyContent_ex(id->system_properties, do_id_user);
MEM_delete(id->system_properties);
IDP_FreeProperty_ex(id->system_properties, do_id_user);
id->system_properties = nullptr;
}

View file

@ -3884,8 +3884,7 @@ bNodeSocket *node_add_static_socket(bNodeTree &ntree,
static void node_socket_free(bNodeSocket *sock, const bool do_id_user)
{
if (sock->prop) {
IDP_FreePropertyContent_ex(sock->prop, do_id_user);
MEM_delete(sock->prop);
IDP_FreeProperty_ex(sock->prop, do_id_user);
}
if (sock->default_value) {
@ -4935,13 +4934,11 @@ void node_free_node(bNodeTree *ntree, bNode &node)
if (node.prop) {
/* Remember, no ID user refcount management here! */
IDP_FreePropertyContent_ex(node.prop, false);
MEM_delete(node.prop);
IDP_FreeProperty_ex(node.prop, false);
}
if (node.system_properties) {
/* Remember, no ID user refcount management here! */
IDP_FreePropertyContent_ex(node.system_properties, false);
MEM_delete(node.system_properties);
IDP_FreeProperty_ex(node.system_properties, false);
}
if (node.runtime->declaration) {

View file

@ -726,8 +726,7 @@ static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user)
MEM_SAFE_DELETE(socket.default_attribute_name);
MEM_SAFE_DELETE(socket.identifier);
if (socket.properties) {
IDP_FreePropertyContent_ex(socket.properties, do_id_user);
MEM_delete(socket.properties);
IDP_FreeProperty_ex(socket.properties, do_id_user);
}
break;
}

View file

@ -374,8 +374,7 @@ static void scene_free_markers(Scene *scene, bool do_id_user)
{
for (TimeMarker &marker : scene->markers.items_mutable()) {
if (marker.prop != nullptr) {
IDP_FreePropertyContent_ex(marker.prop, do_id_user);
MEM_delete(marker.prop);
IDP_FreeProperty_ex(marker.prop, do_id_user);
}
MEM_delete(&marker);
}

View file

@ -1858,8 +1858,7 @@ static wmOperatorStatus ed_marker_delete_exec(bContext *C, wmOperator * /*op*/)
nmarker = marker->next;
if (marker->flag & SELECT) {
if (marker->prop != nullptr) {
IDP_FreePropertyContent(marker->prop);
MEM_delete(marker->prop);
IDP_FreeProperty(marker->prop);
}
BLI_freelinkN(markers, marker);
changed = true;

View file

@ -4973,7 +4973,7 @@ void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA
}
IDP_AppendArray(idprop, item);
/* IDP_AppendArray does a shallow copy (memcpy), only free memory. */
// IDP_FreePropertyContent(item);
// IDP_FreeProperty(item);
MEM_delete(item);
rna_idproperty_touch(idprop);
}
@ -4991,7 +4991,7 @@ void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA
}
IDP_AppendArray(idprop, item);
/* #IDP_AppendArray does a shallow copy (memcpy), only free memory. */
// IDP_FreePropertyContent(item);
// IDP_FreeProperty(item);
MEM_delete(item);
}
}

View file

@ -2124,7 +2124,11 @@ PyDoc_STRVAR(
/* Wrap. */
BPy_IDGroup_Type_doc,
"A dictionary-like group of ID properties, "
"supporting key access, iteration, and membership testing.");
"supporting key access, iteration, and membership testing.\n"
"\n"
".. note::\n"
"\n"
" Only supports a maximum of 1024 levels of nesting.");
PyTypeObject BPy_IDGroup_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)

View file

@ -216,12 +216,10 @@ static void seq_strip_free_ex(Scene *scene,
}
if (strip->prop) {
IDP_FreePropertyContent_ex(strip->prop, do_id_user);
MEM_delete(strip->prop);
IDP_FreeProperty_ex(strip->prop, do_id_user);
}
if (strip->system_properties) {
IDP_FreePropertyContent_ex(strip->system_properties, do_id_user);
MEM_delete(strip->system_properties);
IDP_FreeProperty_ex(strip->system_properties, do_id_user);
}
/* free modifiers */