Re: Kernel 5.0 regression in /dev/tpm0 access

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jarkko,
On 3/11/19 6:09 AM, Jarkko Sakkinen wrote:
> Tadeusz, why on earth the code does not lock buffer_mutex?? Just noticed
> when I checked the function in question. It is an awful bug alone.

Because the tpm_common_poll() just reads the flags and doesn't modify
them, so the logic was that if the response_length is not 0 or
response_read is flase then the first poll should return EPOLLIN
and if not, the application should call the poll again and only call read
if the EPOLLIN is set in the mask. It looks like the tpm_timeout_work()
kicks in and messes things out.
Looking at it again I think the response_read flag is redundant
and only the response_length should be used.

>
> Maybe I'm missing something but that is the root cause for this mess
> i.e. race condition with tpm_common_write().
>
> I'm sorry for not noticing that during the review process  So
> obviously wrong...

Mantas, could you please give this below a quick try and see if it helps.
I have tested this quickly, but couldn't reproduce the original problem.
If it will help I will send a proper patch.
Thanks,

--- 8< ---
diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
index 5eecad233ea1..496b965c865f 100644
--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -63,7 +63,6 @@ static void tpm_timeout_work(struct work_struct *work)
 					      timeout_work);
 
 	mutex_lock(&priv->buffer_mutex);
-	priv->response_read = true;
 	priv->response_length = 0;
 	memset(priv->data_buffer, 0, sizeof(priv->data_buffer));
 	mutex_unlock(&priv->buffer_mutex);
@@ -75,7 +74,6 @@ void tpm_common_open(struct file *file, struct tpm_chip *chip,
 {
 	priv->chip = chip;
 	priv->space = space;
-	priv->response_read = true;
 
 	mutex_init(&priv->buffer_mutex);
 	timer_setup(&priv->user_read_timer, user_reader_timeout, 0);
@@ -95,8 +93,6 @@ ssize_t tpm_common_read(struct file *file, char __user *buf,
 	mutex_lock(&priv->buffer_mutex);
 
 	if (priv->response_length) {
-		priv->response_read = true;
-
 		ret_size = min_t(ssize_t, size, priv->response_length);
 		if (!ret_size) {
 			priv->response_length = 0;
@@ -140,8 +136,7 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
 	 * tpm_read or a user_read_timer timeout. This also prevents split
 	 * buffered writes from blocking here.
 	 */
-	if ((!priv->response_read && priv->response_length) ||
-	    priv->command_enqueued) {
+	if (priv->response_length || priv->command_enqueued) {
 		ret = -EBUSY;
 		goto out;
 	}
@@ -167,7 +162,6 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
 	}
 
 	priv->response_length = 0;
-	priv->response_read = false;
 	*off = 0;
 
 	/*
@@ -202,13 +196,15 @@ __poll_t tpm_common_poll(struct file *file, poll_table *wait)
 	struct file_priv *priv = file->private_data;
 	__poll_t mask = 0;
 
+	mutex_lock(&priv->buffer_mutex);
 	poll_wait(file, &priv->async_wait, wait);
 
-	if (!priv->response_read || priv->response_length)
+	if (priv->response_length)
 		mask = EPOLLIN | EPOLLRDNORM;
 	else
 		mask = EPOLLOUT | EPOLLWRNORM;
 
+	mutex_unlock(&priv->buffer_mutex);
 	return mask;
 }
 
diff --git a/drivers/char/tpm/tpm-dev.h b/drivers/char/tpm/tpm-dev.h
index 1089fc0bb290..403ae3efc6e0 100644
--- a/drivers/char/tpm/tpm-dev.h
+++ b/drivers/char/tpm/tpm-dev.h
@@ -15,7 +15,6 @@ struct file_priv {
 	struct work_struct async_work;
 	wait_queue_head_t async_wait;
 	size_t response_length;
-	bool response_read;
 	bool command_enqueued;
 
 	u8 data_buffer[TPM_BUFSIZE];



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux Kernel Hardening]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux