Re: [RFC 1/7] staging: fbtft: add lcd register abstraction

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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(&reg->lock);
> +}
> +
> +static inline void lcdreg_unlock(struct lcdreg *reg)
> +{
> +	mutex_unlock(&reg->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





[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux