I know that copy_to_user
/copy_from_user
, get_user
/put_user
functions are for this purpose.
My question is that, given a user space address/pointer, how can I access the data pointed to by the address from the kernel in general?
I can imagine that first I have to make sure the containing page should be in physical memory (instead of in disk).
What is the next step? Can I use *p
, where p
is the pointer pointing to some user space data, directly to refer to the data?
Or do I have to first invoke kmap
to map the containing physical page frame to the kernel virtual address space? Why?
You may find this useful.
Let us repeat that the buff argument to the read and write methods is
a user-space pointer. Therefore, it cannot be directly dereferenced by
kernel code. There are a few reasons for this restriction:
Depending on which architecture your driver is running on, and how the
kernel was configured, the user-space pointer may not be valid while
running in kernel mode at all. There may be no mapping for that
address, or it could point to some other, random data.
Even if the pointer does mean the same thing in kernel space,
user-space memory is paged, and the memory in question might not be
resident in RAM when the system call is made. Attempting to reference
the user-space memory directly could generate a page fault, which is
something that kernel code is not allowed to do. The result would be
an "oops," which would result in the death of the process that made
the system call.
The pointer in question has been supplied by a user program, which
could be buggy or malicious. If your driver ever blindly dereferences
a user-supplied pointer, it provides an open doorway allowing a
user-space program to access or overwrite memory anywhere in the
system. If you do not wish to be responsible for compromising the
security of your users' systems, you cannot ever dereference a
user-space pointer directly.
Source: http://www.makelinux.net/ldd3/chp-3-sect-7
That said, I am myself curious to know what happens if the user-space address is indeed valid, and none of the above conditions apply...
The pointer alone is not enough! You need to know which process that pointer "belongs" to.
When the process gets preempted, the pointer points into the address space of another process. The address may not be mapped any more, yadda yadda,
If that process will be the current process when you access the data, then you should use the copy_to_user/copy_from_user functions.
If the process may be scheduled out, you can try to mlock() the page in RAM and find out which is the physical ram address of the page. Whenever you want to access it, you map that physical page into a kernel virtual address.
NOTE:
- A malicious process can munlock() the page and trick you into accessing a wrong RAM page.
- I'm not sure mlock() semantics demand the underlining RAM page MUSTN'T change.
- the kernel should be able to lock a page into RAM, I'm not familiar with the mm subsystem.
Different user space application has different page table.
1) you need to get user space program pid.
2) search addree in the pid's page table.
Below is a sample code to translate user space virtual address's into physical address.
It works at x86 platform.
taskpid = find_get_pid(curpid);
task = pid_task(taskpid, PIDTYPE_PID );
mm = get_task_mm(task);
down_read(&mm->mmap_sem);
start_vaddr = vaddr;
end_vaddr = 0xC0000000;
while( start_vaddr < end_vaddr){
u32 end;
end = (( start_vaddr + PMD_SIZE) & PMD_MASK);
if( end < start_vaddr || end > end_vaddr)
end = end_vaddr;
ret = walk_pgd(start_vaddr, end, mm);
if(ret != 0){
printk("ret: %08x \n", ret);
break;
}
start_vaddr = end;
}
up_read(&mm->mmap_sem);
paddr = ret;
kaddr = __va(paddr);
mmput(mm);
You'll need to follow
an address to get a corresponding page
struct (see follow_page for the example). Next, getting the page
struct you'll need to map it to kernel's address space via kmap
or kmap_atomic
.