Skip to content

v3.7.0

Choose a tag to compare

@github-actions github-actions released this 07 Sep 15:58
· 169 commits to main since this release

A command is words, not a line — and the boundary answers instead of
stopping.
A path an editor handed the language server could run a shell
command; closing that gave the library PasProcess.Execute, an audit of the
fix the same afternoon closed six more findings, and a project-wide audit
after it found nine foreign scalars bound at the wrong width and nineteen
routines that stopped a program on a name holding chr(0). Beside that, a
TOML library and the project reader rewritten over it. Minor, because the
library and the command line grew and nothing already accepted changed
meaning; a program that met one of the fixed defects met a trap or a wrong
width, and neither is a behaviour to preserve.

Added

  • PasProcess.Deadline — how long any run of an ArgV may take; on expiry
    the child is killed, the pipe is closed so a grandchild cannot hold the
    caller either, and the run is errIO. PasFS.TemporaryDirectory — a
    directory of the program's own, over mkdtemp, for a caller that wants
    several files by names of its choosing (ADR-0363).

  • A command as words, not as a line — PasProcess.Execute and the four
    routines beside it (ExecuteInto, ExecuteBoth, ExecuteLines,
    ExecuteToFile), over an ArgV built with NewArgs, AddArg, ArgsLen
    and DropArgs. The words are carried as words and no shell reads them, so
    an argument holding a space, an apostrophe, a semicolon or a backquote is an
    argument. Run, Capture and CaptureLines are unchanged and remain the
    right answer for a pipeline or a redirection; prefer Execute wherever any
    part of the command came from outside the program (ADR-0362).

  • A TOML library — lib/dialect/pastoml.pas, TOML v1.0.0 whole rather
    than a subset: bare, quoted and dotted keys; basic, literal and both
    multi-line string forms; decimal, hexadecimal, octal and binary integers with
    underscores; floats with inf and nan; all four date-time forms; arrays
    over several lines and with a trailing comma; inline tables; tables; and
    arrays of tables. TomlParse and TomlParseChars answer a TomlResult, and
    TomlPositionOf turns the byte a refusal stopped at into a line and a
    column. TomlPath(doc, 'server.port') is the dotted lookup a configuration
    reader wants; TomlRender writes a document back, and parsing that and
    rendering again reproduces it byte for byte. A date-time is §6.4.3.4's
    own TimeStamp with a nanosecond and a UTC offset beside it, so a program
    hands the result straight to PasTime. An integer is integer, so a
    value outside -maxint..maxint is errRange and never a wrapped number.
    ADR-0360.

  • PasText.RealToStr — the shortest decimal that reads back as a given real,
    which was PasJson's and is now where two formats can reach it. ADR-0359.

  • bin/apconfig — the project-file reader pascalcc build, run and
    test call, written in this language over PasToml and installed beside
    pascalc. APCONFIG overrides where the driver looks for it, as PASCALC
    does for the compiler. ADR-0361.

Fixed

  • Nineteen library routines stopped the program on a name holding
    chr(0)
    -- every path, name, host or service taken by PasFS, PasDir,
    PasEnv, PasIO, PasStream and PasNet reached ADR-0122's trap. Each
    now answers a code (errSyntax, false or nil) through the shared
    PasError.HoldsNul, which is ADR-0363's rule applied to the whole library
    rather than to the two routines it was written for.

  • Nine more foreign scalars bound at the wrong width, and the emitter's own
    slice count
    (ADR-0364): read/write (ssize_t), fflush (a pointer)
    and six OpenSSL bindings (longs and pointers) were int64; now csize
    or clong. A slice's count crossed every foreign call as i64, on i386
    too, where size_t is 32 bits -- it is the target's width now. The new
    foreign-width gate holds both, because no behavioural case can: on a
    host whose spare register is clean the wrong width gives the right answer.

  • PasProcess.Seconds and CpuSeconds answered garbage on i386: time
    and clock were bound as returning int64, and both are long, 32 bits
    there -- the high word was whatever the register held. Bound as clong
    now (AP 6.4.2.7); found by ADR-0363's deadline case, the first to compute
    with Seconds, under the target32 gate on CI.

  • Six findings of the security audit over PasProcess.Execute (ADR-0363),
    four of them older than it: a path holding chr(0) stopped the language
    server on one request, and is now refused as a code; ExecuteToFile
    followed a symbolic link, and the server's scratch files were composed at
    the top of TMPDIR under a guessable name — O_NOFOLLOW now, and a private
    mkdtemp directory removed at exit; no Execute had a deadline; a child
    inherited every open descriptor (FD_CLOEXEC on pipes and sockets, mode
    e on every fopen); three posix_spawn file-action returns went
    unchecked; and a failed fdopen dropped the output silently.

  • A path the language server was given could run a command (ADR-0362).
    lsp/pasls.pas assembled a shell command and wrapped the source path in
    apostrophes; a path holding an apostrophe closed the quoting, so a file named
    a'; touch PWNED; echo '.pas ran touch PWNED when an editor asked for its
    outline — and under MCP the path comes from whatever is driving the model.
    The server now spawns the compiler with an argument vector and quotes
    nothing. tools/pascalcc was never affected: it is bash and uses arrays.
    The new command-injection gate holds both halves — that nothing ran, and
    that such a file still compiles.

  • afterschool-pascal.toml is read as TOML. The driver read it with a
    declared subset written in awk, which got two kinds of legal document
    wrong: a # inside a quoted string ended the value, so output = "build/demo#1" silently built build/demo; and an array was split on every
    comma, so ldflags = ["-Wl,-rpath,/opt/lib"] — one flag — was rejected as
    three malformed strings. Both now read as TOML says, a syntax error names a
    line and a column, and an unknown key or a value of the wrong shape is
    still refused by name. ADR-0361.

  • Writing a NaN through PasJson stopped the program. The guard against a
    value with no decimal spelling asked x <> x, which is false for a NaN on
    this processor — <> on reals is ordered not-equal — so the guard never
    fired and the scan below it read past the end of what writestr had
    written, reporting array index out of bounds against a line of the
    library. It asks not (x = x) now. A program could only reach it by
    computing a NaN, this language having no literal for one. ADR-0359.