[Privoxy-commits] [privoxy] 02/08: Parse the chunk-size with a dedicated function

User Git git at git.privoxy.org
Sun May 31 09:38:25 CEST 2026


This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository privoxy.

commit 5b3bb22b771c93adddf1726ec904c9378d584a66
Author: Fabian Keil <fk at fabiankeil.de>
AuthorDate: Fri May 15 08:12:47 2026 +0200

    Parse the chunk-size with a dedicated function
    
    ... and reject "unreasonably" large values to prevent silent
    truncation by sscanf(), integer overflows and misinterpretation
    of the content later on.
    
    Heap buffer overflows on platforms with 32-bit pointers were
    alleged as well.
    
    OVE-20260515-0002.
    
    Reported by @TristanInSec.
---
 encode.c  |  2 +-
 encode.h  |  1 +
 filters.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 filters.h |  1 +
 jcc.c     |  2 +-
 5 files changed, 96 insertions(+), 5 deletions(-)

diff --git a/encode.c b/encode.c
index 6cb57cf5..f3aa1ace 100644
--- a/encode.c
+++ b/encode.c
@@ -270,7 +270,7 @@ char * url_encode(const char *s)
  * Returns     :  The integer value, or -1 for non-hex characters.
  *
  *********************************************************************/
-static int xdtoi(const int d)
+int xdtoi(const int d)
 {
    if ((d >= '0') && (d <= '9'))
    {
diff --git a/encode.h b/encode.h
index b4d954b3..9bbfd7fb 100644
--- a/encode.h
+++ b/encode.h
@@ -37,6 +37,7 @@
 
 extern char * html_encode(const char *s);
 extern char * url_encode(const char *s);
+extern int xdtoi(const int d);
 extern char * url_decode(const char *str);
 extern int    xtoi(const char *s);
 extern char * html_encode_and_free_original(char *s);
diff --git a/filters.c b/filters.c
index 8c227832..9a396bba 100644
--- a/filters.c
+++ b/filters.c
@@ -2251,7 +2251,7 @@ static int get_bytes_to_next_chunk_start(char *buffer, size_t size, size_t offse
       return -1;
    }
 
-   if (sscanf(chunk_start, "%x", &chunk_size) != 1)
+   if (JB_ERR_OK != parse_chunk_size(chunk_start, size, &chunk_size))
    {
       /* XXX: Write test case to trigger this. */
       log_error(LOG_LEVEL_ERROR, "Failed to parse chunk size. "
@@ -2370,6 +2370,94 @@ int chunked_data_is_complete(char *buffer, size_t size, size_t offset)
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  parse_chunk_size
+ *
+ * Description :  Parses the chunk-size or returns an error if the
+ *                size is considered "unreasonably" large.
+ *
+ * Parameters  :
+ *          1  :  buffer = Pointer to the chunk-encoded content.
+ *          2  :  buffer_size =  Size of the buffer.
+ *          3  :  chunk_size = Storage for the parsed chunk-size.
+ *                             Only valid if the function returns
+ *                             JB_ERR_OK
+ *
+ * Returns     :  JB_ERR_OK for success,
+ *                JB_ERR_PARSE otherwise
+ *
+ *********************************************************************/
+jb_err parse_chunk_size(char *buffer, size_t buffer_size, unsigned int *chunk_size)
+{
+   char *p = buffer;
+   const unsigned int max_hex_digits = 7;
+   unsigned int hex_digits_that_matter = 0;
+   unsigned int leading_zeros = 0;
+   int skipping_leading_zeros = TRUE;
+
+   *chunk_size = 0;
+
+   while (p < buffer + buffer_size && xdtoi(*p) != -1)
+   {
+      if (skipping_leading_zeros)
+      {
+         if (*p == '0')
+         {
+            p++;
+            leading_zeros++;
+
+            continue;
+         }
+         skipping_leading_zeros = FALSE;
+      }
+
+      p++;
+      hex_digits_that_matter++;
+
+      /*
+       * We cap the number of hex digits that matter to make
+       * sure we can represent the chunk-size with an unsigned
+       * integer.
+       */
+      if (hex_digits_that_matter == max_hex_digits)
+      {
+         log_error(LOG_LEVEL_ERROR, "Chunk-size 'unreasonably' large. "
+            "Counted %u hex digits that matter and %u leading zeros.",
+            hex_digits_that_matter, leading_zeros);
+         return JB_ERR_PARSE;
+      }
+   }
+
+   if (leading_zeros != 0)
+   {
+      if (xdtoi(*(buffer + leading_zeros)) == -1)
+      {
+         /*
+          * Looks like the chunk-size consists entirely of zeros
+          * so let's not skip the last one.
+          */
+         leading_zeros--;
+      }
+
+      if (leading_zeros != 0)
+      {
+         log_error(LOG_LEVEL_RE_FILTER,
+            "Parsing the chunk-size after skipping %d leading zeros.",
+            leading_zeros);
+      }
+   }
+
+   if (sscanf(buffer + leading_zeros, "%x", chunk_size) != 1)
+   {
+      return JB_ERR_PARSE;
+   }
+
+   return JB_ERR_OK;
+
+}
+
+
 /*********************************************************************
  *
  * Function    :  remove_chunked_transfer_coding
@@ -2420,7 +2508,7 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
    }
 #endif
 
-   if (sscanf(buffer, "%x", &chunksize) != 1)
+   if (JB_ERR_OK != parse_chunk_size(buffer, *size, &chunksize))
    {
       log_error(LOG_LEVEL_ERROR, "Invalid first chunksize while stripping \"chunked\" transfer coding");
       return JB_ERR_PARSE;
@@ -2487,7 +2575,8 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
          return JB_ERR_PARSE;
       }
       from_p += 2;
-      if (sscanf(from_p, "%x", &chunksize) != 1)
+      assert(*size > newsize);
+      if (JB_ERR_OK != parse_chunk_size(from_p, *size - newsize, &chunksize))
       {
          log_error(LOG_LEVEL_INFO, "Invalid \"chunked\" transfer encoding detected and ignored.");
          break;
diff --git a/filters.h b/filters.h
index 6e27a696..1f0e638c 100644
--- a/filters.h
+++ b/filters.h
@@ -104,6 +104,7 @@ extern struct http_response *direct_response(struct client_state *csp);
 
 extern int get_bytes_missing_from_chunked_data(char *buffer, size_t size, size_t offset);
 extern int chunked_data_is_complete(char *buffer, size_t size, size_t offset);
+extern jb_err parse_chunk_size(char *buffer, size_t buffer_size, unsigned int *chunk_size);
 
 #ifdef FUZZ
 extern char *gif_deanimate_response(struct client_state *csp);
diff --git a/jcc.c b/jcc.c
index 00171164..45d24dc7 100644
--- a/jcc.c
+++ b/jcc.c
@@ -1522,7 +1522,7 @@ static enum chunk_status chunked_body_is_complete(struct iob *iob, size_t *lengt
       {
          return CHUNK_STATUS_MISSING_DATA;
       }
-      if (sscanf(p, "%x", &chunksize) != 1)
+      if (JB_ERR_OK != parse_chunk_size(p, *length, &chunksize))
       {
          return CHUNK_STATUS_PARSE_ERROR;
       }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Privoxy-commits mailing list