This is an automated email from Gerrit.

"Jan Matyas <jan.mat...@codasip.com>" just uploaded a new patch set to Gerrit, 
which you can find at https://review.openocd.org/c/openocd/+/8315

-- gerrit

commit 8e7a7638e6801dfe0391e32bcd8655d9af964291
Author: Jan Matyas <jan.mat...@codasip.com>
Date:   Mon Jun 3 10:23:02 2024 +0200

    binarybuffer: Fix str_to_buf() parsing function
    
    The function str_to_buf() was too benevolent and did
    not perform sufficient error checking on the input
    string being parsed. Especially:
    
    - Invalid numbers were silently ignored.
    - Out-of-range nubmers were silently truncated.
    
    The following commands that use str_to_buf()
    were affected:
    
    - reg (when writing a register value)
    - set_reg
    - jtag drscan
    
    This pull request fixes that by:
    
    - Rewriting str_to_buf() to add the missing checks.
    - Adding function command_parse_str_to_buf() which can
      be used in command handlers. It parses the input
      numbers and provides user-readable error messages
      in case of parsing errors.
    
    Examples:
    
    jtag drscan 10 huh10
    
    - Old behavior: The string "huh10" is silently
      converted to 10 and the command is then executed.
      No warning error or warning is shown to the user.
    - New behavior: Error message is shown:
      "Number huh10 (base-10) contains an invalid digit"
    
    reg pc 0x123456789
    
    - Old behavior: The register value is silently
      truncated to 0x23456789 and the command is performed.
    - New behavior: Error message is shown to the user:
      "Number 0x123456789 exceeds 32 bits"
    
    Change-Id: I079e19cd153aec853a3c2eb66953024b8542d0f4
    Signed-off-by: Jan Matyas <jan.mat...@codasip.com>

diff --git a/src/helper/binarybuffer.c b/src/helper/binarybuffer.c
index 5f38b43ae1..b5bace2c9d 100644
--- a/src/helper/binarybuffer.c
+++ b/src/helper/binarybuffer.c
@@ -102,7 +102,6 @@ bool buf_cmp_mask(const void *_buf1, const void *_buf2,
        return buf_cmp_trailing(buf1[last], buf2[last], mask[last], trailing);
 }
 
-
 void *buf_set_ones(void *_buf, unsigned size)
 {
        uint8_t *buf = _buf;
@@ -206,34 +205,55 @@ char *buf_to_hex_str(const void *_buf, unsigned buf_len)
        return str;
 }
 
-/** identify radix, and skip radix-prefix (0, 0x or 0X) */
-static void str_radix_guess(const char **_str, unsigned *_str_len,
-       unsigned *_radix)
+/** Identify radix, and skip the radix-prefix (0, 0x or 0X) */
+static void str_radix_guess(const char **_str, unsigned int *_str_len,
+       unsigned int *_radix)
 {
+       assert(_str);
+       assert(_str_len);
+       assert(_radix);
+
        unsigned radix = *_radix;
-       if (radix != 0)
-               return;
+       assert(radix == 0 || radix == 8 || radix == 10 || radix == 16);
+
        const char *str = *_str;
        unsigned str_len = *_str_len;
-       if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
+
+       const bool has_hex_prefix = (str[0] == '0') && (str[1] == 'x' || str[1] 
== 'X');
+       const bool has_oct_prefix = (str[0] == '0') && (str_len != 1);
+
+       if (has_hex_prefix && (radix == 16 || radix == 0)) {
+               /* OK, detected HEX number, strip the prefix */
                radix = 16;
                str += 2;
                str_len -= 2;
-       } else if ((str[0] == '0') && (str_len != 1)) {
+       } else if (has_oct_prefix && (radix == 8 || radix == 0)) {
+               /* OK, detected an octal number, strip the prefix */
                radix = 8;
                str += 1;
                str_len -= 1;
-       } else
+       } else if (radix == 0) {
+               /* Assume decadic number */
                radix = 10;
+       }
+
        *_str = str;
        *_str_len = str_len;
        *_radix = radix;
 }
 
-int str_to_buf(const char *str, unsigned str_len,
-       void *_buf, unsigned buf_len, unsigned radix)
+int str_to_buf(const char *str, void *_buf, unsigned int buf_len,
+       unsigned int radix, unsigned int *_detected_radix)
 {
+       assert(radix == 0 || radix == 8 || radix == 10 || radix == 16);
+
+       unsigned int str_len = strlen(str);
+
        str_radix_guess(&str, &str_len, &radix);
+       assert(radix != 0);
+
+       if (_detected_radix)
+               *_detected_radix = radix;
 
        float factor;
        if (radix == 16)
@@ -243,41 +263,70 @@ int str_to_buf(const char *str, unsigned str_len,
        else if (radix == 8)
                factor = 0.375; /* log(8) / log(256) = 0.375 */
        else
-               return 0;
+               assert(false);
 
-       /* copy to zero-terminated buffer */
-       char *charbuf = strndup(str, str_len);
+       const unsigned int b256_len = ceil_f_to_u32(str_len * factor);
+       uint8_t *b256_buf = NULL;
 
-       /* number of digits in base-256 notation */
-       unsigned b256_len = ceil_f_to_u32(str_len * factor);
-       uint8_t *b256_buf = calloc(b256_len, 1);
+    /* Allocate a buffer for digits in base-256 notation */
+       b256_buf = calloc(b256_len, 1);
+       if (!b256_buf) {
+               LOG_ERROR("Unable to allocate memory");
+               return ERROR_FAIL;
+       }
 
-       /* go through zero terminated buffer
-        * input digits (ASCII) */
-       unsigned i;
-       for (i = 0; charbuf[i]; i++) {
-               uint32_t tmp = charbuf[i];
-               if ((tmp >= '0') && (tmp <= '9'))
+       /* Go through the zero-terminated buffer
+        * of input digits (ASCII) */
+       for (unsigned int i = 0; str[i]; i++) {
+               uint32_t tmp = str[i];
+               if ((tmp >= '0') && (tmp <= '9')) {
                        tmp = (tmp - '0');
-               else if ((tmp >= 'a') && (tmp <= 'f'))
+               } else if ((tmp >= 'a') && (tmp <= 'f')) {
                        tmp = (tmp - 'a' + 10);
-               else if ((tmp >= 'A') && (tmp <= 'F'))
+               } else if ((tmp >= 'A') && (tmp <= 'F')) {
                        tmp = (tmp - 'A' + 10);
-               else
-                       continue;       /* skip characters other than 
[0-9,a-f,A-F] */
+               } else {
+                   /* Characters other than [0-9,a-f,A-F] are invalid */
+                       free(b256_buf);
+                       return ERROR_INVALID_DIGIT;
+               }
 
-               if (tmp >= radix)
-                       continue;       /* skip digits invalid for the current 
radix */
+               if (tmp >= radix) {
+                   /* Encountered a digit that is invalid for the current 
radix */
+                       free(b256_buf);
+                       return ERROR_INVALID_DIGIT;
+               }
 
-               /* base-256 digits */
-               for (unsigned j = 0; j < b256_len; j++) {
+               /* Add the current digit (tmp) to the intermediate result
+                * in b256_buf (base-256 digits) */
+               for (unsigned int j = 0; j < b256_len; j++) {
                        tmp += (uint32_t)b256_buf[j] * radix;
                        b256_buf[j] = (uint8_t)(tmp & 0xFF);
                        tmp >>= 8;
                }
 
+               /* The b256_t buffer is large enough to contain the whole 
result. */
+               assert(tmp == 0);
        }
 
+       /* The result must not contain more bits than buf_len. */
+       /* Check the whole bytes: */
+       for (unsigned int j = DIV_ROUND_UP(buf_len, 8); j < b256_len; j++) {
+               if (b256_buf[j] != 0x0) {
+                       free(b256_buf);
+                       return ERROR_NUMBER_EXCEEDS_BUFFER;
+               }
+       }
+       /* Check the partial byte: */
+       if (buf_len % 8) {
+               const uint8_t mask = 0xffu << (buf_len % 8);
+               if ((b256_buf[(buf_len / 8)] & mask) != 0x0) {
+                       free(b256_buf);
+                       return ERROR_NUMBER_EXCEEDS_BUFFER;
+               }
+       }
+
+       /* Copy the digits to the output buffer */
        uint8_t *buf = _buf;
        for (unsigned j = 0; j < DIV_ROUND_UP(buf_len, 8); j++) {
                if (j < b256_len)
@@ -286,14 +335,8 @@ int str_to_buf(const char *str, unsigned str_len,
                        buf[j] = 0;
        }
 
-       /* mask out bits that don't belong to the buffer */
-       if (buf_len % 8)
-               buf[(buf_len / 8)] &= 0xff >> (8 - (buf_len % 8));
-
        free(b256_buf);
-       free(charbuf);
-
-       return i;
+       return ERROR_OK;
 }
 
 void bit_copy_queue_init(struct bit_copy_queue *q)
diff --git a/src/helper/binarybuffer.h b/src/helper/binarybuffer.h
index 3446296817..159b4cbd26 100644
--- a/src/helper/binarybuffer.h
+++ b/src/helper/binarybuffer.h
@@ -14,6 +14,9 @@
 #include <helper/list.h>
 #include <helper/types.h>
 
+#define ERROR_INVALID_DIGIT          (-1700)
+#define ERROR_NUMBER_EXCEEDS_BUFFER  (-1701)
+
 /** @file
  * Support functions to access arbitrary bits in a byte array
  */
@@ -189,8 +192,18 @@ void *buf_set_ones(void *buf, unsigned size);
 void *buf_set_buf(const void *src, unsigned src_start,
                  void *dst, unsigned dst_start, unsigned len);
 
-int str_to_buf(const char *str, unsigned len,
-               void *bin_buf, unsigned buf_size, unsigned radix);
+/**
+ * Parse an unsigned number (provided as a zero-terminated string)
+ * into a bit buffer whose size is buf_len bits.
+ * @param str Input number, zero-terminated string
+ * @param _buf Output buffer, allocated by the caller
+ * @param buf_len Output buffer size in bits
+ * @param radix Base of the input number - 16, 10, 8 or 0.
+ *              0 means auto-detect the radix.
+ */
+int str_to_buf(const char *str, void *_buf, unsigned int buf_len,
+       unsigned int radix, unsigned int *_detected_radix);
+
 char *buf_to_hex_str(const void *buf, unsigned size);
 
 /* read a uint32_t from a buffer in target memory endianness */
diff --git a/src/helper/command.c b/src/helper/command.c
index a775c730b8..49c6885740 100644
--- a/src/helper/command.c
+++ b/src/helper/command.c
@@ -1360,6 +1360,28 @@ int command_parse_bool_arg(const char *in, bool *out)
        return ERROR_COMMAND_SYNTAX_ERROR;
 }
 
+int command_parse_str_to_buf(const char *str, void *buf, unsigned int buf_len,
+       unsigned int radix)
+{
+       assert(str);
+       assert(buf);
+
+       unsigned int detected_radix = -1;
+       int ret = str_to_buf(str, buf, buf_len, radix, &detected_radix);
+       if (ret == ERROR_OK)
+               return ret;
+
+       /* Provide a user-readable error message */
+       if (ret == ERROR_INVALID_DIGIT)
+               LOG_ERROR("Number %s (base-%u) contains an invalid digit", str, 
detected_radix);
+       else if (ret == ERROR_NUMBER_EXCEEDS_BUFFER)
+               LOG_ERROR("Number %s exceeds %u bits", str, buf_len);
+       else
+               LOG_ERROR("Could not parse number %s", str);
+
+       return ERROR_COMMAND_ARGUMENT_INVALID;
+}
+
 COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label)
 {
        switch (CMD_ARGC) {
diff --git a/src/helper/command.h b/src/helper/command.h
index fc26dda81a..e3c830cd51 100644
--- a/src/helper/command.h
+++ b/src/helper/command.h
@@ -517,6 +517,18 @@ DECLARE_PARSE_WRAPPER(_target_addr, target_addr_t);
 int command_parse_bool_arg(const char *in, bool *out);
 COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label);
 
+/**
+ * Parse a number (base 10, base 16 or base 8) and store the result
+ * into a bit buffer. In case of parsing error, produce a user-readable
+ * error message.
+ * @param str Input number as a zero-terminated string
+ * @param buf Bit buffer, allocated by the caller
+ * @param buf_len Size of the buf in bits
+ * @param radix base of the number - 16, 10, 8 (or 0 for autodetection)
+ */
+int command_parse_str_to_buf(const char *str, void *buf, unsigned int buf_len,
+       unsigned int radix);
+
 /** parses an on/off command argument */
 #define COMMAND_PARSE_ON_OFF(in, out) \
        COMMAND_PARSE_BOOL(in, out, "on", "off")
diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c
index 7995529018..e9da9ac513 100644
--- a/src/jtag/tcl.c
+++ b/src/jtag/tcl.c
@@ -87,8 +87,11 @@ static COMMAND_HELPER(handle_jtag_command_drscan_fields, 
struct scan_field *fiel
                        LOG_ERROR("Out of memory");
                        return ERROR_FAIL;
                }
+
                fields[field_count].out_value = t;
-               str_to_buf(CMD_ARGV[i + 1], strlen(CMD_ARGV[i + 1]), t, bits, 
0);
+               int ret = command_parse_str_to_buf(CMD_ARGV[i + 1], t, bits, 0);
+               if (ret != ERROR_OK)
+                       return ret;
                fields[field_count].in_value = t;
                field_count++;
        }
diff --git a/src/target/target.c b/src/target/target.c
index efc168903b..6fcf323265 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -3128,11 +3128,18 @@ COMMAND_HANDLER(handle_reg_command)
        /* set register value */
        if (CMD_ARGC == 2) {
                uint8_t *buf = malloc(DIV_ROUND_UP(reg->size, 8));
-               if (!buf)
+               if (!buf) {
+                       LOG_ERROR("Failed to allocate memory");
                        return ERROR_FAIL;
-               str_to_buf(CMD_ARGV[1], strlen(CMD_ARGV[1]), buf, reg->size, 0);
+               }
 
-               int retval = reg->type->set(reg, buf);
+               int retval = command_parse_str_to_buf(CMD_ARGV[1], buf, 
reg->size, 0);
+               if (retval != ERROR_OK) {
+                       free(buf);
+                       return retval;
+               }
+
+               retval = reg->type->set(reg, buf);
                if (retval != ERROR_OK) {
                        LOG_ERROR("Could not write to register '%s'", 
reg->name);
                } else {
@@ -4834,8 +4841,13 @@ static int target_jim_set_reg(Jim_Interp *interp, int 
argc,
                        return JIM_ERR;
                }
 
-               str_to_buf(reg_value, strlen(reg_value), buf, reg->size, 0);
-               int retval = reg->type->set(reg, buf);
+               int retval = command_parse_str_to_buf(reg_value, buf, 
reg->size, 0);
+               if (retval != ERROR_OK) {
+                       free(buf);
+                       return retval;
+               }
+
+               retval = reg->type->set(reg, buf);
                free(buf);
 
                if (retval != ERROR_OK) {

-- 

Reply via email to