shithub: puzzles

Download patch

ref: d71bba1a17d6b228e7dd8b437dccbd3f6bc4698c
parent: fcda12f4b73e69841fd8957b8e33930c584093e5
author: Ben Harris <bjh21@bjh21.me.uk>
date: Wed Jan 11 18:11:46 EST 2023

Limit maximum grid size in Loopy

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.

--- 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 @@
 
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 #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)
 {
@@ -2476,6 +2597,18 @@
 #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)
 {
@@ -2556,6 +2689,18 @@
     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)
 {
@@ -2670,6 +2815,19 @@
     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)
 {
@@ -2839,6 +2997,19 @@
     return g;
 }
 
+static const char *grid_validate_params_compassdodecagonal(
+    int width, int height)
+{
+    int a = DODEC_A;
+    int b = DODEC_B;
+
+    if (width > INT_MAX / (4*a + 2*b) ||  /* xextent */
+        height > INT_MAX / (4*a + 2*b) || /* yextent */
+        width > INT_MAX / 18 / height)    /* max_dots */
+        return "Grid must not be unreasonably large";
+    return NULL;
+}
+
 static void grid_size_compassdodecagonal(int width, int height,
                                          int *tilesize, int *xextent, int *yextent)
 {
@@ -2985,6 +3156,17 @@
 
 #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)
 {
@@ -3187,6 +3369,16 @@
     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)
 {
@@ -3211,11 +3403,23 @@
 
 /* ----------- 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)
 {
--- a/grid.h
+++ b/grid.h
@@ -115,6 +115,8 @@
 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
@@ -684,6 +684,7 @@
 
 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 ||
@@ -692,6 +693,8 @@
     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