0

I've been receiving SIGSEGV (11) exceptions in multi-platform apps with seemingly simple code. Debugger seems to take me to a different place each time this happens. So I threw together a simple app to demonstrate this. It does not happen in Windows, but it does happen on all other platforms. Windows works fine. This only example I'm able to recreate every time and demonstrate below comes from the XSuperObject library.

Start a new blank Multi-platform application and add just one button.

DFM

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 259
  ClientWidth = 310
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  DesignerMasterStyle = 0
  object Button1: TButton
    Position.X = 136.000000000000000000
    Position.Y = 96.000000000000000000
    TabOrder = 0
    Text = 'Button1'
    OnClick = Button1Click
  end
end

Now add just a handler for this button's OnClick event.

CODE

unit uMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  XSuperObject;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  IOUtils;

procedure TForm1.Button1Click(Sender: TObject);
var
  O: ISuperObject;
  L: TStringList;
  FN: String;
begin
  FN:= TPath.Combine(TPath.GetHomePath, 'MyApp');
  ForceDirectories(FN);
  FN:= TPath.Combine(FN, 'Test.json');
  O:= SO;
  O.S['foo']:= 'bar';
  L:= TStringList.Create;
  try
    L.Text:= O.AsJSON(True);
    L.SaveToFile(FN);
  finally
    L.Free;
  end;
  O:= TSuperObject.ParseFile(FN); // <-- SIGSEGV (11) happens here
end;

end.

On a test with OSX, for example, pressing the button produces the exception:

Project SIGSEGVTest raised exception class SIGSEGV (11).

When I click on Break, it takes me into some System.Character.inc file on line 1394, which appears to be a bunch of binary of some sort:

    db  $59,$40,$00,$00,$00,$00,$00,$40,$8F,$40,$00,$00,$00,$00,$00

In some cases, I get this exception repeatedly for what appears to be infinitely. I can't seem to trace where it's coming from. I'm not having any luck searching for this, everything I find is either in another language, unanswered, or for another pascal IDE (such as Lazarus). Every now and then, in the same scenario as above, the app just locks up rather than giving this exception. Also sometimes, the debugger doesn't take me anywhere when I click Break.

I do understand what the exception means (essentially the same as Access Violation), but why am I getting this exception on all platforms other than Windows?

NOTE

I did manage to fix this in one place in the past by using the generics TList<> rather than the traditional deprecated TList. But that's just one, and I'm struggling to find out the actual reason I'm getting this exception in so many places. Been battling it for weeks now.

I'm using Delphi XE7 Update 2, and my copy of XSuperObject was just updated right now with the latest, but no luck.

I also installed the latest IDE Fix Pack and still no luck.

UPDATE

When, for example, running this same app from within an iOS simulator (iOS 7.1) without debug, I get the following:

Access violation at address 0060907D, accessing address 00000000.

UPDATE

Here's the crash report from OSX (too big to fit in here):

http://pastebin.com/0Tg7pNn8

UPDATE

It almost seems as if pointers are becoming corrupted somehow. In most cases I've seen so far, it happens when I'm accessing a pointer which should be initialized. For example, when I had a classic TList, it let me use it just fine, except when I tried to read one of the pointers and I got this same error. Same exact code works perfect in Windows. For example, MyObj:= TMyObj(MyTList[0]); where I can see the correct pointer is there, but produces this exception anyway. Unfortunately, since I've tried to reproduce this particular scenario, I cannot.

UPDATE

I just finally managed to step through the XSuperObject library (previously I was getting other strange issues while trying to set a breakpoint to step through). It breaks on line 587 which is inside the following constructor:

constructor TBaseJSON<T, Typ>.Create(JSON: String; const CheckDate: Boolean);
type PInterface = ^IInterface;
var
  JVal: IJSONAncestor;
  PIntf: PInterface;
begin
  FCheckDate := CheckDate;
  if (Self.InheritsFrom(TSuperArray)) and (Trim(JSON) = '{}') then JSON := '[]';
  JVal := TJSONObject.ParseJSONValue(JSON, FCheckDate);
  if JVal.QueryInterface(GetTypeData(TypeInfo(T)).Guid, FJSONObj) = S_OK then // <-- Happens here
     FInterface := TValue.From<T>(FJSONObj).AsInterface
  else
     FCasted := JVal
end;
Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
  • Now that I'm looking back at my past work, I realized I got around this exact same bug in my production app by loading it into a string list first and then parsing it. Seems the only way. I would still love to know why though. – Jerry Dodge Jan 01 '15 at 02:35
  • You need to find the code that gives the error – David Heffernan Jan 01 '15 at 13:31
  • @David I just managed to step through the `XSuperObject` library finally. See my update above. I was kinda expecting that someone else must have faced this before and knows what's going on, but I guess I'm the only person in the world this is happening to? – Jerry Dodge Jan 01 '15 at 16:47
  • Dig deeper. Is JVAL nil? Or does TypeInfo return nil? – David Heffernan Jan 01 '15 at 17:09
  • @David Neither of them are `nil`. – Jerry Dodge Jan 01 '15 at 17:13
  • @David Take that back, I was at this breakpoint at a different point of time. `JVal` is nil in this particular scenario. – Jerry Dodge Jan 01 '15 at 17:15
  • Now you need to debug why that is so. – David Heffernan Jan 01 '15 at 17:22
  • @David Indeed, this is a rather monstrous library way more complex than my expertise. I can see so far that `TJSONBuilder.ReadValue` returns nil. `LGen.Check` is where it becomes way over my head :-) I'm still wondering if I'm the only person in the world this is happening to. But again, this is just one of multiple different scenarios, this is the only one I'm able to reproduce in a test app. – Jerry Dodge Jan 01 '15 at 17:25
  • Can anyone else at least confirm this issue on their end? Or am I alone here? – Jerry Dodge Jan 01 '15 at 17:35
  • Have you asked the developer? Make a simple repro and submit to the developer. – David Heffernan Jan 01 '15 at 18:10
  • The problem is that you are saving a file with a BOM. That makes it an invalid JSON file. You need to save the file without the BOM. – David Heffernan Jan 03 '15 at 09:33
  • The other thing you've learnt here is that your Windows code doesn't support Unicode. – David Heffernan Jan 03 '15 at 11:55

1 Answers1

2

That's a bug in XSuperObject. It crashes when a JSON file starts with a Unicode BOM. You are required to save your JSON file without a BOM, but in Unicode:

L.WriteBOM := False;
L.SaveToFile(FN, TEncoding.UTF8);

But you'd still crash if you get a JSON file that has a BOM. In order to avoid that, you could patch TSuperObject.ParseStream as follows:

class function TSuperObject.ParseStream(Stream: TStream): TSuperObject;
var
  Strm: TStringList;
begin
  Strm := TStringList.Create;
  try
    Strm.LoadFromStream(Stream);
    Result := TSuperObject.Create( Strm.Text);
  finally
    Strm.Free;
  end;
end;

This is only a quick workaround. A better approach should take a closer look at the encoding of the JSON file.

When your file is saved on OSX, it looks like this:

Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000  EF BB BF 7B 0D 0A 20 20 22 66 6F 6F 22 3A 22 62  {..  "foo":"b
00000010  61 72 22 0D 0A 7D 0D 0A                          ar"..}..

The first 3 bytes is the BOM, which causes the trouble for XSuperObject. The crash happens because JVal is nil in line 587 the second time it hits this line.

Update:

RFC 7159 says that json implementations must not add a byte order mark at the beginning. But they may ignore it if it is there. So even if being able to handle a BOM is not a requirement, it should not crash in that case.

The JSON encoding should be detected by looking at the file instead (this is explained in the obsolete RFC 4327)

Community
  • 1
  • 1
Sebastian Z
  • 4,520
  • 1
  • 15
  • 30