fix for long standing problem with blender 2.5x py api.
authorCampbell Barton <ideasman42@gmail.com>
Thu, 1 Nov 2012 15:56:42 +0000 (15:56 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 1 Nov 2012 15:56:42 +0000 (15:56 +0000)
Removing data then accessing would allow invalid memory access and often crash.

Example:
  import bpy
  image = bpy.data.images.new(name="a", width=5, height=5)
  bpy.data.images.remove(image)
  print(image.name)

Now access to the removed data raises an error:
  ReferenceError: StructRNA of type Image has been removed

This is the same level of error checking that was done in blender 2.4x but was made difficult by RNA functions not having access to the PyObject's.

source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_define.c
source/blender/makesrna/intern/rna_main_api.c
source/blender/python/intern/bpy_rna.c

index 60ab231ffc91ba39ee62f0e119d1b3782fba3555..a9dbbe4273fb5681823fe25bbaf8e4acad54a468 100644 (file)
@@ -1032,6 +1032,13 @@ short RNA_type_to_ID_code(StructRNA *type);
 StructRNA *ID_code_to_RNA_type(short idcode);
 
 
+#define RNA_POINTER_INVALIDATE(ptr) {                                         \
+       /* this is checked for validity */                                        \
+       (ptr)->type =                                                             \
+       /* should not be needed but prevent bad pointer access, just in case */   \
+       (ptr)->id.data = NULL;                                                    \
+} (void)0
+
 /* macro which inserts the function name */
 #if defined __GNUC__ || defined __sun
 #  define RNA_warning(format, args ...) _RNA_warning("%s: " format "\n", __func__, ##args)
index 2d0b64f8df535e372cfd0d3b8aeba5b8a7bb3a11..8c7e4a5316355e72433dbf678ff91e001db7c04c 100644 (file)
@@ -2095,6 +2095,8 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA
                /* fixed size arrays and RNA pointers are pre-allocated on the ParameterList stack, pass a pointer to it */
                else if (type == PROP_POINTER || dparm->prop->arraydimension)
                        ptrstr = "*";
+               else if ((type == PROP_POINTER) && (flag & PROP_RNAPTR) && !(flag & PROP_THICK_WRAP))
+                       ptrstr = "*";
                /* PROP_THICK_WRAP strings are pre-allocated on the ParameterList stack,
                 * but type name for string props is already char*, so leave empty */
                else if (type == PROP_STRING && (flag & PROP_THICK_WRAP))
@@ -2146,6 +2148,10 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA
                                ptrstr = "**";
                                valstr = "*";
                        }
+                       else if ((type == PROP_POINTER) && !(flag & PROP_THICK_WRAP)) {
+                               ptrstr = "**";
+                               valstr = "*";
+                       }
                        else if (type == PROP_POINTER || dparm->prop->arraydimension) {
                                ptrstr = "*";
                                valstr = "";
index ffeb221a847105a2e57fc47c4f1e4607ccb0b4a8..bb1ecea6a24358a4006f8f9c1a4394fbc8ae0fd1 100644 (file)
@@ -981,8 +981,10 @@ PropertyRNA *RNA_def_property(StructOrFunctionRNA *cont_, const char *identifier
                        sprop->defaultvalue = "";
                        break;
                }
-               case PROP_ENUM:
                case PROP_POINTER:
+                       prop->flag |= PROP_THICK_WRAP;  /* needed for default behavior when PROP_RNAPTR is set */
+                       break;
+               case PROP_ENUM:
                case PROP_COLLECTION:
                        break;
                default:
@@ -2810,14 +2812,26 @@ int rna_parameter_size(PropertyRNA *parm)
                        {
 #ifdef RNA_RUNTIME
                                if (parm->flag & PROP_RNAPTR)
-                                       return sizeof(PointerRNA);
+                                       if (parm->flag & PROP_THICK_WRAP) {
+                                               return sizeof(PointerRNA);
+                                       }
+                                       else {
+                                               return sizeof(PointerRNA *);
+                                       }
                                else
                                        return sizeof(void *);
 #else
-                               if (parm->flag & PROP_RNAPTR)
-                                       return sizeof(PointerRNA);
-                               else
+                               if (parm->flag & PROP_RNAPTR) {
+                                       if (parm->flag & PROP_THICK_WRAP) {
+                                               return sizeof(PointerRNA);
+                                       }
+                                       else {
+                                               return sizeof(PointerRNA *);
+                                       }
+                               }
+                               else {
                                        return sizeof(void *);
+                               }
 #endif
                        }
                        case PROP_COLLECTION:
index eaa1149b3b48348be00450bed46085a00f1034d4..0f4ca7e044a8ef269de10380b8d883b13adf49d1 100644 (file)
@@ -296,15 +296,17 @@ static Image *rna_Main_images_load(Main *UNUSED(bmain), ReportList *reports, con
 
        return ima;
 }
-static void rna_Main_images_remove(Main *bmain, ReportList *reports, Image *image)
+static void rna_Main_images_remove(Main *bmain, ReportList *reports, struct PointerRNA *image_ptr)
 {
-       if (ID_REAL_USERS(image) <= 0)
+       Image *image = image_ptr->data;
+       if (ID_REAL_USERS(image) <= 0) {
                BKE_libblock_free(&bmain->image, image);
-       else
+               RNA_POINTER_INVALIDATE(image_ptr);
+       }
+       else {
                BKE_reportf(reports, RPT_ERROR, "Image '%s' must have zero users to be removed, found %d",
                            image->id.name + 2, ID_REAL_USERS(image));
-
-       /* XXX python now has invalid pointer? */
+       }
 }
 
 static Lattice *rna_Main_lattices_new(Main *UNUSED(bmain), const char *name)
@@ -1015,7 +1017,8 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop)
        RNA_def_function_flag(func, FUNC_USE_REPORTS);
        RNA_def_function_ui_description(func, "Remove an image from the current blendfile");
        parm = RNA_def_pointer(func, "image", "Image", "", "Image to remove");
-       RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+       RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+       RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);  /* pass the pointer direct */
 
        func = RNA_def_function(srna, "tag", "rna_Main_images_tag");
        parm = RNA_def_boolean(func, "value", 0, "Value", "");
index db9ff2e1a32b681672599470298e9d1bfd2a4298..3b57e550c2872a894cd74c816ba6641ec4e64d27 100644 (file)
@@ -121,8 +121,7 @@ int pyrna_prop_validity_check(BPy_PropertyRNA *self)
 
 void pyrna_invalidate(BPy_DummyPointerRNA *self)
 {
-       self->ptr.type = NULL; /* this is checked for validity */
-       self->ptr.id.data = NULL; /* should not be needed but prevent bad pointer access, just in case */
+       RNA_POINTER_INVALIDATE(&self->ptr);
 }
 
 #ifdef USE_PYRNA_INVALIDATE_GC
@@ -1776,10 +1775,21 @@ static int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyOb
                                        if (data) {
 
                                                if (flag & PROP_RNAPTR) {
-                                                       if (value == Py_None)
-                                                               memset(data, 0, sizeof(PointerRNA));
-                                                       else
-                                                               *((PointerRNA *)data) = param->ptr;
+                                                       if (flag & PROP_THICK_WRAP) {
+                                                               if (value == Py_None)
+                                                                       memset(data, 0, sizeof(PointerRNA));
+                                                               else
+                                                                       *((PointerRNA *)data) = param->ptr;
+                                                       }
+                                                       else {
+                                                               /* for function calls, we sometimes want to pass the 'ptr' directly,
+                                                                * watch out that it remains valid!, possibly we could support this later if needed */
+                                                               BLI_assert(value_new == NULL);
+                                                               if (value == Py_None)
+                                                                       *((void **)data) = NULL;
+                                                               else
+                                                                       *((PointerRNA **)data) = &param->ptr;
+                                                       }
                                                }
                                                else if (value == Py_None) {
                                                        *((void **)data) = NULL;