[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1035311: marked as done (bullseye-pu: package sgt-puzzles/20191231.79a5378-3+deb11u1)



Your message dated Sat, 07 Oct 2023 12:41:28 +0100
with message-id <84bb5ff8312f749ebe536897993782bf35aa1977.camel@adam-barratt.org.uk>
and subject line Closing opu requests for updates included in 11.8
has caused the Debian Bug report #1035311,
regarding bullseye-pu: package sgt-puzzles/20191231.79a5378-3+deb11u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1035311: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1035311
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
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 ---
--- Begin Message ---
Package: release.debian.org
Version: 11.8

Hi,

The updates referred to by each of these requests were included in
today's 11.8 bullseye point release.

Regards,

Adam

--- End Message ---

Reply to: