Control: retitle -1 bsdgames: sail, adventure and trek misbehave where plain char is unsigned (arm64, armhf, ppc64el, s390x, riscv64)
Dear Maintainer,
after reporting sail I went through the rest of the package for the same
assumption. Two more games are affected; a second patch is attached
(0024-..., DEP-3, applies with -p1 on top of the sail patch or without it).
adventure (bsdgames-adventure): end of input is never noticed.
$ printf 'no\n' | /usr/games/bsdgames-adventure | head -c 100000 | grep -c
'Give me a break'
2479
getin() in adventure/io.c stores getchar() in a char and then has "case EOF:"
in a switch on that char. With unsigned char the case is unreachable, so on
^D, or when piped input runs out, the game prints "Give me a break!!" and
"I don't know that word." for ever (at full speed) instead of "user closed
input stream, quitting...". The same source built with -fsigned-char prints
that message and exits 0.
trek: a lost game gets the bonus for winning.
Start trek, take a short novice game, and "destruct". On arm64 the score is
Penalty for 5 klingons remaining -2000
Bonus for winning a novice game 100
Penalty for getting killed -500
Move.endgame (trek/trek.h) is a plain char; lose() sets it to -1, win() to 1,
and score() tests "Move.endgame > 0". 255 > 0. Built with -fsigned-char the
bonus line is absent and the total is 100 lower.
How I looked, so that you can judge what may have been missed:
* every game built with gcc 14 and -funsigned-char -Wtype-limits
-Wswitch-outside-range: the only relevant diagnostics in the whole tree
are sail/sync.c:260 and adventure/io.c:88 (both "case EOF" on a char);
no comparison of a plain char with a negative constant or with EOF exists
anywhere else;
* every source file through -fsyntax-only -funsigned-char -Wsign-conversion,
looking for negative constants converted to char: 93 in sail/globals.c
(the tables of the first patch) and one in trek/lose.c (endgame = -1);
* hack keeps its small signed numbers in "schar", which config.h already
defines as short int for exactly this reason;
* the 230-odd remaining scalar plain-char variables and the char arrays
that are assigned differences at run time were read through: letters,
symbols, flags and counts that never go negative;
* adventure and trek were then built twice, with -fsigned-char and with
-funsigned-char, and behave differently exactly as described; with the
patch the unsigned build behaves like the signed one.
Not related to signedness, but seen on the way: several games loop at end of
input whatever the architecture (monop asks "How many players?" for ever,
atc, backgammon, teachgammon and snake likewise fill the terminal).
0024-adventure-trek-plain-char-is-unsigned-on-arm.patch
Description: Binary data
# Other bsdgames and plain char signedness (audit of 2026-09-20) Source: bsdgames 2.17 + Debian's 22 patches, all 40-odd games Debian builds (hunt, dm, fortune, factor are not built and were not looked at). | Game | Finding | Confirmed | |---|---|---| | sail | tables of signed values in plain char; `case EOF` on a char | yes, filed | | adventure | `getin()`: `switch (*s = getchar())` ⦠`case EOF:` unreachable â endless "Give me a break!!" at end of input | packaged binary (2479 repeats in 100 kB) and signed/unsigned builds | | trek | `char endgame` set to -1 on losing, tested `> 0` â a lost game gets "Bonus for winning" (+100 à skill) | packaged binary and signed/unsigned builds | | canfield (cfscores) | `pw_uid < 0` always false â uid_t, nothing to do with char | harmless | | everything else | nothing found | â | Method: (1) full build with `-funsigned-char -Wextra -Wtype-limits -Wswitch-outside-range -Wchar-subscripts` (311 objects): three diagnostics in all; (2) `-fsyntax-only -funsigned-char -Wsign-conversion` on every file with `LC_ALL=C`, keeping "to 'char' changes value from '-": sail/globals.c Ã93, trek/lose.c Ã1 (the detector was first checked against the known sail bug); (3) typedefs: hack's `schar` is already `short int`; (4) the 234 scalar plain-char declarations and the char arrays assigned differences at run time, read by hand; (5) whole tree built twice (signed / unsigned) and the two suspects compared in behaviour; patched unsigned build re-tested. Limits: a plain char that goes negative only through arithmetic and is then used without any comparison against a negative constant would escape (1) and (2); (4) was a reading, not a proof. Games were not played through. Files: `0024-adventure-trek-plain-char-is-unsigned-on-arm.patch` (13 changed lines), `followup.txt` (mail to the existing bug; put the bug number in). Also seen, unrelated to signedness: monop, atc, backgammon, teachgammon and snake loop for ever at end of input on any architecture.
chartest.c
Description: Binary data

