ctl: add dimension validator

Linux 4.7 or former have no validator of dimension information. This can
causes an issue related to user-defined element set. For example, When
calculated total members in multi-dimensional matrix is larger than actual
capacity of snd_ctl_elem_value_t, processes to handle the element can
cause buffer-over-run.

For backward portability of this userspace library, this commit adds a
validator of dimension information. When userspace applications give
invalid dimension information to APIs to add element set, they receive
-EINVAL.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Sakamoto 2016-07-08 06:33:17 +09:00 committed by Takashi Iwai
parent 3ef712844f
commit 025e451b83

View file

@ -91,6 +91,7 @@ I/O operations.
#include <fcntl.h>
#include <signal.h>
#include <sys/poll.h>
#include <stdbool.h>
#include "control_local.h"
/**
@ -302,6 +303,32 @@ int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)
return ctl->ops->element_info(ctl, info);
}
static bool validate_element_member_dimension(snd_ctl_elem_info_t *info)
{
unsigned int members;
unsigned int i;
if (info->dimen.d[0] == 0)
return true;
members = 1;
for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) {
if (info->dimen.d[i] == 0)
break;
members *= info->dimen.d[i];
if (members > info->count)
return false;
}
for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) {
if (info->dimen.d[i] > 0)
return false;
}
return members == info->count;
}
/**
* \brief Create and add some user-defined control elements of integer type.
* \param ctl A handle of backend module for control interface.
@ -366,6 +393,9 @@ int snd_ctl_elem_add_integer_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
info->value.integer.max = max;
info->value.integer.step = step;
if (!validate_element_member_dimension(info))
return -EINVAL;
err = ctl->ops->element_add(ctl, info);
if (err < 0)
return err;
@ -451,6 +481,9 @@ int snd_ctl_elem_add_integer64_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
info->value.integer64.max = max;
info->value.integer64.step = step;
if (!validate_element_member_dimension(info))
return -EINVAL;
err = ctl->ops->element_add(ctl, info);
if (err < 0)
return err;
@ -524,6 +557,9 @@ int snd_ctl_elem_add_boolean_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
info->value.integer.min = 0;
info->value.integer.max = 1;
if (!validate_element_member_dimension(info))
return -EINVAL;
return ctl->ops->element_add(ctl, info);
}
@ -605,6 +641,9 @@ int snd_ctl_elem_add_enumerated_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
p += strlen(labels[i]) + 1;
}
if (!validate_element_member_dimension(info))
return -EINVAL;
err = ctl->ops->element_add(ctl, info);
free(buf);
@ -663,6 +702,9 @@ int snd_ctl_elem_add_bytes_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
info->owner = element_count;
info->count = member_count;
if (!validate_element_member_dimension(info))
return -EINVAL;
return ctl->ops->element_add(ctl, info);
}