mirror of
https://github.com/X11Libre/xserver.git
synced 2026-11-15 05:23:40 +00:00
We make two glyphsets, GS1 and GS2, both with the same format depth. GS1 contains any glyph different from the others, let's call it U (unique). GS2 contains two glyphs with the same content, or at least sha1 hash. Let's call them A and B. We first register GS1 and GS2. GS1 has no problems, but before the fix, registering GS2 breaks accounting in `globalGlyphs`. Since there is no check for duplicate glyphs within a single set, we make two `GlyphNew` entries for A and B. Now in the loop at the end, we go over each entry and call `AddGlyph` and `FreeGlyph`: 1. AddGlyph(GS2, A, idA) inserts A into `globalGlyphs` 2. FreeGlyph(A) drops A's refcnt by one, but as it's being held in `globalGlyphs` it's kept alive. 3. AddGlyph(GS2, B, idB) finds A in `globalGlyphs` and reuses it instead of inserting B 4. FreeGlyph(B) drops B's refcnt by one (to zero), and B is freed. Before the fix, the lookup in `globalGlyphs` finds A's entry, and since it isn't a DeletedGlyph, it erroneously removes the `gr->glyph` pointing to `A` and decrements `tableEntries`. This results in `tableEntries` being off-by-one as `A` is still alive. Now, we free the glyphsets in a particular order. Freeing GS1 results in calling FreeGlyph(U), which will decrement U's refcnt to zero, and free U. This clears out the entry in `globalGlyphs` for U and decrements `tableEntries`, which due to the earlier error is now zero. When `FreeGlyphSet` sees this, it clears the `globalGlyphs` for that format depth. Now freeing A or GS2 (which transitively frees A) will try to check the `globalGlyphs` at that format depth, but because it was cleared out this crashes. The fix here is to change FreeGlyph to only free a glyph from `globalGlyphs` that is exactly the glyph being freed, so that FreeGlyph(B) doesn't touch `globalGlyphs` at all. This keeps a proper count in `tableEntries` and prevents `globalGlyphs[fdepth]` from being freed before A is freed. Closes: #1881 Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2264> (cherry picked from commit 81edc9caf557f7a44c5b308742814457de0eb6c0) (cherry picked from commit 716d876142280cd66bdcf000f81428dadd5d3937) Signed-off-by: Octavia Togami <octy@octyl.net>
354 lines
8.8 KiB
Python
354 lines
8.8 KiB
Python
# SPDX-License-Identifier: MIT
|
|
#
|
|
# Render extension protocol request builders
|
|
|
|
import struct
|
|
from dataclasses import dataclass, field
|
|
|
|
# Render minor opcodes
|
|
RenderQueryVersion = 0
|
|
RenderQueryPictFormats = 1
|
|
RenderCreatePicture = 4
|
|
RenderCreateGlyphSet = 17
|
|
RenderFreeGlyphSet = 19
|
|
RenderAddGlyphs = 20
|
|
RenderCompositeGlyphs8 = 23
|
|
RenderCompositeGlyphs16 = 24
|
|
RenderCompositeGlyphs32 = 25
|
|
RenderSetPictureFilter = 30
|
|
|
|
|
|
@dataclass
|
|
class QueryVersionRequest:
|
|
"""RenderQueryVersion request."""
|
|
|
|
opcode: int
|
|
major: int = 0
|
|
minor: int = 11
|
|
|
|
def to_bytes(self, byte_order: str = "<") -> bytes:
|
|
return struct.pack(
|
|
f"{byte_order}BBHII",
|
|
self.opcode,
|
|
RenderQueryVersion,
|
|
3,
|
|
self.major,
|
|
self.minor,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class QueryPictFormatsRequest:
|
|
"""RenderQueryPictFormats request."""
|
|
|
|
opcode: int
|
|
|
|
def to_bytes(self, byte_order: str = "<") -> bytes:
|
|
return struct.pack(
|
|
f"{byte_order}BBH",
|
|
self.opcode,
|
|
RenderQueryPictFormats,
|
|
1,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class PictFormInfo:
|
|
"""Parsed xPictFormInfo (28 bytes).
|
|
|
|
Layout:
|
|
id(4) type(1) depth(1) pad(2) direct(16) colormap(4)
|
|
|
|
direct is xDirectFormat:
|
|
red(2) redMask(2) green(2) greenMask(2)
|
|
blue(2) blueMask(2) alpha(2) alphaMask(2)
|
|
"""
|
|
|
|
id: int
|
|
type: int
|
|
depth: int
|
|
red: int
|
|
red_mask: int
|
|
green: int
|
|
green_mask: int
|
|
blue: int
|
|
blue_mask: int
|
|
alpha: int
|
|
alpha_mask: int
|
|
colormap: int
|
|
|
|
SIZE = 28
|
|
|
|
@classmethod
|
|
def from_bytes(
|
|
cls, data: bytes, offset: int, byte_order: str = "<"
|
|
) -> "PictFormInfo":
|
|
(
|
|
fid,
|
|
ftype,
|
|
fdepth,
|
|
_pad,
|
|
red,
|
|
red_mask,
|
|
green,
|
|
green_mask,
|
|
blue,
|
|
blue_mask,
|
|
alpha,
|
|
alpha_mask,
|
|
colormap,
|
|
) = struct.unpack_from(f"{byte_order}IBBH HHHH HHHH I", data, offset)
|
|
return cls(
|
|
id=fid,
|
|
type=ftype,
|
|
depth=fdepth,
|
|
red=red,
|
|
red_mask=red_mask,
|
|
green=green,
|
|
green_mask=green_mask,
|
|
blue=blue,
|
|
blue_mask=blue_mask,
|
|
alpha=alpha,
|
|
alpha_mask=alpha_mask,
|
|
colormap=colormap,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class QueryPictFormatsReply:
|
|
"""Parsed xRenderQueryPictFormatsReply.
|
|
|
|
Reply layout (32 bytes header):
|
|
type(1) pad(1) sequenceNumber(2) length(4)
|
|
numFormats(4) numScreens(4) numDepths(4) numVisuals(4)
|
|
numSubpixel(4) pad(4)
|
|
|
|
Followed by numFormats xPictFormInfo entries (28 bytes each).
|
|
"""
|
|
|
|
formats: list[PictFormInfo]
|
|
|
|
@classmethod
|
|
def from_reply(cls, data: bytes, byte_order: str = "<") -> "QueryPictFormatsReply":
|
|
"""Parse an X11Reply's raw data into a QueryPictFormatsReply."""
|
|
num_formats = struct.unpack_from(f"{byte_order}I", data, 8)[0]
|
|
|
|
formats = []
|
|
for i in range(num_formats):
|
|
off = 32 + i * PictFormInfo.SIZE
|
|
if off + PictFormInfo.SIZE > len(data):
|
|
break
|
|
formats.append(PictFormInfo.from_bytes(data, off, byte_order))
|
|
|
|
return cls(formats=formats)
|
|
|
|
|
|
@dataclass
|
|
class CreatePictureRequest:
|
|
"""RenderCreatePicture request."""
|
|
|
|
opcode: int
|
|
picture_id: int
|
|
drawable: int
|
|
format_id: int
|
|
value_mask: int = 0
|
|
|
|
def to_bytes(self, byte_order: str = "<") -> bytes:
|
|
return struct.pack(
|
|
f"{byte_order}BBHIIII",
|
|
self.opcode,
|
|
RenderCreatePicture,
|
|
5,
|
|
self.picture_id,
|
|
self.drawable,
|
|
self.format_id,
|
|
self.value_mask,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class CreateGlyphSetRequest:
|
|
"""RenderCreateGlyphSet request."""
|
|
|
|
opcode: int
|
|
glyph_set_id: int
|
|
format_id: int
|
|
|
|
def to_bytes(self, byte_order: str = "<") -> bytes:
|
|
return struct.pack(
|
|
f"{byte_order}BBHII",
|
|
self.opcode,
|
|
RenderCreateGlyphSet,
|
|
3,
|
|
self.glyph_set_id,
|
|
self.format_id,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class _CompositeGlyphsRequestBase:
|
|
"""Base class for RenderCompositeGlyphs{8,16,32} requests.
|
|
|
|
glyph_elts is raw xGlyphElt + glyph ID data.
|
|
Subclasses set _minor_opcode for the glyph size (8/16/32 bit IDs).
|
|
"""
|
|
|
|
_minor_opcode: int = field(default=0, init=False, repr=False)
|
|
|
|
opcode: int = 0
|
|
src_picture: int = 0
|
|
dst_picture: int = 0
|
|
glyph_set: int = 0
|
|
mask_format: int = 0
|
|
op: int = 3 # PictOpOver
|
|
src_x: int = 0
|
|
src_y: int = 0
|
|
glyph_elts: bytes = b""
|
|
|
|
def to_bytes(self, byte_order: str = "<") -> bytes:
|
|
total = 28 + len(self.glyph_elts)
|
|
pad_len = (4 - total % 4) % 4
|
|
length = (total + pad_len) // 4
|
|
|
|
header = struct.pack(
|
|
f"{byte_order}BBH BxH IIII hh",
|
|
self.opcode,
|
|
self._minor_opcode,
|
|
length,
|
|
self.op,
|
|
0, # pad2
|
|
self.src_picture,
|
|
self.dst_picture,
|
|
self.mask_format,
|
|
self.glyph_set,
|
|
self.src_x,
|
|
self.src_y,
|
|
)
|
|
return header + self.glyph_elts + b"\x00" * pad_len
|
|
|
|
|
|
@dataclass
|
|
class CompositeGlyphs8Request(_CompositeGlyphsRequestBase):
|
|
"""RenderCompositeGlyphs8 request (minor opcode 23)."""
|
|
|
|
_minor_opcode: int = field(default=RenderCompositeGlyphs8, init=False, repr=False)
|
|
|
|
|
|
@dataclass
|
|
class CompositeGlyphs16Request(_CompositeGlyphsRequestBase):
|
|
"""RenderCompositeGlyphs16 request (minor opcode 24)."""
|
|
|
|
_minor_opcode: int = field(default=RenderCompositeGlyphs16, init=False, repr=False)
|
|
|
|
|
|
@dataclass
|
|
class CompositeGlyphs32Request(_CompositeGlyphsRequestBase):
|
|
"""RenderCompositeGlyphs32 request (minor opcode 25)."""
|
|
|
|
_minor_opcode: int = field(default=RenderCompositeGlyphs32, init=False, repr=False)
|
|
|
|
|
|
@dataclass
|
|
class FreeGlyphSetRequest:
|
|
"""RenderFreeGlyphSet request."""
|
|
|
|
opcode: int
|
|
glyph_set_id: int
|
|
|
|
def to_bytes(self, byte_order: str = "<") -> bytes:
|
|
return struct.pack(
|
|
f"{byte_order}BBHI",
|
|
self.opcode,
|
|
RenderFreeGlyphSet,
|
|
2,
|
|
self.glyph_set_id,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class AddGlyphsRequest:
|
|
"""RenderAddGlyphs request.
|
|
|
|
Each glyph is described by a (glyph_id, glyph_info, data) tuple.
|
|
glyph_info is an xGlyphInfo: (width, height, x, y, x_off, y_off).
|
|
data is the raw pixel data for that glyph.
|
|
"""
|
|
|
|
opcode: int
|
|
glyph_set_id: int
|
|
glyphs: list[tuple[int, tuple[int, int, int, int, int, int], bytes]] = field(
|
|
default_factory=list
|
|
)
|
|
|
|
def to_bytes(self, byte_order: str = "<") -> bytes:
|
|
nglyphs = len(self.glyphs)
|
|
|
|
# glyph IDs (CARD32 each)
|
|
ids_data = b""
|
|
for gid, _, _ in self.glyphs:
|
|
ids_data += struct.pack(f"{byte_order}I", gid)
|
|
|
|
# xGlyphInfo entries (12 bytes each: HHhhhh)
|
|
infos_data = b""
|
|
for _, info, _ in self.glyphs:
|
|
infos_data += struct.pack(f"{byte_order}HHhhhh", *info)
|
|
|
|
# glyph pixel data (concatenated, each padded to 4-byte boundary)
|
|
pixel_data = b""
|
|
for _, _, data in self.glyphs:
|
|
pixel_data += data
|
|
pad = (4 - len(data) % 4) % 4
|
|
pixel_data += b"\x00" * pad
|
|
|
|
body = ids_data + infos_data + pixel_data
|
|
total = 12 + len(body)
|
|
pad_len = (4 - total % 4) % 4
|
|
length = (total + pad_len) // 4
|
|
|
|
header = struct.pack(
|
|
f"{byte_order}BBHII",
|
|
self.opcode,
|
|
RenderAddGlyphs,
|
|
length,
|
|
self.glyph_set_id,
|
|
nglyphs,
|
|
)
|
|
return header + body + b"\x00" * pad_len
|
|
|
|
|
|
@dataclass
|
|
class SetPictureFilterRequest:
|
|
"""RenderSetPictureFilter request.
|
|
|
|
The filter name is a variable-length string followed by padding,
|
|
then xFixed (CARD32) filter parameter values.
|
|
"""
|
|
|
|
opcode: int
|
|
picture: int
|
|
filter_name: str = "nearest"
|
|
params: list[int] | None = None # xFixed values (CARD32)
|
|
|
|
def to_bytes(self, byte_order: str = "<") -> bytes:
|
|
name_bytes = self.filter_name.encode("ascii")
|
|
name_len = len(name_bytes)
|
|
name_padded = name_bytes + b"\x00" * ((4 - name_len % 4) % 4)
|
|
|
|
param_data = b""
|
|
if self.params:
|
|
for p in self.params:
|
|
param_data += struct.pack(f"{byte_order}I", p)
|
|
|
|
total = 12 + len(name_padded) + len(param_data)
|
|
pad_len = (4 - total % 4) % 4
|
|
length = (total + pad_len) // 4
|
|
|
|
header = struct.pack(
|
|
f"{byte_order}BBH I H xx",
|
|
self.opcode,
|
|
RenderSetPictureFilter,
|
|
length,
|
|
self.picture,
|
|
name_len,
|
|
)
|
|
return header + name_padded + param_data + b"\x00" * pad_len
|