From fae1be9ca6173eda0d3bbde6bedbbff2f922a97f Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 24 Apr 2026 01:07:10 +0300 Subject: [PATCH] veb: fix veb: segfault when serving static files (fixes #26939) --- vlib/veb/controller.v | 7 +++++-- vlib/veb/tests/static_handler_test.v | 12 ++++++++++++ vlib/veb/veb.v | 12 ++++++++++-- vlib/veb/veb_fasthttp.v | 12 ++++++++++-- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/vlib/veb/controller.v b/vlib/veb/controller.v index f4a7b4841..4668102ce 100644 --- a/vlib/veb/controller.v +++ b/vlib/veb/controller.v @@ -58,8 +58,11 @@ pub fn controller[A, X](path string, mut global_app A) !&ControllerPath { user_context.Context = ctx handle_route[A, X](mut global_app, mut user_context, url, host, &routes) - // we need to explicitly tell the V compiler to return a reference - return &user_context.Context + // Preserve the handled context on the heap before the stack-local user context goes away. + unsafe { + *ctx = user_context.Context + } + return ctx } } } diff --git a/vlib/veb/tests/static_handler_test.v b/vlib/veb/tests/static_handler_test.v index a2f7f7f23..46673f753 100644 --- a/vlib/veb/tests/static_handler_test.v +++ b/vlib/veb/tests/static_handler_test.v @@ -23,6 +23,11 @@ pub fn (mut app App) index(mut ctx Context) veb.Result { return ctx.text('Hello V!') } +@['/redirect_root'] +pub fn (mut app App) redirect_root(mut ctx Context) veb.Result { + return ctx.redirect('/root.txt') +} + @[post] pub fn (mut app App) post_request(mut ctx Context) veb.Result { return ctx.text(ctx.req.data) @@ -90,6 +95,13 @@ fn test_static_root() { assert x.body == 'root' } +fn test_redirect_to_static_root() { + x := http.get('${localserver}/redirect_root')! + + assert x.status() == .ok + assert x.body == 'root' +} + fn test_scans_subdirs() { x := http.get('${localserver}/sub_folder/sub.txt')! diff --git a/vlib/veb/veb.v b/vlib/veb/veb.v index f63dffc92..1965a699d 100644 --- a/vlib/veb/veb.v +++ b/vlib/veb/veb.v @@ -297,7 +297,11 @@ fn handle_ssl_request[A, X](req http.Request, params &SslRequestParams) ?&Contex global_app.static_compression_max_size, global_app.static_compression_mime_types, global_app.enable_markdown_negotiation), mut user_context, url, host) { - return &user_context.Context + // Preserve the handled context on the heap before the stack-local user context goes away. + unsafe { + *ctx = user_context.Context + } + return ctx } } $if A is ControllerInterface { @@ -308,7 +312,11 @@ fn handle_ssl_request[A, X](req http.Request, params &SslRequestParams) ?&Contex mut user_context := X{} user_context.Context = ctx handle_route[A, X](mut global_app, mut user_context, url, host, params.routes) - return &user_context.Context + // Preserve the handled context on the heap before the stack-local user context goes away. + unsafe { + *ctx = user_context.Context + } + return ctx } fn write_ssl_context_response(mut ssl_conn mbedtls.SSLConn, completed_context &Context) ! { diff --git a/vlib/veb/veb_fasthttp.v b/vlib/veb/veb_fasthttp.v index c0b2fa4cc..5c05ceefe 100644 --- a/vlib/veb/veb_fasthttp.v +++ b/vlib/veb/veb_fasthttp.v @@ -189,7 +189,11 @@ fn handle_request_and_route[A, X](mut app A, req http.Request, _client_fd int, p app.static_compression_mime_types, app.enable_markdown_negotiation), mut user_context, url, host) { - return &user_context.Context + // Preserve the handled context on the heap before the stack-local user context goes away. + unsafe { + *ctx = user_context.Context + } + return ctx } } // Match controller paths first @@ -202,7 +206,11 @@ fn handle_request_and_route[A, X](mut app A, req http.Request, _client_fd int, p mut user_context := X{} user_context.Context = ctx handle_route[A, X](mut app, mut user_context, url, host, params.routes) - return &user_context.Context + // Preserve the handled context on the heap before the stack-local user context goes away. + unsafe { + *ctx = user_context.Context + } + return ctx } // decode_chunked_body decodes a chunked transfer-encoded body string -- 2.39.5