Re: character device driver reading only last character of buffer

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

 



Some programming 101 feedback below:

On Thu, Sep 23, 2010 at 12:47 PM, Bond <jamesbond.2k.g@xxxxxxxxx> wrote:
> I wrote a small hello world type of character device driver.
> When I type echo -n "abcdef" > /dev/bond
> and do a cat /dev/bond
> then only last "f" of above input abcdef is displayed rest nothing is
> displayed.
> I asked this question earlier and some people suggested me some
> modifications
> I have done and experimented all that but I am unable to catch the error.
> Can some one point out the error?
> Here is the code
>
> /* Necessary includes for device drivers */
> #include <linux/init.h>
> //#include <linux/config.h>
> #include <linux/module.h>
> #include <linux/kernel.h> /* printk() */
> #include <linux/slab.h> /* kmalloc() */
> #include <linux/fs.h> /* everything... */
> #include <linux/errno.h> /* error codes */
> #include <linux/types.h> /* size_t */
> #include <linux/proc_fs.h>
> #include <linux/fcntl.h> /* O_ACCMODE */
> #include <asm/system.h> /* cli(), *_flags */
> #include <asm/uaccess.h> /* copy_from/to_user */
> MODULE_LICENSE("Dual BSD/GPL");
> /* Declaration of memory.c functions */
> int memory_open(struct inode *inode, struct file *filp);
> int memory_release(struct inode *inode, struct file *filp);
> ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t
> *f_pos);
> ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t
> *f_pos);
> void memory_exit(void);
> int memory_init(void);
> /* Structure that declares the usual file */
> /* access functions */
> struct file_operations memory_fops = {
>   read: memory_read,
>   write: memory_write,
>   open: memory_open,
>   release: memory_release
> };
> /* Declaration of the init and exit functions */
> module_init(memory_init);
> module_exit(memory_exit);
> /* Global variables of the driver */
> /* Major number */
> int memory_major = 60;
> /* Buffer to store data */
> char *memory_buffer;
> int memory_init(void) {
>   int result;
>   /* Registering device */
>   result = register_chrdev(memory_major, "bond", &memory_fops);
>   if (result < 0) {
>     printk(KERN_ALERT  "memory: cannot obtain major number %d\n",
> memory_major);
>     return result;
>   }
>   /* Allocating memory for the buffer */
>   memory_buffer = kmalloc(1, GFP_KERNEL);

1 is the number of bytes your allocating, it needs to be your max
buffer length.  So are by design writing a buffer overflow the way you
have it.  That is one of the typical security failures that can allow
a major breach.

Change it to:

#define MY_BUFFER_SIZE
memory_buffer = kmalloc(MY_BUFFER_SIZE, GFP_KERNEL);


>   if (!memory_buffer) {
>     result = -ENOMEM;
>     goto fail;
>   }
>   memset(memory_buffer, 0, 10);

Never hard code a length.  At least use a define.  This becomes

memset(memory_buffer, 0, MY_BUFFER_SIZE);

>   printk(KERN_ALERT "Inserting bond module\n");
>   return 0;
>   fail:
>     memory_exit();
>     return result;
> }
>
> void memory_exit(void) {
>   /* Freeing the major number */
>   unregister_chrdev(memory_major, "bond");
>   /* Freeing buffer memory */
>   if (memory_buffer) {
>     kfree(memory_buffer);
>   }
>   printk( KERN_ALERT "Removing bond module\n");
> }
>
> int memory_open(struct inode *inode, struct file *filp) {
>   /* Success */
>   return 0;
> }
>
> int memory_release(struct inode *inode, struct file *filp) {
>
>   /* Success */
>   return 0;
> }
>
> ssize_t memory_read(struct file *filp, char *buf,
>                     size_t count, loff_t *f_pos) {
>
>   /* Transfering data to user space */
>   copy_to_user(buf,memory_buffer,10);

again, don't have 10 hardcoded, but the read should actually work as
is except if the userspace buffer is too small, then your going to
overflow it with untold damage.

You need:
copy_to_user(buf,memory_buffer,min(count, MY_BUFFER_SIZE));


>   /* Changing reading position as best suits */
>   if (*f_pos == 0) {
>     *f_pos+=1;
>     return 1;
>   } else {
>     return 0;
>   }

Since you don't use f_pos, I have no idea why you're don this.  Future
plans I assume.  While troubleshooting I'm surprised you didn't
comment this out.

I would often just wrap it in a #ifdef until you get the basics working.

> }
> ssize_t memory_write( struct file *filp, char *buf,
>                       size_t count, loff_t *f_pos) {
>   char *tmp;
>   tmp=buf+count-1;

tmp now points at the last char in buf.  Clearly not what you want.

I can't even think of a logical explanation for what you're trying to
do.  I'd just delete tmp and its assignment.  Those 2 lines offer no
value I see.

>   copy_from_user(memory_buffer,tmp,10);

Try this instead
copy_from_user(memory_buffer, buf, min(count, MY_BUFFER_SIZE));

>   return 1;
> }

It looks like you're just randomly cutting and pasting code from
various places and have no idea what it really does.

I'm putting you in my autodelete list, so I won't be responding to you again.

Greg

--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to ecartis@xxxxxxxxxxxx
Please read the FAQ at http://kernelnewbies.org/FAQ




[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux