fix: close the tail items from the targeted re-audit

Resolves the 13 rows still partial in docs/rewrite/notes/review-rv4-
codex.md (details in final-Z5.md):
- Escaped config strings are decoded to raw bytes at every OS boundary:
  Flatpak cwd/argv to D-Bus, screen-file paths, the default config
  template, GHOSTTY_RESOURCES_DIR/TERMINFO/shell integration/
  bindtextdomain consumers, custom shader loading, list-themes hyperlinks.
- osbytes escapes literal U+FDD0 on non-Unix too (bijective everywhere);
  the GTK paste dialog only decodes text that was actually encoded.
- shadertoy: whole-array uses of iChannelTime rebuild a plain float[4]
  from the std140-padded members (naga-validated, MSL offsets checked).
- libghostty-vt: the OSC parser holds caller-allocated storage inline
  and command payloads borrow from it, so ghostty_osc_end performs no
  global allocations (verified from Rust and C).
- crash: aarch64 Linux minidump context fixed; macOS fatal signals record
  siginfo, registers and a fault-safe stack walk.
- tripwire: selectionString map allocation-failure cleanup test ported.
- libghostty-vt emits the versioned soname libghostty-vt.so.0 at build
  time (no patchelf needed at install).

Test Plan:
- cargo test --workspace (3342 passed)
- just check-macos; just check-macos x86_64-apple-darwin;
  just check-windows; just check-wasm
- cargo check --target aarch64-unknown-linux-gnu -p ghostty-crash
This commit is contained in:
ddidderr committed 2026-10-12 08:45:21 +02:00
1 parent bc297c823b
commit 184cdd5c62
49 files changed
+1785 -557

No files matched your search

+45 -6
View File
@@ -124,7 +124,7 @@ pub fn load_from_files(paths: &[PathItem], target: Target) -> Result<Vec<String>
PathItem::Required(p) => (p, false),
};
let shader = match load_from_file(Path::new(path), target) {
let shader = match load_from_file(&ghostty_os::osbytes::config_str_to_path(path), target) {
Ok(s) => s,
Err(Error::Io(e)) if optional && e.kind() == std::io::ErrorKind::NotFound => {
continue;
@@ -262,6 +262,22 @@ fn adapt_for_naga(full: &str) -> String {
.replacen("#version 430 core", "#version 460 core", 1)
.replace(" uniform ", " ")
.replacen("layout(location = 0) in vec4 gl_FragCoord;", "", 1)
// std140 pads `float iChannelTime[4]` to stride 16, which naga types
// differently from a plain local `float[4]`, so whole-array loads and
// copies (`float t[4] = iChannelTime;`, passing it to a function) do
// not validate. The block member is renamed and `iChannelTime` becomes
// a function that reconstructs the plain array from the padded
// elements; element reads (`iChannelTime[i]`) keep working.
.replacen(
" float iChannelTime[4];\n",
" float iChannelTimeUbo[4];\n",
1,
)
.replacen(
"\n#define CURSORSTYLE_BLOCK ",
"\nfloat[4] iChannelTime_plain() {\n return float[4](iChannelTimeUbo[0], iChannelTimeUbo[1], iChannelTimeUbo[2], iChannelTimeUbo[3]);\n}\n#define iChannelTime iChannelTime_plain()\n\n#define CURSORSTYLE_BLOCK ",
1,
)
.replacen(
"layout(binding = 0) uniform sampler2D iChannel0;",
"layout(set = 0, binding = 0) uniform texture2D iChannel0_tex;\n\
@@ -803,10 +819,32 @@ mod tests {
#[test]
fn msl_uniform_block_matches_std140_upload_layout() {
use std::mem::offset_of as o;
// A shader that reads the scalar array so the accessor path is used.
let user = "void mainImage(out vec4 c, in vec2 p) { \
int i = int(p.x) & 3; c = vec4(iChannelTime[i], iChannelTime[2], iTime, iSampleRate); }";
check_msl_layout(
"void mainImage(out vec4 c, in vec2 p) { \
int i = int(p.x) & 3; c = vec4(iChannelTime[i], iChannelTime[2], iTime, iSampleRate); }",
);
}
#[test]
fn whole_array_copy_of_padded_uniform_validates_and_keeps_layout() {
// Whole-array loads/copies of the std140-padded `iChannelTime` must
// validate (naga types the padded array differently from a plain
// local `float[4]`) and not disturb the MSL block layout.
let user = "float sum4(float a[4]) { return a[0] + a[1] + a[2] + a[3]; }\n\
void mainImage(out vec4 c, in vec2 p) { \
float t[4] = iChannelTime; float u[4]; u = iChannelTime; \
c = vec4(t[1] + u[2], sum4(iChannelTime), iChannelTime[3], iTime); }";
let msl = check_msl_layout(user);
// The block member keeps the padded wrapper and is read per element.
assert!(msl.contains(".iChannelTimeUbo.inner[0].x"), "{msl}");
// GLSL output (OpenGL) validates too.
let compiled = compile_shadertoy(user, None).unwrap();
glsl_from_compiled(&compiled).unwrap();
}
fn check_msl_layout(user: &str) -> String {
use std::mem::offset_of as o;
let compiled = compile_shadertoy(user, None).unwrap();
let msl = msl_from_compiled(&compiled).unwrap();
let (members, size) = msl_globals_layout(&msl);
@@ -817,7 +855,7 @@ mod tests {
("iTimeDelta", o!(Uniforms, time_delta)),
("iFrameRate", o!(Uniforms, frame_rate)),
("iFrame", o!(Uniforms, frame)),
("iChannelTime", o!(Uniforms, channel_time)),
("iChannelTimeUbo", o!(Uniforms, channel_time)),
("iChannelResolution", o!(Uniforms, channel_resolution)),
("iMouse", o!(Uniforms, mouse)),
("iDate", o!(Uniforms, date)),
@@ -856,7 +894,8 @@ mod tests {
}
assert_eq!(size, std::mem::size_of::<Uniforms>());
// Reads of the padded array take the first lane.
assert!(msl.contains(".iChannelTime.inner["), "{msl}");
assert!(msl.contains(".iChannelTimeUbo.inner["), "{msl}");
assert!(msl.contains("].x"), "{msl}");
msl
}
}