Commit 208d8454e2 for openssl.org
commit 208d8454e2d386b29e6fdeaec5b651cc0c641d87
Author: Andrew Dinh <andrewd@openssl.org>
Date: Thu Aug 20 09:32:53 2026 +0700
Fix tick race in radix fault-injection scripts
Guard any `OP_SET_INJECT_WORD`+`OP_WRITE` with 2 new functions
`OP_ENGINE_TICK_ENABLE`/`OP_ENGINE_TICK_DISABLE` that enable/disable ticking
in the QUIC engine. Otherwise, the radix test interpreter can tick between the
2 ops and cause the peer to close the connection prematurely
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Merge-date: Wed Aug 26 12:00:35 2026
Merged-from: https://github.com/openssl/openssl/pull/32406
diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c
index de577468da..50fb65ff69 100644
--- a/test/radix/quic_ops.c
+++ b/test/radix/quic_ops.c
@@ -1075,6 +1075,27 @@ err:
return ok;
}
+/* Inhibit ticking at the QUIC_ENGINE level */
+DEF_FUNC(hf_set_engine_tick_inhibit)
+{
+ int ok = 0;
+ uint64_t inhibit;
+ SSL *ssl;
+ QUIC_CHANNEL *ch;
+
+ F_POP(inhibit);
+ REQUIRE_SSL(ssl);
+
+ if (!TEST_ptr(ch = ossl_quic_conn_get_channel(ssl)))
+ goto err;
+
+ ossl_quic_engine_set_inhibit_tick(ossl_quic_channel_get0_engine(ch),
+ inhibit != 0);
+ ok = 1;
+err:
+ return ok;
+}
+
/*
* Skip fake time and wait for the assist thread to catch up. It waits on real
* time internally, so wake it and spin until the event timeout is back in the
@@ -1771,6 +1792,20 @@ err:
OP_PUSH_U64(1), \
OP_FUNC(hf_set_tick_active))
+/*
+ * Inhibits/uninhibits ticking of the QUIC_ENGINE that "name" belongs (see
+ * see hf_set_engine_tick_inhibit).
+ */
+#define OP_ENGINE_TICK_DISABLE(name) \
+ (OP_SELECT_SSL(0, name), \
+ OP_PUSH_U64(1), \
+ OP_FUNC(hf_set_engine_tick_inhibit))
+
+#define OP_ENGINE_TICK_ENABLE(name) \
+ (OP_SELECT_SSL(0, name), \
+ OP_PUSH_U64(0), \
+ OP_FUNC(hf_set_engine_tick_inhibit))
+
#define OP_SKIP_TIME_WAIT(name, ms) \
(OP_SELECT_SSL(0, name), \
OP_PUSH_U64(ms), \
diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c
index 92de214c0f..52ab5bff50 100644
--- a/test/radix/quic_tests.c
+++ b/test/radix/quic_tests.c
@@ -1902,9 +1902,11 @@ DEF_SCRIPT(script_21, "Fault injection - unknown frame in 1-RTT packet")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(QUIC_PKT_TYPE_1RTT, OSSL_QUIC_VLINT_MAX);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0);
}
@@ -1931,9 +1933,11 @@ DEF_SCRIPT(script_22, "Fault injection - non-zero packet header reserved bits")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(1, 0);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
}
@@ -1979,9 +1983,11 @@ DEF_SCRIPT(script_23, "Fault injection - empty NEW_TOKEN")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(1, 0);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0);
}
@@ -2027,9 +2033,11 @@ DEF_SCRIPT(script_24, "Fault injection - excess value of MAX_STREAMS_BIDI")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(1, OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0);
}
@@ -2046,9 +2054,11 @@ DEF_SCRIPT(script_25, "Fault injection - excess value of MAX_STREAMS_UNI")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(1, OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0);
}
@@ -2065,9 +2075,11 @@ DEF_SCRIPT(script_26, "Fault injection - excess value of STREAMS_BLOCKED_BIDI")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(1, OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_LIMIT_ERROR, 0, 0);
}
@@ -2084,9 +2096,11 @@ DEF_SCRIPT(script_27, "Fault injection - excess value of STREAMS_BLOCKED_UNI")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(1, OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_LIMIT_ERROR, 0, 0);
}
@@ -2145,8 +2159,10 @@ DEF_SCRIPT(script_28, "Fault injection - received RESET_STREAM for send-only str
OP_ACCEPT_STREAM_WAIT(S, Sb, 0);
OP_READ_EXPECT(Sb, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_UNI_ID(0) + 1, OSSL_QUIC_FRAME_TYPE_RESET_STREAM);
OP_WRITE(Sa, "fruit", 5);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2171,8 +2187,10 @@ DEF_SCRIPT(script_29, "Fault injection - received RESET_STREAM for nonexistent s
OP_ACCEPT_STREAM_WAIT(S, Sb, 0);
OP_READ_EXPECT(Sb, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_UNI_ID(1) + 1, OSSL_QUIC_FRAME_TYPE_RESET_STREAM);
OP_WRITE(Sa, "fruit", 5);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2191,9 +2209,11 @@ DEF_SCRIPT(script_30, "Fault injection - received STOP_SENDING for receive-only
OP_ACCEPT_STREAM_WAIT(C, Ca, 0);
OP_READ_EXPECT(Ca, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(S_UNI_ID(0) + 1, OSSL_QUIC_FRAME_TYPE_STOP_SENDING);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2212,8 +2232,10 @@ DEF_SCRIPT(script_31, "Fault injection - received STOP_SENDING for nonexistent r
OP_ACCEPT_STREAM_WAIT(C, Ca, 0);
OP_READ_EXPECT(Ca, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_UNI_ID(0) + 1, OSSL_QUIC_FRAME_TYPE_STOP_SENDING);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2295,8 +2317,10 @@ DEF_SCRIPT(script_32, "Fault injection - STREAM frame for nonexistent stream")
OP_ACCEPT_STREAM_WAIT(C, Ca, 0);
OP_READ_EXPECT(Ca, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_UNI_ID(0) + 1, 1);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2315,8 +2339,10 @@ DEF_SCRIPT(script_33, "Fault injection - STREAM frame with illegal offset")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_BIDI_ID(0) + 1, 2);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0);
}
@@ -2334,8 +2360,10 @@ DEF_SCRIPT(script_34, "Fault injection - STREAM frame which exceeds FC")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_BIDI_ID(0) + 1, 3);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_FLOW_CONTROL_ERROR, 0, 0);
}
@@ -2354,9 +2382,11 @@ DEF_SCRIPT(script_35, "Fault injection - MAX_STREAM_DATA for receive-only stream
OP_ACCEPT_STREAM_WAIT(C, Ca, 0);
OP_READ_EXPECT(Ca, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(S_UNI_ID(0) + 1, OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2375,8 +2405,10 @@ DEF_SCRIPT(script_36, "Fault injection - MAX_STREAM_DATA for nonexistent stream"
OP_ACCEPT_STREAM_WAIT(C, Ca, 0);
OP_READ_EXPECT(Ca, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_BIDI_ID(0) + 1, OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2396,8 +2428,10 @@ DEF_SCRIPT(script_37, "Fault injection - STREAM_DATA_BLOCKED for send-only strea
OP_READ_EXPECT(Sa, "apple", 5);
OP_NEW_STREAM(S, Sb, SSL_STREAM_FLAG_UNI);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_UNI_ID(0) + 1, OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED);
OP_WRITE(Sb, "orange", 5);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2416,10 +2450,12 @@ DEF_SCRIPT(script_38, "Fault injection - STREAM_DATA_BLOCKED for non-existent st
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(C_BIDI_ID(0) + 1, OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED);
OP_NEW_STREAM(S, Sb, SSL_STREAM_FLAG_UNI);
OP_WRITE(Sb, "orange", 5);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_STREAM_STATE_ERROR, 0, 0);
}
@@ -2522,8 +2558,10 @@ DEF_SCRIPT(script_39, "Fault injection - NEW_CONN_ID with zero-len CID")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(0, 1);
OP_WRITE(Sa, "orange", 5);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0);
}
@@ -2732,8 +2770,10 @@ DEF_SCRIPT(script_42, "Fault injection - CRYPTO frame with illegal offset")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(1, (((uint64_t)1) << 62) - 1);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0);
}
@@ -2752,8 +2792,10 @@ DEF_SCRIPT(script_43, "Fault injection - CRYPTO frame exceeding FC")
OP_ACCEPT_STREAM_WAIT(S, Sa, 0);
OP_READ_EXPECT(Sa, "apple", 5);
+ OP_ENGINE_TICK_DISABLE(S);
OP_SET_INJECT_WORD(1, 0x100000 /* 1 MiB */);
OP_WRITE(Sa, "orange", 6);
+ OP_ENGINE_TICK_ENABLE(S);
OP_EXPECT_CONN_CLOSE_INFO(C, OSSL_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED, 0, 0);
}