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
-
Silent hardware misconfiguration:
wm8731_hw_params()programs theWM8731_SRATEregister with the coefficients for48000 Hz / 12288000 Hzregardless of what was actually requested. -
No error returned to userspace:
wm8731_hw_params()returns0(success) even when the requested sample rate is unsupported by the current clock configuration. The PCM open/prepare path completes without error. -
Exploitable via uninitialized state: If a userspace process opens a PCM stream and calls
hw_paramsbeforeset_sysclkhas been called (leavingwm8731->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. -
Interplay with deemphasis:
wm8731_hw_params()also callswm8731_set_deemph()which readswm8731->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–270get_coeff(): lines 302–311wm8731_hw_params(): lines 313–347wm8731_priv.sysclkinitialized to0(zero-allocation):wm8731.hline 51