#1 2017-07-12 10:30:55

mingda
Member
Registered: 2013-01-04
Posts: 121

Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

using TSQLRestServerDB, when a error batchupdate cause by "EngineBatchSend: Wrong PUT",
then subsequent batchupdate will generate Server Error 500: Internal Server Error.

Debug find av occur at

function TSQLRestServerDB.InternalExecute(const aSQL: RawUTF8;

        if (ValueInt=nil) and (ValueUTF8=nil) then begin
          // default execution: loop through all rows
          repeat until fStatement^.Step<>SQLITE_ROW;     //<====== av occure at here, 

//raised exception class EAccessViolation with message 'Access violation at address 006BE19C

thanks!

Offline

#2 2017-07-12 11:21:02

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,205
Website

Re: Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

I can't reproduce such a problem here.

The context is unclear...
Is there a "Wrong PUT" error before, or after the AV?

GetAndPrepareStatement() should have set the fStatement instance.
Could you step in the debugger and find out why there is something wrong with fStatement?

Offline

#3 2017-07-12 13:48:26

houdw2006
Member
Registered: 2015-05-23
Posts: 48

Re: Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

I meet the BatchSend returns HTTP 500 or 501 by chance of using BatchAdd with ForceID wrongly.

Something like this:

aPeople: TSQLPeople;
Client: TSQLRestClientURI;
ResultsArray: TIDDynArray;

.....
aPeople.IDValue := 1000;

Client.BatchStart(nil, 1000);
Client.BatchAdd(aPeople, True, True);
Client.BatchAdd(aPeople, True, True);  // reproduce the duplicated key in the table People.
Client.BatchSend(ResultsArray);

After this BatchSend fails, all the subsequent BatchStart..BatchSend will fail as well. And the server goes malfunction and consumes nearly all the CPU resource available.

Offline

#4 2017-07-13 02:47:45

mingda
Member
Registered: 2013-01-04
Posts: 121

Re: Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

ab wrote:

The context is unclear...
Is there a "Wrong PUT" error before, or after the AV?

"Wrong PUT" error is first, this is the call stack

:76c85608 KERNELBASE.RaiseException + 0x48
mORMot.TSQLRestServer.EngineBatchSend(nil,'["automaticTransactionPerRow",2147483647,,"PUT@VoyageFee",{"ID":null,"Flag":1,"UpdateBy":"User","UpdateTime":1499911738644,"RemoteIP":"127.0.0.1"},"PUT@VoyageFee",{"ID":3096,"Flag":1,"UpdateBy":"User","UpdateTime":1499911738644,"RemoteIP":"127.0.0.1"},"PUT@VoyageFee",{"ID":3086,"Flag":1,"UpdateBy":"User","UpdateTime":1499911738644,"RemoteIP":"127.0.0.1"},"PUT@VoyageFee",{"ID":3084,"Flag":0,"DoneBill":1,"UpdateBy":"User","UpdateTime":1499911738644,"RemoteIP":"127.0.0.1"},"PUT@VoyageFee",{"ID":3086,"Flag":0,"DoneBill":1,"UpdateBy":"User","UpdateTime":1499911738644,"RemoteIP":"127.0.0.1"},"PUT@VoyageFee",{"ID":3096,"Flag":0,"DoneBill":1,"UpdateBy":"User","UpdateTime":1499911738644,"RemoteIP":"127.0.0.1"},"PUT@VoyageFee",{"ID":null,"Flag":0,"DoneBill":1,"UpdateBy":"User","UpdateTime":1499911738644,"RemoteIP":"127.0.0.1...
mORMot.TSQLRestServer.Batch($221C680)
uShipAgentRestServer.TShipAgentRestServer.ApplyUpdates($221C680)
mORMot.TSQLRestServerURIContext.ExecuteSOAByMethod
mORMot.TSQLRestServerURIContext.ExecuteCommand
mORMot.TSQLRestServer.URI($54CFD80)
mORMotHttpServer.TSQLHttpServer.Request($22B78D8)
SynCrtSock.THttpServerGeneric.Request($22B78D8)
SynCrtSock.THttpServer.Process($22152E0,4,$22CC2F8)
SynCrtSock.THttpServerResp.Execute
:004574b9 ThreadProc + $45
:00407e4a ThreadWrapper + $2A
:76e97c04 KERNEL32.BaseThreadInitThunk + 0x24
:7718ad2f ntdll.RtlInitializeExceptionChain + 0x8f
:7718acfa ntdll.RtlInitializeExceptionChain + 0x5a

after wrong Put, BatchUpdate will error, there have two debug Exception Notification:
when click Break, the first callstack is

:76c85608 KERNELBASE.RaiseException + 0x48
SynSQLite3Static.sqlite3_realloc($2212F78,145)

the second callstack is

:76c85608 KERNELBASE.RaiseException + 0x48
:00407224 NotifyNonDelphiException + $1C
:7716ff13 ; 
SynCommons.MoveX87
:7717068f ntdll.KiUserExceptionDispatcher + 0xf
SynLog.TSynMapFile.Log($22CC7D8,6696455,False)
SynLog.TSynLog.AddStackTrace(nil)
SynLog.TSynLog.LogInternal(sllError,'% for % // %',(...),$288E960)
SynLog.TSynLog.Log(sllError,'% for % // %',(...),$288E960)
mORMot.TSQLRest.InternalLog('% for % // %',(...),sllError)
mORMotSQLite3.TSQLRestServerDB.GetAndPrepareStatementRelease($22AD0F8,'')
mORMotSQLite3.TSQLRestServerDB.InternalExecute('UPDATE InvoiceType SET Attribute=:(''5''):,UpdateBy=:(''User''):,UpdateTime=:(1499911820772):,RemoteIP=:(''127.0.0.1''): WHERE RowID=:(4):;',False,nil,nil {''},nil,nil,nil)
mORMotSQLite3.TSQLRestServerDB.EngineExecute('UPDATE InvoiceType SET Attribute=:(''5''):,UpdateBy=:(''User''):,UpdateTime=:(1499911820772):,RemoteIP=:(''127.0.0.1''): WHERE RowID=:(4):;')
mORMot.TSQLRest.ExecuteFmt('UPDATE % SET % WHERE RowID=:(%):;',(...))
mORMotSQLite3.TSQLRestServerDB.MainEngineUpdate(23,4,'{"Attribute":"5","UpdateBy":"User","UpdateTime":1499911820772,"RemoteIP":"127.0.0.1"}')
mORMot.TSQLRestServer.EngineUpdate(23,4,'{"Attribute":"5","UpdateBy":"User","UpdateTime":1499911820772,"RemoteIP":"127.0.0.1"}')
mORMot.TSQLRestServer.EngineBatchSend(nil,'["automaticTransactionPerRow",2147483647,"PUT@InvoiceType'#0#0'{"ID":4{"Attribute":"5","UpdateBy":"User","UpdateTime":1499911820772,"RemoteIP":"127.0.0.1"}]',(304),0)
mORMot.TSQLRestServer.Batch($221C680)
uShipAgentRestServer.TShipAgentRestServer.ApplyUpdates($221C680)
mORMot.TSQLRestServerURIContext.ExecuteSOAByMethod
mORMot.TSQLRestServerURIContext.ExecuteCommand
mORMot.TSQLRestServer.URI($54CFD80)
mORMotHttpServer.TSQLHttpServer.Request($22B78D8)
SynCrtSock.THttpServerGeneric.Request($22B78D8)
SynCrtSock.THttpServer.Process($22152E0,5,$22CC2F8)
SynCrtSock.THttpServerResp.Execute
:004574b9 ThreadProc + $45
:00407e4a ThreadWrapper + $2A
:76e97c04 KERNEL32.BaseThreadInitThunk + 0x24
:7718ad2f ntdll.RtlInitializeExceptionChain + 0x8f
:7718acfa ntdll.RtlInitializeExceptionChain + 0x5a

I use this code make test:

  TShipAgentRestServer= class(TSQLRestServerDB)
  private
    f: boolean;
  publshied
    procedure ApplyUpdates(Ctx: TSQLRestServerURIContext);

procedure TShipAgentRestServer.ApplyUpdates(Ctx: TSQLRestServerURIContext);
var
  tempStr: rawUTF8;
begin
  if not f then begin
    Tempstr := AnyTextFileToRawUTF8('error.json');
    Ctx.Call.InBody := TempStr;
    f := True;
  end else begin
    Tempstr := AnyTextFileToRawUTF8('good.json');
    Ctx.Call.InBody := TempStr;
  end;

  Batch(Ctx);
end;

error.json 

["automaticTransactionPerRow", 2147483647, "PUT@VoyageFee", {
	"ID": 3084,
	"Flag": 1,
	"UpdateTime": 1499822856618,
	"RemoteIP": "127.0.0.1"
}, "PUT@VoyageFee", {
	"ID": 3096,
	"Flag": 0,
	"UpdateTime": 1499822856618,
	"RemoteIP": "127.0.0.1"
}, "PUT@VoyageFee", {
	"ID": null,
	"Flag": 1,
	"UpdateTime": 1499822856618,
	"RemoteIP": "127.0.0.1"
}, "PUT@VoyageFee", {
	"ID": 3098,
	"Flag": 1,
	"UpdateTime": 1499822856618,
	"RemoteIP": "127.0.0.1"
}]

good.json

["automaticTransactionPerRow", 2147483647, "PUT@VoyageFee", {
	"ID": 3084,
	"Flag": 1,
	"UpdateTime": 1499822856618,
	"RemoteIP": "127.0.0.1"
}, "PUT@VoyageFee", {
	"ID": 3096,
	"Flag": 0,
	"UpdateTime": 1499822856618,
	"RemoteIP": "127.0.0.1"
}, "PUT@VoyageFee", {
	"ID": 3098,
	"Flag": 1,
	"UpdateTime": 1499822856618,
	"RemoteIP": "127.0.0.1"
}]

make a web client,  call ApplyUpdates with any success content,
first call trigger error, then subsequent call will error,

Use DelphiXE, mORMot 3705, thanks!

Offline

#5 2017-07-19 02:00:24

mingda
Member
Registered: 2013-01-04
Posts: 121

Re: Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

Further debug, after first "Wrong PUT" error, subsequent BatchUpdate generate av at

function TSQLRequest.Step: integer;
{$ifdef RESETFPUEXCEPTION} // safest to reset x87 exceptions - inlined TSynFPUException
var cw87: word;
{$endif}
begin
  if Request=0 then
    raise ESQLite3Exception.Create(RequestDB,SQLITE_MISUSE,'Step');
  {$ifdef RESETFPUEXCEPTION}
  cw87 := Get8087CW;
  try
  {$endif}
    result := sqlite3_check(RequestDB,sqlite3.step(Request),'Step');     //<==== here generate av
  {$ifdef RESETFPUEXCEPTION}
  finally
    Set8087CW(cw87);
  end;
  {$endif}
end;

can't debug further because my ability, seem all av relative to the first "Wrong PUT" error.
When debug first "Wrong Put" error, follow step also generate some av, and when you close
applicaiton directly, final will have Runtime error 216.

thanks!

Offline

#6 2017-07-19 08:16:38

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,205
Website

Re: Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

Please check https://synopse.info/fossil/info/1d39554271

This was a nasty bug, for sure...
Sorry! sad

Offline

#7 2017-07-19 08:53:25

mingda
Member
Registered: 2013-01-04
Posts: 121

Re: Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

After test, I confirm AV gone, server is still online.
Wonderful, you are a super man!
thanks!

Offline

#8 2017-07-19 09:01:55

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,205
Website

Re: Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

Such buffer overflows can be tricky to fix.

I found the issue by reproducing the problem in the batch, with FastMM4 in FullDebugMode.
This mode allows to trigger the Access violation as soon as possible, i.e. in the UniqueRawUTF8ZeroToTilde() function, sooner than in TSQLRequest.Step

Offline

#9 2017-07-19 09:44:50

mingda
Member
Registered: 2013-01-04
Posts: 121

Re: Wrong BatchUpdate will make subsequent BatchUpdate Server Error 500

ab wrote:

Such buffer overflows can be tricky to fix.

something need to learn, thank your share!

Offline

Board footer

Powered by FluxBB