From 42098fd8c1ec72d412b66a57009d4f5ef4e61a6a Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Fri, 25 Jul 2025 16:39:38 -0400 Subject: [PATCH] pod: Add validation to spa_pod_get_values() Return no values if the size is too small for its type. Return at most one value if the size is misaligned for its type. Document all of this. --- doc/dox/api/spa-pod.dox | 12 ++++-- spa/include/spa/pod/body.h | 81 ++++++++++++++++++++++++++++++++------ spa/include/spa/pod/iter.h | 2 +- 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/doc/dox/api/spa-pod.dox b/doc/dox/api/spa-pod.dox index 0bba2a474..52818ef96 100644 --- a/doc/dox/api/spa-pod.dox +++ b/doc/dox/api/spa-pod.dox @@ -435,10 +435,14 @@ spa_pod_parser_get_object(&p, \endcode `spa_pod_get_values()` is a useful function. It returns a -`struct spa_pod*` with and array of values. For normal POD's -and choice none values, it simply returns the POD and one value. -For other choice values it returns the choice type and an array -of values: +`struct spa_pod*` with and array of values. For invalid PODs +it returns the POD and no values. For normal PODs it returns +the POD and one value. For choice values it returns the choice +type and an array of values. If the choice doesn't fit even a +single value, the array will have no values. If the choice is +of the `SPA_CHOICE_None` or the length of each element is +misaligned, the array will have one value. Otherwise, +the array will have the same length as the choice. \code{.c} struct spa_pod *value; diff --git a/spa/include/spa/pod/body.h b/spa/include/spa/pod/body.h index 07758c7e6..a32f7f6bc 100644 --- a/spa/include/spa/pod/body.h +++ b/spa/include/spa/pod/body.h @@ -78,6 +78,44 @@ SPA_API_POD_BODY uint32_t spa_pod_type_size(uint32_t type) return 0; } +SPA_API_POD_BODY uint32_t spa_pod_type_align(uint32_t type) +{ + switch (type) { + case SPA_TYPE_None: + case SPA_TYPE_Bytes: + case SPA_TYPE_String: + case SPA_TYPE_Bitmap: + return 1; + case SPA_TYPE_Bool: + case SPA_TYPE_Int: + return SPA_ALIGNOF(int32_t); + case SPA_TYPE_Id: + return SPA_ALIGNOF(uint32_t); + case SPA_TYPE_Long: + return SPA_ALIGNOF(int64_t); + case SPA_TYPE_Float: + return SPA_ALIGNOF(float); + case SPA_TYPE_Double: + return SPA_ALIGNOF(double); + case SPA_TYPE_Rectangle: + return SPA_ALIGNOF(struct spa_rectangle); + case SPA_TYPE_Fraction: + return SPA_ALIGNOF(struct spa_fraction); + case SPA_TYPE_Pointer: + return SPA_ALIGNOF(struct spa_pod_pointer_body); + case SPA_TYPE_Fd: + return SPA_ALIGNOF(int64_t); + case SPA_TYPE_Sequence: + case SPA_TYPE_Choice: + case SPA_TYPE_Struct: + case SPA_TYPE_Pod: + case SPA_TYPE_Object: + case SPA_TYPE_Array: + default: + return SPA_POD_ALIGN; + } +} + SPA_API_POD_BODY int spa_pod_body_from_data(void *data, size_t maxsize, off_t offset, size_t size, struct spa_pod *pod, const void **body) { @@ -326,13 +364,22 @@ SPA_API_POD_BODY int spa_pod_body_get_array(const struct spa_pod *pod, const voi *arr_body = SPA_PTROFF(body, sizeof(struct spa_pod_array_body), void); return 0; } -SPA_API_POD_BODY const void *spa_pod_array_body_get_values(const struct spa_pod_array *arr, +SPA_API_POD_BODY const void *spa_pod_array_body_get_values(const struct spa_pod_array *pod, const void *body, uint32_t *n_values, uint32_t *val_size, uint32_t *val_type) { - uint32_t child_size = arr->body.child.size; - *n_values = child_size ? (arr->pod.size - sizeof(arr->body)) / child_size : 0; - *val_size = child_size; - *val_type = arr->body.child.type; + uint32_t child_size = *val_size = pod->body.child.size; + uint32_t child_type = *val_type = pod->body.child.type; + uint32_t child_type_size = spa_pod_type_size(child_size); + uint32_t max_body_size = pod->pod.size - sizeof(pod->body); + + if (SPA_UNLIKELY(child_size < child_type_size) || + child_size == 0) { + *n_values = 0; + } else if (SPA_UNLIKELY(child_size != child_type_size) && + SPA_UNLIKELY((child_size & (spa_pod_type_align(child_type) - 1)) != 0)) + *n_values = max_body_size >= child_size ? 1u : 0u; + else + *n_values = max_body_size / child_size; return body; } @@ -366,13 +413,23 @@ SPA_API_POD_BODY const void *spa_pod_choice_body_get_values(const struct spa_pod const void *body, uint32_t *n_values, uint32_t *choice, uint32_t *val_size, uint32_t *val_type) { - uint32_t child_size = pod->body.child.size; - *val_size = child_size; - *val_type = pod->body.child.type; - *n_values = child_size ? (pod->pod.size - sizeof(pod->body)) / child_size : 0; - *choice = pod->body.type; - if (*choice == SPA_CHOICE_None) - *n_values = SPA_MIN(1u, *n_values); + /* precondition check */ + spa_assert_se(pod->pod.size >= sizeof(pod->body)); + uint32_t child_size = *val_size = pod->body.child.size; + uint32_t child_type = *val_type = pod->body.child.type; + uint32_t child_type_size = spa_pod_type_size(child_size); + uint32_t choice_type = *choice = pod->body.type; + uint32_t max_body_size = pod->pod.size - sizeof(pod->body); + + if (SPA_UNLIKELY(child_size < child_type_size) || + child_size == 0) { + *n_values = 0; + } else if (choice_type == SPA_CHOICE_None || + (SPA_UNLIKELY(child_size != child_type_size) && + SPA_UNLIKELY((child_size & (spa_pod_type_align(child_type) - 1)) != 0))) + *n_values = max_body_size >= child_size ? 1 : 0; + else + *n_values = max_body_size / child_size; return body; } diff --git a/spa/include/spa/pod/iter.h b/spa/include/spa/pod/iter.h index a0da580ea..d3d580475 100644 --- a/spa/include/spa/pod/iter.h +++ b/spa/include/spa/pod/iter.h @@ -228,7 +228,7 @@ SPA_API_POD_ITER struct spa_pod *spa_pod_get_values(const struct spa_pod *pod, spa_pod_choice_body_get_values(p, SPA_POD_BODY_CONST(p), n_vals, choice, &size, &type); return (struct spa_pod*)&p->body.child; } else { - *n_vals = 1; + *n_vals = (pod->size >= spa_pod_type_size(pod->type) ? 1u : 0u); *choice = SPA_CHOICE_None; return (struct spa_pod*)pod; }