[Xcb] [PATCH libxcb 4/4 V2] generator: fix align-pads for switches which start at unaligned pos

Christian Linhart chris at DemoRecorder.com
Tue Sep 9 10:50:28 PDT 2014


On 09/09/14 18:35, Ran Benita wrote:
> On Tue, Sep 02, 2014 at 06:13:04PM +0200, Christian Linhart wrote:
>> Fix the alignment computation inside switches which start at
>> an unaligned pos.
>> This affects both explicit and implicit align pads.
> 
> Can you describe the problem a bit more? Like, what the current code
> does wrong? (What happens if you add the <pad align="4"> mentioned
> below?).

The current code computes alignment based on the number of bytes counted
from the start of the *switch*, but it should compute alignment based on
the number of bytes counted from the start of the enclosing *struct*.
( or better: from the start of the enclosing protocol frame, like
request, reply, ... )

This is no problem if the switch starts at an aligned position.

But we have several examples in XInput where a switch starts at position 2
which is clearly not 4 byte aligned.

Simplified Example:
    <struct name="Foo">
        <field type="CARD16" name="class_id" />
        <switch name="data">
            <fieldref>class_id</fieldref>
            <case name="bar">
                <enumref ref="Classes">Bar</enumref>
	        <field type="Baz" name="myBaz" />
	        <pad align="4" />
	        <field type="CARD32" name="bar" />
            </case>
            ...
        </switch>
    </struct>

Let us assume that 
* class_id has value "Bar" so that case "bar" is chosen, 
* and "myBaz" is a var-sized field so that the align pad makes sense.
  but lets assume it has size 3 in this example

Then this *should* have the following layout:

	Start-Offset	Start-Offset	Size	Field
	rel		rel		in
	to struct	to switch	Bytes
	0		n/a		2	class_id
	2		0		3	myBaz
	5		3		3	align-pad. 
	8		6		4	bar

	To get 4-byte alignment this has to have size 3 because 5 + 3 = 8 which is 4-byte aligned


The current code bases the alignment on the start-offset 
of the switch. So the align-pad will get a size of 1 because 3 + 1 = 4 which is 4-byte aligned.
Therefore, "bar" will start on offset 6 relative to the start of the struct
and therefore "bar" is *not* 4-byte aligned in the struct, even though it is after a 4-byte align-pad.

So, the current code creates the following, which is not correct:

	Start-Offset	Start-Offset	Size	Field
	rel		rel		in
	to struct	to switch	Bytes
	0		n/a		2	class_id
	2		0		3	myBaz
	5		3		1=WRONG	align-pad. 
	6		4		4	bar

***

In the code, the start-offset relative to the start of the switch
is somehow [1] represented by variable "xcb_block_len".
xcb_padding_offset corrects for that so it takes into account
that the switch starts at a non-4-byte-aligned offset.

Of course, deriving "xcb_padding_offset" from the pointer value
assumes that the protocol data is store at a 4-byte aligned address
which is usually a safe assumption.

BTW, On some hardware platforms ( like SPARC ), 
unaligned data-access leads to a crash. ( with a SIGBUS on SPARC )
So, there, the protocol data has to be stored at an aligned address
or otherwise the program would crash very very soon.
Besides performance considerations thats probably
the main reason why all 4-byte values in the X-protocol
are stored on 4-byte aligned offsets relative to the 
start of the protocol frame. ( same with 2-byte values ).

I hope I could explain this in a comprehensible way.

Footnotes:
[1] Above, I write "somehow" because xcb_block_len 
    is set to 0 after an align-pad. 
    This probably creates other problems,
    when a 2-byte align is later followed by 4-byte align.

    So there probably needs to be another fix in that area, 
    but that is unrelated to this one.
    I'll create another patch for that unrelated, old potential bug.
    I will make that other patch in an extra patchset
    because it affects all code and may need more
    thorough review and testing.

    We probably should use ( xcb_buffer_len + xcb_padding_offset )
    as bases for alignment, and update xcb_buffer_len
    before computing the alignment.

    I have to think about this second problem a bit
    and make a testcase.
    I just realized it while writing the explanation. :-)


> 
>> The alignment offset is derived from the lowest 3 bits of
>> the pointer to the protocol-data at the start of the switch.
>> This is sufficient for correcting all alignments up to 8-byte alignment.
>> As far as I know there is no bigger alignment than 8-byte for the
>> X-protocol.
> 
> This sounds undefined-behavior-y to me... Maybe it's not...
It needs the assumption that the protocol-data is stored
at an aligned address.
The address needs to be 4-byte aligned if we deal with 4-byte align-pads.
I think we can generally assume that. 
But it is not ideal.

If protocol data is 4-byte aligned then every request, reply, event, etc
starts on a 4-byte aligned address because all X11 protocol frames
have a size which is a multiple of 4.

> Is there no easy way to figure out this number "statically"?

Unfortunately, this is not possible. :-(
(Well, it were be possible if we'd add an additional parameter to 
all of those functions. But that'd change the API in incompatible ways.)

For a discussion of that, see the messages 
    <53FF3818.10208 at DemoRecorder.com>
    <53FF450D.5040800 at opentext.com>
in the discussion for [PATCH proto 2/2].

In that discussion we reached agreement that
deriving the alignment offset from the pointer is
the lesser evil.

But maybe we need to discuss that again.

> 
>> Example:
>> struct InputState, where the switch starts after two 1-byte fields,
>> which is a 2 byte offset for 4-byte and 8-byte alignment.
>>
>> The previous problem can be demonstrated when adding a
>> <pad align="4"/> at the end of case "key".
>>
>> (Or when finding a testcase which reports the case "valuator" not
>> at the last position of the QueryDeviceState-reply.
>> I didn't find such a testcase, so I have used the pad align
>> as described above.)
> 
> This patch creates a lot of
> 
>     xcb_padding_offset = 0;
>     <...>
>     ... + xcb_padding_offset ...
> 
> Which is a no-op. Is looks like if `is_switch` is `False` you can avoid
> generating those?
Yes, I can do that. 
This will then change muss less code, which will make a review
of the generator-output easier.
I'll change the patch accordingly 
and post a new version of this patch
(after testing it of course)

> 
>> V2: patch modified in order to fix bugs which I found when working on the
>> next issue:
>> * xcb_padding_offset has to be set 0 when xcb_block_len is set 0
>> * xcb_padding_offset cannot be "const" therefore
>> * for unpack and unserialize, the padding_offset must computed
>>   from _buffer instead of from the aux_var.
> 
> There are some tabs mixed with spaces below which doesn't go well in
> python. But in the QueryDeviceState-V3 branch it is already fixed.
Sorry for the tabs.
I try to avoid these, but manual stuff is not perfect. :-(
However, since today, they get auto-corrected before being added to the repo. :-)

Chris

> 
> I'll wait for you reply to look at this further.
> 
> Ran
> 


More information about the Xcb mailing list