From bdfc6ea5894af446746c2b6e378d57febce951ea Mon Sep 17 00:00:00 2001 From: dickbonnema Date: Mon, 28 Sep 2026 21:10:13 +0200 Subject: [PATCH] Fix access violation in initialize when client omits optional fields TOptionalString/TOptionalInteger are classes that stay nil when the client omits the field. Dereferencing them in ShowConfigStatus (Params.clientInfo.version.HasValue) and TDiagnostic.Assign (Src.source.HasValue) caused an access violation during initialize for clients that send clientInfo without a version (or no clientInfo). Guard both derefs with Assigned() checks. Verified end-to-end against lazarus-mcp LSP smoke tests (5/5 passing with full capabilities). --- src/protocol/LSP.Basic.pas | 4 +++- src/serverprotocol/PasLS.General.pas | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/protocol/LSP.Basic.pas b/src/protocol/LSP.Basic.pas index c36c0b3..b96bcd1 100644 --- a/src/protocol/LSP.Basic.pas +++ b/src/protocol/LSP.Basic.pas @@ -1107,7 +1107,9 @@ procedure TDiagnostic.Assign(Source : TPersistent); Severity:=Src.severity; Code:=Src.Code; self.Source:=Src.Source; - if Src.source.HasValue then + // source is a TOptionalString class and stays nil when unset; + // guard before dereferencing to avoid an access violation. + if Assigned(Src.source) and Src.source.HasValue then Message:=Src.Source.Value; end else diff --git a/src/serverprotocol/PasLS.General.pas b/src/serverprotocol/PasLS.General.pas index 606f1d5..7ca3c58 100644 --- a/src/serverprotocol/PasLS.General.pas +++ b/src/serverprotocol/PasLS.General.pas @@ -252,13 +252,20 @@ procedure TInitialize.ShowConfigStatus(Params: TInitializeParams; CodeToolsOptio I: Integer; FPCOptions: TStringArray; begin - if Params.clientInfo.version.HasValue then + // clientInfo and its version are optional in the LSP spec; both may be + // nil here (TOptionalString is a class that stays nil when the client + // omits the field), so guard before dereferencing. + if Assigned(Params.clientInfo) and Assigned(Params.clientInfo.version) + and Params.clientInfo.version.HasValue then clientInfoVersion := ' ' + Params.clientInfo.version.Value else clientInfoVersion := ''; DoLog(kStatusPrefix+'Server: ' + {$INCLUDE %DATE%}); - DoLog(kStatusPrefix+'Client: ' + Params.clientInfo.name + clientInfoVersion); + if Assigned(Params.clientInfo) then + DoLog(kStatusPrefix+'Client: ' + Params.clientInfo.name + clientInfoVersion) + else + DoLog(kStatusPrefix+'Client: '); DoLog(kStatusPrefix+'FPCPath: ' + CodeToolsOptions.FPCPath); DoLog(kStatusPrefix+'FPCSrcDir: ' + CodeToolsOptions.FPCSrcDir);