Recently, I’ve found a bug in JWSCL. It happened that the function GetInheritanceSourceW didn’t work for me anymore. A long time ago I used this function for the last time and now again. However the strange thing was that the function returned no error but it didn’t fill a result array with expected inheritance data. The result was always zero for all members of the structure. What happened?

After long back and forth trying to find the error by reviewing codes, I decided to go back to an older version of JWSCL. I had to get back about 300 revisions (commits) to find a working JWSCL code. However I still couldn’t find out what the difference was until…

Well, after the 301th revision (anywhere near that) I decided to rewrite the codes which maintain the access control list and elements for WinAPI functions (structures like ACL, ACE_HEADER) . I conducted intensive tests but it seemed that this particular function GetInheritanceSourceW wasn’t tested at all! Shame on me.

… until I could compare old, but working ACL structures with new ones. Each ACL consists of access control elements (ACE) which usually look like this:

_ACCESS_XXX_ACE = record
 Header: ACE_HEADER;
 Mask: ACCESS_MASK;
 SidStart: DWORD;
end;

(XXX stands for ALLOW or DENY.)

This record is much more complicated than it looks. If you need to find out its real size you cannot just use the sizeof operator. Some background information will explain why. An ACE consists of a header which is also a structure (but not the problem here), its access mask (no problem again) and a SidStart. What about the Start in Sid? Well, a Security Identifier (SID – the user’s ID) is assigned to every ACE. But how does it work? A SID is a variable length structure. And so usually, we store a SID into a structure and retrieve a pointer to it. But a DWORD is apparently not a pointer and it would not be a good idea to just typecast it to DWORD. The main idea is that SidStart is a placeholder where the SID structure starts. If you’re ever going to create an ACE structure you need to create a block of memory that can hold the first two members of the ACE structure and the SID you want to store. So actually the ACE structure looks like this:

_ACCESS_XXX_ACE = record
 Header: ACE_HEADER;
 Mask: ACCESS_MASK;
 Sid: array[0..variable_sid_size-1] of byte;
end;

As you already figured out, this structure is invalid in Delphi. However it should just show you how the structure is intended to be used.

The bug, this article is about, came from the fact that in addition to the size of the structure, the size of the SID was added. So in total 4+4+4+x = 12+x bytes were returned where x is size of the SID.
The real size is in fact: 4+4+x = 8+x bytes.

The reason why this can be a problem comes from the fact that these ACE structures are stored in a row in memory. If you jump 12+x bytes from the first ACE structure you’ll get to the second one. 12+x bytes more you have the 3rd one and so on. The size of an ACE structure is applied to the AddAce function so it knows how far to jump to get to a specific ACE. However if a function doesn’t care about the size and uses the “correct” 8+x bytes to jump – well, it gets messy.
IMO this happened here.

Many functions obviously don’t care about this because they use the official API (GetAce etc) . I didn’t find this problem because I’ve used a lot of JWSCL functions which internally convert the ACL classes to C-structures with no problem most of the WinAPI functions. However GetInheritanceSourceW is a big function which is messing around with the ACL directly to obtain the correct inheritance path. I didn’t try to understand it completely because I’ve already created a method which simulates the behavior of the original. I call it TJwSecureFileObject.GetFileInheritanceSource (currently only files are supported).

Strange things can happen if you dive deep into Win32 programming. As an advice you should always check every byte several times over and over to avoid or find such errors.

Happy programming!

Update

This error was fixed in revision 761 of 0.9.2.a branch and, of course, the developer version (trunk).