1
Fork 0

spa: Bounds-check pointers before dereferencing them

Add a new overflow-safe function to check if region p2 of size s2 fits
completely in p1 of size s1 and, if it does, return the amount of bytes
in p1 that come after the end of p2.  Use this to bounds check the pod
iterators while ensuring that the pointer is bounds checked before being
dereferenced.

The spa_pod*_next() functions can still create an out-of-bounds pointer,
but this will not be dereferenced.  Fixing this requires either
additional complexity in these functions or forbidding POD structs,
objects, and sequences that have a length that is not a multiple of 8
bytes.

Fixes: 92ac9a355f ("spa: add spa_ptrinside")
Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
This commit is contained in:
Demi Marie Obenour 2023-12-21 14:17:53 -05:00 committed by Wim Taymans
parent c130ec9de9
commit 0227a30bcf
2 changed files with 56 additions and 14 deletions

View File

@ -28,7 +28,10 @@ struct spa_pod_frame {
static inline bool spa_pod_is_inside(const void *pod, uint32_t size, const void *iter)
{
return spa_ptrinside(pod, size, iter, SPA_POD_SIZE(iter));
size_t remaining;
return spa_ptr_type_inside(pod, size, iter, struct spa_pod, &remaining) &&
remaining >= SPA_POD_BODY_SIZE(iter);
}
static inline void *spa_pod_next(const void *iter)
@ -44,7 +47,10 @@ static inline struct spa_pod_prop *spa_pod_prop_first(const struct spa_pod_objec
static inline bool spa_pod_prop_is_inside(const struct spa_pod_object_body *body,
uint32_t size, const struct spa_pod_prop *iter)
{
return spa_ptrinside(body, size, iter, SPA_POD_PROP_SIZE(iter));
size_t remaining;
return spa_ptr_type_inside(body, size, iter, struct spa_pod_prop, &remaining) &&
remaining >= iter->value.size;
}
static inline struct spa_pod_prop *spa_pod_prop_next(const struct spa_pod_prop *iter)
@ -60,7 +66,10 @@ static inline struct spa_pod_control *spa_pod_control_first(const struct spa_pod
static inline bool spa_pod_control_is_inside(const struct spa_pod_sequence_body *body,
uint32_t size, const struct spa_pod_control *iter)
{
return spa_ptrinside(body, size, iter, SPA_POD_CONTROL_SIZE(iter));
size_t remaining;
return spa_ptr_type_inside(body, size, iter, struct spa_pod_control, &remaining) &&
remaining >= iter->value.size;
}
static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_control *iter)
@ -70,7 +79,7 @@ static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_
#define SPA_POD_ARRAY_BODY_FOREACH(body, _size, iter) \
for ((iter) = (__typeof__(iter))SPA_PTROFF((body), sizeof(struct spa_pod_array_body), void); \
spa_ptrinside(body, _size, iter, (body)->child.size); \
spa_ptrinside(body, _size, iter, (body)->child.size, NULL); \
(iter) = (__typeof__(iter))SPA_PTROFF((iter), (body)->child.size, void))
#define SPA_POD_ARRAY_FOREACH(obj, iter) \
@ -78,7 +87,7 @@ static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_
#define SPA_POD_CHOICE_BODY_FOREACH(body, _size, iter) \
for ((iter) = (__typeof__(iter))SPA_PTROFF((body), sizeof(struct spa_pod_choice_body), void); \
spa_ptrinside(body, _size, iter, (body)->child.size); \
spa_ptrinside(body, _size, iter, (body)->child.size, NULL); \
(iter) = (__typeof__(iter))SPA_PTROFF((iter), (body)->child.size, void))
#define SPA_POD_CHOICE_FOREACH(obj, iter) \

View File

@ -9,17 +9,25 @@
extern "C" {
# if __cplusplus >= 201103L
# define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) static_assert(expr, msg)
# define SPA_ALIGNOF alignof
# endif
#elif __STDC_VERSION__ >= 202311L
# define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) static_assert(expr, msg)
# define SPA_ALIGNOF alignof
#else
# include <stdbool.h>
# if __STDC_VERSION__ >= 201112L
# define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) _Static_assert(expr, msg)
# define SPA_ALIGNOF _Alignof
# endif
#endif
#ifndef SPA_STATIC_ASSERT_IMPL
#define SPA_STATIC_ASSERT_IMPL(expr, ...) \
((void)sizeof(struct { int spa_static_assertion_failed : 2 * !!(expr) - 1; }))
#endif
#ifndef SPA_ALIGNOF
#define SPA_ALIGNOF __alignof__
#endif
#define SPA_STATIC_ASSERT(expr, ...) SPA_STATIC_ASSERT_IMPL(expr, ## __VA_ARGS__, "`" #expr "` evaluated to false")
@ -191,15 +199,6 @@ struct spa_fraction {
#define SPA_PTRDIFF(p1,p2) ((intptr_t)(p1) - (intptr_t)(p2))
static inline bool spa_ptrinside(const void *p1, size_t s1, const void *p2, size_t s2)
{
return (uintptr_t)p1 <= (uintptr_t)p2 && s2 <= s1 &&
(uintptr_t)p2 - (uintptr_t)p1 <= s1 - s2;
}
#define SPA_PTR_TO_INT(p) ((int) ((intptr_t) (p)))
#define SPA_INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
#define SPA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
#define SPA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
@ -287,6 +286,40 @@ static inline bool spa_ptrinside(const void *p1, size_t s1, const void *p2, size
#endif
#endif
static inline bool spa_ptrinside(const void *p1, size_t s1, const void *p2, size_t s2,
size_t *remaining)
{
if (SPA_LIKELY((uintptr_t)p1 <= (uintptr_t)p2 && s2 <= s1 &&
(uintptr_t)p2 - (uintptr_t)p1 <= s1 - s2)) {
if (remaining != NULL)
*remaining = ((uintptr_t)p1 + s1) - ((uintptr_t)p2 + s2);
return true;
} else {
if (remaining != NULL)
*remaining = 0;
return false;
}
}
static inline bool spa_ptr_inside_and_aligned(const void *p1, size_t s1,
const void *p2, size_t s2, size_t align,
size_t *remaining)
{
if (SPA_IS_ALIGNED(p2, align)) {
return spa_ptrinside(p1, s1, p2, s2, remaining);
} else {
if (remaining != NULL)
*remaining = 0;
return false;
}
}
#define spa_ptr_type_inside(p1, s1, p2, type, remaining) \
spa_ptr_inside_and_aligned(p1, s1, p2, sizeof(type), SPA_ALIGNOF(type), remaining)
#define SPA_PTR_TO_INT(p) ((int) ((intptr_t) (p)))
#define SPA_INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
#define SPA_STRINGIFY_1(...) #__VA_ARGS__
#define SPA_STRINGIFY(...) SPA_STRINGIFY_1(__VA_ARGS__)