1

I have sporadic problems (access violation in unit System).
Application is running 24 x 7 and it happens one - two times in a week.
I have a procedure with local string array, and I found there are cases I assign non initialised array member to string variable like in code below.
Could it be a reason for access violation?

procedure tform1.DoSomething;
var
   sarr: array [1..4] of String;
   s: String;
begin
   sarr[1] := 'aaaa';
   sarr[2] := 'bbbb';
   s := sarr[3]; // Can I get here an access violation???
end;

Real function code below exception happens when obj.opcode = cmdp_done function is called from thread message queue. obj is created in another thread, and sent in PostThreadMessage as msg.lparam

procedure ORTR_ProcessFiscalResponse(obj: TDataForOrpak);
    const
      maxdim = 4;
    var
      s: array [1..maxdim] of String;
      i, n, fiscalNr, statusToSend: Integer;
      sql, pdf, ortrName, docType, rut: String;
      ortr: TCustomizedOrtr;
      oFiscal: TFiscalDevice;
      fpds: TFPDeviceState;
    begin
      try
        case obj.opcode of
          cmdp_status:    N := 3;
          cmdp_done:      N := 2;
          plcl_docissued: N := 4;
          plcl_docfailed: N := 1;
          else
            Exit;
        end;
        for i:=1 to n do
          s[i] := GetTerm(obj.ident, i, ';');
        if s[1] = '' then
          Exit;
        statusToSend := 0;
        ortrName := GetTerm(s[1], 1, '+');
        fiscalNr := StrToIntDef(GetTerm(s[1]+'+', 2, '+'), 999999);
        docType := s[3];
        rut := s[4];
        ortr := TCustomizedOrtr.GetTerminalByName(ortrName) as TCustomizedOrtr;
        if ortr = nil then
          Exit;
        if (ortr.FPState = fps_idle) or (ortr.fiscalNr <> fiscalNr) then begin
          if (StrToIntDef(s[2], 0) <> 0) and (obj.opcode = cmdp_done) then
            fiscal_Confirm(obj.otherdevname, obj.ident);
          if obj.opcode = plcl_docissued then begin
            try
              PLCL_SetDocState(s[1], rut, false, StrToInt(s[2]), StrToInt(docType));
            except
              AddToLogFile('*** PLCL_SetDocState', log_exceptions);
            end;
          end;
          Exit;
        end;
        oFiscal := fiscal_Assigned(ortr.ctlPump.PumpID) as TFiscalDevice;
        case obj.opcode of
          plcl_docissued:
            begin
              ortr.authData.ECRReceiptNr := s[2];
              pdf := StringFromHexPresentation(obj.rawdata);
              sql := format(sql_PaperlessAdd, [
                ToLocalSQL_DateTime(ortr.ctlPump.FinalTime),
                ToLocalSQL_Integer(ortr.ctlPump.PumpID),
                ToLocalSQL_String(pdf)]);
              try
                UpdateLocalDB(sql);
              except
                AddToLogFile('*** PL save pdf', log_exceptions);
              end;
              try
                PLCL_SetDocState(s[1], rut, true, StrToInt(s[2]), StrToInt(docType));
              except
                AddToLogFile('*** PLCL_SetDocState', log_exceptions);
              end;
              ortr.FPState := fps_idle;
              ortr.currStage := TTextIndexType(0);
              ortr.currStage := tivirt_towelcome;  // VADIM
              ExternalProcessPumpState(ortr.authData.gsPump.PumpID);
            end;
          plcl_docfailed:
            ortr.FPState := fps_plerror;
          cmdp_status:
            begin
              ortr.FPError := StrToIntDef(s[3], 0);
              fpds := TFPDeviceState(StrToIntDef(s[2], 0));
              ortr.FPState := fpds;
              if (fpds in [fps_nocomm, fps_error]) and (ortr.fiscalMode = 1) and
                 (ortr.authData = nil) and (ortr.fiscalNr < 0) then
                    SpecialInterface.SendFiscalNrToPromax(-ortr.fiscalNr, '0');
              case fpds of
                fps_nopaper:  statusToSend := wph_prnpaperout;
                fps_nocomm:   statusToSend := wph_prncommfailure;
                fps_error:    statusToSend := wph_prngenericfailure;
              end;
            end;
          cmdp_done:
            begin
              if ortr.fiscalMode = 1 then begin
                if ortr.authData = nil then begin // DRY GOOD
                  SpecialInterface.SendFiscalNrToPromax(-fiscalNr, s[2]);
                end
                else begin
                  ortr.authData.ECRReceiptNr := s[2];
                  ExternalProcessPumpState(ortr.authData.gsPump.PumpID);
                end
              end;
              if StrToIntDef(s[2], 0) <> 0 then
                fiscal_Confirm(obj.otherdevname, obj.ident);
              statusToSend := wph_prnidle;
              ortr.FPState := fps_idle;
              ortr.currStage := ti_takereceipt;
            end;
        end;
        if (statusToSend <> 0) and (oFiscal <> nil) then
          PostNonVisualCommand(nv_devicestate, statusToSend, Integer(oFiscal));
      finally
        obj.free;
      end;
    end;
vadim
  • 19
  • 2
  • 4
    Does this code actually reproduce the problem or is it "representative" of the actual code that appears to have the issue? At first glance, this code should not cause an access violation since the compiler goes to great lengths to ensure local string variables are initialized to known values within the function prologue. In this instance, sarr elements should be initialized to nil. Thus, the "s := sarr[3];" statement should only assign an empty string to "s". – Allen Bauer Dec 27 '13 at 00:22
  • this code is "representative". this function is called very frequent, and mostly without problem. If function is failed from any reason, it is called again after 5 seconds. When acess violation happens it happens number of times , but after number of times function works without AV. Last time it was 20 times access violation in the sequence and after it 8 days without problem. – vadim Dec 27 '13 at 01:04
  • 2
    In the real code, are there any intervening calls or operations done to the array between the "sarr[2] := 'bbbb';" and "s := sarr[3];" statements? I'd look for memory overwrites. The code above, as written, has a very low (read none) probability of failing. – Allen Bauer Dec 27 '13 at 01:19
  • I even not sure the problem is with array. Simply it was only suspected place in the code, and i hoped it can be a reason for AV. I will add tracing logging inside the function and may be will know better in what place it happens. AV i have is "Access violation at address 00545697 in module 'FCCsrv.exe'. Read of address 6D757084." From the map file i see it is in System unit, and i hoped it is related to reference to uniniatilized array item. – vadim Dec 27 '13 at 01:26
  • Strings are generally "safe". But there situations where the auto-management can go wrong. As @AllenBauer says, the code given cannot fail (in this case even if called on a totally garbage instance of TForm1). If your map file has given a ballpark area, please post the actual code. Please also post the signatures of any functions/procedures called - the use of **const** or **var** parameters can have some rather surprising effects. – Disillusioned Dec 27 '13 at 08:02
  • 2
    Your problem is elsewhere. Put madexcept and full debug fastmm in your process and do some debugging. You cannot solve this problem without real debugging. – David Heffernan Dec 27 '13 at 09:18
  • I put here the source code where i have AV – vadim Dec 27 '13 at 13:41
  • I do not know if source code can help, because too much things involved, i think David is right and i will need to make real debugging. I only suspect assignment of uninitialized array item, it was before debugging only in code review. Thanks to all for your comments. I will update if will find something interesting. I only put tracing logging and initialization of the array with empty strings and will wait for next failure – vadim Dec 27 '13 at 13:52

1 Answers1

0

Your initial piece of code, the tform1.DoSomething routine, is unable of producing an access violation:

Thus you are simply assigning an empty string, and s remains empty.


Concerning your actual code, assuming that it does produce the access violation, my guess would be that:

  • the obj parameter still refers to an already destroyed object,
  • that obj.opcode reads an invalid piece of memory, but since it is compared to an numerical value, will pass,
  • that Exit is called in de case else clause, and
  • that obj.Free fails in the finally clause.

1 All string variables are initilized to empty, except string function results:

If the function exits without assigning a value to Result or the function name, then the function's return value is undefined.

The missing compiler warning is still a bug.

NGLN
  • 43,011
  • 8
  • 105
  • 200