Skip to content

Torvik v1.5.3

Choose a tag to compare

@torvik-lang torvik-lang released this 20 Aug 13:26
· 12 commits to main since this release
Immutable release. Only release title and notes can be modified.
4dc2693

A security fix and eight correctness bugs, no new
features and no changes to anything that already worked.*

This is a maintenance release. Every entry below either replaces a wrong answer
with a correct one, turns a program that would not compile into one that does, or
adds a warning about a bug the compiler could already see. Nothing here changes
the behavior of a program that was already correct.
Upgrading from v1.5.2
should be uneventful.


Security

Command injection through torvc run arguments.

Arguments forwarded to a program run with torvc run were passed through a shell
without adequate quoting, so an argument containing shell metacharacters could
execute arbitrary commands. This is the sixth issue in this family; the previous
five were fixed in v1.5.1 and v1.5.2.

Anyone who runs torvc run with arguments from an untrusted source — a CI job, a
script, anything reading a filename it did not choose — should upgrade.

A Security Advisory accompanies this release (see SECURITY.md).


Correctness

fmt printed a memory address for i128 and u128 (issue #23). A 128-bit
value is a heap box, and fmt handed the box pointer to the integer stringifier;
echo printed the value correctly, which is how the difference went unnoticed.

The same family of bugs affects u64 maximum (printed as -1) and lists (printed
as an address). Those are not fixed here: reaching the argument's real type
needs plumbing that does not exist on the 1.x line, and adding it means
restructuring the expression compiler — the last thing a security patch should
touch. Both are fixed in 2.0, where fmt and echo share one stringifier.

Hex escapes dropped bytes outside printable ASCII. "\x1b[2J" compiled
cleanly and produced [2J — the escape character was silently discarded, along
with DEL and every byte from 0x80 up. So the most common use of a hex escape, an
ANSI terminal sequence, was the case that did not work. Any byte written as
\x00–\xFF now decodes to that byte.

list<f64> could not be updated. xs[0] = 9.0 on a list of floats was
rejected by the backend. Reading an element worked, so a list of floats could be
built and read but never modified.

readint and readfloat read freed memory (issue #22). Both validated the
input by checking a pointer into the string after releasing that string — a
use-after-free that usually appeared to work, because the freed block still held
the old bytes. That is exactly what makes this class of bug survive testing. The
check now happens before the release.

(x >> n) & mask failed with an internal compiler error (issue #18). A
parenthesized expression used as the left operand of a further expression was
always converted as though it were a pointer, but a folded arithmetic chain is a
real integer — so the compiler emitted invalid code and clang rejected it. This is
why 255 & (x >> 4) and (x >> 4) on its own both worked while (x >> 4) & 255
did not. Reported against shifts; it affected any parenthesized arithmetic in that
position.

unwrap on a result of any integer width except i64 (issue #19). fixed v: u8 = unwrap(r); reported "can't assign a value of type str to 'v'" — the code
tested for i64 exactly, so u8, i8, i16, i32, u16, u32 and u64 all
fell through to the string path. All widths now work. (Bind the result to a
variable before unwrapping; unwrapping a call directly has no declared type to
read, as was already the case for i64.)

An integer assigned to an f64 produced a meaningless value (issue #17).
set n: i64 = 5; fixed f: f64 = n; compiled and printed 2.47033e-323 — the
integer's bits read as a floating-point number. The same assignment from a literal
or a call was caught correctly; only the variable form slipped through. It is now
a clean type error pointing at to_f64(...).

Three builtins denied their own existence. fs_mkdir, cwd and list_pop
called with the wrong number of arguments reported call to undefined function —
about builtins that exist and work. cwd(1) was the worst: it takes no arguments,
so passing one made the builtin vanish. All three now report the argument count
and the correct form.


Diagnostics

Narrowing conversions now warn (new). On the 1.x line a declared integer width
is not a storage constraint — every integer occupies a full-width slot, and u8
affects only literal range-checking and unsigned printing. Two things follow, both
silent:

  • fixed x: u8 = b; where b holds 70000 leaves x holding 70000. The
    declared type is not enforced.
  • The same conversion from a function call truncates without any diagnostic.
    Real data loss, no warning.

Torvik v2.0 refuses both at compile time, but that fix cannot be backported: it
would turn working 1.x programs into ones that do not build, which a patch release
must never do. So 1.5.3 warns instead. Your program builds and behaves exactly
as it did; you simply find out where the narrowing happens.

Each site the warning flags is also a site that will need an explicit
to_u8(...) / try_to_u8(...) on the 2.0 line, so clearing these warnings is
also the migration.

The warning fires where a value is narrowed from a call, which is the case that
silently truncates. Narrowing from a variable cannot be judged at that point in
the 1.x compiler — that case leaves the annotation unenforced but does not lose the
value, and issue #20 stays open for the 2.0 fix.

Silence it with !@ALLOW[narrowing]; per file, or --no-warn per build.

Link failures explain themselves. The message shown when a link fails claimed
that an undefined symbol "usually means a signature does not match the C header."
That is the one thing it cannot mean — C does not encode types in symbol names, so
a mismatched signature links cleanly and goes wrong at run time instead. The
message now lists the real causes, including the one that catches people out: a C
function that exists only as a macro or static inline in its header emits no
symbol at all, and no declaration however correct can bind it.


Upgrading

rune update

or reinstall from the install script. Nothing in this release changes the language
or the standard library surface, so no source changes are required.