Skip to content

alsa-lib: security hardening — UCM, conf parser, topology, PCM plugins (6 findings) - #527

Open
HarshRajSinghania wants to merge 5 commits into
alsa-project:masterfrom
HarshRajSinghania:alsa-security-hardening-2026-09
Open

HarshRajSinghania wants to merge 5 commits into
alsa-project:masterfrom
HarshRajSinghania:alsa-security-hardening-2026-09

Conversation

@HarshRajSinghania

Copy link
Copy Markdown
Contributor

This PR addresses six independently-triggered security hardening issues found during a source audit of commit f84cd4c, all verified with proof-of-concept programs.

A3 (shell/system()) and A6 (PCM file popen) are intentional features and are not patched here — hardening suggestions noted inline for maintainer consideration.


A1 — UCM sysw path traversal (CWE-22) · src/ucm/main.c

execute_sysw() concatenates the sysfs root with a caller-supplied string via snprintf() with no .. rejection or realpath() confinement. A UCM path like ../../etc/cron.d/x:value writes to an arbitrary filesystem location. Fix: realpath() confinement — reject if resolved path does not remain within the sysfs root.

A2 — UCM cfg-save unrestricted path (CWE-73) · src/ucm/main.c

execute_cfgsave() calls snd_output_stdio_open(&out, file, "w+") with a raw caller-supplied file and no restriction. Any writable path (e.g. ~/.ssh/authorized_keys) is reachable. Fix: realpath() check — confine to XDG_RUNTIME_DIR (fallback /tmp).

A4 — Config parser absolute include traversal (CWE-22) · src/conf.c

input_stdio_open() immediately opens any absolute path (file[0]=='/') without checking for .. components. A planted config with </../../etc/shadow> can read arbitrary files. Fix: reject paths containing /../.

A5 — Topology control uint32_t integer overflow (CWE-190) · src/topology/ctl.c

size2 = x->size + x->priv.size is computed in 32-bit unsigned arithmetic before being widened to size_t. On a 64-bit host, 0xFFFFFFF0 + 0x20 = 0x10 (wraps), bypassing the subsequent size2 > size guard. Three sites: tplg_decode_control_mixer, tplg_decode_control_enum, tplg_decode_control_bytes (distinct from CVE-2026-25068 which was tplg_decode_control_mixer1). Fix: pre-check for overflow, then (size_t)x->size + (size_t)x->priv.size.

A7 — PCM route plugin unbounded ttable allocation (CWE-770) · src/pcm/pcm_route.c

csize/ssize grow from config-supplied channel indices with no upper bound before malloc(csize * ssize * sizeof(entry)). Index 1073741823 → malloc(8 GB). Fix: cap both at 1024 (consistent with dmix, softvol, other PCM plugins).

A8 — PCM multi sparse bindings OOB assert (CWE-617) · src/pcm/pcm_multi.c

Sparse binding maps leave sidxs[i] == -1 for gap indices. assert(schannels[i] < schannels_count[sidxs[i]]) then dereferences schannels_count[-1] → SIGABRT (debug) / OOB read (NDEBUG). Fix: replace both assert()s with explicit -EINVAL returns with bounds and sign checks.


Verified: all six PoC programs confirmed on libasound2 1.2.14 / gcc 14.2, commit f84cd4c.

Signed-off-by: Harsh Raj Singhania raj.harshraut@gmail.com

@tiwai

tiwai commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Like your another PR, all issues look independent, so could you rather split the changes to individual commits instead of squashing into one?

@HarshRajSinghania
HarshRajSinghania force-pushed the alsa-security-hardening-2026-09 branch from ece3e32 to f84cd4c Compare September 30, 2026 18:57
input_stdio_open() previously opened any absolute path without checking
for path traversal. Reject paths that contain "/../" so a planted config
cannot read arbitrary files (CWE-22).

Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
size2 = x->size + x->priv.size was computed in 32-bit arithmetic and could
wrap on a 64-bit host, bypassing the subsequent bounds check. Pre-check
for overflow then cast to size_t. Affects tplg_decode_control_mixer,
tplg_decode_control_enum, and tplg_decode_control_bytes (CWE-190).

Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
csize/ssize grew from config-supplied channel indices with no upper
bound before malloc(csize * ssize * sizeof(entry)). Cap both at 1024,
consistent with other PCM plugins (CWE-770).

Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
Sparse binding maps leave sidxs[i] == -1 for gap indices. The previous
assert() calls caused SIGABRT in debug builds and OOB reads under NDEBUG.
Return -EINVAL with explicit bounds and sign checks instead (CWE-617).

Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
A1 (sysw): execute_sysw() concatenated the sysfs root with a caller path
via snprintf() with no traversal check. Use realpath() and reject paths
that escape the sysfs root (CWE-22).

A2 (cfg-save): execute_cfgsave() opened a caller-supplied filename with
no restriction. Confine writes to XDG_RUNTIME_DIR (or /tmp) via realpath()
(CWE-73).

Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
@HarshRajSinghania

Copy link
Copy Markdown
Contributor Author

Thanks @tiwai — done. The changes are now split into individual commits instead of one squashed commit:

  1. conf: reject absolute include paths containing /../
  2. topology: fix uint32_t overflow in control decoder size checks
  3. pcm: cap route plugin ttable channel dimensions at 1024
  4. pcm: replace multi plugin binding asserts with -EINVAL checks
  5. ucm: confine sysw and cfg-save paths with realpath()

Please take another look when you have a moment.

@perexg

perexg commented Oct 15, 2026

Copy link
Copy Markdown
Member

There is issue with the last commit, the XDG_RUNTIME_DIR assumption is wrong:

ucm2/lib/card-init.conf:Define.LibDir "/var/lib/alsa/card${CardNumber}.conf.d"
ucm2/common/pcm/hdmi.conf:		cfg-save "${var:LibDir}/${var:__Name}.conf:hdmi-pcm"
ucm2/lib/ctl-remap.conf:	cfg-save "${var:LibDir}/ctl-remap.conf:+ctl.default"

@perexg

perexg commented Oct 15, 2026

Copy link
Copy Markdown
Member

Also, I find realpath() checks too expensive, perhaps '/../' check would be okay like in f5377bb .

@tiwai

tiwai commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

OK, then let's scratch the last one. Others can remain, right?

jollaitbot pushed a commit to sailfishos-mirror/alsa-lib that referenced this pull request Oct 16, 2026
size2 = x->size + x->priv.size was computed in 32-bit arithmetic and could
wrap on a 64-bit host, bypassing the subsequent bounds check. Pre-check
for overflow then cast to size_t. Affects tplg_decode_control_mixer,
tplg_decode_control_enum, and tplg_decode_control_bytes.

Closes: alsa-project/alsa-lib#527
Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
jollaitbot pushed a commit to sailfishos-mirror/alsa-lib that referenced this pull request Oct 16, 2026
csize/ssize grew from config-supplied channel indices with no upper
bound before malloc(csize * ssize * sizeof(entry)). Cap both at 1024,
consistent with other PCM plugins.

[ fixed coding style -- tiwai ]

Closes: alsa-project/alsa-lib#527
Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
jollaitbot pushed a commit to sailfishos-mirror/alsa-lib that referenced this pull request Oct 16, 2026
Sparse binding maps leave sidxs[i] == -1 for gap indices. The previous
assert() calls caused SIGABRT in debug builds and OOB reads under NDEBUG.
Return -EINVAL with explicit bounds and sign checks instead.

[ fixed coding style -- tiwai ]

Closes: alsa-project/alsa-lib#527
Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
jollaitbot pushed a commit to sailfishos-mirror/alsa-lib that referenced this pull request Oct 16, 2026
1: execute_sysw() concatenated the sysfs root with a caller path via
snprintf() with no traversal check. Use realpath() and reject paths
that escape the sysfs root.

2: execute_cfgsave() opened a caller-supplied filename with no
restriction. Confine writes to XDG_RUNTIME_DIR (or /tmp) via
realpath().

[ rewritten with check_realpath() helper function -- tiwai ]

Closes: alsa-project/alsa-lib#527
Signed-off-by: Harsh Raj Singhania <raj.harshraut@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants