[SCSI] ses: fix data corruption

one system: initrd get courrupted:

RAMDISK: Compressed image found at block 0
RAMDISK: incomplete write (-28 != 2048) 134217728
crc error
VFS: Mounted root (ext2 filesystem).
Freeing unused kernel memory: 388k freed
init_special_inode: bogus i_mode (177777)
Warning: unable to open an initial console.
init_special_inode: bogus i_mode (177777)
init_special_inode: bogus i_mode (177777)
Kernel panic - not syncing: No init found.  Try passing init= option to kernel.

bisected to
commit 9927c68864
Author: James Bottomley <James.Bottomley@HansenPartnership.com>
Date:   Sun Feb 3 15:48:56 2008 -0600

    [SCSI] ses: add new Enclosure ULD

changes:
1. change char to unsigned char to avoid type change later.
2. preserve len for page1
3. need to move desc_ptr even the entry is not enclosure_component_device/raid.
   so keep desc_ptr on right position
4. record page7 len, and double check if desc_ptr out of boundary before touch.
5. fix typo in subenclosure checking: should use hdr_buf instead.

[jejb: style fixes]

Signed-off-by: Yinghai Lu <yinghai.lu@sun.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
Yinghai Lu 2008-02-13 16:25:16 -08:00 committed by James Bottomley
parent 1309d4e684
commit 691b4773aa

View file

@ -33,9 +33,9 @@
#include <scsi/scsi_host.h>
struct ses_device {
char *page1;
char *page2;
char *page10;
unsigned char *page1;
unsigned char *page2;
unsigned char *page10;
short page1_len;
short page2_len;
short page10_len;
@ -67,7 +67,7 @@ static int ses_probe(struct device *dev)
static int ses_recv_diag(struct scsi_device *sdev, int page_code,
void *buf, int bufflen)
{
char cmd[] = {
unsigned char cmd[] = {
RECEIVE_DIAGNOSTIC,
1, /* Set PCV bit */
page_code,
@ -85,7 +85,7 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code,
{
u32 result;
char cmd[] = {
unsigned char cmd[] = {
SEND_DIAGNOSTIC,
0x10, /* Set PF bit */
0,
@ -104,13 +104,13 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code,
static int ses_set_page2_descriptor(struct enclosure_device *edev,
struct enclosure_component *ecomp,
char *desc)
unsigned char *desc)
{
int i, j, count = 0, descriptor = ecomp->number;
struct scsi_device *sdev = to_scsi_device(edev->cdev.dev);
struct ses_device *ses_dev = edev->scratch;
char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
char *desc_ptr = ses_dev->page2 + 8;
unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
unsigned char *desc_ptr = ses_dev->page2 + 8;
/* Clear everything */
memset(desc_ptr, 0, ses_dev->page2_len - 8);
@ -133,14 +133,14 @@ static int ses_set_page2_descriptor(struct enclosure_device *edev,
return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len);
}
static char *ses_get_page2_descriptor(struct enclosure_device *edev,
static unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev,
struct enclosure_component *ecomp)
{
int i, j, count = 0, descriptor = ecomp->number;
struct scsi_device *sdev = to_scsi_device(edev->cdev.dev);
struct ses_device *ses_dev = edev->scratch;
char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
char *desc_ptr = ses_dev->page2 + 8;
unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
unsigned char *desc_ptr = ses_dev->page2 + 8;
ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len);
@ -160,9 +160,10 @@ static char *ses_get_page2_descriptor(struct enclosure_device *edev,
static void ses_get_fault(struct enclosure_device *edev,
struct enclosure_component *ecomp)
{
char *desc;
unsigned char *desc;
desc = ses_get_page2_descriptor(edev, ecomp);
if (desc)
ecomp->fault = (desc[3] & 0x60) >> 4;
}
@ -170,7 +171,7 @@ static int ses_set_fault(struct enclosure_device *edev,
struct enclosure_component *ecomp,
enum enclosure_component_setting val)
{
char desc[4] = {0 };
unsigned char desc[4] = {0 };
switch (val) {
case ENCLOSURE_SETTING_DISABLED:
@ -190,18 +191,20 @@ static int ses_set_fault(struct enclosure_device *edev,
static void ses_get_status(struct enclosure_device *edev,
struct enclosure_component *ecomp)
{
char *desc;
unsigned char *desc;
desc = ses_get_page2_descriptor(edev, ecomp);
if (desc)
ecomp->status = (desc[0] & 0x0f);
}
static void ses_get_locate(struct enclosure_device *edev,
struct enclosure_component *ecomp)
{
char *desc;
unsigned char *desc;
desc = ses_get_page2_descriptor(edev, ecomp);
if (desc)
ecomp->locate = (desc[2] & 0x02) ? 1 : 0;
}
@ -209,7 +212,7 @@ static int ses_set_locate(struct enclosure_device *edev,
struct enclosure_component *ecomp,
enum enclosure_component_setting val)
{
char desc[4] = {0 };
unsigned char desc[4] = {0 };
switch (val) {
case ENCLOSURE_SETTING_DISABLED:
@ -229,7 +232,7 @@ static int ses_set_active(struct enclosure_device *edev,
struct enclosure_component *ecomp,
enum enclosure_component_setting val)
{
char desc[4] = {0 };
unsigned char desc[4] = {0 };
switch (val) {
case ENCLOSURE_SETTING_DISABLED:
@ -409,11 +412,11 @@ static int ses_intf_add(struct class_device *cdev,
{
struct scsi_device *sdev = to_scsi_device(cdev->dev);
struct scsi_device *tmp_sdev;
unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr,
*addl_desc_ptr;
unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr = NULL,
*addl_desc_ptr = NULL;
struct ses_device *ses_dev;
u32 result;
int i, j, types, len, components = 0;
int i, j, types, len, page7_len = 0, components = 0;
int err = -ENOMEM;
struct enclosure_device *edev;
struct ses_component *scomp = NULL;
@ -447,7 +450,7 @@ static int ses_intf_add(struct class_device *cdev,
* traversal routines more complex */
sdev_printk(KERN_ERR, sdev,
"FIXME driver has no support for subenclosures (%d)\n",
buf[1]);
hdr_buf[1]);
goto err_free;
}
@ -461,9 +464,8 @@ static int ses_intf_add(struct class_device *cdev,
goto recv_failed;
types = buf[10];
len = buf[11];
type_ptr = buf + 12 + len;
type_ptr = buf + 12 + buf[11];
for (i = 0; i < types; i++, type_ptr += 4) {
if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
@ -494,8 +496,7 @@ static int ses_intf_add(struct class_device *cdev,
/* The additional information page --- allows us
* to match up the devices */
result = ses_recv_diag(sdev, 10, hdr_buf, INIT_ALLOC_SIZE);
if (result)
goto no_page10;
if (!result) {
len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
buf = kzalloc(len, GFP_KERNEL);
@ -508,8 +509,8 @@ static int ses_intf_add(struct class_device *cdev,
ses_dev->page10 = buf;
ses_dev->page10_len = len;
buf = NULL;
}
no_page10:
scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL);
if (!scomp)
goto err_free;
@ -530,7 +531,7 @@ static int ses_intf_add(struct class_device *cdev,
if (result)
goto simple_populate;
len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
/* add 1 for trailing '\0' we'll use */
buf = kzalloc(len + 1, GFP_KERNEL);
if (!buf)
@ -547,6 +548,7 @@ static int ses_intf_add(struct class_device *cdev,
len = (desc_ptr[2] << 8) + desc_ptr[3];
/* skip past overall descriptor */
desc_ptr += len + 4;
if (ses_dev->page10)
addl_desc_ptr = ses_dev->page10 + 8;
}
type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
@ -557,6 +559,9 @@ static int ses_intf_add(struct class_device *cdev,
struct enclosure_component *ecomp;
if (desc_ptr) {
if (desc_ptr >= buf + page7_len) {
desc_ptr = NULL;
} else {
len = (desc_ptr[2] << 8) + desc_ptr[3];
desc_ptr += 4;
/* Add trailing zero - pushes into
@ -564,22 +569,25 @@ static int ses_intf_add(struct class_device *cdev,
desc_ptr[len] = '\0';
name = desc_ptr;
}
if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE &&
type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE)
continue;
}
if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) {
ecomp = enclosure_component_register(edev,
components++,
type_ptr[0],
name);
if (desc_ptr) {
desc_ptr += len;
if (!IS_ERR(ecomp))
if (!IS_ERR(ecomp) && addl_desc_ptr)
ses_process_descriptor(ecomp,
addl_desc_ptr);
}
if (desc_ptr)
desc_ptr += len;
if (addl_desc_ptr)
addl_desc_ptr += addl_desc_ptr[1] + 2;
}
}
}
kfree(buf);