fix(glx): terminate the extension string with a space, as Xorg does - #120
Merged
Merged
Conversation
The GLX extension string yserver sends is not space-terminated; Xorg's is (glx/extension_string.c:144-145). libGLX_nvidia loses the final token of an unterminated list -- costing us the GLX_SGIX_fbconfig we advertise -- and additionally withholds GLX_ARB_get_proc_address from the client extension string, which aborts libepoxy and kills kwin_x11. Design at rev 3 after two Opus review rounds (REJECT, then APPROVE WITH EDITS); plan after one round (APPROVE WITH EDITS). Evidence is six controlled rows with preserved artifacts, produced with a new X proxy that mutates replies so a working XWayland can be degraded toward yserver's shape without a KMS session.
libGLX_nvidia loses the final token of an unterminated GLX extension list and additionally withholds GLX_ARB_get_proc_address from the client extension string. The first cost us the GLX_SGIX_fbconfig we do advertise; the second aborts libepoxy and kills kwin_x11 with SIGABRT. Xorg writes a space after every enabled extension (glx/extension_string.c:144-145). Apply the terminator when a chunk is appended so the invariant holds for extensions added later.
glx_extension_string_contains_sgix_fbconfig re-implemented the builder's body in the test and asserted against its own copy, so it would have passed if the function were deleted -- and it did pass throughout the missing-terminator defect. Call the real function, and match whole tokens rather than substrings.
Owner
|
Thx. Not sure why you keep trying to make KDE use TFP, this is known impossible with the nvidia proprietary driver. |
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.
fix(glx): terminate the extension string with a space, as Xorg does
Branch:
glx-extension-string-terminator, offmaster@f4c0b60c.The whole fix is one byte. Finding it took eight wrong hypotheses, so the
design doc is longer than the diff — deliberately, so nobody has to
re-derive it.
Problem
glx_extension_stringjoined tokens with separating spaces and ended thestring at the last token. Xorg writes
' 'after every enabledextension (
glx/extension_string.c:144-145):so every Xorg extension string ends with a space before the NUL. Ours did
not.
libGLX_nvidiaresponds to an unterminated list by losing its lasttoken, and separately by dropping
GLX_ARB_get_proc_addressfrom theclient extension string it computes. The first silently cost us the
GLX_SGIX_fbconfigwe do advertise — the anomaly that made this so hardto find, since the driver was withholding an extension we were sending.
The second makes libepoxy abort:
which killed
kwin_x11with SIGABRT on every session.Mesa's libGLX tokenises correctly and never noticed, which is why this
looked like an NVIDIA problem for four days when it was ours.
Evidence
An X proxy (
glxmangle.py) sits between a client and a live XWayland andrewrites only the
QueryServerString(GLX_EXTENSIONS)reply, so aworking server can be degraded toward yserver's shape one variable at a
time. Oracle is
glXQueryExtensionsString(dpy, 0)— the same callepoxy_has_glx_extensionmakes.get_proc_addressSGIX_fbconfigRows 1/2 and 3/4 are one-variable pairs — identical tokens, differing by
the single trailing byte. Rows 2 and 5 separate the two effects: the lost
token is whichever one is last (
GLX_SGIX_fbconfigsurvives when itisn't), while
GLX_ARB_get_proc_addressis lost regardless of position.Every row's injected string is preserved with
repr()and byte length in~/yserver-glx-logs/2026-08-07-terminator-evidence/. Corroboratedindependently by an earlier capture taken on real KMS with no proxy in
the path (
2026-08-06-defect-d-ab/v3-xwayland-exts-probe.txt).Hardware gate, RTX 5060 Ti / NVIDIA 610.57.04 / open kernel modules,
against a preserved pre-fix baseline:
epoxy aborta en glXGetProcAddressARBepoxy resuelve glXGetProcAddressARBThe server log shows the string going out terminated:
... GLX_EXT_libglvnd GLX_SGIX_fbconfig ".The fix
The terminator is applied when a chunk is appended rather than added once
at the end, so the builder's output ends in a terminator by construction
and a future extension cannot reintroduce the defect:
SERVER_EXTENSIONSstays a space-separated constant and is notmodified — keeping the terminator in one place avoids a second convention
where callers must remember whether a given constant is self-terminating.
This mirrors Xorg, where
known_glx_extensions[].nameare bare tokens andextension_string.c:144supplies the space.Both reply paths already route through this one builder — opcode 19
(
QueryServerString/GLX_EXTENSIONS) and opcode 18(
QueryExtensionsString) — so both are fixed by construction.ngrows by one; the padded reply is unchanged in size in both realconfigurations (232→233 bytes of string,
n233→234, padded stays 236;and 260→261,
n261→262, padded stays 264).A second, unrelated defect this turned up
glx_extension_string_contains_sgix_fbconfigdid not callglx_extension_string. Its body said so verbatim — "glx_extension_string()is a private fn; mirror its logic here" — and asserted against its own
copy of the builder, so it would have passed if the function were deleted,
and it passed throughout this defect. It calls the real function now, and
matches whole tokens rather than substrings.
Known limitation — KWin still does not composite on NVIDIA
Worth reading before merging, because this change moves the failure rather
than removing it.
KWin now starts compositing instead of aborting, but windows get no
texture: no wallpaper, panel renders grey, menus wrong. Traced to
GlxBackend::infoForVisual(kwin-x11 6.6.6), which skips every fbconfiglacking
GLX_BIND_TO_TEXTURE_RGB(A)_EXT; we emit those attributes onlywhen
glx_tfp_supported, which is forced false on NVIDIA. With nosurviving config,
GlxPixmapTexture::createreturns false beforereaching
glXBindTexImageEXT— hence no epoxy abort and zeroGLX::CreatePixmapin the trace.Un-suppressing TFP was measured and is worse: KWin then finds a
config, calls
glXBindTexImageEXT, and libepoxy aborts, becauselibGLX_nvidiadoes not listGLX_EXT_texture_from_pixmapin itsclient extension string — verified against XWayland, which does
advertise it server-side and still gets a client string without it. So
this is a driver-side limitation, not a yserver defect, and not something
this PR can address. Applications are unaffected: Steam and games run on
the GPU.
Two further divergences from Xorg were found while investigating, measured
not to cause any of the above, and deliberately left out to keep this a
one-variable change (both recorded in the design's "Deferred" section):
GetVisualConfigsdeclares 18 properties where Xorg declares 40, andGetFBConfigsdeclares 33 attributes where Xorg declares 44. The second isnot a drop-in — implemented as an experiment it broke Mesa outright
(
No matching fbConfigs or visuals found), becausedriConfigEqualscalar-compares those attributes.
Notes
cargo clippy --all-targets -- -D warningsclean,
cargo +nightly fmt --checkclean. All four commits signed.for presenting measurements whose artifacts had not been preserved) and
the implementation plan are included under
docs/superpowers/.docs/status.mdupdated.🤖 Generated with Claude Code