2014-10-23 08:52:33

by Russ Dill

[permalink] [raw]
Subject: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

From: Russ Dill <[email protected]>

This patch provides the FTDI genuine product verification steps
as contained within the new 2.12.00 official release. It ensures
that counterfeiters don't exploit engineering investment made
by FTDI. Counterfeit ICs are destroying innovation in the
industry.

FTDI recommends that to guarantee genuine FTDI products
please purchase either from FTDI directly or an authorised
distributor.

This is definitely not targeting end users - if you're unsure if
ICs are genuine then please don't use the drivers.

Signed-off-by: Russ Dill <[email protected]>
---
drivers/usb/serial/ftdi_sio.c | 111 +++++++++++++++++++++++++++++++++++++++++-
drivers/usb/serial/ftdi_sio.h | 41 ++++++++++++++++
2 files changed, 151 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index dc72b924c399..ef8b0dd632d3 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1377,6 +1377,48 @@ static int read_latency_timer(struct usb_serial_port *port)
return rv;
}

+static int write_eeprom(struct usb_serial_port *port, u8 addr, u16 data)
+{
+ struct usb_device *udev = port->serial->dev;
+ int rv;
+
+ rv = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_WRITE_EEPROM_REQUEST,
+ FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE,
+ data, addr,
+ NULL, 0, WDR_TIMEOUT);
+ if (rv < 0)
+ dev_err(&port->dev, "Unable to write EEPROM: %i\n", rv);
+ return rv;
+}
+
+static int read_eeprom(struct usb_serial_port *port, u8 addr, u16 *data)
+{
+ struct usb_device *udev = port->serial->dev;
+ u16 *buf;
+ int rv;
+
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ rv = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
+ FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
+ 0, addr,
+ buf, 2, WDR_TIMEOUT);
+ if (rv < 0)
+ dev_err(&port->dev, "Unable to read from EEPROM: %i\n", rv);
+ else
+ *data = *buf;
+
+ kfree(buf);
+
+ return rv;
+}
+
static int get_serial_info(struct usb_serial_port *port,
struct serial_struct __user *retinfo)
{
@@ -1723,12 +1765,78 @@ static int ftdi_sio_probe(struct usb_serial *serial,
return 0;
}

+static u16 ftdi_checksum(u16 *data, int n)
+{
+ u16 checksum;
+ int i;
+
+ checksum = 0xaaaa;
+ for (i = 0; i < n - 1; i++) {
+ checksum ^= be16_to_cpu(data[i]);
+ checksum = (checksum << 1) | (checksum >> 15);
+ }
+
+ return cpu_to_be16(checksum);
+}
+
+static void ftdi_verify(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ u16 *eeprom_data;
+ u16 checksum;
+ int eeprom_size;
+ int i;
+
+ switch (priv->chip_type) {
+ case FT232RL:
+ eeprom_size = 0x40;
+ break;
+ default:
+ /* Unsupported for verification */
+ return;
+ }
+
+ /* Latency timer needs to be 0x77 to unlock EEPROM programming */
+ if (priv->latency != 0x77) {
+ int orig_latency = priv->latency;
+ priv->latency = 0x77;
+ write_latency_timer(port);
+ priv->latency = orig_latency;
+ }
+
+ eeprom_data = kzalloc(eeprom_size * 2, GFP_KERNEL);
+ if (!eeprom_data)
+ return;
+
+ /* Read in EEPROM */
+ for (i = 0; i < eeprom_size; i++)
+ if (read_eeprom(port, i, eeprom_data + i) < 0)
+ goto end_verify;
+
+ /* Verify EEPROM is valid */
+ checksum = ftdi_checksum(eeprom_data, eeprom_size);
+ if (checksum != eeprom_data[eeprom_size - 1])
+ goto end_verify;
+
+ /* Attempt to set Vendor ID to 0 */
+ eeprom_data[1] = 0;
+
+ /* Calculate new checksum to avoid bricking devices */
+ checksum = ftdi_checksum(eeprom_data, eeprom_size);
+
+ /* Verify EEPROM programming behavior/nonbehavior */
+ write_eeprom(port, 1, 0);
+ write_eeprom(port, eeprom_size - 1, checksum);
+
+end_verify:
+ kfree(eeprom_data);
+}
+
static int ftdi_sio_port_probe(struct usb_serial_port *port)
{
struct ftdi_private *priv;
struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);

-
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -1746,6 +1854,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
ftdi_set_max_packet_size(port);
if (read_latency_timer(port) < 0)
priv->latency = 16;
+ ftdi_verify(port);
write_latency_timer(port);
create_sysfs_attrs(port);
return 0;
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index ed58c6fa8dbe..9ed6b7645cae 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -35,6 +35,9 @@
#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
#define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */
+#define FTDI_SIO_READ_EEPROM 0x90 /* Read 2 bytes from EEPROM */
+#define FTDI_SIO_WRITE_EEPROM 0x91 /* Write 2 bytes to EEPROM */
+

/* Interface indices for FT2232, FT2232H and FT4232H devices */
#define INTERFACE_A 1
@@ -345,6 +348,44 @@ enum ftdi_sio_baudrate {
*/

/*
+ * FTDI_SIO_READ_EEPROM
+ *
+ * Read 2 bytes of EEPROM data.
+ */
+#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM
+#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xC0
+
+/*
+ * BmRequestType: 1100 0000b
+ * bRequest: FTDI_SIO_READ_EEPROM
+ * wValue: 0
+ * wIndex: EEPROM address / 2
+ * wLength: 0
+ * Data: 2 bytes of data from EEPROM
+ */
+
+/*
+ * FTDI_SIO_WRITE_EEPROM
+ *
+ * Write 2 bytes of data to EEPROM.
+ */
+#define FTDI_SIO_WRITE_EEPROM_REQUEST FTDI_SIO_WRITE_EEPROM
+#define FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE 0x40
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_WRITE_EEPROM
+ * wValue: 2 bytes for EEPROM
+ * wIndex: EEPROM address / 2
+ * wLength: 0
+ * Data: None
+ *
+ * wValue:
+ * B0..15 EEPROM data
+ *
+ */
+
+/*
* FTDI_SIO_SET_EVENT_CHAR
*
* Set the special event character for the specified communications port.
--
2.1.0


2014-10-23 09:42:05

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

On Thu, Oct 23, 2014 at 01:52:24AM -0700, [email protected] wrote:
> From: Russ Dill <[email protected]>
>
> This patch provides the FTDI genuine product verification steps
> as contained within the new 2.12.00 official release. It ensures
> that counterfeiters don't exploit engineering investment made
> by FTDI. Counterfeit ICs are destroying innovation in the
> industry.
>
> FTDI recommends that to guarantee genuine FTDI products
> please purchase either from FTDI directly or an authorised
> distributor.
>
> This is definitely not targeting end users - if you're unsure if
> ICs are genuine then please don't use the drivers.
>
> Signed-off-by: Russ Dill <[email protected]>
> ---
> drivers/usb/serial/ftdi_sio.c | 111 +++++++++++++++++++++++++++++++++++++++++-
> drivers/usb/serial/ftdi_sio.h | 41 ++++++++++++++++
> 2 files changed, 151 insertions(+), 1 deletion(-)

Funny patch, you should have saved it for April 1, otherwise people
might have actually taken this seriously :)

Patches as performance art, now I've seen everything...

greg k-h

2014-10-23 09:53:55

by Frans Klaver

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

On Thu, Oct 23, 2014 at 11:40 AM, Greg KH <[email protected]> wrote:
> On Thu, Oct 23, 2014 at 01:52:24AM -0700, [email protected] wrote:
>> From: Russ Dill <[email protected]>
>>
>> This patch provides the FTDI genuine product verification steps
>> as contained within the new 2.12.00 official release. It ensures
>> that counterfeiters don't exploit engineering investment made
>> by FTDI. Counterfeit ICs are destroying innovation in the
>> industry.
>>
>> FTDI recommends that to guarantee genuine FTDI products
>> please purchase either from FTDI directly or an authorised
>> distributor.
>>
>> This is definitely not targeting end users - if you're unsure if
>> ICs are genuine then please don't use the drivers.
>>
>> Signed-off-by: Russ Dill <[email protected]>
>> ---
>> drivers/usb/serial/ftdi_sio.c | 111 +++++++++++++++++++++++++++++++++++++++++-
>> drivers/usb/serial/ftdi_sio.h | 41 ++++++++++++++++
>> 2 files changed, 151 insertions(+), 1 deletion(-)
>
> Funny patch, you should have saved it for April 1, otherwise people
> might have actually taken this seriously :)
>
> Patches as performance art, now I've seen everything...

This just made my day :)

2014-10-23 11:18:29

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

> > drivers/usb/serial/ftdi_sio.c | 111 +++++++++++++++++++++++++++++++++++++++++-
> > drivers/usb/serial/ftdi_sio.h | 41 ++++++++++++++++
> > 2 files changed, 151 insertions(+), 1 deletion(-)
>
> Funny patch, you should have saved it for April 1, otherwise people
> might have actually taken this seriously :)
>
> Patches as performance art, now I've seen everything...

Chuckle. Sillyness aside a pure detection version of that patch might be
useful so it can warn users "Running Windows may damage your adapter" 8)

Is the 0x0000, 0x0401 they end up with consistent - can we add that to the
default table so end users can at least make use of devices that have
been attacked by malware ?

Alan

2014-10-23 11:22:38

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

On Thu, Oct 23, 2014 at 12:18:15PM +0100, One Thousand Gnomes wrote:
> > > drivers/usb/serial/ftdi_sio.c | 111 +++++++++++++++++++++++++++++++++++++++++-
> > > drivers/usb/serial/ftdi_sio.h | 41 ++++++++++++++++
> > > 2 files changed, 151 insertions(+), 1 deletion(-)
> >
> > Funny patch, you should have saved it for April 1, otherwise people
> > might have actually taken this seriously :)
> >
> > Patches as performance art, now I've seen everything...
>
> Chuckle. Sillyness aside a pure detection version of that patch might be
> useful so it can warn users "Running Windows may damage your adapter" 8)
>
> Is the 0x0000, 0x0401 they end up with consistent - can we add that to the
> default table so end users can at least make use of devices that have
> been attacked by malware ?

Way ahead of you. ;)

http://marc.info/?l=linux-usb&m=141405129201389&w=2

Johan

2014-10-23 12:52:25

by Hector Martin

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

NAK. This patch neither accomplishes what FTDI intended, nor what the
author humorously intended.

> + /* Attempt to set Vendor ID to 0 */
> + eeprom_data[1] = 0;
> +
> + /* Calculate new checksum to avoid bricking devices */
> + checksum = ftdi_checksum(eeprom_data, eeprom_size);
> +
> + /* Verify EEPROM programming behavior/nonbehavior */
> + write_eeprom(port, 1, 0);
> + write_eeprom(port, eeprom_size - 1, checksum);

FTDI's verification routine sets the Product ID (at address 2) to 0 and
a dummy word (at address 0x3e) to a correctly crafted value that makes
the existing checksum pass. This bricks clone devices (setting PID to
0), while original FT232RL devices are not affected as they only commit
writes when they receive a write command to an odd EEPROM address,
combining it with the most recently issued write to an even address and
writing 32 bits at a time.

This patch instead writes the Vendor ID (at address 1) and the real
checksum (at address 0x3f). As amusing as bricking all devices would be,
unfortunately, a real FT232RL would just write garbage at addresses 0
and 0x3e too (as writes are still 32 bits, and no prior even-addressed
writes have occurred, so the holding register on the chip contains
garbage). Therefore, the real effect of this patch is to brick clone
devices (in a different way from the official driver, killing the VID
instead of the PID), while merely resetting original FT232RL devices to
defaults, due to the inadvertently corrupted even words now causing a
checksum mismatch.

Props on the humor, try again with better code next time ;-). I suggest
the following:

+ write_eeprom(port, 0, eeprom_data[0]);
+ write_eeprom(port, 1, 0);
+ write_eeprom(port, eeprom_size - 2, eeprom_data[eeprom_size - 2]);
+ write_eeprom(port, eeprom_size - 1, checksum);

This will correctly set the Vendor ID to zero on all devices, counterfeit
or not.

--
Hector Martin ([email protected])
Public Key: http://www.marcansoft.com/marcan.asc

2014-10-23 12:55:29

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

On Thu, Oct 23, 2014 at 12:18:15PM +0100, One Thousand Gnomes wrote:
> > > drivers/usb/serial/ftdi_sio.c | 111 +++++++++++++++++++++++++++++++++++++++++-
> > > drivers/usb/serial/ftdi_sio.h | 41 ++++++++++++++++
> > > 2 files changed, 151 insertions(+), 1 deletion(-)

> > Funny patch, you should have saved it for April 1, otherwise people
> > might have actually taken this seriously :)

> > Patches as performance art, now I've seen everything...

> Chuckle. Sillyness aside a pure detection version of that patch might be
> useful so it can warn users "Running Windows may damage your adapter" 8)

The EEPROM read function might also be interesting too, especially with
a userspace interface for reading it back.


Attachments:
(No filename) (721.00 B)
signature.asc (473.00 B)
Digital signature
Download all attachments

2014-10-23 14:14:32

by Russ Dill

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

On Thu, Oct 23, 2014 at 5:44 AM, Hector Martin <[email protected]> wrote:
> NAK. This patch neither accomplishes what FTDI intended, nor what the
> author humorously intended.
>
>> + /* Attempt to set Vendor ID to 0 */
>> + eeprom_data[1] = 0;
>> +
>> + /* Calculate new checksum to avoid bricking devices */
>> + checksum = ftdi_checksum(eeprom_data, eeprom_size);
>> +
>> + /* Verify EEPROM programming behavior/nonbehavior */
>> + write_eeprom(port, 1, 0);
>> + write_eeprom(port, eeprom_size - 1, checksum);
>
> FTDI's verification routine sets the Product ID (at address 2) to 0 and
> a dummy word (at address 0x3e) to a correctly crafted value that makes
> the existing checksum pass. This bricks clone devices (setting PID to
> 0), while original FT232RL devices are not affected as they only commit
> writes when they receive a write command to an odd EEPROM address,
> combining it with the most recently issued write to an even address and
> writing 32 bits at a time.
>
> This patch instead writes the Vendor ID (at address 1) and the real
> checksum (at address 0x3f). As amusing as bricking all devices would be,
> unfortunately, a real FT232RL would just write garbage at addresses 0
> and 0x3e too (as writes are still 32 bits, and no prior even-addressed
> writes have occurred, so the holding register on the chip contains
> garbage). Therefore, the real effect of this patch is to brick clone
> devices (in a different way from the official driver, killing the VID
> instead of the PID), while merely resetting original FT232RL devices to
> defaults, due to the inadvertently corrupted even words now causing a
> checksum mismatch.
>
> Props on the humor, try again with better code next time ;-). I suggest
> the following:
>
> + write_eeprom(port, 0, eeprom_data[0]);
> + write_eeprom(port, 1, 0);
> + write_eeprom(port, eeprom_size - 2, eeprom_data[eeprom_size - 2]);
> + write_eeprom(port, eeprom_size - 1, checksum);

Damned off by one errors. Yes, it should be the product ID, not the
vendor ID. These write u16's though, writing to wIndex 2 writes to
bytes 4 and 5. the correct code is:

write_eeprom(port, 2, 0);
write_eeprom(port, eeprom_size - 2, checksum);

And yes, the checksum code needs to be modified to create a specially
crafted value that allows the existing checksum to pass.

2014-10-23 17:05:38

by Hector Martin

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

On 23/10/14 23:14, Russ Dill wrote:
> On Thu, Oct 23, 2014 at 5:44 AM, Hector Martin <[email protected]> wrote:
>> + write_eeprom(port, 0, eeprom_data[0]);
>> + write_eeprom(port, 1, 0);
>> + write_eeprom(port, eeprom_size - 2, eeprom_data[eeprom_size - 2]);
>> + write_eeprom(port, eeprom_size - 1, checksum);
>
> Damned off by one errors. Yes, it should be the product ID, not the
> vendor ID. These write u16's though, writing to wIndex 2 writes to
> bytes 4 and 5. the correct code is:
>
> write_eeprom(port, 2, 0);
> write_eeprom(port, eeprom_size - 2, checksum);
>
> And yes, the checksum code needs to be modified to create a specially
> crafted value that allows the existing checksum to pass.

Oh, I thought/assumed that the switch to VID and the real checksum was a
deliberate attempt at perversing the original code into something that
bricks even real FTDIs :-) (In which case all you'd need to add is the
two extra dummy writes, to load the proper data into the buffer register
on a real FT232RL to prevent it from corrupting the adjacent EEPROM words).

--
Hector Martin ([email protected])
Public Key: http://www.marcansoft.com/marcan.asc

2014-10-23 19:41:46

by Russ Dill

[permalink] [raw]
Subject: [PATCH v2] usb: serial: Perform verification for FTDI FT232R devices

v2: Perform check using product ID instead of vendor ID and
update half word before checksum rather than checksum
itself.

This patch provides the FTDI genuine product verification steps
as contained within the new 2.12.00 official release. It ensures
that counterfeiters don't exploit engineering investment made
by FTDI. Counterfeit ICs are destroying innovation in the
industry.

FTDI recommends that to guarantee genuine FTDI products
please purchase either from FTDI directly or an authorised
distributor.

This is definitely not targeting end users - if you're unsure if
ICs are genuine then please don't use the drivers.

Signed-off-by: Russ Dill <[email protected]>
---
drivers/usb/serial/ftdi_sio.c | 113 +++++++++++++++++++++++++++++++++++++++++-
drivers/usb/serial/ftdi_sio.h | 41 +++++++++++++++
2 files changed, 153 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index dc72b924c399..c7041fd9e213 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1377,6 +1377,48 @@ static int read_latency_timer(struct usb_serial_port *port)
return rv;
}

+static int write_eeprom(struct usb_serial_port *port, u8 addr, u16 data)
+{
+ struct usb_device *udev = port->serial->dev;
+ int rv;
+
+ rv = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_WRITE_EEPROM_REQUEST,
+ FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE,
+ data, addr,
+ NULL, 0, WDR_TIMEOUT);
+ if (rv < 0)
+ dev_err(&port->dev, "Unable to write EEPROM: %i\n", rv);
+ return rv;
+}
+
+static int read_eeprom(struct usb_serial_port *port, u8 addr, u16 *data)
+{
+ struct usb_device *udev = port->serial->dev;
+ u16 *buf;
+ int rv;
+
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ rv = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ FTDI_SIO_READ_EEPROM_REQUEST,
+ FTDI_SIO_READ_EEPROM_REQUEST_TYPE,
+ 0, addr,
+ buf, 2, WDR_TIMEOUT);
+ if (rv < 0)
+ dev_err(&port->dev, "Unable to read from EEPROM: %i\n", rv);
+ else
+ *data = *buf;
+
+ kfree(buf);
+
+ return rv;
+}
+
static int get_serial_info(struct usb_serial_port *port,
struct serial_struct __user *retinfo)
{
@@ -1723,12 +1765,80 @@ static int ftdi_sio_probe(struct usb_serial *serial,
return 0;
}

+static u16 ftdi_checksum(u16 *data, int n)
+{
+ u16 checksum;
+ int i;
+
+ checksum = 0xaaaa;
+ for (i = 0; i < n; i++) {
+ checksum ^= be16_to_cpu(data[i]);
+ checksum = (checksum << 1) | (checksum >> 15);
+ }
+
+ return checksum;
+}
+
+static void ftdi_verify(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ u16 *eeprom_data;
+ u16 checksum;
+ u16 fill;
+ int eeprom_size;
+ int i;
+
+ switch (priv->chip_type) {
+ case FT232RL:
+ eeprom_size = 0x40;
+ break;
+ default:
+ /* Unsupported for verification */
+ return;
+ }
+
+ /* Latency timer needs to be 0x77 to unlock EEPROM programming */
+ if (priv->latency != 0x77) {
+ int orig_latency = priv->latency;
+ priv->latency = 0x77;
+ write_latency_timer(port);
+ priv->latency = orig_latency;
+ }
+
+ eeprom_data = kzalloc(eeprom_size * 2, GFP_KERNEL);
+ if (!eeprom_data)
+ return;
+
+ /* Read in EEPROM */
+ for (i = 0; i < eeprom_size; i++)
+ if (read_eeprom(port, i, eeprom_data + i) < 0)
+ goto end_verify;
+
+ /* Verify EEPROM is valid */
+ checksum = ftdi_checksum(eeprom_data, eeprom_size - 1);
+ if (checksum != be16_to_cpu(eeprom_data[eeprom_size - 1]))
+ goto end_verify;
+
+ /* Attempt to set Product ID to 0 */
+ eeprom_data[2] = 0;
+
+ /* Calculate value to write to memory to make checksum still valid */
+ fill = ftdi_checksum(eeprom_data, eeprom_size - 2);
+ fill ^= (checksum >> 1) | (checksum << 15);
+
+ /* Verify EEPROM programming behavior/nonbehavior */
+ write_eeprom(port, 2, 0);
+ write_eeprom(port, eeprom_size - 2, cpu_to_be16(fill));
+
+end_verify:
+ kfree(eeprom_data);
+}
+
static int ftdi_sio_port_probe(struct usb_serial_port *port)
{
struct ftdi_private *priv;
struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);

-
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -1746,6 +1856,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
ftdi_set_max_packet_size(port);
if (read_latency_timer(port) < 0)
priv->latency = 16;
+ ftdi_verify(port);
write_latency_timer(port);
create_sysfs_attrs(port);
return 0;
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index ed58c6fa8dbe..9ed6b7645cae 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -35,6 +35,9 @@
#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
#define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */
+#define FTDI_SIO_READ_EEPROM 0x90 /* Read 2 bytes from EEPROM */
+#define FTDI_SIO_WRITE_EEPROM 0x91 /* Write 2 bytes to EEPROM */
+

/* Interface indices for FT2232, FT2232H and FT4232H devices */
#define INTERFACE_A 1
@@ -345,6 +348,44 @@ enum ftdi_sio_baudrate {
*/

/*
+ * FTDI_SIO_READ_EEPROM
+ *
+ * Read 2 bytes of EEPROM data.
+ */
+#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM
+#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xC0
+
+/*
+ * BmRequestType: 1100 0000b
+ * bRequest: FTDI_SIO_READ_EEPROM
+ * wValue: 0
+ * wIndex: EEPROM address / 2
+ * wLength: 0
+ * Data: 2 bytes of data from EEPROM
+ */
+
+/*
+ * FTDI_SIO_WRITE_EEPROM
+ *
+ * Write 2 bytes of data to EEPROM.
+ */
+#define FTDI_SIO_WRITE_EEPROM_REQUEST FTDI_SIO_WRITE_EEPROM
+#define FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE 0x40
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_WRITE_EEPROM
+ * wValue: 2 bytes for EEPROM
+ * wIndex: EEPROM address / 2
+ * wLength: 0
+ * Data: None
+ *
+ * wValue:
+ * B0..15 EEPROM data
+ *
+ */
+
+/*
* FTDI_SIO_SET_EVENT_CHAR
*
* Set the special event character for the specified communications port.
--
2.1.0

2014-10-23 20:12:35

by John Stoffel

[permalink] [raw]
Subject: Re: [PATCH v2] usb: serial: Perform verification for FTDI FT232R devices



So what else is in those magic 2.12.00 official drivers besides this
eeprom magic? And why don't you printer a much more informative
message to the logs when you do fail a chip?

No matter what you say here, you're targetting end users with this
patch, even if you're just trying to put pressure on the vendors
making knock off copies of the chip. Which isn't good, but I know who
I'd be mad at if this bricked my USB to serial cable in the name of
vendor chip purity.

John

2014-10-24 06:22:43

by Perry Hung

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

Looks good Russ, just a minor fix:

- /* Attempt to set Vendor ID to 0 */
- eeprom_data[1] = 0;
-
- /* Calculate new checksum to avoid bricking devices */
- checksum = ftdi_checksum(eeprom_data, eeprom_size);
-
- /* Verify EEPROM programming behavior/nonbehavior */
- write_eeprom(port, 1, 0);
- write_eeprom(port, eeprom_size - 1, checksum);
+ /* Attempt to set Product ID */
+ switch (priv->chip_type) {
+ case FT232BM:
+ case FT232RL:
+ case FT232H:
+ if (eeprom_data[2] == 0) {
+ eeprom_data[2] = FTDI_232RL_PID;
+ /* Calculate new checksum to avoid bricking devices */
+ checksum = ftdi_checksum(eeprom_data, eeprom_size);
+
+ /* Verify EEPROM programming behavior/nonbehavior */
+ write_eeprom(port, 2, eeprom_data[2]);
+ write_eeprom(port, 3, eeprom_data[3]);
+ write_eeprom(port, eeprom_size - 2, eeprom_data[eeprom_size - 2]);
+ write_eeprom(port, eeprom_size - 1, checksum);
+ }
+ break;
+ default:
+ break;
+ }

end_verify:

On Thu, Oct 23, 2014 at 10:14 PM, Russ Dill <[email protected]> wrote:
> On Thu, Oct 23, 2014 at 5:44 AM, Hector Martin <[email protected]> wrote:
>> NAK. This patch neither accomplishes what FTDI intended, nor what the
>> author humorously intended.
>>
>>> + /* Attempt to set Vendor ID to 0 */
>>> + eeprom_data[1] = 0;
>>> +
>>> + /* Calculate new checksum to avoid bricking devices */
>>> + checksum = ftdi_checksum(eeprom_data, eeprom_size);
>>> +
>>> + /* Verify EEPROM programming behavior/nonbehavior */
>>> + write_eeprom(port, 1, 0);
>>> + write_eeprom(port, eeprom_size - 1, checksum);
>>
>> FTDI's verification routine sets the Product ID (at address 2) to 0 and
>> a dummy word (at address 0x3e) to a correctly crafted value that makes
>> the existing checksum pass. This bricks clone devices (setting PID to
>> 0), while original FT232RL devices are not affected as they only commit
>> writes when they receive a write command to an odd EEPROM address,
>> combining it with the most recently issued write to an even address and
>> writing 32 bits at a time.
>>
>> This patch instead writes the Vendor ID (at address 1) and the real
>> checksum (at address 0x3f). As amusing as bricking all devices would be,
>> unfortunately, a real FT232RL would just write garbage at addresses 0
>> and 0x3e too (as writes are still 32 bits, and no prior even-addressed
>> writes have occurred, so the holding register on the chip contains
>> garbage). Therefore, the real effect of this patch is to brick clone
>> devices (in a different way from the official driver, killing the VID
>> instead of the PID), while merely resetting original FT232RL devices to
>> defaults, due to the inadvertently corrupted even words now causing a
>> checksum mismatch.
>>
>> Props on the humor, try again with better code next time ;-). I suggest
>> the following:
>>
>> + write_eeprom(port, 0, eeprom_data[0]);
>> + write_eeprom(port, 1, 0);
>> + write_eeprom(port, eeprom_size - 2, eeprom_data[eeprom_size - 2]);
>> + write_eeprom(port, eeprom_size - 1, checksum);
>
> Damned off by one errors. Yes, it should be the product ID, not the
> vendor ID. These write u16's though, writing to wIndex 2 writes to
> bytes 4 and 5. the correct code is:
>
> write_eeprom(port, 2, 0);
> write_eeprom(port, eeprom_size - 2, checksum);
>
> And yes, the checksum code needs to be modified to create a specially
> crafted value that allows the existing checksum to pass.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/

2014-10-27 10:58:31

by Oliver Neukum

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

On Thu, 2014-10-23 at 01:52 -0700, [email protected] wrote:
> From: Russ Dill <[email protected]>
>
> This patch provides the FTDI genuine product verification steps
> as contained within the new 2.12.00 official release. It ensures
> that counterfeiters don't exploit engineering investment made
> by FTDI. Counterfeit ICs are destroying innovation in the
> industry.
>
> FTDI recommends that to guarantee genuine FTDI products
> please purchase either from FTDI directly or an authorised
> distributor.
>
> This is definitely not targeting end users - if you're unsure if
> ICs are genuine then please don't use the drivers.

So you don't want this merged?
If you want to have it merged please explain the benefit.

> +static void ftdi_verify(struct usb_serial_port *port)
> +{
> + struct ftdi_private *priv = usb_get_serial_port_data(port);
> + u16 *eeprom_data;
> + u16 checksum;
> + int eeprom_size;
> + int i;
> +
> + switch (priv->chip_type) {
> + case FT232RL:
> + eeprom_size = 0x40;
> + break;
> + default:
> + /* Unsupported for verification */
> + return;
> + }
> +
> + /* Latency timer needs to be 0x77 to unlock EEPROM programming */
> + if (priv->latency != 0x77) {
> + int orig_latency = priv->latency;
> + priv->latency = 0x77;
> + write_latency_timer(port);
> + priv->latency = orig_latency;
> + }
> +
> + eeprom_data = kzalloc(eeprom_size * 2, GFP_KERNEL);
> + if (!eeprom_data)
> + return;
> +
> + /* Read in EEPROM */
> + for (i = 0; i < eeprom_size; i++)
> + if (read_eeprom(port, i, eeprom_data + i) < 0)
> + goto end_verify;
> +
> + /* Verify EEPROM is valid */
> + checksum = ftdi_checksum(eeprom_data, eeprom_size);
> + if (checksum != eeprom_data[eeprom_size - 1])
> + goto end_verify;
> +
> + /* Attempt to set Vendor ID to 0 */
> + eeprom_data[1] = 0;
> +
> + /* Calculate new checksum to avoid bricking devices */
> + checksum = ftdi_checksum(eeprom_data, eeprom_size);
> +
> + /* Verify EEPROM programming behavior/nonbehavior */
> + write_eeprom(port, 1, 0);

In case of disconnect here, what have we just done to the device?

> + write_eeprom(port, eeprom_size - 1, checksum);
> +
> +end_verify:
> + kfree(eeprom_data);
> +}
> +

Oliver

2014-10-27 11:09:09

by Oliver Neukum

[permalink] [raw]
Subject: Re: [PATCH] usb: serial: Perform verification for FTDI FT232R devices

On Thu, 2014-10-23 at 01:52 -0700, [email protected] wrote:
> From: Russ Dill <[email protected]>
>
> This patch provides the FTDI genuine product verification steps
> as contained within the new 2.12.00 official release. It ensures
> that counterfeiters don't exploit engineering investment made
> by FTDI. Counterfeit ICs are destroying innovation in the
> industry.

Sorry about not getting the joke. I should read news before
going through a backlog.

Regards
Oliver