This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat-connectors.git


The following commit(s) were added to refs/heads/main by this push:
     new 97da2b17a Add additional bounds and error checking when reading AJP 
messages.
97da2b17a is described below

commit 97da2b17a08dacfde785cf29c8fba07194e08997
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Sep 6 15:22:31 2023 +0100

    Add additional bounds and error checking when reading AJP messages.
    
    schultz/markt
---
 native/common/jk_ajp_common.c     | 59 +++++++++++++++++++++++++++++++--------
 native/common/jk_msg_buff.c       | 15 ++++++++--
 xdocs/miscellaneous/changelog.xml |  6 +++-
 3 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/native/common/jk_ajp_common.c b/native/common/jk_ajp_common.c
index bd6dc46f5..1cb2c6405 100644
--- a/native/common/jk_ajp_common.c
+++ b/native/common/jk_ajp_common.c
@@ -730,6 +730,12 @@ static int ajp_unmarshal_response(jk_msg_buf_t *msg,
         JK_TRACE_EXIT(l);
         return JK_FALSE;
     }
+    if (d->status == 0xFFFF) {
+        jk_log(l, JK_LOG_ERROR,
+               "(%s) Not enough bytes available to read status", 
ae->worker->name);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
 
     d->msg = jk_b_get_string(msg);
     if (d->msg) {
@@ -743,6 +749,12 @@ static int ajp_unmarshal_response(jk_msg_buf_t *msg,
                "(%s) status = %d", ae->worker->name, d->status);
 
     d->num_headers = jk_b_get_int(msg);
+    if (d->num_headers == 0xFFFF) {
+        jk_log(l, JK_LOG_ERROR,
+               "(%s) Not enough bytes available to read header count", 
ae->worker->name);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
     d->header_names = d->header_values = NULL;
 
     if (JK_IS_DEBUG_LEVEL(l))
@@ -757,9 +769,15 @@ static int ajp_unmarshal_response(jk_msg_buf_t *msg,
         if (d->header_names && d->header_values) {
             unsigned int i;
             for (i = 0; i < d->num_headers; i++) {
+                /*
+                 * Looking for a specific value. No need to check for errors.
+                 * That will be handled by jk_b_get_string if the specific
+                 * value is not found.
+                 */
                 unsigned short name = jk_b_pget_int(msg, msg->pos);
 
                 if ((name & 0XFF00) == 0XA000) {
+                    /* Consume bytes just peeked with jk_b_pget_int */
                     jk_b_get_int(msg);
                     name = name & 0X00FF;
                     if (name <= SC_RES_HEADERS_NUM) {
@@ -2076,16 +2094,26 @@ static int ajp_process_callback(jk_msg_buf_t *msg,
             return JK_AJP13_ERROR;
         }
         if (!s->response_blocked) {
-            unsigned int len = (unsigned int)jk_b_get_int(msg);
+            unsigned int len;
+            /* Check there is sufficient data in the ajp message to read a 
valid
+             * length. It must be at least 3 bytes - the magic byte for
+             * JK_AJP13_SEND_BODY_CHUNK (1 byte) and the length of the chunk
+             * (2 bytes). The remaining part of the message is the chunk.
+             */
+            if (msg->len < 3) {
+                jk_log(l, JK_LOG_ERROR,
+                       "(%s) Invalid AJP message. Length of AJP message "
+                       "is %d, but it should be at least 3.",
+                       ae->worker->name, msg->len);
+                JK_TRACE_EXIT(l);
+                return JK_INTERNAL_ERROR;
+            }
             /*
-             * Do a sanity check on len to prevent write reading beyond buffer
-             * boundaries and thus revealing possible sensitive memory
+             * Once we have len, do a sanity check to prevent reading beyond
+             * buffer boundaries and thus revealing possible sensitive memory
              * contents to the client.
-             * len cannot be larger than msg->len - 3 because the ajp message
-             * contains the magic byte for JK_AJP13_SEND_BODY_CHUNK (1 byte)
-             * and the length of the chunk (2 bytes). The remaining part of
-             * the message is the chunk.
              */
+            len = (unsigned int)jk_b_get_int(msg);
             if (len > (unsigned int)(msg->len - 3)) {
                 jk_log(l, JK_LOG_ERROR,
                        "(%s) Chunk length too large. Length of AJP message "
@@ -2126,9 +2154,12 @@ static int ajp_process_callback(jk_msg_buf_t *msg,
     case JK_AJP13_GET_BODY_CHUNK:
         {
             int len = (int)jk_b_get_int(msg);
-
-            if (len < 0) {
-                len = 0;
+            if (len == 0xFFFF) {
+                jk_log(l, JK_LOG_ERROR,
+                       "(%s) Invalid AJP message: Not enough bytes available 
to read chunk length",
+                       ae->worker->name);
+                JK_TRACE_EXIT(l);
+                return JK_AJP13_ERROR;
             }
 
             /* the right place to add file storage for upload
@@ -2150,7 +2181,13 @@ static int ajp_process_callback(jk_msg_buf_t *msg,
 
     case JK_AJP13_END_RESPONSE:
         ae->reuse = (int)jk_b_get_byte(msg);
-        if (!ae->reuse) {
+        if (ae->reuse == 0xFF) {
+            jk_log(l, JK_LOG_ERROR,
+                   "(%s) Not enough bytes available to read reuse flag",
+                   ae->worker->name);
+            JK_TRACE_EXIT(l);
+            return JK_AJP13_ERROR;
+        } else  if (!ae->reuse) {
             /*
              * AJP13 protocol reuse flag set to false.
              * Tomcat will close its side of the connection.
diff --git a/native/common/jk_msg_buff.c b/native/common/jk_msg_buff.c
index c6b4a3c4e..731eb7005 100644
--- a/native/common/jk_msg_buff.c
+++ b/native/common/jk_msg_buff.c
@@ -215,7 +215,7 @@ int jk_b_append_bytes(jk_msg_buf_t *msg, const unsigned 
char *param, int len)
 unsigned long jk_b_get_long(jk_msg_buf_t *msg)
 {
     unsigned long i;
-    if (msg->pos + 3 > msg->len) {
+    if (msg->pos + 4 > msg->len) {
         return 0xFFFFFFFF;
     }
     i = ((msg->buf[(msg->pos++)] & 0xFF) << 24);
@@ -228,6 +228,9 @@ unsigned long jk_b_get_long(jk_msg_buf_t *msg)
 unsigned long jk_b_pget_long(jk_msg_buf_t *msg, int pos)
 {
     unsigned long i;
+    if (pos + 4 > msg->len) {
+        return 0xFFFFFFFF;
+    }
     i = ((msg->buf[(pos++)] & 0xFF) << 24);
     i |= ((msg->buf[(pos++)] & 0xFF) << 16);
     i |= ((msg->buf[(pos++)] & 0xFF) << 8);
@@ -239,7 +242,7 @@ unsigned long jk_b_pget_long(jk_msg_buf_t *msg, int pos)
 unsigned short jk_b_get_int(jk_msg_buf_t *msg)
 {
     unsigned short i;
-    if (msg->pos + 1 > msg->len) {
+    if (msg->pos + 2 > msg->len) {
         return 0xFFFF;
     }
     i = ((msg->buf[(msg->pos++)] & 0xFF) << 8);
@@ -250,6 +253,9 @@ unsigned short jk_b_get_int(jk_msg_buf_t *msg)
 unsigned short jk_b_pget_int(jk_msg_buf_t *msg, int pos)
 {
     unsigned short i;
+    if (pos + 2 > msg->len) {
+        return 0xFFFF;
+    }
     i = ((msg->buf[pos++] & 0xFF) << 8);
     i += ((msg->buf[pos] & 0xFF));
     return i;
@@ -258,7 +264,7 @@ unsigned short jk_b_pget_int(jk_msg_buf_t *msg, int pos)
 unsigned char jk_b_get_byte(jk_msg_buf_t *msg)
 {
     unsigned char rc;
-    if (msg->pos > msg->len) {
+    if (msg->pos + 1 > msg->len) {
         return 0xFF;
     }
     rc = msg->buf[msg->pos++];
@@ -268,6 +274,9 @@ unsigned char jk_b_get_byte(jk_msg_buf_t *msg)
 
 unsigned char jk_b_pget_byte(jk_msg_buf_t *msg, int pos)
 {
+    if (pos + 1 > msg->len) {
+        return 0xFF;
+    }
     return msg->buf[pos];
 }
 
diff --git a/xdocs/miscellaneous/changelog.xml 
b/xdocs/miscellaneous/changelog.xml
index a99f7b050..62af3e643 100644
--- a/xdocs/miscellaneous/changelog.xml
+++ b/xdocs/miscellaneous/changelog.xml
@@ -100,7 +100,11 @@
         Sam James. (markt)
       </fix>
       <update>
-        Improve XSS hardening i status worker. (rjung)
+        Improve XSS hardening in status worker. (rjung)
+      </update>
+      <update>
+        Add additional bounds and error checking when reading AJP messages.
+        (schultz/markt)
       </update>
     </changelog>
   </subsection>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to