Skip to contentSkip to conversation

tpm: tpm_vtpm_proxy: do not reference kernel memory as user memory

RFC PATCH In review Jarkko Sakkinen 5 participants 8 messages May 30, 2023 – May 31, 2023

Subsystems (3)
lore
Share & tools
lore

Jarkko Sakkinen

author maintainer
#0 lore raw

RFC PATCH v2 1/1 tpm: tpm_vtpm_proxy: do not reference kernel memory as user memory

From: Jarkko Sakkinen <[email protected]>

The driver has two issues (in priority order) in the locality change:

1. The driver uses __user pointer and copy_to_user() and copy_from_user() with a kernel address during the locality change.
2. For invalid locality change request from user space, the driver sets errno to EFAULT, while for invalid input data EINVAL should be used.

Address this by:

1. Introduce __vtpm_proxy_read_unlocked(), __vtpm_proxy_write_unlocked() and __vtpm_proxy_read_write_locked().
2. Make locality change atomic by calling __vtpm_proxy_read_write_locked(), instead of tpm_transmit_cmd().

Fixes be4c9acfe297 ("tpm: vtpm_proxy: Implement request_locality function.")

---
v2:
* Acquiring and releasing mutex in-between should not be an issue because they are executed with the chip locked.

---
 drivers/char/tpm/tpm_vtpm_proxy.c | 162 ++++++++++++++----------------
 1 file changed, 73 insertions(+), 89 deletions(-)
drivers/char/tpm/tpm_vtpm_proxy.c +73 -89
index 30e953988cab..8f43a82e5590 100644
@@ -38,7 +38,6 @@ struct proxy_dev {
 #define STATE_OPENED_FLAG        BIT(0)
 #define STATE_WAIT_RESPONSE_FLAG BIT(1)  /* waiting for emulator response */
 #define STATE_REGISTERED_FLAG	 BIT(2)
-#define STATE_DRIVER_COMMAND     BIT(3)  /* sending a driver specific command */
 
 	size_t req_len;              /* length of queued TPM request */
 	size_t resp_len;             /* length of queued TPM response */
@@ -58,106 +57,112 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
  * Functions related to 'server side'
  */
 
-/**
- * vtpm_proxy_fops_read - Read TPM commands on 'server side'
- *
- * @filp: file pointer
- * @buf: read buffer
- * @count: number of bytes to read
- * @off: offset
- *
- * Return:
- *	Number of bytes read or negative error code
- */
-static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
-				    size_t count, loff_t *off)
+static ssize_t __vtpm_proxy_read_unlocked(struct proxy_dev *proxy_dev, char __user *buf,
+					  size_t count)
 {
-	struct proxy_dev *proxy_dev = filp->private_data;
 	size_t len;
-	int sig, rc;
-
-	sig = wait_event_interruptible(proxy_dev->wq,
-		proxy_dev->req_len != 0 ||
-		!(proxy_dev->state & STATE_OPENED_FLAG));
-	if (sig)
-		return -EINTR;
-
-	mutex_lock(&proxy_dev->buf_lock);
+	int rc;
 
-	if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
-		mutex_unlock(&proxy_dev->buf_lock);
+	if (!(proxy_dev->state & STATE_OPENED_FLAG))
 		return -EPIPE;
-	}
 
 	len = proxy_dev->req_len;
 
 	if (count < len || len > sizeof(proxy_dev->buffer)) {
-		mutex_unlock(&proxy_dev->buf_lock);
 		pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
 			 count, len);
 		return -EIO;
 	}
 
-	rc = copy_to_user(buf, proxy_dev->buffer, len);
+	if (buf)
+		rc = copy_to_user(buf, proxy_dev->buffer, len);
+
 	memset(proxy_dev->buffer, 0, len);
 	proxy_dev->req_len = 0;
 
 	if (!rc)
 		proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
 
-	mutex_unlock(&proxy_dev->buf_lock);
-
 	if (rc)
 		return -EFAULT;
 
 	return len;
 }
 
-/**
- * vtpm_proxy_fops_write - Write TPM responses on 'server side'
- *
- * @filp: file pointer
- * @buf: write buffer
- * @count: number of bytes to write
- * @off: offset
- *
- * Return:
- *	Number of bytes read or negative error value
- */
-static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
-				     size_t count, loff_t *off)
+static ssize_t __vtpm_proxy_write_unlocked(struct proxy_dev *proxy_dev, const char __user *buf,
+					   size_t count)
 {
-	struct proxy_dev *proxy_dev = filp->private_data;
-
-	mutex_lock(&proxy_dev->buf_lock);
-
-	if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
-		mutex_unlock(&proxy_dev->buf_lock);
+	if (!(proxy_dev->state & STATE_OPENED_FLAG))
 		return -EPIPE;
-	}
 
 	if (count > sizeof(proxy_dev->buffer) ||
-	    !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
-		mutex_unlock(&proxy_dev->buf_lock);
+	    !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG))
 		return -EIO;
-	}
 
 	proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
 
 	proxy_dev->req_len = 0;
 
-	if (copy_from_user(proxy_dev->buffer, buf, count)) {
-		mutex_unlock(&proxy_dev->buf_lock);
+	if (buf && copy_from_user(proxy_dev->buffer, buf, count))
 		return -EFAULT;
-	}
 
 	proxy_dev->resp_len = count;
+	return count;
+}
 
+static ssize_t __vtpm_proxy_read_write_unlocked(struct proxy_dev *proxy_dev, char __user *buf,
+						size_t count)
+{
+	ssize_t rc;
+
+	do {
+		rc = __vtpm_proxy_write_unlocked(proxy_dev, buf, count);
+		if (rc < 0)
+			break;
+		rc = __vtpm_proxy_read_unlocked(proxy_dev, buf, rc);
+	} while (0);
+
+	return rc;
+}
+
+/*
+ * See struct file_operations.
+ */
+static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
+				    size_t count, loff_t *off)
+{
+	struct proxy_dev *proxy_dev = filp->private_data;
+	ssize_t rc;
+	int sig;
+
+	sig = wait_event_interruptible(proxy_dev->wq,
+		proxy_dev->req_len != 0 ||
+		!(proxy_dev->state & STATE_OPENED_FLAG));
+	if (sig)
+		return -EINTR;
+
+	mutex_lock(&proxy_dev->buf_lock);
+	rc = __vtpm_proxy_read_unlocked(proxy_dev, buf, count);
 	mutex_unlock(&proxy_dev->buf_lock);
 
+	return rc;
+}
+
+/*
+ * See struct file_operations.
+ */
+static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
+				     size_t count, loff_t *off)
+{
+	struct proxy_dev *proxy_dev = filp->private_data;
+	int rc;
+
+	mutex_lock(&proxy_dev->buf_lock);
+	rc = __vtpm_proxy_write_unlocked(proxy_dev, buf, count);
+	mutex_unlock(&proxy_dev->buf_lock);
 	wake_up_interruptible(&proxy_dev->wq);
 
-	return count;
+	return rc;
 }
 
 /*
@@ -295,28 +300,6 @@ static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 	return len;
 }
 
-static int vtpm_proxy_is_driver_command(struct tpm_chip *chip,
-					u8 *buf, size_t count)
-{
-	struct tpm_header *hdr = (struct tpm_header *)buf;
-
-	if (count < sizeof(struct tpm_header))
-		return 0;
-
-	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
-		switch (be32_to_cpu(hdr->ordinal)) {
-		case TPM2_CC_SET_LOCALITY:
-			return 1;
-		}
-	} else {
-		switch (be32_to_cpu(hdr->ordinal)) {
-		case TPM_ORD_SET_LOCALITY:
-			return 1;
-		}
-	}
-	return 0;
-}
-
 /*
  * Called when core TPM driver forwards TPM requests to 'server side'.
  *
@@ -330,6 +313,7 @@ static int vtpm_proxy_is_driver_command(struct tpm_chip *chip,
 static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
 {
 	struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+	unsigned int ord = ((struct tpm_header *)buf)->ordinal;
 
 	if (count > sizeof(proxy_dev->buffer)) {
 		dev_err(&chip->dev,
@@ -338,9 +322,11 @@ static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
 		return -EIO;
 	}
 
-	if (!(proxy_dev->state & STATE_DRIVER_COMMAND) &&
-	    vtpm_proxy_is_driver_command(chip, buf, count))
-		return -EFAULT;
+	if ((chip->flags & TPM_CHIP_FLAG_TPM2) && ord == TPM2_CC_SET_LOCALITY)
+		return -EINVAL;
+
+	if (ord == TPM_ORD_SET_LOCALITY)
+		return -EINVAL;
 
 	mutex_lock(&proxy_dev->buf_lock);
 
@@ -409,12 +395,10 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
 		return rc;
 	tpm_buf_append_u8(&buf, locality);
 
-	proxy_dev->state |= STATE_DRIVER_COMMAND;
-
-	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");
-
-	proxy_dev->state &= ~STATE_DRIVER_COMMAND;
-
+	mutex_lock(&proxy_dev->buf_lock);
+	memcpy(proxy_dev->buffer, buf.data, tpm_buf_length(&buf));
+	rc = __vtpm_proxy_read_write_unlocked(proxy_dev, NULL, tpm_buf_length(&buf));
+	mutex_unlock(&proxy_dev->buf_lock);
 	if (rc < 0) {
 		locality = rc;
 		goto out;
Signature · 1 line
2.39.2
#1 lore raw
kernel test robot noticed the following build warnings:

Hi Jarkko,

[This is a private test report for your RFC patch.] kernel test robot noticed the following build warnings:

[auto build test WARNING on char-misc/char-misc-testing]
[also build test WARNING on char-misc/char-misc-next char-misc/char-misc-linus linus/master v6.4-rc4 next-20230530] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jarkko-Sakkinen/tpm-tpm_vtpm_proxy-do-not-reference-kernel-memory-as-user-memory/20230530-100537
base:   char-misc/char-misc-testing
patch link:    https://lore.kernel.org/r/20230530020133.235765-1-jarkko%40kernel.org
patch subject: [PATCH RFC v2] tpm: tpm_vtpm_proxy: do not reference kernel memory as user memory
config: arm64-randconfig-r034-20230530 (https://download.01.org/0day-ci/archive/20230530/[email protected]/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project 4faf3aaf28226a4e950c103a14f6fc1d1fdabb1b)

reproduce (this is a W=1 build):
mkdir -p ~/bin
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# https://github.com/intel-lab-lkp/linux/commit/919bd177830a9508a5a67fc17014cd10047d8c2f
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Jarkko-Sakkinen/tpm-tpm_vtpm_proxy-do-not-reference-kernel-memory-as-user-memory/20230530-100537 git checkout 919bd177830a9508a5a67fc17014cd10047d8c2f
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang ~/bin/make.cross W=1 O=build_dir ARCH=arm64 olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang ~/bin/make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash drivers/char/tpm/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

In file included from drivers/char/tpm/tpm_vtpm_proxy.c:24:
In file included from drivers/char/tpm/tpm.h:28:
include/linux/tpm_eventlog.h:167:6: warning: variable 'mapping_size' set but not used [-Wunused-but-set-variable]

           int mapping_size;
               ^
>> drivers/char/tpm/tpm_vtpm_proxy.c:77:6: warning: variable 'rc' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
           if (buf)
               ^~~
   drivers/char/tpm/tpm_vtpm_proxy.c:83:7: note: uninitialized use occurs here
           if (!rc)
                ^~
   drivers/char/tpm/tpm_vtpm_proxy.c:77:2: note: remove the 'if' if its condition is always true
           if (buf)
           ^~~~~~~~
   drivers/char/tpm/tpm_vtpm_proxy.c:64:8: note: initialize the variable 'rc' to silence this warning
           int rc;
                 ^
                  = 0

2 warnings generated.

vim +77 drivers/char/tpm/tpm_vtpm_proxy.c

    55	
    56	/*
    57	 * Functions related to 'server side'
    58	 */
    59	
    60	static ssize_t __vtpm_proxy_read_unlocked(struct proxy_dev *proxy_dev, char __user *buf,
    61						  size_t count)
    62	{
    63		size_t len;
    64		int rc;
    65	
    66		if (!(proxy_dev->state & STATE_OPENED_FLAG))
    67			return -EPIPE;
    68	
    69		len = proxy_dev->req_len;
    70	
    71		if (count < len || len > sizeof(proxy_dev->buffer)) {
    72			pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
    73				 count, len);
    74			return -EIO;
    75		}
    76	
  > 77		if (buf)
    78			rc = copy_to_user(buf, proxy_dev->buffer, len);
    79	
    80		memset(proxy_dev->buffer, 0, len);
    81		proxy_dev->req_len = 0;
    82	
    83		if (!rc)
    84			proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
    85	
    86		if (rc)
    87			return -EFAULT;
    88	
    89		return len;
    90	}
    91	
Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#2 lore raw
In reply to Jarkko Sakkinen
Quoted 6 lines · Jarkko Sakkinen

From: Jarkko Sakkinen <[email protected]>

The driver has two issues (in priority order) in the locality change:

1. The driver uses __user pointer and copy_to_user() and copy_from_user() with a kernel address during the locality change.

This can lead to ... bad things ?

Quoted 10 lines

2. For invalid locality change request from user space, the driver sets errno to EFAULT, while for invalid input data EINVAL should be used.

Address this by:

1. Introduce __vtpm_proxy_read_unlocked(), __vtpm_proxy_write_unlocked() and __vtpm_proxy_read_write_locked().
2. Make locality change atomic by calling __vtpm_proxy_read_write_locked(), instead of tpm_transmit_cmd().

2. sounds like it should be addressable by changing -EFAULT to -EINVAL but there's more to it?

Quoted 173 lines
Fixes be4c9acfe297 ("tpm: vtpm_proxy: Implement request_locality function.")

---
v2:
* Acquiring and releasing mutex in-between should not be an issue because they are executed with the chip locked.

---
  drivers/char/tpm/tpm_vtpm_proxy.c | 162 ++++++++++++++----------------
  1 file changed, 73 insertions(+), 89 deletions(-)
drivers/char/tpm/tpm_vtpm_proxy.c +48 -57
index 30e953988cab..8f43a82e5590 100644
@@ -38,7 +38,6 @@ struct proxy_dev {
  #define STATE_OPENED_FLAG        BIT(0)
  #define STATE_WAIT_RESPONSE_FLAG BIT(1)  /* waiting for emulator response */
  #define STATE_REGISTERED_FLAG	 BIT(2)
-#define STATE_DRIVER_COMMAND     BIT(3)  /* sending a driver specific command */
 
  	size_t req_len;              /* length of queued TPM request */
  	size_t resp_len;             /* length of queued TPM response */
@@ -58,106 +57,112 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
   * Functions related to 'server side'
   */
 
-/**
- * vtpm_proxy_fops_read - Read TPM commands on 'server side'
- *
- * @filp: file pointer
- * @buf: read buffer
- * @count: number of bytes to read
- * @off: offset
- *
- * Return:
- *	Number of bytes read or negative error code
- */
-static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
-				    size_t count, loff_t *off)
+static ssize_t __vtpm_proxy_read_unlocked(struct proxy_dev *proxy_dev, char __user *buf,
+					  size_t count)
  {
-	struct proxy_dev *proxy_dev = filp->private_data;
  	size_t len;
-	int sig, rc;
-
-	sig = wait_event_interruptible(proxy_dev->wq,
-		proxy_dev->req_len != 0 ||
-		!(proxy_dev->state & STATE_OPENED_FLAG));
-	if (sig)
-		return -EINTR;
-
-	mutex_lock(&proxy_dev->buf_lock);
+	int rc;
 
-	if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
-		mutex_unlock(&proxy_dev->buf_lock);
+	if (!(proxy_dev->state & STATE_OPENED_FLAG))
  		return -EPIPE;
-	}
 
  	len = proxy_dev->req_len;
 
  	if (count < len || len > sizeof(proxy_dev->buffer)) {
-		mutex_unlock(&proxy_dev->buf_lock);
  		pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
  			 count, len);
  		return -EIO;
  	}
 
-	rc = copy_to_user(buf, proxy_dev->buffer, len);
+	if (buf)
+		rc = copy_to_user(buf, proxy_dev->buffer, len);
+
  	memset(proxy_dev->buffer, 0, len);
  	proxy_dev->req_len = 0;
 
  	if (!rc)
  		proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
 
-	mutex_unlock(&proxy_dev->buf_lock);
-
  	if (rc)
  		return -EFAULT;
 
  	return len;
  }
 
-/**
- * vtpm_proxy_fops_write - Write TPM responses on 'server side'
- *
- * @filp: file pointer
- * @buf: write buffer
- * @count: number of bytes to write
- * @off: offset
- *
- * Return:
- *	Number of bytes read or negative error value
- */
-static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
-				     size_t count, loff_t *off)
+static ssize_t __vtpm_proxy_write_unlocked(struct proxy_dev *proxy_dev, const char __user *buf,
+					   size_t count)
  {
-	struct proxy_dev *proxy_dev = filp->private_data;
-
-	mutex_lock(&proxy_dev->buf_lock);
-
-	if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
-		mutex_unlock(&proxy_dev->buf_lock);
+	if (!(proxy_dev->state & STATE_OPENED_FLAG))
  		return -EPIPE;
-	}
 
  	if (count > sizeof(proxy_dev->buffer) ||
-	    !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
-		mutex_unlock(&proxy_dev->buf_lock);
+	    !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG))
  		return -EIO;
-	}
 
  	proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
 
  	proxy_dev->req_len = 0;
 
-	if (copy_from_user(proxy_dev->buffer, buf, count)) {
-		mutex_unlock(&proxy_dev->buf_lock);
+	if (buf && copy_from_user(proxy_dev->buffer, buf, count))
  		return -EFAULT;
-	}
 
  	proxy_dev->resp_len = count;
+	return count;
+}
 
+static ssize_t __vtpm_proxy_read_write_unlocked(struct proxy_dev *proxy_dev, char __user *buf,
+						size_t count)
+{
+	ssize_t rc;
+
+	do {
+		rc = __vtpm_proxy_write_unlocked(proxy_dev, buf, count);
+		if (rc < 0)
+			break;
+		rc = __vtpm_proxy_read_unlocked(proxy_dev, buf, rc);
+	} while (0);
+
+	return rc;
+}
+
+/*
+ * See struct file_operations.
+ */
+static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
+				    size_t count, loff_t *off)
+{
+	struct proxy_dev *proxy_dev = filp->private_data;
+	ssize_t rc;
+	int sig;
+
+	sig = wait_event_interruptible(proxy_dev->wq,
+		proxy_dev->req_len != 0 ||
+		!(proxy_dev->state & STATE_OPENED_FLAG));
+	if (sig)
+		return -EINTR;
+
+	mutex_lock(&proxy_dev->buf_lock);
+	rc = __vtpm_proxy_read_unlocked(proxy_dev, buf, count);
  	mutex_unlock(&proxy_dev->buf_lock);
 
+	return rc;

drivers/char/tpm/tpm_vtpm_proxy.c static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev); · line 158

Quoted 10 lines
drivers/char/tpm/tpm_vtpm_proxy.c +10 -0
+}
+
+/*
+ * See struct file_operations.
+ */
+static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
+				     size_t count, loff_t *off)
+{
+	struct proxy_dev *proxy_dev = filp->private_data;
+	int rc;

ssize_t rc;

Quoted 41 lines
drivers/char/tpm/tpm_vtpm_proxy.c +5 -23
+
+	mutex_lock(&proxy_dev->buf_lock);
+	rc = __vtpm_proxy_write_unlocked(proxy_dev, buf, count);
+	mutex_unlock(&proxy_dev->buf_lock);
  	wake_up_interruptible(&proxy_dev->wq);
 
-	return count;
+	return rc;
  }
 
  /*
@@ -295,28 +300,6 @@ static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
  	return len;
  }
 
-static int vtpm_proxy_is_driver_command(struct tpm_chip *chip,
-					u8 *buf, size_t count)
-{
-	struct tpm_header *hdr = (struct tpm_header *)buf;
-
-	if (count < sizeof(struct tpm_header))
-		return 0;
-
-	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
-		switch (be32_to_cpu(hdr->ordinal)) {
-		case TPM2_CC_SET_LOCALITY:
-			return 1;
-		}
-	} else {
-		switch (be32_to_cpu(hdr->ordinal)) {
-		case TPM_ORD_SET_LOCALITY:
-			return 1;
-		}
-	}
-	return 0;
-}
-
  /*
   * Called when core TPM driver forwards TPM requests to 'server side'.
   *

drivers/char/tpm/tpm_vtpm_proxy.c static int vtpm_proxy_is_driver_command(struct tpm_chip *chip, · line 316

Quoted 4 lines
drivers/char/tpm/tpm_vtpm_proxy.c +1 -0
@@ -330,6 +313,7 @@ static int vtpm_proxy_is_driver_command(struct tpm_chip *chip,
  static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
  {
  	struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+	unsigned int ord = ((struct tpm_header *)buf)->ordinal;

'u32 ordinal' like in tpm_try_transmit ?

you need this here in some form: be32_to_cpu(hdr->ordinal)

Quoted 5 lines
drivers/char/tpm/tpm_vtpm_proxy.c +0 -0
  	if (count > sizeof(proxy_dev->buffer)) {
  		dev_err(&chip->dev,(chip->flags & TPM_CHIP_FLAG_TPM2)
@@ -338,9 +322,11 @@ static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
  		return -EIO;

drivers/char/tpm/tpm_vtpm_proxy.c static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) · line 329

Quoted 10 lines
drivers/char/tpm/tpm_vtpm_proxy.c +5 -3
  	}
 
-	if (!(proxy_dev->state & STATE_DRIVER_COMMAND) &&
-	    vtpm_proxy_is_driver_command(chip, buf, count))
-		return -EFAULT;
+	if ((chip->flags & TPM_CHIP_FLAG_TPM2) && ord == TPM2_CC_SET_LOCALITY)
+		return -EINVAL;
+
+	if (ord == TPM_ORD_SET_LOCALITY)
+		return -EINVAL;
if ((chip->flags & TPM_CHIP_FLAG_TPM2)) {
    if (ord == )
       return -EINVAL;
} else if (ord == ) {
    return -EINVAL;
}
Quoted 10 lines
drivers/char/tpm/tpm_vtpm_proxy.c +0 -3
  	mutex_lock(&proxy_dev->buf_lock);
 
@@ -409,12 +395,10 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
  		return rc;
  	tpm_buf_append_u8(&buf, locality);
 
-	proxy_dev->state |= STATE_DRIVER_COMMAND;
-
-	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");

drivers/char/tpm/tpm_vtpm_proxy.c static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) · line 404

Quoted 10 lines
drivers/char/tpm/tpm_vtpm_proxy.c +4 -3
-
-	proxy_dev->state &= ~STATE_DRIVER_COMMAND;
-
+	mutex_lock(&proxy_dev->buf_lock);
+	memcpy(proxy_dev->buffer, buf.data, tpm_buf_length(&buf));
+	rc = __vtpm_proxy_read_write_unlocked(proxy_dev, NULL, tpm_buf_length(&buf));
+	mutex_unlock(&proxy_dev->buf_lock);
  	if (rc < 0) {
  		locality = rc;
  		goto out;

Stefan

#3 lore raw
In reply to Jarkko Sakkinen

drivers/char/tpm/tpm_vtpm_proxy.c static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev); · line 79

Quoted 7 lines · Jarkko Sakkinen

From: Jarkko Sakkinen <[email protected]>

drivers/char/tpm/tpm_vtpm_proxy.c +3 -1
-	rc = copy_to_user(buf, proxy_dev->buffer, len);
+	if (buf)
+		rc = copy_to_user(buf, proxy_dev->buffer, len);
+

Looking through other drivers it seems buf is always expected to be a valid non-NULL pointer on file_operations.read().

https://elixir.bootlin.com/linux/latest/source/arch/x86/mm/tlb.c#L1279 simple_read_from_buffer will pass the pointer to the user buffer along and it ('to') ends up in copy_to_user(to, ...);

Same here: https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_fs.c#L41

#4 lore raw

From: Stefan Berger

Quoted 6 lines

Sent: 30 May 2023 18:46

Quoted 2 lines · Jarkko Sakkinen

From: Jarkko Sakkinen <[email protected]>

Quoted 7 lines

drivers/char/tpm/tpm_vtpm_proxy.c static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev); · line 79

Quoted 4 lines
drivers/char/tpm/tpm_vtpm_proxy.c +3 -1
-	rc = copy_to_user(buf, proxy_dev->buffer, len);
+	if (buf)
+		rc = copy_to_user(buf, proxy_dev->buffer, len);
+

Looking through other drivers it seems buf is always expected to be a valid non-NULL pointer on file_operations.read().

If the user passes NULL the copy_to/from_user() fails and -EFAULT is returned.

Adding the NULL check makes the request silently succeed. I doubt that is anywhere near right when you ignore copy_from_user().

I'm not sure what the rational/subject is about either. copy_to/from_user() calls access_ok() and will fail on a kernel address.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

author maintainer
#5 lore raw
In reply to Stefan Berger
Quoted 13 lines · Stefan Berger

drivers/char/tpm/tpm_vtpm_proxy.c static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev); · line 79

Quoted 7 lines · Jarkko Sakkinen

From: Jarkko Sakkinen <[email protected]>

drivers/char/tpm/tpm_vtpm_proxy.c +3 -1
-	rc = copy_to_user(buf, proxy_dev->buffer, len);
+	if (buf)
+		rc = copy_to_user(buf, proxy_dev->buffer, len);
+

Looking through other drivers it seems buf is always expected to be a valid non-NULL pointer on file_operations.read().

Quoted 4 lines

https://elixir.bootlin.com/linux/latest/source/arch/x86/mm/tlb.c#L1279 simple_read_from_buffer will pass the pointer to the user buffer along and it ('to') ends up in copy_to_user(to, ...);

Same here: https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_fs.c#L41

It is good to mention here that IMA uses __user tagged pointers correctly, and it does not really compare to the vtpm driver code by any possible means. So let's not add illegit comparison points.

BR, Jarkko

#6 lore raw
Quoted 14 lines · Jarkko Sakkinen
Quoted 13 lines · Stefan Berger

drivers/char/tpm/tpm_vtpm_proxy.c static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev); · line 79

Quoted 7 lines · Jarkko Sakkinen

From: Jarkko Sakkinen <[email protected]>

drivers/char/tpm/tpm_vtpm_proxy.c +3 -1
-	rc = copy_to_user(buf, proxy_dev->buffer, len);
+	if (buf)
+		rc = copy_to_user(buf, proxy_dev->buffer, len);
+

Looking through other drivers it seems buf is always expected to be a valid non-NULL pointer on file_operations.read().

Quoted 8 lines
Quoted 4 lines

https://elixir.bootlin.com/linux/latest/source/arch/x86/mm/tlb.c#L1279 simple_read_from_buffer will pass the pointer to the user buffer along and it ('to') ends up in copy_to_user(to, ...);

Same here: https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_fs.c#L41

It is good to mention here that IMA uses __user tagged pointers correctly, and it does not really compare to the vtpm driver code by any possible means. So let's not add illegit comparison points.

Yes, sir. Did you read David' response?

Quoted 2 lines

BR, Jarkko

#7 lore raw
In reply to Jarkko Sakkinen

Hi Jarkko,

[This is a private test report for your RFC patch.] kernel test robot noticed the following build warnings:

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jarkko-Sakkinen/tpm-tpm_vtpm_proxy-do-not-reference-kernel-memory-as-user-memory/20230530-100537
base:   char-misc/char-misc-testing
patch link:    https://lore.kernel.org/r/20230530020133.235765-1-jarkko%40kernel.org
patch subject: [PATCH RFC v2] tpm: tpm_vtpm_proxy: do not reference kernel memory as user memory
config: m68k-randconfig-m031-20230530 (https://download.01.org/0day-ci/archive/20230531/[email protected]/config)
compiler: m68k-linux-gcc (GCC) 12.3.0

If you fix the issue, kindly add following tag where applicable

| Reported-by: kernel test robot <[email protected]>
| Reported-by: Dan Carpenter <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/

smatch warnings:
drivers/char/tpm/tpm_vtpm_proxy.c:83 __vtpm_proxy_read_unlocked() error: uninitialized symbol 'rc'.

vim +/rc +83 drivers/char/tpm/tpm_vtpm_proxy.c

919bd177830a95 Jarkko Sakkinen 2023-05-30  60  static ssize_t __vtpm_proxy_read_unlocked(struct proxy_dev *proxy_dev, char __user *buf,
919bd177830a95 Jarkko Sakkinen 2023-05-30  61  					  size_t count)
6f99612e250041 Stefan Berger   2016-04-18  62  {
6f99612e250041 Stefan Berger   2016-04-18  63  	size_t len;
919bd177830a95 Jarkko Sakkinen 2023-05-30  64  	int rc;
6f99612e250041 Stefan Berger   2016-04-18  65  
919bd177830a95 Jarkko Sakkinen 2023-05-30  66  	if (!(proxy_dev->state & STATE_OPENED_FLAG))
6f99612e250041 Stefan Berger   2016-04-18  67  		return -EPIPE;
6f99612e250041 Stefan Berger   2016-04-18  68  
6f99612e250041 Stefan Berger   2016-04-18  69  	len = proxy_dev->req_len;
6f99612e250041 Stefan Berger   2016-04-18  70  
e52432e1642309 Kees Cook       2022-01-19  71  	if (count < len || len > sizeof(proxy_dev->buffer)) {
6f99612e250041 Stefan Berger   2016-04-18  72  		pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
6f99612e250041 Stefan Berger   2016-04-18  73  			 count, len);
6f99612e250041 Stefan Berger   2016-04-18  74  		return -EIO;
6f99612e250041 Stefan Berger   2016-04-18  75  	}
6f99612e250041 Stefan Berger   2016-04-18  76  
919bd177830a95 Jarkko Sakkinen 2023-05-30  77  	if (buf)
6f99612e250041 Stefan Berger   2016-04-18  78  		rc = copy_to_user(buf, proxy_dev->buffer, len);

rc not initialized on the else path.

It must be really embarrassing for the kbuild-bot to be calling attention to your private trees. :P

919bd177830a95 Jarkko Sakkinen 2023-05-30  79  
6f99612e250041 Stefan Berger   2016-04-18  80  	memset(proxy_dev->buffer, 0, len);
6f99612e250041 Stefan Berger   2016-04-18  81  	proxy_dev->req_len = 0;
6f99612e250041 Stefan Berger   2016-04-18  82  
6f99612e250041 Stefan Berger   2016-04-18 @83  	if (!rc)
6f99612e250041 Stefan Berger   2016-04-18  84  		proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
6f99612e250041 Stefan Berger   2016-04-18  85  
6f99612e250041 Stefan Berger   2016-04-18  86  	if (rc)
6f99612e250041 Stefan Berger   2016-04-18  87  		return -EFAULT;
6f99612e250041 Stefan Berger   2016-04-18  88  
6f99612e250041 Stefan Berger   2016-04-18  89  	return len;
6f99612e250041 Stefan Berger   2016-04-18  90  }
Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki