Skip to content

Commit c808fc6

Browse files
kdt3rdcary-ilm
authored andcommitted
Fix memory leaks (#2422)
* fix memory leak in test only - invalid api call Signed-off-by: Kimball Thurston <kdt3rd@gmail.com> * simplify string init Signed-off-by: Kimball Thurston <kdt3rd@gmail.com> * refactor part memory handling for parts failing to add a part was not reverting cleanly. However, when reverting, the array storing the list of the parts was not being updated correctly, causing more potential memory leaks with lots of failures. Signed-off-by: Kimball Thurston <kdt3rd@gmail.com> --------- Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
1 parent 45c2bf9 commit c808fc6

5 files changed

Lines changed: 59 additions & 57 deletions

File tree

src/lib/OpenEXRCore/internal_structs.c

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -162,26 +162,22 @@ internal_exr_destroy_part (exr_context_t ctxt, exr_priv_part_t cur)
162162
atomic_store (&(cur->chunk_table), (uintptr_t) (0));
163163
#endif
164164
if (ctable && ((uintptr_t) ctable) != UINTPTR_MAX) dofree (ctable);
165+
166+
/* we avoid malloc on one part files, but need to check */
167+
if (cur != &(ctxt->first_part)) { dofree (cur); }
168+
else { memset (cur, 0, sizeof (struct _priv_exr_part_t)); }
165169
}
166170

167171
/**************************************/
168172

169173
static void
170174
internal_exr_destroy_parts (exr_context_t ctxt)
171175
{
172-
exr_memory_free_func_t dofree = ctxt->free_fn;
173176
for (int p = 0; p < ctxt->num_parts; ++p)
174-
{
175-
exr_priv_part_t cur = ctxt->parts[p];
176-
177-
internal_exr_destroy_part (ctxt, cur);
178-
179-
/* the first one is always the one that is part of the file */
180-
if (cur != &(ctxt->first_part)) { dofree (cur); }
181-
else { memset (cur, 0, sizeof (struct _priv_exr_part_t)); }
182-
}
177+
internal_exr_destroy_part (ctxt, ctxt->parts[p]);
183178

184-
if (ctxt->num_parts > 1) dofree (ctxt->parts);
179+
if (ctxt->parts != &(ctxt->init_part)) ctxt->free_fn (ctxt->parts);
180+
ctxt->init_part = NULL;
185181
ctxt->parts = NULL;
186182
ctxt->num_parts = 0;
187183
}
@@ -200,15 +196,14 @@ internal_exr_add_part (
200196

201197
if (ncount == 1)
202198
{
203-
/* no need to zilch, the parent struct will have already been zero'ed */
199+
/* avoid an initial malloc by embedding a part in the struct... */
204200
part = &(f->first_part);
201+
/* also need a pointer to pointer... */
205202
f->init_part = part;
206203
nptrs = &(f->init_part);
207204
}
208205
else
209206
{
210-
struct _priv_exr_part_t nil = {0};
211-
212207
part = f->alloc_fn (sizeof (struct _priv_exr_part_t));
213208
if (!part) return f->standard_error (f, EXR_ERR_OUT_OF_MEMORY);
214209

@@ -218,8 +213,8 @@ internal_exr_add_part (
218213
f->free_fn (part);
219214
return f->standard_error (f, EXR_ERR_OUT_OF_MEMORY);
220215
}
221-
*part = nil;
222216
}
217+
memset (part, 0, sizeof (struct _priv_exr_part_t));
223218

224219
/* assign appropriately invalid values */
225220
part->storage_mode = EXR_STORAGE_LAST_TYPE;
@@ -234,16 +229,13 @@ internal_exr_add_part (
234229
part->dwa_compression_level = f->default_dwa_quality;
235230

236231
/* put it into the part table */
237-
if (ncount > 1)
232+
for (int p = 0; p < f->num_parts; ++p)
238233
{
239-
for (int p = 0; p < f->num_parts; ++p)
240-
{
241-
nptrs[p] = f->parts[p];
242-
}
243-
nptrs[ncount - 1] = part;
234+
nptrs[p] = f->parts[p];
244235
}
236+
nptrs[f->num_parts] = part;
245237

246-
if (f->num_parts > 1) { f->free_fn (f->parts); }
238+
if (f->parts && f->parts != &(f->init_part)) { f->free_fn (f->parts); }
247239
f->parts = nptrs;
248240
f->num_parts = ncount;
249241
if (outpart) *outpart = part;
@@ -264,20 +256,8 @@ internal_exr_revert_add_part (
264256
*new_index = -1;
265257

266258
internal_exr_destroy_part (ctxt, part);
267-
if (ncount == 0)
268-
{
269-
ctxt->num_parts = 0;
270-
ctxt->init_part = NULL;
271-
ctxt->parts = NULL;
272-
}
273-
else if (ncount == 1)
274-
{
275-
if (part == &(ctxt->first_part)) ctxt->first_part = *(ctxt->parts[1]);
276-
ctxt->init_part = &(ctxt->first_part);
277-
ctxt->free_fn (ctxt->parts);
278-
ctxt->parts = &(ctxt->init_part);
279-
}
280-
else
259+
260+
if ( ncount > 0 )
281261
{
282262
int np = 0;
283263
for (int p = 0; p < ctxt->num_parts; ++p)
@@ -287,6 +267,13 @@ internal_exr_revert_add_part (
287267
++np;
288268
}
289269
}
270+
else
271+
{
272+
if (ctxt->parts && ctxt->parts != &(ctxt->init_part))
273+
ctxt->free_fn (ctxt->parts);
274+
275+
ctxt->parts = NULL;
276+
}
290277
ctxt->num_parts = ncount;
291278
}
292279

src/lib/OpenEXRCore/part.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,16 @@ exr_add_part (
8383
(uint64_t) pnamelen));
8484
}
8585

86-
rv = internal_exr_add_part (ctxt, &part, new_index);
87-
if (rv != EXR_ERR_SUCCESS) return EXR_UNLOCK_AND_RETURN (rv);
88-
8986
if (ctxt->num_parts > 0)
9087
{
9188
// ensure multi part has at least some name?
9289
if (!partname) partname = "";
9390

94-
for ( int pidx = 0; pidx < ctxt->num_parts - 1; ++pidx )
91+
for ( int pidx = 0; pidx < ctxt->num_parts; ++pidx )
9592
{
9693
const exr_attribute_t* pname = ctxt->parts[pidx]->name;
9794
if (!pname)
9895
{
99-
internal_exr_revert_add_part (ctxt, &part, new_index);
10096
return EXR_UNLOCK_AND_RETURN (
10197
ctxt->print_error (
10298
ctxt,
@@ -106,7 +102,6 @@ exr_add_part (
106102
}
107103
if (!strcmp (partname, pname->string->str))
108104
{
109-
internal_exr_revert_add_part (ctxt, &part, new_index);
110105
return EXR_UNLOCK_AND_RETURN (
111106
ctxt->print_error (
112107
ctxt,
@@ -117,6 +112,9 @@ exr_add_part (
117112
}
118113
}
119114

115+
rv = internal_exr_add_part (ctxt, &part, new_index);
116+
if (rv != EXR_ERR_SUCCESS) return EXR_UNLOCK_AND_RETURN (rv);
117+
120118
part->storage_mode = type;
121119
switch (type)
122120
{

src/lib/OpenEXRCore/string.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
exr_result_t
1616
exr_attr_string_init (exr_context_t ctxt, exr_attr_string_t* s, int32_t len)
1717
{
18-
exr_attr_string_t nil = {0};
1918
if (!ctxt) return EXR_ERR_MISSING_CONTEXT_ARG;
2019

2120
if (len < 0)
@@ -31,10 +30,10 @@ exr_attr_string_init (exr_context_t ctxt, exr_attr_string_t* s, int32_t len)
3130
EXR_ERR_INVALID_ARGUMENT,
3231
"Invalid reference to string object to initialize");
3332

34-
*s = nil;
3533
s->str = (char*) ctxt->alloc_fn ((size_t) (len + 1));
3634
if (s->str == NULL)
3735
return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
36+
3837
s->length = len;
3938
s->alloc_size = len + 1;
4039
return EXR_ERR_SUCCESS;
@@ -46,7 +45,6 @@ exr_result_t
4645
exr_attr_string_init_static_with_length (
4746
exr_context_t ctxt, exr_attr_string_t* s, const char* v, int32_t len)
4847
{
49-
exr_attr_string_t nil = {0};
5048
if (!ctxt) return EXR_ERR_MISSING_CONTEXT_ARG;
5149

5250
if (len < 0)
@@ -68,9 +66,9 @@ exr_attr_string_init_static_with_length (
6866
EXR_ERR_INVALID_ARGUMENT,
6967
"Invalid reference to string object to initialize");
7068

71-
*s = nil;
72-
s->length = len;
73-
s->str = v;
69+
s->length = len;
70+
s->alloc_size = 0;
71+
s->str = v;
7472
return EXR_ERR_SUCCESS;
7573
}
7674

@@ -132,7 +130,11 @@ exr_attr_string_create_with_length (
132130
# pragma GCC diagnostic ignored "-Wstringop-truncation"
133131
#endif
134132
if (d)
135-
strncpy (outs, d, (size_t) len);
133+
{
134+
size_t slen = strnlen( d, (size_t) len);
135+
memcpy (outs, d, slen);
136+
memset (outs + slen, '\0', ((size_t)len - slen) + 1);
137+
}
136138
else
137139
memset (outs, 0, (size_t) len);
138140
#ifdef _MSC_VER
@@ -143,7 +145,7 @@ exr_attr_string_create_with_length (
143145
# pragma GCC diagnostic pop
144146
#endif
145147
}
146-
outs[len] = '\0';
148+
else outs[0] = '\0';
147149
}
148150
return rv;
149151
}
@@ -210,9 +212,13 @@ exr_attr_string_set_with_length (
210212
# pragma GCC diagnostic ignored "-Wstringop-truncation"
211213
#endif
212214
if (d)
213-
strncpy (sstr, d, (size_t) len);
215+
{
216+
size_t slen = strnlen( d, (size_t) len);
217+
memcpy (sstr, d, slen);
218+
memset (sstr + slen, '\0', ((size_t)len - slen) + 1);
219+
}
214220
else
215-
memset (sstr, 0, (size_t) len);
221+
memset (sstr, 0, (size_t) (len + 1));
216222
#ifdef _MSC_VER
217223
# pragma warning(pop)
218224
#elif defined(__clang__)
@@ -221,7 +227,7 @@ exr_attr_string_set_with_length (
221227
# pragma GCC diagnostic pop
222228
#endif
223229
}
224-
sstr[len] = '\0';
230+
else sstr[0] = '\0';
225231
return EXR_ERR_SUCCESS;
226232
}
227233
exr_attr_string_destroy (ctxt, s);

src/test/OpenEXRCoreTest/general_attr.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1022,7 +1022,6 @@ testBytesHelper (exr_context_t f)
10221022

10231023
EXRCORE_TEST_RVAL (exr_attr_bytes_init (f, &b, 0, 0));
10241024
EXRCORE_TEST_RVAL (exr_attr_bytes_destroy (f, &b));
1025-
EXRCORE_TEST_RVAL (exr_attr_bytes_init (f, &b, 0, 0));
10261025
EXRCORE_TEST_RVAL (exr_attr_bytes_create (f, &b, 0, 4, NULL, data4));
10271026
EXRCORE_TEST_RVAL (exr_attr_bytes_copy (f, &b2, &b));
10281027
EXRCORE_TEST_RVAL (exr_attr_bytes_destroy (f, &b2));

src/test/OpenEXRCoreTest/write.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,20 @@ testStartWriteScan (const std::string& tempdir)
8888
EXRCORE_TEST_RVAL (
8989
exr_add_part (outf, "beauty", EXR_STORAGE_SCANLINE, &partidx));
9090
EXRCORE_TEST (partidx == 0);
91-
EXRCORE_TEST_RVAL (exr_get_count (outf, &partidx));
91+
/* dup name check */
92+
EXRCORE_TEST_RVAL_FAIL (
93+
EXR_ERR_INVALID_ARGUMENT,
94+
exr_add_part (outf, "beauty", EXR_STORAGE_SCANLINE, &partidx));
95+
/* this add should work, making 2 parts */
96+
EXRCORE_TEST_RVAL (
97+
exr_add_part (outf, "other", EXR_STORAGE_SCANLINE, &partidx));
9298
EXRCORE_TEST (partidx == 1);
99+
/* test repeated add failure doesn't leak */
100+
EXRCORE_TEST_RVAL_FAIL (
101+
EXR_ERR_INVALID_ARGUMENT,
102+
exr_add_part (outf, "beauty", EXR_STORAGE_SCANLINE, &partidx));
103+
EXRCORE_TEST_RVAL (exr_get_count (outf, &partidx));
104+
EXRCORE_TEST (partidx == 2);
93105
partidx = 0;
94106

95107
exr_chunk_info_t cinfo;
@@ -105,7 +117,7 @@ testStartWriteScan (const std::string& tempdir)
105117
exr_get_name (outf, partidx - 1, &partname));
106118
EXRCORE_TEST_RVAL_FAIL (
107119
EXR_ERR_ARGUMENT_OUT_OF_RANGE,
108-
exr_get_name (outf, partidx + 1, &partname));
120+
exr_get_name (outf, partidx + 2, &partname));
109121
EXRCORE_TEST_RVAL_FAIL (
110122
EXR_ERR_INVALID_ARGUMENT, exr_get_name (outf, partidx, NULL));
111123
EXRCORE_TEST_RVAL (exr_get_name (outf, partidx, &partname));
@@ -119,7 +131,7 @@ testStartWriteScan (const std::string& tempdir)
119131
exr_get_storage (outf, partidx - 1, &storage));
120132
EXRCORE_TEST_RVAL_FAIL (
121133
EXR_ERR_ARGUMENT_OUT_OF_RANGE,
122-
exr_get_storage (outf, partidx + 1, &storage));
134+
exr_get_storage (outf, partidx + 2, &storage));
123135
EXRCORE_TEST_RVAL_FAIL (
124136
EXR_ERR_INVALID_ARGUMENT, exr_get_storage (outf, partidx, NULL));
125137
EXRCORE_TEST_RVAL (exr_get_storage (outf, partidx, &storage));

0 commit comments

Comments
 (0)