Merge branch 'master' into kazuho/psk2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 42692a5..4e96103 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,18 +10,12 @@
 ENDIF ()
 
 FIND_PACKAGE(PkgConfig REQUIRED)
-INCLUDE(cmake/dtrace-utils.cmake)
 INCLUDE(cmake/boringssl-adjust.cmake)
+INCLUDE(cmake/dtrace-utils.cmake)
+INCLUDE(cmake/fusion.cmake)
 
 CHECK_DTRACE(${PROJECT_SOURCE_DIR}/picotls-probes.d)
-IF ((CMAKE_SIZEOF_VOID_P EQUAL 8) AND
-    (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") OR
-     (CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64") OR
-     (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64"))
-    SET(WITH_FUSION_DEFAULT "ON")
-ELSE ()
-    SET(WITH_FUSION_DEFAULT "OFF")
-ENDIF ()
+CHECK_FUSION_PREREQUISITES()
 
 OPTION(WITH_DTRACE "use USDT (userspace Dtrace probes)" ${HAVE_DTRACE})
 OPTION(WITH_FUSION "build 'fusion' AES-GCM engine" ${WITH_FUSION_DEFAULT})
diff --git a/cmake/fusion.cmake b/cmake/fusion.cmake
new file mode 100644
index 0000000..2b7ef1c
--- /dev/null
+++ b/cmake/fusion.cmake
@@ -0,0 +1,30 @@
+INCLUDE(CheckCSourceCompiles)
+INCLUDE(CMakePushCheckState)
+
+FUNCTION (CHECK_FUSION_PREREQUISITES)
+    MESSAGE(STATUS "Detecting fusion support")
+
+    CMAKE_PUSH_CHECK_STATE()
+    SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -mavx2 -maes -mpclmul -mvaes -mvpclmulqdq")
+    CHECK_C_SOURCE_COMPILES("
+    #include <emmintrin.h>
+    #include <immintrin.h>
+    int main(void) {
+        __m256i  ord0, ord1, ord2, ord3 = _mm256_setzero_si256();
+        ord0 = _mm256_aesenc_epi128(ord1, ord2);
+        ord3 = _mm256_aesenclast_epi128(ord0, ord1);
+        ord1 = _mm256_clmulepi64_epi128(ord3, ord2, 0x00);
+        _mm_insert_epi64(_mm_setr_epi32(0, 1, 2, 3), 0, 0);
+        return 0;
+    }
+    " CC_HAS_AESNI256)
+    CMAKE_POP_CHECK_STATE()
+
+    IF (CC_HAS_AESNI256)
+        MESSAGE(STATUS "Can use fusion")
+        SET(WITH_FUSION_DEFAULT "ON" PARENT_SCOPE)
+    ELSE ()
+        MESSAGE(STATUS "Cannot use fusion")
+        SET(WITH_FUSION_DEFAULT "OFF" PARENT_SCOPE)
+    ENDIF ()
+ENDFUNCTION ()
diff --git a/lib/openssl.c b/lib/openssl.c
index 294fb60..a6abafe 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -608,6 +608,9 @@
     return ret;
 }
 
+/**
+ * Upon success, ownership of `pkey` is transferred to the object being created. Otherwise, the refcount remains unchanged.
+ */
 static int evp_keyex_init(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx, EVP_PKEY *pkey)
 {
     struct st_evp_keyex_context_t *ctx = NULL;
@@ -630,8 +633,10 @@
     *_ctx = &ctx->super;
     ret = 0;
 Exit:
-    if (ret != 0 && ctx != NULL)
+    if (ret != 0 && ctx != NULL) {
+        ctx->privkey = NULL; /* do not decrement refcount of pkey in case of error */
         evp_keyex_free(ctx);
+    }
     return ret;
 }
 
diff --git a/lib/picotls.c b/lib/picotls.c
index 985f69a..3400527 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -2401,7 +2401,10 @@
 
     /* initialize key schedule */
     if (!is_second_flight) {
-        tls->key_schedule = key_schedule_new(tls->cipher_suite, tls->ctx->cipher_suites, tls->ech.aead != NULL);
+        if ((tls->key_schedule = key_schedule_new(tls->cipher_suite, tls->ctx->cipher_suites, tls->ech.aead != NULL)) == NULL) {
+            ret = PTLS_ERROR_NO_MEMORY;
+            goto Exit;
+        }
         if ((ret = key_schedule_extract(tls->key_schedule, psk_secret)) != 0)
             goto Exit;
     }
@@ -3608,10 +3611,10 @@
         src = end;
     });
 
-    /* CH defined in TLS versions below 1.2 might not have extensions (or they might, see what OpenSSL 1.0.0 sends); so bail out
-     * after parsing the main variables. Zero is returned as it is a valid ClientHello. However `ptls_t::selected_version` remains
-     * zero indicating that no compatible version were found. */
-    if (ch->legacy_version < 0x0303 && src == end) {
+    /* In TLS versions 1.2 and earlier CH might not have an extensions block (or they might, see what OpenSSL 1.0.0 sends); so bail
+     * out if that is the case after parsing the main variables. Zero is returned as it is a valid ClientHello. However
+     * `ptls_t::selected_version` remains zero indicating that no compatible version were found. */
+    if (src == end) {
         ret = 0;
         goto Exit;
     }
@@ -4433,7 +4436,10 @@
             goto Exit;
         if (!is_second_flight) {
             tls->cipher_suite = cs;
-            tls->key_schedule = key_schedule_new(cs, NULL, 0);
+            if ((tls->key_schedule = key_schedule_new(cs, NULL, 0)) == NULL) {
+                ret = PTLS_ERROR_NO_MEMORY;
+                goto Exit;
+            }
         } else {
             if (tls->cipher_suite != cs) {
                 ret = PTLS_ALERT_HANDSHAKE_FAILURE;
diff --git a/picotls.xcodeproj/project.pbxproj b/picotls.xcodeproj/project.pbxproj
index 6882e19..d8a20e8 100644
--- a/picotls.xcodeproj/project.pbxproj
+++ b/picotls.xcodeproj/project.pbxproj
@@ -205,6 +205,7 @@
 		081F00CC291A358800534A86 /* asn1.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = asn1.h; sourceTree = "<group>"; };
 		081F00CD291A358800534A86 /* pembase64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pembase64.h; sourceTree = "<group>"; };
 		081F00CE291A358800534A86 /* ptlsbcrypt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ptlsbcrypt.h; sourceTree = "<group>"; };
+		0883D3272AEF8F2500B711CC /* fusion.cmake */ = {isa = PBXFileReference; lastKnownFileType = text; path = fusion.cmake; sourceTree = "<group>"; };
 		08A835E12995E04100D872CE /* chacha20poly1305.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = chacha20poly1305.h; sourceTree = "<group>"; };
 		08A835EB2996971300D872CE /* boringssl-adjust.cmake */ = {isa = PBXFileReference; lastKnownFileType = text; path = "boringssl-adjust.cmake"; sourceTree = "<group>"; };
 		08B3298229419DFC009D6766 /* ech-live.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = "ech-live.t"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.perl; };
@@ -527,6 +528,7 @@
 			children = (
 				08A835EB2996971300D872CE /* boringssl-adjust.cmake */,
 				E95EBCCA227EA0180022C32D /* dtrace-utils.cmake */,
+				0883D3272AEF8F2500B711CC /* fusion.cmake */,
 			);
 			path = cmake;
 			sourceTree = "<group>";
diff --git a/t/picotls.c b/t/picotls.c
index 558c470..b375b9b 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -2140,6 +2140,13 @@
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x32, 0x00, 0x0a, 0x00, 0x16,
         0x00, 0x13, 0x00, 0x09, 0x00, 0x15, 0x00, 0x12, 0x00, 0x03, 0x00, 0x08, 0x00, 0x14, 0x00, 0x11, 0x00, 0xff, 0x01, 0x00};
+    static const uint8_t tls12_no_exts[] = {
+        0x16, 0x03, 0x01, 0x00, 0x67, 0x01, 0x00, 0x00, 0x63, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xee, 0x8a, 0x29, 0xdd, 0xcf, 0x6d, 0x64, 0xfd, 0xd0, 0xcd,
+        0xa0, 0x9b, 0xc1, 0x32, 0x46, 0xbf, 0x53, 0xda, 0x29, 0x23, 0x81, 0x5f, 0x54, 0x1f, 0xbd, 0xe0, 0x8e, 0x97,
+        0x17, 0x5b, 0x03, 0x5d, 0x00, 0x1c, 0x00, 0xff, 0x00, 0x9c, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x0a, 0xc0, 0x09,
+        0xc0, 0x13, 0xc0, 0x14, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x9e, 0x00, 0x33, 0x00, 0x39, 0x00, 0x0a, 0x01, 0x00};
     /* client hello generated by openssl 1.0.0s; s_client -bugs -no_ticket -cipher DES-CBC-SHA -connect */
     static const uint8_t tls10_with_exts[] = {0x16, 0x03, 0x01, 0x00, 0x2f, 0x01, 0x00, 0x00, 0x2b, 0x03, 0x01, 0x63, 0xfc,
                                               0x5a, 0x16, 0xde, 0x7a, 0xfc, 0xc1, 0x0c, 0x54, 0x12, 0xa6, 0xd3, 0x8c, 0xcf,
@@ -2202,6 +2209,14 @@
     legacy_params = NULL;
 
     tls = ptls_new(ctx, 1);
+    len = sizeof(tls12_no_exts);
+    ret = ptls_handshake(tls, &sendbuf, tls12_no_exts, &len, NULL);
+    ptls_free(tls);
+    ok(ret == PTLS_ALERT_PROTOCOL_VERSION);
+    free(legacy_params);
+    legacy_params = NULL;
+
+    tls = ptls_new(ctx, 1);
     len = sizeof(tls10_with_exts);
     ret = ptls_handshake(tls, &sendbuf, tls10_with_exts, &len, NULL);
     ptls_free(tls);