Received: by 2002:a05:6a10:5bc5:0:0:0:0 with SMTP id os5csp4252581pxb; Tue, 2 Nov 2021 06:46:19 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzB/X1gvD8Et2P+/d+ng4/MBqMAz2RyAA/go8ZHiX25Hn3xHbRbaCVdTtqC73jZrnNUXa6E X-Received: by 2002:a17:906:258d:: with SMTP id m13mr46816845ejb.208.1635860779290; Tue, 02 Nov 2021 06:46:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1635860779; cv=none; d=google.com; s=arc-20160816; b=q8tk19KOLbHfuKXO99/Yzz/xhj2Ge0zMtcOXkcGOUhIKPyLehnytXepfZpP83WTRrp jcyK+XqztHtU+Bc/l85k0+jw85JyQ1GcP0JEYb5N48dMsKIT1o9BW7UE1YyCT9vADv9K 6k9YKPV9tLipPQt3soIFjfCgOMXb+cf+DZzl99agEbPjwBy/3DnEah3xj/IgplmVLE4R 5hsQ2TJf42XHMHwn6lzfvmLLUdjGBJIbZNxySsERlEvp4w8TsxmK1E7Pq83aWBJfe9a9 yqLYyvkIHA7fXfsXeMMkTHiSwOXXspkiMAe9cM2CCvmvQrojybQC9vJ7Gg1oS1vIUIkp 4D6g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:message-id:date:subject:cc:to:from; bh=viiYseJg8v3IYtCvOsTlZbEliQyrwKgZGnuNELnftr4=; b=nnjuK86+IBvknJvKZQNzSmtONQfbZgNUtZZoP0fK82NCJ/3whFZ+lJ4Pj3Haz4fB5a jMBWl5O0vwpUzEX1Ddo+UnVnaK52BalJRzQa4RShlMl/M/8Cl2+8QoPm8xEUb7PxcFPF /FcFDk8lchPCaqykT7IunApeJ1HDyc7GdreW1O7NTCwnBA1vR92VEAzFEV+iFJ1gVfPd FhbsfvzKaqnX4/kHHxDOf4Yo6yxrd68IGrLIQf1tKptWB+e3WGHNwFBm4Ep/V0qX4eBF jMhlIsWwQfNgbapZbUGdr7bZxCdLu6E8agPzzyCrQgvkiqLkvqkm6vkTnS8YnrJR2Q9S MeKA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=huawei.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id h12si6968287edb.124.2021.11.02.06.45.54; Tue, 02 Nov 2021 06:46:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=huawei.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230308AbhKBNpn (ORCPT + 99 others); Tue, 2 Nov 2021 09:45:43 -0400 Received: from szxga01-in.huawei.com ([45.249.212.187]:14702 "EHLO szxga01-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229557AbhKBNpl (ORCPT ); Tue, 2 Nov 2021 09:45:41 -0400 Received: from dggeme762-chm.china.huawei.com (unknown [172.30.72.55]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4Hk9yx4jCJzZcf8; Tue, 2 Nov 2021 21:40:57 +0800 (CST) Received: from huawei.com (10.175.112.208) by dggeme762-chm.china.huawei.com (10.3.19.108) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2308.15; Tue, 2 Nov 2021 21:43:01 +0800 From: Wang Wensheng To: , , , , , , CC: Subject: [PATCH -next] ALSA: timer: Fix use-after-free problem Date: Tue, 2 Nov 2021 13:41:07 +0000 Message-ID: <20211102134107.35126-1-wangwensheng4@huawei.com> X-Mailer: git-send-email 2.17.1 MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.175.112.208] X-ClientProxiedBy: dggems705-chm.china.huawei.com (10.3.19.182) To dggeme762-chm.china.huawei.com (10.3.19.108) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org When the timer instance was add into ack_list but was not currently in process, the user could stop it via snd_timer_stop1() without delete it from the ack_list. Then the user could free the timer instance and when it was actually processed UAF occurred. This issue could be reproduced via testcase snd_timer01 in ltp - running several instances of that testcase at the same time. What I actually met was that the ack_list of the timer broken and the kernel went into deadloop with irqoff. That could be detected by hardlockup detector on board or when we run it on qemu, we could use gdb to dump the ack_list when the console has no response. To fix this issue, we introduce a new flag SNDRV_TIMER_IFLG_ACKING to indicate the state where the timer instance is in ack_list but not currently processed and check against the new flag in snd_timer_stop1() and delete it from ack_list if the flag is set. Signed-off-by: Wang Wensheng --- include/sound/timer.h | 1 + sound/core/timer.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/sound/timer.h b/include/sound/timer.h index 760e132cc0cd..549288e94a39 100644 --- a/include/sound/timer.h +++ b/include/sound/timer.h @@ -31,6 +31,7 @@ #define SNDRV_TIMER_IFLG_CALLBACK 0x00000020 /* timer callback is active */ #define SNDRV_TIMER_IFLG_EXCLUSIVE 0x00000040 /* exclusive owner - no more instances */ #define SNDRV_TIMER_IFLG_EARLY_EVENT 0x00000080 /* write early event to the poll queue */ +#define SNDRV_TIMER_IFLG_ACKING 0x00000100 /* the timeri was added to ack_list */ #define SNDRV_TIMER_FLG_CHANGE 0x00000001 #define SNDRV_TIMER_FLG_RESCHED 0x00000002 /* need reschedule */ diff --git a/sound/core/timer.c b/sound/core/timer.c index 92b7008fcdb8..1d1e4274919c 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -625,10 +625,12 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) return -EINVAL; spin_lock_irqsave(&timer->lock, flags); if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | - SNDRV_TIMER_IFLG_START))) { + SNDRV_TIMER_IFLG_START | + SNDRV_TIMER_IFLG_ACKING))) { result = -EBUSY; goto unlock; } + list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); if (timer->card && timer->card->shutdown) @@ -649,7 +651,8 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) } } } - timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); + timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START | + SNDRV_TIMER_IFLG_ACKING); if (stop) timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED; else @@ -786,6 +789,7 @@ static void snd_timer_process_callbacks(struct snd_timer *timer, /* remove from ack_list and make empty */ list_del_init(&ti->ack_list); + ti->flags &= ~SNDRV_TIMER_IFLG_ACKING; if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { ticks = ti->pticks; @@ -890,8 +894,10 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ack_list_head = &timer->ack_list_head; else ack_list_head = &timer->sack_list_head; - if (list_empty(&ti->ack_list)) + if (list_empty(&ti->ack_list)) { list_add_tail(&ti->ack_list, ack_list_head); + ti->flags |= SNDRV_TIMER_IFLG_ACKING; + } list_for_each_entry(ts, &ti->slave_active_head, active_list) { ts->pticks = ti->pticks; ts->resolution = resolution; -- 2.17.1