From a1d18284e0a173c4ef2b28425535d0f640ae0a82 Mon Sep 17 00:00:00 2001
From: Roman Arutyunyan <arut@nginx.com>
Date: Mon, 16 Mar 2026 20:13:03 +0400
Subject: [PATCH] Dav: destination length validation for COPY and MOVE.

Previously, when alias was used in a location with Dav COPY or MOVE
enabled, and the destination URI was shorter than the alias, integer
underflow could happen in ngx_http_map_uri_to_path(), which could
result in heap buffer overwrite, followed by a possible segfault.
With some implementations of memcpy(), the segfault could be avoided
and the overwrite could result in a change of the source or destination
file names to be outside of the location root.

Reported by Calif.io in collaboration with Claude and Anthropic Research.
---
 src/http/modules/ngx_http_dav_module.c | 39 +++++++++++++++++---------
 1 file changed, 26 insertions(+), 13 deletions(-)

diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c
index cfb98929e9..4619b139a2 100644
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -535,19 +535,20 @@ ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)
 static ngx_int_t
 ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
 {
-    u_char                   *p, *host, *last, ch;
-    size_t                    len, root;
-    ngx_err_t                 err;
-    ngx_int_t                 rc, depth;
-    ngx_uint_t                overwrite, slash, dir, flags;
-    ngx_str_t                 path, uri, duri, args;
-    ngx_tree_ctx_t            tree;
-    ngx_copy_file_t           cf;
-    ngx_file_info_t           fi;
-    ngx_table_elt_t          *dest, *over;
-    ngx_ext_rename_file_t     ext;
-    ngx_http_dav_copy_ctx_t   copy;
-    ngx_http_dav_loc_conf_t  *dlcf;
+    u_char                    *p, *host, *last, ch;
+    size_t                     len, root;
+    ngx_err_t                  err;
+    ngx_int_t                  rc, depth;
+    ngx_uint_t                 overwrite, slash, dir, flags;
+    ngx_str_t                  path, uri, duri, args;
+    ngx_tree_ctx_t             tree;
+    ngx_copy_file_t            cf;
+    ngx_file_info_t            fi;
+    ngx_table_elt_t           *dest, *over;
+    ngx_ext_rename_file_t      ext;
+    ngx_http_dav_copy_ctx_t    copy;
+    ngx_http_dav_loc_conf_t   *dlcf;
+    ngx_http_core_loc_conf_t  *clcf;
 
     if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -644,6 +645,18 @@ ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
         return NGX_HTTP_CONFLICT;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (clcf->alias
+        && clcf->alias != NGX_MAX_SIZE_T_VALUE
+        && duri.len < clcf->alias)
+    {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "client sent invalid \"Destination\" header: \"%V\"",
+                      &dest->value);
+        return NGX_HTTP_BAD_REQUEST;
+    }
+
     depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
 
     if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
