--- Begin Message ---
- To: Debian Bug Tracking System <submit@bugs.debian.org>
- Subject: bullseye-pu: package sgt-puzzles/20191231.79a5378-3+deb11u1
- From: Ben Hutchings <ben@decadent.org.uk>
- Date: Sun, 30 Apr 2023 19:15:23 +0200
- Message-id: <168287492385.2069665.6141649755315519360.reportbug@deadeye.wl.decadent.org.uk>
Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: sgt-puzzles@packages.debian.org
Control: affects -1 + src:sgt-puzzles
The changes for this look very large, but most patches only change a
few lines. Combined diffstat for the new patches is:
blackbox.c | 8 +-
bridges.c | 42 +++++++----
cube.c | 30 +++++++-
devel.but | 11 +++
dominosa.c | 11 ++-
fifteen.c | 3
filling.c | 7 +
flip.c | 10 ++
flood.c | 32 ++++++--
galaxies.c | 53 +++++++-------
grid.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
grid.h | 2
guess.c | 6 +
inertia.c | 11 +--
keen.c | 11 ++-
latin.c | 45 +++++++-----
latin.h | 9 +-
lightup.c | 5 +
loopy.c | 19 ++++-
magnets.c | 18 ++++
map.c | 11 ++-
midend.c | 162 ++++++++++++++++++++++++++++++++++----------
mines.c | 36 +++++++++
net.c | 6 +
netslide.c | 15 +++-
palisade.c | 60 +++++++++-------
pattern.c | 9 ++
pearl.c | 59 +++++++++++++---
pegs.c | 18 ++++
puzzles.h | 3
random.c | 4 -
range.c | 5 -
rect.c | 2
samegame.c | 7 +
signpost.c | 3
sixteen.c | 7 +
slant.c | 2
solo.c | 5 -
tents.c | 25 ++++++
towers.c | 1
tracks.c | 20 ++++-
twiddle.c | 11 ++-
undead.c | 29 ++++---
unequal.c | 38 +++++-----
unfinished/group.c | 15 ++--
unruly.c | 2
untangle.c | 9 ++
47 files changed, 864 insertions(+), 226 deletions(-)
[ Reason ]
Fix security flaws in game loading. Also fix one non-security
crasher.
[ Impact ]
Loading a crafted game description or save file could have security
impacts up to and including arbitrary code execution.
[ Tests ]
There are no automated tests for this package.
I have tested that:
1. Each puzzle is playable to completion.
2. Each puzzle can generate and start a game from each of its
size/shape/difficulty presets.
3. Each puzzle can generate 10 random game save files and load them
without error.
[ Risks ]
As I have cherry-picked fixes from upstream, there is a possibility
that I missed some dependency for the fixes. However, I did review
all the upstream changes.
This is a leaf package.
[ Checklist ]
[X] *all* changes are documented in the d/changelog
[X] I reviewed all changes and I approve them
[X] attach debdiff against the package in (old)stable
[X] the issue is verified as fixed in unstable
[ Changes ]
There is one fix for a crash that can be triggered by user input
(Debian bug #905852).
All the other changes are:
- Fixes for security flaws in game loading
- Preparation for those fixes
- Fixes for regressions
Most of the security flaws are summarised in Debian bugs #1028986 and
##1034190, but this includes additional fixes made in 2020-2022.
The security team decided that these flaws are not serious enough to
require a stable security update.
[ Other info ]
diff -Nru sgt-puzzles-20191231.79a5378/debian/.gitignore sgt-puzzles-20191231.79a5378/debian/.gitignore
--- sgt-puzzles-20191231.79a5378/debian/.gitignore 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/.gitignore 1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +0,0 @@
-*~
-.#*
-/*.debhelper*
-/*.substvars
-/files
-/sgt-puzzles/
diff -Nru sgt-puzzles-20191231.79a5378/debian/changelog sgt-puzzles-20191231.79a5378/debian/changelog
--- sgt-puzzles-20191231.79a5378/debian/changelog 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/changelog 2023-04-30 17:35:04.000000000 +0200
@@ -1,3 +1,139 @@
+sgt-puzzles (20191231.79a5378-3+deb11u1) bullseye; urgency=medium
+
+ * Fix various security issues in game loading (Closes: #1028986, #1034190):
+ - Mines: add validation for negative mine count.
+ - Galaxies: fix assertion failure when adding out-of-bounds association.
+ - Filling: fix assertion failure in 3x1 game generation.
+ - Map: add missing sresize in new_game_desc().
+ - Add more validation to midend deserialisation routine
+ - Correct and enable the range check on statepos when loading
+ - Add an assertion to check the format of encoded parameters
+ - Add assertions that game descriptions consist only of printable ASCII.
+ - Hex-encode non-ASCII random seeds in save files
+ - Assert that everything written to a save file is printable ASCII
+ - Build fix: take declarations out of for loops.
+ - galaxies: Use the same code for handling all dropped arrows
+ - magnets: Area constraints; fix message.
+ - lightup: Ban 2x2 with either 4-way type
+ - Remove _() introduced from Android port.
+ - Solo: Set max difficulty for small jigsaw puzzles
+ - Add a macro of an upper bound on the formatted length of an integer
+ - Guess: Don't allow any moves once the game is solved (CVE-2023-24283)
+ - Guess: validate peg colours in decode_ui() (CVE-2023-24284)
+ - Netslide: Reject moves wider than the grid (CVE-2023-24285)
+ - Sixteen: limit length of moves
+ - Undead: check for valid commands in execute_move()
+ - Undead: fix buffer overrun in "M" command (CVE-2023-24287)
+ - Correct RANGECHECK macro in Black Box
+ - Range-check normal moves in Undead
+ - Range-check record lengths when deserialising games (CVE-2023-24291)
+ - Don't load too many states just because there's no STATEPOS
+ (CVE-2023-24288)
+ - Palisade: forbid moves that remove grid edges
+ - Last-ditch maximum size limit for Bridges
+ - Last-ditch grid-size limit for Dominosa
+ - Last-ditch grid-size limit for Galaxies
+ - Last-ditch grid-size limit for Fifteen
+ - Last-ditch maximum size limit for Flip
+ - Last-ditch grid-size limit for Flood
+ - Insist that Flood grids must have non-zero size
+ - Last-ditch grid-size limit for Inertia
+ - Last-ditch maximum size limit for Light Up
+ - Limit maximum grid size in Loopy
+ - Last-ditch maximum size limit for Magnets
+ - Last-ditch maximum size limit for Map
+ - Last-ditch maximum size limit for Mines
+ - Also check for tiny grids in Mines
+ - Last-ditch maximum size limit for Net
+ - Last-ditch maximum size limit for Netslide
+ - Integer overflow protection in Pattern
+ - Last-ditch maximum size limit for Palisade
+ - Last-ditch maximum size limit for Pearl
+ - Last-ditch maximum size limit for Pegs
+ - Also limit Pegs to at least 1x1 even when not doing full validation
+ - Last-ditch maximum size limit for Same Game
+ - Last-ditch maximum size limit for Signpost
+ - Last-ditch maximum size limit for Sixteen
+ - Limit size of puzzle in Tents to avoid integer overflow
+ - Last-ditch maximum size limit for Tracks
+ - Last-ditch maximum size limit for Twiddle
+ - Adjust Undead upper grid-size limit to avoid overflow
+ - Last-ditch point-count limit for Untangle
+ - Black Box: correct order of validation checks for "F" commands
+ - Palisade: don't leak memory on a bad move
+ - Don't allow negative clues in Pattern
+ - When loading, don't decode_ui unless we have a UI
+ - Palisade: remove assertion from decode_ui()
+ - Same Game: reject moves with unexpected characters in
+ - Filling: validate length of auto-solve move strings
+ - Tighten Bridges' validate_desc()
+ - Untangle: forbid descriptions that connect a node to itself
+ - Mines: No moving once you're dead!
+ - Towers: reject descriptions with odd characters at the end
+ - Tracks: make sure moves are valid in execute_move()
+ - Tracks: let solve make illegal moves
+ - Tracks: tighten up the 'illegal solve submoves' fix.
+ - Allow repeated "solve" operations in Guess
+ - Black Box: reject negative ball counts in game_params.
+ - Add validate_params bounds checks in a few more games.
+ - Don't allow Bridges games with < 2 islands
+ - Forbid moves that fill with the current colour in Flood
+ - Cleanly reject ill-formed solve moves in Flood
+ - Don't segfault on premature solve moves in Mines
+ - Limit number of mines in Mines game description
+ - Validate the number of pegs and holes in a Pegs game ID
+ - Mines: forbid moves that flag or unflag an exposed square
+ - Mines: Don't check if the player has won if they've already lost
+ - Avoid invalid moves when solving Tracks
+ - Fix move validation in Netslide
+ - Tighten validation of Tents game descriptions
+ - Dominosa: require the two halves of a domino to be adjacent
+ - Forbid lines off the grid in Pearl
+ - Tolerate incorrect solutions in Inertia
+ - Palisade: replace dfs_dsf() with a simple iteration.
+ - latin_solver_alloc: handle clashing numbers in input grid.
+ - Pearl: fix assertion failure on bad puzzle.
+ - Pearl: fix bounds check in previous commit.
+ - Unequal: Don't insist that solve moves must actually solve
+ - Range: Don't fail an assertion on an all-black board
+ - Limit width and height to SHRT_MAX in Mines
+ - Mines: Add assertions to range-check conversions to short
+ - Unequal: fix sense error in latin_solver_alloc fix.
+ - Forbid impossible moves in Bridges
+ - Forbid game descriptions with joined islands in Bridges
+ - Check state is valid at the end of a move in Pearl
+ - Cleanly reject more ill-formed solve moves in Flood
+ - Don't allow moves that change the constraints in Unequal
+ - Fix memory leaks in Keen's validate_desc()
+ - Don't leak grids in Loopy's validate_desc()
+ - Remember to free the to_draw member from Net's drawstate
+ - Undead: check the return value of sscanf() in execute_move()
+ - Don't leak duplicate edges in Untangle
+ - Remember to free the numcolours array from Pattern's drawstate
+ - Twiddle: don't read off the end of parameter strings ending 'm'
+ - Loopy: free the grid description string if it's invalid
+ - Avoid division by zero in Cube grid-size checks
+ - Validate that save file values are ASCII (mostly)
+ - More validation of solve moves in Flood
+ - Make sure that moves in Flood use only valid colours
+ - Tighten grid-size limit in Mines
+ - Tracks: set drag_s{x,y} even if starting off-grid
+ - Undead: be a bit more careful about sprintf buffer sizes
+ - Fix memory leak in midend_game_id_int()
+ - Flood: don't read off the end of some parameter strings
+ - Be more careful with type of left operand of <<
+ - Map: reduce maximum size
+ - Correctly handle some short save files
+ - Inertia: insist that solutions must be non-empty
+ - Galaxies: fix recursion depth limit in solver.
+ - Correct a range check in Magnets' layout verification
+ - Magnets: add a check that magnets don't wrap between lines
+ - Net: assert that cx and cy are in range in compute_active()
+ - Don't allow zero clues in Pattern
+ * Solo: cope with pencil marks when tilesize == 1 (Closes: #905852)
+
+ -- Ben Hutchings <benh@debian.org> Sun, 30 Apr 2023 17:35:04 +0200
+
sgt-puzzles (20191231.79a5378-3) unstable; urgency=medium
* Update German translation, thanks to Helge Kreutzmann
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0001-Black-Box-reject-negative-ball-counts-in-game_params.patch sgt-puzzles-20191231.79a5378/debian/patches/0001-Black-Box-reject-negative-ball-counts-in-game_params.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0001-Black-Box-reject-negative-ball-counts-in-game_params.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0001-Black-Box-reject-negative-ball-counts-in-game_params.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,34 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Sun, 22 Jan 2023 08:54:06 +0000
+Subject: [PATCH 001/159] Black Box: reject negative ball counts in
+ game_params.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=5cac6a09c4db2b7e05c3e8dfd8920e2cdd1b8b03
+Bug-Debian: https://bugs.debian.org/1034190
+
+You can inject one via a game desc string such as "10x10M5m-1", and
+it's clearly silly.
+
+_Zero_ balls, on the other hand, are a perfectly fine number: there's
+nothing incoherent about a BB puzzle in which the possible numbers of
+balls vary from (say) 0 to 5 inclusive, so that part of the challenge
+is to work out as efficiently as possible whether there are even any
+balls at all.
+
+(We only need to check minballs, because once we know minballs >= 0,
+the subsequent check ensures that maxballs >= minballs, and hence, by
+transitivity, maxballs >= 0 too.)
+---
+ blackbox.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/blackbox.c
++++ b/blackbox.c
+@@ -192,6 +192,8 @@ static const char *validate_params(const
+ * types, and could be worked around if required. */
+ if (params->w > 255 || params->h > 255)
+ return "Widths and heights greater than 255 are not supported";
++ if (params->minballs < 0)
++ return "Negative number of balls";
+ if (params->minballs > params->maxballs)
+ return "Minimum number of balls may not be greater than maximum";
+ if (params->minballs >= params->w * params->h)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0002-Add-validate_params-bounds-checks-in-a-few-more-game.patch sgt-puzzles-20191231.79a5378/debian/patches/0002-Add-validate_params-bounds-checks-in-a-few-more-game.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0002-Add-validate_params-bounds-checks-in-a-few-more-game.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0002-Add-validate_params-bounds-checks-in-a-few-more-game.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,125 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Sun, 22 Jan 2023 09:30:57 +0000
+Subject: [PATCH 002/159] Add validate_params bounds checks in a few more
+ games.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=b907e278751b740da7b9dc00c0cbdb93e7498919
+Bug-Debian: https://bugs.debian.org/1034190
+
+Ben tells me that his recent work in this area was entirely driven by
+fuzzing: he added bounds checks in validate_params when the fuzzer had
+managed to prove that the lack of them allowed something buggy to
+happen.
+
+It seemed worth doing an eyeball-review pass to complement that
+strategy, so in this commit I've gone through and added a few more
+checks that restrict the area of the grid to be less than INT_MAX.
+
+Notable in this commit: cube.c had to do something complicated because
+in the triangular-grid modes the area isn't calculated as easily as
+w*h, and Range's existing check that w+h-1 < SCHAR_MAX is sufficient
+to rule out w*h being overlarge _but_ should be done before w*h is
+ever computed.
+---
+ cube.c | 24 ++++++++++++++++++++++++
+ filling.c | 2 ++
+ range.c | 2 +-
+ rect.c | 2 ++
+ slant.c | 2 ++
+ unruly.c | 2 ++
+ 6 files changed, 33 insertions(+), 1 deletion(-)
+
+--- a/cube.c
++++ b/cube.c
+@@ -542,12 +542,36 @@ static const char *validate_params(const
+ if (params->solid < 0 || params->solid >= lenof(solids))
+ return "Unrecognised solid type";
+
++ if (params->d1 < 0 || params->d2 < 0)
++ return "Grid dimensions may not be negative";
++
+ if (solids[params->solid]->order == 4) {
+ if (params->d1 <= 1 || params->d2 <= 1)
+ return "Both grid dimensions must be greater than one";
++ if (params->d2 > INT_MAX / params->d1)
++ return "Grid area must not be unreasonably large";
+ } else {
+ if (params->d1 <= 0 && params->d2 <= 0)
+ return "At least one grid dimension must be greater than zero";
++
++ /*
++ * Check whether d1^2 + d2^2 + 4 d1 d2 > INT_MAX, without overflow:
++ *
++ * First check d1^2 doesn't overflow by itself.
++ *
++ * Then check d2^2 doesn't exceed the remaining space between
++ * d1^2 and INT_MAX.
++ *
++ * If that's all OK then we know both d1 and d2 are
++ * individually less than the square root of INT_MAX, so we
++ * can safely multiply them and compare against the
++ * _remaining_ space.
++ */
++ if ((params->d1 > INT_MAX / params->d1) ||
++ (params->d2 > (INT_MAX - params->d1*params->d1) / params->d2) ||
++ (params->d1*params->d2 > (INT_MAX - params->d1*params->d1 -
++ params->d2*params->d2) / params->d2))
++ return "Grid area must not be unreasonably large";
+ }
+
+ for (i = 0; i < 4; i++)
+--- a/filling.c
++++ b/filling.c
+@@ -188,6 +188,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 1) return "Width must be at least one";
+ if (params->h < 1) return "Height must be at least one";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+
+ return NULL;
+ }
+--- a/range.c
++++ b/range.c
+@@ -911,8 +911,8 @@ static const char *validate_params(const
+ int const w = params->w, h = params->h;
+ if (w < 1) return "Error: width is less than 1";
+ if (h < 1) return "Error: height is less than 1";
++ if (w > SCHAR_MAX - (h - 1)) return "Error: w + h is too big";
+ if (w * h < 1) return "Error: size is less than 1";
+- if (w + h - 1 > SCHAR_MAX) return "Error: w + h is too big";
+ /* I might be unable to store clues in my puzzle_size *grid; */
+ if (full) {
+ if (w == 2 && h == 2) return "Error: can't create 2x2 puzzles";
+--- a/rect.c
++++ b/rect.c
+@@ -218,6 +218,8 @@ static const char *validate_params(const
+ {
+ if (params->w <= 0 || params->h <= 0)
+ return "Width and height must both be greater than zero";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (params->w*params->h < 2)
+ return "Grid area must be greater than one";
+ if (params->expandfactor < 0.0F)
+--- a/slant.c
++++ b/slant.c
+@@ -226,6 +226,8 @@ static const char *validate_params(const
+
+ if (params->w < 2 || params->h < 2)
+ return "Width and height must both be at least two";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+
+ return NULL;
+ }
+--- a/unruly.c
++++ b/unruly.c
+@@ -284,6 +284,8 @@ static const char *validate_params(const
+ return "Width and height must both be even";
+ if (params->w2 < 6 || params->h2 < 6)
+ return "Width and height must be at least 6";
++ if (params->w2 > INT_MAX / params->h2)
++ return "Width times height must not be unreasonably large";
+ if (params->unique) {
+ static const long A177790[] = {
+ /*
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0004-Makefile.doc-Remove-CHM-files-in-clean-rule.patch sgt-puzzles-20191231.79a5378/debian/patches/0004-Makefile.doc-Remove-CHM-files-in-clean-rule.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0004-Makefile.doc-Remove-CHM-files-in-clean-rule.patch 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0004-Makefile.doc-Remove-CHM-files-in-clean-rule.patch 2023-04-23 21:12:52.000000000 +0200
@@ -1,6 +1,7 @@
From: Ben Hutchings <ben@decadent.org.uk>
Date: Fri, 10 Aug 2018 09:05:43 +0100
Subject: Makefile.doc: Remove CHM files in clean rule
+Applied-Upstream: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=f0f974055b7f8ee91bae6505aad166923ec7117f
---
Makefile.doc | 2 +-
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0006-Don-t-allow-Bridges-games-with-2-islands.patch sgt-puzzles-20191231.79a5378/debian/patches/0006-Don-t-allow-Bridges-games-with-2-islands.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0006-Don-t-allow-Bridges-games-with-2-islands.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0006-Don-t-allow-Bridges-games-with-2-islands.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,56 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 28 Jan 2023 00:45:38 +0000
+Subject: [PATCH 006/159] Don't allow Bridges games with < 2 islands
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=a98ac4bb428ab5c1ff665aa1def6cc14d02a4e19
+Bug-Debian: https://bugs.debian.org/1034190
+
+Technically, a game with no islands is always solved, but it causes a
+null-pointer dereference at startup because there's nowhere to put the
+cursor. Games with one island are always insoluble because the island
+must have at least one bridge and there's nowhere for it to go. So
+the minimum playable game has two islands.
+
+To demonstrate the segfault, try loading this save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :7:Bridges
+PARAMS :1:3
+CPARAMS :1:3
+DESC :1:i
+NSTATES :1:1
+STATEPOS:1:1
+---
+ bridges.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/bridges.c
++++ b/bridges.c
+@@ -2007,15 +2007,15 @@ generated:
+
+ static const char *validate_desc(const game_params *params, const char *desc)
+ {
+- int i, wh = params->w * params->h;
++ int i, wh = params->w * params->h, nislands = 0;
+
+ for (i = 0; i < wh; i++) {
+ if (*desc >= '1' && *desc <= '9')
+- /* OK */;
++ nislands++;
+ else if (*desc >= 'a' && *desc <= 'z')
+ i += *desc - 'a'; /* plus the i++ */
+ else if (*desc >= 'A' && *desc <= 'G')
+- /* OK */;
++ nislands++;
+ else if (!*desc)
+ return "Game description shorter than expected";
+ else
+@@ -2024,6 +2024,8 @@ static const char *validate_desc(const g
+ }
+ if (*desc || i > wh)
+ return "Game description longer than expected";
++ if (nislands < 2)
++ return "Game description has too few islands";
+
+ return NULL;
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0007-Forbid-moves-that-fill-with-the-current-colour-in-Fl.patch sgt-puzzles-20191231.79a5378/debian/patches/0007-Forbid-moves-that-fill-with-the-current-colour-in-Fl.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0007-Forbid-moves-that-fill-with-the-current-colour-in-Fl.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0007-Forbid-moves-that-fill-with-the-current-colour-in-Fl.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,36 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 28 Jan 2023 18:49:47 +0000
+Subject: [PATCH 007/159] Forbid moves that fill with the current colour in
+ Flood
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=eb1ae3f3d041f9ff0c11b04613a695be11bda706
+Bug-Debian: https://bugs.debian.org/1034190
+
+This avoids an assertion failure, "oldcolour != newcolour" in fill(),
+by catching it it execute_move(). As far as I know this couldn't be
+triggered from the UI, but it could be demonstrated with this save
+file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :5:Flood
+PARAMS :1:3
+CPARAMS :1:3
+DESC :12:231353400,11
+NSTATES :1:3
+STATEPOS:1:3
+MOVE :2:M3
+MOVE :2:M3
+---
+ flood.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/flood.c
++++ b/flood.c
+@@ -873,6 +873,7 @@ static game_state *execute_move(const ga
+ if (move[0] == 'M' &&
+ sscanf(move+1, "%d", &c) == 1 &&
+ c >= 0 &&
++ c != state->grid[FILLY * state->w + FILLX] &&
+ !state->complete) {
+ int *queue = snewn(state->w * state->h, int);
+ ret = dup_game(state);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0007-Mines-add-validation-for-negative-mine-count.patch sgt-puzzles-20191231.79a5378/debian/patches/0007-Mines-add-validation-for-negative-mine-count.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0007-Mines-add-validation-for-negative-mine-count.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0007-Mines-add-validation-for-negative-mine-count.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,22 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Tue, 17 Mar 2020 18:12:33 +0000
+Subject: [PATCH 007/389] Mines: add validation for negative mine count.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=d71ac73d8a4397c35b21ec08388a1c6f94691b64
+
+If this gets through validation, it causes an infinite loop after
+gameplay begins.
+---
+ mines.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -258,6 +258,8 @@ static const char *validate_params(const
+ */
+ if (full && params->unique && (params->w <= 2 || params->h <= 2))
+ return "Width and height must both be greater than two";
++ if (params->n < 0)
++ return "Mine count may not be negative";
+ if (params->n > params->w * params->h - 9)
+ return "Too many mines for grid size";
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0008-Cleanly-reject-ill-formed-solve-moves-in-Flood.patch sgt-puzzles-20191231.79a5378/debian/patches/0008-Cleanly-reject-ill-formed-solve-moves-in-Flood.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0008-Cleanly-reject-ill-formed-solve-moves-in-Flood.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0008-Cleanly-reject-ill-formed-solve-moves-in-Flood.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,41 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 28 Jan 2023 19:06:24 +0000
+Subject: [PATCH 008/159] Cleanly reject ill-formed solve moves in Flood
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=e4112b322e299a461ddc46daee741c73733e186d
+Bug-Debian: https://bugs.debian.org/1034190
+
+A solve move containing characters other than digits and commas would
+cause an assertion failure, "*p == ','", in execute_move(). Such a move
+can't as far as I know be generated in play, but can be read from a
+corrupt save file.
+
+Here's a sample of such a save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :5:Flood
+PARAMS :7:3x3c6m5
+CPARAMS :7:3x3c6m5
+DESC :12:403011503,10
+NSTATES :1:2
+STATEPOS:1:2
+SOLVE :2:SA
+---
+ flood.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/flood.c
++++ b/flood.c
+@@ -928,7 +928,11 @@ static game_state *execute_move(const ga
+ sol->moves[i] = atoi(p);
+ p += strspn(p, "0123456789");
+ if (*p) {
+- assert(*p == ',');
++ if (*p != ',') {
++ sfree(sol->moves);
++ sfree(sol);
++ return NULL;
++ }
+ p++;
+ }
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0009-Don-t-segfault-on-premature-solve-moves-in-Mines.patch sgt-puzzles-20191231.79a5378/debian/patches/0009-Don-t-segfault-on-premature-solve-moves-in-Mines.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0009-Don-t-segfault-on-premature-solve-moves-in-Mines.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0009-Don-t-segfault-on-premature-solve-moves-in-Mines.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,38 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 28 Jan 2023 19:34:28 +0000
+Subject: [PATCH 009/159] Don't segfault on premature solve moves in Mines
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=28671e76b736aeb860b1f725898c45fe70ae6212
+Bug-Debian: https://bugs.debian.org/1034190
+
+If a save file contained a solve move as the first move, Mines would
+dereference a null pointer trying to look up the (at that point
+undetermined) mine locations. Now execute_move() politely returns
+NULL instead.
+
+This save file demonstrates the problem:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :5:Mines
+PARAMS :5:3x3n0
+CPARAMS :5:3x3n0
+DESC :127:r0,u,7a142789cabddc3fc4dcb7d2baa4a4937b33c9613ea870ac098e217981ad339930af585557d62048ea745d05b01475d9699596b394cc0adeebf0440a02
+UI :2:D0
+TIME :1:0
+NSTATES :1:2
+STATEPOS:1:2
+SOLVE :1:S
+---
+ mines.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -2608,6 +2608,7 @@ static game_state *execute_move(const ga
+ if (!strcmp(move, "S")) {
+ int yy, xx;
+
++ if (!from->layout->mines) return NULL; /* Game not started. */
+ ret = dup_game(from);
+ if (!ret->dead) {
+ /*
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0010-Limit-number-of-mines-in-Mines-game-description.patch sgt-puzzles-20191231.79a5378/debian/patches/0010-Limit-number-of-mines-in-Mines-game-description.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0010-Limit-number-of-mines-in-Mines-game-description.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0010-Limit-number-of-mines-in-Mines-game-description.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,26 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 28 Jan 2023 23:12:52 +0000
+Subject: [PATCH 010/159] Limit number of mines in Mines game description
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=75e8a1a9cabe7567f6019b1226783b61ba1ac42f
+Bug-Debian: https://bugs.debian.org/1034190
+
+Without this, it's possible to specify a description that has more
+mines than there are places on the board to place them, which
+eventually leads to a division by zero. This can be demonstrated by
+entering a game description of "3:r8,u," and then clicking anywhere on
+the board.
+---
+ mines.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -2006,6 +2006,8 @@ static const char *validate_desc(const g
+ desc++;
+ if (!*desc || !isdigit((unsigned char)*desc))
+ return "No initial mine count in game description";
++ if (atoi(desc) > wh - 9)
++ return "Too many mines for grid size";
+ while (*desc && isdigit((unsigned char)*desc))
+ desc++; /* skip over mine count */
+ if (*desc != ',')
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0011-Validate-the-number-of-pegs-and-holes-in-a-Pegs-game.patch sgt-puzzles-20191231.79a5378/debian/patches/0011-Validate-the-number-of-pegs-and-holes-in-a-Pegs-game.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0011-Validate-the-number-of-pegs-and-holes-in-a-Pegs-game.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0011-Validate-the-number-of-pegs-and-holes-in-a-Pegs-game.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,42 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 28 Jan 2023 23:45:48 +0000
+Subject: [PATCH 011/159] Validate the number of pegs and holes in a Pegs game
+ ID
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=4359f59dd22770a94e29b2ddd54b533ad1713550
+Bug-Debian: https://bugs.debian.org/1034190
+
+Without this, "1:O" causes an assertion violation, '!"new_ui found
+nowhere for cursor"'. We may as well require two pegs and one hole,
+since that's the minimum for a game in which there are any moves to
+make.
+---
+ pegs.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+--- a/pegs.c
++++ b/pegs.c
+@@ -663,7 +663,7 @@ static char *new_game_desc(const game_pa
+
+ static const char *validate_desc(const game_params *params, const char *desc)
+ {
+- int len;
++ int len, i, npeg = 0, nhole = 0;
+
+ len = params->w * params->h;
+
+@@ -671,6 +671,15 @@ static const char *validate_desc(const g
+ return "Game description is wrong length";
+ if (len != strspn(desc, "PHO"))
+ return "Invalid character in game description";
++ for (i = 0; i < len; i++) {
++ npeg += desc[i] == 'P';
++ nhole += desc[i] == 'H';
++ }
++ /* The minimal soluble game has two pegs and a hole: "3x1:PPH". */
++ if (npeg < 2)
++ return "Too few pegs in game description";
++ if (nhole < 1)
++ return "Too few holes in game description";
+
+ return NULL;
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0012-unruly-Fix-memory-leak-in-dup_game.patch sgt-puzzles-20191231.79a5378/debian/patches/0012-unruly-Fix-memory-leak-in-dup_game.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0012-unruly-Fix-memory-leak-in-dup_game.patch 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0012-unruly-Fix-memory-leak-in-dup_game.patch 2023-04-23 21:12:52.000000000 +0200
@@ -1,6 +1,7 @@
From: Ben Hutchings <ben@decadent.org.uk>
Date: Sat, 17 Aug 2019 17:33:54 +0100
Subject: unruly: Fix memory leak in dup_game()
+Applied-Upstream: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=cd338a1a35394a7abfd517569e908b54bf657aaa
The common structure is ref-counted and dup_game() bumps the reference
count rather than copying it. However, blank_state() always allocates
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0013-bridges-Fix-off-by-one-in-WITHIN.patch sgt-puzzles-20191231.79a5378/debian/patches/0013-bridges-Fix-off-by-one-in-WITHIN.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0013-bridges-Fix-off-by-one-in-WITHIN.patch 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0013-bridges-Fix-off-by-one-in-WITHIN.patch 2023-04-23 21:12:52.000000000 +0200
@@ -1,6 +1,7 @@
From: Ben Hutchings <ben@decadent.org.uk>
Date: Sat, 17 Aug 2019 17:36:51 +0100
Subject: bridges: Fix off-by-one in WITHIN()
+Applied-Upstream: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=4c1e47b272274972556d7790384998e593d0ae57
WITHIN() used to treat the min and max as inclusive bounds but was
changed to treat the max as exclusive, apparently accidentally.
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0014-pattern-Fix-build-failure-with-fortify-and-gcc-9-on-.patch sgt-puzzles-20191231.79a5378/debian/patches/0014-pattern-Fix-build-failure-with-fortify-and-gcc-9-on-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0014-pattern-Fix-build-failure-with-fortify-and-gcc-9-on-.patch 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0014-pattern-Fix-build-failure-with-fortify-and-gcc-9-on-.patch 2023-04-23 21:12:52.000000000 +0200
@@ -5,6 +5,7 @@
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
+Forwarded: not-needed
Building with gcc 9 and _FORTIFY_SOURCE=2 (the default on Debian)
fails for some architectures, with error messages such as:
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0017-Mines-forbid-moves-that-flag-or-unflag-an-exposed-sq.patch sgt-puzzles-20191231.79a5378/debian/patches/0017-Mines-forbid-moves-that-flag-or-unflag-an-exposed-sq.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0017-Mines-forbid-moves-that-flag-or-unflag-an-exposed-sq.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0017-Mines-forbid-moves-that-flag-or-unflag-an-exposed-sq.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,28 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 1 Feb 2023 17:07:12 +0000
+Subject: [PATCH 017/159] Mines: forbid moves that flag or unflag an exposed
+ square
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=17364455186ae61e015d0f0de3f09423f78d0727
+Bug-Debian: https://bugs.debian.org/1034190
+
+interpret_move() couldn't generate them, but execute_move() also needs
+to forbid them to defend against corrupt save files. I don't think this
+actually caused any crashes, but it did cause unexpected "1" squares not
+adjacent to mines.
+---
+ mines.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/mines.c
++++ b/mines.c
+@@ -2672,7 +2672,9 @@ static game_state *execute_move(const ga
+ while (*move) {
+ if (move[0] == 'F' &&
+ sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
+- cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) {
++ cx >= 0 && cx < from->w && cy >= 0 && cy < from->h &&
++ (ret->grid[cy * from->w + cx] == -1 ||
++ ret->grid[cy * from->w + cx] == -2)) {
+ ret->grid[cy * from->w + cx] ^= (-2 ^ -1);
+ } else if (move[0] == 'O' &&
+ sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0018-Mines-Don-t-check-if-the-player-has-won-if-they-ve-a.patch sgt-puzzles-20191231.79a5378/debian/patches/0018-Mines-Don-t-check-if-the-player-has-won-if-they-ve-a.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0018-Mines-Don-t-check-if-the-player-has-won-if-they-ve-a.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0018-Mines-Don-t-check-if-the-player-has-won-if-they-ve-a.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,42 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 1 Feb 2023 20:12:29 +0000
+Subject: [PATCH 018/159] Mines: Don't check if the player has won if they've
+ already lost
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=2a9be2b89df3e6a07a1d90a06f8ac00a92d789e5
+Bug-Debian: https://bugs.debian.org/1034190
+
+It can't happen in normal play, but if a save file had a "C" (clear
+around) move that set off a mine, Mines could end up hitting an
+assertion failure, "ncovered >= nmines". This was because it would
+check if the player had won by counting covered squares and mines, and
+of course an uncovered mine is no longer a covered square but is still a
+mine.
+
+Since winning after you're dead isn't possible (at least in Mines), we
+now skip the check entirely if the player has already died.
+
+This save file demonstrates the problem:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :5:Mines
+PARAMS :1:7
+CPARAMS :1:7
+DESC :22:r31,u,0000C000d0000020
+NSTATES :1:2
+STATEPOS:1:1
+MOVE :4:C6,2
+---
+ mines.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -2146,6 +2146,8 @@ static int open_square(game_state *state
+ break;
+ }
+
++ /* If the player has already lost, don't let them win as well. */
++ if (state->dead) return 0;
+ /*
+ * Finally, scan the grid and see if exactly as many squares
+ * are still covered as there are mines. If so, set the `won'
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0019-Avoid-invalid-moves-when-solving-Tracks.patch sgt-puzzles-20191231.79a5378/debian/patches/0019-Avoid-invalid-moves-when-solving-Tracks.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0019-Avoid-invalid-moves-when-solving-Tracks.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0019-Avoid-invalid-moves-when-solving-Tracks.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,64 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 1 Feb 2023 21:28:35 +0000
+Subject: [PATCH 019/159] Avoid invalid moves when solving Tracks
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=875f0af21fbced5cbf6cf63b86fe3dc51682c863
+Bug-Debian: https://bugs.debian.org/1034190
+
+The solver, when it decided that an edge or square should be both TRACK
+and NOTRACK, would correctly decide that the puzzle was insoluble, but
+would also mark the edge with both flags in its working copy. This
+could then lead to assertion violations if that working copy of the
+board was used for something else, for instance if it was fed back into
+the solver. This couldn't happen in normal play, since failed solutions
+just cause the solve command to fail, but the diagnostic "H" command
+could trigger it from a save file, causing an assertion failure:
+"state->sflags[y*state->p.w + x] & S_CLUE".
+
+Now when the solver runs into this situation, it marks the puzzle as
+insoluble but doesn't set the invalid flag, so the board remains valid
+and future solve operations are safe.
+
+This save file is the one that demonstrated the problem:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :12:Train Tracks
+PARAMS :5:6x6t0
+CPARAMS :5:6x6t0
+DESC :31:b0t9l,,S0,00,0,0,4,0,0,S0,0,0,0
+NSTATES :1:8
+STATEPOS:1:2
+MOVE :1:H
+MOVE :1:H
+MOVE :1:H
+MOVE :1:H
+MOVE :1:H
+MOVE :1:H
+MOVE :1:H
+---
+ tracks.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/tracks.c
++++ b/tracks.c
+@@ -897,8 +897,8 @@ static int solve_set_sflag(game_state *s
+ if (state->sflags[i] & (f == S_TRACK ? S_NOTRACK : S_TRACK)) {
+ debug(("solve: opposite flag already set there, marking IMPOSSIBLE"));
+ state->impossible = true;
+- }
+- state->sflags[i] |= f;
++ } else
++ state->sflags[i] |= f;
+ return 1;
+ }
+
+@@ -915,8 +915,8 @@ static int solve_set_eflag(game_state *s
+ if (sf & (f == E_TRACK ? E_NOTRACK : E_TRACK)) {
+ debug(("solve: opposite flag already set there, marking IMPOSSIBLE"));
+ state->impossible = true;
+- }
+- S_E_SET(state, x, y, d, f);
++ } else
++ S_E_SET(state, x, y, d, f);
+ return 1;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0020-Fix-move-validation-in-Netslide.patch sgt-puzzles-20191231.79a5378/debian/patches/0020-Fix-move-validation-in-Netslide.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0020-Fix-move-validation-in-Netslide.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0020-Fix-move-validation-in-Netslide.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,40 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 1 Feb 2023 23:00:14 +0000
+Subject: [PATCH 020/159] Fix move validation in Netslide
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=ed0e4c304bed990948541fc0cf87309d75653806
+Bug-Debian: https://bugs.debian.org/1034190
+
+The maximum length of a column move in Netslide is the height of the
+puzzle, and the maximum length of a row move is the width, not the
+other way around.
+
+Moves of absolute length more than 1 can't be generated by
+interpret_move(), but they can come from save files. On non-square
+grids, the incorrect check led to assertion failures: "0 <= tx && tx <
+w" and "0 <= ty && ty < h". This save file demonstrates the problem:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :8:Netslide
+PARAMS :3:4x9
+CPARAMS :3:4x9
+DESC :39:0000000000000h0h0000000000000000000000v
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :4:R0,5
+---
+ netslide.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/netslide.c
++++ b/netslide.c
+@@ -1139,8 +1139,8 @@ static game_state *execute_move(const ga
+ if ((move[0] == 'C' || move[0] == 'R') &&
+ sscanf(move+1, "%d,%d", &c, &d) == 2 &&
+ c >= 0 && c < (move[0] == 'C' ? from->width : from->height) &&
+- d <= (move[0] == 'C' ? from->width : from->height) &&
+- d >= -(move[0] == 'C' ? from->width : from->height) && d != 0) {
++ d <= (move[0] == 'C' ? from->height : from->width) &&
++ d >= -(move[0] == 'C' ? from->height : from->width) && d != 0) {
+ col = (move[0] == 'C');
+ } else if (move[0] == 'S' &&
+ strlen(move) == from->width * from->height + 1) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0021-Tighten-validation-of-Tents-game-descriptions.patch sgt-puzzles-20191231.79a5378/debian/patches/0021-Tighten-validation-of-Tents-game-descriptions.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0021-Tighten-validation-of-Tents-game-descriptions.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0021-Tighten-validation-of-Tents-game-descriptions.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,55 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 2 Feb 2023 21:58:10 +0000
+Subject: [PATCH 021/159] Tighten validation of Tents game descriptions
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=ed682bd5c608156d12ebaa2d84c4ce2e2877c10a
+Bug-Debian: https://bugs.debian.org/1034190
+
+Specifically, TENT and NONTENT markers ('!' and '-') cannot appear as
+the first or last character of a description, because that would
+attempt to place them on squares outside the grid. This was caught by
+assertions in new_game(), as can be demonstrated by feeding these
+descriptions to older versions of Tents: "4:-p,0,0,0,0,0,0,0,0"
+("new_game: Assertion `i >= 0 && i <= w*h' failed.") and
+4:p-,0,0,0,0,0,0,0,0 ("new_game: Assertion `*desc == ','' failed.").
+---
+ tents.c | 22 +++++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+--- a/tents.c
++++ b/tents.c
+@@ -1191,6 +1191,21 @@ static char *new_game_desc(const game_pa
+ return ret;
+ }
+
++/*
++ * Grid description format:
++ *
++ * _ = tree
++ * a = 1 BLANK then TREE
++ * ...
++ * y = 25 BLANKs then TREE
++ * z = 25 BLANKs
++ * ! = set previous square to TENT
++ * - = set previous square to NONTENT
++ *
++ * Last character must be one that would insert a tree as the first
++ * square after the grid.
++ */
++
+ static const char *validate_desc(const game_params *params, const char *desc)
+ {
+ int w = params->w, h = params->h;
+@@ -1204,9 +1219,10 @@ static const char *validate_desc(const g
+ area += *desc - 'a' + 2;
+ else if (*desc == 'z')
+ area += 25;
+- else if (*desc == '!' || *desc == '-')
+- /* do nothing */;
+- else
++ else if (*desc == '!' || *desc == '-') {
++ if (area == 0 || area > w * h)
++ return "Tent or non-tent placed off the grid";
++ } else
+ return "Invalid character in grid specification";
+
+ desc++;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0022-Dominosa-require-the-two-halves-of-a-domino-to-be-ad.patch sgt-puzzles-20191231.79a5378/debian/patches/0022-Dominosa-require-the-two-halves-of-a-domino-to-be-ad.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0022-Dominosa-require-the-two-halves-of-a-domino-to-be-ad.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0022-Dominosa-require-the-two-halves-of-a-domino-to-be-ad.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,49 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 2 Feb 2023 22:26:24 +0000
+Subject: [PATCH 022/159] Dominosa: require the two halves of a domino to be
+ adjacent
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=294a3ac6e703c2820ceb7b28a1a5492b61e9a531
+Bug-Debian: https://bugs.debian.org/1034190
+
+Also that a line indicating no domino be between adjacent squares.
+Without this, execute_move would allow you to place dominos and edges
+between any pair ot squares, and then generate assertion failures
+("execute_move: Assertion `d2 - w >= 0' failed." and "execute_move:
+Assertion `d1 - w >= 0' failed.") when a domino was placed over an
+invalid edge. This example save file demonstrates the problem:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :8:Dominosa
+PARAMS :1:6
+CPARAMS :1:6
+DESC :56:55521461210004364611033535444421636022603153156422620503
+NSTATES :1:3
+STATEPOS:1:3
+MOVE :4:E0,2
+MOVE :4:D0,2
+---
+ dominosa.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/dominosa.c
++++ b/dominosa.c
+@@ -2870,7 +2870,8 @@ static game_state *execute_move(const ga
+ move++;
+ } else if (move[0] == 'D' &&
+ sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
+- d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2) {
++ d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
++ (d2 - d1 == 1 || d2 - d1 == w)) {
+
+ /*
+ * Toggle domino presence between d1 and d2.
+@@ -2938,7 +2939,8 @@ static game_state *execute_move(const ga
+ } else if (move[0] == 'E' &&
+ sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
+ d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
+- ret->grid[d1] == d1 && ret->grid[d2] == d2) {
++ ret->grid[d1] == d1 && ret->grid[d2] == d2 &&
++ (d2 - d1 == 1 || d2 - d1 == w)) {
+
+ /*
+ * Toggle edge presence between d1 and d2.
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0023-Forbid-lines-off-the-grid-in-Pearl.patch sgt-puzzles-20191231.79a5378/debian/patches/0023-Forbid-lines-off-the-grid-in-Pearl.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0023-Forbid-lines-off-the-grid-in-Pearl.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0023-Forbid-lines-off-the-grid-in-Pearl.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,48 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 2 Feb 2023 23:09:19 +0000
+Subject: [PATCH 023/159] Forbid lines off the grid in Pearl
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=15f4fa851a5781cf77984a6046405ffa758e7b33
+Bug-Debian: https://bugs.debian.org/1034190
+
+While they couldn't be generated in normal play, execute_move() would
+permit lines and marks across the edge of the grid that would then
+generate assertion failures ("dsf_update_completion: Assertion
+`INGRID(state, bx, by)' failed.").
+
+I've added a check to execute_move() that after updating a square, the
+square doesn't have any lines or marks that leave the grid.
+
+This save file demonstrated the problem:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSZON :1:1
+GAME :5:Pearl
+PARAMS :5:5x6dt
+CPARAMS :5:5x6dt
+DESC :6:eeeeee
+NSTATES :1:2
+STATEPOS:1:1
+MOVE :6:F1,4,2
+---
+ pearl.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/pearl.c
++++ b/pearl.c
+@@ -2235,6 +2235,16 @@ static game_state *execute_move(const ga
+ (ret->marks[y*w + x] & (char)l))
+ goto badmove;
+
++ /*
++ * Similarly, if we've ended up with a line or mark going
++ * off the board, that's not acceptable.
++ */
++ for (l = 1; l <= 8; l <<= 1)
++ if (((ret->lines[y*w + x] & (char)l) ||
++ (ret->marks[y*w + x] & (char)l)) &&
++ !INGRID(state, x+DX(l), y+DY(l)))
++ goto badmove;
++
+ move += n;
+ } else if (strcmp(move, "H") == 0) {
+ pearl_solve(ret->shared->w, ret->shared->h,
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0023-Galaxies-fix-assertion-failure-when-adding-out-of-bo.patch sgt-puzzles-20191231.79a5378/debian/patches/0023-Galaxies-fix-assertion-failure-when-adding-out-of-bo.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0023-Galaxies-fix-assertion-failure-when-adding-out-of-bo.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0023-Galaxies-fix-assertion-failure-when-adding-out-of-bo.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,40 @@
+From: Franklin Wei <franklin@rockbox.org>
+Date: Sun, 5 Jul 2020 19:32:26 -0400
+Subject: [PATCH 023/389] Galaxies: fix assertion failure when adding
+ out-of-bounds association.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=84cb4c6701e027090ff3fd955ce08065e20121b2
+
+Adding an association with an out-of-bounds square (i.e. by pressing Return
+with a dot selected, and then moving the cursor so the `opposite' arrow was
+off the screen) would cause space_opposite_dot() to return NULL, in turn
+causing ok_to_add_assoc_with_opposite_internal() to return false, failing
+the assertion.
+
+This assertion appears to have been introduced in 68363231.
+---
+ galaxies.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+--- a/galaxies.c
++++ b/galaxies.c
+@@ -382,12 +382,15 @@ static bool ok_to_add_assoc_with_opposit
+ static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
+ space *opposite = space_opposite_dot(state, tile, dot);
+
+- assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
++ if(opposite)
++ {
++ assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
+
+- remove_assoc_with_opposite(state, tile);
+- add_assoc(state, tile, dot);
+- remove_assoc_with_opposite(state, opposite);
+- add_assoc(state, opposite, dot);
++ remove_assoc_with_opposite(state, tile);
++ add_assoc(state, tile, dot);
++ remove_assoc_with_opposite(state, opposite);
++ add_assoc(state, opposite, dot);
++ }
+ }
+
+ static space *sp2dot(const game_state *state, int x, int y)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0024-Tolerate-incorrect-solutions-in-Inertia.patch sgt-puzzles-20191231.79a5378/debian/patches/0024-Tolerate-incorrect-solutions-in-Inertia.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0024-Tolerate-incorrect-solutions-in-Inertia.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0024-Tolerate-incorrect-solutions-in-Inertia.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,50 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Fri, 3 Feb 2023 20:52:05 +0000
+Subject: [PATCH 024/159] Tolerate incorrect solutions in Inertia
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=843d4ca17def11671809786f2a5aebd75f230dd9
+Bug-Debian: https://bugs.debian.org/1034190
+
+The "solve" operation in Inertia generates a proposed solution as a
+move string. But if such a move string is loaded from a save file it
+might not actually describe a solution. If that happens then it's
+possible to reach the end of the "solution" without winning, and doing
+so should probably cause a recalculation of the solution rather than
+an assertion failure ("execute_move: Assertion `ret->solnpos <
+ret->soln->len' failed.").
+
+I am a little concerned by the way that normal solve operations end up
+encoded in the save file, but the re-solvings caused by going off
+course don't, but I haven't got a good answer to that.
+
+Here's a save file that demonstrates the assertion failure:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :7:Inertia
+PARAMS :3:8x8
+CPARAMS :3:8x8
+DESC :64:sbgwsmswwgggwggmmbwgwbssbwbsbwbbwsSmwbbsbbmggbmssgmgwbmmwmbmmwsw
+NSTATES :2:3
+STATEPOS:1:1
+MOVE 000:2:S0
+MOVE 000:2:00
+---
+ inertia.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/inertia.c
++++ b/inertia.c
+@@ -1732,11 +1732,10 @@ static game_state *execute_move(const ga
+ if (ret->soln) {
+ if (ret->dead || ret->gems == 0)
+ discard_solution(ret);
+- else if (ret->soln->list[ret->solnpos] == dir) {
++ else if (ret->soln->list[ret->solnpos] == dir &&
++ ret->solnpos+1 < ret->soln->len)
+ ++ret->solnpos;
+- assert(ret->solnpos < ret->soln->len); /* or gems == 0 */
+- assert(!ret->dead); /* or not a solution */
+- } else {
++ else {
+ const char *error = NULL;
+ char *soln = solve_game(NULL, ret, NULL, &error);
+ if (!error) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0025-Palisade-replace-dfs_dsf-with-a-simple-iteration.patch sgt-puzzles-20191231.79a5378/debian/patches/0025-Palisade-replace-dfs_dsf-with-a-simple-iteration.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0025-Palisade-replace-dfs_dsf-with-a-simple-iteration.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0025-Palisade-replace-dfs_dsf-with-a-simple-iteration.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,109 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Fri, 3 Feb 2023 23:12:38 +0000
+Subject: [PATCH 025/159] Palisade: replace dfs_dsf() with a simple iteration.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=517b14e666b0b71fc0bcd5da1b22cdc90d3434c9
+Bug-Debian: https://bugs.debian.org/1034190
+
+The whole purpose of a dsf is that you can traverse the edges of your
+graph in any order you feel like. So if you want to build the
+connected components of a graph you can just loop over all the edges
+once. There's no need to run a depth-first search.
+
+In fact there were an amazing number of things wrong with this 10-line
+function:
+
+ - As Ben points out in commit 21193eaf9308ace, it didn't bother with
+ bounds checking when searching the grid, instead relying on the
+ never-removed grid boundary to stop the search - which was fragile in
+ the face of other bugs.
+
+ - The recursion uses linear stack, which is much worse than linear
+ heap, since stacks are often much more limited. (And the dsf _also_
+ used linear heap.)
+
+ - The recursion was completely unnecessary.
+
+ - The function used internal knowledge about dsf.c in order to define
+ the value UNVISITED to match what would happen to work.
+
+ - The name 'dfs_dsf' is totally confusing and almost impossible to
+ type!
+---
+ palisade.c | 36 ++++++++++++++++--------------------
+ 1 file changed, 16 insertions(+), 20 deletions(-)
+
+--- a/palisade.c
++++ b/palisade.c
+@@ -505,19 +505,20 @@ static bool solver_equivalent_edges(solv
+ return changed;
+ }
+
+-#define UNVISITED 6
+-
+ /* build connected components in `dsf', along the lines of `borders'. */
+-static void dfs_dsf(int i, int w, borderflag *border, int *dsf, bool black)
++static void build_dsf(int w, int h, borderflag *border, int *dsf, bool black)
+ {
+- int dir;
+- for (dir = 0; dir < 4; ++dir) {
+- int ii = i + dx[dir] + w*dy[dir], bdir = BORDER(dir);
+- if (black ? (border[i] & bdir) : !(border[i] & DISABLED(bdir)))
+- continue;
+- if (dsf[ii] != UNVISITED) continue;
+- dsf_merge(dsf, i, ii);
+- dfs_dsf(ii, w, border, dsf, black);
++ int x, y;
++
++ for (y = 0; y < h; y++) {
++ for (x = 0; x < w; x++) {
++ if (x+1 < w && (black ? !(border[y*w+x] & BORDER_R) :
++ (border[y*w+x] & DISABLED(BORDER_R))))
++ dsf_merge(dsf, y*w+x, y*w+(x+1));
++ if (y+1 < h && (black ? !(border[y*w+x] & BORDER_D) :
++ (border[y*w+x] & DISABLED(BORDER_D))))
++ dsf_merge(dsf, y*w+x, (y+1)*w+x);
++ }
+ }
+ }
+
+@@ -528,7 +529,7 @@ static bool is_solved(const game_params
+ int i, x, y;
+ int *dsf = snew_dsf(wh);
+
+- assert (dsf[0] == UNVISITED); /* check: UNVISITED and dsf.c match up */
++ build_dsf(w, h, border, dsf, true);
+
+ /*
+ * A game is solved if:
+@@ -539,7 +540,6 @@ static bool is_solved(const game_params
+ * - the borders also satisfy the clue set
+ */
+ for (i = 0; i < wh; ++i) {
+- if (dsf[i] == UNVISITED) dfs_dsf(i, params->w, border, dsf, true);
+ if (dsf_size(dsf, i) != k) goto error;
+ if (clues[i] == EMPTY) continue;
+ if (clues[i] != bitcount[border[i] & BORDER_MASK]) goto error;
+@@ -1177,7 +1177,7 @@ static void game_redraw(drawing *dr, gam
+ float animtime, float flashtime)
+ {
+ int w = state->shared->params.w, h = state->shared->params.h, wh = w*h;
+- int r, c, i, flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2;
++ int r, c, flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2;
+ int *black_border_dsf = snew_dsf(wh), *yellow_border_dsf = snew_dsf(wh);
+ int k = state->shared->params.k;
+
+@@ -1199,12 +1199,8 @@ static void game_redraw(drawing *dr, gam
+ status_bar(dr, buf);
+ }
+
+- for (i = 0; i < wh; ++i) {
+- if (black_border_dsf[i] == UNVISITED)
+- dfs_dsf(i, w, state->borders, black_border_dsf, true);
+- if (yellow_border_dsf[i] == UNVISITED)
+- dfs_dsf(i, w, state->borders, yellow_border_dsf, false);
+- }
++ build_dsf(w, h, state->borders, black_border_dsf, true);
++ build_dsf(w, h, state->borders, yellow_border_dsf, false);
+
+ for (r = 0; r < h; ++r)
+ for (c = 0; c < w; ++c) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0026-latin_solver_alloc-handle-clashing-numbers-in-input-.patch sgt-puzzles-20191231.79a5378/debian/patches/0026-latin_solver_alloc-handle-clashing-numbers-in-input-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0026-latin_solver_alloc-handle-clashing-numbers-in-input-.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0026-latin_solver_alloc-handle-clashing-numbers-in-input-.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,212 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Sun, 5 Feb 2023 10:29:42 +0000
+Subject: [PATCH 026/159] latin_solver_alloc: handle clashing numbers in input
+ grid.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=5030d87903191d581586ecda2382ad5bcd70f63d
+Bug-Debian: https://bugs.debian.org/1034190
+
+In the setup phase of the centralised latin.c solver, we start by
+going over the input grid containing already-placed clue numbers, and
+calling latin_solver_place to enter each on into the solver's data
+structure. This has the side effect of ruling out each number from the
+rest of the row and column, and _also_ checking by assertion that the
+number being placed is not ruled out.
+
+Those are a bad combination, because it means that if you give an
+obviously inconsistent input grid to latin_solver_alloc (e.g. with two
+identical numbers in a row already), it will fail an assertion. In
+that situation, you want the solver run as a whole to return
+diff_impossible so that the error is reported cleanly.
+
+This assertion failure could be provoked by giving either Towers or
+Group a manually-constructed game description inconsistent in that
+way, and hitting Solve. Worse, it could be provoked during live play
+in Unequal, by filling in a number clashing with a clue and then
+pressing 'h' to get hints.
+
+[benh: Backported to 20191231: The latin_solver functions don't take
+ a validator callback.]
+---
+ latin.c | 45 +++++++++++++++++++++++++++++----------------
+ latin.h | 9 ++++++---
+ unequal.c | 26 ++++++++++++++------------
+ unfinished/group.c | 15 ++++++++-------
+ 4 files changed, 57 insertions(+), 38 deletions(-)
+
+--- a/latin.c
++++ b/latin.c
+@@ -563,7 +563,7 @@ void latin_solver_free_scratch(struct la
+ sfree(scratch);
+ }
+
+-void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o)
++bool latin_solver_alloc(struct latin_solver *solver, digit *grid, int o)
+ {
+ int x, y;
+
+@@ -577,14 +577,23 @@ void latin_solver_alloc(struct latin_sol
+ memset(solver->row, 0, o*o);
+ memset(solver->col, 0, o*o);
+
+- for (x = 0; x < o; x++)
+- for (y = 0; y < o; y++)
+- if (grid[y*o+x])
+- latin_solver_place(solver, x, y, grid[y*o+x]);
+-
+ #ifdef STANDALONE_SOLVER
+ solver->names = NULL;
+ #endif
++
++ for (x = 0; x < o; x++) {
++ for (y = 0; y < o; y++) {
++ int n = grid[y*o+x];
++ if (n) {
++ if (cube(x, y, n))
++ latin_solver_place(solver, x, y, n);
++ else
++ return false; /* puzzle is already inconsistent */
++ }
++ }
++ }
++
++ return true;
+ }
+
+ void latin_solver_free(struct latin_solver *solver)
+@@ -810,14 +819,16 @@ static int latin_solver_recurse
+ } else {
+ newctx = ctx;
+ }
+- latin_solver_alloc(&subsolver, outgrid, o);
+ #ifdef STANDALONE_SOLVER
+ subsolver.names = solver->names;
+ #endif
+- ret = latin_solver_top(&subsolver, diff_recursive,
+- diff_simple, diff_set_0, diff_set_1,
+- diff_forcing, diff_recursive,
+- usersolvers, newctx, ctxnew, ctxfree);
++ if (latin_solver_alloc(&subsolver, outgrid, o))
++ ret = latin_solver_top(&subsolver, diff_recursive,
++ diff_simple, diff_set_0, diff_set_1,
++ diff_forcing, diff_recursive,
++ usersolvers, newctx, ctxnew, ctxfree);
++ else
++ ret = diff_impossible;
+ latin_solver_free(&subsolver);
+ if (ctxnew)
+ ctxfree(newctx);
+@@ -1046,11 +1057,13 @@ int latin_solver(digit *grid, int o, int
+ struct latin_solver solver;
+ int diff;
+
+- latin_solver_alloc(&solver, grid, o);
+- diff = latin_solver_main(&solver, maxdiff,
+- diff_simple, diff_set_0, diff_set_1,
+- diff_forcing, diff_recursive,
+- usersolvers, ctx, ctxnew, ctxfree);
++ if (latin_solver_alloc(&solver, grid, o))
++ diff = latin_solver_main(&solver, maxdiff,
++ diff_simple, diff_set_0, diff_set_1,
++ diff_forcing, diff_recursive,
++ usersolvers, ctx, ctxnew, ctxfree);
++ else
++ diff = diff_impossible;
+ latin_solver_free(&solver);
+ return diff;
+ }
+--- a/latin.h
++++ b/latin.h
+@@ -61,10 +61,13 @@ int latin_solver_forcing(struct latin_so
+ /* --- Solver allocation --- */
+
+ /* Fills in (and allocates members for) a latin_solver struct.
+- * Will allocate members of snew, but not snew itself
++ * Will allocate members of solver, but not solver itself
+ * (allowing 'struct latin_solver' to be the first element in a larger
+- * struct, for example). */
+-void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o);
++ * struct, for example).
++ *
++ * latin_solver_alloc returns false if the digits already in the grid
++ * could not be legally placed. */
++bool latin_solver_alloc(struct latin_solver *solver, digit *grid, int o);
+ void latin_solver_free(struct latin_solver *solver);
+
+ /* Allocates scratch space (for _set and _forcing) */
+--- a/unequal.c
++++ b/unequal.c
+@@ -822,12 +822,13 @@ static int solver_state(game_state *stat
+ struct latin_solver solver;
+ int diff;
+
+- latin_solver_alloc(&solver, state->nums, state->order);
+-
+- diff = latin_solver_main(&solver, maxdiff,
+- DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
+- DIFF_EXTREME, DIFF_RECURSIVE,
+- unequal_solvers, ctx, clone_ctx, free_ctx);
++ if (!latin_solver_alloc(&solver, state->nums, state->order))
++ diff = latin_solver_main(&solver, maxdiff,
++ DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
++ DIFF_EXTREME, DIFF_RECURSIVE,
++ unequal_solvers, ctx, clone_ctx, free_ctx);
++ else
++ diff = DIFF_IMPOSSIBLE;
+
+ memcpy(state->hints, solver.cube, state->order*state->order*state->order);
+
+@@ -2150,12 +2151,13 @@ static int solve(game_params *p, char *d
+ solver_show_working = debug;
+ game_debug(state);
+
+- latin_solver_alloc(&solver, state->nums, state->order);
+-
+- diff = latin_solver_main(&solver, DIFF_RECURSIVE,
+- DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
+- DIFF_EXTREME, DIFF_RECURSIVE,
+- unequal_solvers, ctx, clone_ctx, free_ctx);
++ if (latin_solver_alloc(&solver, state->nums, state->order))
++ diff = latin_solver_main(&solver, DIFF_RECURSIVE,
++ DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
++ DIFF_EXTREME, DIFF_RECURSIVE,
++ unequal_solvers, ctx, clone_ctx, free_ctx);
++ else
++ diff = DIFF_IMPOSSIBLE;
+
+ free_ctx(ctx);
+
+--- a/unfinished/group.c
++++ b/unfinished/group.c
+@@ -369,13 +369,11 @@ static int solver(const game_params *par
+ int w = params->w;
+ int ret;
+ struct latin_solver solver;
++
+ #ifdef STANDALONE_SOLVER
+ char *p, text[100], *names[50];
+ int i;
+-#endif
+
+- latin_solver_alloc(&solver, grid, w);
+-#ifdef STANDALONE_SOLVER
+ for (i = 0, p = text; i < w; i++) {
+ names[i] = p;
+ *p++ = TOCHAR(i+1, params->id);
+@@ -384,10 +382,13 @@ static int solver(const game_params *par
+ solver.names = names;
+ #endif
+
+- ret = latin_solver_main(&solver, maxdiff,
+- DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME,
+- DIFF_EXTREME, DIFF_UNREASONABLE,
+- group_solvers, NULL, NULL, NULL);
++ if (latin_solver_alloc(&solver, grid, w))
++ ret = latin_solver_main(&solver, maxdiff,
++ DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME,
++ DIFF_EXTREME, DIFF_UNREASONABLE,
++ group_solvers, NULL, NULL, NULL);
++ else
++ ret = diff_impossible;
+
+ latin_solver_free(&solver);
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0027-Pearl-fix-assertion-failure-on-bad-puzzle.patch sgt-puzzles-20191231.79a5378/debian/patches/0027-Pearl-fix-assertion-failure-on-bad-puzzle.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0027-Pearl-fix-assertion-failure-on-bad-puzzle.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0027-Pearl-fix-assertion-failure-on-bad-puzzle.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,85 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Sun, 5 Feb 2023 11:19:30 +0000
+Subject: [PATCH 027/159] Pearl: fix assertion failure on bad puzzle.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=05c536e50d0c3114e1de5283eac97ff22bad0fc7
+Bug-Debian: https://bugs.debian.org/1034190
+
+Similarly to the previous commit, if you started Pearl with at least
+some kinds of invalid puzzle (e.g. "6x6:abBfWcWWrBa") and then pressed
+'h' to get hints, you could provoke an assertion failure. But this
+time the assertion wasn't in the solver itself; the solver gave up
+gracefully and didn't crash, but it _did_ leave the links between
+squares in the game_state in an inconsistent state, in that one square
+was marked as linking to its neighbour without the neighbour also
+linking back to it. This caused the /* should have reciprocal link */
+assertion in dsf_update_completion to fail, when that was called from
+check_completion after the solver had finished, to decide whether the
+puzzle was now solved.
+
+In this commit, I arrange that whether or not pearl_solve returns a
+grid layout that's legal by the rules of the _puzzle_, it at least
+returns one that's legal by the rules of the _data representation_, in
+that every link between squares is either bidirectional or absent.
+
+This is a better solution than just removing the assertion, because if
+the inconsistent data were allowed to persist, it would lead to
+further problems in gameplay. For example, if you just remove that
+assertion instead of this fix and press 'h' on the example puzzle id
+above, you'll find that the non-reciprocal links are actually visible,
+in the form of several thick lines that stop at a grid square boundary
+instead of connecting two square-centres. (It looks even sillier if
+you set PEARL_GUI_LOOPY=y.)
+
+That's a situation that can't be created by a normal move, and if you
+try to make normal moves after it (e.g. click one of the weird edges),
+you'll find that both sides of the half-link get toggled, so now it's
+a half-link the other way round. So not only can't you _create_ this
+situation in normal play, you can't get rid of it either!
+
+That assertion in dsf_update_completion was commented out at one
+point, and I put it back in commit c5500926bf7458a saying that if it
+failed I'd like to know about it. And indeed, I'm glad I did, because
+this kind of unfixable wrongness in the resulting game_state was worth
+noticing and getting rid of!
+---
+ pearl.c | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+--- a/pearl.c
++++ b/pearl.c
+@@ -855,6 +855,35 @@ cleanup:
+ if (ret == 1) assert(b < 0xD); /* we should have had a break by now */
+ }
+ }
++
++ /*
++ * Ensure we haven't left the _data structure_ inconsistent,
++ * regardless of the consistency of the _puzzle_. In
++ * particular, we should never have marked one square as
++ * linked to its neighbour if the neighbour is not
++ * reciprocally linked back to the original square.
++ *
++ * This can happen if we get part way through solving an
++ * impossible puzzle and then give up trying to make further
++ * progress. So here we fix it up to avoid confusing the rest
++ * of the game.
++ */
++ for (y = 0; y < h; y++) {
++ for (x = 0; x < w; x++) {
++ for (d = 1; d <= 8; d += d) {
++ int nx = x + DX(d), ny = y + DY(d);
++ int rlink;
++ if (0 <= nx && nx < w && 0 <= ny && ny < w)
++ rlink = result[ny*w+nx] & F(d);
++ else
++ rlink = 0; /* off-board squares don't link back */
++
++ /* If other square doesn't link to us, don't link to it */
++ if (!rlink)
++ result[y*w+x] &= ~d;
++ }
++ }
++ }
+ }
+
+ sfree(dsfsize);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0028-Filling-fix-assertion-failure-in-3x1-game-generation.patch sgt-puzzles-20191231.79a5378/debian/patches/0028-Filling-fix-assertion-failure-in-3x1-game-generation.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0028-Filling-fix-assertion-failure-in-3x1-game-generation.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0028-Filling-fix-assertion-failure-in-3x1-game-generation.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,34 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Sun, 14 Mar 2021 22:05:23 +0000
+Subject: [PATCH 028/389] Filling: fix assertion failure in 3x1 game
+ generation.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=1fcb61cffe538c11a11fc2ec94d6470a61df019e
+
+This would come up on the game id "3x1#12345", for example. The
+failing assertion was (s->board[f] != EMPTY) in expand(), called in
+turn from learn_expand_or_one().
+
+It looks as if the problem was that the #define SENTINEL was set too
+small. It was intended to be a value that can't coincide with the true
+size of any region - and it was set to precisely the area of the whole
+board. But on a 3x1 grid, that _can_ coincide with the size of a
+region! So a board entry was set to a real region size, and then
+mistaken for SENTINEL by another part of the code.
+
+Easy fix: set SENTINEL to be sz+1. Now it really can't coincide with a
+region area.
+---
+ filling.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/filling.c
++++ b/filling.c
+@@ -310,7 +310,7 @@ static void print_board(int *board, int
+ static game_state *new_game(midend *, const game_params *, const char *);
+ static void free_game(game_state *);
+
+-#define SENTINEL sz
++#define SENTINEL (sz+1)
+
+ static bool mark_region(int *board, int w, int h, int i, int n, int m) {
+ int j;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0028-Pearl-fix-bounds-check-in-previous-commit.patch sgt-puzzles-20191231.79a5378/debian/patches/0028-Pearl-fix-bounds-check-in-previous-commit.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0028-Pearl-fix-bounds-check-in-previous-commit.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0028-Pearl-fix-bounds-check-in-previous-commit.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,22 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Sun, 5 Feb 2023 12:05:28 +0000
+Subject: [PATCH 028/159] Pearl: fix bounds check in previous commit.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=9ce0a6d93212ffc0986a2b399fd8e9e983f62904
+Bug-Debian: https://bugs.debian.org/1034190
+
+Ahem. That's what I get for testing the fix on a square puzzle.
+---
+ pearl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/pearl.c
++++ b/pearl.c
+@@ -873,7 +873,7 @@ cleanup:
+ for (d = 1; d <= 8; d += d) {
+ int nx = x + DX(d), ny = y + DY(d);
+ int rlink;
+- if (0 <= nx && nx < w && 0 <= ny && ny < w)
++ if (0 <= nx && nx < w && 0 <= ny && ny < h)
+ rlink = result[ny*w+nx] & F(d);
+ else
+ rlink = 0; /* off-board squares don't link back */
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0029-Unequal-Don-t-insist-that-solve-moves-must-actually-.patch sgt-puzzles-20191231.79a5378/debian/patches/0029-Unequal-Don-t-insist-that-solve-moves-must-actually-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0029-Unequal-Don-t-insist-that-solve-moves-must-actually-.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0029-Unequal-Don-t-insist-that-solve-moves-must-actually-.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,58 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 4 Feb 2023 16:18:27 +0000
+Subject: [PATCH 029/159] Unequal: Don't insist that solve moves must actually
+ solve
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=84ec2a0a77d63450311f7c25b36d4b9f7e3c53e1
+Bug-Debian: https://bugs.debian.org/1034190
+
+A corrupt save file can include an "S" move that doesn't give a valid
+solution. An assertion failure ("execute_move: Assertion `rc > 0'
+failed.") at that point is rude, so now we just don't set the
+"completed" flag in that case. We still set the "cheated" flag, to
+reward (lack of) effort.
+
+Here's a trivial test case:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :7:Unequal
+CPARAMS :1:3
+PARAMS :1:3
+DESC :17:0,0,0,0,0,0,0,0,0
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :10:S222222222
+---
+ unequal.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/unequal.c
++++ b/unequal.c
+@@ -1556,7 +1556,7 @@ static char *interpret_move(const game_s
+ static game_state *execute_move(const game_state *state, const char *move)
+ {
+ game_state *ret = NULL;
+- int x, y, n, i, rc;
++ int x, y, n, i;
+
+ debug(("execute_move: %s", move));
+
+@@ -1581,7 +1581,7 @@ static game_state *execute_move(const ga
+ const char *p;
+
+ ret = dup_game(state);
+- ret->completed = ret->cheated = true;
++ ret->cheated = true;
+
+ p = move+1;
+ for (i = 0; i < state->order*state->order; i++) {
+@@ -1592,8 +1592,8 @@ static game_state *execute_move(const ga
+ p++;
+ }
+ if (*p) goto badmove;
+- rc = check_complete(ret->nums, ret, true);
+- assert(rc > 0);
++ if (!ret->completed && check_complete(ret->nums, ret, true) > 0)
++ ret->completed = true;
+ return ret;
+ } else if (move[0] == 'M') {
+ ret = dup_game(state);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0030-Range-Don-t-fail-an-assertion-on-an-all-black-board.patch sgt-puzzles-20191231.79a5378/debian/patches/0030-Range-Don-t-fail-an-assertion-on-an-all-black-board.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0030-Range-Don-t-fail-an-assertion-on-an-all-black-board.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0030-Range-Don-t-fail-an-assertion-on-an-all-black-board.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,27 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 4 Feb 2023 16:50:55 +0000
+Subject: [PATCH 030/159] Range: Don't fail an assertion on an all-black board
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=ae73ad76ef95f0e40868436cb750126322051dd0
+Bug-Debian: https://bugs.debian.org/1034190
+
+If there are no white squares, then Range's check that all the white
+squares form a connected component goes wrong. Skip the check in that
+case to avoid an assretion violation ("edsf_canonify: Assertion `index
+>= 0' failed."). This can be demonstrated by starting a game with no
+clues (e.g. "range 3:i") and then filling in every square.
+---
+ range.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/range.c
++++ b/range.c
+@@ -1474,7 +1474,8 @@ static bool find_errors(const game_state
+ if (state->grid[r*w+c] != BLACK &&
+ state->grid[r*w+(c+1)] != BLACK)
+ dsf_merge(dsf, r*w+c, r*w+(c+1));
+- if (nblack + dsf_size(dsf, any_white_cell) < n) {
++ if (any_white_cell != -1 &&
++ nblack + dsf_size(dsf, any_white_cell) < n) {
+ int biggest, canonical;
+
+ if (!report) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0031-Limit-width-and-height-to-SHRT_MAX-in-Mines.patch sgt-puzzles-20191231.79a5378/debian/patches/0031-Limit-width-and-height-to-SHRT_MAX-in-Mines.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0031-Limit-width-and-height-to-SHRT_MAX-in-Mines.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0031-Limit-width-and-height-to-SHRT_MAX-in-Mines.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,39 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 28 Jan 2023 22:27:21 +0000
+Subject: [PATCH 031/159] Limit width and height to SHRT_MAX in Mines
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=c0e08f308792b15425e10ad494263d77a45ad92d
+Bug-Debian: https://bugs.debian.org/1034190
+
+Mines' "struct set" stores co-ordinates within the grid in a pair of
+shorts, which leads to very bad behaviour (including heap-based buffer
+overruns) if the grid is bigger than SHRT_MAX in either dimension. So
+now we don't allow that.
+
+The overrun can be demonstrated by loading this save file, though the
+precise crash is quite variable. In particular, you seem to get
+better crashes if the file doesn't have a trailing newline.
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+PARAMS :5:06000
+CPARAMS :7:6x60000
+NSTATES :1:3
+STATEPOS:1:2
+MOVE :5:C0,00
+GAME :5:Mines
+DESC :22:r8,u,00000000000000000
+MOVE ::
+---
+ mines.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -263,6 +263,8 @@ static const char *validate_params(const
+ return "Width and height must both be greater than two";
+ if (params->w < 1 || params->h < 1)
+ return "Width and height must both be at least one";
++ if (params->w > SHRT_MAX || params->h > SHRT_MAX)
++ return "Neither width nor height may be unreasonably large";
+ if (params->w > INT_MAX / params->h)
+ return "Width times height must not be unreasonably large";
+ if (params->n < 0)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0032-Mines-Add-assertions-to-range-check-conversions-to-s.patch sgt-puzzles-20191231.79a5378/debian/patches/0032-Mines-Add-assertions-to-range-check-conversions-to-s.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0032-Mines-Add-assertions-to-range-check-conversions-to-s.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0032-Mines-Add-assertions-to-range-check-conversions-to-s.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,35 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 31 Jan 2023 21:08:05 +0000
+Subject: [PATCH 032/159] Mines: Add assertions to range-check conversions to
+ short
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=49841bd0fc04490d94cf32c0e6f9d3f4ffabe098
+Bug-Debian: https://bugs.debian.org/1034190
+
+I think these should be adequately guarded by the new restrictions on
+grid size, but I'd prefer to be sure.
+---
+ mines.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -435,7 +435,9 @@ static void ss_add(struct setstore *ss,
+ * Create a set structure and add it to the tree.
+ */
+ s = snew(struct set);
++ assert(SHRT_MIN <= x && x <= SHRT_MAX);
+ s->x = x;
++ assert(SHRT_MIN <= y && y <= SHRT_MAX);
+ s->y = y;
+ s->mask = mask;
+ s->mines = mines;
+@@ -506,7 +508,9 @@ static struct set **ss_overlap(struct se
+ /*
+ * Find the first set with these top left coordinates.
+ */
++ assert(SHRT_MIN <= xx && xx <= SHRT_MAX);
+ stmp.x = xx;
++ assert(SHRT_MIN <= yy && yy <= SHRT_MAX);
+ stmp.y = yy;
+ stmp.mask = 0;
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0033-Unequal-fix-sense-error-in-latin_solver_alloc-fix.patch sgt-puzzles-20191231.79a5378/debian/patches/0033-Unequal-fix-sense-error-in-latin_solver_alloc-fix.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0033-Unequal-fix-sense-error-in-latin_solver_alloc-fix.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0033-Unequal-fix-sense-error-in-latin_solver_alloc-fix.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,28 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Wed, 8 Feb 2023 18:22:23 +0000
+Subject: [PATCH 033/159] Unequal: fix sense error in latin_solver_alloc fix.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=bd5c0a37a019c540eda05f8291cad90ffd598134
+Bug-Debian: https://bugs.debian.org/1034190
+
+In commit 5030d87903191d5 I gave latin_solver_alloc a return value,
+and introduced a check of that value at every call site. One of the
+checks was backwards, with the effect that Unequal game generation now
+more or less always fails an assertion. For example:
+
+$ unequal --generate 1 4#12345
+unequal: unequal.c:1072: gg_best_clue: Assertion `best != -1' failed.
+---
+ unequal.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/unequal.c
++++ b/unequal.c
+@@ -822,7 +822,7 @@ static int solver_state(game_state *stat
+ struct latin_solver solver;
+ int diff;
+
+- if (!latin_solver_alloc(&solver, state->nums, state->order))
++ if (latin_solver_alloc(&solver, state->nums, state->order))
+ diff = latin_solver_main(&solver, maxdiff,
+ DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
+ DIFF_EXTREME, DIFF_RECURSIVE,
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0034-Forbid-impossible-moves-in-Bridges.patch sgt-puzzles-20191231.79a5378/debian/patches/0034-Forbid-impossible-moves-in-Bridges.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0034-Forbid-impossible-moves-in-Bridges.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0034-Forbid-impossible-moves-in-Bridges.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,46 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Fri, 10 Feb 2023 17:09:18 +0000
+Subject: [PATCH 034/159] Forbid impossible moves in Bridges
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=bf9abb2a127a4a81babe50ecb419527f8aeffe28
+Bug-Debian: https://bugs.debian.org/1034190
+
+Specifically, a bridge or a non-bridge must connect two islands that
+differ in precisely one co-ordinate. Without this, a save file that
+tries to connect or disconnect two non-orthogonal islands will cause
+"island_join: Assertion `!"island_join: islands not orthogonal."'
+failed."
+
+Here's a save file demonstrating the problem:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :7:Bridges
+PARAMS :13:3x3i30e10m2d0
+CPARAMS :13:3x3i30e10m2d0
+DESC :6:b1c1a2
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :10:L0,2,2,0,1
+---
+ bridges.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/bridges.c
++++ b/bridges.c
+@@ -2543,6 +2543,8 @@ static game_state *execute_move(const ga
+ goto badmove;
+ if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
+ goto badmove;
++ /* Precisely one co-ordinate must differ between islands. */
++ if ((x1 != x2) + (y1 != y2) != 1) goto badmove;
+ is1 = INDEX(ret, gridi, x1, y1);
+ is2 = INDEX(ret, gridi, x2, y2);
+ if (!is1 || !is2) goto badmove;
+@@ -2554,6 +2556,7 @@ static game_state *execute_move(const ga
+ goto badmove;
+ if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
+ goto badmove;
++ if ((x1 != x2) + (y1 != y2) != 1) goto badmove;
+ is1 = INDEX(ret, gridi, x1, y1);
+ is2 = INDEX(ret, gridi, x2, y2);
+ if (!is1 || !is2) goto badmove;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0035-Forbid-game-descriptions-with-joined-islands-in-Brid.patch sgt-puzzles-20191231.79a5378/debian/patches/0035-Forbid-game-descriptions-with-joined-islands-in-Brid.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0035-Forbid-game-descriptions-with-joined-islands-in-Brid.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0035-Forbid-game-descriptions-with-joined-islands-in-Brid.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,62 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Fri, 10 Feb 2023 18:28:36 +0000
+Subject: [PATCH 035/159] Forbid game descriptions with joined islands in
+ Bridges
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=ad2fb760fc881d1ceb1ac1151bf60b85deb16c71
+Bug-Debian: https://bugs.debian.org/1034190
+
+A game description with islands in adjacent grid squares, like
+"3x3:11g", shouldn't be allowed. If it is, then bridges between the
+islands are invisible and clicking one of them causes an assertion
+failure: "Assertion `is_loop->adj.points[j].off > 1' failed."
+
+The code to check this is really rather complex, but I think the
+complexity is mostly necessary.
+---
+ bridges.c | 27 ++++++++++++++++++++-------
+ 1 file changed, 20 insertions(+), 7 deletions(-)
+
+--- a/bridges.c
++++ b/bridges.c
+@@ -2007,21 +2007,34 @@ generated:
+
+ static const char *validate_desc(const game_params *params, const char *desc)
+ {
+- int i, wh = params->w * params->h, nislands = 0;
++ int i, j, wh = params->w * params->h, nislands = 0;
++ bool *last_row = snewn(params->w, bool);
+
++ memset(last_row, 0, params->w * sizeof(bool));
+ for (i = 0; i < wh; i++) {
+- if (*desc >= '1' && *desc <= '9')
++ if ((*desc >= '1' && *desc <= '9') || (*desc >= 'A' && *desc <= 'G')) {
+ nislands++;
+- else if (*desc >= 'a' && *desc <= 'z')
++ /* Look for other islands to the left and above. */
++ if ((i % params->w > 0 && last_row[i % params->w - 1]) ||
++ last_row[i % params->w]) {
++ sfree(last_row);
++ return "Game description contains joined islands";
++ }
++ last_row[i % params->w] = true;
++ } else if (*desc >= 'a' && *desc <= 'z') {
++ for (j = 0; j < *desc - 'a' + 1; j++)
++ last_row[(i + j) % params->w] = false;
+ i += *desc - 'a'; /* plus the i++ */
+- else if (*desc >= 'A' && *desc <= 'G')
+- nislands++;
+- else if (!*desc)
++ } else if (!*desc) {
++ sfree(last_row);
+ return "Game description shorter than expected";
+- else
++ } else {
++ sfree(last_row);
+ return "Game description contains unexpected character";
++ }
+ desc++;
+ }
++ sfree(last_row);
+ if (*desc || i > wh)
+ return "Game description longer than expected";
+ if (nislands < 2)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0037-Check-state-is-valid-at-the-end-of-a-move-in-Pearl.patch sgt-puzzles-20191231.79a5378/debian/patches/0037-Check-state-is-valid-at-the-end-of-a-move-in-Pearl.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0037-Check-state-is-valid-at-the-end-of-a-move-in-Pearl.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0037-Check-state-is-valid-at-the-end-of-a-move-in-Pearl.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,129 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 11 Feb 2023 21:22:49 +0000
+Subject: [PATCH 037/159] Check state is valid at the end of a move in Pearl
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=c0b2f0fc98e87392dcb4dd8faf3076786fc49367
+Bug-Debian: https://bugs.debian.org/1034190
+
+A Pearl move string contains a sequence of sub-moves, each of which
+can affect the state of the connection between the centre of a square
+and one of its edges. interpret_move() generates these in pairs so
+that the two halves of a connection between the centres of adjacent
+squares stay in the same state.
+
+If, however, a save file contains mismatched half-moves,
+execute_move() should ideally return NULL rather than causing an
+assertion failure. This has to be checked at the end of the whole
+move string, so I've arranged for check_completion() to return a
+boolean indicating whether the current state (and hence the move
+preceding it) is valid. It now returns 'false' when a connection
+stops at a square boundary or when it goes off the board. These
+conditions used to be assertion failures, and now they just cause the
+move to be rejected.
+
+This supersedes the check for off-board connections added in 15f4fa8,
+since now check_completion() can check for off-board links for the
+whole board at once.
+
+This save file trivially demonstrates the problem, causing
+"dsf_update_completion: Assertion `state->lines[bc] & F(dir)' failed"
+without this fix:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :5:Pearl
+PARAMS :5:6x6t0
+CPARAMS :5:6x6t0
+DESC :17:BbBfWceBbWaBWWgWB
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :6:R1,0,0
+---
+ pearl.c | 37 ++++++++++++++++++-------------------
+ 1 file changed, 18 insertions(+), 19 deletions(-)
+
+--- a/pearl.c
++++ b/pearl.c
+@@ -1525,25 +1525,30 @@ static char nbits[16] = { 0, 1, 1, 2,
+
+ #define ERROR_CLUE 16
+
+-static void dsf_update_completion(game_state *state, int ax, int ay, char dir,
++/* Returns false if the state is invalid. */
++static bool dsf_update_completion(game_state *state, int ax, int ay, char dir,
+ int *dsf)
+ {
+ int w = state->shared->w /*, h = state->shared->h */;
+ int ac = ay*w+ax, bx, by, bc;
+
+- if (!(state->lines[ac] & dir)) return; /* no link */
++ if (!(state->lines[ac] & dir)) return true; /* no link */
+ bx = ax + DX(dir); by = ay + DY(dir);
+
+- assert(INGRID(state, bx, by)); /* should not have a link off grid */
++ if (!INGRID(state, bx, by))
++ return false; /* should not have a link off grid */
+
+ bc = by*w+bx;
+- assert(state->lines[bc] & F(dir)); /* should have reciprocal link */
+- if (!(state->lines[bc] & F(dir))) return;
++ if (!(state->lines[bc] & F(dir)))
++ return false; /* should have reciprocal link */
++ if (!(state->lines[bc] & F(dir))) return true;
+
+ dsf_merge(dsf, ac, bc);
++ return true;
+ }
+
+-static void check_completion(game_state *state, bool mark)
++/* Returns false if the state is invalid. */
++static bool check_completion(game_state *state, bool mark)
+ {
+ int w = state->shared->w, h = state->shared->h, x, y, i, d;
+ bool had_error = false;
+@@ -1571,8 +1576,11 @@ static void check_completion(game_state
+ /* Build the dsf. */
+ for (x = 0; x < w; x++) {
+ for (y = 0; y < h; y++) {
+- dsf_update_completion(state, x, y, R, dsf);
+- dsf_update_completion(state, x, y, D, dsf);
++ if (!dsf_update_completion(state, x, y, R, dsf) ||
++ !dsf_update_completion(state, x, y, D, dsf)) {
++ sfree(dsf);
++ return false;
++ }
+ }
+ }
+
+@@ -1727,6 +1735,7 @@ static void check_completion(game_state
+ if (!had_error)
+ state->completed = true;
+ }
++ return true;
+ }
+
+ /* completion check:
+@@ -2264,16 +2273,6 @@ static game_state *execute_move(const ga
+ (ret->marks[y*w + x] & (char)l))
+ goto badmove;
+
+- /*
+- * Similarly, if we've ended up with a line or mark going
+- * off the board, that's not acceptable.
+- */
+- for (l = 1; l <= 8; l <<= 1)
+- if (((ret->lines[y*w + x] & (char)l) ||
+- (ret->marks[y*w + x] & (char)l)) &&
+- !INGRID(state, x+DX(l), y+DY(l)))
+- goto badmove;
+-
+ move += n;
+ } else if (strcmp(move, "H") == 0) {
+ pearl_solve(ret->shared->w, ret->shared->h,
+@@ -2290,7 +2289,7 @@ static game_state *execute_move(const ga
+ goto badmove;
+ }
+
+- check_completion(ret, true);
++ if (!check_completion(ret, true)) goto badmove;
+
+ return ret;
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0038-Cleanly-reject-more-ill-formed-solve-moves-in-Flood.patch sgt-puzzles-20191231.79a5378/debian/patches/0038-Cleanly-reject-more-ill-formed-solve-moves-in-Flood.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0038-Cleanly-reject-more-ill-formed-solve-moves-in-Flood.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0038-Cleanly-reject-more-ill-formed-solve-moves-in-Flood.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,45 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 11 Feb 2023 22:00:49 +0000
+Subject: [PATCH 038/159] Cleanly reject more ill-formed solve moves in Flood
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=896a73bd7ff8cbde44e97d89cef57346478f0072
+Bug-Debian: https://bugs.debian.org/1034190
+
+The fix in e4112b3 was incomplete: there was another assertion that could be failed by a save file with an ill-formed solve move. That now gets rejected properly. Here's an example save file to demonstrate the problem:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :5:Flood
+PARAMS :7:6x6c6m0
+CPARAMS :7:6x6c6m0
+DESC :39:000000000000000000000000000000000000,00
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :1:S
+---
+ flood.c | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+--- a/flood.c
++++ b/flood.c
+@@ -924,15 +924,16 @@ static game_state *execute_move(const ga
+
+ sol->moves = snewn(sol->nmoves, char);
+ for (i = 0, p = move; i < sol->nmoves; i++) {
+- assert(*p);
++ if (!*p) {
++ badsolve:
++ sfree(sol->moves);
++ sfree(sol);
++ return NULL;
++ };
+ sol->moves[i] = atoi(p);
+ p += strspn(p, "0123456789");
+ if (*p) {
+- if (*p != ',') {
+- sfree(sol->moves);
+- sfree(sol);
+- return NULL;
+- }
++ if (*p != ',') goto badsolve;
+ p++;
+ }
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0039-Don-t-allow-moves-that-change-the-constraints-in-Une.patch sgt-puzzles-20191231.79a5378/debian/patches/0039-Don-t-allow-moves-that-change-the-constraints-in-Une.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0039-Don-t-allow-moves-that-change-the-constraints-in-Une.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0039-Don-t-allow-moves-that-change-the-constraints-in-Une.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,51 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 11 Feb 2023 22:49:36 +0000
+Subject: [PATCH 039/159] Don't allow moves that change the constraints in
+ Unequal
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=97b03cc67a31c1d0869a21c50b9ca31f78775ff9
+Bug-Debian: https://bugs.debian.org/1034190
+
+Unequal has a flags word per cell. Some of those flags are fixed,
+like the locations of the ">" signs, but others indicate errors and
+are used to allow the player to mark clues as "spent". Move strings
+beginning with "F" allow the user to change the "spent" flags, but
+they shouldn't allow the user to change any other flags, especially
+those marking the constraints.
+
+Without this fix, the following save file gives a "solver_nminmax:
+Assertion `x >= 0 && y >= 0 && x < o && y < o' failed" after it adds a
+clue that points off the board:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+GAME :7:Unequal
+PARAMS :3:3e0
+CPARAMS :3:3e0
+DESC :17:0,0,0,0,0,0,0,0,0
+NSTATES :2:3
+STATEPOS:1:3
+MOVE :6:F2,0,4
+MOVE :1:H
+---
+ unequal.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/unequal.c
++++ b/unequal.c
+@@ -84,6 +84,7 @@ struct game_params {
+ #define ADJ_TO_SPENT(x) ((x) << 9)
+
+ #define F_ERROR_MASK (F_ERROR|F_ERROR_UP|F_ERROR_RIGHT|F_ERROR_DOWN|F_ERROR_LEFT)
++#define F_SPENT_MASK (F_SPENT_UP|F_SPENT_RIGHT|F_SPENT_DOWN|F_SPENT_LEFT)
+
+ struct game_state {
+ int order;
+@@ -1610,7 +1611,8 @@ static game_state *execute_move(const ga
+ check_complete(ret->nums, ret, true);
+ return ret;
+ } else if (move[0] == 'F' && sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
+- x >= 0 && x < state->order && y >= 0 && y < state->order) {
++ x >= 0 && x < state->order && y >= 0 && y < state->order &&
++ (n & ~F_SPENT_MASK) == 0) {
+ ret = dup_game(state);
+ GRID(ret, flags, x, y) ^= n;
+ return ret;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0041-Fix-memory-leaks-in-Keen-s-validate_desc.patch sgt-puzzles-20191231.79a5378/debian/patches/0041-Fix-memory-leaks-in-Keen-s-validate_desc.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0041-Fix-memory-leaks-in-Keen-s-validate_desc.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0041-Fix-memory-leaks-in-Keen-s-validate_desc.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,50 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 09:31:03 +0000
+Subject: [PATCH 041/159] Fix memory leaks in Keen's validate_desc()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=bb31efdbc91495a4385ca0afffa4c8bb8f564d7b
+Bug-Debian: https://bugs.debian.org/1034190
+
+Keen uses a DSF to validate its game descriptions and almost always
+failed to free it, even when the validation succeeded.
+---
+ keen.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/keen.c
++++ b/keen.c
+@@ -1224,8 +1224,10 @@ static const char *validate_desc(const g
+ return ret;
+ }
+
+- if (*p != ',')
++ if (*p != ',') {
++ sfree(dsf);
+ return "Expected ',' after block structure description";
++ }
+ p++;
+
+ /*
+@@ -1237,17 +1239,22 @@ static const char *validate_desc(const g
+ if (*p == 'a' || *p == 'm') {
+ /* these clues need no validation */
+ } else if (*p == 'd' || *p == 's') {
+- if (dsf_size(dsf, i) != 2)
++ if (dsf_size(dsf, i) != 2) {
++ sfree(dsf);
+ return "Subtraction and division blocks must have area 2";
++ }
+ } else if (!*p) {
++ sfree(dsf);
+ return "Too few clues for block structure";
+ } else {
++ sfree(dsf);
+ return "Unrecognised clue type";
+ }
+ p++;
+ while (*p && isdigit((unsigned char)*p)) p++;
+ }
+ }
++ sfree(dsf);
+ if (*p)
+ return "Too many clues for block structure";
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0043-Don-t-leak-grids-in-Loopy-s-validate_desc.patch sgt-puzzles-20191231.79a5378/debian/patches/0043-Don-t-leak-grids-in-Loopy-s-validate_desc.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0043-Don-t-leak-grids-in-Loopy-s-validate_desc.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0043-Don-t-leak-grids-in-Loopy-s-validate_desc.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,33 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 09:45:08 +0000
+Subject: [PATCH 043/159] Don't leak grids in Loopy's validate_desc()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=11b631ea870355306c4b1d03458bb3cea8f29188
+Bug-Debian: https://bugs.debian.org/1034190
+
+---
+ loopy.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+--- a/loopy.c
++++ b/loopy.c
+@@ -788,13 +788,18 @@ static const char *validate_desc(const g
+ count += *desc - 'a' + 1;
+ continue;
+ }
++ grid_free(g);
+ return "Unknown character in description";
+ }
+
+- if (count < g->num_faces)
++ if (count < g->num_faces) {
++ grid_free(g);
+ return "Description too short for board size";
+- if (count > g->num_faces)
++ }
++ if (count > g->num_faces) {
++ grid_free(g);
+ return "Description too long for board size";
++ }
+
+ grid_free(g);
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0044-Remember-to-free-the-to_draw-member-from-Net-s-draws.patch sgt-puzzles-20191231.79a5378/debian/patches/0044-Remember-to-free-the-to_draw-member-from-Net-s-draws.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0044-Remember-to-free-the-to_draw-member-from-Net-s-draws.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0044-Remember-to-free-the-to_draw-member-from-Net-s-draws.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,21 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 09:48:16 +0000
+Subject: [PATCH 044/159] Remember to free the to_draw member from Net's
+ drawstate
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=493bf16ddbe2185664d6c3053f7891a9f232c75c
+Bug-Debian: https://bugs.debian.org/1034190
+
+---
+ net.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/net.c
++++ b/net.c
+@@ -2464,6 +2464,7 @@ static game_drawstate *game_new_drawstat
+ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
+ {
+ sfree(ds->visible);
++ sfree(ds->to_draw);
+ sfree(ds);
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0045-Undead-check-the-return-value-of-sscanf-in-execute_m.patch sgt-puzzles-20191231.79a5378/debian/patches/0045-Undead-check-the-return-value-of-sscanf-in-execute_m.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0045-Undead-check-the-return-value-of-sscanf-in-execute_m.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0045-Undead-check-the-return-value-of-sscanf-in-execute_m.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,29 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 10:04:47 +0000
+Subject: [PATCH 045/159] Undead: check the return value of sscanf() in
+ execute_move()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=0a7c531e8f4c1970662f7c30aea006e65d5ff010
+Bug-Debian: https://bugs.debian.org/1034190
+
+sscanf() assigns its output in order, so if a conversion specifier fails
+to match, a later "%n" specifier will also not get its result assigned.
+In Undead's execute_move(), this led to the result of "%n" being used
+without being initialised. That could cause it to try to parse
+arbitrary memory as part of the move string, which shouldn't be a
+security problem (since execute_move() handles untrusted input anyway),
+but could lead to a crash and certainly wasn't helpful.
+---
+ undead.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/undead.c
++++ b/undead.c
+@@ -2059,7 +2059,7 @@ static game_state *execute_move(const ga
+ } else if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' ||
+ c == 'g' || c == 'v' || c == 'z') {
+ move++;
+- sscanf(move, "%d%n", &x, &n);
++ if (sscanf(move, "%d%n", &x, &n) != 1) goto badmove;
+ if (x < 0 || x >= ret->common->num_total) goto badmove;
+ if (c == 'G') ret->guess[x] = 1;
+ if (c == 'V') ret->guess[x] = 2;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0046-Don-t-leak-duplicate-edges-in-Untangle.patch sgt-puzzles-20191231.79a5378/debian/patches/0046-Don-t-leak-duplicate-edges-in-Untangle.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0046-Don-t-leak-duplicate-edges-in-Untangle.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0046-Don-t-leak-duplicate-edges-in-Untangle.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,27 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 11:08:14 +0000
+Subject: [PATCH 046/159] Don't leak duplicate edges in Untangle
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=19401e95e0a75577103e9c1a877611234a0d8ab5
+Bug-Debian: https://bugs.debian.org/1034190
+
+Untangle game descriptions are allowed to contain duplicate edges, and
+add234() can handle deduping them. However, when add234() reports that
+your newly-allocated edge is a duplicate, it's important to free it
+again.
+---
+ untangle.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/untangle.c
++++ b/untangle.c
+@@ -420,7 +420,9 @@ static void addedge(tree234 *edges, int
+ e->a = min(a, b);
+ e->b = max(a, b);
+
+- add234(edges, e);
++ if (add234(edges, e) != e)
++ /* Duplicate edge. */
++ sfree(e);
+ }
+
+ static bool isedge(tree234 *edges, int a, int b)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0047-Remember-to-free-the-numcolours-array-from-Pattern-s.patch sgt-puzzles-20191231.79a5378/debian/patches/0047-Remember-to-free-the-numcolours-array-from-Pattern-s.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0047-Remember-to-free-the-numcolours-array-from-Pattern-s.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0047-Remember-to-free-the-numcolours-array-from-Pattern-s.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,22 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 11:13:03 +0000
+Subject: [PATCH 047/159] Remember to free the numcolours array from Pattern's
+ drawstate
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=1aa67e7a75ac21d15780d6aaab65a2c0f6f65198
+Bug-Debian: https://bugs.debian.org/1034190
+
+[benh: Backported to 20191231: Adjust context]
+---
+ pattern.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/pattern.c
++++ b/pattern.c
+@@ -1706,6 +1706,7 @@ static game_drawstate *game_new_drawstat
+ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
+ {
+ sfree(ds->visible);
++ sfree(ds->numcolours);
+ sfree(ds);
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0049-Twiddle-don-t-read-off-the-end-of-parameter-strings-.patch sgt-puzzles-20191231.79a5378/debian/patches/0049-Twiddle-don-t-read-off-the-end-of-parameter-strings-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0049-Twiddle-don-t-read-off-the-end-of-parameter-strings-.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0049-Twiddle-don-t-read-off-the-end-of-parameter-strings-.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,35 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 14:31:39 +0000
+Subject: [PATCH 049/159] Twiddle: don't read off the end of parameter strings
+ ending 'm'
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=73c7bc090155ab8c4661feaeea9e6a6e74ee6f77
+Bug-Debian: https://bugs.debian.org/1034190
+
+The overrun could be demonstrated by specifying a parameter string of
+"3x3m" to a build with AddressSanitizer.
+---
+ twiddle.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/twiddle.c
++++ b/twiddle.c
+@@ -124,14 +124,16 @@ static void decode_params(game_params *r
+ while (*string) {
+ if (*string == 'r') {
+ ret->rowsonly = true;
++ string++;
+ } else if (*string == 'o') {
+ ret->orientable = true;
++ string++;
+ } else if (*string == 'm') {
+ string++;
+ ret->movetarget = atoi(string);
+- while (string[1] && isdigit((unsigned char)string[1])) string++;
+- }
+- string++;
++ while (*string && isdigit((unsigned char)*string)) string++;
++ } else
++ string++;
+ }
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0050-Loopy-free-the-grid-description-string-if-it-s-inval.patch sgt-puzzles-20191231.79a5378/debian/patches/0050-Loopy-free-the-grid-description-string-if-it-s-inval.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0050-Loopy-free-the-grid-description-string-if-it-s-inval.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0050-Loopy-free-the-grid-description-string-if-it-s-inval.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,29 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 20:17:58 +0000
+Subject: [PATCH 050/159] Loopy: free the grid description string if it's
+ invalid
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=e336513be755159158c5ba017c91b018ad4cd36c
+Bug-Debian: https://bugs.debian.org/1034190
+
+---
+ loopy.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/loopy.c
++++ b/loopy.c
+@@ -774,10 +774,13 @@ static const char *validate_desc(const g
+ * know is the precise number of faces. */
+ grid_desc = extract_grid_desc(&desc);
+ ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc);
+- if (ret) return ret;
++ if (ret) {
++ sfree(grid_desc);
++ return ret;
++ }
+
+ g = loopy_generate_grid(params, grid_desc);
+- if (grid_desc) sfree(grid_desc);
++ sfree(grid_desc);
+
+ for (; *desc; ++desc) {
+ if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0052-Avoid-division-by-zero-in-Cube-grid-size-checks.patch sgt-puzzles-20191231.79a5378/debian/patches/0052-Avoid-division-by-zero-in-Cube-grid-size-checks.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0052-Avoid-division-by-zero-in-Cube-grid-size-checks.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0052-Avoid-division-by-zero-in-Cube-grid-size-checks.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,32 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 00:14:22 +0000
+Subject: [PATCH 052/159] Avoid division by zero in Cube grid-size checks
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=df783b93e3271264a8d54f90876f41a80ef2247d
+Bug-Debian: https://bugs.debian.org/1034190
+
+On a triangular grid, Cube allows either d1 or d2 (but not both) to be
+zero, so it's important to check that each one is not zero before
+dividing by it.
+
+The crash could be triggered by, for instance "cube t0x2".
+---
+ cube.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/cube.c
++++ b/cube.c
+@@ -567,9 +567,11 @@ static const char *validate_params(const
+ * can safely multiply them and compare against the
+ * _remaining_ space.
+ */
+- if ((params->d1 > INT_MAX / params->d1) ||
+- (params->d2 > (INT_MAX - params->d1*params->d1) / params->d2) ||
+- (params->d1*params->d2 > (INT_MAX - params->d1*params->d1 -
++ if ((params->d1 > 0 && params->d1 > INT_MAX / params->d1) ||
++ (params->d2 > 0 &&
++ params->d2 > (INT_MAX - params->d1*params->d1) / params->d2) ||
++ (params->d2 > 0 &&
++ params->d1*params->d2 > (INT_MAX - params->d1*params->d1 -
+ params->d2*params->d2) / params->d2))
+ return "Grid area must not be unreasonably large";
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0055-Validate-that-save-file-values-are-ASCII-mostly.patch sgt-puzzles-20191231.79a5378/debian/patches/0055-Validate-that-save-file-values-are-ASCII-mostly.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0055-Validate-that-save-file-values-are-ASCII-mostly.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0055-Validate-that-save-file-values-are-ASCII-mostly.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,37 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 12 Feb 2023 23:04:12 +0000
+Subject: [PATCH 055/159] Validate that save file values are ASCII (mostly)
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=c3a5a7842eb6c41fb75a8a110a3f2cbc1c8fc5d9
+Bug-Debian: https://bugs.debian.org/1034190
+
+Apart from "SEED" records, all values in save files generated by Puzzles
+should be printable ASCII. This is enforced by assertion in the saving
+code. However, if a save file with non-ASCII move strings (for
+instance) manages to get loaded then these non-ASCII values can cause an
+assertion failure on saving. Instead, the loading code now checks
+values for ASCIIness.
+
+This will not only avoid problems when re-saving files, but will also
+defend the various internal parsers from at least some evil strings. It
+shouldn't invalidate any save files actually generated by Puzzles, but
+it will sadly invalidate some of my fuzzing corpus.
+---
+ midend.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/midend.c
++++ b/midend.c
+@@ -2198,6 +2198,13 @@ static const char *midend_deserialise_in
+ goto cleanup;
+ }
+ val[len] = '\0';
++ /* Validate that all values (apart from SEED) are printable ASCII. */
++ if (strcmp(key, "SEED"))
++ for (i = 0; val[i]; i++)
++ if (val[i] < 32 || val[i] >= 127) {
++ ret = "Forbidden characters in saved game file";
++ goto cleanup;
++ }
+
+ if (!started) {
+ if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0056-More-validation-of-solve-moves-in-Flood.patch sgt-puzzles-20191231.79a5378/debian/patches/0056-More-validation-of-solve-moves-in-Flood.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0056-More-validation-of-solve-moves-in-Flood.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0056-More-validation-of-solve-moves-in-Flood.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,29 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 00:00:30 +0000
+Subject: [PATCH 056/159] More validation of solve moves in Flood
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=e8668dc883e940f0852ff4520abc3d30cae90aef
+Bug-Debian: https://bugs.debian.org/1034190
+
+To avoid assertion failures while painting it, we need to ensure that
+the purported solution in a solve move doesn't include filling with the
+current top-left colour at any point. That means checking the first
+entry against the current top-left colours, and each later one against
+its predecessor.
+---
+ flood.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/flood.c
++++ b/flood.c
+@@ -931,6 +931,11 @@ static game_state *execute_move(const ga
+ return NULL;
+ };
+ sol->moves[i] = atoi(p);
++ if (i == 0 ?
++ sol->moves[i] == state->grid[FILLY * state->w + FILLX] :
++ sol->moves[i] == sol->moves[i-1])
++ /* Solution contains a fill with the current colour. */
++ goto badsolve;
+ p += strspn(p, "0123456789");
+ if (*p) {
+ if (*p != ',') goto badsolve;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0058-Make-sure-that-moves-in-Flood-use-only-valid-colours.patch sgt-puzzles-20191231.79a5378/debian/patches/0058-Make-sure-that-moves-in-Flood-use-only-valid-colours.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0058-Make-sure-that-moves-in-Flood-use-only-valid-colours.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0058-Make-sure-that-moves-in-Flood-use-only-valid-colours.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,54 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 14 Feb 2023 22:02:35 +0000
+Subject: [PATCH 058/159] Make sure that moves in Flood use only valid colours
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=7364ce8e266d947be146d635958a7b282752aac6
+Bug-Debian: https://bugs.debian.org/1034190
+
+If execute_move() receieves a move that uses a colour beyond the range
+for the current game, it now rejects it. Without this a solve string
+containing an invalid colour would cause an assertion failure: "fill:
+Assertion `oldcolour != newcolour' failed." While I was in the area I
+put a range check on colours for normal moves as well. To demonstrate
+the problem, load this save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :5:Flood
+PARAMS :7:6x6c6m5
+CPARAMS :7:6x6c6m3
+DESC :39:432242034203340350204502505323231342,17
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :2:S6
+---
+ flood.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/flood.c
++++ b/flood.c
+@@ -872,7 +872,7 @@ static game_state *execute_move(const ga
+
+ if (move[0] == 'M' &&
+ sscanf(move+1, "%d", &c) == 1 &&
+- c >= 0 &&
++ c >= 0 && c < state->colours &&
+ c != state->grid[FILLY * state->w + FILLX] &&
+ !state->complete) {
+ int *queue = snewn(state->w * state->h, int);
+@@ -931,10 +931,12 @@ static game_state *execute_move(const ga
+ return NULL;
+ };
+ sol->moves[i] = atoi(p);
+- if (i == 0 ?
+- sol->moves[i] == state->grid[FILLY * state->w + FILLX] :
+- sol->moves[i] == sol->moves[i-1])
+- /* Solution contains a fill with the current colour. */
++ if (sol->moves[i] < 0 || sol->moves[i] >= state->colours ||
++ (i == 0 ?
++ sol->moves[i] == state->grid[FILLY * state->w + FILLX] :
++ sol->moves[i] == sol->moves[i-1]))
++ /* Solution contains a fill with an invalid colour or
++ * the current colour. */
+ goto badsolve;
+ p += strspn(p, "0123456789");
+ if (*p) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0059-Tighten-grid-size-limit-in-Mines.patch sgt-puzzles-20191231.79a5378/debian/patches/0059-Tighten-grid-size-limit-in-Mines.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0059-Tighten-grid-size-limit-in-Mines.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0059-Tighten-grid-size-limit-in-Mines.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,46 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 15 Feb 2023 14:07:34 +0000
+Subject: [PATCH 059/159] Tighten grid-size limit in Mines
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=9394e9c74bdb48dc1c74693bcb41fd35f8fc743c
+Bug-Debian: https://bugs.debian.org/1034190
+
+Mines uses random_upto() to decide where to place mines, and
+random_upto() takes a maximum limit of 2^28-1, so limit the number of
+grid squares to that (or INT_MAX if someone's still trying to build on
+a 16-bit system).
+
+This avoids an assertion failure: "random_upto: Assertion `bits < 32'
+failed." which can be demonstrated by this save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :5:Mines
+PARAMS :5:18090
+CPARAMS :5:18090
+DESC :11:r9,u,MEdff6
+UI :2:D0
+TIME :1:0
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :4:O2,1
+---
+ mines.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -265,7 +265,14 @@ static const char *validate_params(const
+ return "Width and height must both be at least one";
+ if (params->w > SHRT_MAX || params->h > SHRT_MAX)
+ return "Neither width nor height may be unreasonably large";
++ /*
++ * We use random_upto() to place mines, and its maximum limit is 2^28-1.
++ */
++#if (1<<28)-1 < INT_MAX
++ if (params->w > ((1<<28)-1) / params->h)
++#else
+ if (params->w > INT_MAX / params->h)
++#endif
+ return "Width times height must not be unreasonably large";
+ if (params->n < 0)
+ return "Mine count may not be negative";
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0061-Solo-cope-with-pencil-marks-when-tilesize-1.patch sgt-puzzles-20191231.79a5378/debian/patches/0061-Solo-cope-with-pencil-marks-when-tilesize-1.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0061-Solo-cope-with-pencil-marks-when-tilesize-1.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0061-Solo-cope-with-pencil-marks-when-tilesize-1.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,30 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 16 Feb 2023 15:54:17 +0000
+Subject: [PATCH 061/159] Solo: cope with pencil marks when tilesize == 1
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=3cd51d001769c657ebb4184bd05343af4d7e12b1
+Bug-Debian: https://bugs.debian.org/905852
+
+Solo's layout calculations for pencil marks could fail with a tilesize
+of 1, generating an assertion failure: "draw_number: Assertion `pbest
+> 0' failed." This was reported as Debian bug #905852.
+
+My solution is slightly silly, namely to change a ">" in the test for
+whether a new layout is the best so far to ">=". This allows for
+finding a (terrible) layout even for tilesize == 1, and also has the
+side-effect of slightly preserring wide layouts over tall ones.
+Personally, I think that's an improvement.
+---
+ solo.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/solo.c
++++ b/solo.c
+@@ -5105,7 +5105,7 @@ static void draw_number(drawing *dr, gam
+ fw = (pr - pl) / (float)pw;
+ fh = (pb - pt) / (float)ph;
+ fs = min(fw, fh);
+- if (fs > bestsize) {
++ if (fs >= bestsize) {
+ bestsize = fs;
+ pbest = pw;
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0065-Tracks-set-drag_s-x-y-even-if-starting-off-grid.patch sgt-puzzles-20191231.79a5378/debian/patches/0065-Tracks-set-drag_s-x-y-even-if-starting-off-grid.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0065-Tracks-set-drag_s-x-y-even-if-starting-off-grid.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0065-Tracks-set-drag_s-x-y-even-if-starting-off-grid.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,28 @@
+From: Chris Boyle <chris@boyle.name>
+Date: Fri, 10 Feb 2023 15:23:33 +0000
+Subject: [PATCH 065/159] Tracks: set drag_s{x,y} even if starting off-grid
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=111db0743bd273c340dd683449076fd9ec5797f5
+Bug-Debian: https://bugs.debian.org/1034190
+
+Otherwise, if subsequent mouse/finger movement lines up with the previous
+drag attempt's start, then suddenly a drag is in progress from there, which
+is confusing.
+
+Fixes #588
+
+(cherry picked from Android port,
+commit 8ce1bbe460d70a915caf2dbeb30354d22dc8a8ef)
+---
+ tracks.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/tracks.c
++++ b/tracks.c
+@@ -1916,6 +1916,7 @@ static char *interpret_move(const game_s
+
+ if (!INGRID(state, gx, gy)) {
+ /* can't drag from off grid */
++ ui->drag_sx = ui->drag_sy = -1;
+ return NULL;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0080-Undead-be-a-bit-more-careful-about-sprintf-buffer-si.patch sgt-puzzles-20191231.79a5378/debian/patches/0080-Undead-be-a-bit-more-careful-about-sprintf-buffer-si.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0080-Undead-be-a-bit-more-careful-about-sprintf-buffer-si.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0080-Undead-be-a-bit-more-careful-about-sprintf-buffer-si.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 18 Feb 2023 21:26:38 +0000
+Subject: [PATCH 080/159] Undead: be a bit more careful about sprintf buffer
+ sizes
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=448095ede815b1a63ddedc602c3ac768a0d52968
+Bug-Debian: https://bugs.debian.org/1034190
+
+---
+ undead.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/undead.c
++++ b/undead.c
+@@ -2405,7 +2405,7 @@ static void draw_monster(drawing *dr, ga
+ static void draw_monster_count(drawing *dr, game_drawstate *ds,
+ const game_state *state, int c, bool hflash) {
+ int dx,dy;
+- char buf[8];
++ char buf[MAX_DIGITS(int) + 1];
+ char bufm[8];
+
+ dy = TILESIZE/4;
+@@ -2450,7 +2450,7 @@ static void draw_path_hint(drawing *dr,
+ const struct game_params *params,
+ int hint_index, bool hflash, int hint) {
+ int x, y, color, dx, dy, text_dx, text_dy, text_size;
+- char buf[4];
++ char buf[MAX_DIGITS(int) + 1];
+
+ if (ds->hint_errors[hint_index])
+ color = COL_ERROR;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0081-Map-add-missing-sresize-in-new_game_desc.patch sgt-puzzles-20191231.79a5378/debian/patches/0081-Map-add-missing-sresize-in-new_game_desc.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0081-Map-add-missing-sresize-in-new_game_desc.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0081-Map-add-missing-sresize-in-new_game_desc.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,30 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Sat, 11 Dec 2021 11:03:20 +0000
+Subject: [PATCH 081/389] Map: add missing sresize in new_game_desc().
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=3e006158451a7ff8f130cbcb7dd80f165a58396e
+
+Every time we append to the string 'ret', we check first that there's
+enough space, and realloc it larger if it's getting close to full.
+Except that I missed one case at the join between the two parts of the
+encoding.
+
+(Spotted because apparently on someone's build platform this led to a
+compiler warning that 'ret' might be null. I think _that's_ not a
+serious worry, but the missing resize was definitely unintentional.)
+---
+ map.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/map.c
++++ b/map.c
+@@ -1659,6 +1659,10 @@ static char *new_game_desc(const game_pa
+ }
+ }
+
++ if (retlen + 10 >= retsize) {
++ retsize = retlen + 256;
++ ret = sresize(ret, retsize, char);
++ }
+ ret[retlen++] = 'a'-1 + run;
+ ret[retlen++] = ',';
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0090-Fix-memory-leak-in-midend_game_id_int.patch sgt-puzzles-20191231.79a5378/debian/patches/0090-Fix-memory-leak-in-midend_game_id_int.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0090-Fix-memory-leak-in-midend_game_id_int.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0090-Fix-memory-leak-in-midend_game_id_int.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,32 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 20 Feb 2023 14:50:22 +0000
+Subject: [PATCH 090/159] Fix memory leak in midend_game_id_int()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=4e09175fdaaffc0483fc9bf767311268794d956c
+Bug-Debian: https://bugs.debian.org/1034190
+
+The "par" string wasn't getting freed on some error paths. Fixed by
+freeing it immediately after its last use, which is before any of the
+error paths.
+---
+ midend.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -1669,6 +1669,7 @@ static const char *midend_game_id_int(mi
+ newcurparams = me->ourgame->default_params();
+ }
+ me->ourgame->decode_params(newcurparams, par);
++ sfree(par);
+ error = me->ourgame->validate_params(newcurparams, desc == NULL);
+ if (error) {
+ me->ourgame->free_params(newcurparams);
+@@ -1744,8 +1745,6 @@ static const char *midend_game_id_int(mi
+ me->genmode = GOT_SEED;
+ }
+
+- sfree(par);
+-
+ me->newgame_can_store_undo = false;
+
+ return NULL;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0092-Flood-don-t-read-off-the-end-of-some-parameter-strin.patch sgt-puzzles-20191231.79a5378/debian/patches/0092-Flood-don-t-read-off-the-end-of-some-parameter-strin.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0092-Flood-don-t-read-off-the-end-of-some-parameter-strin.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0092-Flood-don-t-read-off-the-end-of-some-parameter-strin.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,35 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 20 Feb 2023 14:57:31 +0000
+Subject: [PATCH 092/159] Flood: don't read off the end of some parameter
+ strings
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=bbe866a3819c6a754a5b1d8c5bc5d0701796acfb
+Bug-Debian: https://bugs.debian.org/1034190
+
+This is essentially the same fix as 73c7bc090155ab8c was for Twiddle.
+The new code is less clever but more correct (and more obviously
+correct). The bug could be demonstrated by using a parameter string
+of "c" or "m" with an AddressSanitizer build of Flood.
+---
+ flood.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/flood.c
++++ b/flood.c
+@@ -141,13 +141,13 @@ static void decode_params(game_params *r
+ if (*string == 'c') {
+ string++;
+ ret->colours = atoi(string);
+- while (string[1] && isdigit((unsigned char)string[1])) string++;
++ while (*string && isdigit((unsigned char)*string)) string++;
+ } else if (*string == 'm') {
+ string++;
+ ret->leniency = atoi(string);
+- while (string[1] && isdigit((unsigned char)string[1])) string++;
+- }
+- string++;
++ while (*string && isdigit((unsigned char)*string)) string++;
++ } else
++ string++;
+ }
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0101-Be-more-careful-with-type-of-left-operand-of.patch sgt-puzzles-20191231.79a5378/debian/patches/0101-Be-more-careful-with-type-of-left-operand-of.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0101-Be-more-careful-with-type-of-left-operand-of.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0101-Be-more-careful-with-type-of-left-operand-of.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,45 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 26 Feb 2023 14:24:38 +0000
+Subject: [PATCH 101/159] Be more careful with type of left operand of <<
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=93be3f7ccaa63b0fd953bcfd88d685b47b76605e
+Bug-Debian: https://bugs.debian.org/1034190
+
+On a 32-bit system, evaluating 1<<31 causes undefined behaviour because
+1 is signed and so it produces signed overflow. UBSan has spotted a
+couple of occasions where this happens in Puzzles, so in each case I've
+converted the left operand to the unsigned result type we actually want.
+---
+ cube.c | 4 ++--
+ random.c | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/cube.c
++++ b/cube.c
+@@ -202,8 +202,8 @@ struct game_grid {
+ };
+
+ #define SET_SQUARE(state, i, val) \
+- ((state)->bluemask[(i)/32] &= ~(1 << ((i)%32)), \
+- (state)->bluemask[(i)/32] |= ((!!val) << ((i)%32)))
++ ((state)->bluemask[(i)/32] &= ~(1UL << ((i)%32)), \
++ (state)->bluemask[(i)/32] |= ((unsigned long)(!!val) << ((i)%32)))
+ #define GET_SQUARE(state, i) \
+ (((state)->bluemask[(i)/32] >> ((i)%32)) & 1)
+
+--- a/random.c
++++ b/random.c
+@@ -254,12 +254,12 @@ unsigned long random_bits(random_state *
+ }
+
+ /*
+- * `(1 << bits) - 1' is not good enough, since if bits==32 on a
++ * `(1UL << bits) - 1' is not good enough, since if bits==32 on a
+ * 32-bit machine, behaviour is undefined and Intel has a nasty
+ * habit of shifting left by zero instead. We'll shift by
+ * bits-1 and then separately shift by one.
+ */
+- ret &= (1 << (bits-1)) * 2 - 1;
++ ret &= (1UL << (bits-1)) * 2 - 1;
+ return ret;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0102-Map-reduce-maximum-size.patch sgt-puzzles-20191231.79a5378/debian/patches/0102-Map-reduce-maximum-size.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0102-Map-reduce-maximum-size.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0102-Map-reduce-maximum-size.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,23 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 26 Feb 2023 14:51:09 +0000
+Subject: [PATCH 102/159] Map: reduce maximum size
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=e2d390aae872cee4cb16d746af3b2eeb7713cbf5
+Bug-Debian: https://bugs.debian.org/1034190
+
+validate_desc relies on being able to calculate 2*wh in an int, so the
+maximum grid size is at most INT_MAX/2.
+---
+ map.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/map.c
++++ b/map.c
+@@ -255,7 +255,7 @@ static const char *validate_params(const
+ {
+ if (params->w < 2 || params->h < 2)
+ return "Width and height must be at least two";
+- if (params->w > INT_MAX / params->h)
++ if (params->w > INT_MAX / 2 / params->h)
+ return "Width times height must not be unreasonably large";
+ if (params->n < 5)
+ return "Must have at least five regions";
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0103-Correctly-handle-some-short-save-files.patch sgt-puzzles-20191231.79a5378/debian/patches/0103-Correctly-handle-some-short-save-files.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0103-Correctly-handle-some-short-save-files.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0103-Correctly-handle-some-short-save-files.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,40 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 26 Feb 2023 21:48:10 +0000
+Subject: [PATCH 103/159] Correctly handle some short save files
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=6ee62a43abe7d7e77226415b21d1cbf16dbda85a
+Bug-Debian: https://bugs.debian.org/1034190
+
+A save file that ended in the middle of a value before the "SAVEFILE"
+field had been loaded would cause a read from uninitialised memory.
+While technically undefined behaviour this was practically pretty
+harmless. Fixed by handling unexpected EOF here the same an
+unexpected EOF anywhere else.
+
+This bug could be demonstrated by loading a truncated save file like
+this in a build with MemorySanitizer enabled:
+
+SAVEFILE:41:Simo
+---
+ midend.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -2192,7 +2192,7 @@ static const char *midend_deserialise_in
+
+ val = snewn(len+1, char);
+ if (!read(rctx, val, len)) {
+- if (started)
++ /* unexpected EOF */
+ goto cleanup;
+ }
+ val[len] = '\0';
+@@ -2597,7 +2597,7 @@ const char *identify_game(char **name,
+
+ val = snewn(len+1, char);
+ if (!read(rctx, val, len)) {
+- if (started)
++ /* unexpected EOF */
+ goto cleanup;
+ }
+ val[len] = '\0';
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0104-Inertia-insist-that-solutions-must-be-non-empty.patch sgt-puzzles-20191231.79a5378/debian/patches/0104-Inertia-insist-that-solutions-must-be-non-empty.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0104-Inertia-insist-that-solutions-must-be-non-empty.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0104-Inertia-insist-that-solutions-must-be-non-empty.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,29 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 26 Feb 2023 23:18:44 +0000
+Subject: [PATCH 104/159] Inertia: insist that solutions must be non-empty
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=5a491c5ad333ef34c1e7713f920f51cbb205af60
+Bug-Debian: https://bugs.debian.org/1034190
+
+Any solution actually generated by the solver will contain at least one
+move, because it refuses to solve games that are already solved.
+However, a save file might contain an empty "solve" move. This causes
+an uninitialised read when execute_move() then tries to check if the
+next move is in accordance with the solution, because the check for
+running off the end of the solution happens after that.
+
+We now avoid this by treating a zero-length "solution" as an invalid
+move.
+---
+ inertia.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/inertia.c
++++ b/inertia.c
+@@ -1688,6 +1688,7 @@ static game_state *execute_move(const ga
+ * This is a solve move, so we don't actually _change_ the
+ * grid but merely set up a stored solution path.
+ */
++ if (move[1] == '\0') return NULL; /* Solution must be non-empty. */
+ ret = dup_game(state);
+ install_new_solution(ret, move);
+ return ret;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0114-Add-more-validation-to-midend-deserialisation-routin.patch sgt-puzzles-20191231.79a5378/debian/patches/0114-Add-more-validation-to-midend-deserialisation-routin.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0114-Add-more-validation-to-midend-deserialisation-routin.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0114-Add-more-validation-to-midend-deserialisation-routin.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,98 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 16 Oct 2022 18:31:54 +0100
+Subject: [PATCH 114/389] Add more validation to midend deserialisation routine
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=02e5e93046d1ee2ce7acde629a6562db9b36fa5d
+
+These are all pretty obvious and enforce constraints that would
+otherwise be enforced by segfault.
+---
+ midend.c | 45 +++++++++++++++++++++++++++++----------------
+ 1 file changed, 29 insertions(+), 16 deletions(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -2205,15 +2205,15 @@ static const char *midend_deserialise_in
+ } else if (!strcmp(key, "TIME")) {
+ data.elapsed = (float)atof(val);
+ } else if (!strcmp(key, "NSTATES")) {
++ if (data.states) {
++ ret = "Two state counts provided in save file";
++ goto cleanup;
++ }
+ data.nstates = atoi(val);
+ if (data.nstates <= 0) {
+ ret = "Number of states in save file was negative";
+ goto cleanup;
+ }
+- if (data.states) {
+- ret = "Two state counts provided in save file";
+- goto cleanup;
+- }
+ data.states = snewn(data.nstates, struct midend_state_entry);
+ for (i = 0; i < data.nstates; i++) {
+ data.states[i].state = NULL;
+@@ -2222,19 +2222,20 @@ static const char *midend_deserialise_in
+ }
+ } else if (!strcmp(key, "STATEPOS")) {
+ data.statepos = atoi(val);
+- } else if (!strcmp(key, "MOVE")) {
+- gotstates++;
+- data.states[gotstates].movetype = MOVE;
+- data.states[gotstates].movestr = val;
+- val = NULL;
+- } else if (!strcmp(key, "SOLVE")) {
+- gotstates++;
+- data.states[gotstates].movetype = SOLVE;
+- data.states[gotstates].movestr = val;
+- val = NULL;
+- } else if (!strcmp(key, "RESTART")) {
++ } else if (!strcmp(key, "MOVE") ||
++ !strcmp(key, "SOLVE") ||
++ !strcmp(key, "RESTART")) {
++ if (!data.states) {
++ ret = "No state count provided in save file";
++ goto cleanup;
++ }
+ gotstates++;
+- data.states[gotstates].movetype = RESTART;
++ if (!strcmp(key, "MOVE"))
++ data.states[gotstates].movetype = MOVE;
++ else if (!strcmp(key, "SOLVE"))
++ data.states[gotstates].movetype = SOLVE;
++ else
++ data.states[gotstates].movetype = RESTART;
+ data.states[gotstates].movestr = val;
+ val = NULL;
+ }
+@@ -2245,12 +2246,20 @@ static const char *midend_deserialise_in
+ }
+
+ data.params = me->ourgame->default_params();
++ if (!data.parstr) {
++ ret = "Long-term parameters in save file are missing";
++ goto cleanup;
++ }
+ me->ourgame->decode_params(data.params, data.parstr);
+ if (me->ourgame->validate_params(data.params, true)) {
+ ret = "Long-term parameters in save file are invalid";
+ goto cleanup;
+ }
+ data.cparams = me->ourgame->default_params();
++ if (!data.cparstr) {
++ ret = "Short-term parameters in save file are missing";
++ goto cleanup;
++ }
+ me->ourgame->decode_params(data.cparams, data.cparstr);
+ if (me->ourgame->validate_params(data.cparams, false)) {
+ ret = "Short-term parameters in save file are invalid";
+@@ -2280,6 +2289,10 @@ static const char *midend_deserialise_in
+ ret = "Game position in save file is out of range";
+ }
+
++ if (!data.states) {
++ ret = "No state count provided in save file";
++ goto cleanup;
++ }
+ data.states[0].state = me->ourgame->new_game(
+ me, data.cparams, data.privdesc ? data.privdesc : data.desc);
+ for (i = 1; i < data.nstates; i++) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0115-Correct-and-enable-the-range-check-on-statepos-when-.patch sgt-puzzles-20191231.79a5378/debian/patches/0115-Correct-and-enable-the-range-check-on-statepos-when-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0115-Correct-and-enable-the-range-check-on-statepos-when-.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0115-Correct-and-enable-the-range-check-on-statepos-when-.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,35 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 16 Oct 2022 19:14:24 +0100
+Subject: [PATCH 115/389] Correct and enable the range check on statepos when
+ loading
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=1bab1d1d2ab472bb8fc7cddfce1d3c37e63a2ed5
+
+statepos == 0 shouldn't ever occur in a save file because it indicates
+an uninitialised midend. OTOH statepos == nstates is normal. Also
+added an equivalent assertion when saving because Simon and I spent
+some time discussing whether it could happen.
+---
+ midend.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -2031,6 +2031,7 @@ void midend_serialise(midend *me,
+ char buf[80];
+ sprintf(buf, "%d", me->nstates);
+ wr("NSTATES", buf);
++ assert(me->statepos >= 1 && me->statepos <= me->nstates);
+ sprintf(buf, "%d", me->statepos);
+ wr("STATEPOS", buf);
+ }
+@@ -2285,8 +2286,9 @@ static const char *midend_deserialise_in
+ ret = "Game private description in save file is invalid";
+ goto cleanup;
+ }
+- if (data.statepos < 0 || data.statepos >= data.nstates) {
++ if (data.statepos < 1 || data.statepos > data.nstates) {
+ ret = "Game position in save file is out of range";
++ goto cleanup;
+ }
+
+ if (!data.states) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0115-Galaxies-fix-recursion-depth-limit-in-solver.patch sgt-puzzles-20191231.79a5378/debian/patches/0115-Galaxies-fix-recursion-depth-limit-in-solver.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0115-Galaxies-fix-recursion-depth-limit-in-solver.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0115-Galaxies-fix-recursion-depth-limit-in-solver.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,89 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Sun, 12 Mar 2023 14:11:34 +0000
+Subject: [PATCH 115/159] Galaxies: fix recursion depth limit in solver.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=f018ef97d34b9126744e63cc2112c302fcae4ab4
+Bug-Debian: https://bugs.debian.org/1034190
+
+The static variable 'solver_recurse_depth' is _mostly_ used by the
+standalone solver, to appropriately indent the solver diagnostics for
+the current recursion level. So most uses of it are guarded by an
+'#ifdef STANDALONE_SOLVER' statement, or some equivalent (such as
+being inside the solvep() macro).
+
+One exception is the check that limits the recursion depth to 5, to
+avoid getting hung up forever on a too-hard game. Unfortunately, this
+check depends on the variable actually incrementing when we recurse
+another level - and it wasn't, because the increment itself was under
+ifdef! So the generator in live Galaxies could recurse arbitrarily
+deep, and generate puzzles that the standalone solver found too hard
+_even_ at Unreasonable mode.
+
+Removed the ifdefs, so that solver_recurse_depth is now incremented
+and decremented. Also, make sure to initialise the depth to 0 at the
+start of a solver run, just in case it had a bogus value left over
+from a previous run.
+---
+ galaxies.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/galaxies.c
++++ b/galaxies.c
+@@ -150,6 +150,7 @@ struct game_state {
+ };
+
+ static bool check_complete(const game_state *state, int *dsf, int *colours);
++static int solver_state_inner(game_state *state, int maxdiff);
+ static int solver_state(game_state *state, int maxdiff);
+ static int solver_obvious(game_state *state);
+ static int solver_obvious_dot(game_state *state, space *dot);
+@@ -2156,9 +2157,7 @@ static int solver_recurse(game_state *st
+ solver_recurse_depth*4, "",
+ rctx.best->x, rctx.best->y, rctx.bestn));
+
+-#ifdef STANDALONE_SOLVER
+ solver_recurse_depth++;
+-#endif
+
+ ingrid = snewn(gsz, space);
+ memcpy(ingrid, state->grid, gsz * sizeof(space));
+@@ -2173,7 +2172,7 @@ static int solver_recurse(game_state *st
+ state->dots[n]->x, state->dots[n]->y,
+ "Attempting for recursion");
+
+- ret = solver_state(state, maxdiff);
++ ret = solver_state_inner(state, maxdiff);
+
+ if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) {
+ /* we found our first solved grid; copy it away. */
+@@ -2205,9 +2204,7 @@ static int solver_recurse(game_state *st
+ break;
+ }
+
+-#ifdef STANDALONE_SOLVER
+ solver_recurse_depth--;
+-#endif
+
+ if (outgrid) {
+ /* we found (at least one) soln; copy it back to state */
+@@ -2218,7 +2215,7 @@ static int solver_recurse(game_state *st
+ return diff;
+ }
+
+-static int solver_state(game_state *state, int maxdiff)
++static int solver_state_inner(game_state *state, int maxdiff)
+ {
+ solver_ctx *sctx = new_solver(state);
+ int ret, diff = DIFF_NORMAL;
+@@ -2282,6 +2279,12 @@ got_result:
+ return diff;
+ }
+
++static int solver_state(game_state *state, int maxdiff)
++{
++ solver_recurse_depth = 0;
++ return solver_state_inner(state, maxdiff);
++}
++
+ #ifndef EDITOR
+ static char *solve_game(const game_state *state, const game_state *currstate,
+ const char *aux, const char **error)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0121-Add-an-assertion-to-check-the-format-of-encoded-para.patch sgt-puzzles-20191231.79a5378/debian/patches/0121-Add-an-assertion-to-check-the-format-of-encoded-para.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0121-Add-an-assertion-to-check-the-format-of-encoded-para.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0121-Add-an-assertion-to-check-the-format-of-encoded-para.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,134 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 15 Oct 2022 20:46:28 +0100
+Subject: [PATCH 121/389] Add an assertion to check the format of encoded
+ parameters
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=e29d8a3ecad734967cdcf2d4ce222ab27e9c524b
+
+Whenever the midend calls encode_params, it also checks that the
+result is a printable ASCII string that doesn't contain '#' or ':'.
+
+Parameter strings are embedded in save files, so they have to fit within
+ASCII. They can't contain '#' or ':' because those delimit the
+parameter section of a game ID. Nothing explicitly says they can't
+contain control characters, but those would be a particularly egregious
+violation of the recommendation that parameter strings be easy to type
+into a shell.
+---
+ midend.c | 36 +++++++++++++++++++++++++------------
+ 1 file changed, 24 insertions(+), 12 deletions(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -380,6 +380,18 @@ game_params *midend_get_params(midend *m
+ return me->ourgame->dup_params(me->params);
+ }
+
++static char *encode_params(midend *me, const game_params *params, bool full)
++{
++ char *encoded = me->ourgame->encode_params(params, full);
++
++ /* Assert that the params consist of printable ASCII containing
++ * neither '#' nor ':'. */
++ for (int i = 0; encoded[i]; i++)
++ assert(encoded[i] >= 32 && encoded[i] < 127 &&
++ encoded[i] != '#' && encoded[i] != ':');
++ return encoded;
++}
++
+ static void midend_set_timer(midend *me)
+ {
+ me->timing = (me->ourgame->is_timed &&
+@@ -613,8 +626,8 @@ static const char *newgame_undo_deserial
+ * We check both params and cparams, to be as safe as possible.
+ */
+
+- old = me->ourgame->encode_params(me->params, true);
+- new = me->ourgame->encode_params(data->params, true);
++ old = encode_params(me, me->params, true);
++ new = encode_params(me, data->params, true);
+ if (strcmp(old, new)) {
+ /* Set a flag to distinguish this deserialise failure
+ * from one due to faulty decoding */
+@@ -622,8 +635,8 @@ static const char *newgame_undo_deserial
+ return "Undoing this new-game operation would change params";
+ }
+
+- old = me->ourgame->encode_params(me->curparams, true);
+- new = me->ourgame->encode_params(data->cparams, true);
++ old = encode_params(me, me->curparams, true);
++ new = encode_params(me, data->cparams, true);
+ if (strcmp(old, new)) {
+ ctx->refused = true;
+ return "Undoing this new-game operation would change params";
+@@ -1359,7 +1372,7 @@ static void preset_menu_encode_params(mi
+ for (i = 0; i < menu->n_entries; i++) {
+ if (menu->entries[i].params) {
+ me->encoded_presets[menu->entries[i].id] =
+- me->ourgame->encode_params(menu->entries[i].params, true);
++ encode_params(me, menu->entries[i].params, true);
+ } else {
+ preset_menu_encode_params(me, menu->entries[i].submenu);
+ }
+@@ -1438,7 +1451,7 @@ struct preset_menu *midend_get_presets(m
+
+ int midend_which_preset(midend *me)
+ {
+- char *encoding = me->ourgame->encode_params(me->params, true);
++ char *encoding = encode_params(me, me->params, true);
+ int i, ret;
+
+ ret = -1;
+@@ -1516,7 +1529,7 @@ config_item *midend_get_config(midend *m
+ * the former is likely to persist across many code
+ * changes).
+ */
+- parstr = me->ourgame->encode_params(me->curparams, which == CFG_SEED);
++ parstr = encode_params(me, me->curparams, which == CFG_SEED);
+ assert(parstr);
+ if (which == CFG_DESC) {
+ rest = me->desc ? me->desc : "";
+@@ -1660,7 +1673,7 @@ static const char *midend_game_id_int(mi
+
+ newparams = me->ourgame->dup_params(me->params);
+
+- tmpstr = me->ourgame->encode_params(newcurparams, false);
++ tmpstr = encode_params(me, newcurparams, false);
+ me->ourgame->decode_params(newparams, tmpstr);
+
+ sfree(tmpstr);
+@@ -1732,7 +1745,7 @@ char *midend_get_game_id(midend *me)
+ {
+ char *parstr, *ret;
+
+- parstr = me->ourgame->encode_params(me->curparams, false);
++ parstr = encode_params(me, me->curparams, false);
+ assert(parstr);
+ assert(me->desc);
+ ret = snewn(strlen(parstr) + strlen(me->desc) + 2, char);
+@@ -1748,7 +1761,7 @@ char *midend_get_random_seed(midend *me)
+ if (!me->seedstr)
+ return NULL;
+
+- parstr = me->ourgame->encode_params(me->curparams, true);
++ parstr = encode_params(me, me->curparams, true);
+ assert(parstr);
+ ret = snewn(strlen(parstr) + strlen(me->seedstr) + 2, char);
+ sprintf(ret, "%s#%s", parstr, me->seedstr);
+@@ -1957,7 +1970,7 @@ void midend_serialise(midend *me,
+ * The current long-term parameters structure, in full.
+ */
+ if (me->params) {
+- char *s = me->ourgame->encode_params(me->params, true);
++ char *s = encode_params(me, me->params, true);
+ wr("PARAMS", s);
+ sfree(s);
+ }
+@@ -1966,7 +1979,7 @@ void midend_serialise(midend *me,
+ * The current short-term parameters structure, in full.
+ */
+ if (me->curparams) {
+- char *s = me->ourgame->encode_params(me->curparams, true);
++ char *s = encode_params(me, me->curparams, true);
+ wr("CPARAMS", s);
+ sfree(s);
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0122-Add-assertions-that-game-descriptions-consist-only-o.patch sgt-puzzles-20191231.79a5378/debian/patches/0122-Add-assertions-that-game-descriptions-consist-only-o.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0122-Add-assertions-that-game-descriptions-consist-only-o.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0122-Add-assertions-that-game-descriptions-consist-only-o.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,73 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 15 Oct 2022 21:25:08 +0100
+Subject: [PATCH 122/389] Add assertions that game descriptions consist only of
+ printable ASCII.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=9f2eef876275a451b015c22961130b2e507ddd49
+
+That they are ASCII is implied by their inclusion in save files.
+Nothing requires an absence of control characters, but it seems polite
+to make them slightly readable.
+---
+ midend.c | 16 ++++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/midend.c
++++ b/midend.c
+@@ -392,6 +392,14 @@ static char *encode_params(midend *me, c
+ return encoded;
+ }
+
++static void assert_printable_ascii(char const *s)
++{
++ /* Assert that s is entirely printable ASCII, and hence safe for
++ * writing in a save file. */
++ for (int i = 0; s[i]; i++)
++ assert(s[i] >= 32 && s[i] < 127);
++}
++
+ static void midend_set_timer(midend *me)
+ {
+ me->timing = (me->ourgame->is_timed &&
+@@ -499,6 +507,7 @@ void midend_new_game(midend *me)
+ */
+ me->desc = me->ourgame->new_desc(me->curparams, rs,
+ &me->aux_info, (me->drawing != NULL));
++ assert_printable_ascii(me->desc);
+ me->privdesc = NULL;
+ random_free(rs);
+ }
+@@ -921,6 +930,7 @@ static bool midend_really_process_key(mi
+ if (movestr == UI_UPDATE)
+ s = me->states[me->statepos-1].state;
+ else {
++ assert_printable_ascii(movestr);
+ s = me->ourgame->execute_move(me->states[me->statepos-1].state,
+ movestr);
+ assert(s != NULL);
+@@ -1479,6 +1489,10 @@ void midend_request_id_changes(midend *m
+ void midend_supersede_game_desc(midend *me, const char *desc,
+ const char *privdesc)
+ {
++ /* Assert that the descriptions consists only of printable ASCII. */
++ assert_printable_ascii(desc);
++ if (privdesc)
++ assert_printable_ascii(privdesc);
+ sfree(me->desc);
+ sfree(me->privdesc);
+ me->desc = dupstr(desc);
+@@ -1838,6 +1852,7 @@ const char *midend_solve(midend *me)
+ msg = "Solve operation failed"; /* _shouldn't_ happen, but can */
+ return msg;
+ }
++ assert_printable_ascii(movestr);
+ s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr);
+ assert(s);
+
+@@ -2021,6 +2036,7 @@ void midend_serialise(midend *me,
+ */
+ if (me->ui) {
+ char *s = me->ourgame->encode_ui(me->ui);
++ assert_printable_ascii(s);
+ if (s) {
+ wr("UI", s);
+ sfree(s);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0123-Hex-encode-non-ASCII-random-seeds-in-save-files.patch sgt-puzzles-20191231.79a5378/debian/patches/0123-Hex-encode-non-ASCII-random-seeds-in-save-files.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0123-Hex-encode-non-ASCII-random-seeds-in-save-files.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0123-Hex-encode-non-ASCII-random-seeds-in-save-files.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,67 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 17 Oct 2022 00:56:37 +0100
+Subject: [PATCH 123/389] Hex-encode non-ASCII random seeds in save files
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=304796f9f184a783d0af21e445c690ed69de048b
+
+The developer documentation claims that save files are long ASCII
+strings. This is mostly true, but there's nothing stopping a user
+from entering non-ASCII characters as random seeds. The ASCII
+property of save files is useful, so encode seeds in hex before
+writing them unless they consist only of printable ASCII characters.
+
+Hex-encoded seeds are written under a new key, HEXSEED, to distinguish
+them from unencoded seeds. This means that old versions of the code
+won't be able to load encoded seeds, but that's not a great loss:
+seeds aren't generally portable between versions anyway.
+---
+ midend.c | 32 ++++++++++++++++++++++++++++++--
+ 1 file changed, 30 insertions(+), 2 deletions(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -2001,8 +2001,27 @@ void midend_serialise(midend *me,
+ /*
+ * The current game description, the privdesc, and the random seed.
+ */
+- if (me->seedstr)
+- wr("SEED", me->seedstr);
++ if (me->seedstr) {
++ /*
++ * Random seeds are not necessarily printable ASCII.
++ * Hex-encode the seed if necessary. Printable ASCII seeds
++ * are emitted unencoded for compatibility with older
++ * versions.
++ */
++ int i;
++
++ for (i = 0; me->seedstr[i]; i++)
++ if (me->seedstr[i] < 32 || me->seedstr[i] >= 127)
++ break;
++ if (me->seedstr[i]) {
++ char *hexseed = bin2hex((unsigned char *)me->seedstr,
++ strlen(me->seedstr));
++
++ wr("HEXSEED", hexseed);
++ sfree(hexseed);
++ } else
++ wr("SEED", me->seedstr);
++ }
+ if (me->desc)
+ wr("DESC", me->desc);
+ if (me->privdesc)
+@@ -2204,6 +2223,15 @@ static const char *midend_deserialise_in
+ sfree(data.cparstr);
+ data.cparstr = val;
+ val = NULL;
++ } else if (!strcmp(key, "HEXSEED")) {
++ unsigned char *tmp;
++ int len = strlen(val) / 2; /* length in bytes */
++ tmp = hex2bin(val, len);
++ sfree(data.seed);
++ data.seed = snewn(len + 1, char);
++ memcpy(data.seed, tmp, len);
++ data.seed[len] = '\0';
++ sfree(tmp);
+ } else if (!strcmp(key, "SEED")) {
+ sfree(data.seed);
+ data.seed = val;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0124-Assert-that-everything-written-to-a-save-file-is-pri.patch sgt-puzzles-20191231.79a5378/debian/patches/0124-Assert-that-everything-written-to-a-save-file-is-pri.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0124-Assert-that-everything-written-to-a-save-file-is-pri.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0124-Assert-that-everything-written-to-a-save-file-is-pri.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 17 Oct 2022 00:59:18 +0100
+Subject: [PATCH 124/389] Assert that everything written to a save file is
+ printable ASCII
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=7f4d038258d2bd585a0dce87177bf429d168ffb5
+
+Apart from the newlines of course.
+---
+ midend.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -1959,7 +1959,9 @@ void midend_serialise(midend *me,
+ char lbuf[9]; \
+ copy_left_justified(lbuf, sizeof(lbuf), h); \
+ sprintf(hbuf, "%s:%d:", lbuf, (int)strlen(str)); \
++ assert_printable_ascii(hbuf); \
+ write(wctx, hbuf, strlen(hbuf)); \
++ assert_printable_ascii(str); \
+ write(wctx, str, strlen(str)); \
+ write(wctx, "\n", 1); \
+ } while (0)
+@@ -2055,7 +2057,6 @@ void midend_serialise(midend *me,
+ */
+ if (me->ui) {
+ char *s = me->ourgame->encode_ui(me->ui);
+- assert_printable_ascii(s);
+ if (s) {
+ wr("UI", s);
+ sfree(s);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0129-Build-fix-take-declarations-out-of-for-loops.patch sgt-puzzles-20191231.79a5378/debian/patches/0129-Build-fix-take-declarations-out-of-for-loops.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0129-Build-fix-take-declarations-out-of-for-loops.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0129-Build-fix-take-declarations-out-of-for-loops.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,35 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Fri, 21 Oct 2022 07:52:14 +0100
+Subject: [PATCH 129/389] Build fix: take declarations out of for loops.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=373dadacc06210197e3d68c55a6d611126915120
+
+The NestedVM build is still unhappy with this C99ism, unfortunately.
+---
+ midend.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -383,10 +383,11 @@ game_params *midend_get_params(midend *m
+ static char *encode_params(midend *me, const game_params *params, bool full)
+ {
+ char *encoded = me->ourgame->encode_params(params, full);
++ int i;
+
+ /* Assert that the params consist of printable ASCII containing
+ * neither '#' nor ':'. */
+- for (int i = 0; encoded[i]; i++)
++ for (i = 0; encoded[i]; i++)
+ assert(encoded[i] >= 32 && encoded[i] < 127 &&
+ encoded[i] != '#' && encoded[i] != ':');
+ return encoded;
+@@ -396,7 +397,8 @@ static void assert_printable_ascii(char
+ {
+ /* Assert that s is entirely printable ASCII, and hence safe for
+ * writing in a save file. */
+- for (int i = 0; s[i]; i++)
++ int i;
++ for (i = 0; s[i]; i++)
+ assert(s[i] >= 32 && s[i] < 127);
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0138-Correct-a-range-check-in-Magnets-layout-verification.patch sgt-puzzles-20191231.79a5378/debian/patches/0138-Correct-a-range-check-in-Magnets-layout-verification.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0138-Correct-a-range-check-in-Magnets-layout-verification.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0138-Correct-a-range-check-in-Magnets-layout-verification.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,24 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Fri, 31 Mar 2023 20:38:31 +0100
+Subject: [PATCH 138/159] Correct a range check in Magnets' layout verification
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=91735e5019be84d2fa693c5d40746c818ace28f8
+Bug-Debian: https://bugs.debian.org/1034190
+
+Squares in the grid are numbered from 0, so the upper limit check
+needs to use "<=" rather than "<". Without this, invalid descriptions
+can cause a read overrun off the end of the board.
+---
+ magnets.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/magnets.c
++++ b/magnets.c
+@@ -520,7 +520,7 @@ nextchar:
+ * (i.e. each end points to the other) */
+ for (idx = 0; idx < state->wh; idx++) {
+ if (state->common->dominoes[idx] < 0 ||
+- state->common->dominoes[idx] > state->wh ||
++ state->common->dominoes[idx] >= state->wh ||
+ state->common->dominoes[state->common->dominoes[idx]] != idx) {
+ *prob = "Domino descriptions inconsistent";
+ goto done;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0139-Magnets-add-a-check-that-magnets-don-t-wrap-between-.patch sgt-puzzles-20191231.79a5378/debian/patches/0139-Magnets-add-a-check-that-magnets-don-t-wrap-between-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0139-Magnets-add-a-check-that-magnets-don-t-wrap-between-.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0139-Magnets-add-a-check-that-magnets-don-t-wrap-between-.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 1 Apr 2023 18:54:29 +0100
+Subject: [PATCH 139/159] Magnets: add a check that magnets don't wrap between
+ lines
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=0bd1a8057841386754f9f4a8a268616c7ce80e80
+Bug-Debian: https://bugs.debian.org/1034190
+
+There was nothing in Magnet's description validation to prevent there
+being the left end of a magnet at the right end of a row and the right
+end of a magnet at the left end of the row below. Indeed as far as I
+can such a game (e.g. 3x3:..2,2..,...,1.1,TLRB*LRLR) plays entirely
+correctly except that one magnet is discontinuous.
+
+While this worked, it was entirely an artefact of the particular memory
+layout that Magnets uses and shouldn't have been allowed, so I've added
+an additional validation rule to stop it.
+---
+ magnets.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/magnets.c
++++ b/magnets.c
+@@ -521,6 +521,8 @@ nextchar:
+ for (idx = 0; idx < state->wh; idx++) {
+ if (state->common->dominoes[idx] < 0 ||
+ state->common->dominoes[idx] >= state->wh ||
++ (state->common->dominoes[idx] % state->w != idx % state->w &&
++ state->common->dominoes[idx] / state->w != idx / state->w) ||
+ state->common->dominoes[state->common->dominoes[idx]] != idx) {
+ *prob = "Domino descriptions inconsistent";
+ goto done;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0155-Net-assert-that-cx-and-cy-are-in-range-in-compute_ac.patch sgt-puzzles-20191231.79a5378/debian/patches/0155-Net-assert-that-cx-and-cy-are-in-range-in-compute_ac.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0155-Net-assert-that-cx-and-cy-are-in-range-in-compute_ac.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0155-Net-assert-that-cx-and-cy-are-in-range-in-compute_ac.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,38 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 13 Feb 2023 22:14:26 +0000
+Subject: [PATCH 155/159] Net: assert that cx and cy are in range in
+ compute_active()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=e411db788cfc0d0ed54b3c9b9deb15edba7d237a
+Bug-Debian: https://bugs.debian.org/1034190
+
+This avoids an out-of-range heap write shortly afterwards. An assertion
+failure is better than a buffer overrun, but still not ideal. Fixing
+the problem properly will require fairly wide-ranging changes, though.
+
+The bug can be demonstrated by loading this save file into a build with
+AddressSanitizer:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :3:Net
+PARAMS :4:5x5w
+CPARAMS :4:5x5w
+DESC :25:9893e85285bb72e6de5182741
+UI :9:O0,0;C6,6
+NSTATES :1:1
+STATEPOS:1:1
+---
+ net.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/net.c
++++ b/net.c
+@@ -1878,6 +1878,8 @@ static unsigned char *compute_active(con
+ active = snewn(state->width * state->height, unsigned char);
+ memset(active, 0, state->width * state->height);
+
++ assert(0 <= cx && cx < state->width);
++ assert(0 <= cy && cy < state->height);
+ /*
+ * We only store (x,y) pairs in todo, but it's easier to reuse
+ * xyd_cmp and just store direction 0 every time.
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0159-Don-t-allow-zero-clues-in-Pattern.patch sgt-puzzles-20191231.79a5378/debian/patches/0159-Don-t-allow-zero-clues-in-Pattern.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0159-Don-t-allow-zero-clues-in-Pattern.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0159-Don-t-allow-zero-clues-in-Pattern.patch 2023-04-23 21:12:52.000000000 +0200
@@ -0,0 +1,34 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 14 Feb 2023 13:16:53 +0000
+Subject: [PATCH 159/159] Don't allow zero clues in Pattern
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=71cf891fdc3ab237ecf0e5d1aae39b6c9fe97a4d
+Bug-Debian: https://bugs.debian.org/1034190
+
+Some nonogram implementations allow zero clues so that a row or column
+with a single zero clue is equivalent to one with no clues, that is it
+has no black squares in it. Pattern, however, doesn't interpret them
+like this and treats a puzzle with a zero clue as insoluble, so it's
+not helpful to permit them.
+
+Permitting zero clues also confuses Pattern's memory allocation so
+that it can suffer a buffer overrun. As an example, before this
+commit a build with AddressSanitizer would report a buffer overrun
+with the description "1:0/0.0" because it tries to put two clues in a
+row that can have a maximum of one.
+---
+ pattern.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/pattern.c
++++ b/pattern.c
+@@ -912,8 +912,8 @@ static const char *validate_desc(const g
+ p = desc;
+ while (*desc && isdigit((unsigned char)*desc)) desc++;
+ n = atoi(p);
+- if (n < 0)
+- return "at least one clue is negative";
++ if (n <= 0)
++ return "all clues must be positive";
+ if (n > INT_MAX - 1)
+ return "at least one clue is grossly excessive";
+ rowspace -= n+1;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0246-galaxies-Use-the-same-code-for-handling-all-dropped-.patch sgt-puzzles-20191231.79a5378/debian/patches/0246-galaxies-Use-the-same-code-for-handling-all-dropped-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0246-galaxies-Use-the-same-code-for-handling-all-dropped-.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0246-galaxies-Use-the-same-code-for-handling-all-dropped-.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,63 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 7 Dec 2022 18:43:34 +0000
+Subject: [PATCH 246/389] galaxies: Use the same code for handling all dropped
+ arrows
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=0b036c9e79578cec5425aaca1b1af13a4ae0d937
+
+The keyboard code was prone to adding null items to the undo history,
+and was also unreadable. Rather than fix it, I've replaced it with a
+jump to the mouse drop handling, lightly enhanced to reject drops on
+things that aren't tiles.
+---
+ galaxies.c | 20 ++++++--------------
+ 1 file changed, 6 insertions(+), 14 deletions(-)
+
+--- a/galaxies.c
++++ b/galaxies.c
+@@ -352,6 +352,8 @@ static bool ok_to_add_assoc_with_opposit
+ int *colors;
+ bool toret;
+
++ if (tile->type != s_tile)
++ return false;
+ if (tile->flags & F_DOT)
+ return false;
+ if (opposite == NULL)
+@@ -2583,14 +2585,14 @@ static char *interpret_move(const game_s
+ ui->dy = y;
+ return UI_UPDATE;
+ } else if (button == RIGHT_RELEASE && ui->dragging) {
+- ui->dragging = false;
+-
+ /*
+ * Drags are always targeted at a single square.
+ */
+ px = 2*FROMCOORD(x+TILE_SIZE) - 1;
+ py = 2*FROMCOORD(y+TILE_SIZE) - 1;
+
++ dropped: /* We arrive here from the end of a keyboard drag. */
++ ui->dragging = false;
+ /*
+ * Dragging an arrow on to the same square it started from
+ * is a null move; just update the ui and finish.
+@@ -2648,18 +2650,8 @@ static char *interpret_move(const game_s
+ }
+ sp = &SPACE(state, ui->cur_x, ui->cur_y);
+ if (ui->dragging) {
+- ui->dragging = false;
+-
+- if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) &&
+- SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) {
+- sprintf(buf, "%sU%d,%d", sep, ui->srcx, ui->srcy);
+- sep = ";";
+- }
+- if (sp->type == s_tile && !(sp->flags & F_DOT) && !(sp->flags & F_TILE_ASSOC)) {
+- sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
+- sep, ui->cur_x, ui->cur_y, ui->dotx, ui->doty);
+- }
+- return dupstr(buf);
++ px = ui->cur_x; py = ui->cur_y;
++ goto dropped;
+ } else if (sp->flags & F_DOT) {
+ ui->dragging = true;
+ ui->dx = SCOORD(ui->cur_x);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0263-magnets-Area-constraints-fix-message.patch sgt-puzzles-20191231.79a5378/debian/patches/0263-magnets-Area-constraints-fix-message.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0263-magnets-Area-constraints-fix-message.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0263-magnets-Area-constraints-fix-message.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,37 @@
+From: Chris Boyle <chris@boyle.name>
+Date: Tue, 20 Dec 2016 09:13:20 +0000
+Subject: [PATCH 263/389] magnets: Area constraints; fix message.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=69e63a810e0bd6f85a46d8490c6e366237e061ef
+
+(The restriction on 2x2 puzzles is because the board layer-out doesn't
+use neutral pieces on such small boards, and the only soluble 2x2 boards
+have neutral pieces. I haven't investigated the Tricky size limit, but
+it seems entirely reasonable that all the smaller boards are too easy.
+--bjh21)
+
+(cherry picked from Android port, commit
+133794977a13767e0c1596be6a5b26f2cf2d1fd1)
+---
+ magnets.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/magnets.c
++++ b/magnets.c
+@@ -230,8 +230,15 @@ static game_params *custom_params(const
+
+ static const char *validate_params(const game_params *params, bool full)
+ {
+- if (params->w < 2) return "Width must be at least one";
+- if (params->h < 2) return "Height must be at least one";
++ if (params->w < 2) return "Width must be at least two";
++ if (params->h < 2) return "Height must be at least two";
++ if (params->diff >= DIFF_TRICKY) {
++ if (params->w < 5 && params->h < 5)
++ return "Either width or height must be at least five for Tricky";
++ } else {
++ if (params->w < 3 && params->h < 3)
++ return "Either width or height must be at least three";
++ }
+ if (params->diff < 0 || params->diff >= DIFFCOUNT)
+ return "Unknown difficulty level";
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0266-lightup-Ban-2x2-with-either-4-way-type.patch sgt-puzzles-20191231.79a5378/debian/patches/0266-lightup-Ban-2x2-with-either-4-way-type.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0266-lightup-Ban-2x2-with-either-4-way-type.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0266-lightup-Ban-2x2-with-either-4-way-type.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,25 @@
+From: Chris Boyle <chris@boyle.name>
+Date: Tue, 20 Dec 2016 23:48:01 +0000
+Subject: [PATCH 266/389] lightup: Ban 2x2 with either 4-way type
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=cbf2ede64ad91346e6399603ff7b8df432be5a06
+
+(2x2 with four-way symmetry must be either all-black (trivial) or
+all-white (ambiguous). --bjh21)
+
+(cherry picked from Android port, commit
+27ae898e118b0a31a98d393bf56aa138845123e6)
+---
+ lightup.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/lightup.c
++++ b/lightup.c
+@@ -353,6 +353,8 @@ static const char *validate_params(const
+ if (params->symm == SYMM_ROT4)
+ return "4-fold symmetry is only available with square grids";
+ }
++ if ((params->symm == SYMM_ROT4 || params->symm == SYMM_REF4) && params->w < 3 && params->h < 3)
++ return _("Width or height must be at least 3 for 4-way symmetry");
+ if (params->symm < 0 || params->symm >= SYMM_MAX)
+ return "Unknown symmetry type";
+ if (params->difficulty < 0 || params->difficulty > DIFFCOUNT)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0267-Remove-_-introduced-from-Android-port.patch sgt-puzzles-20191231.79a5378/debian/patches/0267-Remove-_-introduced-from-Android-port.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0267-Remove-_-introduced-from-Android-port.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0267-Remove-_-introduced-from-Android-port.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,22 @@
+From: Jacob Nevins <jacobn@chiark.greenend.org.uk>
+Date: Fri, 16 Dec 2022 11:17:29 +0000
+Subject: [PATCH 267/389] Remove _() introduced from Android port.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=0d43753ff2e02f54dd35e6872be3dafa14d2ea7d
+
+Introduced in cbf2ede64a. It's used there for marking up text for i18n
+in a gettext stylee, but is not available here.
+---
+ lightup.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/lightup.c
++++ b/lightup.c
+@@ -354,7 +354,7 @@ static const char *validate_params(const
+ return "4-fold symmetry is only available with square grids";
+ }
+ if ((params->symm == SYMM_ROT4 || params->symm == SYMM_REF4) && params->w < 3 && params->h < 3)
+- return _("Width or height must be at least 3 for 4-way symmetry");
++ return "Width or height must be at least 3 for 4-way symmetry";
+ if (params->symm < 0 || params->symm >= SYMM_MAX)
+ return "Unknown symmetry type";
+ if (params->difficulty < 0 || params->difficulty > DIFFCOUNT)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0269-Solo-Set-max-difficulty-for-small-jigsaw-puzzles.patch sgt-puzzles-20191231.79a5378/debian/patches/0269-Solo-Set-max-difficulty-for-small-jigsaw-puzzles.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0269-Solo-Set-max-difficulty-for-small-jigsaw-puzzles.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0269-Solo-Set-max-difficulty-for-small-jigsaw-puzzles.patch 2023-04-23 21:12:44.000000000 +0200
@@ -0,0 +1,26 @@
+From: Michael Quevillon <mquevill@nd.edu>
+Date: Sun, 3 Apr 2022 13:02:30 -0500
+Subject: [PATCH 269/389] Solo: Set max difficulty for small jigsaw puzzles
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=11c1447eac7698f01c2a00764c2b1a900a9d6a90
+
+(cherry picked from Android port, commit
+5c9a7b64a06d07f97a41622c4b91d81f3419a51b)
+---
+ solo.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/solo.c
++++ b/solo.c
+@@ -3654,10 +3654,11 @@ static char *new_game_desc(const game_pa
+ * the puzzle size: all 2x2 puzzles appear to be Trivial
+ * (DIFF_BLOCK) so we cannot hold out for even a Basic
+ * (DIFF_SIMPLE) one.
++ * Jigsaw puzzles of size 2 and 3 are also all trivial.
+ */
+ dlev.maxdiff = params->diff;
+ dlev.maxkdiff = params->kdiff;
+- if (c == 2 && r == 2)
++ if ((c == 2 && r == 2) || (r == 1 && c < 4))
+ dlev.maxdiff = DIFF_BLOCK;
+
+ grid = snewn(area, digit);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0285-Add-a-macro-of-an-upper-bound-on-the-formatted-lengt.patch sgt-puzzles-20191231.79a5378/debian/patches/0285-Add-a-macro-of-an-upper-bound-on-the-formatted-lengt.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0285-Add-a-macro-of-an-upper-bound-on-the-formatted-lengt.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0285-Add-a-macro-of-an-upper-bound-on-the-formatted-lengt.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,50 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 2 Jan 2023 16:48:20 +0000
+Subject: [PATCH 285/389] Add a macro of an upper bound on the formatted length
+ of an integer
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=d246077e78bb1aeafe8829927db23f281cd03c72
+Bug-Debian: https://bugs.debian.org/1028986
+
+There are lots of places where Puzzles formats integers into
+fixed-length buffers using sprintf() with a "%d" format. This isn't
+very safe, since C doesn't guarantee any particular maximum size for an
+"int". However, the restrictions on representations of integers means
+we can infer an upper bound using sizeof(), CHAR_BIT, and an
+approximation to the binary log of 10.
+---
+ devel.but | 11 +++++++++++
+ puzzles.h | 3 +++
+ 2 files changed, 14 insertions(+)
+
+--- a/devel.but
++++ b/devel.but
+@@ -4155,6 +4155,17 @@ returns the one which compares greater o
+ These macros may evaluate their arguments multiple times. Avoid side
+ effects.
+
++\S{utils-max-digits} \cw{MAX_DIGITS()}
++
++The \cw{MAX_DIGITS()} macro, defined in the main Puzzles header file,
++takes a type (or a variable of that type) and expands to an integer
++constant representing a reasonable upper bound on the number of
++characters that a number of that type could expand to when formatted
++as a decimal number using the \c{%u} or \c{%d} format of
++\cw{printf()}. This is useful for allocating a fixed-size buffer
++that's guaranteed to be big enough to \cw{sprintf()} a value into.
++Don't forget to add one for the trailing \cw{'\\0'}!
++
+ \S{utils-pi} \cw{PI}
+
+ The main Puzzles header file defines a macro \cw{PI} which expands
+--- a/puzzles.h
++++ b/puzzles.h
+@@ -18,6 +18,9 @@
+ #define STR_INT(x) #x
+ #define STR(x) STR_INT(x)
+
++/* An upper bound on the length of sprintf'ed integers (signed or unsigned). */
++#define MAX_DIGITS(x) (sizeof(x) * CHAR_BIT / 3 + 2)
++
+ /* NB not perfect because they evaluate arguments multiple times. */
+ #ifndef max
+ #define max(x,y) ( (x)>(y) ? (x) : (y) )
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0299-Guess-Don-t-allow-any-moves-once-the-game-is-solved.patch sgt-puzzles-20191231.79a5378/debian/patches/0299-Guess-Don-t-allow-any-moves-once-the-game-is-solved.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0299-Guess-Don-t-allow-any-moves-once-the-game-is-solved.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0299-Guess-Don-t-allow-any-moves-once-the-game-is-solved.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,40 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 19:45:03 +0000
+Subject: [PATCH 299/389] Guess: Don't allow any moves once the game is solved
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=c84af670b52f09e9e47587584c0559c508d4a37d
+Bug-Debian: https://bugs.debian.org/1028986
+
+If the game is solved (either by a win or a loss), interpret_move()
+can never return a move, but execute_move() should also reject any
+moves in case we're loading a corrupt or malicious save file.
+Otherwise a save file with more guesses than the maximum allowed can
+cause a buffer overrun.
+
+This save file demonstrates the problem when loaded into a build of
+Puzzles with AddressSanitizer:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :5:Guess
+PARAMS :9:c6p4g1Bm
+CPARAMS :9:c6p4g1Bm
+DESC :8:b5f3faed
+NSTATES :1:3
+STATEPOS:1:3
+MOVE :8:G1,1,2,2
+MOVE :8:G4,3,1,1
+---
+ guess.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/guess.c
++++ b/guess.c
+@@ -920,6 +920,8 @@ static game_state *execute_move(const ga
+ game_state *ret;
+ const char *p;
+
++ /* No moves are allowed once the game is solved. */
++ if (from->solved) return NULL;
+ if (!strcmp(move, "S")) {
+ ret = dup_game(from);
+ ret->solved = -1;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0300-Guess-validate-peg-colours-in-decode_ui.patch sgt-puzzles-20191231.79a5378/debian/patches/0300-Guess-validate-peg-colours-in-decode_ui.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0300-Guess-validate-peg-colours-in-decode_ui.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0300-Guess-validate-peg-colours-in-decode_ui.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,50 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 20:28:23 +0000
+Subject: [PATCH 300/389] Guess: validate peg colours in decode_ui()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=5279fd24b2f4a51e760bfde873fe1d29547220a6
+Bug-Debian: https://bugs.debian.org/1028986
+
+Peg colours in the current guess must be within the range of colours
+for the current game, just as they must be for completed moves.
+Otherwise is_markable() can cause a buffer overrun.
+
+Since there's no way for decode_ui() to report an error, it just ignores
+the bad peg colours. I've also added an assertion to catch this problem
+in is_markable().
+
+The following save file demonstrates the problem when loaded in a build
+with AddressSanitizer:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :5:Guess
+PARAMS :9:c6p4g10Bm
+CPARAMS :9:c6p4g10Bm
+DESC :8:2de917c0
+UI :7:7,7,7,7
+NSTATES :1:1
+STATEPOS:1:1
+---
+ guess.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/guess.c
++++ b/guess.c
+@@ -386,6 +386,7 @@ static bool is_markable(const game_param
+ for (i = 0; i < params->npegs; i++) {
+ int c = pegs->pegs[i];
+ if (c > 0) {
++ assert(c <= params->ncolours);
+ colcount->pegs[c-1]++;
+ nset++;
+ }
+@@ -468,6 +469,9 @@ static void decode_ui(game_ui *ui, const
+ const char *p = encoding;
+ for (i = 0; i < ui->curr_pegs->npegs; i++) {
+ ui->curr_pegs->pegs[i] = atoi(p);
++ if (ui->curr_pegs->pegs[i] < 0 ||
++ ui->curr_pegs->pegs[i] > ui->params.ncolours)
++ ui->curr_pegs->pegs[i] = 0; /* Remove invalid pegs. */
+ while (*p && isdigit((unsigned char)*p)) p++;
+ if (*p == '_') {
+ /* NB: old versions didn't store holds */
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0302-Netslide-Reject-moves-wider-than-the-grid.patch sgt-puzzles-20191231.79a5378/debian/patches/0302-Netslide-Reject-moves-wider-than-the-grid.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0302-Netslide-Reject-moves-wider-than-the-grid.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0302-Netslide-Reject-moves-wider-than-the-grid.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,60 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 22:05:33 +0000
+Subject: [PATCH 302/389] Netslide: Reject moves wider than the grid
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=1aded127eb3fb7194a1752d96bfba95a5b7fa4dc
+Bug-Debian: https://bugs.debian.org/1028986
+
+Also add a corresponding assertion to the underlying move primitive.
+Without this limit, long moves cause a buffer overrun.
+
+To demonstrate the problem, build Netslide with AddressSanitizer and
+load this save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :8:Netslide
+PARAMS :3:4x4
+CPARAMS :3:4x4
+DESC :16:49b59aca247714b4
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :5:R3,51
+---
+ netslide.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+--- a/netslide.c
++++ b/netslide.c
+@@ -1004,7 +1004,9 @@ static void slide_row_int(int w, int h,
+ int x = dir > 0 ? -1 : w;
+ int tx = x + dir;
+ int n = w - 1;
+- unsigned char endtile = tiles[row * w + tx];
++ unsigned char endtile;
++ assert(0 <= tx && tx < w);
++ endtile = tiles[row * w + tx];
+ do {
+ x = tx;
+ tx = (x + dir + w) % w;
+@@ -1018,7 +1020,9 @@ static void slide_col_int(int w, int h,
+ int y = dir > 0 ? -1 : h;
+ int ty = y + dir;
+ int n = h - 1;
+- unsigned char endtile = tiles[ty * w + col];
++ unsigned char endtile;
++ assert(0 <= ty && ty < h);
++ endtile = tiles[ty * w + col];
+ do {
+ y = ty;
+ ty = (y + dir + h) % h;
+@@ -1131,7 +1135,9 @@ static game_state *execute_move(const ga
+
+ if ((move[0] == 'C' || move[0] == 'R') &&
+ sscanf(move+1, "%d,%d", &c, &d) == 2 &&
+- c >= 0 && c < (move[0] == 'C' ? from->width : from->height)) {
++ c >= 0 && c < (move[0] == 'C' ? from->width : from->height) &&
++ d <= (move[0] == 'C' ? from->width : from->height) &&
++ d >= -(move[0] == 'C' ? from->width : from->height) && d != 0) {
+ col = (move[0] == 'C');
+ } else if (move[0] == 'S' &&
+ strlen(move) == from->width * from->height + 1) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0303-Sixteen-limit-length-of-moves.patch sgt-puzzles-20191231.79a5378/debian/patches/0303-Sixteen-limit-length-of-moves.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0303-Sixteen-limit-length-of-moves.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0303-Sixteen-limit-length-of-moves.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,44 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 23:06:13 +0000
+Subject: [PATCH 303/389] Sixteen: limit length of moves
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=023ce7554c19dcf6f4432407b9eedb850acc7289
+Bug-Debian: https://bugs.debian.org/1028986
+
+The code that actually executes the moves can only cope with moves of
+at most the width (or height as appropriate) of the grid. Reject any
+longer move, and for symmetry also negative moves of the same
+magnitude.
+
+Without this, the tile-moving code tends to access off the start of the
+tile array. To demonstrate this, build Sixteen with AddressSanitizer
+and load this save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :7:Sixteen
+PARAMS :3:4x4
+CPARAMS :3:4x4
+DESC :38:2,16,3,10,13,8,7,4,9,14,12,11,15,1,5,6
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :4:C1,9
+---
+ sixteen.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/sixteen.c
++++ b/sixteen.c
+@@ -747,11 +747,11 @@ static game_state *execute_move(const ga
+ }
+
+ if (move[0] == 'R' && sscanf(move+1, "%d,%d", &cy, &dx) == 2 &&
+- cy >= 0 && cy < from->h) {
++ cy >= 0 && cy < from->h && -from->h <= dx && dx <= from->w ) {
+ cx = dy = 0;
+ n = from->w;
+ } else if (move[0] == 'C' && sscanf(move+1, "%d,%d", &cx, &dy) == 2 &&
+- cx >= 0 && cx < from->w) {
++ cx >= 0 && cx < from->w && -from->h <= dy && dy <= from->h) {
+ cy = dx = 0;
+ n = from->h;
+ } else
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0304-Undead-check-for-valid-commands-in-execute_move.patch sgt-puzzles-20191231.79a5378/debian/patches/0304-Undead-check-for-valid-commands-in-execute_move.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0304-Undead-check-for-valid-commands-in-execute_move.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0304-Undead-check-for-valid-commands-in-execute_move.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,55 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 23:31:25 +0000
+Subject: [PATCH 304/389] Undead: check for valid commands in execute_move()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=a02c55b0492453ea7ca4e4ae63cb90ba4c93a3a5
+Bug-Debian: https://bugs.debian.org/1028986
+
+Previously, Undead's execute_move would go into a spin when it
+encountered an unexpected command character in a move string. Now it
+rejects the move instead.
+---
+ undead.c | 17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+--- a/undead.c
++++ b/undead.c
+@@ -2056,9 +2056,8 @@ static game_state *execute_move(const ga
+ if (c == 'S') {
+ move++;
+ solver = true;
+- }
+- if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' ||
+- c == 'g' || c == 'v' || c == 'z') {
++ } else if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' ||
++ c == 'g' || c == 'v' || c == 'z') {
+ move++;
+ sscanf(move, "%d%n", &x, &n);
+ if (c == 'G') ret->guess[x] = 1;
+@@ -2069,13 +2068,11 @@ static game_state *execute_move(const ga
+ if (c == 'v') ret->pencils[x] ^= 2;
+ if (c == 'z') ret->pencils[x] ^= 4;
+ move += n;
+- }
+- if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 &&
+- is_clue(ret, x, y)) {
++ } else if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 &&
++ is_clue(ret, x, y)) {
+ ret->hints_done[clue_index(ret, x, y)] ^= 1;
+ move += n + 1;
+- }
+- if (c == 'M') {
++ } else if (c == 'M') {
+ /*
+ * Fill in absolutely all pencil marks in unfilled
+ * squares, for those who like to play by the rigorous
+@@ -2086,6 +2083,10 @@ static game_state *execute_move(const ga
+ if (ret->guess[i] == 7)
+ ret->pencils[i] = 7;
+ move++;
++ } else {
++ /* Unknown move type. */
++ free_game(ret);
++ return NULL;
+ }
+ if (*move == ';') move++;
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0305-Undead-fix-buffer-overrun-in-M-command.patch sgt-puzzles-20191231.79a5378/debian/patches/0305-Undead-fix-buffer-overrun-in-M-command.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0305-Undead-fix-buffer-overrun-in-M-command.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0305-Undead-fix-buffer-overrun-in-M-command.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,27 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 23:44:31 +0000
+Subject: [PATCH 305/389] Undead: fix buffer overrun in "M" command
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=952ef8ca565d803da1134466358bd85683a489a3
+Bug-Debian: https://bugs.debian.org/1028986
+
+The guessable squares are numbered up to num_total, not "wh". The
+latter includes mirror squares that aren't included in the various
+arrays describing the game state.
+
+To reproduce the problem, build Undead with AddressSanitizer and press
+"M".
+---
+ undead.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/undead.c
++++ b/undead.c
+@@ -2079,7 +2079,7 @@ static game_state *execute_move(const ga
+ * approach of starting off in that state and eliminating
+ * things.
+ */
+- for (i = 0; i < ret->common->wh; i++)
++ for (i = 0; i < ret->common->num_total; i++)
+ if (ret->guess[i] == 7)
+ ret->pencils[i] = 7;
+ move++;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0306-Correct-RANGECHECK-macro-in-Black-Box.patch sgt-puzzles-20191231.79a5378/debian/patches/0306-Correct-RANGECHECK-macro-in-Black-Box.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0306-Correct-RANGECHECK-macro-in-Black-Box.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0306-Correct-RANGECHECK-macro-in-Black-Box.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,23 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 8 Jan 2023 10:05:12 +0000
+Subject: [PATCH 306/389] Correct RANGECHECK macro in Black Box
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=4845f3e913a02417fe7a8d84c6407d40807ec0ec
+Bug-Debian: https://bugs.debian.org/1028986
+
+Lasers are numbered from 0 to nlasers-1 inclusive, so the upper limit
+should be "<", not "<=".
+---
+ blackbox.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/blackbox.c
++++ b/blackbox.c
+@@ -305,7 +305,7 @@ struct game_state {
+
+ #define GRID(s,x,y) ((s)->grid[(y)*((s)->w+2) + (x)])
+
+-#define RANGECHECK(s,x) ((x) >= 0 && (x) <= (s)->nlasers)
++#define RANGECHECK(s,x) ((x) >= 0 && (x) < (s)->nlasers)
+
+ /* specify numbers because they must match array indexes. */
+ enum { DIR_UP = 0, DIR_RIGHT = 1, DIR_DOWN = 2, DIR_LEFT = 3 };
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0307-Range-check-normal-moves-in-Undead.patch sgt-puzzles-20191231.79a5378/debian/patches/0307-Range-check-normal-moves-in-Undead.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0307-Range-check-normal-moves-in-Undead.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0307-Range-check-normal-moves-in-Undead.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,41 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 8 Jan 2023 10:20:26 +0000
+Subject: [PATCH 307/389] Range-check normal moves in Undead
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=942d883d9bf86f4240dc7ec22b726d64f6db9af2
+Bug-Debian: https://bugs.debian.org/1028986
+
+Normal moves shouldn't be allowed to write outside the board. This
+buffer overrun can be demonstrated by building Undead with
+AddressSanitizer and loading this save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :6:Undead
+PARAMS :5:4x4dn
+CPARAMS :5:4x4dn
+DESC :48:5,0,5,cRRaLRcLRc,0,2,1,3,1,0,0,3,4,3,2,3,4,2,1,1
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :3:Z10
+---
+ undead.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/undead.c
++++ b/undead.c
+@@ -2060,6 +2060,7 @@ static game_state *execute_move(const ga
+ c == 'g' || c == 'v' || c == 'z') {
+ move++;
+ sscanf(move, "%d%n", &x, &n);
++ if (x < 0 || x >= ret->common->num_total) goto badmove;
+ if (c == 'G') ret->guess[x] = 1;
+ if (c == 'V') ret->guess[x] = 2;
+ if (c == 'Z') ret->guess[x] = 4;
+@@ -2085,6 +2086,7 @@ static game_state *execute_move(const ga
+ move++;
+ } else {
+ /* Unknown move type. */
++ badmove:
+ free_game(ret);
+ return NULL;
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0308-Range-check-record-lengths-when-deserialising-games.patch sgt-puzzles-20191231.79a5378/debian/patches/0308-Range-check-record-lengths-when-deserialising-games.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0308-Range-check-record-lengths-when-deserialising-games.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0308-Range-check-record-lengths-when-deserialising-games.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,42 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 8 Jan 2023 11:31:36 +0000
+Subject: [PATCH 308/389] Range-check record lengths when deserialising games
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=e5717d1ba2184eb6e38b4e2a9d29dc4704aeef30
+Bug-Debian: https://bugs.debian.org/1028986
+
+"1999999999999999999999999999999999999999999999999999" as a record
+length should lead to an error, not a buffer overrun.
+
+(fun fact that was less obvious to me than it should have been: very
+large powers of ten are multiples of large powers of two, so that number
+is -1 mod 2^32)
+
+This bug can be demonstrated by building any puzzle with
+AddressSanitizer and then loading this save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1999999999999999999999999999999999999999999999999999:1
+---
+ midend.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -2181,7 +2181,7 @@ static const char *midend_deserialise_in
+
+ if (c == ':') {
+ break;
+- } else if (c >= '0' && c <= '9') {
++ } else if (c >= '0' && c <= '9' && len < (INT_MAX - 10) / 10) {
+ len = (len * 10) + (c - '0');
+ } else {
+ if (started)
+@@ -2573,7 +2573,7 @@ const char *identify_game(char **name,
+
+ if (c == ':') {
+ break;
+- } else if (c >= '0' && c <= '9') {
++ } else if (c >= '0' && c <= '9' && len < (INT_MAX - 10) / 10) {
+ len = (len * 10) + (c - '0');
+ } else {
+ if (started)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0309-Don-t-load-too-many-states-just-because-there-s-no-S.patch sgt-puzzles-20191231.79a5378/debian/patches/0309-Don-t-load-too-many-states-just-because-there-s-no-S.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0309-Don-t-load-too-many-states-just-because-there-s-no-S.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0309-Don-t-load-too-many-states-just-because-there-s-no-S.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,49 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 19:32:08 +0000
+Subject: [PATCH 309/389] Don't load too many states just because there's no
+ STATEPOS
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=b3d4a4197954c21ac78b68c58dff8f84fe743ea2
+Bug-Debian: https://bugs.debian.org/1028986
+
+If we start seeing state records in a save file (MOVE, SOLVE, or
+RESTART), we should already have seen STATEPOS, so emit an error if not.
+This avoids the situation where we overrun the end of the state array
+because we're continuing loading states in the hope a STATEPOS will come
+along. I've also added an assertion that we're not overrunning the
+state array for added paranoia.
+
+An earlier version of this fix just removed the test for data.statepos
+at the head of the loop, but that's wrong for a file that only has the
+initial state.
+
+This bug can be demonstrated by building Bridges with AddressSanitizer
+and loading this save file:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :7:Bridges
+PARAMS :13:7x7i30e10m2d0
+CPARAMS :13:7x7i30e10m2d0
+DESC :24:a4b4a1g1a2a8a4a4m2b2b3e3
+NSTATES :1:2
+MOVE :10:L1,0,4,0,1
+MOVE :10:L1,0,4,0,2
+---
+ midend.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/midend.c
++++ b/midend.c
+@@ -2289,7 +2289,12 @@ static const char *midend_deserialise_in
+ ret = "No state count provided in save file";
+ goto cleanup;
+ }
++ if (data.statepos < 0) {
++ ret = "No game position provided in save file";
++ goto cleanup;
++ }
+ gotstates++;
++ assert(gotstates < data.nstates);
+ if (!strcmp(key, "MOVE"))
+ data.states[gotstates].movetype = MOVE;
+ else if (!strcmp(key, "SOLVE"))
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0310-Palisade-forbid-moves-that-remove-grid-edges.patch sgt-puzzles-20191231.79a5378/debian/patches/0310-Palisade-forbid-moves-that-remove-grid-edges.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0310-Palisade-forbid-moves-that-remove-grid-edges.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0310-Palisade-forbid-moves-that-remove-grid-edges.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,63 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 12 Jan 2023 20:55:56 +0000
+Subject: [PATCH 310/389] Palisade: forbid moves that remove grid edges
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=21193eaf9308ace41004a19180ff382ec6e8754b
+Bug-Debian: https://bugs.debian.org/1028986
+
+Without this check, a corrupt save file can include a move that
+removes an edge of the grid, and then is_solved() walks off the edge
+of the grid causing a buffer over- or under-run.
+
+To demonstrate the bug, load this save file in a build with
+AddressSanitizer:
+
+SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
+VERSION :1:1
+GAME :8:Palisade
+PARAMS :5:5x5n5
+CPARAMS :5:5x5n5
+DESC :0:
+NSTATES :1:2
+STATEPOS:1:2
+MOVE :6:F0,0,1
+---
+ palisade.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+--- a/palisade.c
++++ b/palisade.c
+@@ -1008,10 +1008,9 @@ static game_state *execute_move(const ga
+ {
+ int w = state->shared->params.w, h = state->shared->params.h, wh = w * h;
+ game_state *ret = dup_game(state);
+- int nchars, x, y, flag;
++ int nchars, x, y, flag, i;
+
+ if (*move == 'S') {
+- int i;
+ ++move;
+ for (i = 0; i < wh && move[i]; ++i)
+ ret->borders[i] =
+@@ -1024,6 +1023,11 @@ static game_state *execute_move(const ga
+ while (sscanf(move, "F%d,%d,%d%n", &x, &y, &flag, &nchars) == 3 &&
+ !OUT_OF_BOUNDS(x, y, w, h)) {
+ move += nchars;
++ for (i = 0; i < 4; i++)
++ if ((flag & BORDER(i)) &&
++ OUT_OF_BOUNDS(x+dx[i], y+dy[i], w, h))
++ /* No toggling the borders of the grid! */
++ goto badmove;
+ ret->borders[y*w + x] ^= flag;
+ }
+
+@@ -1034,6 +1038,10 @@ static game_state *execute_move(const ga
+ ret->borders);
+
+ return ret;
++
++ badmove:
++ sfree(ret);
++ return NULL;
+ }
+
+ /* --- Drawing routines --------------------------------------------- */
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0311-Last-ditch-maximum-size-limit-for-Bridges.patch sgt-puzzles-20191231.79a5378/debian/patches/0311-Last-ditch-maximum-size-limit-for-Bridges.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0311-Last-ditch-maximum-size-limit-for-Bridges.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0311-Last-ditch-maximum-size-limit-for-Bridges.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 00:09:11 +0000
+Subject: [PATCH 311/389] Last-ditch maximum size limit for Bridges
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=97484b098f33266f6c747c292c708281cb15a286
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ bridges.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/bridges.c
++++ b/bridges.c
+@@ -72,6 +72,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -803,6 +804,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 3 || params->h < 3)
+ return "Width and height must be at least 3";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (params->maxb < 1 || params->maxb > MAX_BRIDGES)
+ return "Too many bridges.";
+ if (full) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0312-Last-ditch-grid-size-limit-for-Dominosa.patch sgt-puzzles-20191231.79a5378/debian/patches/0312-Last-ditch-grid-size-limit-for-Dominosa.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0312-Last-ditch-grid-size-limit-for-Dominosa.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0312-Last-ditch-grid-size-limit-for-Dominosa.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,32 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 11 Jan 2023 00:03:57 +0000
+Subject: [PATCH 312/389] Last-ditch grid-size limit for Dominosa
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=b3f3345764b0808a7a97b9c3a2a1888fd62383a0
+Bug-Debian: https://bugs.debian.org/1028986
+
+At least prevent integer overflow when constructing the grid.
+---
+ dominosa.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/dominosa.c
++++ b/dominosa.c
+@@ -47,6 +47,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -243,6 +244,10 @@ static const char *validate_params(const
+ {
+ if (params->n < 1)
+ return "Maximum face number must be at least one";
++ if (params->n > INT_MAX - 2 ||
++ params->n + 2 > INT_MAX / (params->n + 1))
++ return "Maximum face number must not be unreasonably large";
++
+ if (params->diff >= DIFFCOUNT)
+ return "Unknown difficulty rating";
+ return NULL;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0313-Last-ditch-grid-size-limit-for-Galaxies.patch sgt-puzzles-20191231.79a5378/debian/patches/0313-Last-ditch-grid-size-limit-for-Galaxies.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0313-Last-ditch-grid-size-limit-for-Galaxies.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0313-Last-ditch-grid-size-limit-for-Galaxies.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,32 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:53:07 +0000
+Subject: [PATCH 313/389] Last-ditch grid-size limit for Galaxies
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=d422dd6009f3e48e13d5f7f162813537902e125c
+Bug-Debian: https://bugs.debian.org/1028986
+
+At least prevent integer overflow when constructing the grid.
+---
+ galaxies.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/galaxies.c
++++ b/galaxies.c
+@@ -42,6 +42,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -281,6 +282,10 @@ static const char *validate_params(const
+ {
+ if (params->w < 3 || params->h < 3)
+ return "Width and height must both be at least 3";
++ if (params->w > INT_MAX / 2 || params->h > INT_MAX / 2 ||
++ params->w > (INT_MAX - params->w*2 - params->h*2 - 1) / 4 / params->h)
++ return "Width times height must not be unreasonably large";
++
+ /*
+ * This shouldn't be able to happen at all, since decode_params
+ * and custom_params will never generate anything that isn't
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0314-Last-ditch-grid-size-limit-for-Fifteen.patch sgt-puzzles-20191231.79a5378/debian/patches/0314-Last-ditch-grid-size-limit-for-Fifteen.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0314-Last-ditch-grid-size-limit-for-Fifteen.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0314-Last-ditch-grid-size-limit-for-Fifteen.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,30 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 23:37:21 +0000
+Subject: [PATCH 314/389] Last-ditch grid-size limit for Fifteen
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=522588f699b3c910c5a47f8b8185e69250665313
+Bug-Debian: https://bugs.debian.org/1028986
+
+At least prevent integer overflow when constructing the grid.
+---
+ fifteen.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/fifteen.c
++++ b/fifteen.c
+@@ -7,6 +7,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -138,6 +139,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 2 || params->h < 2)
+ return "Width and height must both be at least two";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+
+ return NULL;
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0315-Last-ditch-maximum-size-limit-for-Flip.patch sgt-puzzles-20191231.79a5378/debian/patches/0315-Last-ditch-maximum-size-limit-for-Flip.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0315-Last-ditch-maximum-size-limit-for-Flip.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0315-Last-ditch-maximum-size-limit-for-Flip.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,41 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 11:07:14 +0000
+Subject: [PATCH 315/389] Last-ditch maximum size limit for Flip
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=26d0633f87ccdbaf7035e2e14d9dfbfd7f379527
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be. Also in Flip's case that the square of the area still fits in
+an int.
+---
+ flip.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/flip.c
++++ b/flip.c
+@@ -8,6 +8,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -181,9 +182,16 @@ static game_params *custom_params(const
+
+ static const char *validate_params(const game_params *params, bool full)
+ {
++ int wh;
++
+ if (params->w <= 0 || params->h <= 0)
+ return "Width and height must both be greater than zero";
+- return NULL;
++ if (params->w > (INT_MAX - 3) / params->h)
++ return "Width times height must not be unreasonably large";
++ wh = params->w * params->h;
++ if (wh > (INT_MAX - 3) / wh)
++ return "Width times height is too large";
++ return NULL;
+ }
+
+ static char *encode_bitmap(unsigned char *bmp, int len)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0316-Last-ditch-grid-size-limit-for-Flood.patch sgt-puzzles-20191231.79a5378/debian/patches/0316-Last-ditch-grid-size-limit-for-Flood.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0316-Last-ditch-grid-size-limit-for-Flood.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0316-Last-ditch-grid-size-limit-for-Flood.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,30 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 21:20:35 +0000
+Subject: [PATCH 316/389] Last-ditch grid-size limit for Flood
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=da220a77d1793edd14144fd96584b7f461f06dfb
+Bug-Debian: https://bugs.debian.org/1028986
+
+At least prevent integer overflow when constructing the grid.
+---
+ flood.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/flood.c
++++ b/flood.c
+@@ -31,6 +31,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -209,6 +210,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 2 && params->h < 2)
+ return "Grid must contain at least two squares";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (params->colours < 3 || params->colours > 10)
+ return "Must have between 3 and 10 colours";
+ if (params->leniency < 0)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0317-Insist-that-Flood-grids-must-have-non-zero-size.patch sgt-puzzles-20191231.79a5378/debian/patches/0317-Insist-that-Flood-grids-must-have-non-zero-size.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0317-Insist-that-Flood-grids-must-have-non-zero-size.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0317-Insist-that-Flood-grids-must-have-non-zero-size.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,21 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 11 Jan 2023 09:35:51 +0000
+Subject: [PATCH 317/389] Insist that Flood grids must have non-zero size
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=d60192531e3ee6b492151f6aa8edd481ebddcdf7
+Bug-Debian: https://bugs.debian.org/1028986
+
+---
+ flood.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/flood.c
++++ b/flood.c
+@@ -210,6 +210,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 2 && params->h < 2)
+ return "Grid must contain at least two squares";
++ if (params->w < 1 || params->h < 1)
++ return "Width and height must be at least one";
+ if (params->w > INT_MAX / params->h)
+ return "Width times height must not be unreasonably large";
+ if (params->colours < 3 || params->colours > 10)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0318-Last-ditch-grid-size-limit-for-Inertia.patch sgt-puzzles-20191231.79a5378/debian/patches/0318-Last-ditch-grid-size-limit-for-Inertia.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0318-Last-ditch-grid-size-limit-for-Inertia.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0318-Last-ditch-grid-size-limit-for-Inertia.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,30 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 11 Jan 2023 00:04:59 +0000
+Subject: [PATCH 318/389] Last-ditch grid-size limit for Inertia
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=98724b90936c46e457234a0189bb09242fc727d1
+Bug-Debian: https://bugs.debian.org/1028986
+
+At least prevent integer overflow when constructing the grid.
+---
+ inertia.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/inertia.c
++++ b/inertia.c
+@@ -11,6 +11,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -202,6 +203,8 @@ static const char *validate_params(const
+ */
+ if (params->w < 2 || params->h < 2)
+ return "Width and height must both be at least two";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+
+ /*
+ * The grid construction algorithm creates 1/5 as many gems as
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0319-Last-ditch-maximum-size-limit-for-Light-Up.patch sgt-puzzles-20191231.79a5378/debian/patches/0319-Last-ditch-maximum-size-limit-for-Light-Up.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0319-Last-ditch-maximum-size-limit-for-Light-Up.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0319-Last-ditch-maximum-size-limit-for-Light-Up.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:31:10 +0000
+Subject: [PATCH 319/389] Last-ditch maximum size limit for Light Up
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=fcda12f4b73e69841fd8957b8e33930c584093e5
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ lightup.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/lightup.c
++++ b/lightup.c
+@@ -47,6 +47,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -346,6 +347,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 2 || params->h < 2)
+ return "Width and height must be at least 2";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (full) {
+ if (params->blackpc < 5 || params->blackpc > 100)
+ return "Percentage of black squares must be between 5% and 100%";
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0320-Limit-maximum-grid-size-in-Loopy.patch sgt-puzzles-20191231.79a5378/debian/patches/0320-Limit-maximum-grid-size-in-Loopy.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0320-Limit-maximum-grid-size-in-Loopy.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0320-Limit-maximum-grid-size-in-Loopy.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,373 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 11 Jan 2023 23:11:46 +0000
+Subject: [PATCH 320/389] Limit maximum grid size in Loopy
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=d71bba1a17d6b228e7dd8b437dccbd3f6bc4698c
+Bug-Debian: https://bugs.debian.org/1028986
+
+Every grid shape has its own limit, so this involved adding a new
+interface between loopy.c and grid.c. The limits are based on ensuring
+that the co-ordinate system of the grid doesn't overflow INT_MAX and
+neither do the lengths of the face and dot arrays.
+
+Though now I come to look at it I think the actual limits of grid.c are
+much lower. Hmm.
+
+[benh: Backported to 20191231: Drop the change to compassdodecagonal grid
+ which isn't implemented here.]
+---
+ grid.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ grid.h | 2 +
+ loopy.c | 3 +
+ 3 files changed, 210 insertions(+), 1 deletion(-)
+
+--- a/grid.c
++++ b/grid.c
+@@ -11,8 +11,9 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
+-#include <math.h>
+ #include <float.h>
++#include <limits.h>
++#include <math.h>
+
+ #include "puzzles.h"
+ #include "tree234.h"
+@@ -1388,6 +1389,15 @@ void grid_find_incentre(grid_face *f)
+
+ #define SQUARE_TILESIZE 20
+
++static const char *grid_validate_params_square(int width, int height)
++{
++ if (width > INT_MAX / SQUARE_TILESIZE || /* xextent */
++ height > INT_MAX / SQUARE_TILESIZE || /* yextent */
++ width + 1 > INT_MAX / (height + 1)) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_square(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -1450,6 +1460,18 @@ static grid *grid_new_square(int width,
+ #define HONEY_A 15
+ #define HONEY_B 26
+
++static const char *grid_validate_params_honeycomb(int width, int height)
++{
++ int a = HONEY_A;
++ int b = HONEY_B;
++
++ if (width - 1 > (INT_MAX - 4*a) / (3 * a) || /* xextent */
++ height - 1 > (INT_MAX - 3*b) / (2 * b) || /* yextent */
++ width + 1 > INT_MAX / 2 / (height + 1)) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_honeycomb(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -1519,6 +1541,18 @@ static grid *grid_new_honeycomb(int widt
+ #define TRIANGLE_VEC_X 15
+ #define TRIANGLE_VEC_Y 26
+
++static const char *grid_validate_params_triangular(int width, int height)
++{
++ int vec_x = TRIANGLE_VEC_X;
++ int vec_y = TRIANGLE_VEC_Y;
++
++ if (width > INT_MAX / (2 * vec_x) - 1 || /* xextent */
++ height > INT_MAX / vec_y || /* yextent */
++ width + 1 > INT_MAX / 4 / (height + 1)) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_triangular(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -1716,6 +1750,19 @@ static grid *grid_new_triangular(int wid
+ #define SNUBSQUARE_A 15
+ #define SNUBSQUARE_B 26
+
++static const char *grid_validate_params_snubsquare(int width, int height)
++{
++ int a = SNUBSQUARE_A;
++ int b = SNUBSQUARE_B;
++
++ if (width-1 > (INT_MAX - (a + b)) / (a+b) || /* xextent */
++ height > (INT_MAX - (a + b)) / (a+b) || /* yextent */
++ width > INT_MAX / 3 / height || /* max_faces */
++ width + 1 > INT_MAX / 2 / (height + 1)) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_snubsquare(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -1830,6 +1877,18 @@ static grid *grid_new_snubsquare(int wid
+ #define CAIRO_A 14
+ #define CAIRO_B 31
+
++static const char *grid_validate_params_cairo(int width, int height)
++{
++ int b = CAIRO_B; /* a unused in determining grid size. */
++
++ if (width - 1 > (INT_MAX - 2*b) / (2*b) || /* xextent */
++ height - 1 > (INT_MAX - 2*b) / (2*b) || /* yextent */
++ width > INT_MAX / 2 / height || /* max_faces */
++ width + 1 > INT_MAX / 3 / (height + 1)) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_cairo(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -1936,6 +1995,18 @@ static grid *grid_new_cairo(int width, i
+ #define GREATHEX_A 15
+ #define GREATHEX_B 26
+
++static const char *grid_validate_params_greathexagonal(int width, int height)
++{
++ int a = GREATHEX_A;
++ int b = GREATHEX_B;
++
++ if (width-1 > (INT_MAX - 4*a) / (3*a + b) || /* xextent */
++ height-1 > (INT_MAX - (3*b + a)) / (2*a + 2*b) || /* yextent */
++ width + 1 > INT_MAX / 6 / (height + 1)) /* max_faces */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_greathexagonal(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -2066,6 +2137,18 @@ static grid *grid_new_greathexagonal(int
+ #define KAGOME_A 15
+ #define KAGOME_B 26
+
++static const char *grid_validate_params_kagome(int width, int height)
++{
++ int a = KAGOME_A;
++ int b = KAGOME_B;
++
++ if (width-1 > (INT_MAX - 6*a) / (4*a) || /* xextent */
++ height-1 > (INT_MAX - 2*b) / (2*b) || /* yextent */
++ width + 1 > INT_MAX / 6 / (height + 1)) /* max_faces */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_kagome(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -2162,6 +2245,18 @@ static grid *grid_new_kagome(int width,
+ #define OCTAGONAL_A 29
+ #define OCTAGONAL_B 41
+
++static const char *grid_validate_params_octagonal(int width, int height)
++{
++ int a = OCTAGONAL_A;
++ int b = OCTAGONAL_B;
++
++ if (width > INT_MAX / (2*a + b) || /* xextent */
++ height > INT_MAX / (2*a + b) || /* yextent */
++ height + 1 > INT_MAX / 4 / (width + 1)) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_octagonal(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -2245,6 +2340,18 @@ static grid *grid_new_octagonal(int widt
+ #define KITE_A 15
+ #define KITE_B 26
+
++static const char *grid_validate_params_kites(int width, int height)
++{
++ int a = KITE_A;
++ int b = KITE_B;
++
++ if (width > (INT_MAX - 2*b) / (4*b) || /* xextent */
++ height - 1 > (INT_MAX - 8*a) / (6*a) || /* yextent */
++ width + 1 > INT_MAX / 6 / (height + 1)) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_kites(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -2367,6 +2474,20 @@ static grid *grid_new_kites(int width, i
+ #define FLORET_PX 75
+ #define FLORET_PY -26
+
++static const char *grid_validate_params_floret(int width, int height)
++{
++ int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */
++ int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
++ int ry = qy-py;
++ /* rx unused in determining grid size. */
++
++ if (width - 1 > (INT_MAX - (4*qx + 2*px)) / ((6*px+3*qx)/2) ||/* xextent */
++ height - 1 > (INT_MAX - (4*qy + 2*ry)) / (5*qy-4*py) || /* yextent */
++ width + 1 > INT_MAX / 9 / (height + 1)) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_floret(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -2474,6 +2595,18 @@ static grid *grid_new_floret(int width,
+ #define DODEC_A 15
+ #define DODEC_B 26
+
++static const char *grid_validate_params_dodecagonal(int width, int height)
++{
++ int a = DODEC_A;
++ int b = DODEC_B;
++
++ if (width - 1 > (INT_MAX - 3*(2*a + b)) / (4*a + 2*b) || /* xextent */
++ height - 1 > (INT_MAX - 2*(2*a + b)) / (3*a + 2*b) || /* yextent */
++ width > INT_MAX / 14 / height) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_dodecagonal(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -2554,6 +2687,18 @@ static grid *grid_new_dodecagonal(int wi
+ return g;
+ }
+
++static const char *grid_validate_params_greatdodecagonal(int width, int height)
++{
++ int a = DODEC_A;
++ int b = DODEC_B;
++
++ if (width - 1 > (INT_MAX - (2*(2*a + b) + 3*a + b)) / (6*a + 2*b) ||
++ height - 1 > (INT_MAX - 2*(2*a + b)) / (3*a + 3*b) || /* yextent */
++ width > INT_MAX / 200 / height) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_greatdodecagonal(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -2668,6 +2813,19 @@ static grid *grid_new_greatdodecagonal(i
+ return g;
+ }
+
++static const char *grid_validate_params_greatgreatdodecagonal(
++ int width, int height)
++{
++ int a = DODEC_A;
++ int b = DODEC_B;
++
++ if (width-1 > (INT_MAX - (2*(2*a + b) + 2*a + 2*b)) / (4*a + 4*b) ||
++ height-1 > (INT_MAX - 2*(2*a + b)) / (6*a + 2*b) || /* yextent */
++ width > INT_MAX / 300 / height) /* max_dots */
++ return "Grid size must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_greatgreatdodecagonal(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -2887,6 +3045,17 @@ static int set_faces(penrose_state *stat
+
+ #define PENROSE_TILESIZE 100
+
++static const char *grid_validate_params_penrose(int width, int height)
++{
++ int l = PENROSE_TILESIZE;
++
++ if (width > INT_MAX / l || /* xextent */
++ height > INT_MAX / l || /* yextent */
++ width > INT_MAX / (3 * 3 * 4 * height)) /* max_dots */
++ return "Grid must not be unreasonably large";
++ return NULL;
++}
++
+ static void grid_size_penrose(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -3089,6 +3258,16 @@ static grid *grid_new_penrose(int width,
+ return g;
+ }
+
++static const char *grid_validate_params_penrose_p2_kite(int width, int height)
++{
++ return grid_validate_params_penrose(width, height);
++}
++
++static const char *grid_validate_params_penrose_p3_thick(int width, int height)
++{
++ return grid_validate_params_penrose(width, height);
++}
++
+ static void grid_size_penrose_p2_kite(int width, int height,
+ int *tilesize, int *xextent, int *yextent)
+ {
+@@ -3113,12 +3292,24 @@ static grid *grid_new_penrose_p3_thick(i
+
+ /* ----------- End of grid generators ------------- */
+
++#define FNVAL(upper,lower) &grid_validate_params_ ## lower,
+ #define FNNEW(upper,lower) &grid_new_ ## lower,
+ #define FNSZ(upper,lower) &grid_size_ ## lower,
+
++static const char *(*(grid_validate_paramses[]))(int, int) =
++ { GRIDGEN_LIST(FNVAL) };
+ static grid *(*(grid_news[]))(int, int, const char*) = { GRIDGEN_LIST(FNNEW) };
+ static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ) };
+
++/* Work out if a grid can be made, and complain if not. */
++
++const char *grid_validate_params(grid_type type, int width, int height)
++{
++ if (width <= 0 || height <= 0)
++ return "Width and height must both be positive";
++ return grid_validate_paramses[type](width, height);
++}
++
+ char *grid_new_desc(grid_type type, int width, int height, random_state *rs)
+ {
+ if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
+--- a/grid.h
++++ b/grid.h
+@@ -114,6 +114,8 @@ typedef struct grid {
+ typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type;
+ #undef ENUM
+
++const char *grid_validate_params(grid_type type, int width, int height);
++
+ /* Free directly after use if non-NULL. Will never contain an underscore
+ * (so clients can safely use that as a separator). */
+ char *grid_new_desc(grid_type type, int width, int height, random_state *rs);
+--- a/loopy.c
++++ b/loopy.c
+@@ -681,6 +681,7 @@ static game_params *custom_params(const
+
+ static const char *validate_params(const game_params *params, bool full)
+ {
++ const char *err;
+ if (params->type < 0 || params->type >= NUM_GRID_TYPES)
+ return "Illegal grid type";
+ if (params->w < grid_size_limits[params->type].amin ||
+@@ -689,6 +690,8 @@ static const char *validate_params(const
+ if (params->w < grid_size_limits[params->type].omin &&
+ params->h < grid_size_limits[params->type].omin)
+ return grid_size_limits[params->type].oerr;
++ err = grid_validate_params(grid_types[params->type], params->w, params->h);
++ if (err != NULL) return err;
+
+ /*
+ * This shouldn't be able to happen at all, since decode_params
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0321-Last-ditch-maximum-size-limit-for-Magnets.patch sgt-puzzles-20191231.79a5378/debian/patches/0321-Last-ditch-maximum-size-limit-for-Magnets.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0321-Last-ditch-maximum-size-limit-for-Magnets.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0321-Last-ditch-maximum-size-limit-for-Magnets.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 11 Jan 2023 09:43:01 +0000
+Subject: [PATCH 321/389] Last-ditch maximum size limit for Magnets
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=261a9568faeeb7acc6e56bd67147917c5109ac8a
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ magnets.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/magnets.c
++++ b/magnets.c
+@@ -36,6 +36,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -232,6 +233,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 2) return "Width must be at least two";
+ if (params->h < 2) return "Height must be at least two";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (params->diff >= DIFF_TRICKY) {
+ if (params->w < 5 && params->h < 5)
+ return "Either width or height must be at least five for Tricky";
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0322-Last-ditch-maximum-size-limit-for-Map.patch sgt-puzzles-20191231.79a5378/debian/patches/0322-Last-ditch-maximum-size-limit-for-Map.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0322-Last-ditch-maximum-size-limit-for-Map.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0322-Last-ditch-maximum-size-limit-for-Map.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,43 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 00:19:02 +0000
+Subject: [PATCH 322/389] Last-ditch maximum size limit for Map
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=ed75535fc24217c51f900d42385309c8c8b36cc3
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be. Also a similar check in decode_params when defaulting the
+number of regions.
+---
+ map.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/map.c
++++ b/map.c
+@@ -14,6 +14,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -180,7 +181,9 @@ static void decode_params(game_params *p
+ params->n = atoi(p);
+ while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
+ } else {
+- params->n = params->w * params->h / 8;
++ if (params->h > 0 && params->w > 0 &&
++ params->w <= INT_MAX / params->h)
++ params->n = params->w * params->h / 8;
+ }
+ if (*p == 'd') {
+ int i;
+@@ -252,6 +255,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 2 || params->h < 2)
+ return "Width and height must be at least two";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (params->n < 5)
+ return "Must have at least five regions";
+ if (params->n > params->w * params->h)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0323-Last-ditch-maximum-size-limit-for-Mines.patch sgt-puzzles-20191231.79a5378/debian/patches/0323-Last-ditch-maximum-size-limit-for-Mines.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0323-Last-ditch-maximum-size-limit-for-Mines.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0323-Last-ditch-maximum-size-limit-for-Mines.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,43 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 00:20:36 +0000
+Subject: [PATCH 323/389] Last-ditch maximum size limit for Mines
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=5cc9bfb811854b66c4a570e8100b8a1aad037f0e
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be. Also a similar check in decode_params when defaulting the
+number of mines.
+---
+ mines.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/mines.c
++++ b/mines.c
+@@ -12,6 +12,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "tree234.h"
+@@ -162,7 +163,9 @@ static void decode_params(game_params *p
+ params->n = atoi(p);
+ while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
+ } else {
+- params->n = params->w * params->h / 10;
++ if (params->h > 0 && params->w > 0 &&
++ params->w <= INT_MAX / params->h)
++ params->n = params->w * params->h / 10;
+ }
+
+ while (*p) {
+@@ -258,6 +261,8 @@ static const char *validate_params(const
+ */
+ if (full && params->unique && (params->w <= 2 || params->h <= 2))
+ return "Width and height must both be greater than two";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (params->n < 0)
+ return "Mine count may not be negative";
+ if (params->n > params->w * params->h - 9)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0324-Also-check-for-tiny-grids-in-Mines.patch sgt-puzzles-20191231.79a5378/debian/patches/0324-Also-check-for-tiny-grids-in-Mines.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0324-Also-check-for-tiny-grids-in-Mines.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0324-Also-check-for-tiny-grids-in-Mines.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,23 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 00:22:57 +0000
+Subject: [PATCH 324/389] Also check for tiny grids in Mines
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=9e2e0692ed087dbe0d5f4abbddf3aebd6a11b30e
+Bug-Debian: https://bugs.debian.org/1028986
+
+A zero-size grid isn't acceptable even if someone has generated it for
+us.
+---
+ mines.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -261,6 +261,8 @@ static const char *validate_params(const
+ */
+ if (full && params->unique && (params->w <= 2 || params->h <= 2))
+ return "Width and height must both be greater than two";
++ if (params->w < 1 || params->h < 1)
++ return "Width and height must both be at least one";
+ if (params->w > INT_MAX / params->h)
+ return "Width times height must not be unreasonably large";
+ if (params->n < 0)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0326-Last-ditch-maximum-size-limit-for-Net.patch sgt-puzzles-20191231.79a5378/debian/patches/0326-Last-ditch-maximum-size-limit-for-Net.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0326-Last-ditch-maximum-size-limit-for-Net.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0326-Last-ditch-maximum-size-limit-for-Net.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:24:49 +0000
+Subject: [PATCH 326/389] Last-ditch maximum size limit for Net
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=051357bb24c0f05c291d6f9e6b460839847923b4
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ net.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/net.c
++++ b/net.c
+@@ -7,6 +7,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -313,6 +314,8 @@ static const char *validate_params(const
+ return "Width and height must both be greater than zero";
+ if (params->width <= 1 && params->height <= 1)
+ return "At least one of width and height must be greater than one";
++ if (params->width > INT_MAX / params->height)
++ return "Width times height must not be unreasonably large";
+ if (params->barrier_probability < 0)
+ return "Barrier probability may not be negative";
+ if (params->barrier_probability > 1)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0327-Last-ditch-maximum-size-limit-for-Netslide.patch sgt-puzzles-20191231.79a5378/debian/patches/0327-Last-ditch-maximum-size-limit-for-Netslide.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0327-Last-ditch-maximum-size-limit-for-Netslide.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0327-Last-ditch-maximum-size-limit-for-Netslide.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 00:25:52 +0000
+Subject: [PATCH 327/389] Last-ditch maximum size limit for Netslide
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=40ec3aaf09824face187218f899494aef429a9c6
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ netslide.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/netslide.c
++++ b/netslide.c
+@@ -8,6 +8,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -307,6 +308,8 @@ static const char *validate_params(const
+ {
+ if (params->width <= 1 || params->height <= 1)
+ return "Width and height must both be greater than one";
++ if (params->width > INT_MAX / params->height)
++ return "Width times height must not be unreasonably large";
+ if (params->barrier_probability < 0)
+ return "Barrier probability may not be negative";
+ if (params->barrier_probability > 1)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0328-Integer-overflow-protection-in-Pattern.patch sgt-puzzles-20191231.79a5378/debian/patches/0328-Integer-overflow-protection-in-Pattern.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0328-Integer-overflow-protection-in-Pattern.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0328-Integer-overflow-protection-in-Pattern.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,40 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Wed, 11 Jan 2023 23:15:44 +0000
+Subject: [PATCH 328/389] Integer overflow protection in Pattern
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=dd00e9c532abc7517bd7ca72c8e4db91bb2da821
+Bug-Debian: https://bugs.debian.org/1028986
+
+Both for grid sizes and for clue values.
+---
+ pattern.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/pattern.c
++++ b/pattern.c
+@@ -7,6 +7,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -175,6 +176,9 @@ static const char *validate_params(const
+ {
+ if (params->w <= 0 || params->h <= 0)
+ return "Width and height must both be greater than zero";
++ if (params->w > INT_MAX - 1 || params->h > INT_MAX - 1 ||
++ params->w > INT_MAX / params->h)
++ return "Puzzle must not be unreasonably large";
+ return NULL;
+ }
+
+@@ -908,6 +912,8 @@ static const char *validate_desc(const g
+ p = desc;
+ while (*desc && isdigit((unsigned char)*desc)) desc++;
+ n = atoi(p);
++ if (n > INT_MAX - 1)
++ return "at least one clue is grossly excessive";
+ rowspace -= n+1;
+
+ if (rowspace < 0) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0329-Last-ditch-maximum-size-limit-for-Palisade.patch sgt-puzzles-20191231.79a5378/debian/patches/0329-Last-ditch-maximum-size-limit-for-Palisade.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0329-Last-ditch-maximum-size-limit-for-Palisade.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0329-Last-ditch-maximum-size-limit-for-Palisade.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,40 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 00:28:09 +0000
+Subject: [PATCH 329/389] Last-ditch maximum size limit for Palisade
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=91c0fac1dc98be2ecc074d83368df74f9f755641
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ palisade.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/palisade.c
++++ b/palisade.c
+@@ -17,6 +17,7 @@
+
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -156,13 +157,15 @@ static game_params *custom_params(const
+
+ static const char *validate_params(const game_params *params, bool full)
+ {
+- int w = params->w, h = params->h, k = params->k, wh = w * h;
++ int w = params->w, h = params->h, k = params->k, wh;
+
+ if (k < 1) return "Region size must be at least one";
+ if (w < 1) return "Width must be at least one";
+ if (h < 1) return "Height must be at least one";
++ if (w > INT_MAX / h)
++ return "Width times height must not be unreasonably large";
++ wh = w * h;
+ if (wh % k) return "Region size must divide grid area";
+-
+ if (!full) return NULL; /* succeed partial validation */
+
+ /* MAYBE FIXME: we (just?) don't have the UI for winning these. */
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0330-Last-ditch-maximum-size-limit-for-Pearl.patch sgt-puzzles-20191231.79a5378/debian/patches/0330-Last-ditch-maximum-size-limit-for-Pearl.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0330-Last-ditch-maximum-size-limit-for-Pearl.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0330-Last-ditch-maximum-size-limit-for-Pearl.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:39:57 +0000
+Subject: [PATCH 330/389] Last-ditch maximum size limit for Pearl
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=8a3fb82e230c5cf18b82f54687c2a56f53875a38
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ pearl.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/pearl.c
++++ b/pearl.c
+@@ -35,6 +35,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -272,6 +273,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 5) return "Width must be at least five";
+ if (params->h < 5) return "Height must be at least five";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (params->difficulty < 0 || params->difficulty >= DIFFCOUNT)
+ return "Unknown difficulty level";
+ if (params->difficulty >= DIFF_TRICKY && params->w + params->h < 11)
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0331-Last-ditch-maximum-size-limit-for-Pegs.patch sgt-puzzles-20191231.79a5378/debian/patches/0331-Last-ditch-maximum-size-limit-for-Pegs.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0331-Last-ditch-maximum-size-limit-for-Pegs.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0331-Last-ditch-maximum-size-limit-for-Pegs.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,42 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 00:30:48 +0000
+Subject: [PATCH 331/389] Last-ditch maximum size limit for Pegs
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=6e40605f1ed4ecce400faae5b41c03995e7f862c
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ pegs.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/pegs.c
++++ b/pegs.c
+@@ -7,6 +7,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -182,6 +183,8 @@ static const char *validate_params(const
+ {
+ if (full && (params->w <= 3 || params->h <= 3))
+ return "Width and height must both be greater than three";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+
+ /*
+ * It might be possible to implement generalisations of Cross
+@@ -658,7 +661,9 @@ static char *new_game_desc(const game_pa
+
+ static const char *validate_desc(const game_params *params, const char *desc)
+ {
+- int len = params->w * params->h;
++ int len;
++
++ len = params->w * params->h;
+
+ if (len != strlen(desc))
+ return "Game description is wrong length";
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0332-Also-limit-Pegs-to-at-least-1x1-even-when-not-doing-.patch sgt-puzzles-20191231.79a5378/debian/patches/0332-Also-limit-Pegs-to-at-least-1x1-even-when-not-doing-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0332-Also-limit-Pegs-to-at-least-1x1-even-when-not-doing-.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0332-Also-limit-Pegs-to-at-least-1x1-even-when-not-doing-.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,22 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 00:32:25 +0000
+Subject: [PATCH 332/389] Also limit Pegs to at least 1x1 even when not doing
+ full validation
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=b090c82df1527dcf348c96765f10ab5736c68c29
+Bug-Debian: https://bugs.debian.org/1028986
+
+---
+ pegs.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/pegs.c
++++ b/pegs.c
+@@ -183,6 +183,8 @@ static const char *validate_params(const
+ {
+ if (full && (params->w <= 3 || params->h <= 3))
+ return "Width and height must both be greater than three";
++ if (params->w < 1 || params->h < 1)
++ return "Width and height must both be at least one";
+ if (params->w > INT_MAX / params->h)
+ return "Width times height must not be unreasonably large";
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0333-Last-ditch-maximum-size-limit-for-Same-Game.patch sgt-puzzles-20191231.79a5378/debian/patches/0333-Last-ditch-maximum-size-limit-for-Same-Game.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0333-Last-ditch-maximum-size-limit-for-Same-Game.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0333-Last-ditch-maximum-size-limit-for-Same-Game.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 10:54:37 +0000
+Subject: [PATCH 333/389] Last-ditch maximum size limit for Same Game
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=d5ec2758ee3b7a8934475a1122813ad2312dc850
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ samegame.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/samegame.c
++++ b/samegame.c
+@@ -67,6 +67,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -285,6 +286,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 1 || params->h < 1)
+ return "Width and height must both be positive";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+
+ if (params->ncols > 9)
+ return "Maximum of 9 colours";
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0334-Last-ditch-maximum-size-limit-for-Signpost.patch sgt-puzzles-20191231.79a5378/debian/patches/0334-Last-ditch-maximum-size-limit-for-Signpost.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0334-Last-ditch-maximum-size-limit-for-Signpost.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0334-Last-ditch-maximum-size-limit-for-Signpost.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:41:24 +0000
+Subject: [PATCH 334/389] Last-ditch maximum size limit for Signpost
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=5c36e1536a05abf514b09476813cf71bc9dc1e31
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ signpost.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/signpost.c
++++ b/signpost.c
+@@ -7,6 +7,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -421,6 +422,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 1) return "Width must be at least one";
+ if (params->h < 1) return "Height must be at least one";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ if (full && params->w == 1 && params->h == 1)
+ /* The UI doesn't let us move these from unsolved to solved,
+ * so we disallow generating (but not playing) them. */
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0335-Last-ditch-maximum-size-limit-for-Sixteen.patch sgt-puzzles-20191231.79a5378/debian/patches/0335-Last-ditch-maximum-size-limit-for-Sixteen.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0335-Last-ditch-maximum-size-limit-for-Sixteen.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0335-Last-ditch-maximum-size-limit-for-Sixteen.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:24:59 +0000
+Subject: [PATCH 335/389] Last-ditch maximum size limit for Sixteen
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=91d96fa0bc133c8d967f3c4b01804e7773de8504
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ sixteen.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/sixteen.c
++++ b/sixteen.c
+@@ -9,6 +9,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -173,6 +174,8 @@ static const char *validate_params(const
+ {
+ if (params->w < 2 || params->h < 2)
+ return "Width and height must both be at least two";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+
+ return NULL;
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0336-Limit-size-of-puzzle-in-Tents-to-avoid-integer-overf.patch sgt-puzzles-20191231.79a5378/debian/patches/0336-Limit-size-of-puzzle-in-Tents-to-avoid-integer-overf.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0336-Limit-size-of-puzzle-in-Tents-to-avoid-integer-overf.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0336-Limit-size-of-puzzle-in-Tents-to-avoid-integer-overf.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,30 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 12 Jan 2023 14:34:14 +0000
+Subject: [PATCH 336/389] Limit size of puzzle in Tents to avoid integer
+ overflow
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=07999443c24414602cf046fefc84d1d17b5afd69
+Bug-Debian: https://bugs.debian.org/1028986
+
+---
+ tents.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/tents.c
++++ b/tents.c
+@@ -32,6 +32,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -408,6 +409,8 @@ static const char *validate_params(const
+ */
+ if (params->w < 4 || params->h < 4)
+ return "Width and height must both be at least four";
++ if (params->w > (INT_MAX - 1) / params->h)
++ return "Width times height must not be unreasonably large";
+ return NULL;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0337-Last-ditch-maximum-size-limit-for-Tracks.patch sgt-puzzles-20191231.79a5378/debian/patches/0337-Last-ditch-maximum-size-limit-for-Tracks.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0337-Last-ditch-maximum-size-limit-for-Tracks.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0337-Last-ditch-maximum-size-limit-for-Tracks.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:39:06 +0000
+Subject: [PATCH 337/389] Last-ditch maximum size limit for Tracks
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=c53e0d386793840ad84b8bbcb4760cfb2b6897d4
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ tracks.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/tracks.c
++++ b/tracks.c
+@@ -19,6 +19,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -191,6 +192,8 @@ static const char *validate_params(const
+ */
+ if (params->w < 4 || params->h < 4)
+ return "Width and height must both be at least four";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ return NULL;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0338-Last-ditch-maximum-size-limit-for-Twiddle.patch sgt-puzzles-20191231.79a5378/debian/patches/0338-Last-ditch-maximum-size-limit-for-Twiddle.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0338-Last-ditch-maximum-size-limit-for-Twiddle.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0338-Last-ditch-maximum-size-limit-for-Twiddle.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:28:19 +0000
+Subject: [PATCH 338/389] Last-ditch maximum size limit for Twiddle
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=51dcf4add6e046568557cee2fa01975f14716889
+Bug-Debian: https://bugs.debian.org/1028986
+
+This makes sure that width * height <= INT_MAX, which it rather needs
+to be.
+---
+ twiddle.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/twiddle.c
++++ b/twiddle.c
+@@ -10,6 +10,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -210,6 +211,8 @@ static const char *validate_params(const
+ return "Width must be at least the rotating block size";
+ if (params->h < params->n)
+ return "Height must be at least the rotating block size";
++ if (params->w > INT_MAX / params->h)
++ return "Width times height must not be unreasonably large";
+ return NULL;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0339-Adjust-Undead-upper-grid-size-limit-to-avoid-overflo.patch sgt-puzzles-20191231.79a5378/debian/patches/0339-Adjust-Undead-upper-grid-size-limit-to-avoid-overflo.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0339-Adjust-Undead-upper-grid-size-limit-to-avoid-overflo.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0339-Adjust-Undead-upper-grid-size-limit-to-avoid-overflo.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,23 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:34:06 +0000
+Subject: [PATCH 339/389] Adjust Undead upper grid-size limit to avoid overflow
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=85ccdf2f75e3ea55d5e92d9790e50394e1bec089
+Bug-Debian: https://bugs.debian.org/1028986
+
+---
+ undead.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/undead.c
++++ b/undead.c
+@@ -193,9 +193,9 @@ static game_params *custom_params(const
+
+ static const char *validate_params(const game_params *params, bool full)
+ {
+- if ((params->w * params->h ) > 54) return "Grid is too big";
+ if (params->w < 3) return "Width must be at least 3";
+ if (params->h < 3) return "Height must be at least 3";
++ if (params->w > 54 / params->h) return "Grid is too big";
+ if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating";
+ return NULL;
+ }
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0340-Last-ditch-point-count-limit-for-Untangle.patch sgt-puzzles-20191231.79a5378/debian/patches/0340-Last-ditch-point-count-limit-for-Untangle.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0340-Last-ditch-point-count-limit-for-Untangle.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0340-Last-ditch-point-count-limit-for-Untangle.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,31 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Tue, 10 Jan 2023 20:46:24 +0000
+Subject: [PATCH 340/389] Last-ditch point-count limit for Untangle
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=d5b8a20def7634d4df79800fe54d8e34c6353974
+Bug-Debian: https://bugs.debian.org/1028986
+
+Anything over INT_MAX/3 will cause an integer overflow, so put the
+limit there for now.
+---
+ untangle.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/untangle.c
++++ b/untangle.c
+@@ -31,6 +31,7 @@
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
++#include <limits.h>
+ #include <math.h>
+
+ #include "puzzles.h"
+@@ -206,6 +207,8 @@ static const char *validate_params(const
+ {
+ if (params->n < 4)
+ return "Number of points must be at least four";
++ if (params->n > INT_MAX / 3)
++ return "Number of points must not be unreasonably large";
+ return NULL;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0341-Black-Box-correct-order-of-validation-checks-for-F-c.patch sgt-puzzles-20191231.79a5378/debian/patches/0341-Black-Box-correct-order-of-validation-checks-for-F-c.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0341-Black-Box-correct-order-of-validation-checks-for-F-c.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0341-Black-Box-correct-order-of-validation-checks-for-F-c.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,28 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 8 Jan 2023 10:03:10 +0000
+Subject: [PATCH 341/389] Black Box: correct order of validation checks for "F"
+ commands
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=c2eedeedfe3c48f3f013fb1308f61d7ef94e8b2c
+Bug-Debian: https://bugs.debian.org/1028986
+
+It doesn't do much good to range-check an argument after using it as
+an array index.
+---
+ blackbox.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/blackbox.c
++++ b/blackbox.c
+@@ -1032,10 +1032,10 @@ static game_state *execute_move(const ga
+
+ case 'F':
+ sscanf(move+1, "%d", &rangeno);
+- if (ret->exits[rangeno] != LASER_EMPTY)
+- goto badmove;
+ if (!RANGECHECK(ret, rangeno))
+ goto badmove;
++ if (ret->exits[rangeno] != LASER_EMPTY)
++ goto badmove;
+ fire_laser(ret, rangeno);
+ break;
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0342-Palisade-don-t-leak-memory-on-a-bad-move.patch sgt-puzzles-20191231.79a5378/debian/patches/0342-Palisade-don-t-leak-memory-on-a-bad-move.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0342-Palisade-don-t-leak-memory-on-a-bad-move.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0342-Palisade-don-t-leak-memory-on-a-bad-move.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,41 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 12 Jan 2023 21:00:22 +0000
+Subject: [PATCH 342/389] Palisade: don't leak memory on a bad move
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=38cf1955e5861f67d385ede006c5b5d1701aca8d
+Bug-Debian: https://bugs.debian.org/1028986
+
+Invalid moves can turn up in corrupted save files, and puzzles
+shouldn't leak memory when failing to load a corrupted save file.
+---
+ palisade.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/palisade.c
++++ b/palisade.c
+@@ -1018,7 +1018,7 @@ static game_state *execute_move(const ga
+ for (i = 0; i < wh && move[i]; ++i)
+ ret->borders[i] =
+ (move[i] & BORDER_MASK) | DISABLED(~move[i] & BORDER_MASK);
+- if (i < wh || move[i]) return NULL; /* leaks `ret', then we die */
++ if (i < wh || move[i]) goto badmove;
+ ret->cheated = ret->completed = true;
+ return ret;
+ }
+@@ -1034,7 +1034,7 @@ static game_state *execute_move(const ga
+ ret->borders[y*w + x] ^= flag;
+ }
+
+- if (*move) return NULL; /* leaks `ret', then we die */
++ if (*move) goto badmove;
+
+ if (!ret->completed)
+ ret->completed = is_solved(&ret->shared->params, ret->shared->clues,
+@@ -1043,7 +1043,7 @@ static game_state *execute_move(const ga
+ return ret;
+
+ badmove:
+- sfree(ret);
++ free_game(ret);
+ return NULL;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0343-Don-t-allow-negative-clues-in-Pattern.patch sgt-puzzles-20191231.79a5378/debian/patches/0343-Don-t-allow-negative-clues-in-Pattern.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0343-Don-t-allow-negative-clues-in-Pattern.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0343-Don-t-allow-negative-clues-in-Pattern.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,21 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 12 Jan 2023 21:09:39 +0000
+Subject: [PATCH 343/389] Don't allow negative clues in Pattern
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=e5d106eb279c12d2454802421e723873e4bae6c2
+Bug-Debian: https://bugs.debian.org/1028986
+
+---
+ pattern.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/pattern.c
++++ b/pattern.c
+@@ -912,6 +912,8 @@ static const char *validate_desc(const g
+ p = desc;
+ while (*desc && isdigit((unsigned char)*desc)) desc++;
+ n = atoi(p);
++ if (n < 0)
++ return "at least one clue is negative";
+ if (n > INT_MAX - 1)
+ return "at least one clue is grossly excessive";
+ rowspace -= n+1;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0344-When-loading-don-t-decode_ui-unless-we-have-a-UI.patch sgt-puzzles-20191231.79a5378/debian/patches/0344-When-loading-don-t-decode_ui-unless-we-have-a-UI.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0344-When-loading-don-t-decode_ui-unless-we-have-a-UI.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0344-When-loading-don-t-decode_ui-unless-we-have-a-UI.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,24 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 19:06:51 +0000
+Subject: [PATCH 344/389] When loading, don't decode_ui unless we have a UI
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=68f9fae973e2ffb6c0b9ed1e0761d3a0768455ad
+Bug-Debian: https://bugs.debian.org/1028986
+
+If the save file doesn't have a UI line, it's not sensible to try to
+decode it.
+---
+ midend.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/midend.c
++++ b/midend.c
+@@ -2386,7 +2386,8 @@ static const char *midend_deserialise_in
+ }
+
+ data.ui = me->ourgame->new_ui(data.states[0].state);
+- me->ourgame->decode_ui(data.ui, data.uistr);
++ if (data.uistr)
++ me->ourgame->decode_ui(data.ui, data.uistr);
+
+ /*
+ * Run the externally provided check function, and abort if it
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0346-Palisade-remove-assertion-from-decode_ui.patch sgt-puzzles-20191231.79a5378/debian/patches/0346-Palisade-remove-assertion-from-decode_ui.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0346-Palisade-remove-assertion-from-decode_ui.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0346-Palisade-remove-assertion-from-decode_ui.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,24 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 22:29:17 +0000
+Subject: [PATCH 346/389] Palisade: remove assertion from decode_ui()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=0dbbd52935b8b17b3b3ab3d9ae6271cde891f70b
+Bug-Debian: https://bugs.debian.org/1028986
+
+Other games tolerate receiving an encoded game_ui even if they can
+never generate one. This is sensible, since it means that if a new
+version starts saving UI state, old versions can load save files
+generated by those newer versions.
+---
+ palisade.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/palisade.c
++++ b/palisade.c
+@@ -892,7 +892,6 @@ static char *encode_ui(const game_ui *ui
+
+ static void decode_ui(game_ui *ui, const char *encoding)
+ {
+- assert (encoding == NULL);
+ }
+
+ static void game_changed_state(game_ui *ui, const game_state *oldstate,
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0347-Same-Game-reject-moves-with-unexpected-characters-in.patch sgt-puzzles-20191231.79a5378/debian/patches/0347-Same-Game-reject-moves-with-unexpected-characters-in.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0347-Same-Game-reject-moves-with-unexpected-characters-in.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0347-Same-Game-reject-moves-with-unexpected-characters-in.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,26 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sat, 7 Jan 2023 23:24:39 +0000
+Subject: [PATCH 347/389] Same Game: reject moves with unexpected characters in
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=8c5279cf75e41f1ae75b2daf0d06c883a5ae0bca
+Bug-Debian: https://bugs.debian.org/1028986
+
+Previously if a move string starting with "M" contained anything else
+other than a digit or a comma, execute_move() would spin trying to
+parse it. Now it returns NULL.
+---
+ samegame.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/samegame.c
++++ b/samegame.c
+@@ -1327,6 +1327,10 @@ static game_state *execute_move(const ga
+ move++;
+
+ while (*move) {
++ if (!isdigit((unsigned char)*move)) {
++ free_game(ret);
++ return NULL;
++ }
+ i = atoi(move);
+ if (i < 0 || i >= ret->n) {
+ free_game(ret);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0349-Filling-validate-length-of-auto-solve-move-strings.patch sgt-puzzles-20191231.79a5378/debian/patches/0349-Filling-validate-length-of-auto-solve-move-strings.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0349-Filling-validate-length-of-auto-solve-move-strings.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0349-Filling-validate-length-of-auto-solve-move-strings.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,33 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 8 Jan 2023 09:57:53 +0000
+Subject: [PATCH 349/389] Filling: validate length of auto-solve move strings
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=8a06ff26fc6abd77a4b4a08b839943f588d92dcf
+Bug-Debian: https://bugs.debian.org/1028986
+
+Without this, execute_move() can end up reading off the end of the
+move string, which isn't very friendly. Also remove the comment
+saying that the move string doesn't have to be null-terminated,
+because now it does.
+---
+ filling.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/filling.c
++++ b/filling.c
+@@ -1105,8 +1105,6 @@ static bool solver(const int *orig, int
+ **solution = 's';
+ for (i = 0; i < sz; ++i) (*solution)[i + 1] = ss.board[i] + '0';
+ (*solution)[sz + 1] = '\0';
+- /* We don't need the \0 for execute_move (the only user)
+- * I'm just being printf-friendly in case I wanna print */
+ }
+
+ sfree(ss.dsf);
+@@ -1567,6 +1565,7 @@ static game_state *execute_move(const ga
+
+ if (*move == 's') {
+ int i = 0;
++ if (strlen(move) != sz + 1) return NULL;
+ new_state = dup_game(state);
+ for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0';
+ new_state->cheated = true;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0350-Tighten-Bridges-validate_desc.patch sgt-puzzles-20191231.79a5378/debian/patches/0350-Tighten-Bridges-validate_desc.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0350-Tighten-Bridges-validate_desc.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0350-Tighten-Bridges-validate_desc.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,29 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 8 Jan 2023 10:42:45 +0000
+Subject: [PATCH 350/389] Tighten Bridges' validate_desc()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=ba944f9f95238d0b820e63d9b6c2d83efc09b8a2
+Bug-Debian: https://bugs.debian.org/1028986
+
+It allowed V, W, X, Y, H, I, J, and K to appear in game descriptions
+even though new_game() didn't ascribe any meaning to those letters and
+would fail an assertion if they ever occurred. As far as I can tell,
+those letters have never done anything, so I've just removed the
+checks for them from validate_desc().
+---
+ bridges.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/bridges.c
++++ b/bridges.c
+@@ -2016,11 +2016,6 @@ static const char *validate_desc(const g
+ i += *desc - 'a'; /* plus the i++ */
+ else if (*desc >= 'A' && *desc <= 'G')
+ /* OK */;
+- else if (*desc == 'V' || *desc == 'W' ||
+- *desc == 'X' || *desc == 'Y' ||
+- *desc == 'H' || *desc == 'I' ||
+- *desc == 'J' || *desc == 'K')
+- /* OK */;
+ else if (!*desc)
+ return "Game description shorter than expected";
+ else
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0351-Untangle-forbid-descriptions-that-connect-a-node-to-.patch sgt-puzzles-20191231.79a5378/debian/patches/0351-Untangle-forbid-descriptions-that-connect-a-node-to-.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0351-Untangle-forbid-descriptions-that-connect-a-node-to-.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0351-Untangle-forbid-descriptions-that-connect-a-node-to-.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,24 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 8 Jan 2023 12:34:57 +0000
+Subject: [PATCH 351/389] Untangle: forbid descriptions that connect a node to
+ itself
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=d3290195da55beae04d1bb86f811b6f8dd6b0663
+Bug-Debian: https://bugs.debian.org/1028986
+
+These cause an assertion failure in new_game(), so they should be
+rejected by validate_desc().
+---
+ untangle.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/untangle.c
++++ b/untangle.c
+@@ -755,6 +755,8 @@ static const char *validate_desc(const g
+ return "Expected ',' after number in game description";
+ desc++; /* eat comma */
+ }
++ if (a == b)
++ return "Node linked to itself in game description";
+ }
+
+ return NULL;
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0352-Mines-No-moving-once-you-re-dead.patch sgt-puzzles-20191231.79a5378/debian/patches/0352-Mines-No-moving-once-you-re-dead.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0352-Mines-No-moving-once-you-re-dead.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0352-Mines-No-moving-once-you-re-dead.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,26 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Sun, 8 Jan 2023 21:59:27 +0000
+Subject: [PATCH 352/389] Mines: No moving once you're dead!
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=5bd02f982a878312065b4d81d05a90bc41a98c6c
+Bug-Debian: https://bugs.debian.org/1028986
+
+If a Mines save file contains a move after the player has already
+died, this can lead to an assertion failure once there are more mines
+that covered squares. Better to just reject any move after the
+player's died.
+---
+ mines.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/mines.c
++++ b/mines.c
+@@ -2661,6 +2661,9 @@ static game_state *execute_move(const ga
+
+ return ret;
+ } else {
++ /* Dead players should stop trying to move. */
++ if (from->dead)
++ return NULL;
+ ret = dup_game(from);
+
+ while (*move) {
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0353-Towers-reject-descriptions-with-odd-characters-at-th.patch sgt-puzzles-20191231.79a5378/debian/patches/0353-Towers-reject-descriptions-with-odd-characters-at-th.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0353-Towers-reject-descriptions-with-odd-characters-at-th.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0353-Towers-reject-descriptions-with-odd-characters-at-th.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,27 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 9 Jan 2023 15:07:24 +0000
+Subject: [PATCH 353/389] Towers: reject descriptions with odd characters at
+ the end
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=15974d06bbaad287382c6eeb8deb7c6f3a627278
+Bug-Debian: https://bugs.debian.org/1028986
+
+Towers' new_game() causes an assertion failure on game description
+strings that contain spurious characters after a valid description, so
+validate_desc() should also refuse such a description. The problem
+could be demonstrated by editing the game description in the
+"Specific" dialogue box to add a '!' at the end, which caused
+"new_game: Assertion `!*p' failed.".
+---
+ towers.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/towers.c
++++ b/towers.c
+@@ -861,6 +861,7 @@ static const char *validate_desc(const g
+ return "Too much data to fit in grid";
+ }
+
++ if (*p) return "Rubbish at end of game description";
+ return NULL;
+ }
+
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0354-Tracks-make-sure-moves-are-valid-in-execute_move.patch sgt-puzzles-20191231.79a5378/debian/patches/0354-Tracks-make-sure-moves-are-valid-in-execute_move.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0354-Tracks-make-sure-moves-are-valid-in-execute_move.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0354-Tracks-make-sure-moves-are-valid-in-execute_move.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,37 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Mon, 9 Jan 2023 20:24:15 +0000
+Subject: [PATCH 354/389] Tracks: make sure moves are valid in execute_move()
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=5782e29db43034574763b1d10c48486c3e95f0d2
+Bug-Debian: https://bugs.debian.org/1028986
+
+Tracks allowed moves in execute_move() that shouldn't have been allowed,
+like changing the state of the edges of the board. This moves couldn't
+be generated by interpret_move(), but could be loaded from a save file.
+Now execute_move() uses the same ui_can_flip_*() functions as
+interpret_move() to decide whether a particular move is allowed. This
+should prevent some assertion failures when loading corrupted save
+files.
+---
+ tracks.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/tracks.c
++++ b/tracks.c
+@@ -2078,6 +2078,8 @@ static game_state *execute_move(const ga
+ f = (c == 'T' || c == 't') ? S_TRACK : S_NOTRACK;
+
+ if (d == 'S') {
++ if (!ui_can_flip_square(ret, x, y, f == S_NOTRACK))
++ goto badmove;
+ if (c == 'T' || c == 'N')
+ ret->sflags[y*w+x] |= f;
+ else
+@@ -2087,6 +2089,8 @@ static game_state *execute_move(const ga
+ unsigned df = 1<<i;
+
+ if (MOVECHAR(df) == d) {
++ if (!ui_can_flip_edge(ret, x, y, df, f == S_NOTRACK))
++ goto badmove;
+ if (c == 'T' || c == 'N')
+ S_E_SET(ret, x, y, df, f);
+ else
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0360-Tracks-let-solve-make-illegal-moves.patch sgt-puzzles-20191231.79a5378/debian/patches/0360-Tracks-let-solve-make-illegal-moves.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0360-Tracks-let-solve-make-illegal-moves.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0360-Tracks-let-solve-make-illegal-moves.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,44 @@
+From: Chris Boyle <chris@boyle.name>
+Date: Wed, 18 Jan 2023 20:58:31 +0000
+Subject: [PATCH 360/389] Tracks: let solve make illegal moves
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=b5e02b0b9c1b35b0b907bf6d63e62b9fafd9cb7e
+Bug-Debian: https://bugs.debian.org/1028986
+
+Not only does it set the outer edges to NOTRACK, but it may also overwrite
+any mistakes the user has previously made elsewhere. Otherwise, the entire
+solve is rejected ("Solve unavailable" error on Android) if the user has
+made a single mistake, which is inconsistent with the other games.
+
+This may be giving a free pass to corrupted moves that occur after a solve,
+so this may still want tightening up in some way, but it's still limited to
+squares within the grid, so I agree with Ben's assessment that this is
+likely not to be exploitable.
+
+Fixes #584
+
+(cherry picked from Android port, commit
+33bd14fb6f7cd760e7218fffd90f3a266b1f4123)
+---
+ tracks.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/tracks.c
++++ b/tracks.c
+@@ -2078,7 +2078,7 @@ static game_state *execute_move(const ga
+ f = (c == 'T' || c == 't') ? S_TRACK : S_NOTRACK;
+
+ if (d == 'S') {
+- if (!ui_can_flip_square(ret, x, y, f == S_NOTRACK))
++ if (!ui_can_flip_square(ret, x, y, f == S_NOTRACK) && !ret->used_solve)
+ goto badmove;
+ if (c == 'T' || c == 'N')
+ ret->sflags[y*w+x] |= f;
+@@ -2089,7 +2089,7 @@ static game_state *execute_move(const ga
+ unsigned df = 1<<i;
+
+ if (MOVECHAR(df) == d) {
+- if (!ui_can_flip_edge(ret, x, y, df, f == S_NOTRACK))
++ if (!ui_can_flip_edge(ret, x, y, df, f == S_NOTRACK) && !ret->used_solve)
+ goto badmove;
+ if (c == 'T' || c == 'N')
+ S_E_SET(ret, x, y, df, f);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0361-Tracks-tighten-up-the-illegal-solve-submoves-fix.patch sgt-puzzles-20191231.79a5378/debian/patches/0361-Tracks-tighten-up-the-illegal-solve-submoves-fix.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0361-Tracks-tighten-up-the-illegal-solve-submoves-fix.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0361-Tracks-tighten-up-the-illegal-solve-submoves-fix.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,55 @@
+From: Simon Tatham <anakin@pobox.com>
+Date: Thu, 19 Jan 2023 12:47:55 +0000
+Subject: [PATCH 361/389] Tracks: tighten up the 'illegal solve submoves' fix.
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=b4aaa11943fb72b09fe173bc97bd9313ff94738c
+Bug-Debian: https://bugs.debian.org/1028986
+
+Chris mentioned in the commit message that there was a risk that
+illegal moves might be permitted when playing on after a solve. So
+I've changed the condition so that it depends only on whether the move
+_currently being executed_ is a solve, rather than whether there was a
+solve action anywhere in the undo history.
+
+(Also, wrapped overlong lines while I was here.)
+---
+ tracks.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/tracks.c
++++ b/tracks.c
+@@ -2057,6 +2057,7 @@ static game_state *execute_move(const ga
+ int w = state->p.w, x, y, n, i;
+ char c, d;
+ unsigned f;
++ bool move_is_solve = false;
+ game_state *ret = dup_game(state);
+
+ /* this is breaking the bank on GTK, which vsprintf's into a fixed-size buffer
+@@ -2067,6 +2068,7 @@ static game_state *execute_move(const ga
+ c = *move;
+ if (c == 'S') {
+ ret->used_solve = true;
++ move_is_solve = true;
+ move++;
+ } else if (c == 'T' || c == 't' || c == 'N' || c == 'n') {
+ /* set track, clear track; set notrack, clear notrack */
+@@ -2078,7 +2080,8 @@ static game_state *execute_move(const ga
+ f = (c == 'T' || c == 't') ? S_TRACK : S_NOTRACK;
+
+ if (d == 'S') {
+- if (!ui_can_flip_square(ret, x, y, f == S_NOTRACK) && !ret->used_solve)
++ if (!ui_can_flip_square(ret, x, y, f == S_NOTRACK) &&
++ !move_is_solve)
+ goto badmove;
+ if (c == 'T' || c == 'N')
+ ret->sflags[y*w+x] |= f;
+@@ -2089,7 +2092,8 @@ static game_state *execute_move(const ga
+ unsigned df = 1<<i;
+
+ if (MOVECHAR(df) == d) {
+- if (!ui_can_flip_edge(ret, x, y, df, f == S_NOTRACK) && !ret->used_solve)
++ if (!ui_can_flip_edge(ret, x, y, df, f == S_NOTRACK) &&
++ !move_is_solve)
+ goto badmove;
+ if (c == 'T' || c == 'N')
+ S_E_SET(ret, x, y, df, f);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/0362-Allow-repeated-solve-operations-in-Guess.patch sgt-puzzles-20191231.79a5378/debian/patches/0362-Allow-repeated-solve-operations-in-Guess.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/0362-Allow-repeated-solve-operations-in-Guess.patch 1970-01-01 01:00:00.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/0362-Allow-repeated-solve-operations-in-Guess.patch 2023-04-23 21:12:49.000000000 +0200
@@ -0,0 +1,37 @@
+From: Ben Harris <bjh21@bjh21.me.uk>
+Date: Thu, 19 Jan 2023 20:26:23 +0000
+Subject: [PATCH 362/389] Allow repeated "solve" operations in Guess
+Origin: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=27c97c0ffdda0b91fecf155f3bc29b32ed806bb2
+Bug-Debian: https://bugs.debian.org/1028986
+
+Since using the "solve" option doesn't consume a guess, it's safe to
+allow it to occur multiple times. Without this, selecting "solve" a
+second time causes an assertion failure because solve() returns a move
+string that's rejected by execute_move().
+
+Possible solve() could instead refuse to solve an already-solved
+puzzle, but that seems needlessly pedantic.
+
+[fixes c84af670b52f09e9e47587584c0559c508d4a37d]
+---
+ guess.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/guess.c
++++ b/guess.c
+@@ -924,13 +924,13 @@ static game_state *execute_move(const ga
+ game_state *ret;
+ const char *p;
+
+- /* No moves are allowed once the game is solved. */
+- if (from->solved) return NULL;
+ if (!strcmp(move, "S")) {
+ ret = dup_game(from);
+ ret->solved = -1;
+ return ret;
+ } else if (move[0] == 'G') {
++ /* No guesses are allowed once the game is solved. */
++ if (from->solved) return NULL;
+ p = move+1;
+
+ ret = dup_game(from);
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/102_fix-pearl-min-dimensions.diff sgt-puzzles-20191231.79a5378/debian/patches/102_fix-pearl-min-dimensions.diff
--- sgt-puzzles-20191231.79a5378/debian/patches/102_fix-pearl-min-dimensions.diff 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/102_fix-pearl-min-dimensions.diff 2023-04-23 21:12:52.000000000 +0200
@@ -1,8 +1,8 @@
From: Ben Hutchings <ben@decadent.org.uk>
Date: Fri, 10 Aug 2018 07:00:06 +0100
Subject: pearl: Require width or height to be at least 6 for Tricky
-
Bug-Debian: https://bugs.debian.org/667963
+Applied-Upstream: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=49dbf1f60dc5c6bcb6b3ffadf89e7e0c14106638
Josh Triplett reported:
> If I ask pearl to generate a 5x5 tricky puzzle, it runs forever.
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/fix-ftbfs-with-gcc-6.patch sgt-puzzles-20191231.79a5378/debian/patches/fix-ftbfs-with-gcc-6.patch
--- sgt-puzzles-20191231.79a5378/debian/patches/fix-ftbfs-with-gcc-6.patch 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/fix-ftbfs-with-gcc-6.patch 2023-04-23 21:12:52.000000000 +0200
@@ -1,8 +1,8 @@
From: Ben Hutchings <ben@decadent.org.uk>
Date: Thu, 30 Jun 2016 14:43:16 +0200
Subject: Fix FTBFS with gcc 6
-
Bug-Debian: https://bugs.debian.org/811577
+Applied-Upstream: https://git.tartarus.org/?p=simon/puzzles.git;a=commitdiff;h=0dc46506ca85eb49299fc62a4362c8a4a655e320
gcc 6 warns about statements that are indented as if they were meant to be
part of a preceding conditional block. In this case I don't think that was
diff -Nru sgt-puzzles-20191231.79a5378/debian/patches/series sgt-puzzles-20191231.79a5378/debian/patches/series
--- sgt-puzzles-20191231.79a5378/debian/patches/series 2020-02-02 15:30:23.000000000 +0100
+++ sgt-puzzles-20191231.79a5378/debian/patches/series 2023-04-23 21:12:52.000000000 +0200
@@ -13,3 +13,132 @@
0013-bridges-Fix-off-by-one-in-WITHIN.patch
0014-pattern-Fix-build-failure-with-fortify-and-gcc-9-on-.patch
0015-Update-German-translation-thanks-to-Helge-Kreutzmann.patch
+0007-Mines-add-validation-for-negative-mine-count.patch
+0023-Galaxies-fix-assertion-failure-when-adding-out-of-bo.patch
+0028-Filling-fix-assertion-failure-in-3x1-game-generation.patch
+0081-Map-add-missing-sresize-in-new_game_desc.patch
+0114-Add-more-validation-to-midend-deserialisation-routin.patch
+0115-Correct-and-enable-the-range-check-on-statepos-when-.patch
+0121-Add-an-assertion-to-check-the-format-of-encoded-para.patch
+0122-Add-assertions-that-game-descriptions-consist-only-o.patch
+0123-Hex-encode-non-ASCII-random-seeds-in-save-files.patch
+0124-Assert-that-everything-written-to-a-save-file-is-pri.patch
+0129-Build-fix-take-declarations-out-of-for-loops.patch
+0246-galaxies-Use-the-same-code-for-handling-all-dropped-.patch
+0263-magnets-Area-constraints-fix-message.patch
+0266-lightup-Ban-2x2-with-either-4-way-type.patch
+0267-Remove-_-introduced-from-Android-port.patch
+0269-Solo-Set-max-difficulty-for-small-jigsaw-puzzles.patch
+0285-Add-a-macro-of-an-upper-bound-on-the-formatted-lengt.patch
+0299-Guess-Don-t-allow-any-moves-once-the-game-is-solved.patch
+0300-Guess-validate-peg-colours-in-decode_ui.patch
+0302-Netslide-Reject-moves-wider-than-the-grid.patch
+0303-Sixteen-limit-length-of-moves.patch
+0304-Undead-check-for-valid-commands-in-execute_move.patch
+0305-Undead-fix-buffer-overrun-in-M-command.patch
+0306-Correct-RANGECHECK-macro-in-Black-Box.patch
+0307-Range-check-normal-moves-in-Undead.patch
+0308-Range-check-record-lengths-when-deserialising-games.patch
+0309-Don-t-load-too-many-states-just-because-there-s-no-S.patch
+0310-Palisade-forbid-moves-that-remove-grid-edges.patch
+0311-Last-ditch-maximum-size-limit-for-Bridges.patch
+0312-Last-ditch-grid-size-limit-for-Dominosa.patch
+0313-Last-ditch-grid-size-limit-for-Galaxies.patch
+0314-Last-ditch-grid-size-limit-for-Fifteen.patch
+0315-Last-ditch-maximum-size-limit-for-Flip.patch
+0316-Last-ditch-grid-size-limit-for-Flood.patch
+0317-Insist-that-Flood-grids-must-have-non-zero-size.patch
+0318-Last-ditch-grid-size-limit-for-Inertia.patch
+0319-Last-ditch-maximum-size-limit-for-Light-Up.patch
+0320-Limit-maximum-grid-size-in-Loopy.patch
+0321-Last-ditch-maximum-size-limit-for-Magnets.patch
+0322-Last-ditch-maximum-size-limit-for-Map.patch
+0323-Last-ditch-maximum-size-limit-for-Mines.patch
+0324-Also-check-for-tiny-grids-in-Mines.patch
+0326-Last-ditch-maximum-size-limit-for-Net.patch
+0327-Last-ditch-maximum-size-limit-for-Netslide.patch
+0328-Integer-overflow-protection-in-Pattern.patch
+0329-Last-ditch-maximum-size-limit-for-Palisade.patch
+0330-Last-ditch-maximum-size-limit-for-Pearl.patch
+0331-Last-ditch-maximum-size-limit-for-Pegs.patch
+0332-Also-limit-Pegs-to-at-least-1x1-even-when-not-doing-.patch
+0333-Last-ditch-maximum-size-limit-for-Same-Game.patch
+0334-Last-ditch-maximum-size-limit-for-Signpost.patch
+0335-Last-ditch-maximum-size-limit-for-Sixteen.patch
+0336-Limit-size-of-puzzle-in-Tents-to-avoid-integer-overf.patch
+0337-Last-ditch-maximum-size-limit-for-Tracks.patch
+0338-Last-ditch-maximum-size-limit-for-Twiddle.patch
+0339-Adjust-Undead-upper-grid-size-limit-to-avoid-overflo.patch
+0340-Last-ditch-point-count-limit-for-Untangle.patch
+0341-Black-Box-correct-order-of-validation-checks-for-F-c.patch
+0342-Palisade-don-t-leak-memory-on-a-bad-move.patch
+0343-Don-t-allow-negative-clues-in-Pattern.patch
+0344-When-loading-don-t-decode_ui-unless-we-have-a-UI.patch
+0346-Palisade-remove-assertion-from-decode_ui.patch
+0347-Same-Game-reject-moves-with-unexpected-characters-in.patch
+0349-Filling-validate-length-of-auto-solve-move-strings.patch
+0350-Tighten-Bridges-validate_desc.patch
+0351-Untangle-forbid-descriptions-that-connect-a-node-to-.patch
+0352-Mines-No-moving-once-you-re-dead.patch
+0353-Towers-reject-descriptions-with-odd-characters-at-th.patch
+0354-Tracks-make-sure-moves-are-valid-in-execute_move.patch
+0360-Tracks-let-solve-make-illegal-moves.patch
+0361-Tracks-tighten-up-the-illegal-solve-submoves-fix.patch
+0362-Allow-repeated-solve-operations-in-Guess.patch
+0001-Black-Box-reject-negative-ball-counts-in-game_params.patch
+0002-Add-validate_params-bounds-checks-in-a-few-more-game.patch
+0006-Don-t-allow-Bridges-games-with-2-islands.patch
+0007-Forbid-moves-that-fill-with-the-current-colour-in-Fl.patch
+0008-Cleanly-reject-ill-formed-solve-moves-in-Flood.patch
+0009-Don-t-segfault-on-premature-solve-moves-in-Mines.patch
+0010-Limit-number-of-mines-in-Mines-game-description.patch
+0011-Validate-the-number-of-pegs-and-holes-in-a-Pegs-game.patch
+0017-Mines-forbid-moves-that-flag-or-unflag-an-exposed-sq.patch
+0018-Mines-Don-t-check-if-the-player-has-won-if-they-ve-a.patch
+0019-Avoid-invalid-moves-when-solving-Tracks.patch
+0020-Fix-move-validation-in-Netslide.patch
+0021-Tighten-validation-of-Tents-game-descriptions.patch
+0022-Dominosa-require-the-two-halves-of-a-domino-to-be-ad.patch
+0023-Forbid-lines-off-the-grid-in-Pearl.patch
+0024-Tolerate-incorrect-solutions-in-Inertia.patch
+0025-Palisade-replace-dfs_dsf-with-a-simple-iteration.patch
+0026-latin_solver_alloc-handle-clashing-numbers-in-input-.patch
+0027-Pearl-fix-assertion-failure-on-bad-puzzle.patch
+0028-Pearl-fix-bounds-check-in-previous-commit.patch
+0029-Unequal-Don-t-insist-that-solve-moves-must-actually-.patch
+0030-Range-Don-t-fail-an-assertion-on-an-all-black-board.patch
+0031-Limit-width-and-height-to-SHRT_MAX-in-Mines.patch
+0032-Mines-Add-assertions-to-range-check-conversions-to-s.patch
+0033-Unequal-fix-sense-error-in-latin_solver_alloc-fix.patch
+0034-Forbid-impossible-moves-in-Bridges.patch
+0035-Forbid-game-descriptions-with-joined-islands-in-Brid.patch
+0037-Check-state-is-valid-at-the-end-of-a-move-in-Pearl.patch
+0038-Cleanly-reject-more-ill-formed-solve-moves-in-Flood.patch
+0039-Don-t-allow-moves-that-change-the-constraints-in-Une.patch
+0041-Fix-memory-leaks-in-Keen-s-validate_desc.patch
+0043-Don-t-leak-grids-in-Loopy-s-validate_desc.patch
+0044-Remember-to-free-the-to_draw-member-from-Net-s-draws.patch
+0045-Undead-check-the-return-value-of-sscanf-in-execute_m.patch
+0046-Don-t-leak-duplicate-edges-in-Untangle.patch
+0047-Remember-to-free-the-numcolours-array-from-Pattern-s.patch
+0049-Twiddle-don-t-read-off-the-end-of-parameter-strings-.patch
+0050-Loopy-free-the-grid-description-string-if-it-s-inval.patch
+0052-Avoid-division-by-zero-in-Cube-grid-size-checks.patch
+0055-Validate-that-save-file-values-are-ASCII-mostly.patch
+0056-More-validation-of-solve-moves-in-Flood.patch
+0058-Make-sure-that-moves-in-Flood-use-only-valid-colours.patch
+0059-Tighten-grid-size-limit-in-Mines.patch
+0061-Solo-cope-with-pencil-marks-when-tilesize-1.patch
+0065-Tracks-set-drag_s-x-y-even-if-starting-off-grid.patch
+0080-Undead-be-a-bit-more-careful-about-sprintf-buffer-si.patch
+0090-Fix-memory-leak-in-midend_game_id_int.patch
+0092-Flood-don-t-read-off-the-end-of-some-parameter-strin.patch
+0101-Be-more-careful-with-type-of-left-operand-of.patch
+0102-Map-reduce-maximum-size.patch
+0103-Correctly-handle-some-short-save-files.patch
+0104-Inertia-insist-that-solutions-must-be-non-empty.patch
+0115-Galaxies-fix-recursion-depth-limit-in-solver.patch
+0138-Correct-a-range-check-in-Magnets-layout-verification.patch
+0139-Magnets-add-a-check-that-magnets-don-t-wrap-between-.patch
+0155-Net-assert-that-cx-and-cy-are-in-range-in-compute_ac.patch
+0159-Don-t-allow-zero-clues-in-Pattern.patch
--- End Message ---