plugin types BUGFIX ::ly_err_new() format emphasis
This commit prevents an error where err_msg could contain a conversion
specifier (character '%'), but the variadic arguments are empty, which
causes undefined behavior. Therefore, in the function, the err_msg
parameter has been renamed to err_format to emphasize that the function
expects a format. A compiler attribute has also been added to the
function to check the correct number of variadic arguments.
diff --git a/src/config.h.in b/src/config.h.in
index 65c4934..a6aff60 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -26,4 +26,10 @@
#define LYPLG_TYPE_DIR "@PLUGINS_DIR_EXTENSIONS@"
#define LYPLG_EXT_DIR "@PLUGINS_DIR_TYPES@"
+#if (@CMAKE_C_COMPILER_ID@ == GNU) || (@CMAKE_C_COMPILER_ID@ == Clang)
+# define _FORMAT_PRINTF(FORM, ARGS) __attribute__((format (printf, FORM, ARGS)))
+#else
+# define _FORMAT_PRINTF(FORM, ARGS)
+#endif
+
#endif /* LY_CONFIG_H_ */
diff --git a/src/log.c b/src/log.c
index 9cd5fd0..f38772b 100644
--- a/src/log.c
+++ b/src/log.c
@@ -118,7 +118,7 @@
}
API LY_ERR
-ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_msg, ...)
+ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_format, ...)
{
char *msg = NULL;
struct ly_err_item *e;
@@ -143,13 +143,13 @@
e->path = path;
e->apptag = apptag;
- if (err_msg) {
+ if (err_format) {
va_list print_args;
- va_start(print_args, err_msg);
+ va_start(print_args, err_format);
- if (vasprintf(&msg, err_msg, print_args) == -1) {
- /* we don't have anything more to do, just set err_msg to NULL to avoid undefined content,
+ if (vasprintf(&msg, err_format, print_args) == -1) {
+ /* we don't have anything more to do, just set msg to NULL to avoid undefined content,
* still keep the information about the original error instead of LY_EMEM or other printf's error */
msg = NULL;
}
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 24dec93..bd59b9a 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -490,13 +490,13 @@
PCRE2_UCHAR pcre2_errmsg[LY_PCRE2_MSG_LIMIT] = {0};
pcre2_get_error_message(rc, pcre2_errmsg, LY_PCRE2_MSG_LIMIT);
- return ly_err_new(err, LY_ESYS, 0, NULL, NULL, (const char *)pcre2_errmsg);
+ return ly_err_new(err, LY_ESYS, 0, NULL, NULL, "%s", (const char *)pcre2_errmsg);
} else if (((rc == PCRE2_ERROR_NOMATCH) && !patterns[u]->inverted) ||
((rc != PCRE2_ERROR_NOMATCH) && patterns[u]->inverted)) {
char *eapptag = patterns[u]->eapptag ? strdup(patterns[u]->eapptag) : NULL;
if (patterns[u]->emsg) {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, patterns[u]->emsg);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", patterns[u]->emsg);
} else {
const char *inverted = patterns[u]->inverted ? "inverted " : "";
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
@@ -523,7 +523,7 @@
if ((uint64_t)value < range->parts[u].min_u64) {
char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
if (range->emsg) {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
@@ -535,7 +535,7 @@
/* we have the last range part, so the value is out of bounds */
char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
if (range->emsg) {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
@@ -546,7 +546,7 @@
if (value < range->parts[u].min_64) {
char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
if (range->emsg) {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
@@ -557,7 +557,7 @@
/* we have the last range part, so the value is out of bounds */
char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
if (range->emsg) {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
diff --git a/src/plugins_types.h b/src/plugins_types.h
index c074f3b..1763134 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -213,13 +213,16 @@
* @param[in] vecode Validity error code in case of LY_EVALID error code.
* @param[in] path Path to the node causing the error.
* @param[in] apptag Error-app-tag value.
- * @param[in] err_msg error formating message.
+ * @param[in] err_format Format string (same like at printf) or string literal.
+ * If you want to print just an unknown string, use "%s" for the @p err_format, otherwise undefined behavior may occur
+ * because the unknown string may contain the % character, which is interpreted as conversion specifier.
* @return The given @p ecode value if the @p err is successfully created. The structure can be freed using ::ly_err_free()
* or passed back from callback into libyang.
* @return LY_EMEM If there is not enough memory for allocating error record, the @p err is not touched in that case.
* @return LY_SUCCESS if @p ecode is LY_SUCCESS, the @p err is not touched in this case.
*/
-LY_ERR ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_msg, ...);
+LY_ERR ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_format, ...)
+_FORMAT_PRINTF(6, 7);
/**
* @brief Destructor for the error records created with ::ly_err_new().
diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c
index ab5842a..19e2029 100644
--- a/src/plugins_types/leafref.c
+++ b/src/plugins_types/leafref.c
@@ -79,7 +79,7 @@
/* check leafref target existence */
if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, errmsg);
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "%s", errmsg);
free(errmsg);
return ret;
}
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index 89e81c7..e756a1b 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -167,7 +167,7 @@
type_idx = *(uint32_t *)value;
if (type_idx >= LY_ARRAY_COUNT(type_u->types)) {
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union type index %" PRIu32
- " (type count " LY_PRI_ARRAY_COUNT_TYPE ").", type_idx, LY_ARRAY_COUNT(type_u->types));
+ " (type count %" LY_PRI_ARRAY_COUNT_TYPE ").", type_idx, LY_ARRAY_COUNT(type_u->types));
goto cleanup;
}
}
diff --git a/tests/utests/data/test_types.c b/tests/utests/data/test_types.c
index cddda2a..3944486 100644
--- a/tests/utests/data/test_types.c
+++ b/tests/utests/data/test_types.c
@@ -952,6 +952,9 @@
CHECK_LOG_CTX("Invalid leafref value \"z\" - no existing target instance \"../../l[id=current()/../../../t:str-norestr]"
"[value=current()/../../../t:str-norestr]/value\".",
"Schema location /leafrefs:c/l/lr2, data location /leafrefs:c/l[id='x'][value='x']/lr2.");
+
+ data = "<lref xmlns=\"urn:tests:types\">%n</lref>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
}
static void