From 60ccf54fa632a9ff1547302f5dfe2ca6af5af3f7 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 3 May 2020 17:29:19 +0100 Subject: [PATCH 1/3] Problem: sub/cancel broken with CURVE Solution: handle downgrading sub/cancel messages in CURVE engine --- src/curve_client.cpp | 10 ++++-- src/curve_client.hpp | 4 ++- src/curve_mechanism_base.cpp | 46 +++++++++++++++++++++++---- src/curve_mechanism_base.hpp | 8 +++-- src/curve_server.cpp | 10 ++++-- src/curve_server.hpp | 3 +- src/ws_engine.cpp | 4 +-- src/zmtp_engine.cpp | 14 ++++---- src/zmtp_engine.hpp | 2 +- unittests/unittest_curve_encoding.cpp | 6 ++-- 10 files changed, 78 insertions(+), 29 deletions(-) diff --git a/src/curve_client.cpp b/src/curve_client.cpp index b4b7a380..5d1f64e8 100644 --- a/src/curve_client.cpp +++ b/src/curve_client.cpp @@ -41,10 +41,14 @@ #include "secure_allocator.hpp" zmq::curve_client_t::curve_client_t (session_base_t *session_, - const options_t &options_) : + const options_t &options_, + const bool downgrade_sub_) : mechanism_base_t (session_, options_), - curve_mechanism_base_t ( - session_, options_, "CurveZMQMESSAGEC", "CurveZMQMESSAGES"), + curve_mechanism_base_t (session_, + options_, + "CurveZMQMESSAGEC", + "CurveZMQMESSAGES", + downgrade_sub_), _state (send_hello), _tools (options_.curve_public_key, options_.curve_secret_key, diff --git a/src/curve_client.hpp b/src/curve_client.hpp index c85d1b78..9d51d3a0 100644 --- a/src/curve_client.hpp +++ b/src/curve_client.hpp @@ -44,7 +44,9 @@ class session_base_t; class curve_client_t ZMQ_FINAL : public curve_mechanism_base_t { public: - curve_client_t (session_base_t *session_, const options_t &options_); + curve_client_t (session_base_t *session_, + const options_t &options_, + const bool downgrade_sub_); ~curve_client_t () ZMQ_FINAL; // mechanism implementation diff --git a/src/curve_mechanism_base.cpp b/src/curve_mechanism_base.cpp index c040d899..6f0173e1 100644 --- a/src/curve_mechanism_base.cpp +++ b/src/curve_mechanism_base.cpp @@ -49,9 +49,11 @@ zmq::curve_mechanism_base_t::curve_mechanism_base_t ( session_base_t *session_, const options_t &options_, const char *encode_nonce_prefix_, - const char *decode_nonce_prefix_) : + const char *decode_nonce_prefix_, + const bool downgrade_sub_) : mechanism_base_t (session_, options_), - curve_encoding_t (encode_nonce_prefix_, decode_nonce_prefix_) + curve_encoding_t ( + encode_nonce_prefix_, decode_nonce_prefix_, downgrade_sub_) { } @@ -77,11 +79,13 @@ int zmq::curve_mechanism_base_t::decode (msg_t *msg_) } zmq::curve_encoding_t::curve_encoding_t (const char *encode_nonce_prefix_, - const char *decode_nonce_prefix_) : + const char *decode_nonce_prefix_, + const bool downgrade_sub_) : _encode_nonce_prefix (encode_nonce_prefix_), _decode_nonce_prefix (decode_nonce_prefix_), _cn_nonce (1), - _cn_peer_nonce (1) + _cn_peer_nonce (1), + _downgrade_sub (downgrade_sub_) { } @@ -133,15 +137,26 @@ int zmq::curve_encoding_t::check_validity (msg_t *msg_, int *error_event_code_) int zmq::curve_encoding_t::encode (msg_t *msg_) { + size_t sub_cancel_len = 0; uint8_t message_nonce[crypto_box_NONCEBYTES]; memcpy (message_nonce, _encode_nonce_prefix, nonce_prefix_len); put_uint64 (message_nonce + nonce_prefix_len, get_and_inc_nonce ()); + if (msg_->is_subscribe () || msg_->is_cancel ()) { + if (_downgrade_sub) + sub_cancel_len = 1; + else + sub_cancel_len = msg_->is_cancel () + ? zmq::msg_t::cancel_cmd_name_size + : zmq::msg_t::sub_cmd_name_size; + } + #ifdef ZMQ_HAVE_CRYPTO_BOX_EASY_FNS - const size_t mlen = flags_len + msg_->size (); + const size_t mlen = flags_len + sub_cancel_len + msg_->size (); std::vector message_plaintext (mlen); #else - const size_t mlen = crypto_box_ZEROBYTES + flags_len + msg_->size (); + const size_t mlen = + crypto_box_ZEROBYTES + flags_len + sub_cancel_len + msg_->size (); std::vector message_plaintext_with_zerobytes (mlen); uint8_t *const message_plaintext = &message_plaintext_with_zerobytes[crypto_box_ZEROBYTES]; @@ -153,10 +168,27 @@ int zmq::curve_encoding_t::encode (msg_t *msg_) const uint8_t flags = msg_->flags () & flag_mask; message_plaintext[0] = flags; + + // For backward compatibility subscribe/cancel command messages are not stored with + // the message flags, and are encoded in the encoder, so that messages for < 3.0 peers + // can be encoded in the "old" 0/1 way rather than as commands. + if (sub_cancel_len == 1) + message_plaintext[flags_len] = msg_->is_subscribe () ? 1 : 0; + else if (sub_cancel_len == zmq::msg_t::sub_cmd_name_size) { + message_plaintext[0] |= zmq::msg_t::command; + memcpy (&message_plaintext[flags_len], zmq::sub_cmd_name, + zmq::msg_t::sub_cmd_name_size); + } else if (sub_cancel_len == zmq::msg_t::cancel_cmd_name_size) { + message_plaintext[0] |= zmq::msg_t::command; + memcpy (&message_plaintext[flags_len], zmq::cancel_cmd_name, + zmq::msg_t::cancel_cmd_name_size); + } + // this is copying the data from insecure memory, so there is no point in // using secure_allocator_t for message_plaintext if (msg_->size () > 0) - memcpy (&message_plaintext[flags_len], msg_->data (), msg_->size ()); + memcpy (&message_plaintext[flags_len + sub_cancel_len], msg_->data (), + msg_->size ()); #ifdef ZMQ_HAVE_CRYPTO_BOX_EASY_FNS msg_t msg_box; diff --git a/src/curve_mechanism_base.hpp b/src/curve_mechanism_base.hpp index a341e744..a72965e9 100644 --- a/src/curve_mechanism_base.hpp +++ b/src/curve_mechanism_base.hpp @@ -56,7 +56,8 @@ class curve_encoding_t { public: curve_encoding_t (const char *encode_nonce_prefix_, - const char *decode_nonce_prefix_); + const char *decode_nonce_prefix_, + const bool downgrade_sub_); int encode (msg_t *msg_); int decode (msg_t *msg_, int *error_event_code_); @@ -81,6 +82,8 @@ class curve_encoding_t // Intermediary buffer used to speed up boxing and unboxing. uint8_t _cn_precom[crypto_box_BEFORENMBYTES]; + const bool _downgrade_sub; + ZMQ_NON_COPYABLE_NOR_MOVABLE (curve_encoding_t) }; @@ -91,7 +94,8 @@ class curve_mechanism_base_t : public virtual mechanism_base_t, curve_mechanism_base_t (session_base_t *session_, const options_t &options_, const char *encode_nonce_prefix_, - const char *decode_nonce_prefix_); + const char *decode_nonce_prefix_, + const bool downgrade_sub_); // mechanism implementation int encode (msg_t *msg_) ZMQ_OVERRIDE; diff --git a/src/curve_server.cpp b/src/curve_server.cpp index 91ac85b9..6e34221e 100644 --- a/src/curve_server.cpp +++ b/src/curve_server.cpp @@ -41,12 +41,16 @@ zmq::curve_server_t::curve_server_t (session_base_t *session_, const std::string &peer_address_, - const options_t &options_) : + const options_t &options_, + const bool downgrade_sub_) : mechanism_base_t (session_, options_), zap_client_common_handshake_t ( session_, peer_address_, options_, sending_ready), - curve_mechanism_base_t ( - session_, options_, "CurveZMQMESSAGES", "CurveZMQMESSAGEC") + curve_mechanism_base_t (session_, + options_, + "CurveZMQMESSAGES", + "CurveZMQMESSAGEC", + downgrade_sub_) { int rc; // Fetch our secret key from socket options diff --git a/src/curve_server.hpp b/src/curve_server.hpp index 67b85e67..995efce0 100644 --- a/src/curve_server.hpp +++ b/src/curve_server.hpp @@ -48,7 +48,8 @@ class curve_server_t ZMQ_FINAL : public zap_client_common_handshake_t, public: curve_server_t (session_base_t *session_, const std::string &peer_address_, - const options_t &options_); + const options_t &options_, + const bool downgrade_sub_); ~curve_server_t (); // mechanism implementation diff --git a/src/ws_engine.cpp b/src/ws_engine.cpp index 26404905..20d0f8d4 100644 --- a/src/ws_engine.cpp +++ b/src/ws_engine.cpp @@ -268,10 +268,10 @@ bool zmq::ws_engine_t::select_protocol (const char *protocol_) && strcmp ("ZWS2.0/CURVE", protocol_) == 0) { if (_options.as_server) _mechanism = new (std::nothrow) - curve_server_t (session (), _peer_address, _options); + curve_server_t (session (), _peer_address, _options, false); else _mechanism = - new (std::nothrow) curve_client_t (session (), _options); + new (std::nothrow) curve_client_t (session (), _options, false); alloc_assert (_mechanism); return true; } diff --git a/src/zmtp_engine.cpp b/src/zmtp_engine.cpp index d9a186b1..a8615d4c 100644 --- a/src/zmtp_engine.cpp +++ b/src/zmtp_engine.cpp @@ -347,7 +347,7 @@ bool zmq::zmtp_engine_t::handshake_v2_0 () return true; } -bool zmq::zmtp_engine_t::handshake_v3_x () +bool zmq::zmtp_engine_t::handshake_v3_x (const bool downgrade_sub_) { if (_options.mechanism == ZMQ_NULL && memcmp (_greeting_recv + 12, "NULL\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", @@ -374,11 +374,11 @@ bool zmq::zmtp_engine_t::handshake_v3_x () "CURVE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) == 0) { if (_options.as_server) - _mechanism = new (std::nothrow) - curve_server_t (session (), _peer_address, _options); + _mechanism = new (std::nothrow) curve_server_t ( + session (), _peer_address, _options, downgrade_sub_); else - _mechanism = - new (std::nothrow) curve_client_t (session (), _options); + _mechanism = new (std::nothrow) + curve_client_t (session (), _options, downgrade_sub_); alloc_assert (_mechanism); } #endif @@ -418,7 +418,7 @@ bool zmq::zmtp_engine_t::handshake_v3_0 () _options.in_batch_size, _options.maxmsgsize, _options.zero_copy); alloc_assert (_decoder); - return zmq::zmtp_engine_t::handshake_v3_x (); + return zmq::zmtp_engine_t::handshake_v3_x (true); } bool zmq::zmtp_engine_t::handshake_v3_1 () @@ -430,7 +430,7 @@ bool zmq::zmtp_engine_t::handshake_v3_1 () _options.in_batch_size, _options.maxmsgsize, _options.zero_copy); alloc_assert (_decoder); - return zmq::zmtp_engine_t::handshake_v3_x (); + return zmq::zmtp_engine_t::handshake_v3_x (false); } int zmq::zmtp_engine_t::routing_id_msg (msg_t *msg_) diff --git a/src/zmtp_engine.hpp b/src/zmtp_engine.hpp index 2d18bcfa..10f4134c 100644 --- a/src/zmtp_engine.hpp +++ b/src/zmtp_engine.hpp @@ -92,7 +92,7 @@ class zmtp_engine_t ZMQ_FINAL : public stream_engine_base_t bool handshake_v1_0_unversioned (); bool handshake_v1_0 (); bool handshake_v2_0 (); - bool handshake_v3_x (); + bool handshake_v3_x (bool downgrade_sub); bool handshake_v3_0 (); bool handshake_v3_1 (); diff --git a/unittests/unittest_curve_encoding.cpp b/unittests/unittest_curve_encoding.cpp index 7cccd06e..a331476e 100644 --- a/unittests/unittest_curve_encoding.cpp +++ b/unittests/unittest_curve_encoding.cpp @@ -48,9 +48,11 @@ void test_roundtrip (zmq::msg_t *msg_) + msg_->size ()); zmq::curve_encoding_t encoding_client ("CurveZMQMESSAGEC", - "CurveZMQMESSAGES"); + "CurveZMQMESSAGES", + false); zmq::curve_encoding_t encoding_server ("CurveZMQMESSAGES", - "CurveZMQMESSAGEC"); + "CurveZMQMESSAGEC", + false); uint8_t client_public[32]; uint8_t client_secret[32]; From c8e62cb2e01f80e63fa10addc48028cd480b0599 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 3 May 2020 17:44:04 +0100 Subject: [PATCH 2/3] Problem: no dictionaries and seed corpora for fuzzers Solution: add them and install them where oss-fuzz expects them --- Makefile.am | 27 +++++++++++ tests/fuzzer_corpora/endpoint.dict | 4 ++ .../fuzzer_corpora/test_bind_curve_fuzzer.txt | 1 + .../fuzzer_corpora/test_bind_null_fuzzer.txt | 1 + .../test_connect_curve_fuzzer.txt | 1 + .../test_connect_null_fuzzer.txt | 1 + tests/fuzzer_corpora/zmtp.dict | 45 +++++++++++++++++++ 7 files changed, 80 insertions(+) create mode 100644 tests/fuzzer_corpora/endpoint.dict create mode 100644 tests/fuzzer_corpora/test_bind_curve_fuzzer.txt create mode 100644 tests/fuzzer_corpora/test_bind_null_fuzzer.txt create mode 100644 tests/fuzzer_corpora/test_connect_curve_fuzzer.txt create mode 100644 tests/fuzzer_corpora/test_connect_null_fuzzer.txt create mode 100644 tests/fuzzer_corpora/zmtp.dict diff --git a/Makefile.am b/Makefile.am index b3a8b12c..1590b1a6 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1176,6 +1176,33 @@ endif FUZZINGdir = ${prefix}/${FUZZING_INSTALLDIR} FUZZING_PROGRAMS = ${fuzzer_apps} +FUZZING_DATA = tests/fuzzer_corpora/endpoint.dict tests/fuzzer_corpora/zmtp.dict \ + tests/fuzzer_corpora/test_bind_curve_fuzzer.txt tests/fuzzer_corpora/test_bind_null_fuzzer.txt \ + tests/fuzzer_corpora/test_connect_curve_fuzzer.txt tests/fuzzer_corpora/test_connect_null_fuzzer.txt +install-data-hook: + $(LN_S) -r -f $(DESTDIR)/$(FUZZINGdir)/endpoint.dict $(DESTDIR)/$(FUZZINGdir)/test_bind_fuzzer.dict + $(LN_S) -r -f $(DESTDIR)/$(FUZZINGdir)/endpoint.dict $(DESTDIR)/$(FUZZINGdir)/test_connect_fuzzer.dict + $(LN_S) -r -f $(DESTDIR)/$(FUZZINGdir)/zmtp.dict $(DESTDIR)/$(FUZZINGdir)/test_bind_curve_fuzzer.dict + $(LN_S) -r -f $(DESTDIR)/$(FUZZINGdir)/zmtp.dict $(DESTDIR)/$(FUZZINGdir)/test_bind_null_fuzzer.dict + $(LN_S) -r -f $(DESTDIR)/$(FUZZINGdir)/zmtp.dict $(DESTDIR)/$(FUZZINGdir)/test_connect_curve_fuzzer.dict + $(LN_S) -r -f $(DESTDIR)/$(FUZZINGdir)/zmtp.dict $(DESTDIR)/$(FUZZINGdir)/test_connect_null_fuzzer.dict + $(shell cat $(DESTDIR)/$(FUZZINGdir)/test_bind_curve_fuzzer.txt | perl -e 'print pack "H*", ' > $(DESTDIR)/$(FUZZINGdir)/test_bind_curve_fuzzer.seed; \ + export fn=$$(cat $(DESTDIR)/$(FUZZINGdir)/test_bind_curve_fuzzer.seed | sha1sum | awk '{print $$1}'); \ + mv $(DESTDIR)/$(FUZZINGdir)/test_bind_curve_fuzzer.seed $(DESTDIR)/$(FUZZINGdir)/$$fn; \ + zip -j -m --quiet $(DESTDIR)/$(FUZZINGdir)/test_bind_curve_fuzzer_seed_corpus.zip $(DESTDIR)/$(FUZZINGdir)/$$fn) + $(shell cat $(DESTDIR)/$(FUZZINGdir)/test_bind_null_fuzzer.txt | perl -e 'print pack "H*", ' > $(DESTDIR)/$(FUZZINGdir)/test_bind_null_fuzzer.seed; \ + export fn=$$(cat $(DESTDIR)/$(FUZZINGdir)/test_bind_null_fuzzer.seed | sha1sum | awk '{print $$1}'); \ + mv $(DESTDIR)/$(FUZZINGdir)/test_bind_null_fuzzer.seed $(DESTDIR)/$(FUZZINGdir)/$$fn; \ + zip -j -m --quiet $(DESTDIR)/$(FUZZINGdir)/test_bind_null_fuzzer_seed_corpus.zip $(DESTDIR)/$(FUZZINGdir)/$$fn) + $(shell cat $(DESTDIR)/$(FUZZINGdir)/test_connect_curve_fuzzer.txt | perl -e 'print pack "H*", ' > $(DESTDIR)/$(FUZZINGdir)/test_connect_curve_fuzzer.seed; \ + export fn=$$(cat $(DESTDIR)/$(FUZZINGdir)/test_connect_curve_fuzzer.seed | sha1sum | awk '{print $$1}'); \ + mv $(DESTDIR)/$(FUZZINGdir)/test_connect_curve_fuzzer.seed $(DESTDIR)/$(FUZZINGdir)/$$fn; \ + zip -j -m --quiet $(DESTDIR)/$(FUZZINGdir)/test_connect_curve_fuzzer_seed_corpus.zip $(DESTDIR)/$(FUZZINGdir)/$$fn) + $(shell cat $(DESTDIR)/$(FUZZINGdir)/test_connect_null_fuzzer.txt | perl -e 'print pack "H*", ' > $(DESTDIR)/$(FUZZINGdir)/test_connect_null_fuzzer.seed; \ + export fn=$$(cat $(DESTDIR)/$(FUZZINGdir)/test_connect_null_fuzzer.seed | sha1sum | awk '{print $$1}'); \ + mv $(DESTDIR)/$(FUZZINGdir)/test_connect_null_fuzzer.seed $(DESTDIR)/$(FUZZINGdir)/$$fn; \ + zip -j -m --quiet $(DESTDIR)/$(FUZZINGdir)/test_connect_null_fuzzer_seed_corpus.zip $(DESTDIR)/$(FUZZINGdir)/$$fn) + rm -f $(DESTDIR)/$(FUZZINGdir)/*.txt else test_apps += tests/test_bind_null_fuzzer \ tests/test_connect_null_fuzzer \ diff --git a/tests/fuzzer_corpora/endpoint.dict b/tests/fuzzer_corpora/endpoint.dict new file mode 100644 index 00000000..6a1b65f6 --- /dev/null +++ b/tests/fuzzer_corpora/endpoint.dict @@ -0,0 +1,4 @@ +ipc="ipc://" +inproc="inproc://" +tcp="tcp://" +udp="udp://" \ No newline at end of file diff --git a/tests/fuzzer_corpora/test_bind_curve_fuzzer.txt b/tests/fuzzer_corpora/test_bind_curve_fuzzer.txt new file mode 100644 index 00000000..f9896873 --- /dev/null +++ b/tests/fuzzer_corpora/test_bind_curve_fuzzer.txt @@ -0,0 +1 @@ +ff00000000000000017f03014355525645000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c80548454c4c4f0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a5af606a040b3eb749b38aad3c43ea22f18b1659de5c3db1996b6f880d48e30e000000000000000108706d93931262db2e66acd402ecbe67b85aba0e696fe5d5ada13b14d2ab50917301acbb09f29f85b24c470915951d1ce2973288aca89cda8c484389bba97271a0ded05860f152a5c757b7bdd5849fa206000000000000011408494e495449415445e3d3dd23298ecbdc31d3b471a56f9235701b35296117c8f798a4013f1d3a11d4c4c254b1785ad9cc6f3e01b9387f95468fabe11bd45d73704a9653165e045c2a2baa48130e8d9d9745975fc7600c23679cc044b396a4a7f91c946acf744d072a00000000000000020e56c8aebfb1f9f33f989e76c2cebbe45e0fef500717436e64b53da90ac7d2af3ca8ebd5edbdfb896ed18126d2a4c2836861a935e089503cc5efbfc166b94e504a0cfff64c0410689fd669aa4c1b041cadb21910fe7af4323258461960d969d8c481b47959430732bdb6126d62ddf801b3db7d32c146e5b10bc83cd503fd75badad67e6370c7adb2996a3242b0210e82ef1947ab563a5333a60aaeae9fc7c968c6264a002b074d4553534147450000000000000003cc90b40b5bc78bcb00f8ad69dc162c4b387cf8b9ab2d334455b3a9 \ No newline at end of file diff --git a/tests/fuzzer_corpora/test_bind_null_fuzzer.txt b/tests/fuzzer_corpora/test_bind_null_fuzzer.txt new file mode 100644 index 00000000..de06a338 --- /dev/null +++ b/tests/fuzzer_corpora/test_bind_null_fuzzer.txt @@ -0,0 +1 @@ +ff00000000000000017f03014e554c4c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004190552454144590b536f636b65742d5479706500000003535542040a09535542534352494245 \ No newline at end of file diff --git a/tests/fuzzer_corpora/test_connect_curve_fuzzer.txt b/tests/fuzzer_corpora/test_connect_curve_fuzzer.txt new file mode 100644 index 00000000..2b8010a5 --- /dev/null +++ b/tests/fuzzer_corpora/test_connect_curve_fuzzer.txt @@ -0,0 +1 @@ +ff00000000000000017f03014355525645000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a80757454c434f4d45b82e58be6c89e876fd91e236488b8d4bb37846d2cd38e739264ea8ff66ca06f5930d70920b8429505844f064be5d8c977078b8dd135e340cd8c7f276293ebf9c45bd6b240c0d9c15b8fde2defb11f4f49c864695f07c3456e855dd7eef6ee371dbda86b05f3db853781ce741ecacffcc50a7a8176ec8ebd3c2692beb79977b143f90c77a13eee6ed5282ee27ad45f36fd08a31ecd205dfa9e7b6887fe2ea1ccc0432055245414459000000000000000171f53d3270589095cd304baeedfe0a326ed8488d0173dded2dca5b996d0d31e6469d5f4e0026074d4553534147450000000000000002fbb1f34c99e8d2f5a2cb37dfb01b0a74f2d1c0439f5c0026074d455353414745000000000000000388166e23cf25cda98b8b3f635039b468bb222cbfec30 \ No newline at end of file diff --git a/tests/fuzzer_corpora/test_connect_null_fuzzer.txt b/tests/fuzzer_corpora/test_connect_null_fuzzer.txt new file mode 100644 index 00000000..bba32603 --- /dev/null +++ b/tests/fuzzer_corpora/test_connect_null_fuzzer.txt @@ -0,0 +1 @@ +ff00000000000000017f03014e554c4c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a0552454144590b536f636b65742d547970650000000458505542000568656c6c6f0005776f726c64 \ No newline at end of file diff --git a/tests/fuzzer_corpora/zmtp.dict b/tests/fuzzer_corpora/zmtp.dict new file mode 100644 index 00000000..74a88e3d --- /dev/null +++ b/tests/fuzzer_corpora/zmtp.dict @@ -0,0 +1,45 @@ +# ZMTP magic binary streams +# 1.0 https://rfc.zeromq.org/spec/13/ +# 2.0 https://rfc.zeromq.org/spec/15/ +# 3.1 https://rfc.zeromq.org/spec/37/ +# curve https://rfc.zeromq.org/spec/26/ +signature="\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x7F" +signature_v1="\x01\x00" +version_v2="\x01" +version_v3="\x03\x00" +version_v3_1="\x03\x01" +mechanism_null="\x4E\x55\x4C\x4C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +mechanism_curve="\x43\x55\x52\x56\x45\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +short_command="\x04" +long_command="\x06" +ready="\x05\x52\x45\x41\x44\x59" +error="\x05\x45\x52\x52\x4F\x52" +identity="\x08\x49\x64\x65\x6E\x74\x69\x74\x79" +socket_type="\x0B\x53\x6F\x63\x6B\x65\x74\x2D\x54\x79\x70\x65" +dealer="\x06\x44\x45\x41\x4C\x45\x52" +router="\x06\x52\x4F\x55\x54\x45\x52" +pub="\x03\x50\x55\x42" +sub="\x03\x53\x55\x42" +xpub="\x04\x58\x50\x55\x42" +xsub="\x04\x58\x53\x55\x42" +req="\x03\x52\x45\x51" +rep="\x03\x52\x45\x50" +push="\x04\x50\x55\x53\x48" +pull="\x04\x50\x55\x4C\x4C" +pair="\x04\x50\x41\x49\x52" +client="\x05\x43\x4C\x49\x45\x4E\x54" +server="\x05\x53\x45\x52\x56\x45\x52" +radio="\x05\x52\x41\x44\x49\x4F" +dish="\x04\x44\x49\x53\x48" +scatter="\x06\x53\x43\x41\x54\x54\x45\x52" +gather="\x06\x47\x41\x54\x48\x45\x52" +subscribe="\x09\x53\x55\x42\x53\x43\x52\x49\x42\x45" +cancel="\x06\x43\x41\x4E\x43\x45\x4C" +join="\x04\x4A\x4F\x49\x4E" +leave="\x05\x43\x41\x4E\x43\x45\x4C" +ping="\x04\x50\x49\x4E\x47" +pong="\x04\x50\x4F\x4E\x47" +hello="\x05\x48\x45\x4C\x4C\x4F\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +welcome="\x07\x57\x45\x4C\x43\x4F\x4D\x45" +initiate="\x08\x49\x4E\x49\x54\x49\x41\x54\x45" +message="\x07\x4D\x45\x53\x53\x41\x47\x45" \ No newline at end of file From 6b259224ee3b8b28768f9ed486e0204e67da4e57 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 4 May 2020 01:54:34 +0100 Subject: [PATCH 3/3] Problem: fuzzer regression tests do very little Solution: run them with the seed corpora --- tests/test_bind_curve_fuzzer.cpp | 28 ++++++++++++++++++++++-- tests/test_bind_null_fuzzer.cpp | 22 +++++++++++++++++-- tests/test_connect_curve_fuzzer.cpp | 29 +++++++++++++++++++++++-- tests/test_connect_null_fuzzer.cpp | 33 +++++++++++++++++++++++++---- tests/testutil.cpp | 31 +++++++++++++++++++++++++++ tests/testutil.hpp | 3 +++ 6 files changed, 136 insertions(+), 10 deletions(-) diff --git a/tests/test_bind_curve_fuzzer.cpp b/tests/test_bind_curve_fuzzer.cpp index 46548ce2..f7fcbdad 100644 --- a/tests/test_bind_curve_fuzzer.cpp +++ b/tests/test_bind_curve_fuzzer.cpp @@ -50,6 +50,22 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) my_endpoint); fd_t client = connect_socket (my_endpoint); + // If there is not enough data for a full greeting, just send what we can + // Otherwise send greeting first, as expected by the protocol + uint8_t buf[64]; + if (size >= 64) { + send (client, (void *) data, 64, MSG_NOSIGNAL); + data += 64; + size -= 64; + } + recv (client, buf, 64, 0); + if (size >= 202) { + send (client, (void *) data, 202, MSG_NOSIGNAL); + data += 202; + size -= 202; + } + recv (client, buf, 64, MSG_DONTWAIT); + msleep (250); for (ssize_t sent = 0; size > 0 && (sent != -1 || errno == EINTR); size -= sent > 0 ? sent : 0, data += sent > 0 ? sent : 0) sent = send (client, (const char *) data, size, MSG_NOSIGNAL); @@ -66,8 +82,16 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) #ifndef ZMQ_USE_FUZZING_ENGINE void test_bind_curve_fuzzer () { - TEST_ASSERT_SUCCESS_ERRNO (LLVMFuzzerTestOneInput ( - zmtp_greeting_curve, sizeof (zmtp_greeting_curve))); + uint8_t *data; + size_t len; + if (fuzzer_corpus_encode ("tests/fuzzer_corpora/test_bind_curve_fuzzer.txt", + &data, &len) + != 0) + exit (77); + + TEST_ASSERT_SUCCESS_ERRNO (LLVMFuzzerTestOneInput (data, len)); + + free (data); } int main (int argc, char **argv) diff --git a/tests/test_bind_null_fuzzer.cpp b/tests/test_bind_null_fuzzer.cpp index b94718e2..e6b13d2c 100644 --- a/tests/test_bind_null_fuzzer.cpp +++ b/tests/test_bind_null_fuzzer.cpp @@ -44,6 +44,16 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) bind_loopback_ipv4 (server, my_endpoint, sizeof (my_endpoint)); fd_t client = connect_socket (my_endpoint); + // If there is not enough data for a full greeting, just send what we can + // Otherwise send greeting first, as expected by the protocol + uint8_t buf[64]; + if (size >= 64) { + send (client, (void *) data, 64, MSG_NOSIGNAL); + data += 64; + size -= 64; + } + recv (client, buf, 64, 0); + msleep (250); for (ssize_t sent = 0; size > 0 && (sent != -1 || errno == EINTR); size -= sent > 0 ? sent : 0, data += sent > 0 ? sent : 0) sent = send (client, (const char *) data, size, MSG_NOSIGNAL); @@ -60,8 +70,16 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) #ifndef ZMQ_USE_FUZZING_ENGINE void test_bind_null_fuzzer () { - TEST_ASSERT_SUCCESS_ERRNO ( - LLVMFuzzerTestOneInput (zmtp_greeting_null, sizeof (zmtp_greeting_null))); + uint8_t *data; + size_t len; + if (fuzzer_corpus_encode ("tests/fuzzer_corpora/test_bind_null_fuzzer.txt", + &data, &len) + != 0) + exit (77); + + TEST_ASSERT_SUCCESS_ERRNO (LLVMFuzzerTestOneInput (data, len)); + + free (data); } int main (int argc, char **argv) diff --git a/tests/test_connect_curve_fuzzer.cpp b/tests/test_connect_curve_fuzzer.cpp index aa03e0d4..bf78741f 100644 --- a/tests/test_connect_curve_fuzzer.cpp +++ b/tests/test_connect_curve_fuzzer.cpp @@ -51,11 +51,28 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) fd_t server_accept = TEST_ASSERT_SUCCESS_RAW_ERRNO (accept (server, NULL, NULL)); + + // If there is not enough data for a full greeting, just send what we can + // Otherwise send greeting first, as expected by the protocol + uint8_t buf[64]; + if (size >= 64) { + send (server_accept, (void *) data, 64, MSG_NOSIGNAL); + data += 64; + size -= 64; + } + recv (server_accept, buf, 64, 0); + msleep (250); for (ssize_t sent = 0; size > 0 && (sent != -1 || errno == EINTR); size -= sent > 0 ? sent : 0, data += sent > 0 ? sent : 0) sent = send (server_accept, (const char *) data, size, MSG_NOSIGNAL); + recv (server_accept, buf, 64, MSG_DONTWAIT); msleep (250); + zmq_msg_t msg; + zmq_msg_init (&msg); + while (-1 != zmq_msg_recv (&msg, client, ZMQ_DONTWAIT)) + zmq_msg_close (&msg); + close (server_accept); close (server); @@ -69,8 +86,16 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) #ifndef ZMQ_USE_FUZZING_ENGINE void test_connect_curve_fuzzer () { - TEST_ASSERT_SUCCESS_ERRNO (LLVMFuzzerTestOneInput ( - zmtp_greeting_curve, sizeof (zmtp_greeting_curve))); + uint8_t *data; + size_t len; + if (fuzzer_corpus_encode ( + "tests/fuzzer_corpora/test_connect_curve_fuzzer.txt", &data, &len) + != 0) + exit (77); + + TEST_ASSERT_SUCCESS_ERRNO (LLVMFuzzerTestOneInput (data, len)); + + free (data); } int main (int argc, char **argv) diff --git a/tests/test_connect_null_fuzzer.cpp b/tests/test_connect_null_fuzzer.cpp index e078b49f..57865456 100644 --- a/tests/test_connect_null_fuzzer.cpp +++ b/tests/test_connect_null_fuzzer.cpp @@ -42,16 +42,33 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) char my_endpoint[MAX_SOCKET_STRING]; fd_t server = bind_socket_resolve_port ("127.0.0.1", "0", my_endpoint); - void *client = test_context_socket (ZMQ_PUB); + void *client = test_context_socket (ZMQ_SUB); + TEST_ASSERT_SUCCESS_ERRNO (zmq_setsockopt (client, ZMQ_SUBSCRIBE, "", 0)); TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client, my_endpoint)); fd_t server_accept = TEST_ASSERT_SUCCESS_RAW_ERRNO (accept (server, NULL, NULL)); + + // If there is not enough data for a full greeting, just send what we can + // Otherwise send greeting first, as expected by the protocol + uint8_t buf[64]; + if (size >= 64) { + send (server_accept, (void *) data, 64, MSG_NOSIGNAL); + data += 64; + size -= 64; + } + recv (server_accept, buf, 64, 0); + msleep (250); for (ssize_t sent = 0; size > 0 && (sent != -1 || errno == EINTR); size -= sent > 0 ? sent : 0, data += sent > 0 ? sent : 0) sent = send (server_accept, (const char *) data, size, MSG_NOSIGNAL); msleep (250); + zmq_msg_t msg; + zmq_msg_init (&msg); + while (-1 != zmq_msg_recv (&msg, client, ZMQ_DONTWAIT)) + zmq_msg_close (&msg); + close (server_accept); close (server); @@ -64,13 +81,21 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) #ifndef ZMQ_USE_FUZZING_ENGINE void test_connect_null_fuzzer () { - TEST_ASSERT_SUCCESS_ERRNO ( - LLVMFuzzerTestOneInput (zmtp_greeting_null, sizeof (zmtp_greeting_null))); + uint8_t *data; + size_t len; + if (fuzzer_corpus_encode ( + "tests/fuzzer_corpora/test_connect_null_fuzzer.txt", &data, &len) + != 0) + exit (77); + + TEST_ASSERT_SUCCESS_ERRNO (LLVMFuzzerTestOneInput (data, len)); + + free (data); } int main (int argc, char **argv) { - setup_test_environment (); + setup_test_environment (0); UNITY_BEGIN (); RUN_TEST (test_connect_null_fuzzer); diff --git a/tests/testutil.cpp b/tests/testutil.cpp index 4becce81..f73dc1db 100644 --- a/tests/testutil.cpp +++ b/tests/testutil.cpp @@ -522,3 +522,34 @@ bool strneq (const char *lhs_, const char *rhs_) { return strcmp (lhs_, rhs_) != 0; } + +int fuzzer_corpus_encode (const char *filename, uint8_t **data, size_t *len) +{ + TEST_ASSERT_NOT_NULL (filename); + TEST_ASSERT_NOT_NULL (data); + TEST_ASSERT_NOT_NULL (len); + FILE *f = fopen (filename, "r"); + if (!f) + return -1; + fseek (f, 0, SEEK_END); + size_t text_len = ftell (f); + fseek (f, 0, SEEK_SET); + char *buf = (char *) malloc (text_len); + TEST_ASSERT_NOT_NULL (buf); + size_t read = fread (buf, 1, text_len, f); + fclose (f); + TEST_ASSERT_EQUAL_INT (read, text_len); + + // Convert to binary format, corpus is stored in ascii (hex) + *len = text_len / 2; + *data = (unsigned char *) malloc (*len); + TEST_ASSERT_NOT_NULL (*data); + const char *pos = buf; + for (size_t count = 0; count < *len; ++count, pos += 2) { + char tmp[3] = {pos[0], pos[1], 0}; + (*data)[count] = (uint8_t) strtol (tmp, NULL, 16); + } + free (buf); + + return 0; +} diff --git a/tests/testutil.hpp b/tests/testutil.hpp index 2d6a1c5b..b6992fe4 100644 --- a/tests/testutil.hpp +++ b/tests/testutil.hpp @@ -44,6 +44,7 @@ #else #include #include +#include #endif // This defines the settle time used in tests; raise this if we @@ -227,4 +228,6 @@ fd_t bind_socket_resolve_port (const char *address_, const int af_ = AF_INET, const int protocol_ = IPPROTO_TCP); +int fuzzer_corpus_encode (const char *filename, uint8_t **data, size_t *len); + #endif