Commit 9c6256a5 authored by Xiao Han's avatar Xiao Han Committed by Greg Kroah-Hartman

usb: misc: ftdi-elan: Fix off-by-one memory corruptions

This patch fixes fives off-by-one bugs in the ftdi-elan driver code. The
bug can be triggered by plugging a USB adapter for CardBus 3G cards (model
U132 manufactured by Elan Digital Systems, Ltd), causing a kernel panic.
The fix was tested on Ubuntu 14.04.4 with 4.7.0-rc14.2.0-27-generic+ and
4.4.0-22-generic+ kernel. In the ftdi_elan_synchronize function, an
off-by-one memory corruption occurs when packet_bytes is equal or bigger
than m. After having read m bytes, that is bytes_read is equal to m, "
..\x00" is still copied to the stack variable causing an out bounds write
of 4 bytes, which overwrites the stack canary and results in a kernel
panic.

This off-by-one requires physical access to the machine. It is not
exploitable since we have no control on the overwritten data.  Similar
off-by-one bugs have been observed in 4 other functions:
ftdi_elan_stuck_waiting, ftdi_elan_read, ftdi_elan_edset_output and
ftdi_elan_flush_input_fifo.
Reported-by: default avatarAlex Palesandro <palexster@gmail.com>
Signed-off-by: default avatarXiao Han <xiao.han@orange.fr>
Tested-by: default avatarPaul Chaignon <pchaigno@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 28324936
...@@ -665,7 +665,7 @@ static ssize_t ftdi_elan_read(struct file *file, char __user *buffer, ...@@ -665,7 +665,7 @@ static ssize_t ftdi_elan_read(struct file *file, char __user *buffer,
{ {
char data[30 *3 + 4]; char data[30 *3 + 4];
char *d = data; char *d = data;
int m = (sizeof(data) - 1) / 3; int m = (sizeof(data) - 1) / 3 - 1;
int bytes_read = 0; int bytes_read = 0;
int retry_on_empty = 10; int retry_on_empty = 10;
int retry_on_timeout = 5; int retry_on_timeout = 5;
...@@ -1684,7 +1684,7 @@ wait:if (ftdi->disconnected > 0) { ...@@ -1684,7 +1684,7 @@ wait:if (ftdi->disconnected > 0) {
int i = 0; int i = 0;
char data[30 *3 + 4]; char data[30 *3 + 4];
char *d = data; char *d = data;
int m = (sizeof(data) - 1) / 3; int m = (sizeof(data) - 1) / 3 - 1;
int l = 0; int l = 0;
struct u132_target *target = &ftdi->target[ed]; struct u132_target *target = &ftdi->target[ed];
struct u132_command *command = &ftdi->command[ struct u132_command *command = &ftdi->command[
...@@ -1876,7 +1876,7 @@ more:{ ...@@ -1876,7 +1876,7 @@ more:{
if (packet_bytes > 2) { if (packet_bytes > 2) {
char diag[30 *3 + 4]; char diag[30 *3 + 4];
char *d = diag; char *d = diag;
int m = (sizeof(diag) - 1) / 3; int m = (sizeof(diag) - 1) / 3 - 1;
char *b = ftdi->bulk_in_buffer; char *b = ftdi->bulk_in_buffer;
int bytes_read = 0; int bytes_read = 0;
diag[0] = 0; diag[0] = 0;
...@@ -2053,7 +2053,7 @@ static int ftdi_elan_synchronize(struct usb_ftdi *ftdi) ...@@ -2053,7 +2053,7 @@ static int ftdi_elan_synchronize(struct usb_ftdi *ftdi)
if (packet_bytes > 2) { if (packet_bytes > 2) {
char diag[30 *3 + 4]; char diag[30 *3 + 4];
char *d = diag; char *d = diag;
int m = (sizeof(diag) - 1) / 3; int m = (sizeof(diag) - 1) / 3 - 1;
char *b = ftdi->bulk_in_buffer; char *b = ftdi->bulk_in_buffer;
int bytes_read = 0; int bytes_read = 0;
unsigned char c = 0; unsigned char c = 0;
...@@ -2155,7 +2155,7 @@ more:{ ...@@ -2155,7 +2155,7 @@ more:{
if (packet_bytes > 2) { if (packet_bytes > 2) {
char diag[30 *3 + 4]; char diag[30 *3 + 4];
char *d = diag; char *d = diag;
int m = (sizeof(diag) - 1) / 3; int m = (sizeof(diag) - 1) / 3 - 1;
char *b = ftdi->bulk_in_buffer; char *b = ftdi->bulk_in_buffer;
int bytes_read = 0; int bytes_read = 0;
diag[0] = 0; diag[0] = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment