diff options
author | maxim <maxim@FreeBSD.org> | 2003-03-27 14:56:36 +0000 |
---|---|---|
committer | maxim <maxim@FreeBSD.org> | 2003-03-27 14:56:36 +0000 |
commit | 291fa269633b0a81036a3fc3ca80487f5835a320 (patch) | |
tree | 17e99cebc765c447b120002b2ebccc2bc7a9f4f6 /sys/netinet/ip_dummynet.c | |
parent | 2d56ed957aaef6ee5d66bcc7c86d7832b4658de5 (diff) | |
download | FreeBSD-src-291fa269633b0a81036a3fc3ca80487f5835a320.zip FreeBSD-src-291fa269633b0a81036a3fc3ca80487f5835a320.tar.gz |
o Protect set_fs_param() by splimp(9).
Quote from kern/37573:
There is an obvious race in netinet/ip_dummynet.c:config_pipe().
Interrupts are not blocked when changing the params of an
existing pipe. The specific crash observed:
... -> config_pipe -> set_fs_parms -> config_red
malloc a new w_q_lookup table but take an interrupt before
intializing it, interrupt handler does:
... -> dummynet_io -> red_drops
red_drops dereferences the uninitialized (zeroed) w_q_lookup
table.
o Flush accumulated credits for idle pipes.
o Flush accumulated credits when change pipe characteristics.
o Change dn_flow_queue.numbytes type to unsigned long.
Overlapping dn_flow_queue->numbytes in ready_event() leads to
numbytes becomes negative and SET_TICKS() macro returns a very
big value. heap_insert() overlaps dn_key again and inserts a
queue to a ready heap with a sched_time points to the past.
That leads to an "infinity" loop.
PR: kern/33234, kern/37573, misc/42459, kern/43133,
kern/44045, kern/48099
Submitted by: Mike Hibler <mike@cs.utah.edu> (kern/37573)
MFC after: 6 weeks
Diffstat (limited to 'sys/netinet/ip_dummynet.c')
-rw-r--r-- | sys/netinet/ip_dummynet.c | 24 |
1 files changed, 17 insertions, 7 deletions
diff --git a/sys/netinet/ip_dummynet.c b/sys/netinet/ip_dummynet.c index 82b6f7b..63643fd 100644 --- a/sys/netinet/ip_dummynet.c +++ b/sys/netinet/ip_dummynet.c @@ -565,8 +565,10 @@ ready_event(struct dn_flow_queue *q) /* XXX should check errors on heap_insert, and drain the whole * queue on error hoping next time we are luckier. */ - } else /* RED needs to know when the queue becomes empty */ + } else { /* RED needs to know when the queue becomes empty */ q->q_time = curr_time; + q->numbytes = 0; + } /* * If the delay line was empty call transmit_event(p) now. * Otherwise, the scheduler will take care of it. @@ -1509,8 +1511,9 @@ set_fs_parms(struct dn_flow_set *x, struct dn_flow_set *src) static int config_pipe(struct dn_pipe *p) { - int s ; + int i, s; struct dn_flow_set *pfs = &(p->fs); + struct dn_flow_queue *q; /* * The config program passes parameters as follows: @@ -1542,9 +1545,17 @@ config_pipe(struct dn_pipe *p) */ x->idle_heap.size = x->idle_heap.elements = 0 ; x->idle_heap.offset=OFFSET_OF(struct dn_flow_queue, heap_pos); - } else + } else { x = b; + s = splimp(); + /* Flush accumulated credit for all queues */ + for (i = 0; i <= x->fs.rq_size; i++) + for (q = x->fs.rq[i]; q; q = q->next) + q->numbytes = 0; + splx(s); + } + s = splimp(); x->bandwidth = p->bandwidth ; x->numbytes = 0; /* just in case... */ bcopy(p->if_name, x->if_name, sizeof(p->if_name) ); @@ -1559,14 +1570,13 @@ config_pipe(struct dn_pipe *p) free(x, M_DUMMYNET); return s ; } - s = splimp() ; x->next = b ; if (a == NULL) all_pipes = x ; else a->next = x ; - splx(s); } + splx(s); } else { /* config queue */ struct dn_flow_set *x, *a, *b ; @@ -1595,6 +1605,7 @@ config_pipe(struct dn_pipe *p) return EINVAL ; x = b; } + s = splimp(); set_fs_parms(x, pfs); if ( x->rq == NULL ) { /* a new flow_set */ @@ -1603,14 +1614,13 @@ config_pipe(struct dn_pipe *p) free(x, M_DUMMYNET); return s ; } - s = splimp() ; x->next = b; if (a == NULL) all_flow_sets = x; else a->next = x; - splx(s); } + splx(s); } return 0 ; } |