]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Various extensions fixes
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 5 Dec 2020 04:33:16 +0000 (21:33 -0700)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 5 Dec 2020 04:33:16 +0000 (21:33 -0700)
src/lib/util/dict_ext.c
src/lib/util/dict_ext.h
src/lib/util/dict_ext_priv.h
src/lib/util/ext.c
src/lib/util/ext.h

index d219564e84ad47c995954c1ff4d62ea44f91298f..82cd32ad79c3a60d32acfd26ce60f2efec10a637 100644 (file)
@@ -41,34 +41,46 @@ static fr_table_num_ordered_t const dict_attr_ext_table[] = {
 };
 static size_t dict_attr_ext_table_len = NUM_ELEMENTS(dict_attr_ext_table);
 
+/** Fixup name pointer on realloc
+ *
+ */
+static int fr_dict_attr_ext_name_fixup(UNUSED int ext,
+                                      TALLOC_CTX *chunk,
+                                      void *ext_ptr, UNUSED size_t ext_ptr_len)
+{
+       fr_dict_attr_t                  *da = talloc_get_type_abort(chunk, fr_dict_attr_t);
+
+       da->name = ext_ptr;
+
+       return 0;
+}
+
 /** Copy all enumeration values from one attribute to another
  *
  */
-static void *fr_dict_attr_ext_enumv_copy(void **chunk_p, int ext, void *ext_ptr, UNUSED size_t ext_len)
+static int fr_dict_attr_ext_enumv_copy(UNUSED int ext,
+                                      TALLOC_CTX *chunk,
+                                      void *dst_ext_ptr, UNUSED size_t dst_ext_len,
+                                      void *src_ext_ptr, UNUSED size_t src_ext_len)
 {
-       fr_dict_attr_t                  **da_p = (fr_dict_attr_t **)chunk_p;
-       fr_dict_attr_ext_enumv_t        *new_ext, *old_ext;
+       fr_dict_attr_t                  *da_src = talloc_get_type_abort(chunk, fr_dict_attr_t);
+       fr_dict_attr_ext_enumv_t        *dst_ext = dst_ext_ptr, *src_ext = src_ext_ptr;
        fr_hash_iter_t                  iter;
        fr_dict_enum_t                  *enumv;
-       bool                            has_child = fr_dict_attr_is_key_field(*da_p);
+       bool                            has_child = fr_dict_attr_is_key_field(da_src);
 
-       new_ext = dict_attr_ext_alloc(da_p, ext);
-       if (!new_ext) return NULL;
-
-       old_ext = ext_ptr;
-
-       if (!old_ext->value_by_name && !old_ext->name_by_value) {
-               memset(new_ext, 0, sizeof(*new_ext));
-               return new_ext;
+       if (!src_ext->value_by_name && !src_ext->name_by_value) {
+               memset(dst_ext, 0, sizeof(*dst_ext));
+               return 0;
        }
 
        /*
         *      Add all the enumeration values from
         *      the old attribute to the new attribute.
         */
-       for (enumv = fr_hash_table_iter_init(old_ext->value_by_name, &iter);
+       for (enumv = fr_hash_table_iter_init(src_ext->value_by_name, &iter);
             enumv;
-            enumv = fr_hash_table_iter_next(old_ext->value_by_name, &iter)) {
+            enumv = fr_hash_table_iter_next(src_ext->value_by_name, &iter)) {
                fr_dict_attr_t const *child_struct;
 
                if (!has_child) {
@@ -80,39 +92,43 @@ static void *fr_dict_attr_ext_enumv_copy(void **chunk_p, int ext, void *ext_ptr,
                /*
                 *      Fixme - Child struct copying is probably wrong
                 */
-               if (dict_attr_enum_add_name(*da_p, enumv->name, enumv->value, true, true, child_struct) < 0) {
-                       return NULL;
-               }
+               if (dict_attr_enum_add_name(da_src, enumv->name, enumv->value,
+                                           true, true, child_struct) < 0) return -1;
        }
 
-       return new_ext;
+       return 0;
 }
 
 /** Rediscover the parent of this attribute, and cache it
  *
  */
-static void *fr_dict_attr_ext_vendor_copy(void **chunk_p, int ext, void *ext_ptr, UNUSED size_t ext_len)
+static int fr_dict_attr_ext_vendor_copy(UNUSED int ext,
+                                       TALLOC_CTX *chunk,
+                                       void *dst_ext_ptr, UNUSED size_t dst_ext_len,
+                                       void *src_ext_ptr, UNUSED size_t src_ext_len)
 {
-       fr_dict_attr_t                  **da_p = (fr_dict_attr_t **)chunk_p;
-       fr_dict_attr_ext_vendor_t       *new_ext, *old_ext = ext_ptr;
+       fr_dict_attr_t                  *da_src = talloc_get_type_abort(chunk, fr_dict_attr_t);
+       fr_dict_attr_ext_vendor_t       *dst_ext = dst_ext_ptr, *src_ext = src_ext_ptr;
        fr_dict_attr_t const            **da_stack;
-       fr_dict_attr_t const            *old_vendor = old_ext->vendor;
+       fr_dict_attr_t const            *old_vendor = src_ext->vendor;
        fr_dict_attr_t const            *new_vendor, *da;
 
+       if (!old_vendor) {
+               dst_ext->vendor = NULL;
+               return 0;
+       }
+
        /*
         *      If we have a da stack, see if we can
         *      find a vendor at the same depth as
         *      the old depth.
         */
-       da_stack = fr_dict_attr_da_stack(*da_p);
+       da_stack = fr_dict_attr_da_stack(da_src);
        if (da_stack) {
                new_vendor = da_stack[old_vendor->depth];
                if ((new_vendor->type == old_vendor->type) && (new_vendor->attr == old_vendor->attr)) {
-                       new_ext = dict_attr_ext_alloc(da_p, ext);
-                       if (!new_ext) return NULL;
-
-                       new_ext->vendor = new_vendor;
-                       return new_ext;
+                       dst_ext->vendor = new_vendor;
+                       return 0;
                }
        }
 
@@ -123,17 +139,14 @@ static void *fr_dict_attr_ext_vendor_copy(void **chunk_p, int ext, void *ext_ptr
         *      Theoretically the attribute could
         *      have been moved to a different depth.
         */
-       for (da = (*da_p)->parent; da; da = da->parent) {
+       for (da = da_src->parent; da; da = da->parent) {
                if ((da->type == old_vendor->type) && (da->attr == old_vendor->attr)) {
-                       new_ext = dict_attr_ext_alloc(da_p, ext);
-                       if (!new_ext) return NULL;
-
-                       new_ext->vendor = da;
-                       return new_ext;
+                       dst_ext->vendor = da;
+                       return 0;
                }
        }
 
-       return NULL;
+       return -1;
 }
 
 /** Holds additional information about extension structures
@@ -148,6 +161,7 @@ fr_ext_t const fr_dict_attr_ext_def = {
                [FR_DICT_ATTR_EXT_NAME]         = {
                                                        .min = sizeof(char),
                                                        .has_hdr = true,
+                                                       .fixup = fr_dict_attr_ext_name_fixup,
                                                        .can_copy = false,      /* Name may change, and we can only set it once */
                                                },
                [FR_DICT_ATTR_EXT_CHILDREN]     = {
index edaa974e4824e41c4ca7986bb1a315671b47b88e..193af5fb9832193197bba58eb67a231d36bb616c 100644 (file)
@@ -33,6 +33,9 @@ RCSIDH(dict_ext_h, "$Id$")
 extern "C" {
 #endif
 
+extern fr_ext_t const fr_dict_attr_ext_def;
+extern fr_ext_t const fr_dict_enum_ext_def;
+
 /** Attribute extension - Holds children for an attribute
  *
  * Children are possible for:
@@ -117,7 +120,7 @@ static inline void *fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_
 {
        if (!da->ext[ext]) return NULL;
 
-       return FR_EXT_PTR(da, ext, ext);
+       return fr_ext_ptr(da, da->ext[ext], fr_dict_attr_ext_def.info[ext].has_hdr);
 }
 
 /** Return whether a da has a given extension or not
index 0af23c20a409fbd590ec7695200a6f090a0293d9..1e61227594e74b137711fa9e625567abac463bfb 100644 (file)
@@ -31,9 +31,6 @@ RCSIDH(dict_ext_priv_h, "$Id$")
 extern "C" {
 #endif
 
-extern fr_ext_t const fr_dict_attr_ext_def;
-extern fr_ext_t const fr_dict_enum_ext_def;
-
 /** @name Add extension structures to attributes
  *
  * @{
index bee011b29cd8dde32478abd0797c27b6d7e74c0a..436e25a01fff3741e1eb47c8e30391de28eb33bc 100644 (file)
@@ -30,9 +30,6 @@ RCSID("$Id$")
 #include <freeradius-devel/util/table.h>
 #include <freeradius-devel/util/talloc.h>
 
-#define CHUNK_EXT_PTR(_chunk, _chunk_ext, _ext) ((void *)((chunk_ext[_ext] * FR_EXT_ALIGNMENT) + ((uintptr_t)(_chunk))))
-#define CHUNK_EXT(_chunk, _offset) ((uint8_t *)(((uintptr_t)_chunk) + (_offset)))
-
 /** Add a variable length extension to a talloc chunk
  *
  * This is used to build a structure from a primary struct type and one or more
@@ -65,17 +62,16 @@ void *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext
 
        size_t                  offset;
 
-       fr_ext_info_t const     *info;
+       fr_ext_info_t const     *info = &def->info[ext];
        void                    *n_chunk, *chunk = *chunk_p;
-       uint8_t                 *chunk_ext;
+       uint8_t                 *ext_offsets;
        uint8_t                 *ext_ptr;
        char const              *type;
 
-       chunk_ext = CHUNK_EXT(*chunk_p, def->offset_of_exts);
-       if (chunk_ext[ext]) return CHUNK_EXT_PTR(*chunk_p, chunk_ext, ext);
+       ext_offsets = fr_ext_offsets(def, *chunk_p);
+       if (ext_offsets[ext]) return fr_ext_ptr(*chunk_p, ext_offsets[ext], info->has_hdr);
 
-       info = &def->info[ext];
-       if (info->has_hdr) hdr_len = sizeof(dict_ext_hdr_t);    /* Add space for a length prefix */
+       if (info->has_hdr) hdr_len = sizeof(fr_ext_hdr_t);      /* Add space for a length prefix */
 
        /*
         *      Packing the offsets into a uint8_t array
@@ -84,7 +80,7 @@ void *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext
         *      UINT8_MAX * FR_EXT_ALIGNMENT.
         */
        chunk_len = talloc_get_size(chunk);
-       offset = (chunk_len + hdr_len) / FR_EXT_ALIGNMENT;
+       offset = ROUND_UP_DIV(chunk_len, FR_EXT_ALIGNMENT);
        if (unlikely(offset > UINT8_MAX)) {
                fr_strerror_printf("Insufficient space remaining for extensions");
                return NULL;
@@ -96,22 +92,23 @@ void *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext
         *      record it and set it back again.
         */
        type = talloc_get_name(chunk);
-       n_chunk = talloc_realloc_size(NULL, chunk, chunk_len + hdr_len + aligned_len);
+       n_chunk = talloc_realloc_size(NULL, chunk, (offset * FR_EXT_ALIGNMENT) + hdr_len + aligned_len);
        if (!n_chunk) {
                fr_strerror_printf("Failed reallocing %s (%s).  Tried to realloc %zu bytes -> %zu bytes",
                                   type, fr_syserror(errno), chunk_len, chunk_len + aligned_len);
                return NULL;
        }
        talloc_set_name_const(n_chunk, type);
-       *chunk_p = n_chunk;
 
-       chunk_ext = CHUNK_EXT(*chunk_p, def->offset_of_exts);
-       chunk_ext[ext] = (uint8_t)offset;
+       ext_offsets = fr_ext_offsets(def, n_chunk);
+       ext_offsets[ext] = (uint8_t)offset;
 
        ext_ptr = ((uint8_t *)n_chunk) + chunk_len;
 
+       *chunk_p = n_chunk;
+
        if (info->has_hdr) {
-               dict_ext_hdr_t *ext_hdr = (dict_ext_hdr_t *)ext_ptr;
+               fr_ext_hdr_t *ext_hdr = (fr_ext_hdr_t *)ext_ptr;
 
                ext_hdr->len = ext_len;         /* Record the real size */
                return &ext_hdr->data;          /* Pointer to the data portion */
@@ -129,97 +126,190 @@ void *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext
  *     - 0 if no extension exists or is of zero length.
  *     - >0 the length of the extension.
  */
-size_t fr_ext_len(fr_ext_t const *def, void const *chunk, int ext)
+size_t fr_ext_len(fr_ext_t const *def, TALLOC_CTX const *chunk, int ext)
 {
        uint8_t                 offset;
        fr_ext_info_t const     *info;
-       dict_ext_hdr_t          *ext_hdr;
-       uint8_t                 *chunk_ext;
+       fr_ext_hdr_t            *ext_hdr;
+       uint8_t                 *ext_offsets;
 
-       chunk_ext = CHUNK_EXT(chunk, def->offset_of_exts);
-       offset = chunk_ext[ext];
+       ext_offsets = fr_ext_offsets(def, chunk);
+       offset = ext_offsets[ext];
        if (!offset) return 0;
 
        info = &def->info[ext];
-       if (!info->has_hdr) return info->min;   /* Fixed size */
+       if (!info->has_hdr) return info->min;           /* Fixed size */
 
-       ext_hdr = (dict_ext_hdr_t *)((uintptr_t)chunk) + ((offset * FR_EXT_ALIGNMENT) - sizeof(dict_ext_hdr_t));
+       ext_hdr = fr_ext_ptr(chunk, offset, false);     /* false as we're getting the header */
        return ext_hdr->len;
 }
 
 /** Copy extension data from one attribute to another
  *
  * @param[in] def              Extension definitions.
- * @param[in,out] chunk_out    to copy extension to.
- *                             Under certain circumstances the value of *chunk_out will
+ * @param[in,out] chunk_dst    to copy extension to.
+ *                             Under certain circumstances the value of *chunk_dst will
  *                             be changed to point to a new memory block.
  *                             All cached copies of the previous pointer should be
  *                             updated.
- * @param[in] chunk_in         to copy extension from.
+ * @param[in] chunk_src                to copy extension from.
  * @param[in] ext              to copy.
  * @return
  *     - NULL if we failed to allocate an extension structure.
  *     - A pointer to the offset of the extension in da_out.
  */
-void *fr_ext_copy(fr_ext_t const *def, void **chunk_out, void const *chunk_in, int ext)
+void *fr_ext_copy(fr_ext_t const *def, TALLOC_CTX **chunk_dst, TALLOC_CTX const *chunk_src, int ext)
 {
-       void    *ext_ptr, *new_ext_ptr;
-       uint8_t *chunk_ext;
-       size_t  ext_len;
+       int                     i;
+       uint8_t                 *ext_src_offsets = fr_ext_offsets(def, chunk_src);
+       uint8_t                 *ext_dst_offsets;
+       void                    *ext_src_ptr, *ext_dst_ptr;
+       fr_ext_info_t const     *info = &def->info[ext];
 
-       fr_ext_info_t const *info;
-
-       info = &def->info[ext];
        if (!info->can_copy) {
                fr_strerror_printf("Extension cannot be copied");
                return NULL;
        }
 
-       chunk_ext = CHUNK_EXT(chunk_in, def->offset_of_exts);
-       ext_ptr = CHUNK_EXT_PTR(chunk_in, chunk_ext, ext);
-       if (!ext_ptr) return NULL;
+       if (!ext_src_offsets[ext]) return NULL;
 
-       ext_len = fr_ext_len(def, chunk_in, ext);
+       ext_src_ptr = fr_ext_ptr(chunk_src, ext_src_offsets[ext], info->has_hdr);
 
+
+       if (info->alloc) {
+               ext_dst_ptr = info->alloc(def, chunk_dst, ext,
+                                         ext_src_ptr,
+                                         fr_ext_len(def, chunk_src, ext));
        /*
-        *      Use the special copy function.
-        *      Its responsible for allocating the extension in the
-        *      destination attribute.
+        *      If there's no special alloc function
+        *      we just allocate a chunk of the same
+        *      size.
         */
-       if (info->copy) return info->copy(chunk_out, ext, ext_ptr, ext_len);
+       } else {
+               ext_dst_ptr = fr_ext_alloc_size(def, chunk_dst, ext,
+                                               fr_ext_len(def, chunk_src, ext));
+       }
 
+       if (info->copy) {
+               info->copy(ext,
+                          *chunk_dst,
+                          ext_dst_ptr, fr_ext_len(def, *chunk_dst, ext),
+                          ext_src_ptr, fr_ext_len(def, chunk_src, ext));
        /*
-        *      If there's no special function
-        *      just memcpy the data over.
+        *      If there's no special copy function
+        *      we just copy the data from the old
+        *      extension to the new one.
         */
-       new_ext_ptr = fr_ext_alloc_size(def, chunk_out, ext, ext_len);
-       if (!new_ext_ptr) return NULL;
-       memcpy(new_ext_ptr, ext_ptr, ext_len);
+       } else {
+               memcpy(ext_dst_ptr, ext_src_ptr, fr_ext_len(def, *chunk_dst, ext));
+       }
+
+       /*
+        *      Call any fixup functions
+        */
+       ext_dst_offsets = fr_ext_offsets(def, *chunk_dst);
+       for (i = 0; i < def->max; i++) {
+               if (i == ext) continue;
+
+               if (!ext_dst_offsets[i]) continue;
+
+               if (info->fixup &&
+                   info->fixup(i, *chunk_dst,
+                               fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
+                               fr_ext_len(def, *chunk_dst, i)) < 0) return NULL;
+       }
 
-       return new_ext_ptr;
+       return ext_dst_ptr;
 }
 
 /** Copy all the extensions from one attribute to another
  *
  * @param[in] def              Extension definitions.
- * @param[in,out] chunk_out    to copy extensions to.
- *                             Under certain circumstances the value of *chunk_out will
+ * @param[in,out] chunk_dst    to copy extensions to.
+ *                             Under certain circumstances the value of *chunk_dst will
  *                             be changed to point to a new memory block.
  *                             All cached copies of the previous pointer should be
  *                             updated.
- * @param[in] chunk_in         to copy extensions from.
+ * @param[in] chunk_src                to copy extensions from.
  * @return
  *     - 0 on success.
  *     - -1 if a copy operation failed.
  */
-int fr_ext_copy_all(fr_ext_t const *def, void **chunk_out, void const *chunk_in)
+int fr_ext_copy_all(fr_ext_t const *def, TALLOC_CTX **chunk_dst, TALLOC_CTX const *chunk_src)
 {
        int     i;
-       uint8_t *ext_in = CHUNK_EXT(chunk_in, def->offset_of_exts);
+       uint8_t *ext_src_offsets = fr_ext_offsets(def, chunk_src);      /* old chunk array */
+       uint8_t *ext_dst_offsets = fr_ext_offsets(def, *chunk_dst);     /* new chunk array */
+       bool    ext_copied[def->max];
 
+       /*
+        *      Do the operation in two phases.
+        *
+        *      Phase 1 allocates space for all the extensions.
+        */
+       for (i = 0; i < def->max; i++) {
+               fr_ext_info_t const *info = &def->info[i];
+
+               if (!ext_src_offsets[i] || ext_dst_offsets[i] || !info->can_copy) {
+               no_copy:
+                       ext_copied[i] = false;
+                       continue;
+               }
+
+               if (info->alloc) {
+                       if (!info->alloc(def, chunk_dst, i,
+                                        fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
+                                        fr_ext_len(def, chunk_src, i))) goto no_copy;
+               /*
+                *      If there's no special alloc function
+                *      we just allocate a chunk of the same
+                *      size.
+                */
+               } else {
+                       fr_ext_alloc_size(def, chunk_dst, i, fr_ext_len(def, chunk_src, i));
+               }
+               ext_copied[i] = true;
+               ext_dst_offsets = fr_ext_offsets(def, *chunk_dst);      /* Grab new offsets, chunk might have changed */
+       }
+
+       /*
+        *      Phase 2 populates the extension memory.
+        *
+        *      We do this in two phases to avoid invalidating
+        *      any pointers from extensions back to the extended
+        *      talloc chunk.
+        */
        for (i = 0; i < def->max; i++) {
-               if (!ext_in[i] || !def->info[i].can_copy) continue;
-               if (!fr_ext_copy(def, chunk_out, chunk_in, i)) return -1;
+               fr_ext_info_t const *info = &def->info[i];
+
+               if (!ext_src_offsets[i] || !ext_dst_offsets[i]) continue;
+
+               if (!ext_copied[i]) {
+                       if (info->fixup &&
+                           info->fixup(i, *chunk_dst,
+                                       fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
+                                       fr_ext_len(def, *chunk_dst, i)) < 0) return -1;
+                       continue;
+               }
+               if (!info->can_copy) continue;
+
+               if (info->copy) {
+                       if (info->copy(i,
+                                      *chunk_dst,
+                                      fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
+                                      fr_ext_len(def, *chunk_dst, i),
+                                      fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
+                                      fr_ext_len(def, chunk_src, i)) < 0) return -1;
+               /*
+                *      If there's no special copy function
+                *      we just copy the data from the old
+                *      extension to the new one.
+                */
+               } else {
+                       memcpy(fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
+                              fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
+                              fr_ext_len(def, *chunk_dst, i));
+               }
        }
 
        return 0;
@@ -241,11 +331,10 @@ void fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk)
 
        FR_FAULT_LOG("%s ext total_len=%zu", name, talloc_get_size(chunk));
        for (i = 0; i < (int)def->max; i++) {
-               uint8_t *chunk_ext = CHUNK_EXT(chunk, def->offset_of_exts);
-               if (chunk_ext[i]) {
-                       void            *ext = CHUNK_EXT_PTR(chunk, chunk_ext, i);
+               uint8_t *ext_offsets = fr_ext_offsets(def, chunk);
+               if (ext_offsets[i]) {
+                       void            *ext = fr_ext_ptr(chunk, ext_offsets[i], def[i].info->has_hdr);
                        size_t          ext_len = fr_ext_len(def, chunk, i);
-
                        char const      *ext_name = fr_table_ordered_str_by_num(def->name_table,
                                                                                *def->name_table_len,
                                                                                i, "<INVALID>");
index 3cc51017cd236ee9d57cc7a6ac68b0248e8fc1c4..1b9cc23304fc4d398aaea8e638f21c3ec34a3955 100644 (file)
@@ -54,10 +54,54 @@ extern "C" {
 #  define FR_EXT_ALIGNMENT     sizeof(uint64_t)
 #endif
 
-/** Function for fixing up extensions after they're copied
+typedef struct fr_ext_s fr_ext_t;
+
+/** Function for pre-allocating extension memory for extensions before they're copied
+ *
+ * @param[in] def              Extension definitions.
+ * @param[in,out] dst_chunk_p  to add extensions to.
+ * @param[in] ext              that's being copied.
+ * @param[in] src_ext_ptr      Pointer for the src extension.
+ * @param[in] src_ext_len      Length of the src extension.
+ * @return
+ *     - NULL on error.
+ *     - Pointer to the new extension on success.
+ */
+typedef void *(* fr_ext_alloc_t)(fr_ext_t const *def, TALLOC_CTX **dst_chunk_p,
+                                int ext, void *src_ext_ptr, size_t src_ext_len);
+
+/** Function for re-populating extensions after they're copied
+ *
+ * @param[in] ext              that's being copied.
+ * @param[in] chunk            Talloc chunk we're copying to.
+ * @param[in] dst_ext_ptr      Pointer to the dst extension to populate.
+ * @param[in] dst_ext_len      The length of the dst extension.
+ * @param[in] src_ext_ptr      Pointer for the src extension.
+ * @param[in] src_ext_len      Length of the src extension.
+ * @return
+ *     - NULL on error.
+ *     - Pointer to the new extension on success.
+ */
+typedef int (* fr_ext_copy_t)(int ext, TALLOC_CTX *chunk,
+                             void *dst_ext_ptr, size_t dst_ext_len,
+                             void *src_ext_ptr, size_t src_ext_len);
+
+/** Function for re-establishing internal consistency on realloc
+ *
+ * In some cases the chunk may cache a pointer to an extension.
+ * On realloc this pointer may be invalidated.  This provides a
+ * callback to fixup consistency issues after a realloc.
  *
+ * @param[in] ext              that's being copied.
+ * @param[in] chunk            Talloc chunk.
+ * @param[in] ext_ptr          Pointer to the extension to fixup.
+ * @param[in] ext_len          The length of the extension to fixup.
+ * @return
+ *     - NULL on error.
+ *     - Pointer to the new extension on success.
  */
-typedef void *(* fr_ext_copy_t)(void **chunk_p, int ext, void *ext_ptr, size_t ext_len);
+typedef int (* fr_ext_fixup_t)(int ext, TALLOC_CTX *chunk,
+                              void *ext_ptr, size_t ext_len);
 
 /** Additional information for a given extension
  */
@@ -67,19 +111,22 @@ typedef struct {
                                                        ///< the extension data to record the exact length
                                                        ///< of the extension.
        bool                    can_copy;               //!< Copying this extension between structs is allowed.
+
+       fr_ext_alloc_t          alloc;                  //!< Override the normal alloc operation with a callback.
        fr_ext_copy_t           copy;                   //!< Override the normal copy operation with a callback.
+       fr_ext_fixup_t          fixup;                  //!< Callback for fixing up internal consistency issues.
 } fr_ext_info_t;
 
 /** Structure to define a set of extensions
  *
  */
-typedef struct {
+struct fr_ext_s {
        size_t                  offset_of_exts;         //!< Where in the extended struct the extensions array starts.
        fr_table_num_ordered_t const    *name_table;    //!< String identifiers for the extensions.
        size_t                  *name_table_len;        //!< How many extensions there are in the table.
        int                     max;                    //!< The highest extension value.
        fr_ext_info_t const     info[];                 //!< Additional information about each extension.
-} fr_ext_t;
+};
 
 /** Optional extension header struct
  *
@@ -87,25 +134,36 @@ typedef struct {
 typedef struct {
        size_t                  len;                    //!< Length of extension data.
        uint8_t                 data[];                 //!< Extension data
-} CC_HINT(aligned(FR_EXT_ALIGNMENT)) dict_ext_hdr_t;
+} CC_HINT(aligned(FR_EXT_ALIGNMENT)) fr_ext_hdr_t;
+
+static inline CC_HINT(always_inline) uint8_t *fr_ext_offsets(fr_ext_t const *def, TALLOC_CTX const *chunk)
+{
+       return (uint8_t *)(((uintptr_t)chunk) + def->offset_of_exts);
+}
 
-/** Return a pointer to the specified extension structure
+/** Return a pointer to an extension in a chunk
  *
- * @param[in] _ptr     to fetch extension for.
- * @param[in] _field   Array of extensions.
- * @param[in] _ext     to retrieve.
  */
-#define FR_EXT_PTR(_ptr, _field, _ext) ((void *)(((_ptr)->_field[_ext] * FR_EXT_ALIGNMENT) + ((uintptr_t)(_ptr))))
+static inline CC_HINT(always_inline) void *fr_ext_ptr(TALLOC_CTX const *chunk, size_t offset, bool has_hdr)
+{
+       uintptr_t out;
+
+       out = (uintptr_t)chunk;                                 /* chunk start */
+       out += offset * FR_EXT_ALIGNMENT;                       /* offset described by the extension */
+       out += sizeof(fr_ext_hdr_t) * (has_hdr == true);        /* data field offset by length header */
+
+       return (void *)out;
+}
 
-void   *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext_len);
+void   *fr_ext_alloc_size(fr_ext_t const *def, TALLOC_CTX **chunk_p, int ext, size_t ext_len);
 
-size_t fr_ext_len(fr_ext_t const *def, void const *chunk_in, int ext);
+size_t fr_ext_len(fr_ext_t const *def, TALLOC_CTX const *chunk_in, int ext);
 
-void   *fr_ext_copy(fr_ext_t const *def, void **chunk_out, void const *chunk_in, int ext);
+void   *fr_ext_copy(fr_ext_t const *def, TALLOC_CTX **chunk_out, TALLOC_CTX  const *chunk_in, int ext);
 
-int    fr_ext_copy_all(fr_ext_t const *def, void **chunk_out, void const *chunk_in);
+int    fr_ext_copy_all(fr_ext_t const *def, TALLOC_CTX **chunk_out, TALLOC_CTX  const *chunk_in);
 
-void   fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk);
+void   fr_ext_debug(fr_ext_t const *def, char const *name, TALLOC_CTX const *chunk);
 
 #ifdef __cplusplus
 }