changelog shortlog tags branches changeset files file revisions raw help

Mercurial > hg > plan9front / annotate sys/src/cmd/gs/src/zvmem.c

changeset 7253: 986e26228cfe
parent: eaccc3e8d226
author: cinap_lenrek@felloff.net
date: Thu, 23 May 2019 14:59:28 +0200
permissions: -rw-r--r--
description: gs: apply fixes for CVE-2018-16509 (thanks jsmoody)
taruti@0 1
 /* Copyright (C) 1989, 1995, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
taruti@0 2
   
taruti@0 3
   This software is provided AS-IS with no warranty, either express or
taruti@0 4
   implied.
taruti@0 5
   
taruti@0 6
   This software is distributed under license and may not be copied,
taruti@0 7
   modified or distributed except as expressly authorized under the terms
taruti@0 8
   of the license contained in the file LICENSE in this distribution.
taruti@0 9
   
taruti@0 10
   For more information about licensing, please refer to
taruti@0 11
   http://www.ghostscript.com/licensing/. For information on
taruti@0 12
   commercial licensing, go to http://www.artifex.com/licensing/ or
taruti@0 13
   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
taruti@0 14
   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
taruti@0 15
 */
taruti@0 16
 
taruti@0 17
 /* $Id: zvmem.c,v 1.8 2004/08/04 19:36:13 stefan Exp $ */
taruti@0 18
 /* "Virtual memory" operators */
taruti@0 19
 #include "ghost.h"
taruti@0 20
 #include "gsstruct.h"
taruti@0 21
 #include "oper.h"
taruti@0 22
 #include "estack.h"		/* for checking in restore */
taruti@0 23
 #include "ialloc.h"
taruti@0 24
 #include "idict.h"		/* ditto */
taruti@0 25
 #include "igstate.h"
taruti@0 26
 #include "isave.h"
taruti@0 27
 #include "dstack.h"
taruti@0 28
 #include "stream.h"		/* for files.h */
taruti@0 29
 #include "files.h"		/* for e-stack processing */
taruti@0 30
 #include "store.h"
taruti@0 31
 #include "gsmatrix.h"		/* for gsstate.h */
taruti@0 32
 #include "gsstate.h"
taruti@0 33
 
taruti@0 34
 /* Define whether we validate memory before/after save/restore. */
taruti@0 35
 /* Note that we only actually do this if DEBUG is set and -Z? is selected. */
taruti@0 36
 private const bool I_VALIDATE_BEFORE_SAVE = true;
taruti@0 37
 private const bool I_VALIDATE_AFTER_SAVE = true;
taruti@0 38
 private const bool I_VALIDATE_BEFORE_RESTORE = true;
taruti@0 39
 private const bool I_VALIDATE_AFTER_RESTORE = true;
taruti@0 40
 
taruti@0 41
 /* 'Save' structure */
taruti@0 42
 typedef struct vm_save_s vm_save_t;
taruti@0 43
 struct vm_save_s {
taruti@0 44
     gs_state *gsave;		/* old graphics state */
taruti@0 45
 };
taruti@0 46
 
taruti@0 47
 gs_private_st_ptrs1(st_vm_save, vm_save_t, "savetype",
taruti@0 48
 		    vm_save_enum_ptrs, vm_save_reloc_ptrs, gsave);
taruti@0 49
 
taruti@0 50
 /* Clean up the stacks and validate storage. */
taruti@0 51
 private void
taruti@0 52
 ivalidate_clean_spaces(i_ctx_t *i_ctx_p)
taruti@0 53
 {
taruti@0 54
     if (gs_debug_c('?')) {
taruti@0 55
 	ref_stack_cleanup(&d_stack);
taruti@0 56
 	ref_stack_cleanup(&e_stack);
taruti@0 57
 	ref_stack_cleanup(&o_stack);
taruti@0 58
 	ivalidate_spaces();
taruti@0 59
     }
taruti@0 60
 }
taruti@0 61
 
taruti@0 62
 /* - save <save> */
taruti@0 63
 int
taruti@0 64
 zsave(i_ctx_t *i_ctx_p)
taruti@0 65
 {
taruti@0 66
     os_ptr op = osp;
taruti@0 67
     uint space = icurrent_space;
taruti@0 68
     vm_save_t *vmsave;
taruti@0 69
     ulong sid;
taruti@0 70
     int code;
taruti@0 71
     gs_state *prev;
taruti@0 72
 
taruti@0 73
     if (I_VALIDATE_BEFORE_SAVE)
taruti@0 74
 	ivalidate_clean_spaces(i_ctx_p);
taruti@0 75
     ialloc_set_space(idmemory, avm_local);
taruti@0 76
     vmsave = ialloc_struct(vm_save_t, &st_vm_save, "zsave");
taruti@0 77
     ialloc_set_space(idmemory, space);
taruti@0 78
     if (vmsave == 0)
taruti@0 79
 	return_error(e_VMerror);
taruti@0 80
     sid = alloc_save_state(idmemory, vmsave);
taruti@0 81
     if (sid == 0) {
taruti@0 82
 	ifree_object(vmsave, "zsave");
taruti@0 83
 	return_error(e_VMerror);
taruti@0 84
     }
taruti@0 85
     if_debug2('u', "[u]vmsave 0x%lx, id = %lu\n",
taruti@0 86
 	      (ulong) vmsave, (ulong) sid);
taruti@0 87
     code = gs_gsave_for_save(igs, &prev);
taruti@0 88
     if (code < 0)
taruti@0 89
 	return code;
taruti@0 90
     code = gs_gsave(igs);
taruti@0 91
     if (code < 0)
taruti@0 92
 	return code;
taruti@0 93
     vmsave->gsave = prev;
taruti@0 94
     push(1);
taruti@0 95
     make_tav(op, t_save, 0, saveid, sid);
taruti@0 96
     if (I_VALIDATE_AFTER_SAVE)
taruti@0 97
 	ivalidate_clean_spaces(i_ctx_p);
taruti@0 98
     return 0;
taruti@0 99
 }
taruti@0 100
 
taruti@0 101
 /* <save> restore - */
taruti@0 102
 private int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *);
taruti@0 103
 private int restore_check_stack(const ref_stack_t *, const alloc_save_t *, bool);
taruti@0 104
 private void restore_fix_stack(ref_stack_t *, const alloc_save_t *, bool);
cinap_lenrek@7253 105
 
taruti@0 106
 int
cinap_lenrek@7253 107
 restore_check_save(i_ctx_t *i_ctx_p, alloc_save_t **asave)
taruti@0 108
 {
taruti@0 109
     os_ptr op = osp;
cinap_lenrek@7253 110
     int code = restore_check_operand(op, asave, idmemory);
taruti@0 111
     if (code < 0)
taruti@0 112
 	return code;
taruti@0 113
     if_debug2('u', "[u]vmrestore 0x%lx, id = %lu\n",
cinap_lenrek@7253 114
 	      (ulong) alloc_save_client_data(*asave),
taruti@0 115
 	      (ulong) op->value.saveid);
taruti@0 116
     if (I_VALIDATE_BEFORE_RESTORE)
taruti@0 117
 	ivalidate_clean_spaces(i_ctx_p);
taruti@0 118
     /* Check the contents of the stacks. */
taruti@0 119
     osp--;
cinap_lenrek@7253 120
     if ((code = restore_check_stack(&o_stack, *asave, false)) < 0 ||
cinap_lenrek@7253 121
         (code = restore_check_stack(&e_stack, *asave, true)) < 0 ||
cinap_lenrek@7253 122
         (code = restore_check_stack(&d_stack, *asave, false)) < 0
cinap_lenrek@7253 123
         ) {
cinap_lenrek@7253 124
         osp++;
cinap_lenrek@7253 125
         return code;
cinap_lenrek@7253 126
     }
cinap_lenrek@7253 127
     osp++;
cinap_lenrek@7253 128
     return 0;
cinap_lenrek@7253 129
 }
taruti@0 130
 
cinap_lenrek@7253 131
 /*
cinap_lenrek@7253 132
  * the emantics of restore differ slightly between Level 1 and
cinap_lenrek@7253 133
  * Level 2 and later - the latter inclues restoring the device
cinap_lenrek@7253 134
  * state (whilst Level 1 didn't have "page devices" as such).
cinap_lenrek@7253 135
  * Hence we have two restore operators - one here (Level 1)
cinap_lenrek@7253 136
  * and one in zdevice2.c (Level 2+). For that reason, the
cinap_lenrek@7253 137
  * operand checking and guts of the restore operation are
cinap_lenrek@7253 138
  * separated so both implementations can use them to best
cinap_lenrek@7253 139
  * effect.
cinap_lenrek@7253 140
  */
cinap_lenrek@7253 141
 int
cinap_lenrek@7253 142
 dorestore(i_ctx_t *i_ctx_p, alloc_save_t *asave)
cinap_lenrek@7253 143
 {
cinap_lenrek@7253 144
     os_ptr op = osp;
cinap_lenrek@7253 145
     bool last;
cinap_lenrek@7253 146
     vm_save_t *vmsave;
cinap_lenrek@7253 147
     int code;
cinap_lenrek@7253 148
 
cinap_lenrek@7253 149
     osp--;
cinap_lenrek@7253 150
 
taruti@0 151
     /* Reset l_new in all stack entries if the new save level is zero. */
taruti@0 152
     /* Also do some special fixing on the e-stack. */
taruti@0 153
     restore_fix_stack(&o_stack, asave, false);
taruti@0 154
     restore_fix_stack(&e_stack, asave, true);
taruti@0 155
     restore_fix_stack(&d_stack, asave, false);
taruti@0 156
     /* Iteratively restore the state of memory, */
taruti@0 157
     /* also doing a grestoreall at each step. */
taruti@0 158
     do {
taruti@0 159
 	vmsave = alloc_save_client_data(alloc_save_current(idmemory));
taruti@0 160
 	/* Restore the graphics state. */
taruti@0 161
 	gs_grestoreall_for_restore(igs, vmsave->gsave);
taruti@0 162
 	/*
taruti@0 163
 	 * If alloc_save_space decided to do a second save, the vmsave
taruti@0 164
 	 * object was allocated one save level less deep than the
taruti@0 165
 	 * current level, so ifree_object won't actually free it;
taruti@0 166
 	 * however, it points to a gsave object that definitely
taruti@0 167
 	 * *has* been freed.  In order not to trip up the garbage
taruti@0 168
 	 * collector, we clear the gsave pointer now.
taruti@0 169
 	 */
taruti@0 170
 	vmsave->gsave = 0;
taruti@0 171
 	/* Now it's safe to restore the state of memory. */
taruti@0 172
 	last = alloc_restore_state_step(asave);
taruti@0 173
     }
taruti@0 174
     while (!last);
taruti@0 175
     {
taruti@0 176
 	uint space = icurrent_space;
taruti@0 177
 
taruti@0 178
 	ialloc_set_space(idmemory, avm_local);
taruti@0 179
 	ifree_object(vmsave, "zrestore");
taruti@0 180
 	ialloc_set_space(idmemory, space);
taruti@0 181
     }
taruti@0 182
     dict_set_top();		/* reload dict stack cache */
taruti@0 183
     if (I_VALIDATE_AFTER_RESTORE)
taruti@0 184
 	ivalidate_clean_spaces(i_ctx_p);
taruti@0 185
     /* If the i_ctx_p LockFilePermissions is true, but the userparams */
taruti@0 186
     /* we just restored is false, we need to make sure that we do not */
taruti@0 187
     /* cause an 'invalidaccess' in setuserparams. Temporarily set     */
taruti@0 188
     /* LockFilePermissions false until the gs_lev2.ps can do a        */
taruti@0 189
     /* setuserparams from the restored userparam dictionary.          */
cinap_lenrek@7253 190
     /* NOTE: This is safe to do here, since the restore has           */
cinap_lenrek@7253 191
     /* successfully completed - this should never come before any     */
cinap_lenrek@7253 192
     /* operation that can trigger an error                            */
taruti@0 193
     i_ctx_p->LockFilePermissions = false;
taruti@0 194
     return 0;
taruti@0 195
 }
cinap_lenrek@7253 196
 
cinap_lenrek@7253 197
 int
cinap_lenrek@7253 198
 zrestore(i_ctx_t *i_ctx_p)
cinap_lenrek@7253 199
 {
cinap_lenrek@7253 200
     alloc_save_t *asave;
cinap_lenrek@7253 201
     int code = restore_check_save(i_ctx_p, &asave);
cinap_lenrek@7253 202
     if (code < 0)
cinap_lenrek@7253 203
         return code;
cinap_lenrek@7253 204
     return dorestore(i_ctx_p, asave);
cinap_lenrek@7253 205
 }
cinap_lenrek@7253 206
 
taruti@0 207
 /* Check the operand of a restore. */
taruti@0 208
 private int
taruti@0 209
 restore_check_operand(os_ptr op, alloc_save_t ** pasave,
taruti@0 210
 		      gs_dual_memory_t *idmem)
taruti@0 211
 {
taruti@0 212
     vm_save_t *vmsave;
taruti@0 213
     ulong sid;
taruti@0 214
     alloc_save_t *asave;
taruti@0 215
 
taruti@0 216
     check_type(*op, t_save);
taruti@0 217
     vmsave = r_ptr(op, vm_save_t);
taruti@0 218
     if (vmsave == 0)		/* invalidated save */
taruti@0 219
 	return_error(e_invalidrestore);
taruti@0 220
     sid = op->value.saveid;
taruti@0 221
     asave = alloc_find_save(idmem, sid);
taruti@0 222
     if (asave == 0)
taruti@0 223
 	return_error(e_invalidrestore);
taruti@0 224
     *pasave = asave;
taruti@0 225
     return 0;
taruti@0 226
 }
taruti@0 227
 /* Check a stack to make sure all its elements are older than a save. */
taruti@0 228
 private int
taruti@0 229
 restore_check_stack(const ref_stack_t * pstack, const alloc_save_t * asave,
taruti@0 230
 		    bool is_estack)
taruti@0 231
 {
taruti@0 232
     ref_stack_enum_t rsenum;
taruti@0 233
 
taruti@0 234
     ref_stack_enum_begin(&rsenum, pstack);
taruti@0 235
     do {
taruti@0 236
 	const ref *stkp = rsenum.ptr;
taruti@0 237
 	uint size = rsenum.size;
taruti@0 238
 
taruti@0 239
 	for (; size; stkp++, size--) {
taruti@0 240
 	    const void *ptr;
taruti@0 241
 
taruti@0 242
 	    switch (r_type(stkp)) {
taruti@0 243
 		case t_array:
taruti@0 244
 		    ptr = stkp->value.refs;
taruti@0 245
 		    break;
taruti@0 246
 		case t_dictionary:
taruti@0 247
 		    ptr = stkp->value.pdict;
taruti@0 248
 		    break;
taruti@0 249
 		case t_file:
taruti@0 250
 		    /* Don't check executable or closed literal */
taruti@0 251
 		    /* files on the e-stack. */
taruti@0 252
 		    {
taruti@0 253
 			stream *s;
taruti@0 254
 
taruti@0 255
 			if (is_estack &&
taruti@0 256
 			    (r_has_attr(stkp, a_executable) ||
taruti@0 257
 			     file_is_invalid(s, stkp))
taruti@0 258
 			    )
taruti@0 259
 			    continue;
taruti@0 260
 		    }
taruti@0 261
 		    ptr = stkp->value.pfile;
taruti@0 262
 		    break;
taruti@0 263
 		case t_name:
taruti@0 264
 		    /* Names are special because of how they are allocated. */
taruti@0 265
 		    if (alloc_name_is_since_save((const gs_memory_t *)pstack->memory,
taruti@0 266
 						 stkp, asave))
taruti@0 267
 			return_error(e_invalidrestore);
taruti@0 268
 		    continue;
taruti@0 269
 		case t_string:
taruti@0 270
 		    /* Don't check empty executable strings */
taruti@0 271
 		    /* on the e-stack. */
taruti@0 272
 		    if (r_size(stkp) == 0 &&
taruti@0 273
 			r_has_attr(stkp, a_executable) && is_estack
taruti@0 274
 			)
taruti@0 275
 			continue;
taruti@0 276
 		    ptr = stkp->value.bytes;
taruti@0 277
 		    break;
taruti@0 278
 		case t_mixedarray:
taruti@0 279
 		case t_shortarray:
taruti@0 280
 		    ptr = stkp->value.packed;
taruti@0 281
 		    break;
taruti@0 282
 		case t_device:
taruti@0 283
 		    ptr = stkp->value.pdevice;
taruti@0 284
 		    break;
taruti@0 285
 		case t_fontID:
taruti@0 286
 		case t_struct:
taruti@0 287
 		case t_astruct:
taruti@0 288
 		    ptr = stkp->value.pstruct;
taruti@0 289
 		    break;
taruti@0 290
 		default:
taruti@0 291
 		    continue;
taruti@0 292
 	    }
taruti@0 293
 	    if (alloc_is_since_save(ptr, asave))
taruti@0 294
 		return_error(e_invalidrestore);
taruti@0 295
 	}
taruti@0 296
     } while (ref_stack_enum_next(&rsenum));
taruti@0 297
     return 0;		/* OK */
taruti@0 298
 }
taruti@0 299
 /*
taruti@0 300
  * If the new save level is zero, fix up the contents of a stack
taruti@0 301
  * by clearing the l_new bit in all the entries (since we can't tolerate
taruti@0 302
  * values with l_new set if the save level is zero).
taruti@0 303
  * Also, in any case, fix up the e-stack by replacing empty executable
taruti@0 304
  * strings and closed executable files that are newer than the save
taruti@0 305
  * with canonical ones that aren't.
taruti@0 306
  *
taruti@0 307
  * Note that this procedure is only called if restore_check_stack succeeded.
taruti@0 308
  */
taruti@0 309
 private void
taruti@0 310
 restore_fix_stack(ref_stack_t * pstack, const alloc_save_t * asave,
taruti@0 311
 		  bool is_estack)
taruti@0 312
 {
taruti@0 313
     ref_stack_enum_t rsenum;
taruti@0 314
 
taruti@0 315
     ref_stack_enum_begin(&rsenum, pstack);
taruti@0 316
     do {
taruti@0 317
 	ref *stkp = rsenum.ptr;
taruti@0 318
 	uint size = rsenum.size;
taruti@0 319
 
taruti@0 320
 	for (; size; stkp++, size--) {
taruti@0 321
 	    r_clear_attrs(stkp, l_new);		/* always do it, no harm */
taruti@0 322
 	    if (is_estack) {
taruti@0 323
 		ref ofile;
taruti@0 324
 
taruti@0 325
 		ref_assign(&ofile, stkp);
taruti@0 326
 		switch (r_type(stkp)) {
taruti@0 327
 		    case t_string:
taruti@0 328
 			if (r_size(stkp) == 0 &&
taruti@0 329
 			    alloc_is_since_save(stkp->value.bytes,
taruti@0 330
 						asave)
taruti@0 331
 			    ) {
taruti@0 332
 			    make_empty_const_string(stkp,
taruti@0 333
 						    avm_foreign);
taruti@0 334
 			    break;
taruti@0 335
 			}
taruti@0 336
 			continue;
taruti@0 337
 		    case t_file:
taruti@0 338
 			if (alloc_is_since_save(stkp->value.pfile,
taruti@0 339
 						asave)
taruti@0 340
 			    ) {
taruti@0 341
 			    make_invalid_file(stkp);
taruti@0 342
 			    break;
taruti@0 343
 			}
taruti@0 344
 			continue;
taruti@0 345
 		    default:
taruti@0 346
 			continue;
taruti@0 347
 		}
taruti@0 348
 		r_copy_attrs(stkp, a_all | a_executable,
taruti@0 349
 			     &ofile);
taruti@0 350
 	    }
taruti@0 351
 	}
taruti@0 352
     } while (ref_stack_enum_next(&rsenum));
taruti@0 353
 }
taruti@0 354
 
taruti@0 355
 /* - vmstatus <save_level> <vm_used> <vm_maximum> */
taruti@0 356
 private int
taruti@0 357
 zvmstatus(i_ctx_t *i_ctx_p)
taruti@0 358
 {
taruti@0 359
     os_ptr op = osp;
taruti@0 360
     gs_memory_status_t mstat, dstat;
taruti@0 361
 
taruti@0 362
     gs_memory_status(imemory, &mstat);
taruti@0 363
     if (imemory == imemory_global) {
taruti@0 364
 	gs_memory_status_t sstat;
taruti@0 365
 
taruti@0 366
 	gs_memory_status(imemory_system, &sstat);
taruti@0 367
 	mstat.allocated += sstat.allocated;
taruti@0 368
 	mstat.used += sstat.used;
taruti@0 369
     }
taruti@0 370
     gs_memory_status(imemory->non_gc_memory, &dstat);
taruti@0 371
     push(3);
taruti@0 372
     make_int(op - 2, imemory_save_level(iimemory_local));
taruti@0 373
     make_int(op - 1, mstat.used);
taruti@0 374
     make_int(op, mstat.allocated + dstat.allocated - dstat.used);
taruti@0 375
     return 0;
taruti@0 376
 }
taruti@0 377
 
taruti@0 378
 /* ------ Non-standard extensions ------ */
taruti@0 379
 
taruti@0 380
 /* <save> .forgetsave - */
taruti@0 381
 private int
taruti@0 382
 zforgetsave(i_ctx_t *i_ctx_p)
taruti@0 383
 {
taruti@0 384
     os_ptr op = osp;
taruti@0 385
     alloc_save_t *asave;
taruti@0 386
     vm_save_t *vmsave;
taruti@0 387
     int code = restore_check_operand(op, &asave, idmemory);
taruti@0 388
 
taruti@0 389
     if (code < 0)
taruti@0 390
 	return 0;
taruti@0 391
     vmsave = alloc_save_client_data(asave);
taruti@0 392
     /* Reset l_new in all stack entries if the new save level is zero. */
taruti@0 393
     restore_fix_stack(&o_stack, asave, false);
taruti@0 394
     restore_fix_stack(&e_stack, asave, false);
taruti@0 395
     restore_fix_stack(&d_stack, asave, false);
taruti@0 396
     /*
taruti@0 397
      * Forget the gsaves, by deleting the bottom gstate on
taruti@0 398
      * the current stack and the top one on the saved stack and then
taruti@0 399
      * concatenating the stacks together.
taruti@0 400
      */
taruti@0 401
     {
taruti@0 402
 	gs_state *pgs = igs;
taruti@0 403
 	gs_state *last;
taruti@0 404
 
taruti@0 405
 	while (gs_state_saved(last = gs_state_saved(pgs)) != 0)
taruti@0 406
 	    pgs = last;
taruti@0 407
 	gs_state_swap_saved(last, vmsave->gsave);
taruti@0 408
 	gs_grestore(last);
taruti@0 409
 	gs_grestore(last);
taruti@0 410
     }
taruti@0 411
     /* Forget the save in the memory manager. */
taruti@0 412
     alloc_forget_save(asave);
taruti@0 413
     {
taruti@0 414
 	uint space = icurrent_space;
taruti@0 415
 
taruti@0 416
 	ialloc_set_space(idmemory, avm_local);
taruti@0 417
 	/* See above for why we clear the gsave pointer here. */
taruti@0 418
 	vmsave->gsave = 0;
taruti@0 419
 	ifree_object(vmsave, "zrestore");
taruti@0 420
 	ialloc_set_space(idmemory, space);
taruti@0 421
     }
taruti@0 422
     pop(1);
taruti@0 423
     return 0;
taruti@0 424
 }
taruti@0 425
 
taruti@0 426
 /* ------ Initialization procedure ------ */
taruti@0 427
 
taruti@0 428
 const op_def zvmem_op_defs[] =
taruti@0 429
 {
taruti@0 430
     {"1.forgetsave", zforgetsave},
taruti@0 431
     {"1restore", zrestore},
taruti@0 432
     {"0save", zsave},
taruti@0 433
     {"0vmstatus", zvmstatus},
taruti@0 434
     op_def_end(0)
taruti@0 435
 };