* [PATCH] telemetry: fix autotest failures on Alpine
@ 2023-03-10 18:18 Bruce Richardson
2023-03-10 19:08 ` Stephen Hemminger
` (2 more replies)
0 siblings, 3 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-03-10 18:18 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson, stable, Ciara Power, Declan Doherty, Radu Nicolau
On Alpine linux, the telemetry_data_autotest was failing for the
test where we had dictionaries embedded in other dictionaries up
to three levels deep. Indications are that this issue is due to
excess data being stored on the stack, so replace stack-allocated
buffer data with dynamically allocated data in the case where we
are doing recursive processing of telemetry data structures into
json.
Bugzilla ID: 1177
Fixes: c933bb5177ca ("telemetry: support array values in data object")
Fixes: d2671e642a8e ("telemetry: support dict of dicts")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/telemetry/telemetry.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/lib/telemetry/telemetry.c b/lib/telemetry/telemetry.c
index 7bceadcee7..34d371ab8a 100644
--- a/lib/telemetry/telemetry.c
+++ b/lib/telemetry/telemetry.c
@@ -208,7 +208,9 @@ container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
break;
case RTE_TEL_CONTAINER:
{
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
const struct container *cont =
&v->value.container;
if (container_to_json(cont->data,
@@ -219,6 +221,7 @@ container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
v->name, temp);
if (!cont->keep)
rte_tel_data_free(cont->data);
+ free(temp);
break;
}
}
@@ -275,7 +278,9 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
break;
case RTE_TEL_CONTAINER:
{
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
const struct container *cont =
&v->value.container;
if (container_to_json(cont->data,
@@ -286,6 +291,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
v->name, temp);
if (!cont->keep)
rte_tel_data_free(cont->data);
+ free(temp);
}
}
}
@@ -311,7 +317,9 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
buf_len, used,
d->data.array[i].uval);
else if (d->type == TEL_ARRAY_CONTAINER) {
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
const struct container *rec_data =
&d->data.array[i].container;
if (container_to_json(rec_data->data,
@@ -321,6 +329,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
buf_len, used, temp);
if (!rec_data->keep)
rte_tel_data_free(rec_data->data);
+ free(temp);
}
break;
}
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH] telemetry: fix autotest failures on Alpine
2023-03-10 18:18 [PATCH] telemetry: fix autotest failures on Alpine Bruce Richardson
@ 2023-03-10 19:08 ` Stephen Hemminger
2023-03-13 9:38 ` Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 0/5] telemetry: remove variable length arrays Bruce Richardson
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
2 siblings, 1 reply; 25+ messages in thread
From: Stephen Hemminger @ 2023-03-10 19:08 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, stable, Ciara Power, Declan Doherty, Radu Nicolau
On Fri, 10 Mar 2023 18:18:36 +0000
Bruce Richardson <bruce.richardson@intel.com> wrote:
> On Alpine linux, the telemetry_data_autotest was failing for the
> test where we had dictionaries embedded in other dictionaries up
> to three levels deep. Indications are that this issue is due to
> excess data being stored on the stack, so replace stack-allocated
> buffer data with dynamically allocated data in the case where we
> are doing recursive processing of telemetry data structures into
> json.
>
> Bugzilla ID: 1177
> Fixes: c933bb5177ca ("telemetry: support array values in data object")
> Fixes: d2671e642a8e ("telemetry: support dict of dicts")
> Cc: stable@dpdk.org
Looking at the telemetry code:
- why so many temporary buffers, could this be streamed or redesigned
so that an allocated buffer is returned.
- why is rte_tel_json_XXX all inline? These should just be internal
functions and not in a .h file.
FYI - if this library reused existing json writer it would have
been much simpler.
https://github.com/shemminger/iproute2/blob/main/lib/json_writer.c
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH] telemetry: fix autotest failures on Alpine
2023-03-10 19:08 ` Stephen Hemminger
@ 2023-03-13 9:38 ` Bruce Richardson
0 siblings, 0 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-03-13 9:38 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev, stable, Ciara Power, Declan Doherty, Radu Nicolau
On Fri, Mar 10, 2023 at 11:08:32AM -0800, Stephen Hemminger wrote:
> On Fri, 10 Mar 2023 18:18:36 +0000
> Bruce Richardson <bruce.richardson@intel.com> wrote:
>
> > On Alpine linux, the telemetry_data_autotest was failing for the
> > test where we had dictionaries embedded in other dictionaries up
> > to three levels deep. Indications are that this issue is due to
> > excess data being stored on the stack, so replace stack-allocated
> > buffer data with dynamically allocated data in the case where we
> > are doing recursive processing of telemetry data structures into
> > json.
> >
> > Bugzilla ID: 1177
> > Fixes: c933bb5177ca ("telemetry: support array values in data object")
> > Fixes: d2671e642a8e ("telemetry: support dict of dicts")
> > Cc: stable@dpdk.org
>
> Looking at the telemetry code:
>
> - why so many temporary buffers, could this be streamed or redesigned
> so that an allocated buffer is returned.
>
This is largely what the fix patch does. The temporary buffers are used to
ensure we never end up with a partially written buffer from any truncated
snprintf, since that could cause us to emit invalid json. I think as a
scheme it works well enough for what it was designed for, but this
particular test case has more levels of recursion than was expected, so the
limit of the design are showing. The downside of using memory allocation
using malloc as here, is just more failure cases.
> - why is rte_tel_json_XXX all inline? These should just be internal
> functions and not in a .h file.
>
Sure. Since these are all just internal functions used by the library, I'm
not sure it really matters.
> FYI - if this library reused existing json writer it would have
> been much simpler.
> https://github.com/shemminger/iproute2/blob/main/lib/json_writer.c
Yep. Looked at that previously, but it's at a lower level than what we
have. IMHO, the complicated bit of producing json output is not the
formatting characters for each data-type but ensuring correct termination
of the json string even in case of errors, so each function in our
telemetry json code always includes correct terminators at all points, i.e.
we have no separate functions to be called for terminating objects, arrays
etc. - they are always included in the output of the items. This is why so
many temporary buffers are used - the input string to a function is
well-formed json, and we only actually append to that buffer by copying
from the temporary buffer once we are sure that the output will also be
similarly well-formed.
That said, if you want to replace the current json implementation with a
better one, I'm not opposed to it. [though I'd definitely rather no
external dependencies which would mean we could no longer rely on it always
being available in default builds]
/Bruce
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v2 0/5] telemetry: remove variable length arrays
2023-03-10 18:18 [PATCH] telemetry: fix autotest failures on Alpine Bruce Richardson
2023-03-10 19:08 ` Stephen Hemminger
@ 2023-04-05 15:44 ` Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
` (4 more replies)
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
2 siblings, 5 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 15:44 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
This patchset introduces a series of changes to remove variable-length
arrays from the telemetry code. The first patch replaces a VLA with
malloc memory for the serialization of the json objects contained within
the main response object, which fixes a crash observed on alpine linux.
Subsequent patches rework the json printing code to avoid the use of
temporary buffers where possible, or use malloc-allocated memory where
not.
Based off testing with the unit tests for telemetry, json serialization
for the telemetry callbacks should always use the path where a temporary
buffer is *not* used, but the allocation-case is kept to ensure that any
unexpected edge-cases are covered too.
Bruce Richardson (5):
telemetry: fix autotest failures on Alpine
telemetry: remove variable length array in printf fn
telemetry: split out body of json string format fn
telemetry: rename local variables
telemetry: remove VLA in json string format function
app/test/test_telemetry_json.c | 2 +-
lib/telemetry/telemetry.c | 21 ++++++-
lib/telemetry/telemetry_json.h | 107 +++++++++++++++++++++++++--------
3 files changed, 100 insertions(+), 30 deletions(-)
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v2 1/5] telemetry: fix autotest failures on Alpine
2023-04-05 15:44 ` [PATCH v2 0/5] telemetry: remove variable length arrays Bruce Richardson
@ 2023-04-05 15:44 ` Bruce Richardson
2023-04-07 19:21 ` Tyler Retzlaff
2023-04-05 15:44 ` [PATCH v2 2/5] telemetry: remove variable length array in printf fn Bruce Richardson
` (3 subsequent siblings)
4 siblings, 1 reply; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 15:44 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson, stable
On Alpine linux, the telemetry_data_autotest was failing for the
test where we had dictionaries embedded in other dictionaries up
to three levels deep. Indications are that this issue is due to
excess data being stored on the stack, so replace stack-allocated
buffer data with dynamically allocated data in the case where we
are doing recursive processing of telemetry data structures into
json.
Bugzilla ID: 1177
Fixes: c933bb5177ca ("telemetry: support array values in data object")
Fixes: d2671e642a8e ("telemetry: support dict of dicts")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
V2:
set '\0' in newly malloc'ed buffer to ensure it always has valid
string data.
---
lib/telemetry/telemetry.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/lib/telemetry/telemetry.c b/lib/telemetry/telemetry.c
index 7bceadcee7..728a0bffd4 100644
--- a/lib/telemetry/telemetry.c
+++ b/lib/telemetry/telemetry.c
@@ -208,7 +208,11 @@ container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
break;
case RTE_TEL_CONTAINER:
{
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
+ *temp = '\0'; /* ensure valid string */
+
const struct container *cont =
&v->value.container;
if (container_to_json(cont->data,
@@ -219,6 +223,7 @@ container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
v->name, temp);
if (!cont->keep)
rte_tel_data_free(cont->data);
+ free(temp);
break;
}
}
@@ -275,7 +280,11 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
break;
case RTE_TEL_CONTAINER:
{
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
+ *temp = '\0'; /* ensure valid string */
+
const struct container *cont =
&v->value.container;
if (container_to_json(cont->data,
@@ -286,6 +295,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
v->name, temp);
if (!cont->keep)
rte_tel_data_free(cont->data);
+ free(temp);
}
}
}
@@ -311,7 +321,11 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
buf_len, used,
d->data.array[i].uval);
else if (d->type == TEL_ARRAY_CONTAINER) {
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
+ *temp = '\0'; /* ensure valid string */
+
const struct container *rec_data =
&d->data.array[i].container;
if (container_to_json(rec_data->data,
@@ -321,6 +335,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
buf_len, used, temp);
if (!rec_data->keep)
rte_tel_data_free(rec_data->data);
+ free(temp);
}
break;
}
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v2 2/5] telemetry: remove variable length array in printf fn
2023-04-05 15:44 ` [PATCH v2 0/5] telemetry: remove variable length arrays Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
@ 2023-04-05 15:44 ` Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 3/5] telemetry: split out body of json string format fn Bruce Richardson
` (2 subsequent siblings)
4 siblings, 0 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 15:44 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
The json_snprintf function, used to add json characters on to a buffer,
leaving the buffer unmodified in case of error, used a variable length
array to store the data temporarily while checking for overflow. VLAs
can be unsafe, and are unsupported by some compilers, so remove use of
the VLA.
For the normal case where there is only a small amount of existing text
in the buffer (<4 chars) to be preserved, save that off temporarily to a
local array, and restore on error. To handle cases where there is more
than a few characters in the buffer, we use the existing logic of doing
the print to a temporary buffer initially and then copying. In this
case, though we use malloc-allocated buffer rather than VLA.
Within the unit tests, the "telemetry_data_autotests" test cases - which
mimic real telemetry use - all exercise the first path. The
telemetry_json_autotest cases work directly with generating json, and
use uninitialized buffers so also test the second, malloc-allocated
buffer, cases.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/telemetry/telemetry_json.h | 32 ++++++++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index 744bbfe053..cb18453449 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -8,6 +8,7 @@
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
+#include <stdlib.h>
#include <rte_common.h>
#include <rte_telemetry.h>
@@ -30,17 +31,40 @@ __rte_format_printf(3, 4)
static inline int
__json_snprintf(char *buf, const int len, const char *format, ...)
{
- char tmp[len];
va_list ap;
+ char tmp[4];
+ char *newbuf;
int ret;
+ if (len == 0)
+ return 0;
+
+ /* to ensure unmodified if we overflow, we save off any values currently in buf
+ * before we printf, if they are short enough. We restore them on error.
+ */
+ if (strnlen(buf, sizeof(tmp)) < sizeof(tmp)) {
+ strcpy(tmp, buf); /* strcpy is safe as we know the length */
+ va_start(ap, format);
+ ret = vsnprintf(buf, len, format, ap);
+ va_end(ap);
+ if (ret > 0 && ret < len)
+ return ret;
+ strcpy(buf, tmp); /* restore on error */
+ return 0;
+ }
+
+ /* in normal operations should never hit this, but can do if buffer is
+ * incorrectly initialized e.g. in unit test cases
+ */
va_start(ap, format);
- ret = vsnprintf(tmp, sizeof(tmp), format, ap);
+ ret = vasprintf(&newbuf, format, ap);
va_end(ap);
- if (ret > 0 && ret < (int)sizeof(tmp) && ret < len) {
- strcpy(buf, tmp);
+ if (ret > 0 && ret < len) {
+ strcpy(buf, newbuf);
+ free(newbuf);
return ret;
}
+ free(newbuf);
return 0; /* nothing written or modified */
}
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v2 3/5] telemetry: split out body of json string format fn
2023-04-05 15:44 ` [PATCH v2 0/5] telemetry: remove variable length arrays Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 2/5] telemetry: remove variable length array in printf fn Bruce Richardson
@ 2023-04-05 15:44 ` Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 4/5] telemetry: rename local variables Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 5/5] telemetry: remove VLA in json string format function Bruce Richardson
4 siblings, 0 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 15:44 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
To enable further rework to (efficiently) avoid using variable-length
arrays, we first separate out the body of the __json_format_str
function. This means that the actual VLA buffer is in the wrapper
function, and means we can reuse the actual writing code in multiple
code paths without duplication.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/telemetry/telemetry_json.h | 32 ++++++++++++++++++++++++--------
1 file changed, 24 insertions(+), 8 deletions(-)
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index cb18453449..4c1941ad15 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -76,15 +76,13 @@ static const char control_chars[0x20] = {
/**
* @internal
- * This function acts the same as __json_snprintf(buf, len, "%s%s%s", prefix, str, suffix)
- * except that it does proper escaping of "str" as necessary. Prefix and suffix should be compile-
- * time constants, or values not needing escaping.
- * Drops any invalid characters we don't support
+ * Function that does the actual printing, used by __json_format_str. Modifies buffer
+ * directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
*/
static inline int
-__json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
+__json_format_str_to_buf(char *tmp, const int len,
+ const char *prefix, const char *str, const char *suffix)
{
- char tmp[len];
int tmpidx = 0;
while (*prefix != '\0' && tmpidx < len)
@@ -119,11 +117,29 @@ __json_format_str(char *buf, const int len, const char *prefix, const char *str,
return 0;
tmp[tmpidx] = '\0';
-
- strcpy(buf, tmp);
return tmpidx;
}
+/**
+ * @internal
+ * This function acts the same as __json_snprintf(buf, len, "%s%s%s", prefix, str, suffix)
+ * except that it does proper escaping of "str" as necessary. Prefix and suffix should be compile-
+ * time constants, or values not needing escaping.
+ * Drops any invalid characters we don't support
+ */
+static inline int
+__json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
+{
+ char tmp[len];
+ int ret;
+
+ ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
+ if (ret > 0)
+ strcpy(buf, tmp);
+
+ return ret;
+}
+
/* Copies an empty array into the provided buffer. */
static inline int
rte_tel_json_empty_array(char *buf, const int len, const int used)
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v2 4/5] telemetry: rename local variables
2023-04-05 15:44 ` [PATCH v2 0/5] telemetry: remove variable length arrays Bruce Richardson
` (2 preceding siblings ...)
2023-04-05 15:44 ` [PATCH v2 3/5] telemetry: split out body of json string format fn Bruce Richardson
@ 2023-04-05 15:44 ` Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 5/5] telemetry: remove VLA in json string format function Bruce Richardson
4 siblings, 0 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 15:44 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
In the newly separated out function, rename "tmp" to "buf" to have more
meaningful variable names.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
When committing, this patch can be merged with the previous. I've kept
them separate for now, as it makes reviewing a lot easier.
---
lib/telemetry/telemetry_json.h | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index 4c1941ad15..4d725d938b 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -80,44 +80,44 @@ static const char control_chars[0x20] = {
* directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
*/
static inline int
-__json_format_str_to_buf(char *tmp, const int len,
+__json_format_str_to_buf(char *buf, const int len,
const char *prefix, const char *str, const char *suffix)
{
- int tmpidx = 0;
+ int bufidx = 0;
- while (*prefix != '\0' && tmpidx < len)
- tmp[tmpidx++] = *prefix++;
- if (tmpidx >= len)
+ while (*prefix != '\0' && bufidx < len)
+ buf[bufidx++] = *prefix++;
+ if (bufidx >= len)
return 0;
while (*str != '\0') {
if (*str < (int)RTE_DIM(control_chars)) {
int idx = *str; /* compilers don't like char type as index */
if (control_chars[idx] != 0) {
- tmp[tmpidx++] = '\\';
- tmp[tmpidx++] = control_chars[idx];
+ buf[bufidx++] = '\\';
+ buf[bufidx++] = control_chars[idx];
}
} else if (*str == '"' || *str == '\\') {
- tmp[tmpidx++] = '\\';
- tmp[tmpidx++] = *str;
+ buf[bufidx++] = '\\';
+ buf[bufidx++] = *str;
} else
- tmp[tmpidx++] = *str;
+ buf[bufidx++] = *str;
/* we always need space for (at minimum) closing quote and null character.
* Ensuring at least two free characters also means we can always take an
* escaped character like "\n" without overflowing
*/
- if (tmpidx > len - 2)
+ if (bufidx > len - 2)
return 0;
str++;
}
- while (*suffix != '\0' && tmpidx < len)
- tmp[tmpidx++] = *suffix++;
- if (tmpidx >= len)
+ while (*suffix != '\0' && bufidx < len)
+ buf[bufidx++] = *suffix++;
+ if (bufidx >= len)
return 0;
- tmp[tmpidx] = '\0';
- return tmpidx;
+ buf[bufidx] = '\0';
+ return bufidx;
}
/**
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v2 5/5] telemetry: remove VLA in json string format function
2023-04-05 15:44 ` [PATCH v2 0/5] telemetry: remove variable length arrays Bruce Richardson
` (3 preceding siblings ...)
2023-04-05 15:44 ` [PATCH v2 4/5] telemetry: rename local variables Bruce Richardson
@ 2023-04-05 15:44 ` Bruce Richardson
4 siblings, 0 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 15:44 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
Since variable length arrays (VLAs) are potentially unsecure and
unsupported by some compilers, rework the code to remove their use. As
with previous changes to remove VLAs in the telemetry code, this
function uses two methods to avoid modifying the buffer when adding to
it fails:
* if there are only a few characters in the buffer, save them off to
restore on failure, then use the buffer as-is,
* otherwise use malloc rather than a VLA to allocate a temporary buffer
and copy from that on success only.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
app/test/test_telemetry_json.c | 2 +-
lib/telemetry/telemetry_json.h | 19 +++++++++++++++++--
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/app/test/test_telemetry_json.c b/app/test/test_telemetry_json.c
index e81e3a8a98..5617eac540 100644
--- a/app/test/test_telemetry_json.c
+++ b/app/test/test_telemetry_json.c
@@ -129,7 +129,7 @@ test_string_char_escaping(void)
{
static const char str[] = "A string across\ntwo lines and \"with quotes\"!";
const char *expected = "\"A string across\\ntwo lines and \\\"with quotes\\\"!\"";
- char buf[sizeof(str) + 10];
+ char buf[sizeof(str) + 10] = "";
int used = 0;
used = rte_tel_json_str(buf, sizeof(buf), used, str);
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index 4d725d938b..fceff91842 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -130,13 +130,28 @@ __json_format_str_to_buf(char *buf, const int len,
static inline int
__json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
{
- char tmp[len];
int ret;
+ char saved[4] = "";
+ char *tmp;
+
+ if (strnlen(buf, sizeof(saved)) < sizeof(saved)) {
+ /* we have only a few bytes in buffer, so save them off to restore on error*/
+ strcpy(saved, buf);
+ ret = __json_format_str_to_buf(buf, len, prefix, str, suffix);
+ if (ret == 0)
+ strcpy(buf, saved); /* restore */
+ return ret;
+ }
+
+ tmp = malloc(len);
+ if (tmp == NULL)
+ return 0;
ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
if (ret > 0)
- strcpy(buf, tmp);
+ strcpy(buf, saved);
+ free(tmp);
return ret;
}
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 0/5] telemetry: remove variable length arrays
2023-03-10 18:18 [PATCH] telemetry: fix autotest failures on Alpine Bruce Richardson
2023-03-10 19:08 ` Stephen Hemminger
2023-04-05 15:44 ` [PATCH v2 0/5] telemetry: remove variable length arrays Bruce Richardson
@ 2023-04-05 16:03 ` Bruce Richardson
2023-04-05 16:03 ` [PATCH v3 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
` (5 more replies)
2 siblings, 6 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 16:03 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
This patchset introduces a series of changes to remove variable-length
arrays from the telemetry code. The first patch replaces a VLA with
malloc memory for the serialization of the json objects contained within
the main response object, which fixes a crash observed on alpine linux.
Subsequent patches rework the json printing code to avoid the use of
temporary buffers where possible, or use malloc-allocated memory where
not.
Based off testing with the unit tests for telemetry, json serialization
for the telemetry callbacks should always use the path where a temporary
buffer is *not* used, but the allocation-case is kept to ensure that any
unexpected edge-cases are covered too.
V3: remove use of non-standard asprintf function in patch 2.
V2: expand from single fix for Alpine, to general cleanup to remove VLAs
Bruce Richardson (5):
telemetry: fix autotest failures on Alpine
telemetry: remove variable length array in printf fn
telemetry: split out body of json string format fn
telemetry: rename local variables
telemetry: remove VLA in json string format function
app/test/test_telemetry_json.c | 2 +-
lib/telemetry/telemetry.c | 21 ++++++-
lib/telemetry/telemetry_json.h | 111 +++++++++++++++++++++++++--------
3 files changed, 104 insertions(+), 30 deletions(-)
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 1/5] telemetry: fix autotest failures on Alpine
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
@ 2023-04-05 16:03 ` Bruce Richardson
2023-04-07 19:22 ` Tyler Retzlaff
2023-04-05 16:03 ` [PATCH v3 2/5] telemetry: remove variable length array in printf fn Bruce Richardson
` (4 subsequent siblings)
5 siblings, 1 reply; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 16:03 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson, stable
On Alpine linux, the telemetry_data_autotest was failing for the
test where we had dictionaries embedded in other dictionaries up
to three levels deep. Indications are that this issue is due to
excess data being stored on the stack, so replace stack-allocated
buffer data with dynamically allocated data in the case where we
are doing recursive processing of telemetry data structures into
json.
Bugzilla ID: 1177
Fixes: c933bb5177ca ("telemetry: support array values in data object")
Fixes: d2671e642a8e ("telemetry: support dict of dicts")
Cc: stable@dpdk.org
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
V2:
set '\0' in newly malloc'ed buffer to ensure it always has valid
string data.
---
lib/telemetry/telemetry.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/lib/telemetry/telemetry.c b/lib/telemetry/telemetry.c
index 7bceadcee7..728a0bffd4 100644
--- a/lib/telemetry/telemetry.c
+++ b/lib/telemetry/telemetry.c
@@ -208,7 +208,11 @@ container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
break;
case RTE_TEL_CONTAINER:
{
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
+ *temp = '\0'; /* ensure valid string */
+
const struct container *cont =
&v->value.container;
if (container_to_json(cont->data,
@@ -219,6 +223,7 @@ container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
v->name, temp);
if (!cont->keep)
rte_tel_data_free(cont->data);
+ free(temp);
break;
}
}
@@ -275,7 +280,11 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
break;
case RTE_TEL_CONTAINER:
{
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
+ *temp = '\0'; /* ensure valid string */
+
const struct container *cont =
&v->value.container;
if (container_to_json(cont->data,
@@ -286,6 +295,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
v->name, temp);
if (!cont->keep)
rte_tel_data_free(cont->data);
+ free(temp);
}
}
}
@@ -311,7 +321,11 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
buf_len, used,
d->data.array[i].uval);
else if (d->type == TEL_ARRAY_CONTAINER) {
- char temp[buf_len];
+ char *temp = malloc(buf_len);
+ if (temp == NULL)
+ break;
+ *temp = '\0'; /* ensure valid string */
+
const struct container *rec_data =
&d->data.array[i].container;
if (container_to_json(rec_data->data,
@@ -321,6 +335,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
buf_len, used, temp);
if (!rec_data->keep)
rte_tel_data_free(rec_data->data);
+ free(temp);
}
break;
}
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 2/5] telemetry: remove variable length array in printf fn
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
2023-04-05 16:03 ` [PATCH v3 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
@ 2023-04-05 16:03 ` Bruce Richardson
2023-04-07 19:25 ` Tyler Retzlaff
2023-04-05 16:03 ` [PATCH v3 3/5] telemetry: split out body of json string format fn Bruce Richardson
` (3 subsequent siblings)
5 siblings, 1 reply; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 16:03 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
The json_snprintf function, used to add json characters on to a buffer,
leaving the buffer unmodified in case of error, used a variable length
array to store the data temporarily while checking for overflow. VLAs
can be unsafe, and are unsupported by some compilers, so remove use of
the VLA.
For the normal case where there is only a small amount of existing text
in the buffer (<4 chars) to be preserved, save that off temporarily to a
local array, and restore on error. To handle cases where there is more
than a few characters in the buffer, we use the existing logic of doing
the print to a temporary buffer initially and then copying. In this
case, though we use malloc-allocated buffer rather than VLA.
Within the unit tests, the "telemetry_data_autotests" test cases - which
mimic real telemetry use - all exercise the first path. The
telemetry_json_autotest cases work directly with generating json, and
use uninitialized buffers so also test the second, malloc-allocated
buffer, cases.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
v3: remove use of non-standard vasprintf
---
lib/telemetry/telemetry_json.h | 36 ++++++++++++++++++++++++++++++----
1 file changed, 32 insertions(+), 4 deletions(-)
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index 744bbfe053..1bddd124f9 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -8,6 +8,7 @@
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
+#include <stdlib.h>
#include <rte_common.h>
#include <rte_telemetry.h>
@@ -30,17 +31,44 @@ __rte_format_printf(3, 4)
static inline int
__json_snprintf(char *buf, const int len, const char *format, ...)
{
- char tmp[len];
va_list ap;
+ char tmp[4];
+ char *newbuf;
int ret;
+ if (len == 0)
+ return 0;
+
+ /* to ensure unmodified if we overflow, we save off any values currently in buf
+ * before we printf, if they are short enough. We restore them on error.
+ */
+ if (strnlen(buf, sizeof(tmp)) < sizeof(tmp)) {
+ strcpy(tmp, buf); /* strcpy is safe as we know the length */
+ va_start(ap, format);
+ ret = vsnprintf(buf, len, format, ap);
+ va_end(ap);
+ if (ret > 0 && ret < len)
+ return ret;
+ strcpy(buf, tmp); /* restore on error */
+ return 0;
+ }
+
+ /* in normal operations should never hit this, but can do if buffer is
+ * incorrectly initialized e.g. in unit test cases
+ */
+ newbuf = malloc(len);
+ if (newbuf == NULL)
+ return 0;
+
va_start(ap, format);
- ret = vsnprintf(tmp, sizeof(tmp), format, ap);
+ ret = vsnprintf(newbuf, len, format, ap);
va_end(ap);
- if (ret > 0 && ret < (int)sizeof(tmp) && ret < len) {
- strcpy(buf, tmp);
+ if (ret > 0 && ret < len) {
+ strcpy(buf, newbuf);
+ free(newbuf);
return ret;
}
+ free(newbuf);
return 0; /* nothing written or modified */
}
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 3/5] telemetry: split out body of json string format fn
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
2023-04-05 16:03 ` [PATCH v3 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
2023-04-05 16:03 ` [PATCH v3 2/5] telemetry: remove variable length array in printf fn Bruce Richardson
@ 2023-04-05 16:03 ` Bruce Richardson
2023-04-07 19:28 ` Tyler Retzlaff
2023-04-05 16:03 ` [PATCH v3 4/5] telemetry: rename local variables Bruce Richardson
` (2 subsequent siblings)
5 siblings, 1 reply; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 16:03 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
To enable further rework to (efficiently) avoid using variable-length
arrays, we first separate out the body of the __json_format_str
function. This means that the actual VLA buffer is in the wrapper
function, and means we can reuse the actual writing code in multiple
code paths without duplication.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
lib/telemetry/telemetry_json.h | 32 ++++++++++++++++++++++++--------
1 file changed, 24 insertions(+), 8 deletions(-)
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index 1bddd124f9..aada523a27 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -80,15 +80,13 @@ static const char control_chars[0x20] = {
/**
* @internal
- * This function acts the same as __json_snprintf(buf, len, "%s%s%s", prefix, str, suffix)
- * except that it does proper escaping of "str" as necessary. Prefix and suffix should be compile-
- * time constants, or values not needing escaping.
- * Drops any invalid characters we don't support
+ * Function that does the actual printing, used by __json_format_str. Modifies buffer
+ * directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
*/
static inline int
-__json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
+__json_format_str_to_buf(char *tmp, const int len,
+ const char *prefix, const char *str, const char *suffix)
{
- char tmp[len];
int tmpidx = 0;
while (*prefix != '\0' && tmpidx < len)
@@ -123,11 +121,29 @@ __json_format_str(char *buf, const int len, const char *prefix, const char *str,
return 0;
tmp[tmpidx] = '\0';
-
- strcpy(buf, tmp);
return tmpidx;
}
+/**
+ * @internal
+ * This function acts the same as __json_snprintf(buf, len, "%s%s%s", prefix, str, suffix)
+ * except that it does proper escaping of "str" as necessary. Prefix and suffix should be compile-
+ * time constants, or values not needing escaping.
+ * Drops any invalid characters we don't support
+ */
+static inline int
+__json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
+{
+ char tmp[len];
+ int ret;
+
+ ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
+ if (ret > 0)
+ strcpy(buf, tmp);
+
+ return ret;
+}
+
/* Copies an empty array into the provided buffer. */
static inline int
rte_tel_json_empty_array(char *buf, const int len, const int used)
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 4/5] telemetry: rename local variables
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
` (2 preceding siblings ...)
2023-04-05 16:03 ` [PATCH v3 3/5] telemetry: split out body of json string format fn Bruce Richardson
@ 2023-04-05 16:03 ` Bruce Richardson
2023-04-07 19:50 ` Tyler Retzlaff
2023-04-05 16:03 ` [PATCH v3 5/5] telemetry: remove VLA in json string format function Bruce Richardson
2023-05-24 20:47 ` [PATCH v3 0/5] telemetry: remove variable length arrays Thomas Monjalon
5 siblings, 1 reply; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 16:03 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
In the newly separated out function, rename "tmp" to "buf" to have more
meaningful variable names.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
When committing, this patch can be merged with the previous. I've kept
them separate for now, as it makes reviewing a lot easier.
---
lib/telemetry/telemetry_json.h | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index aada523a27..c087b833eb 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -84,44 +84,44 @@ static const char control_chars[0x20] = {
* directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
*/
static inline int
-__json_format_str_to_buf(char *tmp, const int len,
+__json_format_str_to_buf(char *buf, const int len,
const char *prefix, const char *str, const char *suffix)
{
- int tmpidx = 0;
+ int bufidx = 0;
- while (*prefix != '\0' && tmpidx < len)
- tmp[tmpidx++] = *prefix++;
- if (tmpidx >= len)
+ while (*prefix != '\0' && bufidx < len)
+ buf[bufidx++] = *prefix++;
+ if (bufidx >= len)
return 0;
while (*str != '\0') {
if (*str < (int)RTE_DIM(control_chars)) {
int idx = *str; /* compilers don't like char type as index */
if (control_chars[idx] != 0) {
- tmp[tmpidx++] = '\\';
- tmp[tmpidx++] = control_chars[idx];
+ buf[bufidx++] = '\\';
+ buf[bufidx++] = control_chars[idx];
}
} else if (*str == '"' || *str == '\\') {
- tmp[tmpidx++] = '\\';
- tmp[tmpidx++] = *str;
+ buf[bufidx++] = '\\';
+ buf[bufidx++] = *str;
} else
- tmp[tmpidx++] = *str;
+ buf[bufidx++] = *str;
/* we always need space for (at minimum) closing quote and null character.
* Ensuring at least two free characters also means we can always take an
* escaped character like "\n" without overflowing
*/
- if (tmpidx > len - 2)
+ if (bufidx > len - 2)
return 0;
str++;
}
- while (*suffix != '\0' && tmpidx < len)
- tmp[tmpidx++] = *suffix++;
- if (tmpidx >= len)
+ while (*suffix != '\0' && bufidx < len)
+ buf[bufidx++] = *suffix++;
+ if (bufidx >= len)
return 0;
- tmp[tmpidx] = '\0';
- return tmpidx;
+ buf[bufidx] = '\0';
+ return bufidx;
}
/**
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 5/5] telemetry: remove VLA in json string format function
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
` (3 preceding siblings ...)
2023-04-05 16:03 ` [PATCH v3 4/5] telemetry: rename local variables Bruce Richardson
@ 2023-04-05 16:03 ` Bruce Richardson
2023-04-07 19:54 ` Tyler Retzlaff
2023-05-25 7:12 ` David Marchand
2023-05-24 20:47 ` [PATCH v3 0/5] telemetry: remove variable length arrays Thomas Monjalon
5 siblings, 2 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-05 16:03 UTC (permalink / raw)
To: dev; +Cc: ciara.power, roretzla, Bruce Richardson
Since variable length arrays (VLAs) are potentially insecure and
unsupported by some compilers, rework the code to remove their use. As
with previous changes to remove VLAs in the telemetry code, this
function uses two methods to avoid modifying the buffer when adding to
it fails:
* if there are only a few characters in the buffer, save them off to
restore on failure, then use the buffer as-is,
* otherwise use malloc rather than a VLA to allocate a temporary buffer
and copy from that on success only.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
app/test/test_telemetry_json.c | 2 +-
lib/telemetry/telemetry_json.h | 19 +++++++++++++++++--
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/app/test/test_telemetry_json.c b/app/test/test_telemetry_json.c
index e81e3a8a98..5617eac540 100644
--- a/app/test/test_telemetry_json.c
+++ b/app/test/test_telemetry_json.c
@@ -129,7 +129,7 @@ test_string_char_escaping(void)
{
static const char str[] = "A string across\ntwo lines and \"with quotes\"!";
const char *expected = "\"A string across\\ntwo lines and \\\"with quotes\\\"!\"";
- char buf[sizeof(str) + 10];
+ char buf[sizeof(str) + 10] = "";
int used = 0;
used = rte_tel_json_str(buf, sizeof(buf), used, str);
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index c087b833eb..7999535848 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -134,13 +134,28 @@ __json_format_str_to_buf(char *buf, const int len,
static inline int
__json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
{
- char tmp[len];
int ret;
+ char saved[4] = "";
+ char *tmp;
+
+ if (strnlen(buf, sizeof(saved)) < sizeof(saved)) {
+ /* we have only a few bytes in buffer, so save them off to restore on error*/
+ strcpy(saved, buf);
+ ret = __json_format_str_to_buf(buf, len, prefix, str, suffix);
+ if (ret == 0)
+ strcpy(buf, saved); /* restore */
+ return ret;
+ }
+
+ tmp = malloc(len);
+ if (tmp == NULL)
+ return 0;
ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
if (ret > 0)
- strcpy(buf, tmp);
+ strcpy(buf, saved);
+ free(tmp);
return ret;
}
--
2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v2 1/5] telemetry: fix autotest failures on Alpine
2023-04-05 15:44 ` [PATCH v2 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
@ 2023-04-07 19:21 ` Tyler Retzlaff
2023-04-11 8:43 ` Bruce Richardson
0 siblings, 1 reply; 25+ messages in thread
From: Tyler Retzlaff @ 2023-04-07 19:21 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, ciara.power, stable
On Wed, Apr 05, 2023 at 04:44:10PM +0100, Bruce Richardson wrote:
> On Alpine linux, the telemetry_data_autotest was failing for the
> test where we had dictionaries embedded in other dictionaries up
> to three levels deep. Indications are that this issue is due to
> excess data being stored on the stack, so replace stack-allocated
> buffer data with dynamically allocated data in the case where we
> are doing recursive processing of telemetry data structures into
> json.
>
> Bugzilla ID: 1177
> Fixes: c933bb5177ca ("telemetry: support array values in data object")
> Fixes: d2671e642a8e ("telemetry: support dict of dicts")
> Cc: stable@dpdk.org
>
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
>
> ---
Acked-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
(one observation below)
> V2:
> set '\0' in newly malloc'ed buffer to ensure it always has valid
> string data.
> ---
> lib/telemetry/telemetry.c | 21 ++++++++++++++++++---
> 1 file changed, 18 insertions(+), 3 deletions(-)
>
> diff --git a/lib/telemetry/telemetry.c b/lib/telemetry/telemetry.c
> index 7bceadcee7..728a0bffd4 100644
> --- a/lib/telemetry/telemetry.c
> +++ b/lib/telemetry/telemetry.c
> @@ -208,7 +208,11 @@ container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
> break;
> case RTE_TEL_CONTAINER:
> {
> - char temp[buf_len];
> + char *temp = malloc(buf_len);
> + if (temp == NULL)
> + break;
> + *temp = '\0'; /* ensure valid string */
> +
> const struct container *cont =
> &v->value.container;
> if (container_to_json(cont->data,
> @@ -219,6 +223,7 @@ container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len)
> v->name, temp);
> if (!cont->keep)
> rte_tel_data_free(cont->data);
> + free(temp);
> break;
> }
> }
> @@ -275,7 +280,11 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
> break;
> case RTE_TEL_CONTAINER:
> {
> - char temp[buf_len];
> + char *temp = malloc(buf_len);
> + if (temp == NULL)
> + break;
> + *temp = '\0'; /* ensure valid string */
> +
> const struct container *cont =
> &v->value.container;
> if (container_to_json(cont->data,
> @@ -286,6 +295,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
> v->name, temp);
> if (!cont->keep)
> rte_tel_data_free(cont->data);
> + free(temp);
not expressing a preference just noticing that when
RTE_TEL_CONTAINER cases are the last case in the switch sometimes there
is an explicit break; and sometimes not.
> }
> }
> }
> @@ -311,7 +321,11 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
> buf_len, used,
> d->data.array[i].uval);
> else if (d->type == TEL_ARRAY_CONTAINER) {
> - char temp[buf_len];
> + char *temp = malloc(buf_len);
> + if (temp == NULL)
> + break;
> + *temp = '\0'; /* ensure valid string */
> +
> const struct container *rec_data =
> &d->data.array[i].container;
> if (container_to_json(rec_data->data,
> @@ -321,6 +335,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
> buf_len, used, temp);
> if (!rec_data->keep)
> rte_tel_data_free(rec_data->data);
> + free(temp);
> }
> break;
> }
> --
> 2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 1/5] telemetry: fix autotest failures on Alpine
2023-04-05 16:03 ` [PATCH v3 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
@ 2023-04-07 19:22 ` Tyler Retzlaff
0 siblings, 0 replies; 25+ messages in thread
From: Tyler Retzlaff @ 2023-04-07 19:22 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, ciara.power, stable
On Wed, Apr 05, 2023 at 05:03:22PM +0100, Bruce Richardson wrote:
> On Alpine linux, the telemetry_data_autotest was failing for the
> test where we had dictionaries embedded in other dictionaries up
> to three levels deep. Indications are that this issue is due to
> excess data being stored on the stack, so replace stack-allocated
> buffer data with dynamically allocated data in the case where we
> are doing recursive processing of telemetry data structures into
> json.
>
> Bugzilla ID: 1177
> Fixes: c933bb5177ca ("telemetry: support array values in data object")
> Fixes: d2671e642a8e ("telemetry: support dict of dicts")
> Cc: stable@dpdk.org
>
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
>
> ---
Acked-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
(ack on latest version)
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 2/5] telemetry: remove variable length array in printf fn
2023-04-05 16:03 ` [PATCH v3 2/5] telemetry: remove variable length array in printf fn Bruce Richardson
@ 2023-04-07 19:25 ` Tyler Retzlaff
0 siblings, 0 replies; 25+ messages in thread
From: Tyler Retzlaff @ 2023-04-07 19:25 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, ciara.power
On Wed, Apr 05, 2023 at 05:03:23PM +0100, Bruce Richardson wrote:
> The json_snprintf function, used to add json characters on to a buffer,
> leaving the buffer unmodified in case of error, used a variable length
> array to store the data temporarily while checking for overflow. VLAs
> can be unsafe, and are unsupported by some compilers, so remove use of
> the VLA.
>
> For the normal case where there is only a small amount of existing text
> in the buffer (<4 chars) to be preserved, save that off temporarily to a
> local array, and restore on error. To handle cases where there is more
> than a few characters in the buffer, we use the existing logic of doing
> the print to a temporary buffer initially and then copying. In this
> case, though we use malloc-allocated buffer rather than VLA.
>
> Within the unit tests, the "telemetry_data_autotests" test cases - which
> mimic real telemetry use - all exercise the first path. The
> telemetry_json_autotest cases work directly with generating json, and
> use uninitialized buffers so also test the second, malloc-allocated
> buffer, cases.
>
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
>
> ---
Acked-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 3/5] telemetry: split out body of json string format fn
2023-04-05 16:03 ` [PATCH v3 3/5] telemetry: split out body of json string format fn Bruce Richardson
@ 2023-04-07 19:28 ` Tyler Retzlaff
0 siblings, 0 replies; 25+ messages in thread
From: Tyler Retzlaff @ 2023-04-07 19:28 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, ciara.power
On Wed, Apr 05, 2023 at 05:03:24PM +0100, Bruce Richardson wrote:
> To enable further rework to (efficiently) avoid using variable-length
> arrays, we first separate out the body of the __json_format_str
> function. This means that the actual VLA buffer is in the wrapper
> function, and means we can reuse the actual writing code in multiple
> code paths without duplication.
>
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
Acked-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 4/5] telemetry: rename local variables
2023-04-05 16:03 ` [PATCH v3 4/5] telemetry: rename local variables Bruce Richardson
@ 2023-04-07 19:50 ` Tyler Retzlaff
2023-04-11 8:58 ` Bruce Richardson
0 siblings, 1 reply; 25+ messages in thread
From: Tyler Retzlaff @ 2023-04-07 19:50 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, ciara.power
On Wed, Apr 05, 2023 at 05:03:25PM +0100, Bruce Richardson wrote:
> In the newly separated out function, rename "tmp" to "buf" to have more
> meaningful variable names.
>
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
>
> ---
Acked-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
(with suggestions)
>
> When committing, this patch can be merged with the previous. I've kept
> them separate for now, as it makes reviewing a lot easier.
> ---
> lib/telemetry/telemetry_json.h | 32 ++++++++++++++++----------------
> 1 file changed, 16 insertions(+), 16 deletions(-)
>
> diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
> index aada523a27..c087b833eb 100644
> --- a/lib/telemetry/telemetry_json.h
> +++ b/lib/telemetry/telemetry_json.h
> @@ -84,44 +84,44 @@ static const char control_chars[0x20] = {
> * directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
> */
> static inline int
> -__json_format_str_to_buf(char *tmp, const int len,
> +__json_format_str_to_buf(char *buf, const int len,
> const char *prefix, const char *str, const char *suffix)
does it cascade rubbish into the caller if `len` is made to be type
`size_t` instead of `int`?
> {
> - int tmpidx = 0;
> + int bufidx = 0;
>
> - while (*prefix != '\0' && tmpidx < len)
> - tmp[tmpidx++] = *prefix++;
> - if (tmpidx >= len)
> + while (*prefix != '\0' && bufidx < len)
> + buf[bufidx++] = *prefix++;
> + if (bufidx >= len)
> return 0;
>
> while (*str != '\0') {
> if (*str < (int)RTE_DIM(control_chars)) {
> int idx = *str; /* compilers don't like char type as index */
should be `size_t` instead of `int` type for idx.
> if (control_chars[idx] != 0) {
> - tmp[tmpidx++] = '\\';
> - tmp[tmpidx++] = control_chars[idx];
> + buf[bufidx++] = '\\';
> + buf[bufidx++] = control_chars[idx];
> }
> } else if (*str == '"' || *str == '\\') {
> - tmp[tmpidx++] = '\\';
> - tmp[tmpidx++] = *str;
> + buf[bufidx++] = '\\';
> + buf[bufidx++] = *str;
> } else
> - tmp[tmpidx++] = *str;
> + buf[bufidx++] = *str;
> /* we always need space for (at minimum) closing quote and null character.
> * Ensuring at least two free characters also means we can always take an
> * escaped character like "\n" without overflowing
> */
> - if (tmpidx > len - 2)
> + if (bufidx > len - 2)
> return 0;
> str++;
> }
>
> - while (*suffix != '\0' && tmpidx < len)
> - tmp[tmpidx++] = *suffix++;
> - if (tmpidx >= len)
> + while (*suffix != '\0' && bufidx < len)
> + buf[bufidx++] = *suffix++;
> + if (bufidx >= len)
> return 0;
>
> - tmp[tmpidx] = '\0';
> - return tmpidx;
> + buf[bufidx] = '\0';
> + return bufidx;
> }
>
> /**
> --
> 2.37.2
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 5/5] telemetry: remove VLA in json string format function
2023-04-05 16:03 ` [PATCH v3 5/5] telemetry: remove VLA in json string format function Bruce Richardson
@ 2023-04-07 19:54 ` Tyler Retzlaff
2023-05-25 7:12 ` David Marchand
1 sibling, 0 replies; 25+ messages in thread
From: Tyler Retzlaff @ 2023-04-07 19:54 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, ciara.power
On Wed, Apr 05, 2023 at 05:03:26PM +0100, Bruce Richardson wrote:
> Since variable length arrays (VLAs) are potentially insecure and
> unsupported by some compilers, rework the code to remove their use. As
> with previous changes to remove VLAs in the telemetry code, this
> function uses two methods to avoid modifying the buffer when adding to
> it fails:
> * if there are only a few characters in the buffer, save them off to
> restore on failure, then use the buffer as-is,
> * otherwise use malloc rather than a VLA to allocate a temporary buffer
> and copy from that on success only.
>
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
Acked-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v2 1/5] telemetry: fix autotest failures on Alpine
2023-04-07 19:21 ` Tyler Retzlaff
@ 2023-04-11 8:43 ` Bruce Richardson
0 siblings, 0 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-11 8:43 UTC (permalink / raw)
To: Tyler Retzlaff; +Cc: dev, ciara.power, stable
On Fri, Apr 07, 2023 at 12:21:16PM -0700, Tyler Retzlaff wrote:
> On Wed, Apr 05, 2023 at 04:44:10PM +0100, Bruce Richardson wrote:
> > On Alpine linux, the telemetry_data_autotest was failing for the
> > test where we had dictionaries embedded in other dictionaries up
> > to three levels deep. Indications are that this issue is due to
> > excess data being stored on the stack, so replace stack-allocated
> > buffer data with dynamically allocated data in the case where we
> > are doing recursive processing of telemetry data structures into
> > json.
> >
> > Bugzilla ID: 1177
> > Fixes: c933bb5177ca ("telemetry: support array values in data object")
> > Fixes: d2671e642a8e ("telemetry: support dict of dicts")
> > Cc: stable@dpdk.org
> >
> > Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> >
> > ---
> Acked-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
>
> (one observation below)
>
> > V2:
> > set '\0' in newly malloc'ed buffer to ensure it always has valid
> > string data.
> > ---
<snip>
> > @@ -286,6 +295,7 @@ output_json(const char *cmd, const struct rte_tel_data *d, int s)
> > v->name, temp);
> > if (!cont->keep)
> > rte_tel_data_free(cont->data);
> > + free(temp);
>
> not expressing a preference just noticing that when
> RTE_TEL_CONTAINER cases are the last case in the switch sometimes there
> is an explicit break; and sometimes not.
>
I won't do a new patch revision just for that, but if I end up doing one
for other reasons I'll try and remember to make it more consistent.
thanks,
/Bruce
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 4/5] telemetry: rename local variables
2023-04-07 19:50 ` Tyler Retzlaff
@ 2023-04-11 8:58 ` Bruce Richardson
0 siblings, 0 replies; 25+ messages in thread
From: Bruce Richardson @ 2023-04-11 8:58 UTC (permalink / raw)
To: Tyler Retzlaff; +Cc: dev, ciara.power
On Fri, Apr 07, 2023 at 12:50:06PM -0700, Tyler Retzlaff wrote:
> On Wed, Apr 05, 2023 at 05:03:25PM +0100, Bruce Richardson wrote:
> > In the newly separated out function, rename "tmp" to "buf" to have more
> > meaningful variable names.
> >
> > Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> >
> > ---
> Acked-by: Tyler Retzlaff <roretzla@linux.microsoft.com>
>
> (with suggestions)
>
> >
> > When committing, this patch can be merged with the previous. I've kept
> > them separate for now, as it makes reviewing a lot easier.
> > ---
> > lib/telemetry/telemetry_json.h | 32 ++++++++++++++++----------------
> > 1 file changed, 16 insertions(+), 16 deletions(-)
> >
> > diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
> > index aada523a27..c087b833eb 100644
> > --- a/lib/telemetry/telemetry_json.h
> > +++ b/lib/telemetry/telemetry_json.h
> > @@ -84,44 +84,44 @@ static const char control_chars[0x20] = {
> > * directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
> > */
> > static inline int
> > -__json_format_str_to_buf(char *tmp, const int len,
> > +__json_format_str_to_buf(char *buf, const int len,
> > const char *prefix, const char *str, const char *suffix)
>
> does it cascade rubbish into the caller if `len` is made to be type
> `size_t` instead of `int`?
>
> > {
> > - int tmpidx = 0;
> > + int bufidx = 0;
> >
> > - while (*prefix != '\0' && tmpidx < len)
> > - tmp[tmpidx++] = *prefix++;
> > - if (tmpidx >= len)
> > + while (*prefix != '\0' && bufidx < len)
> > + buf[bufidx++] = *prefix++;
> > + if (bufidx >= len)
> > return 0;
> >
> > while (*str != '\0') {
> > if (*str < (int)RTE_DIM(control_chars)) {
> > int idx = *str; /* compilers don't like char type as index */
>
> should be `size_t` instead of `int` type for idx.
>
Agreed. However, trying to keep this as a pure rename only patch, so I
don't plan on fixing that here. If I do a new version of the series, I'll
see if I can work it into the follow-on patch easily.
/Bruce
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 0/5] telemetry: remove variable length arrays
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
` (4 preceding siblings ...)
2023-04-05 16:03 ` [PATCH v3 5/5] telemetry: remove VLA in json string format function Bruce Richardson
@ 2023-05-24 20:47 ` Thomas Monjalon
5 siblings, 0 replies; 25+ messages in thread
From: Thomas Monjalon @ 2023-05-24 20:47 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, ciara.power, roretzla
05/04/2023 18:03, Bruce Richardson:
> This patchset introduces a series of changes to remove variable-length
> arrays from the telemetry code. The first patch replaces a VLA with
> malloc memory for the serialization of the json objects contained within
> the main response object, which fixes a crash observed on alpine linux.
>
> Subsequent patches rework the json printing code to avoid the use of
> temporary buffers where possible, or use malloc-allocated memory where
> not.
>
> Based off testing with the unit tests for telemetry, json serialization
> for the telemetry callbacks should always use the path where a temporary
> buffer is *not* used, but the allocation-case is kept to ensure that any
> unexpected edge-cases are covered too.
>
> V3: remove use of non-standard asprintf function in patch 2.
>
> V2: expand from single fix for Alpine, to general cleanup to remove VLAs
>
> Bruce Richardson (5):
> telemetry: fix autotest failures on Alpine
> telemetry: remove variable length array in printf fn
> telemetry: split out body of json string format fn
> telemetry: rename local variables
> telemetry: remove VLA in json string format function
Applied, thanks.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 5/5] telemetry: remove VLA in json string format function
2023-04-05 16:03 ` [PATCH v3 5/5] telemetry: remove VLA in json string format function Bruce Richardson
2023-04-07 19:54 ` Tyler Retzlaff
@ 2023-05-25 7:12 ` David Marchand
1 sibling, 0 replies; 25+ messages in thread
From: David Marchand @ 2023-05-25 7:12 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, ciara.power, roretzla, Thomas Monjalon
On Wed, Apr 5, 2023 at 6:05 PM Bruce Richardson
<bruce.richardson@intel.com> wrote:
>
> Since variable length arrays (VLAs) are potentially insecure and
> unsupported by some compilers, rework the code to remove their use. As
> with previous changes to remove VLAs in the telemetry code, this
> function uses two methods to avoid modifying the buffer when adding to
> it fails:
> * if there are only a few characters in the buffer, save them off to
> restore on failure, then use the buffer as-is,
> * otherwise use malloc rather than a VLA to allocate a temporary buffer
> and copy from that on success only.
>
> Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
> ---
> app/test/test_telemetry_json.c | 2 +-
> lib/telemetry/telemetry_json.h | 19 +++++++++++++++++--
> 2 files changed, 18 insertions(+), 3 deletions(-)
This change triggers a unit test failure ("interestingly" only with
gcc, I can't reproduce with clang).
$ ninja -C build-gcc && ./build-gcc/app/test/dpdk-test --no-huge -m
2048 --iova=va -- telemetry_json_autotest
ninja: Entering directory `build-gcc'
ninja: no work to do.
EAL: Detected CPU lcores: 16
EAL: Detected NUMA nodes: 1
EAL: Detected shared linkage of DPDK
EAL: Multi-process socket /run/user/114840/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'VA'
APP: HPET is not enabled, using TSC as default timer
RTE>>telemetry_json_autotest
test_basic_array: buf = '["meaning of life",42]', expected =
'["meaning of life",42]'
OK
test_basic_obj: buf = '{"weddings":4,"funerals":1}', expected =
'{"weddings":4,"funerals":1}'
OK
test_overflow_array: buf = '', expected = '["Arsenal","Chelsea"]'
ERROR
Test Failed
I guess we need:
diff --git a/lib/telemetry/telemetry_json.h b/lib/telemetry/telemetry_json.h
index 7999535848..7a246deacb 100644
--- a/lib/telemetry/telemetry_json.h
+++ b/lib/telemetry/telemetry_json.h
@@ -153,7 +153,7 @@ __json_format_str(char *buf, const int len, const
char *prefix, const char *str,
ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
if (ret > 0)
- strcpy(buf, saved);
+ strcpy(buf, tmp);
free(tmp);
return ret;
--
David Marchand
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2023-05-25 7:13 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-10 18:18 [PATCH] telemetry: fix autotest failures on Alpine Bruce Richardson
2023-03-10 19:08 ` Stephen Hemminger
2023-03-13 9:38 ` Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 0/5] telemetry: remove variable length arrays Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
2023-04-07 19:21 ` Tyler Retzlaff
2023-04-11 8:43 ` Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 2/5] telemetry: remove variable length array in printf fn Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 3/5] telemetry: split out body of json string format fn Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 4/5] telemetry: rename local variables Bruce Richardson
2023-04-05 15:44 ` [PATCH v2 5/5] telemetry: remove VLA in json string format function Bruce Richardson
2023-04-05 16:03 ` [PATCH v3 0/5] telemetry: remove variable length arrays Bruce Richardson
2023-04-05 16:03 ` [PATCH v3 1/5] telemetry: fix autotest failures on Alpine Bruce Richardson
2023-04-07 19:22 ` Tyler Retzlaff
2023-04-05 16:03 ` [PATCH v3 2/5] telemetry: remove variable length array in printf fn Bruce Richardson
2023-04-07 19:25 ` Tyler Retzlaff
2023-04-05 16:03 ` [PATCH v3 3/5] telemetry: split out body of json string format fn Bruce Richardson
2023-04-07 19:28 ` Tyler Retzlaff
2023-04-05 16:03 ` [PATCH v3 4/5] telemetry: rename local variables Bruce Richardson
2023-04-07 19:50 ` Tyler Retzlaff
2023-04-11 8:58 ` Bruce Richardson
2023-04-05 16:03 ` [PATCH v3 5/5] telemetry: remove VLA in json string format function Bruce Richardson
2023-04-07 19:54 ` Tyler Retzlaff
2023-05-25 7:12 ` David Marchand
2023-05-24 20:47 ` [PATCH v3 0/5] telemetry: remove variable length arrays Thomas Monjalon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).