From c8a837521b92082d47f986f61ec9ec0263146b7c Mon Sep 17 00:00:00 2001 From: Maurice Kalinowski Date: Wed, 17 Apr 2019 13:30:04 +0200 Subject: Add libfuzzer test for receiving data Change-Id: If08d050a92db812e95a668d141d8774e56f9516e Reviewed-by: Robert Loehning Reviewed-by: Alex Blasche --- tests/libfuzzer/mqtt/data_receive/data_receive.pro | 8 ++ tests/libfuzzer/mqtt/data_receive/main.cpp | 145 +++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 tests/libfuzzer/mqtt/data_receive/data_receive.pro create mode 100644 tests/libfuzzer/mqtt/data_receive/main.cpp diff --git a/tests/libfuzzer/mqtt/data_receive/data_receive.pro b/tests/libfuzzer/mqtt/data_receive/data_receive.pro new file mode 100644 index 0000000..1b9e3bc --- /dev/null +++ b/tests/libfuzzer/mqtt/data_receive/data_receive.pro @@ -0,0 +1,8 @@ +QT -= gui +QT += mqtt testlib +QT += mqtt-private + +SOURCES += \ + main.cpp + +LIBS += -fsanitize=fuzzer diff --git a/tests/libfuzzer/mqtt/data_receive/main.cpp b/tests/libfuzzer/mqtt/data_receive/main.cpp new file mode 100644 index 0000000..307db64 --- /dev/null +++ b/tests/libfuzzer/mqtt/data_receive/main.cpp @@ -0,0 +1,145 @@ +/****************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtMqtt module. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +******************************************************************************/ +#include + +#include +#include +#include + +Q_DECLARE_METATYPE(QMqttSubscription::SubscriptionState) + +class FakeServer : public QIODevice +{ + Q_OBJECT +public: + explicit FakeServer(QObject *parent); + + qint64 readData(char *data, qint64 maxlen) override; + qint64 writeData(const char *data, qint64 len) override; + + int m_messageState{0}; + QByteArray m_readBuffer; +}; + +FakeServer::FakeServer(QObject *parent) + : QIODevice(parent) +{ + m_messageState = 0; + open(QIODevice::ReadWrite | QIODevice::Unbuffered); +} + +qint64 FakeServer::readData(char *data, qint64 maxlen) +{ + const qint64 dataToWrite = qMax(0, qMin(maxlen, m_readBuffer.size())); + memcpy(data, m_readBuffer.constData(), static_cast(dataToWrite)); + m_readBuffer = m_readBuffer.mid(static_cast(dataToWrite)); + return dataToWrite; +} + +void bufferAppend(QByteArray &buffer, quint16 value) +{ + const quint16 msb = qToBigEndian(value); + const char * msb_c = reinterpret_cast(&msb); + buffer.append(msb_c, 2); +} + +qint64 FakeServer::writeData(const char *data, qint64 len) +{ + if (m_messageState == 0) { // Received CONNECT + QByteArray response; + response += 0x20; + response += quint8(2); // Payload size + response += char(0); // ackFlags + response += char(0); // result + + m_readBuffer = response; + emit readyRead(); + m_messageState = 1; + } else if (m_messageState == 1) { // Received SUBSCRIBE + // Byte 0 == 0x82 (0x80 SUBSCRIBE 0x02 standard) + quint8 msg = reinterpret_cast(data)[0]; + if (msg != quint8(0x82)) + qFatal("Expected subscribe message"); + // Byte 1+2 == ID + const quint16 id_big = *reinterpret_cast(&data[2]); + const quint16 id = qFromBigEndian(id_big); + + QMqttControlPacket packet(QMqttControlPacket::SUBACK); + packet.append(id); + const quint8 qosLevel = 1; + packet.append(static_cast(qosLevel)); + m_readBuffer = packet.serialize(); + // We need to be async to have QMqttConnection prepare its internals + QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection); + + m_messageState = 2; + } + // Ignore any follow up data + + return len; +} + +extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) +{ + static int argc = 1; + static char *argv_1[] = {qstrdup("fuzzprepare")}; + static char **argv = argv_1; + static QCoreApplication a(argc, argv); + + FakeServer serv(&a); + QMqttClient client; + client.setHostname(QLatin1String("localhost")); + client.setPort(1883); + + client.setTransport(&serv, QMqttClient::IODevice); + client.connectToHost(); + + QSignalSpy spy(&client, &QMqttClient::connected); + spy.wait(5); + + if (client.state() != QMqttClient::Connected) { + qFatal("Not able to run test propertly"); + return -1; + } + + auto sub = client.subscribe(QLatin1String("a"), 1); + qRegisterMetaType("SubscriptionState"); + QSignalSpy subSpy(sub, &QMqttSubscription::stateChanged); + spy.wait(5); + if (sub->state() != QMqttSubscription::Subscribed) + spy.wait(10); + if (sub->state() != QMqttSubscription::Subscribed) + qFatal("Could not subscribe"); + + serv.m_readBuffer = QByteArray(Data, static_cast(Size)); + QMetaObject::invokeMethod(&serv, "readyRead"); + QCoreApplication::processEvents(); + return 0; +} + +#include "main.moc" -- cgit v1.2.3