summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Benoit <benno@cvs.openbsd.org>2015-07-18 16:01:29 +0000
committerSebastian Benoit <benno@cvs.openbsd.org>2015-07-18 16:01:29 +0000
commitbba92d5355ab193337ef6636ee333071be41fcca (patch)
treeb4963a7fb545ce3c6a596f63c1e756c1c11c8432
parent157cfa18ebcb64477576ae720a776a46d8f7a188 (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.c18
-rw-r--r--usr.sbin/relayd/relay_http.c15
-rw-r--r--usr.sbin/relayd/relayd.h4
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