Skip to content

Commit c21b2ea

Browse files
Invalidate metadata pointers when data is deleted
(Requires swig >= 4.4) This removes another potential source of segfaults.
1 parent c3b4bf4 commit c21b2ea

File tree

19 files changed

+187
-167
lines changed

19 files changed

+187
-167
lines changed

src/interface/exif.i

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
%include "shared/preamble.i"
2727
%include "shared/buffers.i"
2828
%include "shared/containers.i"
29-
%include "shared/data_iterator.i"
3029
%include "shared/metadatum_wrappers.i"
3130
%include "shared/keep_reference.i"
3231
%include "shared/windows.i"
@@ -55,8 +54,6 @@ INPUT_BUFFER_RO(const Exiv2::byte* buf, BUFLEN_T size)
5554

5655
EXTEND_METADATUM(Exifdatum)
5756

58-
DATA_ITERATOR(ExifData, Exifdatum)
59-
6057
METADATUM_WRAPPERS(ExifData, Exifdatum)
6158

6259
// Get the current (or default if not set) type id of a datum

src/interface/image.i

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ RELEASE_BUFFER(void writeMetadata)
6565
// Convert path encoding on Windows
6666
WINDOWS_PATH(const std::string& path)
6767

68+
// Declare metadatum wrapper classes
69+
DECLARE_METADATUM_WRAPPERS(ExifData, Exifdatum)
70+
6871
// Simplify handling of default parameters
6972
%typemap(default) bool useCurl {$1 = true;}
7073
%ignore Exiv2::ImageFactory::createIo(std::string const &);

src/interface/iptc.i

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
%include "shared/preamble.i"
2727
%include "shared/containers.i"
28-
%include "shared/data_iterator.i"
2928
%include "shared/metadatum_wrappers.i"
3029

3130
%include "stdint.i"
@@ -41,8 +40,6 @@ EXCEPTION()
4140

4241
EXTEND_METADATUM(Iptcdatum)
4342

44-
DATA_ITERATOR(IptcData, Iptcdatum)
45-
4643
METADATUM_WRAPPERS(IptcData, Iptcdatum)
4744

4845
// Get the current (or default if not set) type id of a datum

src/interface/preview.i

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ EXV_ENABLE_FILESYSTEM_FUNCTION(Exiv2::PreviewImage::writeFile)
5959
WINDOWS_PATH(const std::string& path)
6060
WINDOWS_PATH_OUT(extension)
6161

62+
// Declare metadatum wrapper classes
63+
DECLARE_METADATUM_WRAPPERS(ExifData, Exifdatum)
64+
6265
// Convert getPreviewProperties result to a Python tuple
6366
%template() std::vector<Exiv2::PreviewProperties>;
6467

src/interface/shared/containers.i

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
return NULL;
7272
}
7373
#if SWIG_VERSION >= 0x040400
74-
invalidate_iterators(py_self, pos);
74+
invalidate_pointers(py_self, pos);
7575
#endif
7676
$self->erase(pos);
7777
return SWIG_Py_Void();

src/interface/shared/metadatum_wrappers.i

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,25 @@
2020

2121

2222
%include "shared/keep_reference.i"
23+
%include "shared/pointer_store.i"
24+
25+
26+
// Macro to declare metadatum iterators and pointer wrappers
27+
%define DECLARE_METADATUM_WRAPPERS(container_type, datum_type)
28+
%{
29+
class datum_type##_pointer;
30+
class container_type##_iterator;
31+
class datum_type##_reference;
32+
%}
33+
%enddef // DECLARE_METADATUM_WRAPPERS
2334

2435

2536
// Macro to wrap metadatum iterators and pointers
2637
%define METADATUM_WRAPPERS(container_type, datum_type)
2738

39+
// Invalidate pointers when data is deleted
40+
POINTER_STORE(container_type, datum_type)
41+
2842
// Base class of pointer wrappers
2943
%feature("python:slot", "tp_str", functype="reprfunc")
3044
datum_type##_pointer::__str__;
@@ -50,10 +64,10 @@ public:
5064
}
5165
std::string __str__() {
5266
if (invalidated)
53-
return "invalid " + name;
67+
return name + "<deleted data>";
5468
Exiv2::datum_type* ptr = **this;
5569
if (!ptr)
56-
return name + "<end>";
70+
return name + "<data end>";
5771
return name + "<" + ptr->key() + ": " + ptr->print() + ">";
5872
}
5973
// Provide size() C++ method for buffer size check
@@ -67,6 +81,12 @@ public:
6781
}
6882
// Invalidate iterator unilaterally
6983
void _invalidate() { invalidated = true; }
84+
// Invalidate iterator if what it points to has been deleted
85+
bool _invalidate(Exiv2::datum_type& deleted) {
86+
if (&deleted == **this)
87+
invalidated = true;
88+
return invalidated;
89+
}
7090
// Dereference operator gives access to all datum methods
7191
Exiv2::datum_type* operator->() const {
7292
Exiv2::datum_type* ptr = **this;
@@ -87,7 +107,6 @@ public:
87107
%ignore container_type##_iterator::##container_type##_iterator;
88108
%ignore container_type##_iterator::operator*;
89109
%ignore container_type##_iterator::_ptr;
90-
%ignore container_type##_iterator::_invalidate;
91110
%feature("docstring") container_type##_iterator "
92111
Python wrapper for an :class:`" #container_type "` iterator. It has most of
93112
the methods of :class:`" #datum_type "` allowing easy access to the
@@ -131,13 +150,6 @@ public:
131150
return NULL;
132151
return &(*ptr);
133152
}
134-
using datum_type##_pointer::_invalidate;
135-
// Invalidate iterator if what it points to has been deleted
136-
bool _invalidate(Exiv2::container_type::iterator deleted) {
137-
if (deleted == ptr)
138-
invalidated = true;
139-
return invalidated;
140-
}
141153
// Access to ptr, for use in other methods
142154
Exiv2::container_type::iterator _ptr() const {
143155
if (invalidated)
@@ -172,7 +184,7 @@ public:
172184
$descriptor(container_type##_iterator*), SWIG_POINTER_OWN);
173185
#if SWIG_VERSION >= 0x040400
174186
// Keep weak reference to the Python iterator
175-
if (store_iterator(self, $result)) {
187+
if (store_pointer(self, $result)) {
176188
SWIG_fail;
177189
}
178190
#endif // SWIG_VERSION
@@ -181,7 +193,6 @@ public:
181193
// Metadata reference wrapper
182194
%ignore datum_type##_reference::##datum_type##_reference;
183195
%ignore datum_type##_reference::operator*;
184-
%ignore datum_type##_reference::_invalidate;
185196
%feature("docstring") datum_type##_reference "
186197
Python wrapper for an :class:`" #datum_type "` reference. It has most of
187198
the methods of :class:`" #datum_type "` allowing easy access to the
@@ -201,13 +212,6 @@ public:
201212
throw std::runtime_error("datum_type reference is invalid");
202213
return ptr;
203214
}
204-
using datum_type##_pointer::_invalidate;
205-
// Invalidate pointer if what it points to has been deleted
206-
bool _invalidate(Exiv2::datum_type* deleted) {
207-
if (deleted == ptr)
208-
invalidated = true;
209-
return invalidated;
210-
}
211215
};
212216
%}
213217
%typemap(in) const Exiv2::datum_type& {
@@ -223,6 +227,12 @@ public:
223227
$result = SWIG_NewPointerObj(
224228
SWIG_as_voidptr(new datum_type##_reference($1)),
225229
$descriptor(datum_type##_reference*), SWIG_POINTER_OWN);
230+
#if SWIG_VERSION >= 0x040400
231+
// Keep weak reference to the Python result
232+
if (store_pointer(self, $result)) {
233+
SWIG_fail;
234+
}
235+
#endif // SWIG_VERSION
226236
}
227237

228238
%enddef // METADATUM_WRAPPERS
Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,35 @@
1616
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

1818

19-
%include "shared/keep_reference.i"
2019
%include "shared/private_data.i"
2120

2221

23-
// Macro to wrap data iterators
24-
%define DATA_ITERATOR(container_type, datum_type)
22+
// Macro to store weak references to pointers and invalidate the
23+
// pointers when data is deleted
24+
%define POINTER_STORE(container_type, datum_type)
2525

2626
#if SWIG_VERSION >= 0x040400
27-
// Functions to store weak references to iterators (swig >= v4.4)
28-
%fragment("iterator_store", "header", fragment="private_data") {
27+
// Functions to store weak references to pointers (swig >= v4.4)
28+
%fragment("pointer_store", "header", fragment="private_data") {
2929
static void _process_list(PyObject* list, bool invalidate_all,
3030
Exiv2::container_type::iterator* beg,
3131
Exiv2::container_type::iterator* end) {
32-
PyObject* py_it = NULL;
33-
container_type##_iterator* cpp_it = NULL;
32+
PyObject* py_ptr = NULL;
33+
datum_type##_pointer* cpp_ptr = NULL;
3434
for (Py_ssize_t idx = PyList_Size(list); idx > 0; idx--) {
35-
py_it = PyWeakref_GetObject(PyList_GetItem(list, idx-1));
36-
if (py_it == Py_None)
35+
py_ptr = PyWeakref_GetObject(PyList_GetItem(list, idx-1));
36+
if (py_ptr == Py_None)
3737
goto forget;
3838
if (!(invalidate_all || beg))
3939
continue;
40-
if (SWIG_IsOK(SWIG_ConvertPtr(
41-
py_it, (void**)&cpp_it,
42-
$descriptor(container_type##_iterator*), 0))) {
40+
if (SWIG_IsOK(SWIG_ConvertPtr(py_ptr, (void**)&cpp_ptr,
41+
$descriptor(datum_type##_pointer*), 0))) {
4342
if (invalidate_all) {
44-
cpp_it->_invalidate();
43+
cpp_ptr->_invalidate();
4544
goto forget;
4645
}
4746
for (Exiv2::container_type::iterator it=*beg; it!=*end; it++)
48-
if (cpp_it->_invalidate(it))
47+
if (cpp_ptr->_invalidate(*it))
4948
goto forget;
5049
}
5150
continue;
@@ -54,44 +53,44 @@ forget:
5453
continue;
5554
}
5655
};
57-
static void purge_iterators(PyObject* list) {
56+
static void purge_pointers(PyObject* list) {
5857
_process_list(list, false, NULL, NULL);
5958
};
60-
static void invalidate_iterators(PyObject* py_self) {
61-
PyObject* list = private_store_get(py_self, "iterators");
59+
static void invalidate_pointers(PyObject* py_self) {
60+
PyObject* list = private_store_get(py_self, "pointers");
6261
if (list)
6362
_process_list(list, true, NULL, NULL);
6463
};
65-
static void invalidate_iterators(PyObject* py_self,
66-
Exiv2::container_type::iterator pos) {
67-
PyObject* list = private_store_get(py_self, "iterators");
64+
static void invalidate_pointers(PyObject* py_self,
65+
Exiv2::container_type::iterator pos) {
66+
PyObject* list = private_store_get(py_self, "pointers");
6867
if (list) {
6968
Exiv2::container_type::iterator end = pos;
7069
end++;
7170
_process_list(list, false, &pos, &end);
7271
}
7372
};
74-
static void invalidate_iterators(PyObject* py_self,
75-
Exiv2::container_type::iterator beg,
76-
Exiv2::container_type::iterator end) {
77-
PyObject* list = private_store_get(py_self, "iterators");
73+
static void invalidate_pointers(PyObject* py_self,
74+
Exiv2::container_type::iterator beg,
75+
Exiv2::container_type::iterator end) {
76+
PyObject* list = private_store_get(py_self, "pointers");
7877
if (list)
7978
_process_list(list, false, &beg, &end);
8079
};
81-
static int store_iterator(PyObject* py_self, PyObject* iterator) {
82-
PyObject* list = private_store_get(py_self, "iterators");
80+
static int store_pointer(PyObject* py_self, PyObject* py_ptr) {
81+
PyObject* list = private_store_get(py_self, "pointers");
8382
if (list)
84-
purge_iterators(list);
83+
purge_pointers(list);
8584
else {
8685
list = PyList_New(0);
8786
if (!list)
8887
return -1;
89-
int error = private_store_set(py_self, "iterators", list);
88+
int error = private_store_set(py_self, "pointers", list);
9089
Py_DECREF(list);
9190
if (error)
9291
return -1;
9392
}
94-
PyObject* ref = PyWeakref_NewRef(iterator, NULL);
93+
PyObject* ref = PyWeakref_NewRef(py_ptr, NULL);
9594
if (!ref)
9695
return -1;
9796
int result = PyList_Append(list, ref);
@@ -113,24 +112,24 @@ static int store_iterator(PyObject* py_self, PyObject* iterator) {
113112
#endif
114113

115114
#if SWIG_VERSION >= 0x040400
116-
// clear() invalidates all iterators
117-
%typemap(ret, fragment="iterator_store") void clear {
118-
invalidate_iterators(self);
115+
// clear() invalidates all pointers
116+
%typemap(ret, fragment="pointer_store") void clear {
117+
invalidate_pointers(self);
119118
}
120-
// erase() and eraseFamily() invalidate some iterators
121-
%typemap(check, fragment="iterator_store")
119+
// erase() and eraseFamily() invalidate some pointers
120+
%typemap(check, fragment="pointer_store")
122121
Exiv2::container_type::iterator pos {
123-
invalidate_iterators(self, $1);
122+
invalidate_pointers(self, $1);
124123
}
125-
%typemap(check, fragment="iterator_store")
124+
%typemap(check, fragment="pointer_store")
126125
(Exiv2::container_type::iterator beg,
127126
Exiv2::container_type::iterator end) {
128-
invalidate_iterators(self, $1, $2);
127+
invalidate_pointers(self, $1, $2);
129128
}
130-
%typemap(check, fragment="iterator_store")
129+
%typemap(check, fragment="pointer_store")
131130
Exiv2::container_type::iterator& pos {
132-
invalidate_iterators(self, *$1, arg1->end());
131+
invalidate_pointers(self, *$1, arg1->end());
133132
}
134133
#endif // SWIG_VERSION
135134

136-
%enddef // DATA_ITERATOR
135+
%enddef // POINTER_STORE

src/interface/xmp.i

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
%include "shared/preamble.i"
2727
%include "shared/containers.i"
28-
%include "shared/data_iterator.i"
2928
%include "shared/metadatum_wrappers.i"
3029

3130
%include "stdint.i"
@@ -41,8 +40,6 @@ EXCEPTION()
4140

4241
EXTEND_METADATUM(Xmpdatum)
4342

44-
DATA_ITERATOR(XmpData, Xmpdatum)
45-
4643
METADATUM_WRAPPERS(XmpData, Xmpdatum)
4744

4845
// Get the current (or default if not set) type id of a datum

0 commit comments

Comments
 (0)