mORMot and Open Source friends
Check-in [79f5becba8]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:fixed potential buffer overflow in TJSONObjectDecoder.EncodeAsSQLPrepared()
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 79f5becba890b73024302cf5eaca28d48de9b62a
User & Date: User 2013-12-07 20:05:42
Context
2013-12-08
13:08
do not reallocate dynamic array memory for smallest size (<=64 items) check-in: 1a8d821f4c user: User tags: trunk
2013-12-07
20:05
fixed potential buffer overflow in TJSONObjectDecoder.EncodeAsSQLPrepared() check-in: 79f5becba8 user: User tags: trunk
2013-12-06
16:26
renamed FieldNameValid() function into PropNameValid() for better consistency check-in: 4e22df67b6 user: abouchez tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/mORMot.pas.

766
767
768
769
770
771
772

773
774
775
776
777
778
779
....
1280
1281
1282
1283
1284
1285
1286
1287


1288
1289
1290
1291
1292
1293
1294
.....
16698
16699
16700
16701
16702
16703
16704
16705
16706
16707
16708
16709
16710
16711
16712
16713

16714
16715
16716
16717
16718
16719
16720
16721
16722
16723
16724
16725
16726

16727
16728
16729
16730
16731
16732
16733
.....
16735
16736
16737
16738
16739
16740
16741
16742
16743
16744
16745
16746
16747
16748
16749
.....
16755
16756
16757
16758
16759
16760
16761
16762
16763
16764
16765
16766
16767
16768
16769
.....
16803
16804
16805
16806
16807
16808
16809
16810
16811
16812
16813
16814
16815
16816
16817
.....
16851
16852
16853
16854
16855
16856
16857
16858
16859
16860
16861
16862
16863
16864
16865
16866
      / ObjectToJSON() functions and WriteObject method
    - added TTypeInfo.ClassCreate() method to create a TObject instance from RTTI
    - TEnumType.GetEnumNameValue() will now recognize both 'sllWarning' and
      'Warning' text as a sllWarning item (will enhance JSONToObject() process)
    - fix and enhance boolean values parsing from JSON content ("Yes"=true) 
    - implement woHumanReadableFullSetsAsStar and woHumanReadableEnumSetAsComment
      option for JSON serialization and TEnumType.GetEnumNameTrimedAll()

    - added ClassInstanceCreate() function calling any known virtual constructor
    - added GetInterfaceFromEntry() function to speed up interface execution,
      e.g. for TServiceFactoryServer (avoid the RTTI lookup of GetInterface) 
    - added TPropInfo.ClassFromJSON() to properly unserialize TObject properties
    - added TSQLPropInfo.SQLFieldTypeName property
    - fixed [f96cf0fc5d] and [221ee9c767] about TSQLRecordMany JSON serialization
    - fixed issue when retrieving a TSQLRecord containing TSQLRecordMany
................................................................................
    /// contains the decoded field names or value
    FieldNames, FieldValues: array[0..MAX_SQLFIELDS-1] of RawUTF8;
    /// Decode() will set a bit for each field set JSON null value
    FieldNull: TSQLFieldBits;
    /// number of fields decoded in FieldNames[] and FieldValues[]
    FieldCount: integer;
    /// size of the TEXT data (in bytes) in FieldValues[]
    FieldLen: integer;


    /// set to TRUE if parameters are to be :(...): inlined
    InlinedParams: boolean;
    /// internal pointer over field names to be used after Decode() call
    // - either FieldNames, either Fields[] array as defined in Decode()
    DecodedFieldNames: PRawUTF8Array;
    /// the ID=.. value as sent within the JSON object supplied to Decode()
    DecodedRowID: integer;
................................................................................
          end;
        end else
          // non string params (numeric or null/false/true) are passed untouched
          FieldValues[ndx] := res;
      end;
      end;
    end;
    Inc(FieldLen,length(FieldValues[ndx]));
  end;

var FieldName: RawUTF8;
    F: integer;
    FieldIsRowID: Boolean;
begin
  FieldCount := 0;
  FieldLen := 0;

  DecodedRowID := 0;
  FillChar(FieldNull,sizeof(FieldNull),0);
  InlinedParams := Params=pInlined;
  if pointer(Fields)=nil then begin
    // get "COL1"="VAL1" pairs, stopping at '}' or ']'
    DecodedFieldNames := @FieldNames;
    if RowID>0 then begin // insert explicit RowID
      if ReplaceRowIDWithID then
        FieldNames[0] := 'ID' else
        FieldNames[0] := 'RowID';
      FieldValues[0] := Int32ToUtf8(RowID);
      FieldCount := 1;
      FieldLen := Length(FieldNames[0])+Length(FieldValues[0]);

      DecodedRowID := RowID;
    end;
    if P<>nil then
    repeat
      FieldName := GetJSONField(P,P);
      if P=nil then break; // syntax error
      FieldIsRowID := IsRowId(pointer(FieldName));
................................................................................
        if RowID>0 then begin
          GetJSONField(P,P,nil,@EndOfObject); // ignore this if explicit RowID
          if EndOfObject in [#0,'}',']'] then
            break else continue;
        end else
        if ReplaceRowIDWithID then
          FieldName := 'ID';
      inc(FieldLen,length(FieldName));
      FieldNames[FieldCount] := FieldName;
      GetSQLValue(FieldCount); // update EndOfObject
      if FieldIsRowID then
        DecodedRowID := GetInteger(pointer(FieldValues[FieldCount]));
      inc(FieldCount);
      if FieldCount=MAX_SQLFIELDS then
        raise EParsingException.Create('Too many inlines in GetJSONObjectAsSQL');
................................................................................
    if RowID>0 then
      raise EParsingException.Create('GetJSONObjectAsSQL(expanded) won''t handle RowID');
    if length(Fields)>MAX_SQLFIELDS then
      raise EParsingException.Create('Too many inlines in GetJSONObjectAsSQL');
    DecodedFieldNames := pointer(Fields);
    FieldCount := length(Fields);
    for F := 0 to FieldCount-1 do begin
      inc(FieldLen,length(Fields[F]));
      GetSQLValue(F); // update EndOfObject
    end;
  end;
end;

procedure TJSONObjectDecoder.Decode(JSON: RawUTF8; const Fields: TRawUTF8DynArray;
  Params: TJSONObjectDecoderParams; RowID: Integer=0; ReplaceRowIDWithID: Boolean=false);
................................................................................
   'insert into %%', 'update % set % where ID=?');
var F: integer;
    P: PUTF8Char;
    tmp: RawUTF8;
begin
  result := '';
  if FieldCount<>0 then begin
    SetLength(tmp,FieldLen+2*FieldCount+12); // max length
    P := pointer(tmp);
    case Occasion of
    soUpdate: begin
      // returns 'COL1=?,COL2=?' (UPDATE SET format)
      for F := 0 to FieldCount-1 do begin
        P := AppendRawUTF8ToBuffer(P,DecodedFieldNames[F]);
        PInteger(P)^ := Ord('=')+Ord('?')shl 8+Ord(',')shl 16;
................................................................................
var F, Len: integer;
    P: PUTF8Char;
begin
  result := '';
  if FieldCount=0 then
    exit;
  if InlinedParams then
    Len := FieldLen+6*FieldCount else
    Len := FieldLen+2*FieldCount;
  if Update then begin
    // returns 'COL1='VAL1',COL2=VAL2' (UPDATE SET format)
    SetLength(result,Len-1); // -1 for last ','
    P := pointer(result);
    for F := 0 to FieldCount-1 do begin
      P := AppendRawUTF8ToBuffer(P,DecodedFieldNames[F]);
      if InlinedParams then begin






>







 







|
>
>







 







|







|
>












|
>







 







|







 







|







 







|







 







|
|







766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
....
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
.....
16701
16702
16703
16704
16705
16706
16707
16708
16709
16710
16711
16712
16713
16714
16715
16716
16717
16718
16719
16720
16721
16722
16723
16724
16725
16726
16727
16728
16729
16730
16731
16732
16733
16734
16735
16736
16737
16738
.....
16740
16741
16742
16743
16744
16745
16746
16747
16748
16749
16750
16751
16752
16753
16754
.....
16760
16761
16762
16763
16764
16765
16766
16767
16768
16769
16770
16771
16772
16773
16774
.....
16808
16809
16810
16811
16812
16813
16814
16815
16816
16817
16818
16819
16820
16821
16822
.....
16856
16857
16858
16859
16860
16861
16862
16863
16864
16865
16866
16867
16868
16869
16870
16871
      / ObjectToJSON() functions and WriteObject method
    - added TTypeInfo.ClassCreate() method to create a TObject instance from RTTI
    - TEnumType.GetEnumNameValue() will now recognize both 'sllWarning' and
      'Warning' text as a sllWarning item (will enhance JSONToObject() process)
    - fix and enhance boolean values parsing from JSON content ("Yes"=true) 
    - implement woHumanReadableFullSetsAsStar and woHumanReadableEnumSetAsComment
      option for JSON serialization and TEnumType.GetEnumNameTrimedAll()
    - fixed potential buffer overflow in TJSONObjectDecoder.EncodeAsSQLPrepared()
    - added ClassInstanceCreate() function calling any known virtual constructor
    - added GetInterfaceFromEntry() function to speed up interface execution,
      e.g. for TServiceFactoryServer (avoid the RTTI lookup of GetInterface) 
    - added TPropInfo.ClassFromJSON() to properly unserialize TObject properties
    - added TSQLPropInfo.SQLFieldTypeName property
    - fixed [f96cf0fc5d] and [221ee9c767] about TSQLRecordMany JSON serialization
    - fixed issue when retrieving a TSQLRecord containing TSQLRecordMany
................................................................................
    /// contains the decoded field names or value
    FieldNames, FieldValues: array[0..MAX_SQLFIELDS-1] of RawUTF8;
    /// Decode() will set a bit for each field set JSON null value
    FieldNull: TSQLFieldBits;
    /// number of fields decoded in FieldNames[] and FieldValues[]
    FieldCount: integer;
    /// size of the TEXT data (in bytes) in FieldValues[]
    FieldValueLen: integer;
    /// size of the TEXT data (in bytes) in FieldNames[]
    FieldNameLen: integer;
    /// set to TRUE if parameters are to be :(...): inlined
    InlinedParams: boolean;
    /// internal pointer over field names to be used after Decode() call
    // - either FieldNames, either Fields[] array as defined in Decode()
    DecodedFieldNames: PRawUTF8Array;
    /// the ID=.. value as sent within the JSON object supplied to Decode()
    DecodedRowID: integer;
................................................................................
          end;
        end else
          // non string params (numeric or null/false/true) are passed untouched
          FieldValues[ndx] := res;
      end;
      end;
    end;
    Inc(FieldValueLen,length(FieldValues[ndx]));
  end;

var FieldName: RawUTF8;
    F: integer;
    FieldIsRowID: Boolean;
begin
  FieldCount := 0;
  FieldValueLen := 0;
  FieldNameLen := 0;
  DecodedRowID := 0;
  FillChar(FieldNull,sizeof(FieldNull),0);
  InlinedParams := Params=pInlined;
  if pointer(Fields)=nil then begin
    // get "COL1"="VAL1" pairs, stopping at '}' or ']'
    DecodedFieldNames := @FieldNames;
    if RowID>0 then begin // insert explicit RowID
      if ReplaceRowIDWithID then
        FieldNames[0] := 'ID' else
        FieldNames[0] := 'RowID';
      FieldValues[0] := Int32ToUtf8(RowID);
      FieldCount := 1;
      FieldNameLen := Length(FieldNames[0]);
      FieldValueLen := Length(FieldValues[0]);
      DecodedRowID := RowID;
    end;
    if P<>nil then
    repeat
      FieldName := GetJSONField(P,P);
      if P=nil then break; // syntax error
      FieldIsRowID := IsRowId(pointer(FieldName));
................................................................................
        if RowID>0 then begin
          GetJSONField(P,P,nil,@EndOfObject); // ignore this if explicit RowID
          if EndOfObject in [#0,'}',']'] then
            break else continue;
        end else
        if ReplaceRowIDWithID then
          FieldName := 'ID';
      inc(FieldNameLen,length(FieldName));
      FieldNames[FieldCount] := FieldName;
      GetSQLValue(FieldCount); // update EndOfObject
      if FieldIsRowID then
        DecodedRowID := GetInteger(pointer(FieldValues[FieldCount]));
      inc(FieldCount);
      if FieldCount=MAX_SQLFIELDS then
        raise EParsingException.Create('Too many inlines in GetJSONObjectAsSQL');
................................................................................
    if RowID>0 then
      raise EParsingException.Create('GetJSONObjectAsSQL(expanded) won''t handle RowID');
    if length(Fields)>MAX_SQLFIELDS then
      raise EParsingException.Create('Too many inlines in GetJSONObjectAsSQL');
    DecodedFieldNames := pointer(Fields);
    FieldCount := length(Fields);
    for F := 0 to FieldCount-1 do begin
      inc(FieldNameLen,length(Fields[F]));
      GetSQLValue(F); // update EndOfObject
    end;
  end;
end;

procedure TJSONObjectDecoder.Decode(JSON: RawUTF8; const Fields: TRawUTF8DynArray;
  Params: TJSONObjectDecoderParams; RowID: Integer=0; ReplaceRowIDWithID: Boolean=false);
................................................................................
   'insert into %%', 'update % set % where ID=?');
var F: integer;
    P: PUTF8Char;
    tmp: RawUTF8;
begin
  result := '';
  if FieldCount<>0 then begin
    SetLength(tmp,FieldNameLen+3*FieldCount+12); // max length
    P := pointer(tmp);
    case Occasion of
    soUpdate: begin
      // returns 'COL1=?,COL2=?' (UPDATE SET format)
      for F := 0 to FieldCount-1 do begin
        P := AppendRawUTF8ToBuffer(P,DecodedFieldNames[F]);
        PInteger(P)^ := Ord('=')+Ord('?')shl 8+Ord(',')shl 16;
................................................................................
var F, Len: integer;
    P: PUTF8Char;
begin
  result := '';
  if FieldCount=0 then
    exit;
  if InlinedParams then
    Len := FieldNameLen+FieldValueLen+6*FieldCount else
    Len := FieldNameLen+FieldValueLen+2*FieldCount;
  if Update then begin
    // returns 'COL1='VAL1',COL2=VAL2' (UPDATE SET format)
    SetLength(result,Len-1); // -1 for last ','
    P := pointer(result);
    for F := 0 to FieldCount-1 do begin
      P := AppendRawUTF8ToBuffer(P,DecodedFieldNames[F]);
      if InlinedParams then begin