From: Ken Sharp <ken.sharp@artifex.com>
Date: Thu, 3 Aug 2023 12:09:50 +0100
Subject: PS interpreter review colour code for stack pointers
Origin: https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=f22a42f334b9e7965cb99429b1346346d3e7c2d3

No bug report but after the recent slew of fuzzing bugs exploiting
incorrect or missing stack checking, a desk check of the colour and
colour space code.

Mostly the code seems robust, there are a few improvements here; in a
couple of places add checks that the operand stack has at least the
expected number of operands. A number of places add checks that the
exec stack can hold an additional item before incrementing esp. Quite a
few places replace explicit decrement of esp with ref_stack_pop() in
order to ensure we don't fall off the bottom of a stack block.

[Salvatore Bonaccorso: Backport to 10.0.0 for context changes]
---
 psi/zcolor.c | 79 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 46 insertions(+), 33 deletions(-)

--- a/psi/zcolor.c
+++ b/psi/zcolor.c
@@ -205,6 +205,7 @@ zcurrentcolorspace(i_ctx_t * i_ctx_p)
             }
         }
         r_set_attrs(&stref, a_executable);
+        check_estack(1);
         esp++;
         ref_assign(esp, &stref);
         return o_push_estack;
@@ -1005,6 +1006,7 @@ static int setgrayspace(i_ctx_t * i_ctx_
                         memcpy(body, "/DefaultGray ..nosubstdevicetest",32);
                         make_string(&stref, a_all | icurrent_space, 32, body);
                         r_set_attrs(&stref, a_executable);
+                        check_estack(1);
                         esp++;
                         ref_assign(esp, &stref);
                         return o_push_estack;
@@ -1017,6 +1019,7 @@ static int setgrayspace(i_ctx_t * i_ctx_
                         memcpy(body, "{/DefaultGray /ColorSpace findresource} stopped",47);
                         make_string(&stref, a_all | icurrent_space, 47, body);
                         r_set_attrs(&stref, a_executable);
+                        check_estack(1);
                         esp++;
                         ref_assign(esp, &stref);
                         return o_push_estack;
@@ -1177,6 +1180,7 @@ static int grayvalidate(i_ctx_t *i_ctx_p
 {
     os_ptr op = osp;
 
+    check_op(1);
     if (!r_is_number(op))
         return_error(gs_error_typecheck);
 
@@ -1229,6 +1233,7 @@ static int setrgbspace(i_ctx_t * i_ctx_p
                         memcpy(body, "/DefaultRGB ..nosubstdevicetest",31);
                         make_string(&stref, a_all | icurrent_space, 31, body);
                         r_set_attrs(&stref, a_executable);
+                        check_estack(1);
                         esp++;
                         ref_assign(esp, &stref);
                         return o_push_estack;
@@ -1241,6 +1246,7 @@ static int setrgbspace(i_ctx_t * i_ctx_p
                         memcpy(body, "{/DefaultRGB /ColorSpace findresource} stopped", 46);
                         make_string(&stref, a_all | icurrent_space, 46, body);
                         r_set_attrs(&stref, a_executable);
+                        check_estack(1);
                         esp++;
                         ref_assign(esp, &stref);
                         return o_push_estack;
@@ -1577,6 +1583,7 @@ static int setcmykspace(i_ctx_t * i_ctx_
                         memcpy(body, "/DefaultCMYK ..nosubstdevicetest",32);
                         make_string(&stref, a_all | icurrent_space, 32, body);
                         r_set_attrs(&stref, a_executable);
+                        check_estack(1);
                         esp++;
                         ref_assign(esp, &stref);
                         return o_push_estack;
@@ -1589,6 +1596,7 @@ static int setcmykspace(i_ctx_t * i_ctx_
                         memcpy(body, "{/DefaultCMYK /ColorSpace findresource} stopped", 47);
                         make_string(&stref, a_all | icurrent_space, 47, body);
                         r_set_attrs(&stref, a_executable);
+                        check_estack(1);
                         esp++;
                         ref_assign(esp, &stref);
                         return o_push_estack;
@@ -3625,6 +3633,7 @@ static int septransform(i_ctx_t *i_ctx_p
 
     if (*usealternate && *stage == 0) {
         (*stage)++;
+        check_estack(1);
         esp++;
         code = array_get(imemory, sepspace, 3, &proc);
         if (code < 0)
@@ -3775,21 +3784,21 @@ static int devicencolorants_cont(i_ctx_t
     do {
         index = dict_next(pdict, index, (ref *)&space);
         if (index == -1) {
-            esp -= 4;
+            ref_stack_pop(&e_stack, 4);
             return o_pop_estack;
         }
 
         if (stage == 0) {
             code = gs_gsave(igs);
             if (code < 0) {
-                esp -= 4;
+                ref_stack_pop(&e_stack, 4);
                 return code;
             }
 
             code = validate_spaces(i_ctx_p, &space[1], &depth);
             if (code < 0) {
                 (void)gs_grestore(igs);
-                esp -= 4;
+                ref_stack_pop(&e_stack, 4);
                 return code;
             }
 
@@ -3810,7 +3819,7 @@ static int devicencolorants_cont(i_ctx_t
 
             if (code < 0) {
                 (void)gs_grestore(igs);
-                esp -= 4;
+                ref_stack_pop(&e_stack, 4);
                 return code;
             } else
                 return code;
@@ -3828,7 +3837,7 @@ static int devicencolorants_cont(i_ctx_t
              * colour space is the one we want.
              */
             if (!pgs->saved) {
-                esp -= 4;
+                ref_stack_pop(&e_stack, 4);
                 return gs_note_error(gs_error_unknownerror);
             }
             devn_cs = gs_currentcolorspace_inline(pgs->saved);
@@ -3864,7 +3873,7 @@ static int devicencolorants_cont(i_ctx_t
 
             code = gs_grestore(igs);
             if (code < 0) {
-                esp -= 4;
+                ref_stack_pop(&e_stack, 4);
                 return code;
             }
         }
@@ -3888,7 +3897,7 @@ static int devicenprocess_cont(i_ctx_t *
     if (stage == 0) {
         code = gs_gsave(igs);
         if (code < 0) {
-            esp -= 4;
+            ref_stack_pop(&e_stack, 4);
             return code;
         }
         /* If we get a continuation from a sub-procedure, we will want to come back
@@ -3908,7 +3917,7 @@ static int devicenprocess_cont(i_ctx_t *
 
         if (code < 0) {
             (void)gs_grestore(igs);
-            esp -= 4;
+            ref_stack_pop(&e_stack, 5);
             return code;
         } else
             return code;
@@ -3925,14 +3934,14 @@ static int devicenprocess_cont(i_ctx_t *
 
         code = gs_grestore(igs);
         if (code < 0) {
-            esp -= 4;
+            ref_stack_pop(&e_stack, 4);
             return code;
         }
         devn_cs = gs_currentcolorspace_inline(igs);
         devn_cs->params.device_n.devn_process_space = process;
     }
 
-    esp -= 4;
+    ref_stack_pop(&e_stack, 4);
     return o_pop_estack;
 }
 
@@ -4446,10 +4455,11 @@ static int devicentransform(i_ctx_t *i_c
     }
     if (*usealternate && *stage == 0) {
         (*stage)++;
-        esp++;
+        check_estack(1);
         code = array_get(imemory, devicenspace, 3, &proc);
         if (code < 0)
             return code;
+        esp++;
         *esp = proc;
         return o_push_estack;
     }
@@ -4616,17 +4626,19 @@ indexed_cont(i_ctx_t *i_ctx_p)
         int code = float_params(op, m, &r_ptr(&ep[csme_map], gs_indexed_map)->values[i * m]);
 
         if (code < 0) {
-            esp -= num_csme;
+            ref_stack_pop(&e_stack, num_csme);
             return code;
         }
         ref_stack_pop(&o_stack, m);
         op -= m;
         if (i == (int)ep[csme_hival].value.intval) {	/* All done. */
-            esp -= num_csme;
+            ref_stack_pop(&e_stack, num_csme);
             return o_pop_estack;
         }
     }
     push(1);
+    check_estack(2);
+    ep = esp;
     ep[csme_index].value.intval = ++i;
     make_int(op, i);
     make_op_estack(ep + 1, indexed_cont);
@@ -5450,6 +5462,7 @@ static int labvalidate(i_ctx_t *i_ctx_p,
 
     if (num_comps < 3)
         return_error(gs_error_stackunderflow);
+    check_op(3);
     op -= 2;
     for (i=0;i<3;i++) {
         if (!r_is_number(op))
@@ -6448,7 +6461,7 @@ setcolor_cont(i_ctx_t *i_ctx_p)
         for (i=0;i<=depth;i++) {
             code = get_space_object(i_ctx_p, parr, &obj);
             if (code < 0) {
-                esp -= 5;
+                ref_stack_pop(&e_stack, 5);
                 return code;
             }
 
@@ -6461,7 +6474,7 @@ setcolor_cont(i_ctx_t *i_ctx_p)
                 }
                 code = obj->alternateproc(i_ctx_p, parr, &parr, &CIESubst);
                 if (code < 0) {
-                    esp -= 5;
+                    ref_stack_pop(&e_stack, 5);
                     return code;
                 }
             }
@@ -6471,7 +6484,7 @@ setcolor_cont(i_ctx_t *i_ctx_p)
             make_int(&ep[-3], stack_depth);
             make_int(&ep[-1], stage);
             if (code < 0) {
-                esp -= 5;
+                ref_stack_pop(&e_stack, 5);
                 return code;
             }
             if (code != 0)
@@ -6491,7 +6504,7 @@ setcolor_cont(i_ctx_t *i_ctx_p)
     if (IsICC && depth == 0) {
         code = gx_set_dev_color(i_ctx_p->pgs);
         if (code < 0) {
-            esp -= 5;
+            ref_stack_pop(&e_stack, 5);
             return code;
         }
     }
@@ -6501,7 +6514,7 @@ setcolor_cont(i_ctx_t *i_ctx_p)
     /* This would be better done sooner, but we need the color space object first */
     check_op(i);
     pop(i);
-    esp -= 5;
+    ref_stack_pop(&e_stack, 5);
     return o_pop_estack;
 }
 /*
@@ -6550,18 +6563,18 @@ setcolorspace_cont(i_ctx_t *i_ctx_p)
         for (i = 0;i < depth;i++) {
             code = get_space_object(i_ctx_p, parr, &obj);
             if (code < 0) {
-                esp -= 5;
+                ref_stack_pop(&e_stack, 5);
                 return code;
             }
 
             if (i < (depth - 1)) {
                 if (!obj->alternateproc) {
-                    esp -= 5;
+                    ref_stack_pop(&e_stack, 5);
                     return_error(gs_error_typecheck);
                 }
                 code = obj->alternateproc(i_ctx_p, parr, &parr, &CIESubst);
                 if (code < 0) {
-                    esp -= 5;
+                    ref_stack_pop(&e_stack, 5);
                     return code;
                 }
             }
@@ -6571,7 +6584,7 @@ setcolorspace_cont(i_ctx_t *i_ctx_p)
         make_int(pstage, stage);
         if (code != 0) {
             if (code < 0 && code != gs_error_stackoverflow)
-                esp -= 5;
+                ref_stack_pop(&e_stack, 5);
             return code;
         }
         if (!cont) {
@@ -6582,7 +6595,7 @@ setcolorspace_cont(i_ctx_t *i_ctx_p)
     }
     if (code == 0) {
         /* Remove our next continuation and our data */
-        esp -= 5;
+        ref_stack_pop(&e_stack, 5);
         op = osp;
         istate->colorspace[0].array = *op;
         /* Remove the colorspace array form the operand stack */
@@ -6641,12 +6654,12 @@ setdevicecolor_cont(i_ctx_t *i_ctx_p)
                         break;
                 }
                 if (code < 0) {
-                    esp -= 3;
+                    ref_stack_pop(&e_stack, 3);
                     return code;
                 }
                 code = absolute_setcolorspace(i_ctx_p);
                 if (code < 0) {
-                    esp -= 3;
+                    ref_stack_pop(&e_stack, 3);
                     return code;
                 }
                 if (code != 0)
@@ -6656,14 +6669,14 @@ setdevicecolor_cont(i_ctx_t *i_ctx_p)
                 make_int(pstage, ++stage);
                 code = zsetcolor(i_ctx_p);
                 if (code < 0) {
-                    esp -= 3;
+                    ref_stack_pop(&e_stack, 3);
                     return code;
                 }
                 if (code != 0)
                     return code;
                 break;
             case 2:
-                esp -= 3;
+                ref_stack_pop(&e_stack, 3);
                 return o_pop_estack;
                 break;
         }
@@ -6860,7 +6873,7 @@ currentbasecolor_cont(i_ctx_t *i_ctx_p)
      * set the depth to at *least* 1.
      */
     if (depth < 1) {
-        esp -= 7;
+        ref_stack_pop(&e_stack, 7);
         return_error(gs_error_unknownerror);
     }
 
@@ -6881,18 +6894,18 @@ currentbasecolor_cont(i_ctx_t *i_ctx_p)
         for (i = 0;i < depth;i++) {
             code = get_space_object(i_ctx_p, parr, &obj);
             if (code < 0) {
-                esp -= 7;
+                ref_stack_pop(&e_stack, 7);
                 return code;
             }
 
             if (i < (depth - 1)) {
                 if (!obj->alternateproc) {
-                    esp -= 7;
+                    ref_stack_pop(&e_stack, 7);
                     return_error(gs_error_typecheck);
                 }
                 code = obj->alternateproc(i_ctx_p, parr, &parr, &CIESubst);
                 if (code < 0) {
-                    esp -= 7;
+                    ref_stack_pop(&e_stack, 7);
                     return code;
                 }
             }
@@ -6907,7 +6920,7 @@ currentbasecolor_cont(i_ctx_t *i_ctx_p)
         make_int(&ep[-2], ++depth);
     }
     /* Remove our next continuation and our data */
-    esp -= 7;
+    ref_stack_pop(&e_stack, 7);
     code = o_pop_estack;
     return code;
 }
