Vulnerability Report: Silent Misconfiguration via Ambiguous Return Value in get_coeff()

File: sound/soc/codecs/wm8731.c
Severity: High
Type: Logic Error / Incorrect Error Handling (CWE-393: Return of Wrong Status Code)


Summary

get_coeff() returns 0 when no matching clock/rate coefficient is found, but 0 is also the valid index of the first entry in coeff_div[]. The caller wm8731_hw_params() cannot distinguish between “found at index 0” and “not found”, causing silent hardware misconfiguration and a false success return to userspace.


Vulnerable Code

get_coeff() — lines 302–311

static inline int get_coeff(int mclk, int rate)
{
    int i;

    for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
        if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
            return i;
    }
    return 0;  // BUG: "not found" is indistinguishable from index 0
}

wm8731_hw_params() — lines 320–326

int i = get_coeff(wm8731->sysclk, params_rate(params));
u16 srate = (coeff_div[i].sr << 2) |
    (coeff_div[i].bosr << 1) | coeff_div[i].usb;

wm8731->playback_fs = params_rate(params);
snd_soc_component_write(component, WM8731_SRATE, srate);

Root Cause

get_coeff() uses return 0 as its error sentinel. Index 0 in coeff_div[] corresponds to the valid entry {12288000, 48000, 256, 0x0, 0x0, 0x0}. When an unsupported (mclk, rate) pair is requested — for example when wm8731->sysclk is 0 (uninitialized, since wm8731_priv is zero-allocated) or set to a value not present in the table — the function returns 0, which the caller treats as a successful lookup of the first table entry.


Impact

  1. Silent hardware misconfiguration: wm8731_hw_params() programs the WM8731_SRATE register with the coefficients for 48000 Hz / 12288000 Hz regardless of what was actually requested.

  2. No error returned to userspace: wm8731_hw_params() returns 0 (success) even when the requested sample rate is unsupported by the current clock configuration. The PCM open/prepare path completes without error.

  3. Exploitable via uninitialized state: If a userspace process opens a PCM stream and calls hw_params before set_sysclk has been called (leaving wm8731->sysclk == 0), the driver silently accepts the request with wrong hardware register values. An attacker with access to the ALSA PCM device can trigger this path to force the codec into a misconfigured state.

  4. Interplay with deemphasis: wm8731_hw_params() also calls wm8731_set_deemph() which reads wm8731->playback_fs — the value that was just set to the wrong rate — further compounding the misconfiguration.


Proof-of-Concept Trigger

// 1. Open PCM device without calling snd_soc_dai_set_sysclk first
//    => wm8731->sysclk remains 0
// 2. Call hw_params with any valid rate (e.g. 44100)
//    => get_coeff(0, 44100) finds no match, returns 0
//    => coeff_div[0] (48000 Hz coefficients) is written to hardware
//    => hw_params returns 0 (success)
// 3. Audio plays at wrong sample rate; no error is signalled

Fix

get_coeff() must return a negative error code when no match is found, and wm8731_hw_params() must check for it:

static inline int get_coeff(int mclk, int rate)
{
    int i;
    for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
        if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
            return i;
    }
    return -EINVAL;
}

// In wm8731_hw_params():
int i = get_coeff(wm8731->sysclk, params_rate(params));
if (i < 0) {
    dev_err(component->dev,
            "No coefficient for mclk %u rate %u\n",
            wm8731->sysclk, params_rate(params));
    return i;
}

References

  • coeff_div[] table: lines 238–270
  • get_coeff(): lines 302–311
  • wm8731_hw_params(): lines 313–347
  • wm8731_priv.sysclk initialized to 0 (zero-allocation): wm8731.h line 51