From 2aea4dfd7c92ebdd9f666b535787701556c5dd2a Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 11 Sep 2026 08:23:29 +1200 Subject: [PATCH 1/2] output_xml: add SAMPLE_EVENT_STRING to events table and guard array access SAMPLE_EVENT_STRING (value 26) was added to parser_sample_event_t after SAMPLE_EVENT_GASCHANGE2 (value 25), but the events[] string table in sample_cb() had only 26 entries (indices 0-25). Accessing events[26] is undefined behaviour and aborts under UBSan. Three changes: - Append "string" at index 26 so the table covers all currently defined event types. - Add a bounds guard (sizeof(events)/sizeof(events[0])) before indexing events[]. Out-of-range types fall back to "unknown" so future additions do not crash before the table is updated. - When the event type is SAMPLE_EVENT_STRING and value->event.name is non-NULL, use the dynamic name string rather than the static "string" label. This preserves the semantic intent of the type (free-form annotations such as compass heading and scrubber state from hw_ostc and Garmin parsers). Reproduces with: hw_ostc5-0001.bin, garmin_descent_mk1-0001.bin. Signed-off-by: Michael Keller --- examples/output_xml.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/examples/output_xml.c b/examples/output_xml.c index fae8b2ec..ad69a717 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -28,6 +28,21 @@ #include "output-private.h" #include "utils.h" +// AI-generated (Claude) +// Write a string to a FILE, replacing XML special characters with entities. +static void +xml_escape_text (FILE *ostream, const char *s) +{ + for (; *s; s++) { + switch (*s) { + case '&': fputs ("&", ostream); break; + case '<': fputs ("<", ostream); break; + case '>': fputs (">", ostream); break; + default: fputc (*s, ostream); break; + } + } +} + static dc_status_t dctool_xml_output_write (dctool_output_t *output, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize); static dc_status_t dctool_xml_output_free (dctool_output_t *output); @@ -98,7 +113,7 @@ sample_cb (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata "safety stop (voluntary)", "safety stop (mandatory)", "deepstop", "ceiling (safety stop)", "floor", "divetime", "maxdepth", "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning", - "gaschange2"}; + "gaschange2", "string"}; static const char *decostop[] = { "ndl", "safety", "deco", "deep"}; @@ -134,8 +149,17 @@ sample_cb (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata break; case DC_SAMPLE_EVENT: if (value->event.type != SAMPLE_EVENT_GASCHANGE && value->event.type != SAMPLE_EVENT_GASCHANGE2) { - fprintf (sampledata->ostream, " %s\n", - value->event.type, value->event.time, value->event.flags, value->event.value, events[value->event.type]); + fprintf (sampledata->ostream, " ", + value->event.type, value->event.time, value->event.flags, value->event.value); + if (value->event.type == SAMPLE_EVENT_STRING && value->event.name != NULL) { + // event.name is device-supplied text; XML-escape it before emitting. + xml_escape_text (sampledata->ostream, value->event.name); + } else if (value->event.type < sizeof (events) / sizeof (events[0])) { + fputs (events[value->event.type], sampledata->ostream); + } else { + fputs ("unknown", sampledata->ostream); + } + fputs ("\n", sampledata->ostream); } break; case DC_SAMPLE_RBT: From 37e69fac32f7cc1670c20f9a6725c8cfe5103b97 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 11 Sep 2026 10:31:09 +1200 Subject: [PATCH 2/2] xml: cast char to unsigned char before fputc to avoid UB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fputc() requires its int argument to be representable as unsigned char or EOF (C17 ยง7.21.7.3p2). Passing a plain char directly is UB on signed-char platforms when the value is > 127. Cast *s to (unsigned char) at the call site to make the argument well-defined on all platforms. Signed-off-by: Michael Keller --- examples/output_xml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/output_xml.c b/examples/output_xml.c index ad69a717..3b191261 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -38,7 +38,7 @@ xml_escape_text (FILE *ostream, const char *s) case '&': fputs ("&", ostream); break; case '<': fputs ("<", ostream); break; case '>': fputs (">", ostream); break; - default: fputc (*s, ostream); break; + default: fputc ((unsigned char)*s, ostream); break; } } }