diff options
author | Sebastian Benoit <benno@cvs.openbsd.org> | 2015-07-18 16:01:29 +0000 |
---|---|---|
committer | Sebastian Benoit <benno@cvs.openbsd.org> | 2015-07-18 16:01:29 +0000 |
commit | bba92d5355ab193337ef6636ee333071be41fcca (patch) | |
tree | b4963a7fb545ce3c6a596f63c1e756c1c11c8432 | |
parent | 157cfa18ebcb64477576ae720a776a46d8f7a188 (diff) |
Fix unbounded buffer growth. In the case of a slow client reading large files,
we would consume large ammounts of memory.
Found by Matthew Martin <matt DOT a DOT martin AT gmail DOT com> in
httpd, fixed in httpd by florian@
feedback from florian, reyk and bluhm, ok bluhm, reyk
-rw-r--r-- | usr.sbin/relayd/relay.c | 18 | ||||
-rw-r--r-- | usr.sbin/relayd/relay_http.c | 15 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 4 |
3 files changed, 34 insertions, 3 deletions
diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index 85584df9727..107afb52d29 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.196 2015/06/12 14:40:55 reyk Exp $ */ +/* $OpenBSD: relay.c,v 1.197 2015/07/18 16:01:28 benno Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -647,6 +647,7 @@ relay_connected(int fd, short sig, void *arg) { struct rsession *con = arg; struct relay *rlay = con->se_relay; + struct protocol *proto = rlay->rl_proto; evbuffercb outrd = relay_read; evbuffercb outwr = relay_write; struct bufferevent *bev; @@ -713,7 +714,11 @@ relay_connected(int fd, short sig, void *arg) bufferevent_settimeout(bev, rlay->rl_conf.timeout.tv_sec, rlay->rl_conf.timeout.tv_sec); + bufferevent_setwatermark(bev, EV_WRITE, + RELAY_MIN_PREFETCHED * proto->tcpbufsiz, 0); bufferevent_enable(bev, EV_READ|EV_WRITE); + if (con->se_in.bev) + bufferevent_enable(con->se_in.bev, EV_READ); if (relay_splice(&con->se_out) == -1) relay_close(con, strerror(errno)); @@ -723,6 +728,7 @@ void relay_input(struct rsession *con) { struct relay *rlay = con->se_relay; + struct protocol *proto = rlay->rl_proto; evbuffercb inrd = relay_read; evbuffercb inwr = relay_write; @@ -759,6 +765,8 @@ relay_input(struct rsession *con) bufferevent_settimeout(con->se_in.bev, rlay->rl_conf.timeout.tv_sec, rlay->rl_conf.timeout.tv_sec); + bufferevent_setwatermark(con->se_in.bev, EV_WRITE, + RELAY_MIN_PREFETCHED * proto->tcpbufsiz, 0); bufferevent_enable(con->se_in.bev, EV_READ|EV_WRITE); if (relay_splice(&con->se_in) == -1) @@ -777,6 +785,9 @@ relay_write(struct bufferevent *bev, void *arg) goto done; if (relay_splice(cre->dst) == -1) goto fail; + if (cre->dst->bev) + bufferevent_enable(cre->dst->bev, EV_READ); + return; done: relay_close(con, "last write (done)"); @@ -808,6 +819,7 @@ relay_read(struct bufferevent *bev, void *arg) { struct ctl_relay_event *cre = arg; struct rsession *con = cre->con; + struct protocol *proto = con->se_relay->rl_proto; struct evbuffer *src = EVBUFFER_INPUT(bev); getmonotime(&con->se_tv_last); @@ -821,6 +833,10 @@ relay_read(struct bufferevent *bev, void *arg) goto done; if (cre->dst->bev) bufferevent_enable(cre->dst->bev, EV_READ); + if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) > + (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz) + bufferevent_disable(bev, EV_READ); + return; done: relay_close(con, "last read (done)"); diff --git a/usr.sbin/relayd/relay_http.c b/usr.sbin/relayd/relay_http.c index 200700d2dda..351e89181ec 100644 --- a/usr.sbin/relayd/relay_http.c +++ b/usr.sbin/relayd/relay_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay_http.c,v 1.50 2015/06/12 14:40:55 reyk Exp $ */ +/* $OpenBSD: relay_http.c,v 1.51 2015/07/18 16:01:28 benno Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -461,6 +461,8 @@ relay_read_httpcontent(struct bufferevent *bev, void *arg) { struct ctl_relay_event *cre = arg; struct rsession *con = cre->con; + struct protocol *proto = con->se_relay->rl_proto; + struct evbuffer *src = EVBUFFER_INPUT(bev); size_t size; @@ -498,6 +500,11 @@ relay_read_httpcontent(struct bufferevent *bev, void *arg) if (con->se_done) goto done; bufferevent_enable(bev, EV_READ); + + if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) > + (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz) + bufferevent_disable(cre->bev, EV_READ); + if (bev->readcb != relay_read_httpcontent) bev->readcb(bev, arg); /* The callback readcb() might have freed the session. */ @@ -514,6 +521,7 @@ relay_read_httpchunks(struct bufferevent *bev, void *arg) { struct ctl_relay_event *cre = arg; struct rsession *con = cre->con; + struct protocol *proto = con->se_relay->rl_proto; struct evbuffer *src = EVBUFFER_INPUT(bev); char *line; long long llval; @@ -616,6 +624,11 @@ relay_read_httpchunks(struct bufferevent *bev, void *arg) if (con->se_done) goto done; bufferevent_enable(bev, EV_READ); + + if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) > + (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz) + bufferevent_disable(cre->bev, EV_READ); + if (EVBUFFER_LENGTH(src)) bev->readcb(bev, arg); /* The callback readcb() might have freed the session. */ diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index 8230d2a4b67..37a40822a02 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.212 2015/06/12 14:40:55 reyk Exp $ */ +/* $OpenBSD: relayd.h,v 1.213 2015/07/18 16:01:28 benno Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -60,6 +60,8 @@ #define MAX_NAME_SIZE 64 #define SRV_MAX_VIRTS 16 #define TLS_NAME_SIZE 512 +#define RELAY_MAX_PREFETCH 256 +#define RELAY_MIN_PREFETCHED 32 #define FD_RESERVE 5 |