Merge pull request #315 from h2o/kazuho/stack-usage

reduce stack usage
diff --git a/lib/picotls.c b/lib/picotls.c
index 774b879..e6bd528 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -1236,7 +1236,7 @@
     return setup_traffic_protection(tls, is_enc, NULL, 2, 1);
 }
 
-static inline void log_client_random(ptls_t *tls)
+static void log_client_random(ptls_t *tls)
 {
     PTLS_PROBE(CLIENT_RANDOM, tls,
                ptls_hexdump(alloca(sizeof(tls->client_random) * 2 + 1), tls->client_random, sizeof(tls->client_random)));
@@ -2385,21 +2385,24 @@
     return ret;
 }
 
-static int handle_unknown_extension(ptls_t *tls, ptls_handshake_properties_t *properties, uint16_t type, const uint8_t *src,
-                                    const uint8_t *const end, ptls_raw_extension_t *slots)
+static int should_collect_unknown_extension(ptls_t *tls, ptls_handshake_properties_t *properties, uint16_t type)
 {
-    if (properties != NULL && properties->collect_extension != NULL && properties->collect_extension(tls, properties, type)) {
-        size_t i;
-        for (i = 0; slots[i].type != UINT16_MAX; ++i) {
-            assert(i < MAX_UNKNOWN_EXTENSIONS);
-            if (slots[i].type == type)
-                return PTLS_ALERT_ILLEGAL_PARAMETER;
-        }
-        if (i < MAX_UNKNOWN_EXTENSIONS) {
-            slots[i].type = type;
-            slots[i].data = ptls_iovec_init(src, end - src);
-            slots[i + 1].type = UINT16_MAX;
-        }
+    return properties != NULL && properties->collect_extension != NULL && properties->collect_extension(tls, properties, type);
+}
+
+static int collect_unknown_extension(ptls_t *tls, uint16_t type, const uint8_t *src, const uint8_t *const end,
+                                     ptls_raw_extension_t *slots)
+{
+    size_t i;
+    for (i = 0; slots[i].type != UINT16_MAX; ++i) {
+        assert(i < MAX_UNKNOWN_EXTENSIONS);
+        if (slots[i].type == type)
+            return PTLS_ALERT_ILLEGAL_PARAMETER;
+    }
+    if (i < MAX_UNKNOWN_EXTENSIONS) {
+        slots[i].type = type;
+        slots[i].data = ptls_iovec_init(src, end - src);
+        slots[i + 1].type = UINT16_MAX;
     }
     return 0;
 }
@@ -2418,11 +2421,10 @@
 {
     const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len, *esni_nonce = NULL;
     uint16_t type;
-    ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1];
+    static const ptls_raw_extension_t no_unknown_extensions = {UINT16_MAX};
+    ptls_raw_extension_t *unknown_extensions = (ptls_raw_extension_t *)&no_unknown_extensions;
     int ret, skip_early_data = 1;
 
-    unknown_extensions[0].type = UINT16_MAX;
-
     decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, &type, {
         if (tls->ctx->on_extension != NULL &&
             (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, type,
@@ -2477,7 +2479,17 @@
             skip_early_data = 0;
             break;
         default:
-            handle_unknown_extension(tls, properties, type, src, end, unknown_extensions);
+            if (should_collect_unknown_extension(tls, properties, type)) {
+                if (unknown_extensions == &no_unknown_extensions) {
+                    if ((unknown_extensions = malloc(sizeof(*unknown_extensions) * (MAX_UNKNOWN_EXTENSIONS + 1))) == NULL) {
+                        ret = PTLS_ERROR_NO_MEMORY;
+                        goto Exit;
+                    }
+                    unknown_extensions[0].type = UINT16_MAX;
+                }
+                if ((ret = collect_unknown_extension(tls, type, src, end, unknown_extensions)) != 0)
+                    goto Exit;
+            }
             break;
         }
         src = end;
@@ -2511,6 +2523,8 @@
     ret = PTLS_ERROR_IN_PROGRESS;
 
 Exit:
+    if (unknown_extensions != &no_unknown_extensions)
+        free(unknown_extensions);
     return ret;
 }
 
@@ -2955,8 +2969,7 @@
 
     /* save the extension, along with the key of myself */
     ptls_buffer_t ticket_buf;
-    uint8_t ticket_buf_small[512];
-    ptls_buffer_init(&ticket_buf, ticket_buf_small, sizeof(ticket_buf_small));
+    ptls_buffer_init(&ticket_buf, "", 0);
     ptls_buffer_push64(&ticket_buf, tls->ctx->get_time->cb(tls->ctx->get_time));
     ptls_buffer_push16(&ticket_buf, tls->key_share->id);
     ptls_buffer_push16(&ticket_buf, tls->cipher_suite->id);
@@ -3401,7 +3414,10 @@
             ch->status_request = 1;
             break;
         default:
-            handle_unknown_extension(tls, properties, exttype, src, end, ch->unknown_extensions);
+            if (should_collect_unknown_extension(tls, properties, exttype)) {
+                if ((ret = collect_unknown_extension(tls, exttype, src, end, ch->unknown_extensions)) != 0)
+                    goto Exit;
+            }
             break;
         }
         src = end;
@@ -3425,10 +3441,10 @@
     uint64_t issue_at, now = tls->ctx->get_time->cb(tls->ctx->get_time);
     uint32_t age_add;
     uint16_t ticket_key_exchange_id, ticket_csid;
-    uint8_t decbuf_small[256], binder_key[PTLS_MAX_DIGEST_SIZE], verify_data[PTLS_MAX_DIGEST_SIZE];
+    uint8_t binder_key[PTLS_MAX_DIGEST_SIZE];
     int ret;
 
-    ptls_buffer_init(&decbuf, decbuf_small, sizeof(decbuf_small));
+    ptls_buffer_init(&decbuf, "", 0);
 
     for (*psk_index = 0; *psk_index < ch->psk.identities.count; ++*psk_index) {
         struct st_ptls_client_hello_psk_t *identity = ch->psk.identities.list + *psk_index;
@@ -3512,9 +3528,10 @@
     if ((ret = derive_secret(tls->key_schedule, binder_key, "res binder")) != 0)
         goto Exit;
     ptls__key_schedule_update_hash(tls->key_schedule, ch_trunc.base, ch_trunc.len);
-    if ((ret = calc_verify_data(verify_data, tls->key_schedule, binder_key)) != 0)
+    if ((ret = calc_verify_data(binder_key /* to conserve space, reuse binder_key for storing verify_data */, tls->key_schedule,
+                                binder_key)) != 0)
         goto Exit;
-    if (!ptls_mem_equal(ch->psk.identities.list[*psk_index].binder.base, verify_data,
+    if (!ptls_mem_equal(ch->psk.identities.list[*psk_index].binder.base, binder_key,
                         tls->key_schedule->hashes[0].algo->digest_size)) {
         ret = PTLS_ALERT_DECRYPT_ERROR;
         goto Exit;
@@ -3524,7 +3541,6 @@
 Exit:
     ptls_buffer_dispose(&decbuf);
     ptls_clear_memory(binder_key, sizeof(binder_key));
-    ptls_clear_memory(verify_data, sizeof(verify_data));
     return ret;
 }
 
@@ -3580,12 +3596,12 @@
         } while (0);                                                                                                               \
         emitter->buf->off += PTLS_HELLO_RANDOM_SIZE;                                                                               \
         ptls_buffer_push_block(emitter->buf, 1,                                                                                    \
-                               { ptls_buffer_pushv(emitter->buf, ch.legacy_session_id.base, ch.legacy_session_id.len); });         \
+                               { ptls_buffer_pushv(emitter->buf, ch->legacy_session_id.base, ch->legacy_session_id.len); });       \
         ptls_buffer_push16(emitter->buf, tls->cipher_suite->id);                                                                   \
         ptls_buffer_push(emitter->buf, 0);                                                                                         \
         ptls_buffer_push_block(emitter->buf, 2, {                                                                                  \
             buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS,                                            \
-                                  { ptls_buffer_push16(emitter->buf, ch.selected_version); });                                     \
+                                  { ptls_buffer_push16(emitter->buf, ch->selected_version); });                                    \
             do {                                                                                                                   \
                 extensions                                                                                                         \
             } while (0);                                                                                                           \
@@ -3604,8 +3620,7 @@
                               additional_extensions                                                                                \
                           } while (0);                                                                                             \
                       })
-    struct st_ptls_client_hello_t ch = {0, NULL,   {NULL}, {NULL},     0,     {NULL}, {NULL},   {NULL}, {{0}},
-                                        {NULL}, {NULL}, {{{NULL}}}, {{0}}, {{0}},  {{NULL}}, {NULL}, {{UINT16_MAX}}};
+    struct st_ptls_client_hello_t *ch;
     struct {
         ptls_key_exchange_algorithm_t *algorithm;
         ptls_iovec_t peer_key;
@@ -3615,18 +3630,25 @@
     ptls_iovec_t pubkey = {0}, ecdh_secret = {0};
     int accept_early_data = 0, is_second_flight = tls->state == PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO, ret;
 
+    if ((ch = malloc(sizeof(*ch))) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+    *ch = (struct st_ptls_client_hello_t){0,      NULL,   {NULL},     {NULL}, 0,     {NULL},   {NULL}, {NULL},        {{0}},
+                                          {NULL}, {NULL}, {{{NULL}}}, {{0}},  {{0}}, {{NULL}}, {NULL}, {{UINT16_MAX}}};
+
     /* decode ClientHello */
-    if ((ret = decode_client_hello(tls, &ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties)) !=
+    if ((ret = decode_client_hello(tls, ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties)) !=
         0)
         goto Exit;
 
     /* bail out if CH cannot be handled as TLS 1.3, providing the application the raw CH and SNI, to help them fallback */
-    if (!is_supported_version(ch.selected_version)) {
+    if (!is_supported_version(ch->selected_version)) {
         if (!is_second_flight && tls->ctx->on_client_hello != NULL) {
             ptls_on_client_hello_parameters_t params = {
-                .server_name = ch.server_name,
+                .server_name = ch->server_name,
                 .raw_message = message,
-                .negotiated_protocols = {ch.alpn.list, ch.alpn.count},
+                .negotiated_protocols = {ch->alpn.list, ch->alpn.count},
                 .incompatible_version = 1,
             };
             if ((ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, &params)) != 0)
@@ -3638,67 +3660,67 @@
 
     /* Check TLS 1.3-specific constraints. Hereafter, we might exit without calling on_client_hello. That's fine because this CH is
      * ought to be rejected. */
-    if (ch.legacy_version <= 0x0300) {
+    if (ch->legacy_version <= 0x0300) {
         /* RFC 8446 Appendix D.5: any endpoint receiving a Hello message with legacy_version set to 0x0300 MUST abort the handshake
          * with a "protocol_version" alert. */
         ret = PTLS_ALERT_PROTOCOL_VERSION;
         goto Exit;
     }
-    if (!(ch.compression_methods.count == 1 && ch.compression_methods.ids[0] == 0)) {
+    if (!(ch->compression_methods.count == 1 && ch->compression_methods.ids[0] == 0)) {
         ret = PTLS_ALERT_ILLEGAL_PARAMETER;
         goto Exit;
     }
     /* esni */
-    if (ch.esni.cipher != NULL) {
-        if (ch.key_shares.base == NULL) {
+    if (ch->esni.cipher != NULL) {
+        if (ch->key_shares.base == NULL) {
             ret = PTLS_ALERT_ILLEGAL_PARAMETER;
             goto Exit;
         }
     }
     /* pre-shared key */
-    if (ch.psk.hash_end != NULL) {
+    if (ch->psk.hash_end != NULL) {
         /* PSK must be the last extension */
-        if (!ch.psk.is_last_extension) {
+        if (!ch->psk.is_last_extension) {
             ret = PTLS_ALERT_ILLEGAL_PARAMETER;
             goto Exit;
         }
     } else {
-        if (ch.psk.early_data_indication) {
+        if (ch->psk.early_data_indication) {
             ret = PTLS_ALERT_ILLEGAL_PARAMETER;
             goto Exit;
         }
     }
 
     if (tls->ctx->require_dhe_on_psk)
-        ch.psk.ke_modes &= ~(1u << PTLS_PSK_KE_MODE_PSK);
+        ch->psk.ke_modes &= ~(1u << PTLS_PSK_KE_MODE_PSK);
 
     /* handle client_random, legacy_session_id, SNI, ESNI */
     if (!is_second_flight) {
-        memcpy(tls->client_random, ch.random_bytes, sizeof(tls->client_random));
+        memcpy(tls->client_random, ch->random_bytes, sizeof(tls->client_random));
         log_client_random(tls);
-        if (ch.legacy_session_id.len != 0)
+        if (ch->legacy_session_id.len != 0)
             tls->send_change_cipher_spec = 1;
         ptls_iovec_t server_name = {NULL};
         int is_esni = 0;
-        if (ch.esni.cipher != NULL && tls->ctx->esni != NULL) {
-            if ((ret = client_hello_decrypt_esni(tls->ctx, &server_name, &tls->esni, &ch)) != 0)
+        if (ch->esni.cipher != NULL && tls->ctx->esni != NULL) {
+            if ((ret = client_hello_decrypt_esni(tls->ctx, &server_name, &tls->esni, ch)) != 0)
                 goto Exit;
             if (tls->ctx->update_esni_key != NULL) {
-                if ((ret = tls->ctx->update_esni_key->cb(tls->ctx->update_esni_key, tls, tls->esni->secret, ch.esni.cipher->hash,
+                if ((ret = tls->ctx->update_esni_key->cb(tls->ctx->update_esni_key, tls, tls->esni->secret, ch->esni.cipher->hash,
                                                          tls->esni->esni_contents_hash)) != 0)
                     goto Exit;
             }
             is_esni = 1;
-        } else if (ch.server_name.base != NULL) {
-            server_name = ch.server_name;
+        } else if (ch->server_name.base != NULL) {
+            server_name = ch->server_name;
         }
         if (tls->ctx->on_client_hello != NULL) {
             ptls_on_client_hello_parameters_t params = {server_name,
                                                         message,
-                                                        {ch.alpn.list, ch.alpn.count},
-                                                        {ch.signature_algorithms.list, ch.signature_algorithms.count},
-                                                        {ch.cert_compression_algos.list, ch.cert_compression_algos.count},
-                                                        {ch.client_ciphers.list, ch.client_ciphers.count},
+                                                        {ch->alpn.list, ch->alpn.count},
+                                                        {ch->signature_algorithms.list, ch->signature_algorithms.count},
+                                                        {ch->cert_compression_algos.list, ch->cert_compression_algos.count},
+                                                        {ch->client_ciphers.list, ch->client_ciphers.count},
                                                         is_esni};
             ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, &params);
         } else {
@@ -3709,13 +3731,13 @@
         if (ret != 0)
             goto Exit;
     } else {
-        if (ch.psk.early_data_indication) {
+        if (ch->psk.early_data_indication) {
             ret = PTLS_ALERT_DECODE_ERROR;
             goto Exit;
         }
         /* the following check is necessary so that we would be able to track the connection in SSLKEYLOGFILE, even though it
          * might not be for the safety of the protocol */
-        if (!ptls_mem_equal(tls->client_random, ch.random_bytes, sizeof(tls->client_random))) {
+        if (!ptls_mem_equal(tls->client_random, ch->random_bytes, sizeof(tls->client_random))) {
             ret = PTLS_ALERT_HANDSHAKE_FAILURE;
             goto Exit;
         }
@@ -3723,7 +3745,7 @@
          * ignoring the value unless the callback saves the server-name. */
         if (tls->server_name != NULL) {
             size_t l = strlen(tls->server_name);
-            if (!(ch.server_name.len == l && memcmp(ch.server_name.base, tls->server_name, l) == 0)) {
+            if (!(ch->server_name.len == l && memcmp(ch->server_name.base, tls->server_name, l) == 0)) {
                 ret = PTLS_ALERT_HANDSHAKE_FAILURE;
                 goto Exit;
             }
@@ -3732,8 +3754,8 @@
 
     { /* select (or check) cipher-suite, create key_schedule */
         ptls_cipher_suite_t *cs;
-        if ((ret = select_cipher(&cs, tls->ctx->cipher_suites, ch.cipher_suites.base,
-                                 ch.cipher_suites.base + ch.cipher_suites.len)) != 0)
+        if ((ret = select_cipher(&cs, tls->ctx->cipher_suites, ch->cipher_suites.base,
+                                 ch->cipher_suites.base + ch->cipher_suites.len)) != 0)
             goto Exit;
         if (!is_second_flight) {
             tls->cipher_suite = cs;
@@ -3747,8 +3769,8 @@
     }
 
     /* select key_share */
-    if (key_share.algorithm == NULL && ch.key_shares.base != NULL) {
-        const uint8_t *src = ch.key_shares.base, *const end = src + ch.key_shares.len;
+    if (key_share.algorithm == NULL && ch->key_shares.base != NULL) {
+        const uint8_t *src = ch->key_shares.base, *const end = src + ch->key_shares.len;
         ptls_decode_block(src, end, 2, {
             if ((ret = select_key_share(&key_share.algorithm, &key_share.peer_key, tls->ctx->key_exchanges, &src, end, 0)) != 0)
                 goto Exit;
@@ -3756,26 +3778,26 @@
     }
 
     if (!is_second_flight) {
-        if (ch.cookie.all.len != 0 && key_share.algorithm != NULL) {
+        if (ch->cookie.all.len != 0 && key_share.algorithm != NULL) {
 
             /* use cookie to check the integrity of the handshake, and update the context */
-            uint8_t sig[PTLS_MAX_DIGEST_SIZE];
             size_t sigsize = tls->ctx->cipher_suites[0]->hash->digest_size;
-            if ((ret = calc_cookie_signature(tls, properties, key_share.algorithm, ch.cookie.tbs, sig)) != 0)
+            uint8_t *sig = alloca(sigsize);
+            if ((ret = calc_cookie_signature(tls, properties, key_share.algorithm, ch->cookie.tbs, sig)) != 0)
                 goto Exit;
-            if (!(ch.cookie.signature.len == sigsize && ptls_mem_equal(ch.cookie.signature.base, sig, sigsize))) {
+            if (!(ch->cookie.signature.len == sigsize && ptls_mem_equal(ch->cookie.signature.base, sig, sigsize))) {
                 ret = PTLS_ALERT_HANDSHAKE_FAILURE;
                 goto Exit;
             }
             /* integrity check passed; update states */
             key_schedule_update_ch1hash_prefix(tls->key_schedule);
-            ptls__key_schedule_update_hash(tls->key_schedule, ch.cookie.ch1_hash.base, ch.cookie.ch1_hash.len);
+            ptls__key_schedule_update_hash(tls->key_schedule, ch->cookie.ch1_hash.base, ch->cookie.ch1_hash.len);
             key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0));
             /* ... reusing sendbuf to rebuild HRR for hash calculation */
             size_t hrr_start = emitter->buf->off;
-            EMIT_HELLO_RETRY_REQUEST(tls->key_schedule, ch.cookie.sent_key_share ? key_share.algorithm : NULL, {
+            EMIT_HELLO_RETRY_REQUEST(tls->key_schedule, ch->cookie.sent_key_share ? key_share.algorithm : NULL, {
                 buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_COOKIE,
-                                      { ptls_buffer_pushv(emitter->buf, ch.cookie.all.base, ch.cookie.all.len); });
+                                      { ptls_buffer_pushv(emitter->buf, ch->cookie.all.base, ch->cookie.all.len); });
             });
             emitter->buf->off = hrr_start;
             is_second_flight = 1;
@@ -3783,13 +3805,13 @@
         } else if (key_share.algorithm == NULL || (properties != NULL && properties->server.enforce_retry)) {
 
             /* send HelloRetryRequest  */
-            if (ch.negotiated_groups.base == NULL) {
+            if (ch->negotiated_groups.base == NULL) {
                 ret = PTLS_ALERT_MISSING_EXTENSION;
                 goto Exit;
             }
             ptls_key_exchange_algorithm_t *negotiated_group;
-            if ((ret = select_negotiated_group(&negotiated_group, tls->ctx->key_exchanges, ch.negotiated_groups.base,
-                                               ch.negotiated_groups.base + ch.negotiated_groups.len)) != 0)
+            if ((ret = select_negotiated_group(&negotiated_group, tls->ctx->key_exchanges, ch->negotiated_groups.base,
+                                               ch->negotiated_groups.base + ch->negotiated_groups.len)) != 0)
                 goto Exit;
             ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len);
             assert(tls->key_schedule->generation == 0);
@@ -3841,7 +3863,7 @@
                 if ((ret = push_change_cipher_spec(tls, emitter)) != 0)
                     goto Exit;
                 tls->state = PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO;
-                if (ch.psk.early_data_indication)
+                if (ch->psk.early_data_indication)
                     tls->server.early_data_skipped_bytes = 0;
                 ret = PTLS_ERROR_IN_PROGRESS;
             }
@@ -3850,15 +3872,15 @@
     }
 
     /* handle unknown extensions */
-    if ((ret = report_unknown_extensions(tls, properties, ch.unknown_extensions)) != 0)
+    if ((ret = report_unknown_extensions(tls, properties, ch->unknown_extensions)) != 0)
         goto Exit;
 
     /* try psk handshake */
-    if (!is_second_flight && ch.psk.hash_end != 0 &&
-        (ch.psk.ke_modes & ((1u << PTLS_PSK_KE_MODE_PSK) | (1u << PTLS_PSK_KE_MODE_PSK_DHE))) != 0 &&
+    if (!is_second_flight && ch->psk.hash_end != 0 &&
+        (ch->psk.ke_modes & ((1u << PTLS_PSK_KE_MODE_PSK) | (1u << PTLS_PSK_KE_MODE_PSK_DHE))) != 0 &&
         tls->ctx->encrypt_ticket != NULL && !tls->ctx->require_client_authentication) {
-        if ((ret = try_psk_handshake(tls, &psk_index, &accept_early_data, &ch,
-                                     ptls_iovec_init(message.base, ch.psk.hash_end - message.base))) != 0) {
+        if ((ret = try_psk_handshake(tls, &psk_index, &accept_early_data, ch,
+                                     ptls_iovec_init(message.base, ch->psk.hash_end - message.base))) != 0) {
             goto Exit;
         }
     }
@@ -3879,16 +3901,16 @@
         if (properties != NULL)
             properties->server.selected_psk_binder.len = 0;
     } else {
-        ptls__key_schedule_update_hash(tls->key_schedule, ch.psk.hash_end, message.base + message.len - ch.psk.hash_end);
-        if ((ch.psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK)) != 0) {
+        ptls__key_schedule_update_hash(tls->key_schedule, ch->psk.hash_end, message.base + message.len - ch->psk.hash_end);
+        if ((ch->psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK)) != 0) {
             mode = HANDSHAKE_MODE_PSK;
         } else {
-            assert((ch.psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK_DHE)) != 0);
+            assert((ch->psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK_DHE)) != 0);
             mode = HANDSHAKE_MODE_PSK_DHE;
         }
         tls->is_psk_handshake = 1;
         if (properties != NULL) {
-            ptls_iovec_t *selected = &ch.psk.identities.list[psk_index].binder;
+            ptls_iovec_t *selected = &ch->psk.identities.list[psk_index].binder;
             memcpy(properties->server.selected_psk_binder.base, selected->base, selected->len);
             properties->server.selected_psk_binder.len = selected->len;
         }
@@ -3908,7 +3930,7 @@
     /* run key-exchange, to obtain pubkey and secret */
     if (mode != HANDSHAKE_MODE_PSK) {
         if (key_share.algorithm == NULL) {
-            ret = ch.key_shares.base != NULL ? PTLS_ALERT_HANDSHAKE_FAILURE : PTLS_ALERT_MISSING_EXTENSION;
+            ret = ch->key_shares.base != NULL ? PTLS_ALERT_HANDSHAKE_FAILURE : PTLS_ALERT_MISSING_EXTENSION;
             goto Exit;
         }
         if ((ret = key_share.algorithm->exchange(key_share.algorithm, &pubkey, &ecdh_secret, key_share.peer_key)) != 0)
@@ -3949,7 +3971,7 @@
     } else {
         if ((ret = setup_traffic_protection(tls, 0, "c hs traffic", 2, 0)) != 0)
             goto Exit;
-        if (ch.psk.early_data_indication)
+        if (ch->psk.early_data_indication)
             tls->server.early_data_skipped_bytes = 0;
     }
 
@@ -3959,7 +3981,7 @@
         ptls_buffer_push_block(sendbuf, 2, {
             if (tls->esni != NULL) {
                 /* the extension is sent even if the application does not handle server name, because otherwise the handshake
-                 * would fail (FIXME ch.esni.nonce will be zero on HRR) */
+                 * would fail (FIXME ch->esni.nonce will be zero on HRR) */
                 buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME, {
                     uint8_t response_type = PTLS_ESNI_RESPONSE_TYPE_ACCEPT;
                     ptls_buffer_pushv(sendbuf, &response_type, 1);
@@ -4010,9 +4032,9 @@
             }
         }
 
-        ret = send_certificate_and_certificate_verify(tls, emitter, &ch.signature_algorithms, ptls_iovec_init(NULL, 0),
-                                                      PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING, ch.status_request,
-                                                      ch.cert_compression_algos.list, ch.cert_compression_algos.count);
+        ret = send_certificate_and_certificate_verify(tls, emitter, &ch->signature_algorithms, ptls_iovec_init(NULL, 0),
+                                                      PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING, ch->status_request,
+                                                      ch->cert_compression_algos.list, ch->cert_compression_algos.count);
 
         if (ret != 0) {
             goto Exit;
@@ -4047,7 +4069,7 @@
     }
 
     /* send session ticket if necessary */
-    if (ch.psk.ke_modes != 0 && tls->ctx->ticket_lifetime != 0) {
+    if (ch->psk.ke_modes != 0 && tls->ctx->ticket_lifetime != 0) {
         if ((ret = send_session_ticket(tls, emitter)) != 0)
             goto Exit;
     }
@@ -4064,6 +4086,7 @@
         ptls_clear_memory(ecdh_secret.base, ecdh_secret.len);
         free(ecdh_secret.base);
     }
+    free(ch);
     return ret;
 
 #undef EMIT_SERVER_HELLO
@@ -4747,9 +4770,8 @@
 
     const uint8_t *src = input, *const src_end = src + *inlen;
     ptls_buffer_t decryptbuf;
-    uint8_t decryptbuf_small[256];
 
-    ptls_buffer_init(&decryptbuf, decryptbuf_small, sizeof(decryptbuf_small));
+    ptls_buffer_init(&decryptbuf, "", 0);
 
     /* perform handhake until completion or until all the input has been swallowed */
     ret = PTLS_ERROR_IN_PROGRESS;
@@ -5063,7 +5085,7 @@
                       ptls_iovec_t hash_value, const char *label_prefix)
 {
     ptls_buffer_t hkdf_label;
-    uint8_t hkdf_label_buf[512];
+    uint8_t hkdf_label_buf[80];
     int ret;
 
     assert(label_prefix != NULL);