Conversation
0e9bc42 to
bb0984b
Compare
jserv
added a commit
that referenced
this pull request
Jun 8, 2026
…r guard The CI gates surfaced one regression and a cubic-bot review (4451031162) flagged three additional issues. All four are fixed here. CI font-metric regression (build / debug-build / sanitize jobs): - tests/check.c:5516 asserted that "*medium*-r-*--14*" returns the 6x13 BDF metrics (ascent=12, descent=2, max_bounds.width=7), but fonts/6x13.bdf is intentionally not tracked in the repo and the loader returned False on CI runners, gating off the metric overrides in coreFontMetricsForName(). - scripts/embed-bdf-font.py decodes a BDF into a pre-populated unsigned char rows[CHAR_COUNT][HEIGHT] array; the generated src/font-6x13-bitmap.h is checked in as source code rather than as a data file, keeping the BDF asset itself out of the tree. - src/font.c loadFixedBitmapFont() now falls back to the embedded table via useEmbeddedFixedBitmap() when LIBX11_COMPAT_FONT_DIR is unset or its 6x13.bdf is missing, so the fixed-bitmap path is always available and the metric overrides apply unconditionally. Cubic review P3 / src/replay-target.h header guard: - The previous guard _LIBX11_COMPAT_REPLAY_TARGET_H_ used the implementation-reserved underscore-uppercase prefix. Renamed to LIBX11_COMPAT_REPLAY_TARGET_H per C standard reservation rules. Cubic review P3 / src/events.h RESIZE_EVENT_CODE comment: - The doc claimed the payload was (data1<<16 | data2) packed dimensions, but the producer at snapshot.c:142-144 carries width in user.data1 and height in user.data2 as intptr_t. Comment now matches the actual wire format. Cubic review P2 / src/replay-target.c retarget race: - replayTargetWindowId() and replayTargetRootToLocal() each read a single atomic, so a caller using the two together could pick the id from one generation and the root from another if the main thread retargeted between the calls. The result: an XTest event routed to W1 with W2-relative local coords, mis-routing input. - Added a seqlock counter (targetSeq) bumped to odd before the triple write and back to even after, in both replayTargetOffer Window and replayTargetForgetWindow. - New replayTargetTranslateRoot() reads (id, rootX, rootY) under the seqlock with a small retry budget and returns a coherent snapshot plus translated local coordinates. - replayTargetRememberPointer() also uses the seqlock when reading targetRootX/Y so a concurrent retarget cannot combine a new local origin with the previous target's root. - src/xtest.c XTestFakeMotionEvent switches to the new snapshot API. - src/wrapper/sdl-wrapper.c exposes SDL_AtomicAdd which the seqBump uses; previously the dlopen wrapper only forwarded SDL_AtomicGet/ Set/CAS/Lock/Unlock. Verified locally: make check returns 39 ok lines (exit 0).
jserv
added a commit
that referenced
this pull request
Jun 8, 2026
…ter fallback Both gemini and codex flagged a HIGH-severity UAF window in postPointerCrossingEvents: the function locked activePointerWindowLock just long enough to snapshot oldHoverWindow into a local variable, then released the lock and walked the snapshotted XID via buildWindowPathToRoot. A concurrent XDestroyWindow running between the unlock and the path walk passed destroyWindow's clearPointerStateForWindow (lock briefly taken and released) and freed the WindowStruct before the walking thread dereferenced it. clearPointerStateForWindow alone could not close the race because the racing thread already held a stale local copy. - src/events.c postPointerCrossingEvents now holds the lock through buildWindowPathToRoot AND the appendPointerCrossingEvent calls, so a concurrent destroyWindow blocks at clearPointerStateForWindow until the walk completes. Lock-order verification: this function takes activePointer -> putBack (via appendPointer -> appendPutBack); destroyWindow takes putBack first via discardQueuedEventsForWindow and releases before reaching clearPointerStateForWindow, so the two threads never hold both locks simultaneously and there is no circular wait. - queueNestedPointerLeaves was the same shape and gets the same treatment. Codex also flagged a low-severity fallback bug in replayTargetRememberPointer: if all 8 seqlock retries observed either an odd seq or a sequence change, rx/ry stayed at 0, and the next rebasePointerToTargetRoot() converted a local coordinate as if it were root-relative. - src/replay-target.c replayTargetRememberPointer now tracks gotSnapshot explicitly; on retry exhaustion it does a single best- effort read of the current root atomics instead of leaving (0, 0). Gemini also noted that clearPointerStateForWindow omits the spec- mandated LeaveNotify cascade per Xlib 10.5.5 when the hover window is destroyed; that is a real spec gap but does not affect the CI gate or introduce new races, so it is left for a follow-up.
Bring Xlib semantics to spec for the surfaces both clients hit:
- FocusKind tristate (None/PointerRoot/Window) in postFocusChange,
matching Xlib 10.7.1 NotifyNonlinear/NotifyNonlinearVirtual on
transitions that cross the root namespace.
- RevertToParent walks past unviewable ancestors via
isWindowEffectivelyViewable; XGetInputFocus reports the actual
tristate instead of always claiming PointerRoot for None.
- destroyWindow runs auto-revert and discards queued events before
recursing into children, then clears pointer state to avoid UAF
on a stale hover XID; postPointerCrossingEvents and
queueNestedPointerLeaves hold the pointer lock through the full
path walk and event emission.
- Length-counted text path threads size_t through the bitmap font
fallback so XDrawString16 and the embedded 6x13 face render with
the same length checks Xlib applies.
- BDF parser uses strtol with range check instead of atoi.
Add three self-contained subsystems libX11-compat needs to drive a
replay smoke harness from the host:
- src/replay.{c,h}: opcode parser for motion/button/key/wait
sequences fed from LIBX11_COMPAT_REPLAY env.
- src/replay-target.{c,h}: seqlock around the (target window,
root x, root y) triple so XTest readers see a coherent snapshot
while top-level windows map and unmap.
- src/snapshot.{c,h}: PNG capture hook keyed off resize events.
- src/xtest.c + include/X11/extensions/XTest.h: XTest synthetic
input wired through the replay target for deterministic clicks.
The embedded 6x13 fixed face (src/font-6x13-bitmap.h, generated
by scripts/embed-bdf-font.py) keeps the binary BDF out of the
repo while letting font.c fall back when SDL_ttf cannot locate a
matching face on the host.
Regression tests in tests/check.c, test-motif-link.c, and
test-xtest.c cover the focus event cascade, the destroy-while-
hovered race, the length-counted text path, and the replay-target
seqlock under retarget.
Build/CI infrastructure on top of the compat-layer changes:
- mk/motif.mk fetches and builds Motif against libX11-compat under
$(OUT)/motif, runs the Motif demo binaries headless, and gates a Motif
replay smoke (wsm-labels, fileview-done) under Xvfb.
- mk/violawww.mk builds ViolaWWW with the patches under
compat/violawww-patches/ and gates a violawww replay smoke.
- mk/tests.mk introduces a check-unit / check-{smoke,differential}
hierarchy: check-unit is the cheap, sanitizer-safe in-tree subset (no
motif autoconf), and check rolls up unit + motif link/demos + smoke +
differential.
- .github/workflows/ci.yml runs four jobs (build, debug-build, lint,
sanitize) plus a motif job that exercises link, demos, smoke, and the
two differential targets as independent steps. ccache and upstream-src
caches use v2 key prefixes so the sanitize job no longer hits
ASan-poisoned objects from the release-build slot.
- scripts/run-ui-replay.py drives the LIBX11_COMPAT_REPLAY engine under
Xvfb, captures PNGs, and runs the JSON assertion DSL in
tests/ui/assertions/; scripts/run-motif-differential-tests.py and
scripts/run-violawww-differential-tests.py compare against a reference
Xlib stack over SSH.
- compat/motif-patches/ adds two upstream tweaks (ASan-safe string and
printer-list traversal, silencing iconv cascade warnings);
compat/violawww-patches/ collects the eight patches ViolaWWW needs to
render correctly against libX11-compat.
- tests/ui/ ships the replay scripts, assertion JSONs, and the scroll
fixture HTML the violawww smoke serves.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This consolidates differential test that gates the compat stack against the ViolaWWW browser. Affected Xlib surface spans XCopyArea, XGrabPointer, XQueryPointer, XWarpPointer, XGeometry/XWMGeometry, XDrawString family, XListFonts, XPutImage, XDoubleToFixed, and the SDL event/grab pipeline.
Memory safety and contract:
Pointer + grab contract:
Concurrency:
Build:
CI:
Build commands run: make, make check, make
CFLAGS_EXTRA=-DDEBUG_LIBX11_COMPAT check, make STRICT=1.
Summary by cubic
Adds in‑process XTest/replay with resize and snapshot support, and gates deterministic UI smoke tests for Motif and
violawwwacross CI and local runs. Also embeds a 6x13 fixed‑bitmap font and tightens drawing, focus/grab, and event behavior for reliability.New Features
tests/test-xtestadded.violawwwgate: pinned build viamk/violawww.mkwith patches to clear before redraw/scroll/resize and to uselibXpmcompat; new scroll/help replays, assertions, and differential runner.check-unitsplit from differential; CI installsDIFFERENTIAL_PKGSand runs smokes and references.LIBX11_COMPAT_FONT_DIRis unset) with length‑counted text paths; ASCII probe enables deterministic metrics.XRenderSetPictureTransformstub; clampedXDoubleToFixed; top‑level windows start hidden and show on map; debug‑only NULL guards forGET_DISPLAY/GET_GC/GET_WINDOW_STRUCT.Bug Fixes
XCopyAreabounds;XPutImagestride checks and process‑long scratch lock; default opaque alpha for core X11 pixels; present‑flush event on draw.XGetInputFocusreports the real kind; RevertToParent walks past unviewable ancestors; destroy auto‑revert and queued‑event discard; strict grab semantics (active/passive), cursor save/restore, 64‑bitXWarpPointer, NULL‑safeXQueryPointer.XPutImage; event pipe fds init to ‑1 with ordered teardown; replay stops before pipe shutdown; snapshot/resize user events run on the main thread; seqlock root fallback if retries exhaust.STRICT=1applies-Werrorto first‑party only; separate Motif andviolawwwdiff steps; thresholds tuned; Motif fileview smoke stabilized with corrected click coords and delay; README updated withviolawwwexample.Written for commit b8d0fe9. Summary will update on new commits.