fix: Array move ownership, selfhost -g flags, LSP workspace/symbol
Session 36: prevent UAF when Array is moved into a struct field (Nexus headers), align selfhost cc flags with bootstrap debug/release, and add workspace/symbol search to bux-lsp 0.6. - Parser: zero headers after embedding so auto-drop is a no-op - Request_WantsKeepAlive uses RequestHeader_Get again safely - Selfhost: default -O0 -g; --release -O2 -DNDEBUG; BUX_CFLAGS - LSP: workspace/symbol + smoke; version 0.6.0
This commit is contained in:
+22
-8
@@ -133,19 +133,33 @@ module Http {
|
||||
return "";
|
||||
}
|
||||
|
||||
/// Decide keep-alive from the raw request bytes (avoids fragile header Array walk).
|
||||
/// HTTP/1.1 defaults to keep-alive; Connection: close forces close;
|
||||
/// HTTP/1.0 needs explicit keep-alive.
|
||||
pub func RawRequest_WantsKeepAlive(raw: String) -> bool {
|
||||
if String_Contains(raw, "Connection: close") || String_Contains(raw, "connection: close") ||
|
||||
String_Contains(raw, "CONNECTION: CLOSE") {
|
||||
/// HTTP/1.0 needs explicit keep-alive. Uses parsed headers (safe after
|
||||
/// Parser field-move ownership handoff).
|
||||
pub func Request_WantsKeepAlive(req: *HttpRequest) -> bool {
|
||||
let conn: String = RequestHeader_Get(req, "Connection");
|
||||
if String_EqIgnoreCase(conn, "close") {
|
||||
return false;
|
||||
}
|
||||
if String_EqIgnoreCase(conn, "keep-alive") {
|
||||
return true;
|
||||
}
|
||||
if String_StartsWith(req.version, "HTTP/1.0") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Fallback when only raw bytes are available.
|
||||
pub func RawRequest_WantsKeepAlive(raw: String) -> bool {
|
||||
if String_Contains(raw, "Connection: close") || String_Contains(raw, "connection: close") ||
|
||||
String_Contains(raw, "CONNECTION: CLOSE") {
|
||||
return false;
|
||||
}
|
||||
// HTTP/1.0 without Keep-Alive → close
|
||||
if String_Contains(raw, "HTTP/1.0") {
|
||||
if String_Contains(raw, "Connection: keep-alive") ||
|
||||
String_Contains(raw, "Connection: Keep-Alive") ||
|
||||
String_Contains(raw, "connection: keep-alive") {
|
||||
String_Contains(raw, "Connection: Keep-Alive") ||
|
||||
String_Contains(raw, "connection: keep-alive") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -100,7 +100,9 @@ module Parser {
|
||||
|
||||
// Find header/body boundary
|
||||
let boundary: String = bux_strstr(raw, "\r\n\r\n");
|
||||
var headers: Array<HeaderEntry> = Array_New<HeaderEntry>(16);
|
||||
// Only one allocation path — avoid Array_New then overwrite (leak) and
|
||||
// suppress auto-drop after moving into HttpRequest (use-after-free).
|
||||
var headers: Array<HeaderEntry>;
|
||||
var body: String = "";
|
||||
if String_Len(boundary) > 0 {
|
||||
let headerEnd: uint = bux_str_offset(boundary, raw);
|
||||
@@ -114,6 +116,7 @@ module Parser {
|
||||
}
|
||||
|
||||
if String_Eq(path, "") {
|
||||
// auto-drop of `headers` runs on return
|
||||
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
|
||||
}
|
||||
|
||||
@@ -124,6 +127,11 @@ module Parser {
|
||||
body: body,
|
||||
headers: headers,
|
||||
};
|
||||
// Ownership transferred into req — zero local shell so auto-drop is a no-op.
|
||||
// (Compiler does not yet treat field-move as a move-out of the local.)
|
||||
headers.data = null as *HeaderEntry;
|
||||
headers.len = 0;
|
||||
headers.cap = 0;
|
||||
return ParseResult_NewOk(req);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ module Server {
|
||||
import Std::String::{String_Len, String_StartsWith};
|
||||
import Std::Channel::{Channel, Channel_New, Channel_Send, Channel_Recv};
|
||||
import Config::{ServerConfig};
|
||||
import Http::{HttpRequest, HttpResponse, Http_StatusText, Http_NewResponse, RawRequest_WantsKeepAlive};
|
||||
import Http::{HttpRequest, HttpResponse, Http_StatusText, Http_NewResponse, Request_WantsKeepAlive};
|
||||
import Errors::{ParseResult};
|
||||
import Parser::{ParseRequest};
|
||||
import Router::{Router, Router_Dispatch};
|
||||
@@ -87,8 +87,8 @@ module Server {
|
||||
let parsed: ParseResult = ParseRequest(raw);
|
||||
var keepAlive: bool = false;
|
||||
if parsed.tag == ParseResult_Ok {
|
||||
let req: HttpRequest = parsed.data.Ok_0;
|
||||
keepAlive = RawRequest_WantsKeepAlive(raw);
|
||||
var req: HttpRequest = parsed.data.Ok_0;
|
||||
keepAlive = Request_WantsKeepAlive(&req);
|
||||
// Last request on the connection quota must close
|
||||
if reqCount + 1 >= MAX_KEEPALIVE_REQUESTS {
|
||||
keepAlive = false;
|
||||
|
||||
Reference in New Issue
Block a user