Copy_from & to_user

Source: Internet
Author: User
Tags ranges

 

Http://www.chinaunix.net Author: dreamice published on: 17:19:10

[Comment] [view the original article] [Linux discussion board] [close]

 

Copy_from & toza_user

The copy_from_user function is used to copy data from the user space to the kernel space. If data fails to be copied, no Bytes are returned, and 0 is returned.

Such a simple function involves a lot of kernel-related knowledge, such as handling exceptions in the kernel and copying data from the user space.

When data is sent to the kernel, you must be careful if the data address of the user space is invalid or exceeds the user space range, or

The addresses that have not been mapped to may have a great impact on the kernel, such as oops or system security.

The copy_from_user function is not just as simple as copying data from the user space. It also needs to perform pointer checks and process these

Method of problem. Next we will analyze this function carefully. The function prototype is in [ARCH/i386/lib/usercopy. C ].

Unsigned long

Copy_from_user (void * To, const void _ User * From, unsigned long N)

{

Might_sleep ();

If (access_ OK (verify_read, from, n ))

N = _ copy_from_user (to, from, N );

Else

Memset (to, 0, N );

Return N;

}

First, this function can be sleep. It calls might_sleep () for processing. It is defined in include/Linux/kernel. h,

In essence, schedule () is called and transferred to other processes. Next we need to verify the validity of the user space address.

Defined in [/include/asm-i386/uaccess. H.

# Define access_ OK (type, ADDR, size) (likely (_ range_ OK (ADDR, size) = 0) to call _ rang_ OK

The test is simple, that is, compare whether the size of the ADDR + size address exceeds the size of the user's process space,

That is, 0xbfffffff. Some readers may ask, if only the address range check is performed, how can we not check the validity of the pointer?

What should I do if I have mentioned the problem? This will be processed in the following function. Let's take a look. After the address range check is completed, if it succeeds, it will be called.

The _ copy_from_user function starts to copy data. If the data fails, the address of the kernel space pointed to from the to pointer should be in the to + size range.

Filling 0. _ copy_from_user is also defined in uaceess. H,

Static inline unsigned long

_ Copy_from_user (void * To, const void _ User * From, unsigned long N)

{

Might_sleep ();

Return _ copy_from_user_inatomic (to, from, N );

}

Continue to call _ copy_from_user_inatomic.

Static inline unsigned long

_ Copy_from_user_inatomic (void * To, const void _ User * From, unsigned long N)

{

If (_ builtin_constant_p (N )){

Unsigned long ret;

Switch (n ){

Case 1:

_ Get_user_size (* (u8 *) to, from, 1, RET, 1 );

Return ret;

Case 2:

_ Get_user_size (* (2010*) to, from, 2, RET, 2 );

Return ret;

Case 4:

_ Get_user_size (* (u32 *) to, from, 4, RET, 4 );

Return ret;

}

}

Return _ copy_from_user_ll (to, from, N );

}

Here, we first determine the size of the bytes to be copied. For example, if it is 8, 16, and 32, we will call _ get_user_size to copy the data.

This is an optimization in programming.

# DEFINE _ get_user_size (x, PTR, size, retval, errret )\

Do {\

Retval = 0 ;\

_ Chk_user_ptr (PTR );\

Switch (size ){\

Case 1: _ get_user_asm (x, PTR, retval, "B", "B", "= Q", errret); break ;\

Case 2: _ get_user_asm (x, PTR, retval, "W", "W", "= r", errret); break ;\

Case 4: _ get_user_asm (x, PTR, retval, "L", "", "= r", errret); break ;\

Default: (x) = _ get_user_bad ();\

}\

} While (0)

# DEFINE _ get_user_asm (x, ADDR, err, itype, Rtype, ltype, errret )\

_ ASM _ volatile __(\

"1: mov" itype "% 2, %" Rtype "1 \ n "\

"2: \ n "\

". Section. fixup, \" ax \ "\ n "\

"3: movl % 3, % 0 \ n "\

"XOR" itype "%" Rtype "1, %" Rtype "1 \ n "\

"JMP 2B \ n "\

". Previous \ n "\

". Section _ ex_table, \" A \ "\ n "\

". Align 4 \ n "\

". Long 1B, 3B \ n "\

". Previous "\

: "= R" (ERR), ltype (x )\

: "M" (_ m (ADDR), "I" (errret), "0" (ERR ))

In fact, after converting some macros, The movb, movw, and movl commands are used to transmit data.

Embedded. Section. fixup,. Section _ ex_table in the Assembly, which will be discussed in detail later.

If it is not especially large, _ copy_from_user_ll will be called for processing.

Unsigned long

_ Copy_from_user_ll (void * To, const void _ User * From, unsigned long N)

{

If (movsl_is_ OK (to, from, n ))

_ Copy_user_zeroing (to, from, N );

Else

N = _ copy_user_zeroing_intel (to, from, N );

Return N;

}

Directly call _ copy_user_zeroing to start the real data copying. After so many turns, we can see it soon.

The way out. The essence of the copy_from_user function is here.

# DEFINE _ copy_user_zeroing (to, from, size )\

Do {\

Int _ D0, _ D1, _ D2 ;\

_ ASM _ volatile __(\

"CMP $7, % 0 \ n "\

"Jbe 1f \ n "\

"Movl % 1, % 0 \ n "\

"Negl % 0 \ n "\

"Andl $7, % 0 \ n "\

"Subl % 0, % 3 \ n "\

"4: rep; movsb \ n "\

"Movl % 3, % 0 \ n "\

"Shrl $2, % 0 \ n "\

"Andl $3, % 3 \ n "\

". Align 2, 0x90 \ n "\

"0: rep; movsl \ n "\

"Movl % 3, % 0 \ n "\

"1: rep; movsb \ n "\

"2: \ n "\

". Section. fixup, \" ax \ "\ n "\

"5: addl % 3, % 0 \ n "\

"JMP 6f \ n "\

"3: Lea 0 (% 3, % 0, 4), % 0 \ n "\

"6: pushl % 0 \ n "\

"Pushl % eax \ n "\

"Xorl % eax, % eax \ n "\

"Rep; stosb \ n "\

"Popl % eax \ n "\

"Popl % 0 \ n "\

"JMP 2B \ n "\

". Previous \ n "\

". Section _ ex_table, \" A \ "\ n "\

". Align 4 \ n "\

". Long 4B, 5b \ n "\

". Long 0b, 3B \ n "\

". Long 1B, 6B \ n "\

". Previous "\

: "= & C" (size), "= & D" (_ D0), "= & S" (_ D1 ), "= r" (_ D2 )\

: "3" (size), "0" (size), "1" (to), "2" (from )\

: "Memory ");\

} While (0)

The first part of this function is relatively simple, that is, copying data.

What if the address of the user space is not mapped? In some old kernel versions

Verify_area () is used to verify the legality of the address, for example, in the early Linux 0.11 kernel.

[Linux0.11/kenrel/fork. C]

// Verify the function before writing the process space. In the modern CPU, its control register Cr0 has a write protection flag (WP: 16), the kernel can be configured through

// This bit disables code with a privilege level 0 from writing data to a read-only page of the user space. Otherwise, Writing Protection exceptions may occur.

// ADDR is the physical address of the memory.

Void verify_area (void * ADDR, int size)

{

Unsigned long start;

Start = (unsigned long) ADDR;

Size + = Start & 0 xfff; // start & 0xfff is the offset of the starting address ADDR in the page, 2 ^ 12 = 4096

Start & = 0xfffff000; // start is the page START address, that is, the page boundary value. Start is the logical address in the current process space.

Start + = get_base (current-> LDT [2]); // get_base (current-> LDT [2]) is the start address of the process data segment in the linear address space, add start to the address in the linear space of the system.

Page boundary ADDR ---- size ----- page boundary

+ -------------------------------------------------------- +

|... | Start & 0 xfff |... |

+ -------------------------------------------------------- +

| START |

Start ----------- size -------------

While (size> 0 ){

Size-= 4096;

Write_verify (start); // write protection verification in the unit of pages. If the page is read-only, it is changed to writable.

Start + = 4096;

}

}

[Linux0.11/MM/memory. C]

// Verify whether a linear address can be written.

Void write_verify (unsigned long address)

{

Unsigned long page;

// If the corresponding page table is empty, return directly

If (! (Page = * (unsigned long *) (address> 20) & 0 xffc) & 1 ))

Return;

Page & = 0xfffff000;

Page + = (address> 10) & 0 xffc );

// After calculation, the page is the content of the page table item, pointing to the actual physical address of a page

If (3 & * (unsigned long *) page) = 1) // verify whether the page can be written. If the page cannot be written, run un_wp_page to cancel write protection.

Un_wp_page (unsigned long *) page );

Return;

}

However, it would be a waste of time to perform this check every time you copy data in the user space. After all, there are few bad pointers.

Yes. In the new kernel, when copying data from the user space, the check to verify the validity of the pointer is canceled,

Check multiple address ranges, as access_ OK () does. If a bad pointer is encountered, page exception and error handling are required.

The program is processing him. Let's look at the do_page_fault function.

[ARCH/asm-i386/MM/fault. c/do_page_falut ()]

Fastcall void do_page_fault (struct pt_regs * regs, unsigned long error_code)

{

...

...

If (! Down_read_trylock (& mm-> mmap_sem )){

If (error_code & 4) = 0 &&

! Search_exception_tables (regs-> EIP ))

Goto bad_area_nosemaphore;

Down_read (& mm-> mmap_sem );

}

...

...

Bad_area_nosemaphore:

...

No_context:

 

If (fixup_exception (regs ))

Return;

...

...

}

Error_code stores error codes. (error_code & 4) = 0 indicates that the cause of the exception is in the kernel.

He calls fixup_exception (regs) to solve this problem. Since there is an error, how can he fix it?

Let's take a look at the implementation of the fixup_exception () function:

[ARCH/asm-i386/MM/extable. C]

Int fixup_exception (struct pt_regs * regs)

{

Const struct exception_table_entry * fixup;

...

Fixup = search_exception_tables (regs-> EIP );

If (fixup ){

Regs-> EIP = fixup-> fixup;

Return 1;

}

...

}

[Kernel/extable. C]

Const struct exception_table_entry * search_exception_tables (unsigned long ADDR)

{

Const struct prediction_table_entry * E;

E = search_extable (_ start ___ ex_table, _ Stop ___ ex_table-1, ADDR );

If (! E)

E = search_module_extables (ADDR );

Return E;

}

[/Lib/extable. C]

Const struct prediction_table_entry *

Search_extable (const struct prediction_table_entry * first,

Const struct prediction_table_entry * last,

Unsigned long value)

{

While (first insn> value)

Last = mid-1;

Else

Return mid;

}

Return NULL;

}

There is an abnormal error address table in the kernel. The address table also corresponds to the fix address of an error address. Its structure is as follows:

[/Include/asm-i386/uaccess. H]

Struct prediction_table_entry

{

Unsigned long insn, fixup;

};

Insn is the address that generates the exception command, and fixup is used to fix the address of the error address, that is, when the exception occurs, use its

Address to replace the address where the exception command occurs. Section _ ex_table in _ copy_user_zeroing indicates an error.

Address of the address table.. Section. fixup indicates the address to be repaired. They are two special sections in the elf document format.

". Section _ ex_table, \" A \ "\ n "\

". Align 4 \ n "\

". Long 4B, 5b \ n "\

". Long 0b, 3B \ n "\

". Long 1B, 6B \ n"

4B, 5b means that when the error address is on the address corresponding to 4B, it is transferred to the address corresponding to 5B and then run,

That is, the repaired address. And so on. Therefore, the fixup_exception () function is easy to understand.

It is to search for the abnormal address table based on the error address, find the corresponding repair address, and go there to execute it.

OK. Now, the copy_from_user function has been analyzed. If you do not understand anything, you can read

/Usr/src/Linux/documentation/exception.txt to learn more about exception handling.

 

Copy_from&to_user=

 

 

--------------------------------------------------------------------------------

 

Http://www.chinaunix.net Author: dreamice published on: 17:19:10

[Comment] [view the original article] [Linux discussion board] [close]

 

Copy_from & toza_user

The copy_from_user function is used to copy data from the user space to the kernel space. If data fails to be copied, no Bytes are returned, and 0 is returned.

Such a simple function involves a lot of kernel-related knowledge, such as handling exceptions in the kernel and copying data from the user space.

When data is sent to the kernel, you must be careful if the data address of the user space is invalid or exceeds the user space range, or

The addresses that have not been mapped to may have a great impact on the kernel, such as oops or system security.

The copy_from_user function is not just as simple as copying data from the user space. It also needs to perform pointer checks and process these

Method of problem. Next we will analyze this function carefully. The function prototype is in [ARCH/i386/lib/usercopy. C ].

Unsigned long

Copy_from_user (void * To, const void _ User * From, unsigned long N)

{

Might_sleep ();

If (access_ OK (verify_read, from, n ))

N = _ copy_from_user (to, from, N );

Else

Memset (to, 0, N );

Return N;

}

First, this function can be sleep. It calls might_sleep () for processing. It is defined in include/Linux/kernel. h,

In essence, schedule () is called and transferred to other processes. Next we need to verify the validity of the user space address.

Defined in [/include/asm-i386/uaccess. H.

# Define access_ OK (type, ADDR, size) (likely (_ range_ OK (ADDR, size) = 0) to call _ rang_ OK

The test is simple, that is, compare whether the size of the ADDR + size address exceeds the size of the user's process space,

That is, 0xbfffffff. Some readers may ask, if only the address range check is performed, how can we not check the validity of the pointer?

What should I do if I have mentioned the problem? This will be processed in the following function. Let's take a look. After the address range check is completed, if it succeeds, it will be called.

The _ copy_from_user function starts to copy data. If the data fails, the address of the kernel space pointed to from the to pointer should be in the to + size range.

Filling 0. _ copy_from_user is also defined in uaceess. H,

Static inline unsigned long

_ Copy_from_user (void * To, const void _ User * From, unsigned long N)

{

Might_sleep ();

Return _ copy_from_user_inatomic (to, from, N );

}

Continue to call _ copy_from_user_inatomic.

Static inline unsigned long

_ Copy_from_user_inatomic (void * To, const void _ User * From, unsigned long N)

{

If (_ builtin_constant_p (N )){

Unsigned long ret;

Switch (n ){

Case 1:

_ Get_user_size (* (u8 *) to, from, 1, RET, 1 );

Return ret;

Case 2:

_ Get_user_size (* (2010*) to, from, 2, RET, 2 );

Return ret;

Case 4:

_ Get_user_size (* (u32 *) to, from, 4, RET, 4 );

Return ret;

}

}

Return _ copy_from_user_ll (to, from, N );

}

Here, we first determine the size of the bytes to be copied. For example, if it is 8, 16, and 32, we will call _ get_user_size to copy the data.

This is an optimization in programming.

# DEFINE _ get_user_size (x, PTR, size, retval, errret )\

Do {\

Retval = 0 ;\

_ Chk_user_ptr (PTR );\

Switch (size ){\

Case 1: _ get_user_asm (x, PTR, retval, "B", "B", "= Q", errret); break ;\

Case 2: _ get_user_asm (x, PTR, retval, "W", "W", "= r", errret); break ;\

Case 4: _ get_user_asm (x, PTR, retval, "L", "", "= r", errret); break ;\

Default: (x) = _ get_user_bad ();\

}\

} While (0)

# DEFINE _ get_user_asm (x, ADDR, err, itype, Rtype, ltype, errret )\

_ ASM _ volatile __(\

"1: mov" itype "% 2, %" Rtype "1 \ n "\

"2: \ n "\

". Section. fixup, \" ax \ "\ n "\

"3: movl % 3, % 0 \ n "\

"XOR" itype "%" Rtype "1, %" Rtype "1 \ n "\

"JMP 2B \ n "\

". Previous \ n "\

". Section _ ex_table, \" A \ "\ n "\

". Align 4 \ n "\

". Long 1B, 3B \ n "\

". Previous "\

: "= R" (ERR), ltype (x )\

: "M" (_ m (ADDR), "I" (errret), "0" (ERR ))

In fact, after converting some macros, The movb, movw, and movl commands are used to transmit data.

Embedded. Section. fixup,. Section _ ex_table in the Assembly, which will be discussed in detail later.

If it is not especially large, _ copy_from_user_ll will be called for processing.

Unsigned long

_ Copy_from_user_ll (void * To, const void _ User * From, unsigned long N)

{

If (movsl_is_ OK (to, from, n ))

_ Copy_user_zeroing (to, from, N );

Else

N = _ copy_user_zeroing_intel (to, from, N );

Return N;

}

Directly call _ copy_user_zeroing to start the real data copying. After so many turns, we can see it soon.

The way out. The essence of the copy_from_user function is here.

# DEFINE _ copy_user_zeroing (to, from, size )\

Do {\

Int _ D0, _ D1, _ D2 ;\

_ ASM _ volatile __(\

"CMP $7, % 0 \ n "\

"Jbe 1f \ n "\

"Movl % 1, % 0 \ n "\

"Negl % 0 \ n "\

"Andl $7, % 0 \ n "\

"Subl % 0, % 3 \ n "\

"4: rep; movsb \ n "\

"Movl % 3, % 0 \ n "\

"Shrl $2, % 0 \ n "\

"Andl $3, % 3 \ n "\

". Align 2, 0x90 \ n "\

"0: rep; movsl \ n "\

"Movl % 3, % 0 \ n "\

"1: rep; movsb \ n "\

"2: \ n "\

". Section. fixup, \" ax \ "\ n "\

"5: addl % 3, % 0 \ n "\

"JMP 6f \ n "\

"3: Lea 0 (% 3, % 0, 4), % 0 \ n "\

"6: pushl % 0 \ n "\

"Pushl % eax \ n "\

"Xorl % eax, % eax \ n "\

"Rep; stosb \ n "\

"Popl % eax \ n "\

"Popl % 0 \ n "\

"JMP 2B \ n "\

". Previous \ n "\

". Section _ ex_table, \" A \ "\ n "\

". Align 4 \ n "\

". Long 4B, 5b \ n "\

". Long 0b, 3B \ n "\

". Long 1B, 6B \ n "\

". Previous "\

: "= & C" (size), "= & D" (_ D0), "= & S" (_ D1 ), "= r" (_ D2 )\

: "3" (size), "0" (size), "1" (to), "2" (from )\

: "Memory ");\

} While (0)

The first part of this function is relatively simple, that is, copying data.

What if the address of the user space is not mapped? In some old kernel versions

Verify_area () is used to verify the legality of the address, for example, in the early Linux 0.11 kernel.

[Linux0.11/kenrel/fork. C]

// Verify the function before writing the process space. In the modern CPU, its control register Cr0 has a write protection flag (WP: 16), the kernel can be configured through

// This bit disables code with a privilege level 0 from writing data to a read-only page of the user space. Otherwise, Writing Protection exceptions may occur.

// ADDR is the physical address of the memory.

Void verify_area (void * ADDR, int size)

{

Unsigned long start;

Start = (unsigned long) ADDR;

Size + = Start & 0 xfff; // start & 0xfff is the offset of the starting address ADDR in the page, 2 ^ 12 = 4096

Start & = 0xfffff000; // start is the page START address, that is, the page boundary value. Start is the logical address in the current process space.

Start + = get_base (current-> LDT [2]); // get_base (current-> LDT [2]) is the start address of the process data segment in the linear address space, add start to the address in the linear space of the system.

Page boundary ADDR ---- size ----- page boundary

+ -------------------------------------------------------- +

|... | Start & 0 xfff |... |

+ -------------------------------------------------------- +

| START |

Start ----------- size -------------

While (size> 0 ){

Size-= 4096;

Write_verify (start); // write protection verification in the unit of pages. If the page is read-only, it is changed to writable.

Start + = 4096;

}

}

[Linux0.11/MM/memory. C]

// Verify whether a linear address can be written.

Void write_verify (unsigned long address)

{

Unsigned long page;

// If the corresponding page table is empty, return directly

If (! (Page = * (unsigned long *) (address> 20) & 0 xffc) & 1 ))

Return;

Page & = 0xfffff000;

Page + = (address> 10) & 0 xffc );

// After calculation, the page is the content of the page table item, pointing to the actual physical address of a page

If (3 & * (unsigned long *) page) = 1) // verify whether the page can be written. If the page cannot be written, run un_wp_page to cancel write protection.

Un_wp_page (unsigned long *) page );

Return;

}

However, it would be a waste of time to perform this check every time you copy data in the user space. After all, there are few bad pointers.

Yes. In the new kernel, when copying data from the user space, the check to verify the validity of the pointer is canceled,

Check multiple address ranges, as access_ OK () does. If a bad pointer is encountered, page exception and error handling are required.

The program is processing him. Let's look at the do_page_fault function.

[ARCH/asm-i386/MM/fault. c/do_page_falut ()]

Fastcall void do_page_fault (struct pt_regs * regs, unsigned long error_code)

{

...

...

If (! Down_read_trylock (& mm-> mmap_sem )){

If (error_code & 4) = 0 &&

! Search_exception_tables (regs-> EIP ))

Goto bad_area_nosemaphore;

Down_read (& mm-> mmap_sem );

}

...

...

Bad_area_nosemaphore:

...

No_context:

 

If (fixup_exception (regs ))

Return;

...

...

}

Error_code stores error codes. (error_code & 4) = 0 indicates that the cause of the exception is in the kernel.

He calls fixup_exception (regs) to solve this problem. Since there is an error, how can he fix it?

Let's take a look at the implementation of the fixup_exception () function:

[ARCH/asm-i386/MM/extable. C]

Int fixup_exception (struct pt_regs * regs)

{

Const struct exception_table_entry * fixup;

...

Fixup = search_exception_tables (regs-> EIP );

If (fixup ){

Regs-> EIP = fixup-> fixup;

Return 1;

}

...

}

[Kernel/extable. C]

Const struct exception_table_entry * search_exception_tables (unsigned long ADDR)

{

Const struct prediction_table_entry * E;

E = search_extable (_ start ___ ex_table, _ Stop ___ ex_table-1, ADDR );

If (! E)

E = search_module_extables (ADDR );

Return E;

}

[/Lib/extable. C]

Const struct prediction_table_entry *

Search_extable (const struct prediction_table_entry * first,

Const struct prediction_table_entry * last,

Unsigned long value)

{

While (first insn> value)

Last = mid-1;

Else

Return mid;

}

Return NULL;

}

There is an abnormal error address table in the kernel. The address table also corresponds to the fix address of an error address. Its structure is as follows:

[/Include/asm-i386/uaccess. H]

Struct prediction_table_entry

{

Unsigned long insn, fixup;

};

Insn is the address that generates the exception command, and fixup is used to fix the address of the error address, that is, when the exception occurs, use its

Address to replace the address where the exception command occurs. Section _ ex_table in _ copy_user_zeroing indicates an error.

Address of the address table.. Section. fixup indicates the address to be repaired. They are two special sections in the elf document format.

". Section _ ex_table, \" A \ "\ n "\

". Align 4 \ n "\

". Long 4B, 5b \ n "\

". Long 0b, 3B \ n "\

". Long 1B, 6B \ n"

4B, 5b means that when the error address is on the address corresponding to 4B, it is transferred to the address corresponding to 5B and then run,

That is, the repaired address. And so on. Therefore, the fixup_exception () function is easy to understand.

It is to search for the abnormal address table based on the error address, find the corresponding repair address, and go there to execute it.

OK. Now, the copy_from_user function has been analyzed. If you do not understand anything, you can read

/Usr/src/Linux/documentation/exception.txt to learn more about exception handling.

 

Copy_from&to_user=

 

Http://linux.chinaunix.net/bbs/viewthread.php? Tid = 1044255

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.