On Mon, Mar 02, 2015 at 11:54:23AM +0100, Noralf Trønnes wrote: > diff --git a/drivers/staging/fbtft/lcdreg/lcdreg-debugfs.c b/drivers/staging/fbtft/lcdreg/lcdreg-debugfs.c > new file mode 100644 > index 0000000..1cba4c2 > --- /dev/null > +++ b/drivers/staging/fbtft/lcdreg/lcdreg-debugfs.c > @@ -0,0 +1,272 @@ > +/* > + * Copyright (C) 2015 Noralf Trønnes > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#include <linux/debugfs.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/uaccess.h> > + > +#include "lcdreg.h" > + > +static struct dentry *lcdreg_debugfs_root; > + > +static int lcdreg_userbuf_to_u32(const char __user *user_buf, size_t count, > + u32 *dest, size_t dest_size) > +{ > + char *buf, *start; > + unsigned long value; > + int ret = 0; > + int i; > + > + buf = kmalloc(count, GFP_KERNEL); > + if (!buf) > + return -ENOMEM; > + > + if (copy_from_user(buf, user_buf, count)) { > + kfree(buf); > + return -EFAULT; > + } > + buf[count] = 0; Off-by-one overflow. > + for (i = 0; i < count; i++) > + if (buf[i] == ' ' || buf[i] == '\n') > + buf[i] = 0; > + i = 0; > + start = buf; > + while (start < buf + count) { > + if (*start == 0) { Use '\0' instead of 0. if (*start == '\0') > + start++; > + continue; > + } > + if (i == dest_size) { > + ret = -EFBIG; > + break; > + } > + if (kstrtoul(start, 16, &value)) { > + ret = -EINVAL; > + break; > + } Consider changing this to: ret = kstrtou32(start, 16, value); if (ret) break; > + dest[i++] = value; > + while (*start != 0) > + start++; This while loop is not needed because of the if statement earlier. > + }; > + kfree(buf); > + if (ret) > + return ret; > + > + return i ? i : -EINVAL; > +} > + > +static ssize_t lcdreg_debugfs_write_file(struct file *file, > + const char __user *user_buf, > + size_t count, loff_t *ppos) > +{ > + struct lcdreg *reg = file->private_data; > + int ret; > + u32 txbuf[128]; > + > + ret = lcdreg_userbuf_to_u32(user_buf, count, txbuf, ARRAY_SIZE(txbuf)); > + if (ret <= 0) > + return -EINVAL; This function can't return zero so preserve the error code. if (ret < 0) return ret; > + > + add_taint(TAINT_USER, LOCKDEP_STILL_OK); > + > + lcdreg_lock(reg); > + ret = lcdreg_write_buf32(reg, txbuf[0], txbuf + 1, ret - 1); > + lcdreg_unlock(reg); > + > + return ret ? ret : count; > +} > + > +static const struct file_operations lcdreg_debugfs_write_fops = { > + .open = simple_open, > + .write = lcdreg_debugfs_write_file, > + .llseek = default_llseek, > +}; > + > +static ssize_t lcdreg_debugfs_read_wr(struct file *file, > + const char __user *user_buf, > + size_t count, loff_t *ppos) > +{ > + struct lcdreg *reg = file->private_data; > + struct lcdreg_transfer tr = { > + .index = (reg->quirks & LCDREG_INDEX0_ON_READ) ? 0 : 1, > + .width = reg->debugfs_read_width, > + .count = 1, > + }; > + u32 txbuf[1]; > + char *buf = NULL; > + int ret; > + > + ret = lcdreg_userbuf_to_u32(user_buf, count, txbuf, ARRAY_SIZE(txbuf)); > + if (ret != 1) > + return -EINVAL; > + > + tr.buf = kmalloc(lcdreg_bytes_per_word(tr.width), GFP_KERNEL); > + if (!tr.buf) > + return -ENOMEM; > + > + add_taint(TAINT_USER, LOCKDEP_STILL_OK); > + > + lcdreg_lock(reg); > + ret = lcdreg_read(reg, txbuf[0], &tr); > + lcdreg_unlock(reg); > + if (ret) > + goto error_out; > + > + if (!reg->debugfs_read_result) { > + reg->debugfs_read_result = kmalloc(16, GFP_KERNEL); > + if (!reg->debugfs_read_result) { > + ret = -ENOMEM; > + goto error_out; > + } > + } Allocating here is strange. We only free ->debugfs_read_result on error so it's sort of buggy as well. Also is it racy if we have multiple readers and writers at once? > + > + buf = reg->debugfs_read_result; > + > + switch (tr.width) { > + case 8: > + snprintf(buf, 16, "%02x\n", *(u8 *)tr.buf); > + break; > + case 16: > + snprintf(buf, 16, "%04x\n", *(u16 *)tr.buf); > + break; > + case 24: > + case 32: > + snprintf(buf, 16, "%08x\n", *(u32 *)tr.buf); > + break; > + default: > + ret = -EINVAL; > + goto error_out; > + } > + > +error_out: > + kfree(tr.buf); > + if (ret) { > + kfree(reg->debugfs_read_result); > + reg->debugfs_read_result = NULL; > + return ret; > + } > + > + return count; It's often cleaner to separate error paths from the success paths. This is almost an excption because we free (tr.buf) on both paths. But it would like like this, if you decide to go that way: kfree(tr.buf); return 0; err_free: kfree(reg->debugfs_read_result); reg->debugfs_read_result = NULL; kfree(tr.buf); return ret; > +static inline void lcdreg_lock(struct lcdreg *reg) > +{ > + mutex_lock(®->lock); > +} > + > +static inline void lcdreg_unlock(struct lcdreg *reg) > +{ > + mutex_unlock(®->lock); > +} Don't add abstraction around locking. Everyone hates that. Currently we don't have any static analysis tools which do cross function locking analysis correctly. (I am partly to blame for that, sorry). But also we don't want to have to look it up to see if it a mutex or a spinlock. > +#if defined(CONFIG_DYNAMIC_DEBUG) > + > +#define lcdreg_dbg_transfer_buf(transfer) \ > +do { \ > + int groupsize = lcdreg_bytes_per_word(transfer->width); \ > + size_t len = min_t(size_t, 32, transfer->count * groupsize); \ > + \ > + print_hex_dump_debug(" buf=", DUMP_PREFIX_NONE, 32, \ > + groupsize, transfer->buf, len, false); \ > +} while (0) > + > +#elif defined(DEBUG) > + > +#define lcdreg_dbg_transfer_buf(transfer) \ > +do { \ > + int groupsize = lcdreg_bytes_per_word(transfer->width); \ > + size_t len = min_t(size_t, 32, transfer->count * groupsize); \ > + \ > + print_hex_dump(KERN_DEBUG, " buf=", DUMP_PREFIX_NONE, 32, \ > + groupsize, transfer->buf, len, false); \ > +} while (0) I don't understand this. Why can't we use print_hex_dump_debug() for both? In other words: #if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) ... #else Also can we make it an inline function? regards, dan carpenter _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel