Trouble with character device driver - c

I'm a little bit confused about character device driver. I wrote a simple character device driver and it works with strings and simple things. Now I have to implement it and modify the write function to receive an integer N.
Since I have to receive an integer I thought to modify the write function and instead of a char* buffer receive an int* number but when I try to map file_operations with "my_write" terminal gives me an error saying "incompatible array types".
My question is what I have to modify in order to receive an integer with the write function insted of a string
These are my functions:
static ssize_t my_write
(struct file *filp, const char __user *buf, size_t length, loff_t *offset)
{
int nc = 0;
if (length > BUF_LEN)
return BUF_LEN-length;
nc = copy_from_user(msg,buf,length);
msg_ptr = msg;
return length - nc;
}

Related

Problem to create char based on buffer and length in kernel module

I try to get strings from userspace within the kernel module. Till I set my char size manually it seems working properly. However, I need to make it dynamic so if I use len parameter it shows weird symbols on the end of char.
static ssize_t msecurity_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
char chars[12];
if(copy_from_user(chars,buffer,len)){
return -EFAULT;
}
printk(KERN_ALERT "Output> %s", chars);
printk(KERN_ALERT "lengh> %i", len);
return len;
}
First output is for char[len] secound has been set manualy char[12]. Even if you print len it shows value of 12.
C strings are terminated by a null character. This character is not included in any string length calculation but must be included.
Thus a string of length 12 will need 13 bytes with the last byte equal to 0.

Am I correctly implementing copy_from_user?

I am implementing the write file operation for my module, and I want to know if I'm correctly implementing the copy_from_user function. The module is a tictactoe game, so I take an input from the user. Inputs will be strings in the forms, "01 X" or "21 O" where the numbers represent columns and rows and the letters are their pieces. My write function is as follows:
ssize_t tictactoe_write(struct file *pfile, char __user *buffer, size_t length, loff_t *offset){
char* data = kmalloc(sizeof(buffer), GFP_KERNEL);
size_t unread = copy_from_user(data, buffer, sizeof(buffer));
printk(KERN_NOTICE "Written");
//Return number of bytes not read
return unread;
}
With this implementation would the data pointer be initialized with the user input? Which I can then manipulate through the rest of the module? Any help is appreciated.
To elucidate what #Tarracon meant:
ssize_t tictactoe_write(struct file *pfile, char __user *buffer, size_t length, loff_t *offset){
char* data = kmalloc(length, GFP_KERNEL);
size_t unread = copy_from_user(data, buffer, length);
printk(KERN_NOTICE "Written");
//Return number of bytes not read
return unread;
}
Also, you aren't implementing copy_from_user, you are invoking it. Big difference.

How to convert size_t to char* in kernel C?

I use a size_t variable in my kernel module. when I want to write it to a file it must be cast to char* according to vfs_write signature:
extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);
I use this function that uses vfs_write (I found it on internet):
int file_write(struct file *file, unsigned long long offset, unsigned
char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = vfs_write(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
the nbytes variable is size_t I try (char *) cast for convert nbytes to char* but the kernel immediately crashes. here is my code.
index_filename = "/home/rocket/Desktop/index_pool";
index_file = file_open(index_filename,O_WRONLY | O_CREAT, 0644);
if(index_file == NULL)
printk(KERN_ALERT "index_file open error !!.\n");
else{
// file_write(index_file, 0, nbytes, nbytes); => this crashs also
file_write(index_file, 0, (char*) nbytes, 100);
file_close(index_file);
}
Is there a way to safely convert size_t type to char * one in Kernel ?
Of course it would crash - you're trying to write 100 bytes of whatever memory location nbytes is pointing at. Which because it isn't a pointer is extremely unlikely to be a valid area of memory. And even if it was, it might not be 100 bytes in size.
What you want to be passing instead to vfs_write is a pointer to nbytes. And the size of that would be sizeof(nbytes). So you'd call your wrapper function like this
file_write(index_file, 0, (char*) &nbytes, sizeof(nbytes));
That will write out the how ever many bytes that a size_t is at the memory location of nbytes
If you want to write out the value of nbytes, which is different to what you appear to be asking in the question, you need to store it in a string and pass that to your function like this:
char temp_string[20];
sprintf(temp_string,"%zu",nbytes);
file_write(index_file, 0, temp_string, strlen(temp_string));
Is there a way to safely convert size_t type to char * one in Kernel ?
yes there is.
you should use sprintf function in linux/kernel.h library
so you should do something like this:
sprintf(destination_char_star, "%zu", your_s_size_var);
be carefull you should allocate memory to char star if needed.

copy_from_user function prints junk values

I am trying to print the content of the user space buffer in the kernel space, but I am getting some junk characters and I am not sure where I am going wrong.
SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
unsigned, flags, struct sockaddr __user *, addr,
int, addr_len) {
char *messageRead = kmalloc(len,GFP_KERNEL);
unsigned long bytesNotCopied = copy_from_user(messageRead,(char*)buff,len);
printk("The messageRead Read is %s \n",messageRead);
.....
.....
}
The bytesNotCopied returns 0, so I was able to confirm that the content was copied. but when I tried to print the messageRead value, it is returning me some junk values.
A return value of 0 indicates a successful copy.
Why do you want to print the buffer as string i.e using %s in printk(). Is buff a character string in the user process?. If not try dumping this way
for(i=0; i<len; i++) /*make sure you declare i */
{
printk("%x ",messageRead[i]);
}

recvfrom - How to convert buffer to string

I'm currently learning the network programming with C/C++... I have experience .NET but not in C (esp: in Unix network programming)
The problem that I'm facing right now is that when I receive the message, I like to split it into two parts. The format of message will be like [command] filepath (For example: get a.txt or put a.txt)
I'm using recvform to receive the message.
int recvfrom(int sockfd, void *buf, int len, unsigned int flags,
struct sockaddr *from, int *fromlen);
The problem is that I dont know how to convert "void *buff" into string so that I can do String.Split(" ") to get the command and file name.
Could anyone please explain me a way to convert "void *buff" to string?
You actually pass the pointer to the start of a character array as the buffer. The recvfrom() system call writes into that array. You then process the array as a character string - after ensuring that it is properly null terminated (which recvfrom() will not do). Of course, the sending end has to put the data on in a compatible format, but the recvfrom() will give you what the sender sent you.
So, the direct answer is "You don't convert the void * into a character string; you convert the character string into a void * by passing it to recvfrom()".
You will want a character buffer and the size of the buffer. Like this:
char buf[1024];
const size_t buf_len = sizeof(buf);
Then you can call recvfrom like so:
ssize_t r;
r = recvfrom(sock, buf, buf_len-1, flags, &sock_from, &sock_from_len);
if(r<0) {
handle_error(r);
} else if(r == 0) {
handle_closed_socket(sock);
} else {
buf[r] = '\0';
}
The buf[r] = '\0'; will guarantee that your buffer is a proper null-terminated C string. Then you can pass buf around as a string.
C is not C++. There's no string class. The strtok function might help you get what you want, assuming you null terminate your buffer manually.

Resources