SPA POD parser: fix several integer overflows

This fixes several integer overflow problems in the POD parser, as well
as fixing a returns-twice warning from GCC and integer truncation
problems in SPA_FLAG_CLEAR and SPA_ROUND_DOWN_N.  The integer overflows
can result in a tiny POD being treated as a huge one, causing
out-of-bounds reads.
This commit is contained in:
Demi Marie Obenour 2022-07-30 13:10:19 -04:00 committed by Wim Taymans
parent 0e4df09e53
commit 1e848fc299
7 changed files with 59 additions and 16 deletions

View file

@ -3,7 +3,7 @@ project('pipewire', ['c' ],
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
meson_version : '>= 0.59.0',
default_options : [ 'warning_level=3',
'c_std=gnu99',
'c_std=gnu11',
'cpp_std=c++17',
'b_pie=true',
#'b_sanitize=address,undefined',

View file

@ -161,8 +161,9 @@ static inline int spa_buffer_alloc_fill_info(struct spa_buffer_alloc_info *info,
*target += info->chunk_size;
for (i = 0, size = 0; i < n_datas; i++) {
int64_t align = data_aligns[i];
info->max_align = SPA_MAX(info->max_align, data_aligns[i]);
size = SPA_ROUND_UP_N(size, data_aligns[i]);
size = SPA_ROUND_UP_N(size, align);
size += datas[i].maxsize;
}
info->data_size = size;

View file

@ -82,12 +82,20 @@ spa_pod_parser_reset(struct spa_pod_parser *parser, struct spa_pod_parser_state
static inline struct spa_pod *
spa_pod_parser_deref(struct spa_pod_parser *parser, uint32_t offset, uint32_t size)
{
if (offset + 8 <= size) {
struct spa_pod *pod = SPA_PTROFF(parser->data, offset, struct spa_pod);
if (offset + SPA_POD_SIZE(pod) <= size)
return pod;
/* Cast to uint64_t to avoid wraparound. Add 8 for the pod itself. */
const uint64_t long_offset = (uint64_t)offset + 8;
if (long_offset <= size && (offset & 7) == 0) {
/* Use void* because creating a misaligned pointer is undefined. */
void *pod = SPA_PTROFF(parser->data, offset, void);
/*
* Check that the pointer is aligned and that the size (rounded
* to the next multiple of 8) is in bounds.
*/
if (SPA_IS_ALIGNED(pod, 8) &&
long_offset + SPA_ROUND_UP_N((uint64_t)SPA_POD_BODY_SIZE(pod), 8) <= size)
return (struct spa_pod *)pod;
}
return NULL;
return NULL;
}
static inline struct spa_pod *spa_pod_parser_frame(struct spa_pod_parser *parser, struct spa_pod_frame *frame)

View file

@ -39,7 +39,7 @@ extern "C" {
#define SPA_POD_BODY_SIZE(pod) (((struct spa_pod*)(pod))->size)
#define SPA_POD_TYPE(pod) (((struct spa_pod*)(pod))->type)
#define SPA_POD_SIZE(pod) (sizeof(struct spa_pod) + SPA_POD_BODY_SIZE(pod))
#define SPA_POD_SIZE(pod) ((uint64_t)sizeof(struct spa_pod) + SPA_POD_BODY_SIZE(pod))
#define SPA_POD_CONTENTS_SIZE(type,pod) (SPA_POD_SIZE(pod)-sizeof(type))
#define SPA_POD_CONTENTS(type,pod) SPA_PTROFF((pod),sizeof(type),void)

View file

@ -119,7 +119,7 @@ struct spa_system_methods {
#define spa_system_method_r(o,method,version,...) \
({ \
int _res = -ENOTSUP; \
volatile int _res = -ENOTSUP; \
struct spa_system *_o = o; \
spa_interface_call_res(&_o->iface, \
struct spa_system_methods, _res, \

View file

@ -27,8 +27,18 @@
#ifdef __cplusplus
extern "C" {
# if __cplusplus >= 201103L
# define SPA_STATIC_ASSERT static_assert
# endif
#else
#include <stdbool.h>
# include <stdbool.h>
# if __STDC_VERSION__ >= 201112L
# define SPA_STATIC_ASSERT _Static_assert
# endif
#endif
#ifndef SPA_STATIC_ASSERT
#define SPA_STATIC_ASSERT(a, b) \
((void)sizeof(struct { int spa_static_assertion_failed : 2 * !!(a) - 1; }))
#endif
#include <inttypes.h>
#include <signal.h>
@ -74,8 +84,16 @@ extern "C" {
#define SPA_FLAG_MASK(field,mask,flag) (((field) & (mask)) == (flag))
#define SPA_FLAG_IS_SET(field,flag) SPA_FLAG_MASK(field,flag,flag)
#define SPA_FLAG_SET(field,flag) ((field) |= (flag))
#define SPA_FLAG_CLEAR(field,flag) ((field) &= ~(flag))
#define SPA_FLAG_UPDATE(field,flag,val) ((val) ? SPA_FLAG_SET(field,flag) : SPA_FLAG_CLEAR(field,flag))
#define SPA_FLAG_CLEAR(field, flag) \
({ \
SPA_STATIC_ASSERT(__builtin_constant_p(flag) ? \
(__typeof__(flag))(__typeof__(field))(__typeof__(flag))(flag) == (flag) : \
sizeof(field) >= sizeof(flag), \
"truncation problem when masking " #field \
" with ~" #flag); \
((field) &= ~(__typeof__(field))(flag)); \
})
#define SPA_FLAG_UPDATE(field,flag,val) ((val) ? SPA_FLAG_SET((field),(flag)) : SPA_FLAG_CLEAR((field),(flag)))
enum spa_direction {
SPA_DIRECTION_INPUT = 0,
@ -239,7 +257,17 @@ struct spa_fraction {
#define SPA_ROUND_DOWN(num,value) ((num) - ((num) % (value)))
#define SPA_ROUND_UP(num,value) ((((num) + (value) - 1) / (value)) * (value))
#define SPA_ROUND_DOWN_N(num,align) ((num) & ~((align) - 1))
#define SPA_MASK_NEGATED(num1, num2) \
({ \
SPA_STATIC_ASSERT(__builtin_constant_p(num2) ? \
(__typeof__(num2))(__typeof__(num1))(__typeof__(num2))(num2) == (num2) : \
sizeof(num1) >= sizeof(num2), \
"truncation problem when masking " #num1 \
" with ~" #num2); \
((num1) & ~(__typeof__(num1))(num2)); \
})
#define SPA_ROUND_DOWN_N(num,align) SPA_MASK_NEGATED((num), (align) - 1)
#define SPA_ROUND_UP_N(num,align) SPA_ROUND_DOWN_N((num) + ((align) - 1),align)
#define SPA_PTR_ALIGNMENT(p,align) ((intptr_t)(p) & ((align)-1))

View file

@ -31,13 +31,19 @@ int pw_protocol_native_connect_portal_screencast(struct pw_protocol_client *clie
void (*done_callback) (void *data, int res),
void *data);
static inline void *get_first_pod_from_data(void *data, size_t maxsize, off_t offset)
static inline void *get_first_pod_from_data(void *data, uint32_t maxsize, uint64_t offset)
{
void *pod;
if (offset + sizeof(struct spa_pod) > maxsize)
if (maxsize <= offset)
return NULL;
/* spa_pod_parser_advance() rounds up, so round down here to compensate */
maxsize = SPA_ROUND_DOWN_N(maxsize - offset, 8);
if (maxsize < sizeof(struct spa_pod))
return NULL;
pod = SPA_PTROFF(data, offset, void);
if (offset + SPA_POD_SIZE(pod) > maxsize)
if (SPA_POD_BODY_SIZE(pod) > maxsize - sizeof(struct spa_pod))
return NULL;
return pod;
}