From c65592d65958abd321f13a60bd1e68ada1c02095 Mon Sep 17 00:00:00 2001 From: Basile Burg Date: Fri, 25 Jan 2019 13:08:17 +0100 Subject: [PATCH] add a protection against large process output, close #424 --- src/u_ceproject.pas | 9 +++++++++ src/u_dubproject.pas | 3 +++ src/u_main.pas | 11 +++++++++++ src/u_processes.pas | 18 ++++++++++++++++++ src/u_tools.pas | 3 +++ 5 files changed, 44 insertions(+) diff --git a/src/u_ceproject.pas b/src/u_ceproject.pas index 96eb6fcd..13f7ee9b 100644 --- a/src/u_ceproject.pas +++ b/src/u_ceproject.pas @@ -937,6 +937,7 @@ var lst: TStringList; str: string; proc: TProcess; + dproc: TDexedProcess = nil; begin lst := TStringList.Create; try @@ -956,9 +957,17 @@ begin getprocInputHandler.removeProcess(TProcess(sender)); SetCurrentDirUTF8(fRunnerOldCwd); + if proc is TDexedProcess then + dproc := TDexedProcess(proc); + if (proc.ExitStatus <> 0) then + begin fMsgs.message(format('error: the process (%s) has returned the status %s', [proc.Executable, prettyReturnStatus(proc)]), fAsProjectItf, amcProj, amkErr); + if dproc.isNotNil and dproc.autoKilled then + fMsgs.message(format('the process was autokilled because the size of its output exceeded %d', + [dproc.autoKillProcThreshold]), nil, amcProj, amkWarn); + end; end; end; diff --git a/src/u_dubproject.pas b/src/u_dubproject.pas index 4189569f..06203cc7 100644 --- a/src/u_dubproject.pas +++ b/src/u_dubproject.pas @@ -1060,6 +1060,9 @@ begin dubCmd2PostMsg[fNextTerminatedCommand], fAsProjectItf, amcProj, amkWarn); fMsgs.message(format('error: DUB has returned the status %s', [prettyReturnStatus(fDubProc)]), fAsProjectItf, amcProj, amkErr); + if fDubProc.autoKilled then + fMsgs.message(format('the process was autokilled because the size of its output exceeded %d', + [fDubProc.autoKillProcThreshold]), nil, amcProj, amkWarn); end; subjProjCompiled(fProjectSubject, fAsProjectItf, fCompiled); SetCurrentDirUTF8(fPreCompilePath); diff --git a/src/u_main.pas b/src/u_main.pas index c5f230f3..6e412944 100644 --- a/src/u_main.pas +++ b/src/u_main.pas @@ -614,6 +614,7 @@ type fAutoCheckUpdates: boolean; fShowBuildDuration: boolean; fToolBarScaling: TToolBarScaling; + fAutoKillProcThreshold: dword; function getConsoleProgram: string; procedure setConsoleProgram(const value: string); function getAdditionalPATH: string; @@ -624,6 +625,7 @@ type published property additionalPATH: string read getAdditionalPATH write setAdditionalPath; property autoCheckUpdates: boolean read fAutoCheckUpdates write fAutoCheckUpdates; + property autoKillProcThreshold: dword read fAutoKillProcThreshold write fAutoKillProcThreshold default 1024 * 1024 * 2; property consoleProgram: string read getConsoleProgram write setConsoleProgram; property coverModuleTests: boolean read fCovModUt write fCovModUt; property floatingWidgetOnTop: boolean read fFloatingWidgetOnTop write fFloatingWidgetOnTop; @@ -839,6 +841,7 @@ begin fReloadLastDocuments:=true; fFlatLook:=true; fDcdPort:=DCDWrapper.port; + fAutoKillProcThreshold := 1024 * 1024 * 2; end; function TApplicationOptionsBase.getNativeProjecCompiler: DCompiler; @@ -929,6 +932,7 @@ begin MainForm.fDscanUnittests := fDscanUnittests; nativeProjectCompiler:= fBackup.nativeProjectCompiler; fToolBarScaling:= fBackup.fToolBarScaling; + fAutoKillProcThreshold:= fBackup.fAutoKillProcThreshold; end else inherited; end; @@ -945,6 +949,7 @@ begin MainForm.fPrjGrpMru.maxCount:= fMaxRecentGroups; MainForm.updateFloatingWidgetOnTop(fFloatingWidgetOnTop); MainForm.fDscanUnittests := fDscanUnittests; + TDexedProcess.autoKillProcThreshold:= fAutoKillProcThreshold; DcdWrapper.port:=fDcdPort; for i := 0 to MainForm.fWidgList.Count-1 do begin @@ -968,6 +973,7 @@ begin fBackup.fAutoCheckUpdates:= fAutoCheckUpdates; fBackup.fShowBuildDuration:= fShowBuildDuration; fBackup.nativeProjectCompiler:= nativeProjectCompiler; + fBackup.fAutoKillProcThreshold := fAutoKillProcThreshold; end else inherited; end; @@ -2978,8 +2984,13 @@ begin if inph.isNotNil then (inph as IProcInputHandler).removeProcess(proc); if (proc.ExitStatus <> 0) then + begin fMsgs.message(format('error: the process (%s) has returned the status %s', [proc.Executable, prettyReturnStatus(proc)]), fDoc, amcEdit, amkErr); + if proc.autoKilled then + fMsgs.message(format('the process was autokilled because the size of its output exceeded %d', + [proc.autoKillProcThreshold]), nil, amcEdit, amkWarn); + end; end; procedure TMainForm.actSetRunnableSwExecute(Sender: TObject); diff --git a/src/u_processes.pas b/src/u_processes.pas index fb04248d..f4af9d05 100644 --- a/src/u_processes.pas +++ b/src/u_processes.pas @@ -32,6 +32,8 @@ type } TDexedProcess = class(TASyncProcess) private + class var FAutoKillProcThreshold: dword; + var fErrToOut: boolean; fRealOnTerminate: TNotifyEvent; fRealOnReadData: TNotifyEvent; @@ -40,6 +42,7 @@ type fTerminateChecker: TTimer; fDoneTerminated: boolean; fHasRead: boolean; + fAutoKilled: boolean; procedure checkTerminated(sender: TObject); procedure setOnTerminate(value: TNotifyEvent); procedure setOnReadData(value: TNotifyEvent); @@ -65,6 +68,10 @@ type property hasRead: boolean read fHasRead; // indicates if OnTerminated was called property doneTerminated: boolean read fDoneTerminated; + // indicates of the process was autokilled + property autoKilled: boolean read fAutoKilled; + // auto kill the process if its output reach this size + class property autoKillProcThreshold: dword read FAutoKillProcThreshold write FAutoKillProcThreshold; end; { @@ -188,6 +195,7 @@ end; procedure TDexedProcess.Execute; begin + fAutoKilled := false; fHasRead := false; fStdoutEx.Clear; fStderrEx.Clear; @@ -214,6 +222,16 @@ procedure TDexedProcess.fillOutputStack; outStr.SetSize(s + 1024); c := inStr.Read((outStr.Memory + s)^, 1024); s += c; + + if (FAutoKillProcThreshold <> 0) and not fDoneTerminated and + (fStderrEx.Size + fStdoutEx.Size >= FAutoKillProcThreshold) then + begin + fStdoutEx.Clear; + fAutoKilled := true; + Terminate(1); + exit; + end; + end; outStr.SetSize(s); end; diff --git a/src/u_tools.pas b/src/u_tools.pas index ba8bc608..0c869c29 100644 --- a/src/u_tools.pas +++ b/src/u_tools.pas @@ -272,6 +272,9 @@ begin begin fMsgs.message(format('error: the tool (%s) has returned the status %s', [fProcess.Executable, prettyReturnStatus(fProcess)]), nil, amcMisc, amkErr); + if fProcess.autoKilled then + fMsgs.message(format('the process was autokilled because the size of its output exceeded %d', + [fProcess.autoKillProcThreshold]), nil, amcMisc, amkWarn); u_processes.killProcess(fProcess); exit; end