android_kernel_samsung_msm8976/drivers/power/msm_bcl.c
David Keitel 3869552d03 bcl: fix allocation for BCL attribute
The size of the BCL attribute is incorrect due to a precedence bug:

This was observed while booting with Kernel Address Sanitizer(KASan) enabled.

=============================================================================
BUG kmalloc-64 (Tainted: G    B       ): kasan: bad access detected
-----------------------------------------------------------------------------

INFO: Slab 0xffffffbc0661c6e0 objects=64 used=64 fp=0x          (null) flags=0x0080
INFO: Object 0xffffffc0a360bb00 @offset=2816 fp=0xffffffc0a3454728

Bytes b4 ffffffc0a360baf0: 3f 37 9c 1c 00 00 00 00 02 00 02 00 a9 4e ad de  ?7...........N..
Object ffffffc0a360bb00: 28 47 45 a3 c0 ff ff ff 48 47 45 a3 c0 ff ff ff  (GE.....HGE.....
Object ffffffc0a360bb10: 68 47 45 a3 c0 ff ff ff 00 00 00 00 00 00 00 00  hGE.............
Object ffffffc0a360bb20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
Object ffffffc0a360bb30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
CPU: 0 PID: 1 Comm: swapper/0 Tainted: G    B        3.10.49-g465b172-00133-gb931dc1 #134
Call trace:
[<ffffffc00040a2a4>] dump_backtrace+0x0/0x1d4
[<ffffffc00040a488>] show_stack+0x10/0x1c
[<ffffffc000f971a4>] dump_stack+0x1c/0x28
[<ffffffc00054aeb4>] print_trailer+0x144/0x158
[<ffffffc00054b210>] object_err+0x38/0x4c
[<ffffffc00054fed8>] kasan_report_error+0x210/0x3b0
[<ffffffc000550188>] kasan_report+0x68/0x78
[<ffffffc00054f1b0>] __asan_load8+0x90/0x9c
[<ffffffc0005dff78>] internal_create_group+0x1a0/0x2f4
[<ffffffc0005e00dc>] sysfs_create_group+0x10/0x1c
[<ffffffc000c5eb9c>] msm_bcl_register_param+0x384/0x450
[<ffffffc000c61758>] bcl_probe+0x840/0xb84
[<ffffffc000a394b8>] spmi_drv_probe+0x2c/0x3c
[<ffffffc000999150>] driver_probe_device+0x1f4/0x47c
[<ffffffc0009994c4>] __driver_attach+0x88/0xc0
[<ffffffc000996434>] bus_for_each_dev+0xdc/0x11c
[<ffffffc0009988ac>] driver_attach+0x2c/0x3c
[<ffffffc0009981fc>] bus_add_driver+0x1bc/0x32c
[<ffffffc000999d1c>] driver_register+0x10c/0x1d8
[<ffffffc000a39a30>] spmi_driver_register+0x98/0xa8
[<ffffffc00183a300>] bcl_perph_init+0x2c/0x38
[<ffffffc000400b00>] do_one_initcall+0xcc/0x188
[<ffffffc001800b54>] kernel_init_freeable+0x1c0/0x264
[<ffffffc000f89b84>] kernel_init+0x10/0xcc
Memory state around the buggy address:
 ffffffc0a360ba00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ffffffc0a360ba80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffffffc0a360bb00: 00 00 00 01 fc fc fc fc fc fc fc fc fc fc fc fc
                            ^
 ffffffc0a360bb80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
 ffffffc0a360bc00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================

Fix this by adding parantheses to fix precedence.

CRs-Fixed: 826589
Change-Id: Ia58b6e52c491b89b10a2b8fe45445372bfe9fa20
Signed-off-by: David Keitel <dkeitel@codeaurora.org>
2015-04-21 12:01:06 -07:00

375 lines
8.7 KiB
C

/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/sysfs.h>
#include <linux/mutex.h>
#include <linux/msm_bcl.h>
#include <linux/slab.h>
#define BCL_PARAM_MAX_ATTR 3
#define BCL_DEFINE_RO_PARAM(_attr, _name, _attr_gp, _index) \
_attr.attr.name = __stringify(_name); \
_attr.attr.mode = 0444; \
_attr.show = _name##_show; \
_attr_gp.attrs[_index] = &_attr.attr;
static struct bcl_param_data *bcl[BCL_PARAM_MAX];
static ssize_t high_trip_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
int val = 0, ret = 0;
struct bcl_param_data *dev_param = container_of(attr,
struct bcl_param_data, high_trip_attr);
if (!dev_param->registered)
return -ENODEV;
ret = dev_param->ops->get_high_trip(&val);
if (ret) {
pr_err("High trip value read failed. err:%d\n", ret);
return ret;
}
return snprintf(buf, PAGE_SIZE, "%d\n", val);
}
static ssize_t low_trip_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
int val = 0, ret = 0;
struct bcl_param_data *dev_param = container_of(attr,
struct bcl_param_data, low_trip_attr);
if (!dev_param->registered)
return -ENODEV;
ret = dev_param->ops->get_low_trip(&val);
if (ret) {
pr_err("Low trip value read failed. err:%d\n", ret);
return ret;
}
return snprintf(buf, PAGE_SIZE, "%d\n", val);
}
static ssize_t value_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
int32_t val = 0, ret = 0;
struct bcl_param_data *dev_param = container_of(attr,
struct bcl_param_data, val_attr);
if (!dev_param->registered)
return -ENODEV;
ret = dev_param->ops->read(&val);
if (ret) {
pr_err("Value read failed. err:%d\n", ret);
return ret;
}
dev_param->last_read_val = val;
return snprintf(buf, PAGE_SIZE, "%d\n", val);
}
int msm_bcl_set_threshold(enum bcl_param param_type,
enum bcl_trip_type trip_type, struct bcl_threshold *inp_thresh)
{
int ret = 0;
if (!bcl[param_type] || !bcl[param_type]->registered) {
pr_err("BCL not initialized\n");
return -EINVAL;
}
if ((!inp_thresh)
|| (inp_thresh->trip_value < 0)
|| (!inp_thresh->trip_notify)
|| (param_type >= BCL_PARAM_MAX)
|| (trip_type >= BCL_TRIP_MAX)) {
pr_err("Invalid Input\n");
return -EINVAL;
}
bcl[param_type]->thresh[trip_type] = inp_thresh;
if (trip_type == BCL_HIGH_TRIP) {
bcl[param_type]->high_trip = inp_thresh->trip_value;
ret = bcl[param_type]->ops->set_high_trip(
inp_thresh->trip_value);
} else {
bcl[param_type]->low_trip = inp_thresh->trip_value;
ret = bcl[param_type]->ops->set_low_trip(
inp_thresh->trip_value);
}
if (ret) {
pr_err("Error setting trip%d for param%d. err:%d\n", trip_type,
param_type, ret);
return ret;
}
return ret;
}
static int bcl_thresh_notify(struct bcl_param_data *param_data, int val,
enum bcl_trip_type trip_type)
{
if (!param_data || trip_type >= BCL_TRIP_MAX
|| !param_data->registered) {
pr_err("Invalid input\n");
return -EINVAL;
}
param_data->thresh[trip_type]->trip_notify(trip_type, val,
param_data->thresh[trip_type]->trip_data);
return 0;
}
static int bcl_add_sysfs_nodes(enum bcl_param param_type);
struct bcl_param_data *msm_bcl_register_param(enum bcl_param param_type,
struct bcl_driver_ops *param_ops, char *name)
{
int ret = 0;
if (!bcl[param_type]
|| param_type >= BCL_PARAM_MAX || !param_ops || !name
|| !param_ops->read || !param_ops->set_high_trip
|| !param_ops->get_high_trip || !param_ops->set_low_trip
|| !param_ops->get_low_trip || !param_ops->enable
|| !param_ops->disable) {
pr_err("Invalid input\n");
return NULL;
}
if (bcl[param_type]->registered) {
pr_err("param%d already initialized\n", param_type);
return NULL;
}
ret = bcl_add_sysfs_nodes(param_type);
if (ret) {
pr_err("Error creating sysfs nodes. err:%d\n", ret);
return NULL;
}
bcl[param_type]->ops = param_ops;
bcl[param_type]->registered = true;
strlcpy(bcl[param_type]->name, name, BCL_NAME_MAX_LEN);
param_ops->notify = bcl_thresh_notify;
return bcl[param_type];
}
int msm_bcl_unregister_param(struct bcl_param_data *param_data)
{
int i = 0, ret = -EINVAL;
if (!bcl[i] || !param_data) {
pr_err("Invalid input\n");
return ret;
}
for (; i < BCL_PARAM_MAX; i++) {
if (param_data != bcl[i])
continue;
bcl[i]->ops->disable();
bcl[i]->registered = false;
ret = 0;
break;
}
return ret;
}
int msm_bcl_disable(void)
{
int ret = 0, i = 0;
if (!bcl[i]) {
pr_err("BCL not initialized\n");
return -EINVAL;
}
for (; i < BCL_PARAM_MAX; i++) {
if (!bcl[i]->registered)
continue;
ret = bcl[i]->ops->disable();
if (ret) {
pr_err("Error in disabling interrupt. param:%d err%d\n",
i, ret);
return ret;
}
}
return ret;
}
int msm_bcl_enable(void)
{
int ret = 0, i = 0;
struct bcl_param_data *param_data = NULL;
if (!bcl[i] || !bcl[BCL_PARAM_VOLTAGE]->thresh
|| !bcl[BCL_PARAM_CURRENT]->thresh) {
pr_err("BCL not initialized\n");
return -EINVAL;
}
for (; i < BCL_PARAM_MAX; i++) {
if (!bcl[i]->registered)
continue;
param_data = bcl[i];
ret = param_data->ops->set_high_trip(param_data->high_trip);
if (ret) {
pr_err("Error setting high trip. param:%d. err:%d",
i, ret);
return ret;
}
ret = param_data->ops->set_low_trip(param_data->low_trip);
if (ret) {
pr_err("Error setting low trip. param:%d. err:%d",
i, ret);
return ret;
}
ret = param_data->ops->enable();
if (ret) {
pr_err("Error enabling interrupt. param:%d. err:%d",
i, ret);
return ret;
}
}
return ret;
}
int msm_bcl_read(enum bcl_param param_type, int *value)
{
int ret = 0;
if (!value || param_type >= BCL_PARAM_MAX) {
pr_err("Invalid input\n");
return -EINVAL;
}
if (!bcl[param_type] || !bcl[param_type]->registered) {
pr_err("BCL driver not initialized\n");
return -ENOSYS;
}
ret = bcl[param_type]->ops->read(value);
if (ret) {
pr_err("Error reading param%d. err:%d\n", param_type, ret);
return ret;
}
bcl[param_type]->last_read_val = *value;
return ret;
}
static struct class msm_bcl_class = {
.name = "msm_bcl",
};
static int bcl_add_sysfs_nodes(enum bcl_param param_type)
{
char *param_name[BCL_PARAM_MAX] = {"voltage", "current"};
int ret = 0;
bcl[param_type]->device.class = &msm_bcl_class;
dev_set_name(&bcl[param_type]->device, "%s", param_name[param_type]);
ret = device_register(&bcl[param_type]->device);
if (ret) {
pr_err("Error registering device %s. err:%d\n",
param_name[param_type], ret);
return ret;
}
bcl[param_type]->bcl_attr_gp.attrs = kzalloc(sizeof(struct attribute *)
* (BCL_PARAM_MAX_ATTR + 1), GFP_KERNEL);
if (!bcl[param_type]->bcl_attr_gp.attrs) {
pr_err("Sysfs attribute create failed.\n");
ret = -ENOMEM;
goto add_sysfs_exit;
}
BCL_DEFINE_RO_PARAM(bcl[param_type]->val_attr, value,
bcl[param_type]->bcl_attr_gp, 0);
BCL_DEFINE_RO_PARAM(bcl[param_type]->high_trip_attr, high_trip,
bcl[param_type]->bcl_attr_gp, 1);
BCL_DEFINE_RO_PARAM(bcl[param_type]->low_trip_attr, low_trip,
bcl[param_type]->bcl_attr_gp, 2);
bcl[param_type]->bcl_attr_gp.attrs[BCL_PARAM_MAX_ATTR] = NULL;
ret = sysfs_create_group(&bcl[param_type]->device.kobj,
&bcl[param_type]->bcl_attr_gp);
if (ret) {
pr_err("Failure to create sysfs nodes. err:%d", ret);
goto add_sysfs_exit;
}
add_sysfs_exit:
return ret;
}
static int msm_bcl_init(void)
{
int ret = 0, i = 0;
for (; i < BCL_PARAM_MAX; i++) {
bcl[i] = kzalloc(sizeof(struct bcl_param_data),
GFP_KERNEL);
if (!bcl[i]) {
pr_err("kzalloc failed\n");
while ((--i) >= 0)
kfree(bcl[i]);
return -ENOMEM;
}
}
return ret;
}
static int __init msm_bcl_init_driver(void)
{
int ret = 0;
ret = msm_bcl_init();
if (ret) {
pr_err("msm bcl init failed. err:%d\n", ret);
return ret;
}
return class_register(&msm_bcl_class);
}
static void __exit bcl_exit(void)
{
int i = 0;
for (; i < BCL_PARAM_MAX; i++) {
sysfs_remove_group(&bcl[i]->device.kobj,
&bcl[i]->bcl_attr_gp);
kfree(bcl[i]->bcl_attr_gp.attrs);
kfree(bcl[i]);
}
class_unregister(&msm_bcl_class);
}
fs_initcall(msm_bcl_init_driver);
module_exit(bcl_exit);