[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