📜 ⬆️ ⬇️

Once again about the leak of atoms and the VCL bug

Introduction

Looking through the tape, stumbled upon the article Misuse of atoms and the subtle bug in the VCL . After reading the idea arose to describe another problem in the same area, which is not covered in this article. Our team came across it on its own, then it turned out that this is already a known VCL bug. Development is being done on Delphi 7, and I'm not sure if there is an error in newer versions. Judging by the links given below, there is, as is, a correction. It is not necessary to hope, for obvious reasons, to fix version 7.

The article MrShoor describes the overflow of the so-called. tables of atoms in case the application on Delphi is completed incorrectly, and some atoms are not deleted. It turns out that for overflowing the table of atoms it is not at all necessary to "violently kill" your application. It is quite enough to launch it and close it correctly, but many, many times in a row.
Let's see how this happens:

Description of the overflow mechanism

After receiving another complaint a la "out of memory", we found that the table of atoms is filled with elements of the form ControlOfs <hexadecimal ID>. These elements appear at the start of each application (and our application server runs instances, one for each connection), and remain in the table forever.
')
Consider again the section of code from InitControls in Controls.pas is the same as in the article mentioned above:

WindowAtomString := Format('Delphi%.8X',[GetCurrentProcessID]); WindowAtom := GlobalAddAtom(PChar(WindowAtomStrinjg)); ControlAtomString := Format('ControlOfs%.8X%.8X', [HInstance, GetCurrentThreadID]); ControlAtom := GlobalAddAtom(PChar(ControlAtomString)); RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString)); 


The most recent line also seemed to create an atom. When registering a new type of message, a new atom is also added to the table, which each time has a new name. The problem is that RegisterWindowsMessage, see MSDN, in principle, does not imply the opposite unregister action, since This feature allows multiple programs to share a Message ID together:
It is a registerWindowMessage function.

The message is received. Remains message until the session ends .

This is serious, because we have no leverage on the situation. It is impossible to close such an atom with a third-party application - the program proposed in the article that prompted me to write this post is powerless here. Since the table of atoms has roots in the 16-bit era, which imposes a limit on the size of the table, this annoying bug quickly disabled the server part, because It was not possible to run any Delphi application without rebooting the system.

Here is an example of how a table of atoms looks after 20 iterations of “normal start-correct completion” for a simple program from 1 window:

The atoms that remained just after 20 launches are highlighted in red.

This error has been described in several places, for example:
Detailed error description
Shorter bug report
Well, of course, Stack Overflow

Solution Method

Since the post-fact atom cannot be removed, it is necessary to prevent its creation. Our team followed the IAT hook path, which intercepts the call to RegisterWindowMessageA and if a message is registered with the name of the form ControlOfs <something>, any other identifier that is the same for all applications is registered instead. As it turned out, he didn’t necessarily have to be unique, which is also indicated in the bug report to which I already referred .

The hook code is trivial, as is the installation mechanism. Moreover, there are ready-made Delphi libraries for IAT hooks on the Internet. The hook itself simply checks as quickly as possible whether the message being registered does not match the ControlOfs prefix, and, if it does, it replaces RM_GetObjectInstance, a similar identifier that is the same for all applications, with the original RegisterWindowMessage.

Hopefully this will help someone avoid long and difficult debugging.

Source: https://habr.com/ru/post/217333/


All Articles