clinuxlinux-kernelgnukmalloc

Passing a list of values to kernel space


I'm working on a linux project. I need to pass a list of integer values to the kernel from a userspace program. I implemented a system call for this. In the userspace program, I had the following code. The value of num_values is obtained from command line arguments.

int* ptr=NULL;
    ptr = (int*) malloc(sizeof(int)*num_values);

    for(loop_index=0;loop_index<num_values;loop_index++)
    {
        *(ptr+loop_index)=atoi(argv[optind+loop_index]);
    }

Then, I called my system call,

        ret = set_wcet_val(ptr,&num_values);

The syscall 'set_wcet_val' implementation in the kernel is as follows:

asmlinkage long sys_set_wcet_val(int* wcet_val, int* num_values)
{
    int retval=0, retval2=0, index, loop_index;
    int *wcet_ptr=NULL;
    retval2 = get_user(index,(int*)num_values);
    wcet_ptr = kmalloc(((sizeof(int))*index), GFP_ATOMIC);
    if(!wcet_ptr)
            printk("kmalloc:Error in allocating space..\n");
    if (copy_from_user(wcet_ptr, wcet_val, sizeof(wcet_ptr)))
    {
            printk("Syscall-wcet_val failed to copy data..\n");
    }
    for(loop_index=0;loop_index<index;loop_index++)
            printk("wcet_ptr value is %d\n",*(wcet_ptr+loop_index));
    kfree(wcet_ptr);
    return retval;

}

The value 'num_values' is properly copied to 'index'. The problem is only the first two data are printed when I checked dmesg. If the num_values is 3, I'm getting a random positive value for the third data. If the num_values is 4, a random positive value for third and a negative value for fourth data. For num_values > 4, all values from third data are zero. In all cases, only the first two values are copied correctly. What is the reason for this weird behaviour ?


Solution

  • At least correct the copy_from_user call (to copy the entire index entries):

    asmlinkage long sys_set_wcet_val(int* wcet_val, int* num_values)
    {
        int retval=0, retval2=0, index, loop_index;
        int *wcet_ptr=NULL;
        retval2 = get_user(index,(int*)num_values);
        wcet_ptr = kmalloc(((sizeof(int))*index), GFP_ATOMIC);
        if(!wcet_ptr)
                printk("kmalloc:Error in allocating space..\n");
        if (copy_from_user(wcet_ptr, wcet_val, (sizeof(int))*index))
        {
                printk("Syscall-wcet_val failed to copy data..\n");
        }
        for(loop_index=0;loop_index<index;loop_index++)
                printk("wcet_ptr value is %d\n",*(wcet_ptr+loop_index));
        kfree(wcet_ptr);
        return retval;
    }
    

    It would be better to pass num_values directly, instead of a pointer to it (unless you had to modify it somehow):

    asmlinkage long sys_set_wcet_val(int* wcet_val, int num_values)
    {
        int retval=0, loop_index;
        int *wcet_ptr=NULL;
        wcet_ptr = kmalloc(((sizeof(int))*num_values), GFP_ATOMIC);
        if(!wcet_ptr)
                printk("kmalloc:Error in allocating space..\n");
        if (copy_from_user(wcet_ptr, wcet_val, (sizeof(int))*num_values))
        {
                printk("Syscall-wcet_val failed to copy data..\n");
        }
        for(loop_index=0;loop_index<num_values;loop_index++)
                printk("wcet_ptr value is %d\n",*(wcet_ptr+loop_index));
        kfree(wcet_ptr);
        return retval;
    }
    

    Call it as ret = set_wcet_val(ptr, num_values);.

    But I think the best you could do is to get rid of a syscall and use a kobject instead. Look for kobject_create_and_add, sysfs_create_group, struct attribute_group.