]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: tools/log: invalid encode_{chunk,string} usage
authorAurelien DARRAGON <adarragon@haproxy.com>
Tue, 9 Apr 2024 09:44:54 +0000 (11:44 +0200)
committerAurelien DARRAGON <adarragon@haproxy.com>
Tue, 9 Apr 2024 15:35:45 +0000 (17:35 +0200)
encode_{chunk,string}() is often found to be used this way:

  ret = encode_{chunk,string}(start, stop...)
  if (ret == NULL || *ret != '\0') {
//error
  }
  //success

Indeed, encode_{chunk,string} will always try to add terminating NULL byte
to the output string, unless no space is available for even 1 byte.
However, it means that for the caller to be able to spot an error, then it
must provide a buffer (here: start) which is already initialized.

But this is wrong: not only this is very tricky to use, but since those
functions don't return NULL on failure, then if the output buffer was not
properly initialized prior to calling the function, the caller will
perform invalid reads when checking for failure this way. Moreover, even
if the buffer is initialized, we cannot reliably tell if the function
actually failed this way because if the buffer was previously initialized
with NULL byte, then the caller might think that the call actually
succeeded (since the function didn't return NULL and didn't update the
buffer).

Also, sess_build_logline() relies lf_encode_{chunk,string}() functions
which are in fact wrappers for encode_{chunk,string}() functions and thus
exhibit the same error handling mechanism. It turns out that
sess_build_logline() makes unsafe use of those functions because it uses
the error-checking logic mentionned above while buffer (tmplog) is not
guaranteed to be initialized when entering the function. This may
ultimately cause malfunctions or invalid reads if the output buffer is
lacking space.

To fix the issue once and for all and prevent similar bugs from being
introduced, we make it so encode_{string, chunk} and escape_string()
(based on encode_string()) now explicitly return NULL on failure
(when the function failed to write at least the ending NULL byte)

lf_encode_{string,chunk}() helpers had to be patched as well due to code
duplication.

This should be backported to all stable versions.

[ada: for 2.4 and 2.6 the patch won't apply as-is, it might be helpful to
 backport ae1e14d65 ("CLEANUP: tools: removing escape_chunk() function")
 first, considering it's not very relevant to maintain a dead function]

include/haproxy/tools.h
src/log.c
src/tools.c

index 3726f635395b62c1e422860d12fb66cfd639ad39..d76b2d13c85654aa1cbb17845bd0876763d4067a 100644 (file)
@@ -399,11 +399,11 @@ int addr_is_local(const struct netns_entry *ns,
  * <map> with the hexadecimal representation of their ASCII-code (2 digits)
  * prefixed by <escape>, and will store the result between <start> (included)
  * and <stop> (excluded), and will always terminate the string with a '\0'
- * before <stop>. The position of the '\0' is returned if the conversion
- * completes. If bytes are missing between <start> and <stop>, then the
- * conversion will be incomplete and truncated. If <stop> <= <start>, the '\0'
- * cannot even be stored so we return <start> without writing the 0.
+ * before <stop>. If bytes are missing between <start> and <stop>, then the
+ * conversion will be incomplete and truncated.
  * The input string must also be zero-terminated.
+ *
+ * Return the address of the \0 character, or NULL on error
  */
 extern const char hextab[];
 extern long query_encode_map[];
@@ -424,8 +424,9 @@ char *encode_chunk(char *start, char *stop,
  * is reached or NULL-byte is encountered. The result will
  * be stored between <start> (included) and <stop> (excluded). This
  * function will always try to terminate the resulting string with a '\0'
- * before <stop>, and will return its position if the conversion
- * completes.
+ * before <stop>.
+ *
+ * Return the address of the \0 character, or NULL on error
  */
 char *escape_string(char *start, char *stop,
                    const char escape, const long *map,
index a1236e9f2c5bed2c1e5ec92cb2b0efc0fd726bd4..847706d2c78598b62582ef1767d51f1d8508d2e8 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1734,6 +1734,8 @@ int get_log_facility(const char *fac)
  * When using the +E log format option, it will try to escape '"\]'
  * characters with '\' as prefix. The same prefix should not be used as
  * <escape>.
+ *
+ * Return the address of the \0 character, or NULL on error
  */
 static char *lf_encode_string(char *start, char *stop,
                               const char escape, const long *map,
@@ -1764,13 +1766,14 @@ static char *lf_encode_string(char *start, char *stop,
                                string++;
                        }
                        *start = '\0';
+                       return start;
                }
        }
        else {
                return encode_string(start, stop, escape, map, string);
        }
 
-       return start;
+       return NULL;
 }
 
 /*
@@ -1779,6 +1782,8 @@ static char *lf_encode_string(char *start, char *stop,
  * When using the +E log format option, it will try to escape '"\]'
  * characters with '\' as prefix. The same prefix should not be used as
  * <escape>.
+ *
+ * Return the address of the \0 character, or NULL on error
  */
 static char *lf_encode_chunk(char *start, char *stop,
                              const char escape, const long *map,
@@ -1814,13 +1819,14 @@ static char *lf_encode_chunk(char *start, char *stop,
                                str++;
                        }
                        *start = '\0';
+                       return start;
                }
        }
        else {
                return encode_chunk(start, stop, escape, map, chunk);
        }
 
-       return start;
+       return NULL;
 }
 
 /*
index 475922283b02990252babc0da71ac9e719cb252b..b08fb18ce5d669d8480b7f524192c55fd7e6d802 100644 (file)
@@ -1969,11 +1969,11 @@ int addr_is_local(const struct netns_entry *ns,
  * <map> with the hexadecimal representation of their ASCII-code (2 digits)
  * prefixed by <escape>, and will store the result between <start> (included)
  * and <stop> (excluded), and will always terminate the string with a '\0'
- * before <stop>. The position of the '\0' is returned if the conversion
- * completes. If bytes are missing between <start> and <stop>, then the
- * conversion will be incomplete and truncated. If <stop> <= <start>, the '\0'
- * cannot even be stored so we return <start> without writing the 0.
+ * before <stop>. If bytes are missing between <start> and <stop>, then the
+ * conversion will be incomplete and truncated.
  * The input string must also be zero-terminated.
+ *
+ * Return the address of the \0 character, or NULL on error
  */
 const char hextab[16] = "0123456789ABCDEF";
 char *encode_string(char *start, char *stop,
@@ -1995,8 +1995,9 @@ char *encode_string(char *start, char *stop,
                        string++;
                }
                *start = '\0';
+               return start;
        }
-       return start;
+       return NULL;
 }
 
 /*
@@ -2025,8 +2026,9 @@ char *encode_chunk(char *start, char *stop,
                        str++;
                }
                *start = '\0';
+               return start;
        }
-       return start;
+       return NULL;
 }
 
 /*
@@ -2035,8 +2037,9 @@ char *encode_chunk(char *start, char *stop,
  * is reached or NULL-byte is encountered. The result will
  * be stored between <start> (included) and <stop> (excluded). This
  * function will always try to terminate the resulting string with a '\0'
- * before <stop>, and will return its position if the conversion
- * completes.
+ * before <stop>.
+ *
+ * Return the address of the \0 character, or NULL on error
  */
 char *escape_string(char *start, char *stop,
                    const char escape, const long *map,
@@ -2056,8 +2059,9 @@ char *escape_string(char *start, char *stop,
                        string++;
                }
                *start = '\0';
+               return start;
        }
-       return start;
+       return NULL;
 }
 
 /* Check a string for using it in a CSV output format. If the string contains