0

I am trying to implement a new syscall as an experiment, but I always get a segfault. I think the problem is that I try to return a pointer to a char array that is not in the user space. I tried to allocate the memory in the user space by using the GFP_USER as flag for kmalloc, but the problem remains the same.

This is how I call my syscall and I can't change it at the moment (normally would allocate memory for return and provide it as parameter):

#include <errno.h>
#include <linux/unistd.h>
#include <linux/mysyscall.h>

main() {
  printf("Returned: %s\n", mycall("user string arg"));

}

The definition of the syscall function looks like this:

#include <linux/mysyscall.h>
#include <linux/kernel.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

asmlinkage char* sys_mycall (char * str_in) {
    int size = 0;

    printk(KERN_INFO "Starting mycall.\n");
    size = strnlen_user(str_in, 255);
    printk(KERN_INFO "size = %d\n", size);
    char str[size];
    char *ret;
    ret = kmalloc(size, GFP_ATOMIC | GFP_USER); 

    if(str != NULL){
        copy_from_user(str, str_in, size);
        printk(KERN_INFO "Copied string.\n");
    }else{
        printk(KERN_ERR "str is NULL!!!\n");
        return -EFAULT;
    }

    // Doing operations on str

    int acc = access_ok(VERIFY_WRITE, ret, size);
    if(acc){
        printk(KERN_ERR "Not accessible!\n");
        return -EFAULT; 
    }

    int len = copy_to_user(ret, str, size);
    if(len){
        printk(KERN_ERR "Could not copy! len = %d\n", len);
        return -EFAULT;
    }

    return ret;
} 

I have tested it a lot and I'm sure the syscall is called correctly, but it has problems with copy_to_user. copy_to_user always returns a positive value (equal to size, so does not copy at all).
By the way the address of ret is like f74ce400 and str_in 080484ac.

Does anybody have an idea what's wrong?

0

2 Answers 2

1

The destination of the copy should be a user space pointer, while the pointer ret is in Kernel space. You should have the destination pointer be provided from the user as an argument. See for example:

Sign up to request clarification or add additional context in comments.

5 Comments

That's true. Nice find :) That was coming from trying around a lot. But unfortunately the segfault remains. I corrected my code in the question now.
OK, Edited the answer as well
That's totally true, but it is a requirement for me to not modify the syscall definition. So I try to allocate memory in behalf of the user.
Can you call the function with a pointer variable, having the input act as output (char *input_p = "input"; mycall(input_p)) ?
That would be an alternative, but unfortunately that is also not possible in this setting -.-
0

You should do copy_from_user before , calling strlen(). Typically system calls gets an extra argument with length of the argument passed.

For ex; mysyscall(str,strlen)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.