Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp3337009imu; Sun, 11 Nov 2018 12:40:17 -0800 (PST) X-Google-Smtp-Source: AJdET5ckIQOrRm+q8kpVGwy3RngmPMuKroByNAjVedyTyTTEudhtIX4pWsQGTiqAJ9Y6CZv+I98W X-Received: by 2002:a62:9989:: with SMTP id t9-v6mr17313087pfk.179.1541968817588; Sun, 11 Nov 2018 12:40:17 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1541968817; cv=none; d=google.com; s=arc-20160816; b=briDP3HNGRmUiPD/HFSTEA7n7RdAqZQZBr1AlTeUAgxCalsGOHJa9TvgCKIQ9NZuU9 pV7kYLPkUl7aifCJbB3Bh5br3JsL4krNzDFazBoV/M/pHY8z5Dhgg1/zQuP2ruG/mEJ8 Uzg8hutwo2cEwYZJe+7amGCwI2u9FXGUGAEFSeF62TWQtUHdfn7K6ABGsJK8HYxRL14u eplpdL4fh3APEQdISg6Hhu3lj5LvuNEvaxu1YXuJizbcj18n0hK176n672Hue1wZZBq7 lDB6L8/3MpMcmavRsiP5F538/c2QqRGIdEbnfc0fa4o/SNXfkDfotmSGIgT9oRyRlaMZ Sulw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:in-reply-to:subject:message-id:date:cc:to :from:mime-version:content-transfer-encoding:content-disposition; bh=2a/HzasoU0y+ElppufDlOAA2WTl/KOSuXA9sW1kpgkk=; b=eC7TFx1grZLx0gObpiASPhIMlMa+b/CO3xa4ETJGvx4OUhTjLVzGrpBp+eRJll5Wmj tE9U1xe1flJAMwo+yGj/WyLiBAkMjcRyi8ptWUhPLJ7xLarNbaib1S2kVeV6kF1qGmKF /JC7qAsqp+1X0oqddoTzNCJ0fDooW8xZcwi+cfpdb8yjQCraWLxIb9ulU4EZpW8/dwQh aGweHQrNXHk9o0UIY/VS5sf/S4Bpf4lezpC+5/tOAEP8I8XtK89qYUqjtrL7SOtZgLj4 aE30pKKFWbO8P3SyM8bEB+iRVSu/CrajE/OZH6f8l6BECKpmBDh8aekDZhtcvrkE0uh4 FAaw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id p64-v6si16499785pfg.45.2018.11.11.12.40.02; Sun, 11 Nov 2018 12:40:17 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730336AbeKLG3P (ORCPT + 99 others); Mon, 12 Nov 2018 01:29:15 -0500 Received: from shadbolt.e.decadent.org.uk ([88.96.1.126]:49530 "EHLO shadbolt.e.decadent.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729977AbeKLFsH (ORCPT ); Mon, 12 Nov 2018 00:48:07 -0500 Received: from [192.168.4.242] (helo=deadeye) by shadbolt.decadent.org.uk with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1gLvsT-0000lJ-Bk; Sun, 11 Nov 2018 19:58:37 +0000 Received: from ben by deadeye with local (Exim 4.91) (envelope-from ) id 1gLvsQ-0001UP-Hr; Sun, 11 Nov 2018 19:58:34 +0000 Content-Type: text/plain; charset="UTF-8" Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 From: Ben Hutchings To: linux-kernel@vger.kernel.org, stable@vger.kernel.org CC: akpm@linux-foundation.org, "Tadeusz Struk" , "Jarkko Sakkinen" Date: Sun, 11 Nov 2018 19:49:05 +0000 Message-ID: X-Mailer: LinuxStableQueue (scripts by bwh) Subject: [PATCH 3.16 095/366] tpm: fix race condition in tpm_common_write() In-Reply-To: X-SA-Exim-Connect-IP: 192.168.4.242 X-SA-Exim-Mail-From: ben@decadent.org.uk X-SA-Exim-Scanned: No (on shadbolt.decadent.org.uk); SAEximRunCond expanded to false Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 3.16.61-rc1 review patch. If anyone has any objections, please let me know. ------------------ From: Tadeusz Struk commit 3ab2011ea368ec3433ad49e1b9e1c7b70d2e65df upstream. There is a race condition in tpm_common_write function allowing two threads on the same /dev/tpm, or two different applications on the same /dev/tpmrm to overwrite each other commands/responses. Fixed this by taking the priv->buffer_mutex early in the function. Also converted the priv->data_pending from atomic to a regular size_t type. There is no need for it to be atomic since it is only touched under the protection of the priv->buffer_mutex. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Tadeusz Struk Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen [bwh: Backported to 3.16: adjust filenames, context] Signed-off-by: Ben Hutchings --- --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -26,7 +26,7 @@ struct file_priv { struct tpm_chip *chip; /* Data passed to and from the tpm via the read/write calls */ - atomic_t data_pending; + size_t data_pending; struct mutex buffer_mutex; struct timer_list user_read_timer; /* user needs to claim result */ @@ -47,7 +47,7 @@ static void timeout_work(struct work_str struct file_priv *priv = container_of(work, struct file_priv, work); mutex_lock(&priv->buffer_mutex); - atomic_set(&priv->data_pending, 0); + priv->data_pending = 0; memset(priv->data_buffer, 0, sizeof(priv->data_buffer)); mutex_unlock(&priv->buffer_mutex); } @@ -74,7 +74,6 @@ static int tpm_open(struct inode *inode, } priv->chip = chip; - atomic_set(&priv->data_pending, 0); mutex_init(&priv->buffer_mutex); setup_timer(&priv->user_read_timer, user_reader_timeout, (unsigned long)priv); @@ -89,28 +88,24 @@ static ssize_t tpm_read(struct file *fil size_t size, loff_t *off) { struct file_priv *priv = file->private_data; - ssize_t ret_size; + ssize_t ret_size = 0; int rc; del_singleshot_timer_sync(&priv->user_read_timer); flush_work(&priv->work); - ret_size = atomic_read(&priv->data_pending); - if (ret_size > 0) { /* relay data */ - ssize_t orig_ret_size = ret_size; - if (size < ret_size) - ret_size = size; + mutex_lock(&priv->buffer_mutex); - mutex_lock(&priv->buffer_mutex); + if (priv->data_pending) { + ret_size = min_t(ssize_t, size, priv->data_pending); rc = copy_to_user(buf, priv->data_buffer, ret_size); - memset(priv->data_buffer, 0, orig_ret_size); + memset(priv->data_buffer, 0, priv->data_pending); if (rc) ret_size = -EFAULT; - mutex_unlock(&priv->buffer_mutex); + priv->data_pending = 0; } - atomic_set(&priv->data_pending, 0); - + mutex_unlock(&priv->buffer_mutex); return ret_size; } @@ -121,17 +116,19 @@ static ssize_t tpm_write(struct file *fi size_t in_size = size; ssize_t out_size; + if (in_size > TPM_BUFSIZE) + return -E2BIG; + + mutex_lock(&priv->buffer_mutex); + /* cannot perform a write until the read has cleared either via tpm_read or a user_read_timer timeout. This also prevents splitted buffered writes from blocking here. */ - if (atomic_read(&priv->data_pending) != 0) + if (priv->data_pending != 0) { + mutex_unlock(&priv->buffer_mutex); return -EBUSY; - - if (in_size > TPM_BUFSIZE) - return -E2BIG; - - mutex_lock(&priv->buffer_mutex); + } if (copy_from_user (priv->data_buffer, (void __user *) buf, in_size)) { @@ -153,7 +150,7 @@ static ssize_t tpm_write(struct file *fi return out_size; } - atomic_set(&priv->data_pending, out_size); + priv->data_pending = out_size; mutex_unlock(&priv->buffer_mutex); /* Set a timeout by which the reader must come claim the result */ @@ -172,7 +169,7 @@ static int tpm_release(struct inode *ino del_singleshot_timer_sync(&priv->user_read_timer); flush_work(&priv->work); file->private_data = NULL; - atomic_set(&priv->data_pending, 0); + priv->data_pending = 0; clear_bit(0, &priv->chip->is_open); put_device(priv->chip->dev); kfree(priv);