When generics and VCL controls bite you: memory overwrites when you show the control usually ending up in access violations


Recently I got bitten by the 2013 reported http://qc.embarcadero.com/wc/qcmain.aspx?d=112101 (too bad the site is gone and the WayBack machine doesn’t have it archived) as a result of [WayBack] delphi – Why do I get access violations when a control’s class name is very, very long? – Stack Overflow.
It got reentered as [RSP-18399] Buffer overflow in TWinControl.CreateParams() – Embarcadero Technologies but since that requires logon, it’s not search machine indexed so it’s very hard to find unless you know where to look.
So I spent a quite some time to find out what was wrong:
Since Delphi 1, the [WayBack] Controls.TCreateParams Record has a 64-byte WinClassName field that’s blindingly copied by the TWinControl.CreateParams without range checking.
The structure is used by the [WayBack] TWinControl.CreateWnd Method to call the Windows API [WayBack] RegisterClass function that takes a [WayBack] WNDCLASS structure with a lpszClassName field that supports up to 256 characters and it fails when it’s longer.
That overwrite cause spurious other errors depending on the memory that gets overwritten. It took almost a day to figure out the cause of the error was this field, then an hour or to track that down to the long class names created by generic code.
To my surprise, I found back [WayBack] This issue caused coworkers and me quite a few hours wasted:Long story short – refactor some forms/frames to class names longer than 64 chars and boom… – Stefan Glienke – Google+.
As of Delphi 8 (yes, that version that a lot of people want to forget, but did bring a few good things), the structure was defined as below, and the code intialising also got improved:
Params.WinClassName := ClassName;

Params.WinClassName := Format(‘%s.%d’, [Params.WinClassName, AppDomain.CurrentDomain.GetHashCode]);
So there it’s a string that – if it is too long – will get rejected by the Windows API anyway just like the native Delphi VCL implementation should have done 20+ years ago.
The sad part for FMX users: that structure and code got blindingly copied to the FMX.Controls.Win unit.

TCreateParams = record
Caption: string;
Style: DWORD;
ExStyle: DWORD;
X, Y: Integer;
Width, Height: Integer;
WndParent: HWND;
Param: IntPtr;
WindowClass: TWndClassInfo;
WndProc: TFNWndProc;
WinClassName: string;
TCreateParams = record
Caption: PChar;
Style: DWORD;
ExStyle: DWORD;
X, Y: Integer;
Width, Height: Integer;
WndParent: HWnd;
Param: Pointer;
WindowClass: TWndClass;
WinClassName: array[0..63] of Char;


Comments are closed.