|
|
@@ -538,13 +538,28 @@ static inline int _talloc_free(const void *ptr)
|
|
|
final choice is the null context. */
|
|
|
void *child = TC_PTR_FROM_CHUNK(tc->child);
|
|
|
const void *new_parent = null_context;
|
|
|
+ struct talloc_chunk *old_parent = NULL;
|
|
|
if (unlikely(tc->child->refs)) {
|
|
|
struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
|
|
|
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
|
|
|
}
|
|
|
+ /* finding the parent here is potentially quite
|
|
|
+ expensive, but the alternative, which is to change
|
|
|
+ talloc to always have a valid tc->parent pointer,
|
|
|
+ makes realloc more expensive where there are a
|
|
|
+ large number of children.
|
|
|
+
|
|
|
+ The reason we need the parent pointer here is that
|
|
|
+ if _talloc_free_internal() fails due to references
|
|
|
+ or a failing destructor we need to re-parent, but
|
|
|
+ the free call can invalidate the prev pointer.
|
|
|
+ */
|
|
|
+ if (new_parent == null_context && (tc->child->refs || tc->child->destructor)) {
|
|
|
+ old_parent = talloc_parent_chunk(ptr);
|
|
|
+ }
|
|
|
if (unlikely(_talloc_free(child) == -1)) {
|
|
|
if (new_parent == null_context) {
|
|
|
- struct talloc_chunk *p = talloc_parent_chunk(ptr);
|
|
|
+ struct talloc_chunk *p = old_parent;
|
|
|
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
|
|
|
}
|
|
|
__talloc_steal(new_parent, child);
|