Re: 4.13 regression: get_kctl_0dB_offset doesn't handle all possible callbacks

From: Takashi Sakamoto
Date: Sat Oct 14 2017 - 01:33:19 EST


Hi,

On Oct 14 2017 07:46, PaX Team wrote:
> what KERNEXEC on i386 does is that it executes kernel code in its own 0-based
> code segment hence the 'low' code addresses. due to the current logic that
> checks for SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK in get_kctl_0dB_offset, this
> callback address is instead treated as a data pointer (as apparently
> SNDRV_CTL_ELEM_ACCESS_TLV_READ is also set) and since it's not a valid kernel
> address, it causes a GPF under the UDEREF PaX feature (without UDEREF it'd
> have been an oops since such low addresses are not mapped for kernel threads).

There're two ways to use 'struct snd_kcontrol.tlv'; constant array (=.p) an
a handler (= .c). An 'access' flag in each member of
'struct snd_kcontrol.vd' represent which way is used for the member.
Therefore, as long as checking the flag, the 'get_kctl_0dB_offset()' doesn't
handle a pointer to the handler as a pointer to array of TLV container.

> on vanilla kernels all this is a silent read of kernel code bytes that are
> then interpreted as the tlv[] array content, which is probably not what you
> want either.

Yes. Current code include a bug of inappropriate condition statement for the
above. As a result, it allows to handle a pointer to the handler as a
pointer to TLV data.

> as for fixing this, first the above mentioned assumption should be re-evaluated.
> if it's considered correct then there is some logic bug in my case (i can help
> debug it if you tell me what to do) otherwise the current pattern of
>
> if (SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK && callback == snd_hda_mixer_amp_tlv) {
> call wrapped function underneath snd_hda_mixer_amp_tlv
> } else if (SNDRV_CTL_ELEM_ACCESS_TLV_READ) {
> treat unrecognized callback address as data ptr
> }
>
> should be changed to
>
> if (SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK( {
> if (callback == snd_hda_mixer_amp_tlv) {
> call wrapped function underneath snd_hda_mixer_amp_tlv
> } else if (callback == others) {
> handle others, WARN/BUG/etc
> }
> } else if (SNDRV_CTL_ELEM_ACCESS_TLV_READ) {
> no longer treat unrecognized callback address as data ptr
> }

Good enough as a solution. Please test a patch in the end of this message.

> and all other callbacks with userland access should be refactored the same
> way as snd_hda_mixer_amp_tlv was. i'd also suggest that you at least do the
> above suggested if/else pattern change in order to prevent the misuse of
> unexpected callbacks in the future.

This suggestion is better for safety. Do you have some ways to detect the
pattern on current vanilla kernel? Or we should find it by eye-grep?


Thanks

Takashi Sakamoto

======== 8< --------