Commit 20ed6956 authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN: MPPP crash fix

prevent kernel oops with MPPP
add some debugging code for critical MPPP situations

(by Karsten Keil)
parent 2ce4fa29
...@@ -128,8 +128,12 @@ static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp) ...@@ -128,8 +128,12 @@ static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
spin_lock_irqsave(&master_lp->netdev->queue_lock, flags); spin_lock_irqsave(&master_lp->netdev->queue_lock, flags);
lp->last->next = lp->next; lp->last->next = lp->next;
lp->next->last = lp->last; lp->next->last = lp->last;
if (master_lp->netdev->queue == lp) if (master_lp->netdev->queue == lp) {
master_lp->netdev->queue = lp->next; master_lp->netdev->queue = lp->next;
if (lp->next == lp) { /* last in queue */
master_lp->netdev->queue = master_lp->netdev->local;
}
}
lp->next = lp->last = lp; /* (re)set own pointers */ lp->next = lp->last = lp; /* (re)set own pointers */
spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags); spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
} }
......
...@@ -108,8 +108,11 @@ isdn_ppp_free(isdn_net_local * lp) ...@@ -108,8 +108,11 @@ isdn_ppp_free(isdn_net_local * lp)
unsigned long flags; unsigned long flags;
struct ippp_struct *is; struct ippp_struct *is;
if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": ppp_slot(%d) out of range\n",
lp->ppp_slot);
return 0; return 0;
}
save_flags(flags); save_flags(flags);
cli(); cli();
...@@ -125,7 +128,12 @@ isdn_ppp_free(isdn_net_local * lp) ...@@ -125,7 +128,12 @@ isdn_ppp_free(isdn_net_local * lp)
lp->netdev->pb->ref_ct--; lp->netdev->pb->ref_ct--;
spin_unlock(&lp->netdev->pb->lock); spin_unlock(&lp->netdev->pb->lock);
#endif /* CONFIG_ISDN_MPP */ #endif /* CONFIG_ISDN_MPP */
if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": ppp_slot(%d) now invalid\n",
lp->ppp_slot);
restore_flags(flags);
return 0;
}
is = ippp_table[lp->ppp_slot]; is = ippp_table[lp->ppp_slot];
if ((is->state & IPPP_CONNECT)) if ((is->state & IPPP_CONNECT))
isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */ isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */
...@@ -221,11 +229,12 @@ isdn_ppp_bind(isdn_net_local * lp) ...@@ -221,11 +229,12 @@ isdn_ppp_bind(isdn_net_local * lp)
void void
isdn_ppp_wakeup_daemon(isdn_net_local * lp) isdn_ppp_wakeup_daemon(isdn_net_local * lp)
{ {
if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": ppp_slot(%d) out of range\n",
lp->ppp_slot);
return; return;
}
ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq); wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
} }
...@@ -239,13 +248,14 @@ isdn_ppp_closewait(int slot) ...@@ -239,13 +248,14 @@ isdn_ppp_closewait(int slot)
{ {
struct ippp_struct *is; struct ippp_struct *is;
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": slot(%d) out of range\n",
slot);
return 0; return 0;
}
is = ippp_table[slot]; is = ippp_table[slot];
if (is->state) if (is->state)
wake_up_interruptible(&is->wq); wake_up_interruptible(&is->wq);
is->state = IPPP_CLOSEWAIT; is->state = IPPP_CLOSEWAIT;
return 1; return 1;
} }
...@@ -339,6 +349,11 @@ isdn_ppp_release(struct inode *ino, struct file *file) ...@@ -339,6 +349,11 @@ isdn_ppp_release(struct inode *ino, struct file *file)
if (is->lp) { /* a lp address says: this link is still up */ if (is->lp) { /* a lp address says: this link is still up */
isdn_net_dev *p = is->lp->netdev; isdn_net_dev *p = is->lp->netdev;
if (!p) {
printk(KERN_ERR __FUNCTION__": no lp->netdev\n");
unlock_kernel();
return 0;
}
is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */ is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */
/* /*
* isdn_net_hangup() calls isdn_ppp_free() * isdn_net_hangup() calls isdn_ppp_free()
...@@ -642,7 +657,7 @@ isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot) ...@@ -642,7 +657,7 @@ isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
struct ippp_struct *is; struct ippp_struct *is;
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
printk(KERN_WARNING "ippp: illegal slot.\n"); printk(KERN_WARNING "ippp: illegal slot(%d).\n", slot);
return 0; return 0;
} }
is = ippp_table[slot]; is = ippp_table[slot];
...@@ -964,7 +979,8 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf ...@@ -964,7 +979,8 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf
slot = lp->ppp_slot; slot = lp->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) { if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot %d\n", lp->ppp_slot); printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot(%d)\n",
lp->ppp_slot);
kfree_skb(skb); kfree_skb(skb);
return; return;
} }
...@@ -1017,7 +1033,8 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff ...@@ -1017,7 +1033,8 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
slot = lp->ppp_slot; slot = lp->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) { if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot %d\n", lp->ppp_slot); printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot(%d)\n",
lp->ppp_slot);
goto drop_packet; goto drop_packet;
} }
is = ippp_table[slot]; is = ippp_table[slot];
...@@ -1025,7 +1042,8 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff ...@@ -1025,7 +1042,8 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
if (lp->master) { // FIXME? if (lp->master) { // FIXME?
slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot; slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) { if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot %d\n", lp->ppp_slot); printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot(%d)\n",
lp->ppp_slot);
goto drop_packet; goto drop_packet;
} }
} }
...@@ -1059,6 +1077,11 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff ...@@ -1059,6 +1077,11 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
case PPP_VJC_UNCOMP: case PPP_VJC_UNCOMP:
if (is->debug & 0x20) if (is->debug & 0x20)
printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n"); printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
if (net_dev->local->ppp_slot < 0) {
printk(KERN_ERR __FUNCTION__": net_dev->local->ppp_slot(%d) out of range\n",
net_dev->local->ppp_slot);
goto drop_packet;
}
if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) { if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n"); printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
goto drop_packet; goto drop_packet;
...@@ -1080,6 +1103,11 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff ...@@ -1080,6 +1103,11 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
} }
skb_put(skb, skb_old->len + 128); skb_put(skb, skb_old->len + 128);
memcpy(skb->data, skb_old->data, skb_old->len); memcpy(skb->data, skb_old->data, skb_old->len);
if (net_dev->local->ppp_slot < 0) {
printk(KERN_ERR __FUNCTION__": net_dev->local->ppp_slot(%d) out of range\n",
net_dev->local->ppp_slot);
goto drop_packet;
}
pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp, pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp,
skb->data, skb_old->len); skb->data, skb_old->len);
kfree_skb(skb_old); kfree_skb(skb_old);
...@@ -1168,7 +1196,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev) ...@@ -1168,7 +1196,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
slot = mlp->ppp_slot; slot = mlp->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) { if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", mlp->ppp_slot); printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
mlp->ppp_slot);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
...@@ -1203,7 +1232,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev) ...@@ -1203,7 +1232,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
slot = lp->ppp_slot; slot = lp->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) { if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", lp->ppp_slot); printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
lp->ppp_slot);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
...@@ -1408,8 +1438,15 @@ static ippp_bundle * isdn_ppp_mp_bundle_alloc(void) ...@@ -1408,8 +1438,15 @@ static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ) static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
{ {
struct ippp_struct * is = ippp_table[lp->ppp_slot]; struct ippp_struct * is;
if (lp->ppp_slot < 0) {
printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d) out of range\n",
lp->ppp_slot);
return(-EINVAL);
}
is = ippp_table[lp->ppp_slot];
if (add_to) { if (add_to) {
if( lp->netdev->pb ) if( lp->netdev->pb )
lp->netdev->pb->ref_ct--; lp->netdev->pb->ref_ct--;
...@@ -1455,7 +1492,8 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, ...@@ -1455,7 +1492,8 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
stats = &mp->stats; stats = &mp->stats;
slot = lp->ppp_slot; slot = lp->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) { if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_mp_receive: lp->ppp_slot %d\n", lp->ppp_slot); printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d)\n",
lp->ppp_slot);
stats->frame_drops++; stats->frame_drops++;
dev_kfree_skb(skb); dev_kfree_skb(skb);
spin_unlock_irqrestore(&mp->lock, flags); spin_unlock_irqrestore(&mp->lock, flags);
...@@ -1491,7 +1529,8 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, ...@@ -1491,7 +1529,8 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
for (lpq = net_dev->queue;;) { for (lpq = net_dev->queue;;) {
slot = lpq->ppp_slot; slot = lpq->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) { if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_mp_receive: lpq->ppp_slot %d\n", lpq->ppp_slot); printk(KERN_ERR __FUNCTION__": lpq->ppp_slot(%d)\n",
lpq->ppp_slot);
} else { } else {
u32 lls = ippp_table[slot]->last_link_seqno; u32 lls = ippp_table[slot]->last_link_seqno;
if (MP_LT(lls, minseq)) if (MP_LT(lls, minseq))
...@@ -1723,9 +1762,14 @@ void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, ...@@ -1723,9 +1762,14 @@ void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
struct sk_buff * skb; struct sk_buff * skb;
unsigned int tot_len; unsigned int tot_len;
if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d) out of range\n",
lp->ppp_slot);
return;
}
if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) { if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
if( ippp_table[lp->ppp_slot]->debug & 0x40 ) if( ippp_table[lp->ppp_slot]->debug & 0x40 )
printk(KERN_DEBUG"isdn_mppp: reassembly: frame %d, " printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, "
"len %d\n", MP_SEQ(from), from->len ); "len %d\n", MP_SEQ(from), from->len );
skb = from; skb = from;
skb_pull(skb, MP_HEADER_LEN); skb_pull(skb, MP_HEADER_LEN);
...@@ -2487,18 +2531,31 @@ static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, ...@@ -2487,18 +2531,31 @@ static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
struct sk_buff *skb,int proto) struct sk_buff *skb,int proto)
{ {
struct ippp_struct *is = ippp_table[lp->ppp_slot]; struct ippp_struct *is;
struct ippp_struct *mis; struct ippp_struct *mis;
int len; int len;
struct isdn_ppp_resetparams rsparm; struct isdn_ppp_resetparams rsparm;
unsigned char rsdata[IPPP_RESET_MAXDATABYTES]; unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
printk(KERN_DEBUG "Received CCP frame from peer\n"); printk(KERN_DEBUG "Received CCP frame from peer slot(%d)\n",
lp->ppp_slot);
if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d) out of range\n",
lp->ppp_slot);
return;
}
is = ippp_table[lp->ppp_slot];
isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot); isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
if(lp->master) if(lp->master) {
mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]; int slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
else if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": slot(%d) out of range\n",
slot);
return;
}
mis = ippp_table[slot];
} else
mis = is; mis = is;
switch(skb->data[0]) { switch(skb->data[0]) {
...@@ -2650,13 +2707,18 @@ static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, ...@@ -2650,13 +2707,18 @@ static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb) static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
{ {
struct ippp_struct *mis,*is = ippp_table[lp->ppp_slot]; struct ippp_struct *mis,*is;
int proto; int proto, slot = lp->ppp_slot;
unsigned char *data; unsigned char *data;
if(!skb || skb->len < 3) if(!skb || skb->len < 3)
return; return;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d) out of range\n",
slot);
return;
}
is = ippp_table[slot];
/* Daemon may send with or without address and control field comp */ /* Daemon may send with or without address and control field comp */
data = skb->data; data = skb->data;
if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) { if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) {
...@@ -2672,12 +2734,17 @@ static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct ...@@ -2672,12 +2734,17 @@ static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct
printk(KERN_DEBUG "Received CCP frame from daemon:\n"); printk(KERN_DEBUG "Received CCP frame from daemon:\n");
isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot); isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
if(lp->master) if (lp->master) {
mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]; slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
else if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR __FUNCTION__": slot(%d) out of range\n",
slot);
return;
}
mis = ippp_table[slot];
} else
mis = is; mis = is;
if (mis != is)
if(mis != is)
printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n"); printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
switch(data[2]) { switch(data[2]) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment