alsa-pcm: Guard fflush/fclose on state->log_file with NULL check

When an ALSA device enters an inconsistent state, spa_alsa_clear()
may close log_file but later code paths (e.g., set_swparams) still
call fflush(state->log_file) or fclose(state->log_file). This can
lead to use-after-free or passing NULL to fclose, causing crashes.

Add NULL checks before fflush and fclose, and set log_file to NULL
after closing to prevent subsequent use.

Crash backtrace from an actual occurrence (invalid buf in log_write):

 #0  __GI___strchrnul_lasx
 #1  __GI_strcspn
 #2  log_write (cookie=0x5555667f3f08, buf=0x5555670c4000, size=...)
 #3  _IO_cookie_write
 ...
 #8  set_swparams
 #9  do_prepare
 #10 alsa_recover
This commit is contained in:
Chengyi Zhao 2026-04-30 15:15:56 +08:00
parent 6d2600c09d
commit 91b10af3b8

View file

@ -1094,7 +1094,7 @@ int spa_alsa_clear(struct state *state)
if ((err = snd_output_close(state->output)) < 0) if ((err = snd_output_close(state->output)) < 0)
spa_log_warn(state->log, "output close failed: %s", snd_strerror(err)); spa_log_warn(state->log, "output close failed: %s", snd_strerror(err));
fclose(state->log_file); spa_clear_ptr(state->log_file, fclose);
free(state->tag[0]); free(state->tag[0]);
free(state->tag[1]); free(state->tag[1]);
@ -1189,6 +1189,7 @@ static int do_link(struct state *driver, struct state *state)
snd_pcm_status_dump(status, state->output); snd_pcm_status_dump(status, state->output);
snd_pcm_status(state->hndl, status); snd_pcm_status(state->hndl, status);
snd_pcm_status_dump(status, state->output); snd_pcm_status_dump(status, state->output);
if (state->log_file)
fflush(state->log_file); fflush(state->log_file);
res = snd_pcm_link(driver->hndl, state->hndl); res = snd_pcm_link(driver->hndl, state->hndl);
@ -1734,6 +1735,7 @@ static void debug_hw_params(struct state *state, const char *prefix, snd_pcm_hw_
if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) { if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) {
spa_log_debug(state->log, "%s:", prefix); spa_log_debug(state->log, "%s:", prefix);
snd_pcm_hw_params_dump(params, state->output); snd_pcm_hw_params_dump(params, state->output);
if (state->log_file)
fflush(state->log_file); fflush(state->log_file);
} }
} }
@ -2625,6 +2627,7 @@ static int set_swparams(struct state *state)
if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) { if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_DEBUG))) {
spa_log_debug(state->log, "state after sw_params:"); spa_log_debug(state->log, "state after sw_params:");
snd_pcm_dump(hndl, state->output); snd_pcm_dump(hndl, state->output);
if (state->log_file)
fflush(state->log_file); fflush(state->log_file);
} }