xorl %eax, %eax

CVE-2011-1161: Linux kernel TPM Device Driver Buffer Overflow

leave a comment »

Similar to the previous one, this was also reported by Peter Huewe. A quick look in drivers/char/tpm/tpm.c shows us the exact buggy code…

/*
 * Internal kernel interface to transmit TPM commands
 */
static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
                            size_t bufsiz)
{
        ssize_t rc;
        u32 count, ordinal;
        unsigned long stop;
   ...
                u8 status = chip->vendor.status(chip);
                if ((status & chip->vendor.req_complete_mask) ==
                    chip->vendor.req_complete_val)
                        goto out_recv;
   ...
out_recv:
        rc = chip->vendor.recv(chip, (u8 *) buf, bufsiz);
        if (rc < 0)
                dev_err(chip->dev,
                        "tpm_transmit: tpm_recv: error %zd\n", rc);
   ...
}

The problem here as Peter Huewe pointed out is that the ‘buf’ buffer has limited size of ‘TPM_BUFSIZE’ leading to memory corruption with large values of ‘bufsiz’ integer. So, the above routine was patched like this:

 out_recv:
-	rc = chip->vendor.recv(chip, (u8 *) buf, bufsiz);
+	rc = chip->vendor.recv(chip, (u8 *) buf, TPM_BUFSIZE);
 	if (rc < 0)

In addition, the tpm_write() function you see below from the same source code file.

ssize_t tpm_write(struct file *file, const char __user *buf,
                  size_t size, loff_t *off)
{
   ...
        size_t in_size = size, out_size;
   ...
        if (in_size > TPM_BUFSIZE)
                in_size = TPM_BUFSIZE;

        if (copy_from_user
            (chip->data_buffer, (void __user *) buf, in_size)) {
                mutex_unlock(&chip->buffer_mutex);
                return -EFAULT;
        }

        /* atomic tpm command send and result receive */
        out_size = tpm_transmit(chip, chip->data_buffer, in_size);
   ...
        return in_size;
}

Was also updated accordingly…

 	/* atomic tpm command send and result receive */
-	out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
+	out_size = tpm_transmit(chip, chip->data_buffer, in_size);

Written by xorl

June 5, 2011 at 18:09

Posted in bugs, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s