various: avoid function pointer casts

The opt validator functions are casted to generic validator, which has
erased type for value. Calling function by pointer of different
definition is an UB.

Avoid that by generating wrapper function that does proper argument type
conversion and calls validator function. Type erased functions have
mangled type in the name.

Fixes UBSAN failures on Clang 17, which enabled fsanitize=function by
default.
This commit is contained in:
Kacper Michajłow 2024-02-26 04:55:00 +01:00 committed by Dudemanguy
parent d955dfab29
commit 0897604298
10 changed files with 29 additions and 45 deletions

View File

@ -61,11 +61,6 @@ const char m_option_path_separator = OPTION_PATH_SEPARATOR;
#define OPT_INT_MAX(opt, T, Tm) ((opt)->min < (opt)->max \
? ((opt)->max >= (double)(Tm) ? (Tm) : (T)((opt)->max)) : (Tm))
#if defined(__clang__)
// Last argument of validate functions is always a pointer, but not always void*
// which triggers UBSAN warning.
__attribute__((no_sanitize("function")))
#endif
int m_option_parse(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param, void *dst)
{

View File

@ -189,15 +189,20 @@ struct m_opt_choice_alternatives {
const char *m_opt_choice_str(const struct m_opt_choice_alternatives *choices,
int value);
// Validator function signatures. Required to properly type the param value.
typedef int (*m_opt_generic_validate_fn)(struct mp_log *log, const m_option_t *opt,
struct bstr name, void *value);
typedef int (*m_opt_string_validate_fn)(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value);
typedef int (*m_opt_int_validate_fn)(struct mp_log *log, const m_option_t *opt,
struct bstr name, const int *value);
#define OPT_FUNC(name) name
#define OPT_FUNC_IN(name, suffix) name ## _ ## suffix
#define OPT_VALIDATE_FUNC(func, value_type, suffix) \
int OPT_FUNC(func)(struct mp_log *log, const m_option_t *opt, \
struct bstr name, value_type value); \
static inline int OPT_FUNC_IN(func, suffix)(struct mp_log *log, const m_option_t *opt, \
struct bstr name, void *value) { \
return OPT_FUNC(func)(log, opt, name, value); \
} \
int OPT_FUNC(func)(struct mp_log *log, const m_option_t *opt, \
struct bstr name, value_type value)
// m_option.priv points to this if OPT_SUBSTRUCT is used
struct m_sub_options {
@ -685,15 +690,17 @@ extern const char m_option_path_separator;
#define OPT_CHANNELS(field) \
OPT_TYPED_FIELD(m_option_type_channels, struct m_channels, field)
#define OPT_INT_VALIDATE_FUNC(func) OPT_VALIDATE_FUNC(func, const int *, int)
#define OPT_INT_VALIDATE(field, validate_fn) \
OPT_TYPED_FIELD(m_option_type_int, int, field), \
.validate = (m_opt_generic_validate_fn) \
MP_EXPECT_TYPE(m_opt_int_validate_fn, validate_fn)
.validate = OPT_FUNC_IN(validate_fn, int)
#define OPT_STRING_VALIDATE_FUNC(func) OPT_VALIDATE_FUNC(func, const char **, str)
#define OPT_STRING_VALIDATE(field, validate_fn) \
OPT_TYPED_FIELD(m_option_type_string, char*, field), \
.validate = (m_opt_generic_validate_fn) \
MP_EXPECT_TYPE(m_opt_string_validate_fn, validate_fn)
.validate = OPT_FUNC_IN(validate_fn, str)
#define M_CHOICES(...) \
.priv = (void *)&(const struct m_opt_choice_alternatives[]){ __VA_ARGS__, {0}}

View File

@ -65,8 +65,7 @@ static int drm_connector_opt_help(struct mp_log *log, const struct m_option *opt
static int drm_mode_opt_help(struct mp_log *log, const struct m_option *opt,
struct bstr name);
static int drm_validate_mode_opt(struct mp_log *log, const struct m_option *opt,
struct bstr name, const char **value);
static OPT_STRING_VALIDATE_FUNC(drm_validate_mode_opt);
static void drm_show_available_modes(struct mp_log *log, const drmModeConnector *connector);

View File

@ -129,8 +129,7 @@ static int ra_ctx_api_help(struct mp_log *log, const struct m_option *opt,
return M_OPT_EXIT;
}
static int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
struct bstr name, const char **value)
static inline OPT_STRING_VALIDATE_FUNC(ra_ctx_validate_api)
{
struct bstr param = bstr0(*value);
if (bstr_equals0(param, "auto"))
@ -154,8 +153,7 @@ static int ra_ctx_context_help(struct mp_log *log, const struct m_option *opt,
return M_OPT_EXIT;
}
static int ra_ctx_validate_context(struct mp_log *log, const struct m_option *opt,
struct bstr name, const char **value)
static inline OPT_STRING_VALIDATE_FUNC(ra_ctx_validate_context)
{
struct bstr param = bstr0(*value);
if (bstr_equals0(param, "auto"))

View File

@ -69,9 +69,7 @@ IDXGIAdapter1 *mp_get_dxgi_adapter(struct mp_log *log,
bstr requested_adapter_name,
bstr *listing);
int mp_dxgi_validate_adapter(struct mp_log *log,
const struct m_option *opt,
struct bstr name, const char **value);
OPT_STRING_VALIDATE_FUNC(mp_dxgi_validate_adapter);
bool mp_dxgi_list_or_verify_adapters(struct mp_log *log,
bstr adapter_name,

View File

@ -18,12 +18,8 @@ struct ra_hwdec_ctx {
int num_hwdecs;
};
int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value);
int ra_hwdec_validate_drivers_only_opt(struct mp_log *log,
const m_option_t *opt,
struct bstr name, const char **value);
OPT_STRING_VALIDATE_FUNC(ra_hwdec_validate_opt);
OPT_STRING_VALIDATE_FUNC(ra_hwdec_validate_drivers_only_opt);
void ra_hwdec_ctx_init(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs,
const char *opt, bool load_all_by_default);

View File

@ -494,8 +494,7 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
#endif
static int validate_3dlut_size_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value)
static inline OPT_STRING_VALIDATE_FUNC(validate_3dlut_size_opt)
{
int p1, p2, p3;
return gl_parse_3dlut_size(*value, &p1, &p2, &p3) ? 0 : M_OPT_INVALID;

View File

@ -329,14 +329,9 @@ static const struct gl_video_opts gl_video_opts_def = {
.hwdec_interop = "auto",
};
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value);
static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value);
static int validate_error_diffusion_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value);
static OPT_STRING_VALIDATE_FUNC(validate_scaler_opt);
static OPT_STRING_VALIDATE_FUNC(validate_window_opt);
static OPT_STRING_VALIDATE_FUNC(validate_error_diffusion_opt);
#define OPT_BASE_STRUCT struct gl_video_opts

View File

@ -37,8 +37,7 @@ struct vulkan_opts {
bool async_compute;
};
static int vk_validate_dev(struct mp_log *log, const struct m_option *opt,
struct bstr name, const char **value)
static inline OPT_STRING_VALIDATE_FUNC(vk_validate_dev)
{
struct bstr param = bstr0(*value);
int ret = M_OPT_INVALID;

View File

@ -45,9 +45,7 @@
#define DEV_PATH_DEFAULT "/dev/dri/renderD128"
#define DEV_PATH_VALIDATE validate_path
static int validate_path(struct mp_log *log,
const struct m_option *opt,
struct bstr name, const char **value)
static inline OPT_STRING_VALIDATE_FUNC(validate_path)
{
return (*value && **value) ? 0 : M_OPT_INVALID;
}