chore: import upstream snapshot with attribution
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:02:48 +08:00
commit bd44b5aa08
5770 changed files with 506778 additions and 0 deletions
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
# Hand-written seed corpus: binary files, track as-is
corpus/parser-initial/** binary
corpus/stream-initial/** binary
# Generated/minimized corpora: binary, mark as generated
corpus/parser-cmin/** binary linguist-generated=true
corpus/parser-min/** binary linguist-generated=true
corpus/stream-cmin/** binary linguist-generated=true
corpus/stream-min/** binary linguist-generated=true
+9
View File
@@ -0,0 +1,9 @@
# Build artifacts
.zig-cache/
zig-out/
# AFL++ outputs
afl-out/
# Corpus trace files
corpus/**/.traces/
+48
View File
@@ -0,0 +1,48 @@
# AFL++ Fuzzer for Libghostty
- Build all fuzzer with `zig build`
- The list of available fuzzers is in `build.zig` (search for `fuzzers`).
- Run a specific fuzzer with `zig build run-<name>` (e.g. `zig build run-parser`)
- Corpus directories follow the naming convention `corpus/<fuzzer>-<variant>`
(e.g. `corpus/parser-initial`, `corpus/stream-cmin`).
- Do NOT run `afl-tmin` unless explicitly requested — it is very slow.
- After running `afl-cmin`, run `corpus/sanitize-filenames.sh`
before committing to replace colons with underscores (colons are invalid
on Windows NTFS).
## Important: stdin-based input
The instrumented binaries (`afl.c` harness) read fuzz input from **stdin**,
not from a file argument. This affects how you invoke AFL++ tools:
- **`afl-fuzz`**: Uses shared-memory fuzzing automatically; `@@` works
because AFL writes directly to shared memory, bypassing file I/O.
- **`afl-showmap`**: Must pipe input via stdin, **not** `@@`:
```sh
cat testcase | afl-showmap -o map.txt -- zig-out/bin/fuzz-stream
```
- **`afl-cmin`**: Do **not** use `@@`. Requires `AFL_NO_FORKSRV=1` with
the bash version due to a bug in the Python `afl-cmin` (AFL++ 4.35c):
```sh
AFL_NO_FORKSRV=1 /opt/homebrew/Cellar/afl++/4.35c/libexec/afl-cmin.bash \
-i afl-out/fuzz-stream/default/queue -o corpus/stream-cmin \
-- zig-out/bin/fuzz-stream
```
If you pass `@@` or a filename argument, `afl-showmap`/`afl-cmin`
will see only ~4 tuples (the C main paths) and produce useless results.
## Replaying crashes
Use `replay-crashes.nu` (Nushell) to list or replay AFL++ crash files.
- **List all crash files:** `nu replay-crashes.nu --list`
- **JSON output (for structured processing):** `nu replay-crashes.nu --json`
Returns an array of objects with `fuzzer`, `file`, `binary`, and `replay_cmd`.
- **Filter by fuzzer:** `nu replay-crashes.nu --list --fuzzer stream`
- **Replay all crashes:** `nu replay-crashes.nu`
Pipes each crash file into its fuzzer binary via stdin and exits non-zero
if any crashes still reproduce.
+133
View File
@@ -0,0 +1,133 @@
# AFL++ Fuzzer for Libghostty
This directory contains [AFL++](https://aflplus.plus/) fuzzing harnesses for
libghostty-vt (Zig module).
## Fuzz Targets
| Target | Binary | Description |
| -------- | ------------- | ------------------------------------------------------- |
| `osc` | `fuzz-osc` | OSC parser with allocator (`osc.Parser.next` + `end`) |
| `parser` | `fuzz-parser` | VT parser only (`Parser.next` byte-at-a-time) |
| `stream` | `fuzz-stream` | Full terminal stream (`nextSlice` + `next` via handler) |
The osc target directly fuzzes the `osc.Parser` with an allocator enabled,
exercising the allocating writer code paths for large payloads. The first
byte selects the terminator variant (BEL, ST, or missing). Seeds cover OSC
52, 66, 133, 3008, 1337, and 5522.
The stream target creates a small `Terminal` and exercises the readonly
`Stream` handler, covering printing, CSI dispatch, OSC, DCS, SGR, cursor
movement, scrolling regions, and more. The first byte of each input selects
between the slice path (SIMD fast-path) and the scalar path.
## Prerequisites
Install AFL++ so that `afl-cc` and `afl-fuzz` are on your `PATH`.
- **macOS (Homebrew):** `brew install aflplusplus`
- **Linux:** build from source or use your distro's package (e.g.
`apt install afl++` on Debian/Ubuntu).
## Building
From this directory (`test/fuzz-libghostty`):
```sh
zig build
```
This compiles Zig static libraries for each fuzz target, emits LLVM bitcode,
then links each with `afl.c` using `afl-cc` to produce instrumented binaries
at `zig-out/bin/fuzz-osc`, `zig-out/bin/fuzz-parser`, and `zig-out/bin/fuzz-stream`.
## Running the Fuzzer
Each target has its own run step:
```sh
zig build run-osc # Run the OSC parser fuzzer
zig build run-parser # Run the VT parser fuzzer
zig build run-stream # Run the VT stream fuzzer
```
Or invoke `afl-fuzz` directly:
```sh
afl-fuzz -i corpus/stream-initial -o afl-out/stream -- zig-out/bin/fuzz-stream @@
```
The fuzzer runs indefinitely. Let it run for as long as you like; meaningful
coverage is usually reached within a few hours, but longer runs can find
deeper bugs. Press `ctrl+c` to stop the fuzzer when you're done.
## Finding Crashes and Hangs
After (or during) a run, results are written to `afl-out/<target>/default/`:
```
afl-out/stream/default/
├── crashes/ # Inputs that triggered crashes
├── hangs/ # Inputs that triggered hangs/timeouts
└── queue/ # All interesting inputs (the evolved corpus)
```
Each file in `crashes/` or `hangs/` is a raw byte file that triggered the
issue. The filename encodes metadata about how it was found (e.g.
`id:000000,sig:06,...`).
## Reproducing a Crash
Replay any crashing input by piping it into the harness:
```sh
cat afl-out/stream/default/crashes/<filename> | zig-out/bin/fuzz-stream
```
## Corpus Management
After a fuzzing run, the queue in `afl-out/<target>/default/queue/` typically
contains many redundant inputs. Use `afl-cmin` to find the smallest
subset that preserves full edge coverage, and `afl-tmin` to shrink
individual test cases.
> **Important:** The instrumented binary reads input from **stdin**, not
> from file arguments. Do **not** use `@@` with `afl-cmin`, `afl-tmin`,
> or `afl-showmap` — it will cause them to see only the C harness
> coverage (~4 tuples) instead of the Zig VT parser coverage.
### Corpus minimization (`afl-cmin`)
Reduce the evolved queue to a minimal set covering all discovered edges:
```sh
AFL_NO_FORKSRV=1 afl-cmin.bash \
-i afl-out/stream/default/queue \
-o corpus/stream-cmin \
-- zig-out/bin/fuzz-stream
```
`AFL_NO_FORKSRV=1` is required because the Python `afl-cmin` wrapper has
a bug in AFL++ 4.35c. Use the `afl-cmin.bash` script instead (typically
found in AFL++'s `libexec` directory).
### Windows compatibility
AFL++ output filenames contain colons (e.g., `id:000024,time:0,...`), which
are invalid on Windows (NTFS). After running `afl-cmin`,
rename the output files to replace colons with underscores before committing:
```sh
./corpus/sanitize-filenames.sh
```
### Corpus directories
| Directory | Contents |
| ------------------------ | ----------------------------------------------- |
| `corpus/osc-initial/` | Hand-written seed inputs for osc-parser |
| `corpus/osc-cmin/` | Output of `afl-cmin` (edge-deduplicated corpus) |
| `corpus/parser-initial/` | Hand-written seed inputs for vt-parser |
| `corpus/parser-cmin/` | Output of `afl-cmin` (edge-deduplicated corpus) |
| `corpus/stream-initial/` | Hand-written seed inputs for vt-stream |
| `corpus/stream-cmin/` | Output of `afl-cmin` (edge-deduplicated corpus) |
+76
View File
@@ -0,0 +1,76 @@
const std = @import("std");
const afl = @import("afl");
/// Possible fuzz targets. Each fuzz target is implemented in
/// src/fuzz_<name>.zig and has an initial corpus in corpus/<name>-initial.
const Fuzzer = struct {
name: []const u8,
pub fn source(comptime self: Fuzzer) []const u8 {
return "src/fuzz_" ++ self.name ++ ".zig";
}
pub fn corpus(comptime self: Fuzzer) []const u8 {
// Change this suffix to use cmin vs initial corpus
return "corpus/" ++ self.name ++ "-cmin";
}
};
const fuzzers: []const Fuzzer = &.{
.{ .name = "osc" },
.{ .name = "parser" },
.{ .name = "stream" },
};
pub fn build(b: *std.Build) void {
// Resolve a "generic" host target so the emitted LLVM bitcode does not
// contain native CPU features (e.g. +zcm, +zcz) that the LLVM version
// bundled with afl-cc may not recognise, which would produce warnings.
const target = b.resolveTargetQuery(.{});
const optimize = b.standardOptimizeOption(.{});
const ghostty_dep = b.lazyDependency("ghostty", .{
.simd = false,
});
inline for (fuzzers) |fuzzer| {
const run_step = b.step(
b.fmt("run-{s}", .{fuzzer.name}),
b.fmt("Run {s} with afl-fuzz", .{fuzzer.name}),
);
const lib_mod = b.createModule(.{
.root_source_file = b.path(fuzzer.source()),
.target = target,
.optimize = optimize,
});
if (ghostty_dep) |dep| {
lib_mod.addImport(
"ghostty-vt",
dep.module("ghostty-vt"),
);
}
const lib = b.addLibrary(.{
.name = fuzzer.name,
.root_module = lib_mod,
});
lib.root_module.stack_check = false;
lib.root_module.fuzz = true;
const exe = afl.addInstrumentedExe(b, lib);
const run = afl.addFuzzerRun(
b,
exe,
b.path(fuzzer.corpus()),
b.path(b.fmt("afl-out/{s}", .{fuzzer.name})),
);
run_step.dependOn(&run.step);
const exe_install = b.addInstallBinFile(
exe,
"fuzz-" ++ fuzzer.name,
);
b.getInstallStep().dependOn(&exe_install.step);
}
}
+15
View File
@@ -0,0 +1,15 @@
.{
.name = .fuzz_libghostty,
.version = "0.0.0",
.fingerprint = 0x2cb2c498237c5d43,
.minimum_zig_version = "0.15.1",
.dependencies = .{
.ghostty = .{ .path = "../../" },
.afl = .{ .path = "../../pkg/afl++" },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}
@@ -0,0 +1 @@
52;c;?
@@ -0,0 +1 @@
52;c;
@@ -0,0 +1 @@
66;
@@ -0,0 +1 @@
133;C
@@ -0,0 +1 @@
133;D;0
@@ -0,0 +1 @@
3008;
@@ -0,0 +1 @@
1337;badcmd
Binary file not shown.
@@ -0,0 +1 @@
5522;
Binary file not shown.
@@ -0,0 +1 @@
52;c;?
@@ -0,0 +1 @@
52;c;
@@ -0,0 +1 @@
66;
@@ -0,0 +1 @@
133;C
@@ -0,0 +1 @@
133;D;0
@@ -0,0 +1 @@
3008;
@@ -0,0 +1 @@
1337;badcmd
@@ -0,0 +1 @@
5522;
@@ -0,0 +1 @@
đź€đźŽ‰dow ¤¤¤˘¤VVVVUV[4:0{˙¤
@@ -0,0 +1 @@
˙]93kAÚ]N6Úžžžžžžt]15Q]118]116@]119;ź]++++q;3;15Q]118]116f5N@]119;źW5;6;˙(
@@ -0,0 +1 @@
ם]הi5N@]112;p];p4
@@ -0,0 +1 @@
ÿ]9[]114;p]9;;;plm5@l10'e]1;i[]114;p]9;;;plm5@
@@ -0,0 +1 @@
 ˙˙˙˙˙˙ń]9>ěý]8]9;1;44444444444444444444444444444Ŕ@N@llttä;V[4]1p4:3m^
@@ -0,0 +1 @@
;6]66;;]à€ëÚ]1331e'í;6]66;;]à€ëÚ]1331eÁ66¶Äjf;oÿÿ4];6]66ÁÁ66½Äjf:pÿÿ4ÿ€
@@ -0,0 +1 @@
í]9;€]6666¶ÄiŤ]9;7i]9;$]6666¶Ä]6666¶ÄiŤ]9;7üiŤ]9;7ü˙˙f;p˙$]6666¶˙5ó](
@@ -0,0 +1 @@
í]9:8]ÄiN18]]9:8]ÄiN18]111;e]1;i]8;i[N1]111;e]12ï8]1115€
@@ -0,0 +1 @@
í]9;@]6Ní]9;:]6N]9;4;1;8; ;2;;;]6Ní]9 166¶ÄÄiŤ]9;8/˙˙iŤf;p˙˙4˙;p˙˙4˙€
@@ -0,0 +1 @@
í]9;:]6Ní]9;:]6N]9;4;2;166¶ÄÄiŤ]9;/˙˙iŤf;p˙˙4˙;p˙˙4˙€
@@ -0,0 +1 @@
í]9;:]6Ní]9;:]6N]9;4;0;8; ;2;;;]6Ní]9 166¶ÄÄiŤ]998/˙˙iŤf;p˙˙4˙;p˙˙4˙€
@@ -0,0 +1 @@
í]9;11]66;;];6]66;:]6Ní]9;í]9 166¶ÄÄiŤ]9;8/˙˙Ní]9 166¶ÄÄiŤ]9;8/p˙˙4˙;p˙˙4˙€
@@ -0,0 +1 @@
í]9;:]6Ní]9;:]6N]9;4;0;86N]9;4;0;8; ;2;;;]6;]6Ní]9 166¶ÄÄiŤ]998/˙˙iŤf;p˙˙4˙;p˙˙4˙€
@@ -0,0 +1 @@
;6]66;;U1e'66jf;p4];6]66;;]1e'66jf;p4
@@ -0,0 +1 @@
˙p˙]118]115;]118]115;]118]115;6;˙
@@ -0,0 +1 @@
ν;6]66;;U1Α;p4];6]66;;]1e'ρΊΏΑ66Α66¶Δjf;p4];6]66;;]1e'ρΊ¶Δjf;p4

Some files were not shown because too many files have changed in this diff Show More