常见问题常见问题   搜索搜索   会员列表会员列表   团队团队   注册注册    个人资料个人资料   登录查看您的站内信件登录查看您的站内信件   登录登录 

再发一篇,FreeBSD 5内核源代码分析之copyin()实现原理

 
发表新文章   这个论题已经被锁定,您不能发表、回复或者编辑文章。    FreeBSD China -> 中文-桌面-开发-调试
阅读上一个主题 :: 阅读下一个主题  
作者 留言
wheelz
半仙


注册时间: 2002-11-18
文章: 120

文章发表于: Mon 2004-05-10 11:24:59    发表主题: 再发一篇,FreeBSD 5内核源代码分析之copyin()实现原理 引用并回复

FreeBSD 5内核源代码分析之copyin()实现原理
by wheelz
--------------------

在内核中,用户态和内核态之间的数据拷贝主要通过copyin()和copyout()
两个函数完成。与普通的数据拷贝不同,用户态和内核态之间的数据拷贝必须
考虑到用户给出的地址是否有效,即该地址是否有真正的地址映射。同时又要
考虑到效率。因此也不可能对用户给出地址的每个字节检查一遍。

FreeBSD和linux一样(linux中是copy_from_user()和copy_to_user()),
都是先拷贝,出错以后再进行错误处理,有着异曲同工之妙。

本文所有代码,如未注明,均来自sys/i386/i386/support.s

copyin()由汇编语言写成,我们逐句来看。

代码:

/*
 * copyin(from_user, to_kernel, len) - MP SAFE
 */
ENTRY(copyin)
   MEXITCOUNT
   jmp   *copyin_vector

copyin()有三个参数,from_user为用户态数据地址,to_kernel为内核缓冲区地址,
len为数据长度。它们分别位于堆栈的位置是:
代码:

   from_user:   12(%esp)
   to_kernel:   16(%esp)
   len:      20(%esp)


这里copyin_vector定义为
代码:

   .globl   copyin_vector
copyin_vector:
   .long   generic_copyin

定义一个数据copyin_vector,其值是generic_copyin

这里*copyin_vector的值就是generic_copyin,因此将跳转到generic_copyin。

代码:

ENTRY(generic_copyin)
   movl   PCPU(CURPCB),%eax
   movl   $copyin_fault,PCB_ONFAULT(%eax)

先将curpcb地址存入%eax,然后将curpcb->pcb_onfault置为copyin_fault,
这里copyin_fault也是一个程序标号,拷贝出错时将跳转到这里。下面我们将看到。
代码:

   pushl   %esi
   pushl   %edi
   movl   12(%esp),%esi         /* caddr_t from */
   movl   16(%esp),%edi         /* caddr_t to */
   movl   20(%esp),%ecx         /* size_t  len */

分别将from_user, to_kernel, len存入寄存器%esi, %edi, %ecx

代码:

   /*
    * make sure address is valid
    */
   movl   %esi,%edx
   addl   %ecx,%edx
   jc   copyin_fault

这几句看from_user+len是否有整数溢出。

代码:

   cmpl   $VM_MAXUSER_ADDRESS,%edx
   ja   copyin_fault

相加之和是否在用户有效地址空间内。

代码:

#if defined(I586_CPU) && defined(DEV_NPX)
   ALIGN_TEXT
slow_copyin:
#endif
   movb   %cl,%al
   shrl   $2,%ecx            /* copy longword-wise */
   cld
   rep
   movsl

先以4字节为单位拷贝。

代码:

   movb   %al,%cl
   andb   $3,%cl            /* copy remaining bytes */
   rep
   movsb

再拷贝剩余的字节(最多3字节),如果有的话。

代码:

#if defined(I586_CPU) && defined(DEV_NPX)
   ALIGN_TEXT
done_copyin:
#endif
   popl   %edi
   popl   %esi
   xorl   %eax,%eax
   movl   PCPU(CURPCB),%edx
   movl   %eax,PCB_ONFAULT(%edx)
   ret

拷贝完成,恢复寄存器,并清除curpcb->pcb_onfault,返回。
由于%eax用做函数返回值,这就是说,如果成功拷贝就返回0

事情总是有意外发生,如果用户给出的地址段
[from_user, from_user+len]
有问题的话,copyin()将发生异常,进入异常处理函数,

代码:

(sys/i386/i386/trap.c)
---------------------------
/*
 * Exception, fault, and trap interface to the FreeBSD kernel.
 * This common code is called from assembly language IDT gate entry
 * routines that prepare a suitable stack frame, and restore this
 * frame after the exception has been processed.
 */

void
trap(frame)
   struct trapframe frame;
{
   struct thread *td = curthread;
   struct proc *p = td->td_proc;
   ......
   
   type = frame.tf_trapno;
   ......
   
   if(it is user trap){
      ......
      
   }else{ /* kernel trap */
      ......
      switch (type) {
      ......

      case T_PROTFLT:      /* general protection fault */
      case T_STKFLT:      /* stack fault */
         ......
         /* FALL THROUGH */
      case T_SEGNPFLT:   /* segment not present fault */
         ......   
         if (PCPU_GET(curpcb) != NULL &&
             PCPU_GET(curpcb)->pcb_onfault != NULL) {
            frame.tf_eip =
                (int)PCPU_GET(curpcb)->pcb_onfault;
            goto out;
         }
         break;
      ......
      } /* end switch */
      ......
   } /* end else */
   ......
   
out:
   return;
}

对于copyin()产生的错误,"general protection fault"或"stack fault"
或"segment not present fault",都由这段代码处理。
由于在进入copyin()时设置了curpcb->pcb_onfault,这里将异常处理程序退出时
继续运行的eip指针设置为copyin_fault,于是,当异常返回后,程序控制将到达
copyin_fault。

代码:

   ALIGN_TEXT
copyin_fault:
   popl   %edi
   popl   %esi
   movl   PCPU(CURPCB),%edx
   movl   $0,PCB_ONFAULT(%edx)
   movl   $EFAULT,%eax
   ret

在这里,恢复寄存器,清除curpcb->pcb_onfault,返回EFAULT.

注意,此时的核心栈与拷贝成功时的核心栈是相同的,这是因为前述trap()函数
修改%eip后,程序只是将本来应该继续执行拷贝错误语句改为执行copyin_fault,
异常处理程序返回后的核心栈并没有变化。因此,内核将顺着copyin()后的代码执行,
就好象根本没有发生过异常一样。

参考文献:
[1] Sinan "noir" Eren, "Smashing The Kernel Stack For Fun And Profit", phrack60-06
返回页首
阅览会员资料 发送站内信件 浏览发表者的主页
darkcc
老妖


注册时间: 2003-05-19
文章: 3492
来自: 茂名

文章发表于: Mon 2004-05-10 11:31:37    发表主题: 引用并回复

老兄能不能把资料整理成一篇呀.吊我胃口?
返回页首
阅览会员资料 发送站内信件
wheelz
半仙


注册时间: 2002-11-18
文章: 120

文章发表于: Mon 2004-05-10 11:37:52    发表主题: 引用并回复

darkcc 写到:
老兄能不能把资料整理成一篇呀.吊我胃口?


呵呵,我是现写的!写完一篇就发了一篇.
你前面提到的curthread排队问题,我还不明白是怎么回事呢.
我是刚开始看FreeBSD的代码的.
返回页首
阅览会员资料 发送站内信件 浏览发表者的主页
darkcc
老妖


注册时间: 2003-05-19
文章: 3492
来自: 茂名

文章发表于: Mon 2004-05-10 11:45:43    发表主题: 引用并回复

我也不明白我在说什么.

你不用理会我,继续写...
返回页首
阅览会员资料 发送站内信件
从以前的文章开始显示:   
发表新文章   这个论题已经被锁定,您不能发表、回复或者编辑文章。    FreeBSD China -> 中文-桌面-开发-调试 论坛时间为 北京时间
1页/共1

 
转跳到:  
不能发布新主题
不能在这个论坛回复主题
不能在这个论坛编辑自己的文章
不能在这个论坛删除自己的文章
不能在这个论坛发表投票


Powered by phpBB 2023cc © 2003 Opensource Steps; © 2003-2009 The FreeBSD Simplified Chinese Project
Powered by phpBB © 2001, 2005 phpBB Group
Protected by Project Honey Pot and phpBB.cc
silvery-trainer
The FreeBSD China Project 网站: 中文计划网站 社区网站
The FreeBSD China Project 版权所有 (C) 1999 - 2003 网页设计版权 著作权和商标