forked from Minki/linux
cc7a0bb058
Both add_slot_store() and remove_slot_store() try to fix up the drc_name copied from the store buffer by placing a NUL terminator at nbyte + 1 or in place of a '\n' if present. However, the static buffer that we copy the drc_name data into is not zeroed and can contain anything past the n-th byte. This is problematic if a '\n' byte appears in that buffer after nbytes and the string copied into the store buffer was not NUL terminated to start with as the strchr() search for a '\n' byte will mark this incorrectly as the end of the drc_name string resulting in a drc_name string that contains garbage data after the n-th byte. Additionally it will cause us to overwrite that '\n' byte on the stack with NUL, potentially corrupting data on the stack. The following debugging shows an example of the drmgr utility writing "PHB 4543" to the add_slot sysfs attribute, but add_slot_store() logging a corrupted string value. drmgr: drmgr: -c phb -a -s PHB 4543 -d 1 add_slot_store: drc_name = PHB 4543°|<82>!, rc = -19 Fix this by using strscpy() instead of memcpy() to ensure the string is NUL terminated when copied into the static drc_name buffer. Further, since the string is now NUL terminated the code only needs to change '\n' to '\0' when present. Cc: stable@vger.kernel.org Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com> [mpe: Reformat change log and add mention of possible stack corruption] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20210315214821.452959-1-tyreld@linux.ibm.com
124 lines
2.5 KiB
C
124 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Interface for Dynamic Logical Partitioning of I/O Slots on
|
|
* RPA-compliant PPC64 platform.
|
|
*
|
|
* John Rose <johnrose@austin.ibm.com>
|
|
* October 2003
|
|
*
|
|
* Copyright (C) 2003 IBM.
|
|
*/
|
|
#include <linux/kobject.h>
|
|
#include <linux/string.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci_hotplug.h>
|
|
#include "rpaphp.h"
|
|
#include "rpadlpar.h"
|
|
#include "../pci.h"
|
|
|
|
#define DLPAR_KOBJ_NAME "control"
|
|
|
|
/* Those two have no quotes because they are passed to __ATTR() which
|
|
* stringifies the argument (yuck !)
|
|
*/
|
|
#define ADD_SLOT_ATTR_NAME add_slot
|
|
#define REMOVE_SLOT_ATTR_NAME remove_slot
|
|
|
|
static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t nbytes)
|
|
{
|
|
char drc_name[MAX_DRC_NAME_LEN];
|
|
char *end;
|
|
int rc;
|
|
|
|
if (nbytes >= MAX_DRC_NAME_LEN)
|
|
return 0;
|
|
|
|
strscpy(drc_name, buf, nbytes + 1);
|
|
|
|
end = strchr(drc_name, '\n');
|
|
if (end)
|
|
*end = '\0';
|
|
|
|
rc = dlpar_add_slot(drc_name);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return nbytes;
|
|
}
|
|
|
|
static ssize_t add_slot_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "0\n");
|
|
}
|
|
|
|
static ssize_t remove_slot_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t nbytes)
|
|
{
|
|
char drc_name[MAX_DRC_NAME_LEN];
|
|
int rc;
|
|
char *end;
|
|
|
|
if (nbytes >= MAX_DRC_NAME_LEN)
|
|
return 0;
|
|
|
|
strscpy(drc_name, buf, nbytes + 1);
|
|
|
|
end = strchr(drc_name, '\n');
|
|
if (end)
|
|
*end = '\0';
|
|
|
|
rc = dlpar_remove_slot(drc_name);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return nbytes;
|
|
}
|
|
|
|
static ssize_t remove_slot_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "0\n");
|
|
}
|
|
|
|
static struct kobj_attribute add_slot_attr =
|
|
__ATTR(ADD_SLOT_ATTR_NAME, 0644, add_slot_show, add_slot_store);
|
|
|
|
static struct kobj_attribute remove_slot_attr =
|
|
__ATTR(REMOVE_SLOT_ATTR_NAME, 0644, remove_slot_show, remove_slot_store);
|
|
|
|
static struct attribute *default_attrs[] = {
|
|
&add_slot_attr.attr,
|
|
&remove_slot_attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group dlpar_attr_group = {
|
|
.attrs = default_attrs,
|
|
};
|
|
|
|
static struct kobject *dlpar_kobj;
|
|
|
|
int dlpar_sysfs_init(void)
|
|
{
|
|
int error;
|
|
|
|
dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME,
|
|
&pci_slots_kset->kobj);
|
|
if (!dlpar_kobj)
|
|
return -EINVAL;
|
|
|
|
error = sysfs_create_group(dlpar_kobj, &dlpar_attr_group);
|
|
if (error)
|
|
kobject_put(dlpar_kobj);
|
|
return error;
|
|
}
|
|
|
|
void dlpar_sysfs_exit(void)
|
|
{
|
|
sysfs_remove_group(dlpar_kobj, &dlpar_attr_group);
|
|
kobject_put(dlpar_kobj);
|
|
}
|