- 论坛徽章:
- 0
|
原帖由 llzqq 于 2006-2-20 09:09 发表
如果果真硬件没问题,那就是系统配置的问题,再不然就只能是所说的“人品问题”了。因为别人的都没问题。
我在网上找到一篇类似问题的文章。不过我用的是3.8,而且外网连接用的是lan。
------------------------
前段时间用新装的OpenBSD(3.7 Stable)做家里的NAT,使用Kernel Mode 的 PPPoE连接到电信的ADSL,结果系统在大流量的时候内核会crash,经过几天的调试,终于找到了Bug 所在,现在问题已经解决,并且工作非常良好。
这是crash时的信息:
uvm_fault(0xd0291580, 0xdee80000, 0, 1) -> e
kernel: page fault trap, code = 0
stopped at bcopy+0x1a: repe movsl (%esi), %es%edi)
ddb>
经过长时间的调试找到问题是出在/sys/net/if_spppsubr.c中,相关的代码如下:
sppp_input(struct ifnet *ifp,struct mbuf *m) 中的
struct ppp_header *h, ht;
if (sp->pp_flags & PP_NOFRAMING) {
memcpy(&ht.protocol, mtod(m, void *), 2);
m_adj(m, 2);
ht.control = PPP_UI;
ht.address = PPP_ALLSTATIONS;
h = &ht;
} else {
/* Get PPP header. */
h = mtod (m, struct ppp_header*);
m_adj (m, PPP_HEADER_LEN);
}
.....
switch (ntohs (h->protocol)) {
default:
if (sp->state[IDX_LCP] == STATE_OPENED)
sppp_cp_send (sp, PPP_LCP, PROTO_REJ,
++sp->pp_seq, m->m_pkthdr.len + 2,
&h->protocol);
在 sppp_cp_send 函数中有一个 bcopy 调用:
if (len)
bcopy (data, lh+1, len);
当 (sp->pp_flags & PP_NOFRAMING) 为真时, *h 将指向 ht (即结构
ppp_header), 接下来当调用 sppp_cp_send(sp, PPP_LCP, PROTO_REJ,
++sp->pp_seq, m->m_pkthdr.len + 2, &h->protocol) 时会产生一个错误的bcopy调用,即:
bcopy(&h->protocol, lh+1, m->m_pkthdr.len + 2)
此时 &h->protocol 的有效内容只有 2 个字节(因为struct ppp_header只分配4个字节),而m->m_pkthdr.len + 2 将永远 >= 2,所以此时将会出现非法的内存访问,因而就可能造成kernel crash。
修复这个bug只要将m->m_pkthdr.len + 2,改为 2 便可,下面是这一简单的修复方法:
将:
if (sp->state[IDX_LCP] == STATE_OPENED)
sppp_cp_send (sp, PPP_LCP, PROTO_REJ,
++sp->pp_seq, m->m_pkthdr.len + 2,
&h->protocol);
改为:
if (sp->state[IDX_LCP] == STATE_OPENED)
{
if (sp->pp_flags & PP_NOFRAMING)
sppp_cp_send (sp, PPP_LCP, PROTO_REJ,
++sp->pp_seq, 2,
&h->protocol);
else
sppp_cp_send (sp, PPP_LCP, PROTO_REJ,
++sp->pp_seq, m->m_pkthdr.len + 2,
&h->protocol);
}
另外,我看了一下netbsd的内核级PPPoE实现,好像也存在同样的问题,代码同样在 /sys/net/if_spppsubr.c (Revision: 1.85):
u_int16_t prot = htons(protocol);
sppp_cp_send(sp, PPP_LCP, PROTO_REJ,
++sp->pp_seq[IDX_LCP], m->m_pkthdr.len + 2,
&prot);
不过由于我这边没有测试环境,所以无法证实这个问题,希望有测试环境的朋友帮忙测试一下。
我将这个问题提交给了openbsd的开发者,Can Erkin Acar 提供了一个src patch,我在机器上经过长时间的测试,在使用这个patch后没有再出现crash的情况,Can Erkin Acar已经将其提交到openbsd-current中(if_spppsubr.c revision 1.36) ^_^,后附Can Erkin Acar提供的Patch,希望对你有用:
Index: if_spppsubr.c
===================================================================
RCS file: /cvs/src/sys/net/if_spppsubr.c,v
retrieving revision 1.35
diff -u -p -u -p -r1.35 if_spppsubr.c
--- if_spppsubr.c 3 Aug 2005 21:50:21 -0000 1.35
+++ if_spppsubr.c 9 Aug 2005 06:09:20 -0000
@@ -458,6 +458,7 @@ sppp_input(struct ifnet *ifp, struct mbu
struct ifqueue *inq = 0;
struct sppp *sp = (struct sppp *)ifp;
struct timeval tv;
+ void *prej;
int debug = ifp->if_flags & IFF_DEBUG;
int s;
@@ -483,7 +484,8 @@ sppp_input(struct ifnet *ifp, struct mbu
}
if (sp->pp_flags & PP_NOFRAMING) {
- memcpy(&ht.protocol, mtod(m, void *), 2);
+ prej = mtod(m, void *);
+ memcpy(&ht.protocol, prej, sizeof(ht.protocol));
m_adj(m, 2);
ht.control = PPP_UI;
ht.address = PPP_ALLSTATIONS;
@@ -491,6 +493,7 @@ sppp_input(struct ifnet *ifp, struct mbu
} else {
/* Get PPP header. */
h = mtod (m, struct ppp_header*);
+ prej = &h->protocol;
m_adj (m, PPP_HEADER_LEN);
}
@@ -511,8 +514,7 @@ sppp_input(struct ifnet *ifp, struct mbu
default:
if (sp->state[IDX_LCP] == STATE_OPENED)
sppp_cp_send (sp, PPP_LCP, PROTO_REJ,
- ++sp->pp_seq, m->m_pkthdr.len + 2,
- &h->protocol);
+ ++sp->pp_seq, m->m_pkthdr.len + 2, prej);
---------------------------------- |
|