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 ?
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
.