]> sigrok.org Git - libsigrok.git/blobdiff - src/input/trace32_ad.c
input/trace32_ad: fix potential buffer overflow for unexpected input data
[libsigrok.git] / src / input / trace32_ad.c
index 1f69028887b09a297bec438174430a205d94aa5c..b1d801e8a85b9d7f93c4361d602f5addace8cb42 100644 (file)
 #define MAX_POD_COUNT     12
 #define HEADER_SIZE       80
 
+#define SPACE             ' '
+#define CTRLZ             '\x1a'
+#define TRACE32           "trace32"
+
 #define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
 
 /*
  */
 #define DEFAULT_SAMPLERATE 200
 
-enum {
-       AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
-       AD_FORMAT_TXTHDR      /* Textual header, binary data */
+enum ad_format {
+       AD_FORMAT_UNKNOWN,
+       AD_FORMAT_BINHDR,       /* Binary header, binary data, textual setup info */
+       AD_FORMAT_TXTHDR,       /* Textual header, binary data */
 };
 
-enum {
+enum ad_device {
        AD_DEVICE_PI = 1, /* Data recorded by LA-7940 PowerIntegrator or */
                           /* LA-394x PowerIntegrator II. */
        AD_DEVICE_IPROBE  /* Data recorded by LA-769x PowerTrace II IProbe. */
@@ -71,12 +76,12 @@ enum {
        /* Missing file format info for LA-4530 uTrace analog probe */
 };
 
-enum {
+enum ad_mode {
        AD_MODE_250MHZ = 0,
        AD_MODE_500MHZ = 1
 };
 
-enum {
+enum ad_compr {
        AD_COMPR_NONE  = 0, /* File created with /NOCOMPRESS */
        AD_COMPR_QCOMP = 6, /* File created with /COMPRESS or /QUICKCOMPRESS */
 };
@@ -84,7 +89,10 @@ enum {
 struct context {
        gboolean meta_sent;
        gboolean header_read, records_read, trigger_sent;
-       char format, device, record_mode, compression;
+       enum ad_format format;
+       enum ad_device device;
+       enum ad_mode record_mode;
+       enum ad_compr compression;
        char pod_status[MAX_POD_COUNT];
        struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
        uint64_t trigger_timestamp;
@@ -112,7 +120,7 @@ static char *printable_name(const char *name)
                if (g_ascii_isprint(name[i])) {
                        *p++ = name[i];
                } else {
-                       snprintf(p, 5, "\\x%05x", name[i]);
+                       snprintf(p, 5, "\\x%02x", name[i]);
                        p += strlen("\\x00");
                }
        }
@@ -198,7 +206,10 @@ static int process_header(GString *buf, struct context *inc)
 {
        char *format_name, *format_name_sig;
        char *p;
-       int i, record_size, device_id;
+       int has_trace32;
+       size_t record_size;
+       enum ad_device device_id;
+       enum ad_format format;
 
        /*
         * 00-31 (0x00-1F) file format name
@@ -227,41 +238,49 @@ static int process_header(GString *buf, struct context *inc)
         * deal with unexpected or incorrect input data.
         */
 
+       /*
+        * Get up to the first 32 bytes of the file content. File format
+        * names end on SPACE or CTRL-Z (or NUL). Trim trailing SPACE
+        * before further processing.
+        */
        format_name = g_strndup(buf->str, 32);
+       p = strchr(format_name, CTRLZ);
+       if (p)
+               *p = '\0';
+       g_strchomp(format_name);
 
-       /* File format name ends on 0x20/0x1A, let's remove both. */
-       for (i = 1; i < 31; i++) {
-               if (format_name[i] == 0x1A) {
-                       format_name[i - 1] = 0;
-                       format_name[i] = 0;
-               }
-       }
-       g_strchomp(format_name); /* This is for additional padding spaces. */
-
-       format_name_sig = g_strndup(format_name, 5);
+       /*
+        * File format names either start with the "trace32" literal,
+        * or with a digit and SPACE.
+        */
+       format_name_sig = g_strndup(format_name, strlen(TRACE32));
+       has_trace32 = g_strcmp0(format_name_sig, TRACE32) == 0;
+       g_free(format_name_sig);
 
-       /* Desired file formats either start with digit+space or "trace32". */
-       if (g_strcmp0(format_name_sig, "trace32")) {
-               if (inc)
-                       inc->format = AD_FORMAT_BINHDR;
-       } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == 0x20)) {
-               if (inc)
-                       inc->format = AD_FORMAT_TXTHDR;
-               g_free(format_name_sig);
+       format = AD_FORMAT_UNKNOWN;
+       if (has_trace32) {
+               /* Literal "trace32" leader, binary header follows. */
+               format = AD_FORMAT_BINHDR;
+       } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == SPACE)) {
+               /* Digit and SPACE leader, currently unsupported text header. */
+               format = AD_FORMAT_TXTHDR;
                g_free(format_name);
                if (inc)
                        sr_err("This format isn't implemented yet, aborting.");
                return SR_ERR;
        } else {
-               g_free(format_name_sig);
+               /* Unknown kind of format name. Unsupported. */
                g_free(format_name);
                if (inc)
                        sr_err("Don't know this file format, aborting.");
                return SR_ERR;
        }
+       if (!format)
+               return SR_ERR;
 
        p = printable_name(format_name);
-       sr_dbg("File says it's \"%s\"", p);
+       if (inc)
+               sr_dbg("File says it's \"%s\" -> format type %u.", p, format);
        g_free(p);
 
        record_size = R8(buf->str + 56);
@@ -276,20 +295,20 @@ static int process_header(GString *buf, struct context *inc)
        }
 
        if (!device_id) {
-               g_free(format_name_sig);
                g_free(format_name);
-               sr_err("Don't know how to handle this file with record size %d.",
-                       record_size);
+               if (inc)
+                       sr_err("Cannot handle file with record size %zu.",
+                               record_size);
                return SR_ERR;
        }
 
-       g_free(format_name_sig);
        g_free(format_name);
 
        /* Stop processing the header if we just want to identify the file. */
        if (!inc)
                return SR_OK;
 
+       inc->format       = format;
        inc->device       = device_id;
        inc->trigger_timestamp = RL64(buf->str + 32);
        inc->compression  = R8(buf->str + 48); /* Maps to the enum. */
@@ -305,7 +324,7 @@ static int process_header(GString *buf, struct context *inc)
                inc->last_record);
 
        /* Check if we can work with this compression. */
-       if (inc->compression != AD_COMPR_NONE) {
+       if (inc->compression) {
                sr_err("File uses unsupported compression (0x%02X), can't continue.",
                        inc->compression);
                return SR_ERR;