Use-after-free vulnerability (CVE-2016-7835) #1144

Closed
kazuho opened this Issue Dec 20, 2016 · 9 comments

Projects

None yet

5 participants

@kazuho
Member
kazuho commented Dec 20, 2016 edited

A use-after-free vulnerability exists in H2O upto and including version 2.0.4 / 2.1.0-beta3 that can be used to by an remote attacker to mount DoS attacks and / or information theft[1].

Users using H2O version 2.0.x for serving plaintext HTTP (i.e. non-HTTPS) requests are advised to upgrade immediately to version 2.0.5. Users of 2.1-beta series are advised to upgrade to 2.1.0-beta4 regardless of their configuration.

1: possible leakage includes the TLS private keys for deployments that have explicitly turned off neverbleed. Default is on meaning that the private keys are protected within a separate process.

@kazuho kazuho changed the title from _ to Use-after-free vulnerability (CVE-2016-7835) Dec 21, 2016
@tatsushid
tatsushid commented Dec 21, 2016 edited

I've updated following binary package builder repositories too

It is highly recommended that anyone who uses the above upgrades them.

Note: Deb package builder works on my local but has something problem on Travis building so prebuilt packages have not been released. It will be fixed and released later.

It has been fixed.

@csmk
csmk commented Dec 21, 2016 edited

The ebuild for Gentoo has been updated: https://github.com/csmk/frabjous/tree/master/www-servers/h2o

@dch
dch commented Dec 26, 2016

I'm having several issues getting 2.0.5 itself, to build natively on FreeBSD inside the ports tree framework - normally for a security fix I'd expect to need minimal changes.

Is 1b2b6d7 the actual fix? I would prefer to cherry-pick this back to 2.0.4, so I can ship this, and work on the mruby/mgem changes in the new year.

@dch
dch commented Dec 26, 2016

BTW this is the current diff using that patch, in case that's useful.

commit 3eab454238030eb4a7622341c6a9ef1071a0b9a7
Author: Dave Cottlehuber <dch@skunkwerks.at>
Date:   Mon Dec 26 14:45:27 2016 +0000

    www/h2o: patch for CVE-2016-7835
    
    - include https://github.com/h2o/h2o/commit/1b2b6d7.patch
    - 2.0.5 has too many changes to go into a backported security fix

diff --git a/www/h2o/Makefile b/www/h2o/Makefile
index a4414a7..90155fb 100644
--- a/www/h2o/Makefile
+++ b/www/h2o/Makefile
@@ -4,7 +4,7 @@
 PORTNAME=	h2o
 PORTVERSION=	2.0.4
 DISTVERSIONPREFIX=	v
-PORTREVISION=	1
+PORTREVISION=	2
 CATEGORIES=	www
 
 MAINTAINER=	dch@skunkwerks.at
diff --git a/www/h2o/distinfo b/www/h2o/distinfo
index 272d628..f0e5174 100644
--- a/www/h2o/distinfo
+++ b/www/h2o/distinfo
@@ -1,3 +1,3 @@
-TIMESTAMP = 1473753131
+TIMESTAMP = 1482767274
 SHA256 (h2o-h2o-v2.0.4_GH0.tar.gz) = c0efa18f0ffb0f68ee4b60a6ed1feb54c770458c59e48baa2d9d0906ef9c68c0
 SIZE (h2o-h2o-v2.0.4_GH0.tar.gz) = 15015392
diff --git a/www/h2o/files/patch-lib_core_request.c b/www/h2o/files/patch-lib_core_request.c
new file mode 100644
index 0000000..789c2cd
--- /dev/null
+++ b/www/h2o/files/patch-lib_core_request.c
@@ -0,0 +1,145 @@
+--- lib/core/request.c.orig	2016-09-13 06:57:03 UTC
++++ lib/core/request.c
+@@ -31,21 +31,24 @@
+ 
+ #define INITIAL_INBUFSZ 8192
+ 
+-struct st_delegate_request_deferred_t {
++struct st_deferred_request_action_t {
++    h2o_timeout_entry_t timeout;
+     h2o_req_t *req;
++};
++
++struct st_delegate_request_deferred_t {
++    struct st_deferred_request_action_t super;
+     h2o_handler_t *current_handler;
+-    h2o_timeout_entry_t _timeout;
+ };
+ 
+ struct st_reprocess_request_deferred_t {
+-    h2o_req_t *req;
++    struct st_deferred_request_action_t super;
+     h2o_iovec_t method;
+     const h2o_url_scheme_t *scheme;
+     h2o_iovec_t authority;
+     h2o_iovec_t path;
+     h2o_req_overrides_t *overrides;
+     int is_delegated;
+-    h2o_timeout_entry_t _timeout;
+ };
+ 
+ struct st_send_error_deferred_t {
+@@ -57,6 +60,21 @@ struct st_send_error_deferred_t {
+     h2o_timeout_entry_t _timeout;
+ };
+ 
++static void on_deferred_action_dispose(void *_action)
++{
++    struct st_deferred_request_action_t *action = _action;
++    if (h2o_timeout_is_linked(&action->timeout))
++        h2o_timeout_unlink(&action->timeout);
++}
++
++static struct st_deferred_request_action_t *create_deferred_action(h2o_req_t *req, size_t sz, h2o_timeout_cb cb)
++{
++    struct st_deferred_request_action_t *action = h2o_mem_alloc_shared(&req->pool, sz, on_deferred_action_dispose);
++    *action = (struct st_deferred_request_action_t){{0, cb}, req};
++    h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &action->timeout);
++    return action;
++}
++
+ static h2o_hostconf_t *find_hostconf(h2o_hostconf_t **hostconfs, h2o_iovec_t authority, uint16_t default_port)
+ {
+     h2o_iovec_t hostname;
+@@ -205,6 +223,7 @@ void h2o_init_request(h2o_req_t *req, h2
+     req->preferred_chunk_size = SIZE_MAX;
+ 
+     if (src != NULL) {
++        size_t i;
+ #define COPY(buf)                                                                                                                  \
+     do {                                                                                                                           \
+         req->buf.base = h2o_mem_alloc_pool(&req->pool, src->buf.len);                                                              \
+@@ -216,9 +235,6 @@ void h2o_init_request(h2o_req_t *req, h2
+         COPY(input.path);
+         req->input.scheme = src->input.scheme;
+         req->version = src->version;
+-        h2o_vector_reserve(&req->pool, &req->headers, src->headers.size);
+-        memcpy(req->headers.entries, src->headers.entries, sizeof(req->headers.entries[0]) * src->headers.size);
+-        req->headers.size = src->headers.size;
+         req->entity = src->entity;
+         req->http1_is_persistent = src->http1_is_persistent;
+         req->timestamps = src->timestamps;
+@@ -229,8 +245,19 @@ void h2o_init_request(h2o_req_t *req, h2
+             req->upgrade.len = 0;
+         }
+ #undef COPY
++        h2o_vector_reserve(&req->pool, &req->headers, src->headers.size);
++        req->headers.size = src->headers.size;
++        for (i = 0; i != src->headers.size; ++i) {
++            h2o_header_t *dst_header = req->headers.entries + i, *src_header = src->headers.entries + i;
++            if (h2o_iovec_is_token(src_header->name)) {
++                dst_header->name = src_header->name;
++            } else {
++                dst_header->name = h2o_mem_alloc_pool(&req->pool, sizeof(*dst_header->name));
++                *dst_header->name = h2o_strdup(&req->pool, src_header->name->base, src_header->name->len);
++            }
++            dst_header->value = h2o_strdup(&req->pool, src_header->value.base, src_header->value.len);
++        }
+         if (src->env.size != 0) {
+-            size_t i;
+             h2o_vector_reserve(&req->pool, &req->env, src->env.size);
+             req->env.size = src->env.size;
+             for (i = 0; i != req->env.size; ++i)
+@@ -276,16 +303,16 @@ void h2o_delegate_request(h2o_req_t *req
+ 
+ static void on_delegate_request_cb(h2o_timeout_entry_t *entry)
+ {
+-    struct st_delegate_request_deferred_t *args = H2O_STRUCT_FROM_MEMBER(struct st_delegate_request_deferred_t, _timeout, entry);
+-    h2o_delegate_request(args->req, args->current_handler);
++    struct st_delegate_request_deferred_t *args =
++        H2O_STRUCT_FROM_MEMBER(struct st_delegate_request_deferred_t, super.timeout, entry);
++    h2o_delegate_request(args->super.req, args->current_handler);
+ }
+ 
+ void h2o_delegate_request_deferred(h2o_req_t *req, h2o_handler_t *current_handler)
+ {
+-    struct st_delegate_request_deferred_t *args = h2o_mem_alloc_pool(&req->pool, sizeof(*args));
+-    *args = (struct st_delegate_request_deferred_t){req, current_handler};
+-    args->_timeout.cb = on_delegate_request_cb;
+-    h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &args->_timeout);
++    struct st_delegate_request_deferred_t *args =
++        (struct st_delegate_request_deferred_t *)create_deferred_action(req, sizeof(*args), on_delegate_request_cb);
++    args->current_handler = current_handler;
+ }
+ 
+ void h2o_reprocess_request(h2o_req_t *req, h2o_iovec_t method, const h2o_url_scheme_t *scheme, h2o_iovec_t authority,
+@@ -335,17 +362,23 @@ void h2o_reprocess_request(h2o_req_t *re
+ 
+ static void on_reprocess_request_cb(h2o_timeout_entry_t *entry)
+ {
+-    struct st_reprocess_request_deferred_t *args = H2O_STRUCT_FROM_MEMBER(struct st_reprocess_request_deferred_t, _timeout, entry);
+-    h2o_reprocess_request(args->req, args->method, args->scheme, args->authority, args->path, args->overrides, args->is_delegated);
++    struct st_reprocess_request_deferred_t *args =
++        H2O_STRUCT_FROM_MEMBER(struct st_reprocess_request_deferred_t, super.timeout, entry);
++    h2o_reprocess_request(args->super.req, args->method, args->scheme, args->authority, args->path, args->overrides,
++                          args->is_delegated);
+ }
+ 
+ void h2o_reprocess_request_deferred(h2o_req_t *req, h2o_iovec_t method, const h2o_url_scheme_t *scheme, h2o_iovec_t authority,
+                                     h2o_iovec_t path, h2o_req_overrides_t *overrides, int is_delegated)
+ {
+-    struct st_reprocess_request_deferred_t *args = h2o_mem_alloc_pool(&req->pool, sizeof(*args));
+-    *args = (struct st_reprocess_request_deferred_t){req, method, scheme, authority, path, overrides, is_delegated};
+-    args->_timeout.cb = on_reprocess_request_cb;
+-    h2o_timeout_link(req->conn->ctx->loop, &req->conn->ctx->zero_timeout, &args->_timeout);
++    struct st_reprocess_request_deferred_t *args =
++        (struct st_reprocess_request_deferred_t *)create_deferred_action(req, sizeof(*args), on_reprocess_request_cb);
++    args->method = method;
++    args->scheme = scheme;
++    args->authority = authority;
++    args->path = path;
++    args->overrides = overrides;
++    args->is_delegated = is_delegated;
+ }
+ 
+ void h2o_start_response(h2o_req_t *req, h2o_generator_t *generator)
diff --git a/www/h2o/files/patch-lib_http2_connection.c b/www/h2o/files/patch-lib_http2_connection.c
new file mode 100644
index 0000000..1c8cd28
--- /dev/null
+++ b/www/h2o/files/patch-lib_http2_connection.c
@@ -0,0 +1,10 @@
+--- lib/http2/connection.c.orig	2016-09-13 06:57:03 UTC
++++ lib/http2/connection.c
+@@ -1329,6 +1329,7 @@ int h2o_http2_handle_upgrade(h2o_req_t *
+ 
+     return 0;
+ Error:
++    h2o_linklist_unlink(&http2conn->_conns);
+     free(http2conn);
+     return -1;
+ }
@lkwg82
Member
lkwg82 commented Jan 8, 2017

is this resolved already?

@dch
dch commented Jan 8, 2017

@lkwg82 yes: it's fixed in 2.0.5 onwards in h2o itself, and the specific patch was backported to FreeBSD to go into the quarterly "stable" ports branch as well as ports trunk. I have a few tweaks for 2.0.6 to make but hope to get it out in the next week or so, dependent on committers committing.

As usual on FreeBSD vuxml entries are up to date so you can confirm your own systems are safe easily.

@lkwg82
Member
lkwg82 commented Jan 9, 2017

yes: it's fixed in 2.0.5 onwards in h2o itself ...

So why is this still open? Isn't this issue only about h2o itself?

Keeping this open, leaves the impression this not fixed.

@kazuho kazuho closed this Jan 9, 2017
@kazuho
Member
kazuho commented Jan 9, 2017

@lkwg82 Good point! closed now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment