DISPATCH-2355: Implement the `format` attribute for `qd_log_impl` and other such functions to hopefully have compile-time check for format strings (#1630)
This is backported code from skupper-router, https://github.com/skupperproject/skupper-router/issues/1011
diff --git a/include/qpid/dispatch/error.h b/include/qpid/dispatch/error.h
index 0bbce40..1b4f928 100644
--- a/include/qpid/dispatch/error.h
+++ b/include/qpid/dispatch/error.h
@@ -68,7 +68,8 @@
*/
#define qd_verror(code, fmt, ap) qd_error_vimpl(code, __FILE__, __LINE__, fmt, ap)
-qd_error_t qd_error_impl(qd_error_t code, const char *file, int line, const char *fmt, ...);
+qd_error_t qd_error_impl(qd_error_t code, const char *file, int line, const char *fmt, ...)
+ __attribute__((format(printf, 4, 5)));
qd_error_t qd_error_vimpl(qd_error_t code, const char *file, int line, const char *fmt, va_list ap);
/**
@@ -117,6 +118,7 @@
*/
#define qd_error_errno(errnum, ...) qd_error_errno_impl(errnum, __FILE__, __LINE__, __VA_ARGS__)
-qd_error_t qd_error_errno_impl(int errnum, const char *file, int line, const char *fmt, ...);
+qd_error_t qd_error_errno_impl(int errnum, const char *file, int line, const char *fmt, ...)
+ __attribute__((format(printf, 4, 5)));
#endif
diff --git a/include/qpid/dispatch/log.h b/include/qpid/dispatch/log.h
index 6265029..c0c2e27 100644
--- a/include/qpid/dispatch/log.h
+++ b/include/qpid/dispatch/log.h
@@ -44,13 +44,15 @@
/**@internal*/
bool qd_log_enabled(qd_log_source_t *source, qd_log_level_t level);
/**@internal*/
-void qd_log_impl(qd_log_source_t *source, qd_log_level_t level, const char *file, int line, const char *fmt, ...);
+void qd_log_impl(qd_log_source_t *source, qd_log_level_t level, const char *file, int line, const char *fmt, ...)
+ __attribute__((format(printf, 5, 6)));
/**
* Another version of the qd_log_impl function. This function unconditionally writes the the message to the log file.
* It does not check to see if the passed in log level is enabled.
*/
-void qd_log_impl_v1(qd_log_source_t *source, qd_log_level_t level, const char *file, int line, const char *fmt, ...);
+void qd_log_impl_v1(qd_log_source_t *source, qd_log_level_t level, const char *file, int line, const char *fmt, ...)
+ __attribute__((format(printf, 5, 6)));
void qd_vlog_impl(qd_log_source_t *source, qd_log_level_t level, bool check_level, const char *file, int line, const char *fmt, va_list ap);
/** Log a message
@@ -80,6 +82,6 @@
/** Maximum length for a log message */
int qd_log_max_len();
-void qd_format_string(char* buf, int buf_size, const char *fmt, ...);
+void qd_format_string(char *buf, int buf_size, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
#endif
diff --git a/src/aprintf.h b/src/aprintf.h
index b259d44..99c424b 100644
--- a/src/aprintf.h
+++ b/src/aprintf.h
@@ -36,6 +36,6 @@
- n > 0: printing was truncated and would have printed n characters. *begin == end-1
- n < 0: error (return value of vsnprintf) no change to *begin
*/
-int aprintf(char **begin, char *end, const char *format, ...);
+int aprintf(char **begin, char *end, const char *format, ...) __attribute__((format(printf, 3, 4)));
#endif
diff --git a/src/error.c b/src/error.c
index beb4e43..7998f6b 100644
--- a/src/error.c
+++ b/src/error.c
@@ -207,7 +207,7 @@
va_start(arglist, fmt);
vaprintf(&begin, end, fmt, arglist);
va_end(arglist);
- aprintf(&begin, end, ": ", errnum);
+ aprintf(&begin, end, ": ");
char *em = ts.error_message;
if(strerror_r(errnum, begin, end - begin) != 0) {
snprintf(begin, end - begin, "Unknown error %d", errnum);
diff --git a/src/log.c b/src/log.c
index 0bada29..ab848e1 100644
--- a/src/log.c
+++ b/src/log.c
@@ -657,7 +657,7 @@
if (error_in_output) {
char msg[TEXT_MAX];
snprintf(msg, sizeof(msg), "Failed to open log file '%s'", outputFile);
- qd_error(QD_ERROR_CONFIG, msg);
+ qd_error(QD_ERROR_CONFIG, "%s", msg);
}
if (error_in_enable) {
qd_error(QD_ERROR_CONFIG, "'%s' is not a valid log level. Should be one of {%s}.", enable, level_names);
diff --git a/src/message.c b/src/message.c
index 1f9f3d6..8f810b4 100644
--- a/src/message.c
+++ b/src/message.c
@@ -343,7 +343,7 @@
char *begin = buffer;
char *end = buffer + len - sizeof(REPR_END); /* Save space for ending */
bool first = true;
- aprintf(&begin, end, "Message{", msg);
+ aprintf(&begin, end, "Message{");
print_field(msg, QD_FIELD_MESSAGE_ID, "message-id", flags, &first, &begin, end);
print_field(msg, QD_FIELD_USER_ID, "user-id", flags, &first, &begin, end);
print_field(msg, QD_FIELD_TO, "to", flags, &first, &begin, end);
diff --git a/src/python_embedded.c b/src/python_embedded.c
index 0fd6a45..a7e40bc 100644
--- a/src/python_embedded.c
+++ b/src/python_embedded.c
@@ -584,10 +584,10 @@
// Parse an iterator to a python object.
static PyObject *py_iter_parse(qd_iterator_t *iter)
{
- qd_parsed_field_t *parsed=0;
+ qd_parsed_field_t *parsed = 0;
if (iter && (parsed = qd_parse(iter))) {
if (!qd_parse_ok(parsed)) {
- qd_error(QD_ERROR_MESSAGE, qd_parse_error(parsed));
+ qd_error(QD_ERROR_MESSAGE, "%s", qd_parse_error(parsed));
qd_parse_free(parsed);
return 0;
}
diff --git a/src/router_core/core_client_api.c b/src/router_core/core_client_api.c
index c292eed..e24561f 100644
--- a/src/router_core/core_client_api.c
+++ b/src/router_core/core_client_api.c
@@ -181,8 +181,8 @@
NULL, // target terminus
&receiver_endpoint,
client);
- qd_log(core->log, QD_LOG_TRACE,
- "New core client created c=%p", client);
+ qd_log(core->log, QD_LOG_TRACE, //
+ "New core client created c=%p", (void *) client);
return client;
}
@@ -221,8 +221,8 @@
qd_hash_free(client->correlations);
free(client->reply_to);
- qd_log(client->core->log, QD_LOG_TRACE,
- "Core client freed c=%p", client);
+ qd_log(client->core->log, QD_LOG_TRACE, //
+ "Core client freed c=%p", (void *) client);
free_qdrc_client_t(client);
}
@@ -239,8 +239,8 @@
qdrc_client_request_done_CT_t done_cb)
{
qd_log(client->core->log, QD_LOG_TRACE,
- "New core client request created c=%p, rc=%p",
- client, request_context);
+ "New core client request created c=%p, rc=%p", (void *) client,
+ request_context);
qdrc_client_request_t *req = new_qdrc_client_request_t();
ZERO(req);
@@ -296,9 +296,9 @@
DEQ_REMOVE_HEAD_N(SEND_Q, client->send_queue);
req->on_send_queue = false;
- qd_log(client->core->log, QD_LOG_TRACE,
- "Core client request sent c=%p, rc=%p dlv=%p cid=%s",
- client, req->req_context, req->delivery,
+ qd_log(client->core->log, QD_LOG_TRACE, //
+ "Core client request sent c=%p, rc=%p dlv=%p cid=%s", //
+ (void *) client, req->req_context, (void *) req->delivery, //
*req->correlation_id ? req->correlation_id : "<none>");
if (!presettled && req->on_ack_cb) {
@@ -363,9 +363,9 @@
error);
}
- qd_log(client->core->log, QD_LOG_TRACE,
- "Freeing core client request c=%p, rc=%p (%s)",
- client, req->req_context,
+ qd_log(client->core->log, QD_LOG_TRACE, //
+ "Freeing core client request c=%p, rc=%p (%s)", //
+ (void *) client, req->req_context, //
error ? error : "request complete");
free_qdrc_client_request_t(req);
@@ -397,8 +397,8 @@
{
qdrc_client_t *client = (qdrc_client_t *)context;
- qd_log(client->core->log, QD_LOG_TRACE,
- "Core client sender 2nd attach c=%p", client);
+ qd_log(client->core->log, QD_LOG_TRACE, //
+ "Core client sender 2nd attach c=%p", (void *) client);
if (!client->sender_up) {
client->sender_up = true;
@@ -415,8 +415,8 @@
{
qdrc_client_t *client = (qdrc_client_t *)context;
- qd_log(client->core->log, QD_LOG_TRACE,
- "Core client receiver 2nd attach c=%p", client);
+ qd_log(client->core->log, QD_LOG_TRACE, //
+ "Core client receiver 2nd attach c=%p", (void *) client);
if (!client->receiver_up) {
client->receiver_up = true;
@@ -437,9 +437,9 @@
qdr_core_t *core = client->core;
client->tx_credit += available_credit;
- qd_log(core->log, QD_LOG_TRACE,
- "Core client sender flow granted c=%p credit=%d d=%s",
- client, client->tx_credit, (drain) ? "T" : "F");
+ qd_log(core->log, QD_LOG_TRACE, //
+ "Core client sender flow granted c=%p credit=%d d=%s", //
+ (void *) client, client->tx_credit, (drain) ? "T" : "F");
if (client->tx_credit > 0) {
_flush_send_queue_CT(client);
}
@@ -464,9 +464,9 @@
{
qdrc_client_t *client = (qdrc_client_t *)context;
- qd_log(client->core->log, QD_LOG_TRACE,
- "Core client sender update c=%p dlv=%p d=%"PRIx64" %s",
- client, delivery, disposition,
+ qd_log(client->core->log, QD_LOG_TRACE, //
+ "Core client sender update c=%p dlv=%p d=%" PRIx64 " %s", //
+ (void *) client, (void *) delivery, disposition, //
settled ? "settled" : "unsettled");
if (disposition) {
@@ -497,7 +497,7 @@
qd_log(client->core->log, QD_LOG_DEBUG,
"Core client could not find request for disposition update"
" client=%p delivery=%p",
- client, delivery);
+ (void *) client, (void *) delivery);
}
}
}
@@ -512,8 +512,8 @@
bool complete = qd_message_receive_complete(message);
qd_log(core->log, QD_LOG_TRACE,
- "Core client received msg c=%p complete=%s",
- client, complete ? "T" : "F");
+ "Core client received msg c=%p complete=%s", //
+ (void *) client, complete ? "T" : "F");
if (complete) {
uint64_t disposition = PN_ACCEPTED;
@@ -527,10 +527,9 @@
qd_hash_retrieve(client->correlations, cid_iter, (void **)&req);
qd_iterator_free(cid_iter);
if (req) {
-
qd_log(core->log, QD_LOG_TRACE,
- "Core client received msg c=%p rc=%p cid=%s",
- client, req->req_context, req->correlation_id);
+ "Core client received msg c=%p rc=%p cid=%s", //
+ (void *) client, req->req_context, req->correlation_id);
qd_hash_remove_by_handle(client->correlations, req->hash_handle);
qd_hash_handle_free(req->hash_handle);
@@ -578,8 +577,8 @@
{
qdrc_client_t *client = (qdrc_client_t *)client_context;
- qd_log(client->core->log, QD_LOG_TRACE,
- "Core client sender detached c=%p", client);
+ qd_log(client->core->log, QD_LOG_TRACE, //
+ "Core client sender detached c=%p", (void *) client);
if (client->sender_up) {
client->sender_up = false;
@@ -611,8 +610,8 @@
{
qdrc_client_t *client = (qdrc_client_t *)client_context;
- qd_log(client->core->log, QD_LOG_TRACE,
- "Core client receiver detached c=%p", client);
+ qd_log(client->core->log, QD_LOG_TRACE, //
+ "Core client receiver detached c=%p", (void *) client);
if (client->receiver_up) {
client->receiver_up = false;
diff --git a/src/router_core/modules/test_hooks/core_test_hooks.c b/src/router_core/modules/test_hooks/core_test_hooks.c
index d6cba2d..5a68b6a 100644
--- a/src/router_core/modules/test_hooks/core_test_hooks.c
+++ b/src/router_core/modules/test_hooks/core_test_hooks.c
@@ -651,7 +651,7 @@
_on_conn_event,
0, 0, 0, tc);
- qd_log(test_module->core->log, QD_LOG_TRACE, "client test registered %p", tc->conn_events);
+ qd_log(test_module->core->log, QD_LOG_TRACE, "client test registered %p", (void *) tc->conn_events);
}
diff --git a/src/server.c b/src/server.c
index 807fe78..c95e43c 100644
--- a/src/server.c
+++ b/src/server.c
@@ -633,6 +633,8 @@
/* Log the description, set the transport condition (name, description) close the transport tail. */
void connect_fail(qd_connection_t *ctx, const char *name, const char *description, ...)
+ __attribute__((format(printf, 3, 4)));
+void connect_fail(qd_connection_t *ctx, const char *name, const char *description, ...)
{
va_list ap;
va_start(ap, description);
@@ -655,7 +657,6 @@
}
}
-
/* Get the host IP address for the remote end */
static void set_rhost_port(qd_connection_t *ctx) {
pn_transport_t *tport = pn_connection_transport(ctx->pn_conn);
@@ -1081,11 +1082,11 @@
pn_condition_get_name(condition), pn_condition_get_description(condition));
strcpy(ctx->connector->conn_msg, conn_msg);
- qd_log(qd_server->log_source, QD_LOG_INFO, conn_msg);
+ qd_log(qd_server->log_source, QD_LOG_INFO, "%s", conn_msg);
} else {
qd_format_string(conn_msg, 300, "[C%"PRIu64"] Connection to %s failed", ctx->connection_id, config->host_port);
strcpy(ctx->connector->conn_msg, conn_msg);
- qd_log(qd_server->log_source, QD_LOG_INFO, conn_msg);
+ qd_log(qd_server->log_source, QD_LOG_INFO, "%s", conn_msg);
}
} else if (ctx && ctx->listener) { /* Incoming connection */
if (condition && pn_condition_is_set(condition)) {
diff --git a/src/terminus_private.h b/src/terminus_private.h
index 8757ea3..47fb178 100644
--- a/src/terminus_private.h
+++ b/src/terminus_private.h
@@ -28,7 +28,10 @@
// do pointer & length arithmetic without overflowing the destination buffer in
// qdr_terminus_format()
//
-static inline size_t safe_snprintf(char *str, size_t size, const char *format, ...) {
+static inline size_t safe_snprintf(char *str, size_t size, const char *format, ...)
+ __attribute__((format(printf, 3, 4)));
+size_t safe_snprintf(char *str, size_t size, const char *format, ...)
+{
// max size allowed must be INT_MAX (since vsnprintf returns an int)
if (size == 0 || size > INT_MAX) {
return 0;
diff --git a/tests/c_unittests/test_terminus.cpp b/tests/c_unittests/test_terminus.cpp
index 0e5057b..494d6fc 100644
--- a/tests/c_unittests/test_terminus.cpp
+++ b/tests/c_unittests/test_terminus.cpp
@@ -49,37 +49,37 @@
SUBCASE("valid_inputs") {
SUBCASE("") {
- len = safe_snprintf(output, LEN + 10, TEST_MESSAGE);
+ len = safe_snprintf(output, LEN + 10, "%s", TEST_MESSAGE);
CHECK(LEN == len);
CHECK(output == TEST_MESSAGE);
}
SUBCASE("") {
- len = safe_snprintf(output, LEN + 1, TEST_MESSAGE);
+ len = safe_snprintf(output, LEN + 1, "%s", TEST_MESSAGE);
CHECK(LEN == len);
CHECK(output == TEST_MESSAGE);
}
SUBCASE("") {
- len = safe_snprintf(output, LEN, TEST_MESSAGE);
+ len = safe_snprintf(output, LEN, "%s", TEST_MESSAGE);
CHECK(LEN - 1 == len);
CHECK(output == "somethin");
}
SUBCASE("") {
- len = safe_snprintf(output, 0, TEST_MESSAGE);
+ len = safe_snprintf(output, 0, "%s", TEST_MESSAGE);
CHECK(0 == len);
}
SUBCASE("") {
output[0] = 'a';
- len = safe_snprintf(output, 1, TEST_MESSAGE);
+ len = safe_snprintf(output, 1, "%s", TEST_MESSAGE);
CHECK(0 == len);
CHECK('\0' == output[0]);
}
SUBCASE("") {
- len = safe_snprintf(output, (int)-1, TEST_MESSAGE);
+ len = safe_snprintf(output, (int) -1, "%s", TEST_MESSAGE);
CHECK(0 == len);
}
}
@@ -90,7 +90,7 @@
vsnprintf_stub::rc = -1;
output[0] = 'a';
- len = safe_snprintf(output, LEN + 10, TEST_MESSAGE);
+ len = safe_snprintf(output, LEN + 10, "%s", TEST_MESSAGE);
CHECK(0 == len);
CHECK('\0' == output[0]);
}