Skip to content

Incorrect handling of unnamed components in streaming

Summary

When reading a component from a stream by TStream.ReadComponent the reader must make sure that the name of the read component is unique. An unnamed component is a special case since several components are allowed to have an empty Name property. TReader.ReadRootComponent, however, does not handle this correctly in some cases which can cause crashes in component streaming

System Information

  • Operating system: Win-11
  • Processor architecture: x86-64
  • Compiler version: FPC 3.3.1 i386-win32-win32/win64
  • Device: Computer

Steps to reproduce

Unfortunately this issue is hard to reproduce in FPC alone. I am attaching a Lazarus example in which a label with Hint is streamed; the project is taken from the Lazarus report freepascal.org/lazarus/lazarus#40138 (closed) which I closed to avoid confusion due to the introductory discussion.

To reproduce, run the project in Lazarus and follow the instructions.

Without the patch presented below the project will crash when the streamed label is inserted a second time.

Example Project

ComponentExistsIssue-Project.zip

Possible fixes

reader_emptyname.patch

This patch adds a check against CompName='' to TReader.ReadRootComponent:

function TReader.ReadRootComponent(ARoot: TComponent): TComponent;
...
        if not (csDesigning in Result.ComponentState) then
        begin
          Result.FComponentState :=
            Result.FComponentState + [csLoading, csReading];

          if CompName = '' then    // <--- ADDED
            Result.Name := ''      // <--- ADDED
          else                   
          begin                  
            { We need an unique name }
            i := 0;
            { Don't use Result.Name directly, as this would influence
              FindGlobalComponent in successive loop runs }
            ResultName := CompName;
            Lock;
            try
              while Assigned(FindGlobalComponent(ResultName)) do
              begin
                Inc(i);
                ResultName := CompName + '_' + IntToStr(i);
              end;
              Result.Name := ResultName;
            finally
              Unlock;
            end;
          end;   
        end;
      end;
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information