diff --git a/stuffer/s2n_stuffer.h b/stuffer/s2n_stuffer.h index abf294cce80..05e46d49006 100644 --- a/stuffer/s2n_stuffer.h +++ b/stuffer/s2n_stuffer.h @@ -45,7 +45,11 @@ struct s2n_stuffer { uint32_t write_cursor; uint32_t high_water_mark; - /* Was this stuffer alloc()'d ? */ + /* Was this stuffer alloc()'d? + * This field controls whether the stuffer "owns" the blob. If the stuffer + * was allocated, then `blob` must be freed when the stuffer is freed. If the + * stuffer was not allocated, then the blob must not be freed by the stuffer, even if the + * blob itself is allocated. */ unsigned int alloced : 1; /* Is this stuffer growable? */ diff --git a/utils/s2n_blob.c b/utils/s2n_blob.c index 58cd7b0a72b..ecf0e81709d 100644 --- a/utils/s2n_blob.c +++ b/utils/s2n_blob.c @@ -29,6 +29,7 @@ S2N_RESULT s2n_blob_validate(const struct s2n_blob *b) RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->size == 0), S2N_ERR_SAFETY); RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->allocated == 0), S2N_ERR_SAFETY); RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 0, b->allocated == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 1, b->allocated > 0 || b->size == 0), S2N_ERR_SAFETY); RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable != 0, b->size <= b->allocated), S2N_ERR_SAFETY); RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->allocated), S2N_ERR_SAFETY); RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->size), S2N_ERR_SAFETY); diff --git a/utils/s2n_blob.h b/utils/s2n_blob.h index 25e6b7a7646..8e40bb600fa 100644 --- a/utils/s2n_blob.h +++ b/utils/s2n_blob.h @@ -30,11 +30,21 @@ struct s2n_blob { /* The amount of memory allocated for this blob (i.e. the amount of memory * which needs to be freed when the blob is cleaned up). If this blob was * created with s2n_blob_init(), this value is 0. If s2n_alloc() was called, - * this value will be greater than 0. + * this value will be greater than or equal to size. + * + * size < allocated implies that an allocated blob is being reused to store + * a smaller amount of data. */ uint32_t allocated; - /* Can this blob be resized */ + /* An allocated blob (e.g.`s2n_alloc`) is always growable. A "reference" + * blob (from `s2n_blob_init`) is never growable. + * + * This field is necessary to distinguish zero-sized allocated blobs from + * zero-sized "reference" blobs. Zero-sized allocated blobs can not be + * constructed with s2n_alloc or s2n_realloc, but they are directly initialized + * in s2n_free_object. + */ unsigned growable : 1; }; diff --git a/utils/s2n_ensure.h b/utils/s2n_ensure.h index 866db937db0..b7247eadf78 100644 --- a/utils/s2n_ensure.h +++ b/utils/s2n_ensure.h @@ -120,6 +120,9 @@ void *s2n_ensure_memmove_trace(void *to, const void *from, size_t size); #define S2N_OBJECT_PTR_IS_READABLE(ptr) ((ptr) != NULL) #define S2N_OBJECT_PTR_IS_WRITABLE(ptr) ((ptr) != NULL) +/** + * If `a` is true, then `b` must be true. + */ #define S2N_IMPLIES(a, b) (!(a) || (b)) /** * If and only if (iff) is a biconditional logical connective between statements a and b.