From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7583438A73B for ; Fri, 11 Sep 2026 05:16:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789103776; cv=none; b=Wx0tOqjgHPRLOxmnmbiqnhxo7phnNPqDlGPQpuOoQzkhUEIHqh8Y98x8WU1HMD58t01FWEzcDkVXSDouC8kXO4daGZhczUJmMd8h9l907yqC9BpSXxTNTJjH8xsGx3mR+tH2pUkM8B5veHhWgyDlhht/Hs9ksWZAB/qs5It04ss= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789103776; c=relaxed/simple; bh=TLAv+lITIAz9Rqr/UUY++KQ+Omu5YXiO/SRrsb8Jcls=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=WHJkLBTPMGJsaUGSzYos/QIjb4LNBBusTkPS6/u15r0GCKQgrImmjcPmQ4XPnA11edQv7K0qOqbOguazCHofBVnyfHNi5aEOmxmyC0Oe1NEX3luCG6zAikLloOXdYwjvTZT/bODWzZTMgMqoOjlQEIQ/wN90GaqhdgBDsiWN9bA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UDzdhdTI; arc=none smtp.client-ip=209.85.216.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UDzdhdTI" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-398a5aad413so498085a91.3 for ; Thu, 10 Sep 2026 22:16:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789103775; x=1789708575; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=ArmzfnWDQ/PeAsKx2cuKOwfwqEy95uaVW3+H99u9LlU=; b=UDzdhdTI4dJCJcjELosEG6+C9av5Z+YNj0UHmVKQyaLlvE8mnMRio/hYDMA/jrgt92 iuzsWMFoykU8+7cL5DPS6Nqg6lMxay76WEDgKVwVlN+HABrn+4/4yWRmNW+jYt3s20aK fZLBrSXUp/afpi9J+fzbhKelYD6+aUi+RrKS5OsUF3VDLo547fXBNN9t59+/gr7jBRGA 3iNo1T1FaFbvrGUEvkePW53kWgY+z40a5ndoZFzysQS30v6awRZ8czcYBz27fvYZelFp v6SAXpsxXG1ncEGgMXhscplEYEqLX+V4LO2zVIzxqG6KAlKshPgUxXewEW14JBJScq9K dr3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789103775; x=1789708575; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ArmzfnWDQ/PeAsKx2cuKOwfwqEy95uaVW3+H99u9LlU=; b=lQS42bJLfUTg/kW+VAXmvjPd2zDjR0rTArqD8rWPv3sWn6DTZdjEMkncPzk2/Mii89 oN81Gj5TCP4pzPZEQc4xOtT5YWVz0bZ10quz/Qk3PSGVYUM/E8zK61u7flHjYR6OlFPF HID2S5piKd47375u4qnuq6SlUdM/kGFIldtaqKLiPJDNbWLswRZ7Yldt6cdKFbixHwsX 43pPbfjW5pxgfCLNh6KHls5CQ7pQZVdc2RtE5LytOBp49JqllED5hGCw0e252g4DR7Zq WzYCZdM6jcd/zNBRpUkAs2YQNO1gkPLiFuuqJ2CVSFDn9x8zescIpM7m9b7mzsPMq+e2 NOEQ== X-Forwarded-Encrypted: i=1; AKwUvBwPrmsujRVMruKBJ4SQLpNcoSE+HsV6JwJ8X+vA7FhKb1dCfa69XgpzZW58ZYhVMwjRWT8F4e90B27JAQ==@vger.kernel.org X-Gm-Message-State: AFuF++lt55cFEN8uABILJtm9ubKxCmSr4wsXuD9M6AGhpdTOhbZ3IjT5 +gHbn7Ti6LQOARVb50+urRKosbzfZJsNGY7BVZ5eQS+d9uLLhXd163Ac X-Gm-Gg: AYBFou3oONoaf/1uigo6JjSmoo58cL+dc+ropiteLpGZ/sC3MQn1wIBtA6xD9VXJJtb 2lZlWUKXGI9F0UMYvQA629ZQbVv1TdcjlzoUdE7od7QziTM6ynZ1ejV2eBt1ElPvRvdwstXAr9c 19wD7m8CQAd95dI2R/SxBQ4mR+MhEpINIompIzbrXxpImR35+QyD1wjl5CitKrWkAb03oEnZ+I+ 5khGWD0yxOh23vRVep2HG0qjOh39hN+zNEMvTm1DHNRgW36loyQuFRQVBR+yEdVgk3pQq0NGMuW 6/184CnzAnCnYTNQ7brgqKckSVR5c4z0eBJWVpVtrWvEuo7zgnzPyrVQ9J4SIkMMRS1OENVm3FG MeI59bUiLX4BK2OQe/1Qu/pCYywSWt8sWkVoC+Jfq+F3+u8lOQ5RUC73GShZnVNnAmzsZVRDFC1 QDLD6d5pASTGTljkwynL/VLd9PpHVGHuOWKatgFcRykRn/7U6vLqyzBS3OlIP769nOHCGEAx3x7 bxbtFS990wKgd6SzVY85/rsDHUEoWLkTsxryQ== X-Received: by 2002:a17:90b:4cce:b0:38e:7168:281 with SMTP id 98e67ed59e1d1-39d9bec4d91mr4189394a91.10.1789103774430; Thu, 10 Sep 2026 22:16:14 -0700 (PDT) Received: from localhost.localdomain ([180.101.244.69]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-39d98e602d8sm2541024a91.3.2026.09.10.22.16.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Sep 2026 22:16:13 -0700 (PDT) From: Aohan Mei To: tiwai@suse.com Cc: perex@perex.cz, linux-sound@vger.kernel.org, Aohan Mei , stable@vger.kernel.org Subject: [PATCH] ALSA: pcm: Fix race between concurrent START and hw_params/hw_free Date: Fri, 11 Sep 2026 13:15:59 +0800 Message-ID: <20260911051605.3448607-1-ljp1205831794@gmail.com> X-Mailer: git-send-email 2.43.7 Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Aohan Mei snd_pcm_hw_params() and snd_pcm_hw_free() validate the stream state only once, under the stream lock at entry, and then free/realloc the DMA buffer (snd_pcm_lib_free_pages() -> snd_pcm_set_runtime_buffer(NULL), followed by the sleeping allocation in snd_pcm_lib_malloc_pages()) and update the runtime fields without holding the stream lock. The state, however, stays SNDRV_PCM_STATE_PREPARED until the final snd_pcm_set_state() at the very end of the operation. A concurrent SNDRV_PCM_IOCTL_START takes only the stream lock and merely requires state == PREPARED in snd_pcm_pre_start(), so it can slip into that window: snd_pcm_post_start() sets the state RUNNING, arms the driver data plane and fills the initial silence through snd_pcm_playback_silence(). The data plane then keeps operating on the buffer that hw_params/hw_free is tearing down concurrently; once snd_pcm_set_runtime_buffer(NULL) has cleared runtime->dma_area, the silence fill in fill_silence() -> get_dma_ptr() dereferences a NULL pointer, and on real hardware the device may additionally keep DMA-ing into the freed pages. Close the race by leaving the PREPARED state atomically with the entry state check, inside the same stream lock critical section: a START that already completed makes the hw_params/hw_free state check fail with -EBADFD, and a later START observes SETUP and fails in snd_pcm_pre_start() with -EBADFD as well, so the data plane can never be armed while the buffer is being freed or reallocated. Both functions impose SETUP as their resulting state anyway, hence this does not change the state machine semantics visible to user space. The buffer_mutex/buffer_accessing serialization introduced by the earlier fixes for the prepare-vs-hw_params races cannot simply be extended to the START path: the trigger action has to run under the IRQ-off stream spinlock for atomic PCMs, and failing START with -EBUSY whenever a read/write transfer is in flight would be a user visible regression. Transitioning the state atomically at entry avoids both problems. Cc: stable@vger.kernel.org Assisted-by: CodeBuddy:Kimi-K3 Signed-off-by: Aohan Mei --- sound/core/pcm_native.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 62324282fcae..6040eb752873 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -805,6 +805,17 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, err = -EBADFD; break; } + /* + * The buffer free/realloc and the runtime field updates + * below run without the stream lock, while a concurrent + * SNDRV_PCM_IOCTL_START only requires state == PREPARED + * under that lock. Leave the PREPARED state atomically + * with the check above so that a racing START (and the + * data plane it arms) can no longer slip in and operate + * on the buffer while it is being freed or reallocated. + */ + if (!err && runtime->state == SNDRV_PCM_STATE_PREPARED) + __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SETUP); } if (err) goto unlock; @@ -966,6 +977,13 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) result = -EBADFD; break; } + /* Same race as in snd_pcm_hw_params(): leave the PREPARED + * state atomically with the check above, so that a racing + * START cannot arm the data plane while do_hw_free() tears + * down the buffer without the stream lock. + */ + if (!result && runtime->state == SNDRV_PCM_STATE_PREPARED) + __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SETUP); } if (result) goto unlock; -- 2.43.7