[alsa-devel] [PATCH 2/3] ALSA: control: add dimension validator for userspace element

Takashi Iwai tiwai at suse.de
Wed Jul 6 15:34:59 CEST 2016


On Wed, 06 Jul 2016 15:07:41 +0200,
Takashi Sakamoto wrote:
> 
> Sorry to be late. I had a short vacation.
> 
> On Jul 2 2016 16:56, Takashi Iwai wrote:
> > On Fri, 01 Jul 2016 14:29:37 +0200,
> > Takashi Sakamoto wrote:
> >>
> >> On Jul 1 2016 20:10, Takashi Sakamoto wrote:
> >>> The 'dimen' field in struct snd_ctl_elem_info is used to compose all of
> >>> members in the element as multi-dimensional matrix. The field has four
> >>> members. Each member represents the width in each dimension level by
> >>> element member unit. For example, if the members consist of typical
> >>> two dimensional matrix, the dimen[0] represents the number of rows
> >>> and dimen[1] represents the number of columns (or vise-versa).
> >>>
> >>> The total members in the matrix should be within the number of members in
> >>> the element, while current implementation has no validator of this
> >>> information. In a view of userspace applications, the information must be
> >>> valid so that it cannot cause any bugs such as buffer-over-run.
> >>>
> >>> This commit adds a validator of dimension information for userspace
> >>> applications which add new element sets. When they add the element sets
> >>> with wrong dimension information, they receive -EINVAL.
> >>>
> >>> Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
> >>> ---
> >>>  sound/core/control.c | 30 ++++++++++++++++++++++++++++++
> >>>  1 file changed, 30 insertions(+)
> >>>
> >>> diff --git a/sound/core/control.c b/sound/core/control.c
> >>> index a85d455..54da910 100644
> >>> --- a/sound/core/control.c
> >>> +++ b/sound/core/control.c
> >>> @@ -805,6 +805,34 @@ static int snd_ctl_elem_list(struct snd_card *card,
> >>>  	return 0;
> >>>  }
> >>>  
> >>> +static bool validate_element_member_dimension(struct snd_ctl_elem_info *info)
> >>> +{
> >>> +	unsigned int members;
> >>> +	unsigned int i = 0;
> > 
> > Unnecessary initialization.
> > 
> >>> +
> >>> +	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)
> >>> +			return false;
> >>
> >> Mmm... info->dimen.d is declared with 'unsigned short' type. Thus,
> >> negative value check is needless...
> >>
> >> struct snd_ctl_elem_info {
> >>     ...
> >>     union {
> >>         unsigned short d[4];
> >>         ..
> >>     };
> >>     ...
> >> };
> >>
> >> Please abandon this patchset. I'll post new one tomorrow.
> > 
> > Indeed, somehow I overlooked it, too.
> > 
> > While we're at it: maybe it's even safer to check more strictly the
> > result whether it matches with info->count.  I don't think there is
> > any reason to have an unaligned info->count value when the dimen array
> > is given.  It must be a bug.
> > 
> > That is:
> >>> +		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 true;
> > 
> > Here will be
> > 	return members == info->count;
> 
> I don't mind to be strict here. It's the same as my initial idea (not
> posted).
> 
> 
> But here, I have a question related to next patch (3rd patch). The
> validation logic depends on reliability of info->count. In a case of
> user-defined control element set, info->count is validated in advance,
> therefore it's reasonable. But in a case of each kernel driver,
> info->count is not validated yet, here. Thus, reliability of the
> calculation is lost, I think. The result depends on implementation of
> each driver and it can bring disadvantages to userspace.
> 
> When I think back, this is the main reason that I'm unwilling to use
> info->count for prevention of arithmetic overflow. If we apply the same
> logic to each kernel drivers, we need more validators for info->count.
> For example:
> '''
> static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
>                              struct snd_ctl_elem_info *info)
> {
>     ...
>     if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
>         info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) {
>         dev_err(card->dev,
>             "This module has a bug of invalid element type.\n");
>         result = -ENODATA;
>         goto end;
>     }
> 
>     if (info->count < 1 ||
>         info->count > max_element_members[info->type]) {
>         dev_err(card->dev,
>             "This module has a bug of invalid member count.\n");
>         result = -ENODATA;
>         goto end;
>     }
> 
>     /* This is a driver bug. */
>     if (!validate_element_member_dimension(info)) {
>         dev_err(card->dev,
>             "This module has a bug of invalid dimention info.\n");
>         result = -ENODATA;
>         goto end;
>     }
>     ...
> '''
> 
> Assume 'max_element_members' is precomputed table about the maximum
> number of members in an element, like:
> http://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/tree/sound/core/control.c#n1209
> 
> What do you think about it?

Yes, a validation of info->count is a good idea.
And it can be even a simple WARN_ON().  It's a clear driver bug and
it's better to be exposed as loudly as possible.


Takashi


More information about the Alsa-devel mailing list