vtgen

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package vtgen generates terminal input that looks like something a program would actually emit.

Random bytes are a poor fuzzer for a VT emulator. The parser rejects almost all of them in its first state, so a campaign spends its budget proving that garbage is garbage and never reaches the code that moves the cursor, sizes a scroll region or decides how many cells a character takes. What breaks an emulator is a well-formed sequence carrying a parameter nobody expected: a scroll region sized for a taller screen, a repeat count of two billion, a colour with four components, a cluster split across two writes.

So this generates by grammar rather than by byte. Every draw is a real sequence with a name attached, which buys two things: the interesting states are reachable at all, and a failing run reduces to a script a person can read and retype rather than to a hexdump.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Reduce

func Reduce[S ~[]E, E any](s S, still func(S) bool, simpler func(E) []E, note func(pass string, n int, ok bool)) S

Reduce is the shrinking driver shared by every fuzzer in the tree. It runs dropBlocks, dropSingles and simplify to a fixpoint, then simplifies once more. simpler lists the replacements for one element, most aggressive first.

note, when not nil, hears about each candidate: the block pass reports both accepted and rejected candidates, the single and simplify passes report accepted ones only. pass is "block", "single" or "simplify" and n is the candidate's length.

Types

type Gen

type Gen struct {
	// contains filtered or unexported fields
}

Gen draws sequences.

func FromBytes

func FromBytes(b []byte) *Gen

FromBytes decodes a `go test -fuzz` input into a script.

func New

func New(seed uint64) *Gen

New seeds a generator for a local sweep. The same seed always yields the same script.

func (*Gen) Next

func (g *Gen) Next() Seq

Next draws one step.

func (*Gen) Script

func (g *Gen) Script(n int) Script

Script draws n steps.

type Script

type Script []Seq

Script is a run.

func Shrink

func Shrink(s Script, still func(Script) bool) Script

Shrink returns the smallest script it could reach that still fails.

simpler makes the steps that remain plainer without making the script shorter. A repeated text run becomes one copy, an oversized payload becomes a small one, and a sequence carrying four parameters loses the ones the failure does not need. What this buys is a report that says which parameter mattered instead of leaving the reader to work it out.

Every simpler form of a step is strictly shorter than the step, so Shrink runs Reduce until a whole run changes neither the number of steps nor the number of bytes. One run of Reduce accepts one simpler form per step per round and stops on a round that removed no step, which left a 4096-byte payload at 1024 bytes and "\x1b[1;2;3r" at "\x1b[1r".

func (Script) Bytes

func (s Script) Bytes() string

Bytes concatenates everything a script writes, ignoring its resizes. Callers that care about ordering with respect to resizes should walk the steps.

func (Script) SplitWrites

func (s Script) SplitWrites(src uint64) []string

SplitWrites chops everything a script writes into the pieces a reader would actually hand the parser, at boundaries drawn from src. A PTY read boundary falls wherever the kernel put it: inside a multi-byte character, halfway through a CSI parameter list, between a base character and the mark that belongs to it. A parser that only ever sees whole sequences is not the parser that runs in production, and the state it has to carry across a boundary is the state nothing else exercises.

The pieces are small most of the time so those splits happen constantly rather than by luck, with an occasional larger read so a run is not uniformly one byte at a time. Resizes are dropped, the way Bytes drops them.

func (Script) String

func (s Script) String() string

String renders a script as something a person can read and retype. This is the whole point of generating by grammar: a failing run prints as a dozen named lines rather than as a wall of escape bytes.

type Seq

type Seq struct {
	// Kind groups steps for the shrinker and for reading. One of "text",
	// "cc", "esc", "csi", "sgr", "mode", "osc", "dcs", "apc", "kbd",
	// "margin", "erase", "tabs", "resize".
	Kind string

	// Bytes is what gets written to the emulator. Empty for a resize.
	Bytes string

	// Desc names the sequence the way a person would say it.
	Desc string

	// Cols and Rows are the new size when Kind is "resize".
	Cols, Rows int
}

Seq is one step of a script: bytes to write, or a resize, with a description of what it is meant to be.

func (Seq) MarshalJSON

func (s Seq) MarshalJSON() ([]byte, error)

MarshalJSON writes the step so that UnmarshalJSON gives back the same bytes.

func (*Seq) UnmarshalJSON

func (s *Seq) UnmarshalJSON(b []byte) error

UnmarshalJSON reads a step written by MarshalJSON, or by hand with either Bytes or BytesQuoted.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL