Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a076cbc
Fixes in MQTT-SN code
embhorn Jun 2, 2026
3886c27
Fix f-5501 MQTT-SN CONNACK
embhorn Jun 2, 2026
d9699e3
Fix f-3388 SN with MT & NB
embhorn Jun 2, 2026
3dbec7d
Add tests for f-5864
embhorn Jun 2, 2026
f77ffd8
Fix f-3137 harden SN_WillMessage
embhorn Jun 2, 2026
e3a4157
Fix f-3138 harden SN_Client_WillMsgUpdate
embhorn Jun 2, 2026
c269605
Fix f-2756 SN_Client_Subscribe false return code
embhorn Jun 2, 2026
b9771a0
Fix f-3132 SN_Client_Ping with null
embhorn Jun 8, 2026
a0ed6ea
Fix f-4518 SN_Client_HandlePacket type issue in debug
embhorn Jun 8, 2026
820e523
Fix f-5510: MQTT-SN publish encoder harden
embhorn Jun 9, 2026
c953064
Fix f-4659: topic_type check in SN_Decode_Publish
embhorn Jun 9, 2026
80d61da
Fix f-4248: SN_Decode_Publish validate packet_id with QoS>0
embhorn Jun 9, 2026
c63f381
Fix f-2332: SN_Encode_Subscribe/Unsubscribe check topicNameId
embhorn Jun 9, 2026
873fc14
Fix f-4058: SN_Decode_Header check buf_len
embhorn Jun 9, 2026
dd12b55
Fix f-3632: Add tests for SN_Decode_GWInfo
embhorn Jun 9, 2026
bd88c3b
Fix f-2333 and f-2334: SN_Encode_WillTopic/Update check willTopic
embhorn Jun 9, 2026
509b1f3
Fix f-2330: SN_Encode_Register check topicName
embhorn Jun 9, 2026
0fe135c
Fix f-3831: SN_Decode_Register bounds check rejects all valid REGISTE…
embhorn Jun 9, 2026
535a669
Fix f-5663: example use of sn_reg_callback
embhorn Jun 9, 2026
86b512b
Fix f-5664: Check payload in sn_message_cb
embhorn Jun 9, 2026
fd8ed46
Fix f-5146: publish task testing
embhorn Jun 9, 2026
cf6257f
Fix from review
embhorn Jun 10, 2026
7db3f37
Fix from review
embhorn Jun 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,6 @@ src/mqtt_broker
tests/fuzz/broker_fuzz
tests/fuzz/corpus/
tests/test_broker_connect
tests/test_mqtt_sn
tests/test_mqtt_sn_client
tests/unit_tests
1 change: 1 addition & 0 deletions examples/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \
examples/wiot/wiot.h \
examples/mqttnet.h \
examples/mqttexample.h \
examples/mqtt_log.h \
examples/mqttport.h \
examples/nbclient/nbclient.h \
examples/multithread/multithread.h \
Expand Down
127 changes: 127 additions & 0 deletions examples/mqtt_log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* mqtt_log.h
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfMQTT.
*
* wolfMQTT is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfMQTT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/

#ifndef WOLFMQTT_EXAMPLE_LOG_H
#define WOLFMQTT_EXAMPLE_LOG_H

#include "wolfmqtt/mqtt_types.h" /* for byte / word32 */

#ifdef __cplusplus
extern "C" {
#endif

/* mqtt_log_sanitize is a header-only helper, so a translation unit that
* includes it but whose log sink is compiled out (e.g. sn-multithread.c built
* without WOLFMQTT_MULTITHREAD) leaves it unreferenced. Declaring it INLINE
* keeps that quiet on GCC/Clang (-Wunused-function) and MSVC (C4505); the
* attribute is a fallback for GCC/Clang builds that set NO_INLINE, where the
* INLINE macro expands to nothing. */
#if defined(__GNUC__) || defined(__clang__)
#define MQTT_LOG_MAYBE_UNUSED __attribute__((unused))
#else
#define MQTT_LOG_MAYBE_UNUSED
#endif

/* Sanitize an untrusted string for safe logging (CWE-117 defense).
*
* MQTT/MQTT-SN strings such as a REGISTER topic name are controlled by the
* remote peer. For MQTT-SN this peer is reachable over UDP, so an attacker who
* can spoof the gateway's source address controls the bytes outright. Writing
* such a string straight to a PRINTF/log sink lets the peer inject forged log
* lines (embedded CR/LF) or hijack the operator's terminal with ANSI escape
* sequences (ESC), making malicious output indistinguishable from genuine log
* lines.
*
* This helper copies src into the caller's fixed-size dst buffer, replacing
* every control byte (anything < 0x20, plus DEL 0x7f) with a printable escape
* so the result is always safe to hand to PRINTF:
* CR -> "\r" LF -> "\n" TAB -> "\t" VT -> "\v" ESC -> "\e"
* any other control byte -> "\xHH"
*
* dst is always NUL-terminated when dstLen > 0. Output is truncated to fit dst
* (never overflowed) and a multi-byte escape is emitted all-or-nothing, so the
* result never ends in a dangling backslash. Returns dst so the call can be
* used inline as a PRINTF argument. A NULL src yields "(null)".
*/
MQTT_LOG_MAYBE_UNUSED
static INLINE const char* mqtt_log_sanitize(char* dst, word32 dstLen,
const char* src)
{
static const char hex_digits[] = "0123456789abcdef";
word32 di = 0;

if (dst == NULL || dstLen == 0) {
return dst;
}
if (src == NULL) {
src = "(null)";
}

while (*src != '\0') {
byte c = (byte)*src++;
char rep[4];
word32 repLen = 0;

switch (c) {
case '\r': rep[0] = '\\'; rep[1] = 'r'; repLen = 2; break;
case '\n': rep[0] = '\\'; rep[1] = 'n'; repLen = 2; break;
case '\t': rep[0] = '\\'; rep[1] = 't'; repLen = 2; break;
case '\v': rep[0] = '\\'; rep[1] = 'v'; repLen = 2; break;
case 0x1b: rep[0] = '\\'; rep[1] = 'e'; repLen = 2; break;
default:
if (c < 0x20 || c == 0x7f) {
/* Generic non-printable control byte -> \xHH */
rep[0] = '\\';
rep[1] = 'x';
rep[2] = hex_digits[(c >> 4) & 0x0f];
rep[3] = hex_digits[c & 0x0f];
repLen = 4;
}
else {
rep[0] = (char)c;
repLen = 1;
}
break;
}

/* Stop before overflow, always leaving room for the NUL terminator.
* Because the check covers the whole replacement, a multi-byte escape
* is never split across the truncation boundary. */
if (di + repLen + 1 > dstLen) {
break;
}
{
word32 j;
for (j = 0; j < repLen; j++) {
dst[di++] = rep[j];
}
}
}

dst[di] = '\0';
return dst;
}

#ifdef __cplusplus
}
#endif

#endif /* WOLFMQTT_EXAMPLE_LOG_H */
9 changes: 7 additions & 2 deletions examples/sn-client/sn-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "sn-client.h"
#include "examples/mqttnet.h"
#include "examples/mqtt_log.h"

/* Locals */
static int mStopRead = 0;
Expand All @@ -46,6 +47,7 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg,
byte msg_new, byte msg_done)
{
byte buf[PRINT_BUFFER_SIZE+1];
char safebuf[PRINT_BUFFER_SIZE+1];
word32 len;
word16 topicId;
MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx;
Expand Down Expand Up @@ -86,7 +88,8 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg,
XMEMCPY(buf, msg->buffer, len);
buf[len] = '\0'; /* Make sure its null terminated */
PRINTF("........Payload (%d - %d): %s",
msg->buffer_pos, msg->buffer_pos + len, buf);
msg->buffer_pos, msg->buffer_pos + len,
mqtt_log_sanitize(safebuf, (word32)sizeof(safebuf), (char*)buf));

if (msg_done) {
PRINTF("....MQTT-SN Message: Done");
Expand All @@ -100,7 +103,9 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg,
assigns a new topic ID to a topic name. */
static int sn_reg_callback(word16 topicId, const char* topicName, void *ctx)
{
PRINTF("MQTT-SN Register CB: New topic ID: %hu : \"%s\"", topicId, topicName);
char safeTopic[PRINT_BUFFER_SIZE + 1];
PRINTF("MQTT-SN Register CB: New topic ID: %hu : \"%s\"", topicId,
mqtt_log_sanitize(safeTopic, (word32)sizeof(safeTopic), topicName));
(void)ctx;

return(MQTT_CODE_SUCCESS);
Expand Down
38 changes: 30 additions & 8 deletions examples/sn-client/sn-multithread.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "sn-client.h"
#include "examples/mqttnet.h"
#include "examples/mqttexample.h"
#include "examples/mqtt_log.h"

#include <stdint.h>

Expand Down Expand Up @@ -103,6 +104,7 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg,
byte msg_new, byte msg_done)
{
byte buf[PRINT_BUFFER_SIZE+1];
char safebuf[PRINT_BUFFER_SIZE+1];
word32 len;
word16 topicId;
MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx;
Expand Down Expand Up @@ -138,7 +140,8 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg,
XMEMCPY(buf, msg->buffer, len);
buf[len] = '\0'; /* Make sure its null terminated */
PRINTF("........Payload (%d - %d): %s",
msg->buffer_pos, msg->buffer_pos + len, buf);
msg->buffer_pos, msg->buffer_pos + len,
mqtt_log_sanitize(safebuf, (word32)sizeof(safebuf), (char*)buf));

if (msg_done) {
PRINTF("....MQTT-SN Message: Done");
Expand Down Expand Up @@ -192,7 +195,9 @@ static void client_disconnect(MQTTCtx *mqttCtx)
assigns a new topic ID to a topic name. */
static int sn_reg_callback(word16 topicId, const char* topicName, void *ctx)
{
PRINTF("MQTT-SN Register CB: New topic ID: %d : \"%s\"", topicId, topicName);
char safeTopic[PRINT_BUFFER_SIZE + 1];
PRINTF("MQTT-SN Register CB: New topic ID: %d : \"%s\"", topicId,
mqtt_log_sanitize(safeTopic, (word32)sizeof(safeTopic), topicName));
(void)ctx;

return(MQTT_CODE_SUCCESS);
Expand All @@ -214,8 +219,12 @@ static int client_register(MQTTCtx *mqttCtx)

PRINTF("MQTT-SN Register: topic = %s", regist.topicName);

/* Register topic name to get the assigned topic ID */
rc = SN_Client_Register(&mqttCtx->client, &regist);
/* Register topic name to get the assigned topic ID. Retry on
MQTT_CODE_CONTINUE (WOLFMQTT_NONBLOCK) so regist.pendResp is not left
linked in client->firstPendResp when this frame returns. */
do {
rc = SN_Client_Register(&mqttCtx->client, &regist);
} while (rc == MQTT_CODE_CONTINUE);

if ((rc == 0) && (regist.regack.return_code == SN_RC_ACCEPTED)) {
/* Topic ID is returned in RegAck */
Expand Down Expand Up @@ -370,7 +379,11 @@ static void *subscribe_task(void *param)
subscribe.packet_id = mqtt_get_packetid_threadsafe();

PRINTF("MQTT-SN Subscribe: topic name = %s", subscribe.topicNameId);
rc = SN_Client_Subscribe(&mqttCtx->client, &subscribe);
/* Under WOLFMQTT_NONBLOCK the call returns MQTT_CODE_CONTINUE until the
SUBACK arrives. */
do {
rc = SN_Client_Subscribe(&mqttCtx->client, &subscribe);
} while (rc == MQTT_CODE_CONTINUE);

PRINTF("....MQTT-SN Subscribe Ack: topic id = %d, rc = %d",
subscribe.subAck.topicId, (rc != 0) ? rc : subscribe.subAck.return_code);
Expand Down Expand Up @@ -498,7 +511,10 @@ static void *publish_task(void *param)
publish.buffer = (byte*)buf;
publish.total_len = (word16)XSTRLEN(buf);

rc = SN_Client_Publish(&mqttCtx->client, &publish);
/* Retry on MQTT_CODE_CONTINUE (WOLFMQTT_NONBLOCK) */
do {
rc = SN_Client_Publish(&mqttCtx->client, &publish);
} while (rc == MQTT_CODE_CONTINUE);

PRINTF("MQTT-SN Publish: topic id = %d, rc = %d\r\nPayload = %s",
(word16)*publish.topic_name,
Expand Down Expand Up @@ -530,7 +546,10 @@ static void *ping_task(void *param)
/* Keep Alive Ping */
PRINTF("Sending ping keep-alive");

rc = SN_Client_Ping(&mqttCtx->client, &ping);
/* Retry on MQTT_CODE_CONTINUE (WOLFMQTT_NONBLOCK) */
do {
rc = SN_Client_Ping(&mqttCtx->client, &ping);
} while (rc == MQTT_CODE_CONTINUE);
if (rc != MQTT_CODE_SUCCESS) {
PRINTF("MQTT-SN Ping Error: %s (%d)",
MqttClient_ReturnCodeToString(rc), rc);
Expand All @@ -552,7 +571,10 @@ static int unsubscribe_do(MQTTCtx *mqttCtx)
unsubscribe.topicNameId = mqttCtx->topic_name;
unsubscribe.packet_id = mqtt_get_packetid_threadsafe();

rc = SN_Client_Unsubscribe(&mqttCtx->client, &unsubscribe);
/* Retry on MQTT_CODE_CONTINUE (WOLFMQTT_NONBLOCK) */
do {
rc = SN_Client_Unsubscribe(&mqttCtx->client, &unsubscribe);
} while (rc == MQTT_CODE_CONTINUE);

PRINTF("MQTT Unsubscribe: %s (rc = %d)",
MqttClient_ReturnCodeToString(rc), rc);
Expand Down
9 changes: 4 additions & 5 deletions src/mqtt_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
#include "wolfmqtt/mqtt_client.h"

/* Secure memory zeroing - uses volatile pointer to prevent the compiler
* from optimizing away the stores (dead-store elimination). */
static void MqttClient_ForceZero(void* mem, word32 len)
* from optimizing away the stores (dead-store elimination).
* Declared WOLFMQTT_LOCAL in mqtt_client.h so the MQTT-SN client can reuse it
* (see SN_WillMessage) via the shared CLIENT_FORCE_ZERO macro. */
WOLFMQTT_LOCAL void MqttClient_ForceZero(void* mem, word32 len)
{
volatile byte* p = (volatile byte*)mem;
word32 i;
Expand All @@ -37,9 +39,6 @@ static void MqttClient_ForceZero(void* mem, word32 len)
}
}

#define CLIENT_FORCE_ZERO(mem, len) \
MqttClient_ForceZero(mem, (word32)(len))

/* DOCUMENTED BUILD OPTIONS:
*
* WOLFMQTT_MULTITHREAD: Enables multi-thread support with mutex protection on
Expand Down
Loading
Loading