diff options
author | Nicolai Hähnle <[email protected]> | 2019-05-13 16:58:08 +0200 |
---|---|---|
committer | Marek Olšák <[email protected]> | 2019-06-12 18:30:25 -0400 |
commit | de8a919702a377207e83434ab40d91c3013ed96c (patch) | |
tree | ba800d0375de39a447a6690b448ae52807ab01f3 /src/util | |
parent | 71b45bae14be84ab09212c9ded758fa31e31e945 (diff) |
u_dynarray: turn util_dynarray_{grow, resize} into element-oriented macros
The main motivation for this change is API ergonomics: most operations
on dynarrays are really on elements, not on bytes, so it's weird to have
grow and resize as the odd operations out.
The secondary motivation is memory safety. Users of the old byte-oriented
functions would often multiply a number of elements with the element size,
which could overflow, and checking for overflow is tedious.
With this change, we only need to implement the overflow checks once.
The checks are cheap: since eltsize is a compile-time constant and the
functions should be inlined, they only add a single comparison and an
unlikely branch.
v2:
- ensure operations are no-op when allocation fails
- in util_dynarray_clone, call resize_bytes with a compile-time constant element size
v3:
- fix iris, lima, panfrost
Reviewed-by: Marek Olšák <[email protected]>
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/u_dynarray.h | 46 |
1 files changed, 32 insertions, 14 deletions
diff --git a/src/util/u_dynarray.h b/src/util/u_dynarray.h index 769c3820546..000feaa8349 100644 --- a/src/util/u_dynarray.h +++ b/src/util/u_dynarray.h @@ -29,6 +29,7 @@ #include <stdlib.h> #include <string.h> +#include <limits.h> #include "ralloc.h" #ifdef __cplusplus @@ -99,17 +100,18 @@ util_dynarray_ensure_cap(struct util_dynarray *buf, unsigned newcap) return (void *)((char *)buf->data + buf->size); } -static inline void * -util_dynarray_grow_cap(struct util_dynarray *buf, int diff) -{ - return util_dynarray_ensure_cap(buf, buf->size + diff); -} - /* use util_dynarray_trim to reduce the allocated storage */ -static inline void * -util_dynarray_resize(struct util_dynarray *buf, unsigned newsize) +MUST_CHECK static inline void * +util_dynarray_resize_bytes(struct util_dynarray *buf, unsigned nelts, size_t eltsize) { + if (unlikely(nelts > UINT_MAX / eltsize)) + return 0; + + unsigned newsize = nelts * eltsize; void *p = util_dynarray_ensure_cap(buf, newsize); + if (!p) + return 0; + buf->size = newsize; return p; @@ -120,14 +122,27 @@ util_dynarray_clone(struct util_dynarray *buf, void *mem_ctx, struct util_dynarray *from_buf) { util_dynarray_init(buf, mem_ctx); - util_dynarray_resize(buf, from_buf->size); - memcpy(buf->data, from_buf->data, from_buf->size); + if (util_dynarray_resize_bytes(buf, from_buf->size, 1)) + memcpy(buf->data, from_buf->data, from_buf->size); } -static inline void * -util_dynarray_grow(struct util_dynarray *buf, int diff) +MUST_CHECK static inline void * +util_dynarray_grow_bytes(struct util_dynarray *buf, unsigned ngrow, size_t eltsize) { - return util_dynarray_resize(buf, buf->size + diff); + unsigned growbytes = ngrow * eltsize; + + if (unlikely(ngrow > (UINT_MAX / eltsize) || + growbytes > UINT_MAX - buf->size)) + return 0; + + unsigned newsize = buf->size + growbytes; + void *p = util_dynarray_ensure_cap(buf, newsize); + if (!p) + return 0; + + buf->size = newsize; + + return p; } static inline void @@ -153,7 +168,10 @@ util_dynarray_trim(struct util_dynarray *buf) } } -#define util_dynarray_append(buf, type, v) do {type __v = (v); memcpy(util_dynarray_grow((buf), sizeof(type)), &__v, sizeof(type));} while(0) +#define util_dynarray_append(buf, type, v) do {type __v = (v); memcpy(util_dynarray_grow_bytes((buf), 1, sizeof(type)), &__v, sizeof(type));} while(0) +/* Returns a pointer to the space of the first new element (in case of growth) or NULL on failure. */ +#define util_dynarray_resize(buf, type, nelts) util_dynarray_resize_bytes(buf, (nelts), sizeof(type)) +#define util_dynarray_grow(buf, type, ngrow) util_dynarray_grow_bytes(buf, (ngrow), sizeof(type)) #define util_dynarray_top_ptr(buf, type) (type*)((char*)(buf)->data + (buf)->size - sizeof(type)) #define util_dynarray_top(buf, type) *util_dynarray_top_ptr(buf, type) #define util_dynarray_pop_ptr(buf, type) (type*)((char*)(buf)->data + ((buf)->size -= sizeof(type))) |