From d3537a54740d78c5895ec83694a07b3e4f616f61 Mon Sep 17 00:00:00 2001 From: Chris Liddell Date: Wed, 5 Dec 2018 12:22:13 +0000 Subject: [PATCH] Bug700317: Address .force* operators exposure Fix logic for an older change: unlike almost every other function in gs, dict_find_string() returns 1 on success 0 or <0 on failure. The logic for this case was wrong. Sanitize op stack for error conditions We save the stacks to an array and store the array for the error handler to access. For SAFER, we traverse the array, and deep copy any op arrays (procedures). As we make these copies, we check for operators that do *not* exist in systemdict, when we find one, we replace the operator with a name object (of the form "/--opname--"). Any transient procedures that call .force* operators (i.e. for conditionals or loops) make them executeonly. Harden some uses of .force* operators by adding a few immediate evalutions CVE-2019-6116 --- Resource/Init/gs_diskn.ps | 2 +- Resource/Init/gs_dps1.ps | 8 ++-- Resource/Init/gs_fntem.ps | 4 +- Resource/Init/gs_fonts.ps | 34 ++++++++--------- Resource/Init/gs_init.ps | 10 ++--- Resource/Init/gs_lev2.ps | 11 +++--- Resource/Init/gs_pdfwr.ps | 2 +- Resource/Init/gs_res.ps | 4 +- Resource/Init/gs_setpd.ps | 2 +- Resource/Init/pdf_base.ps | 13 ++++--- Resource/Init/pdf_draw.ps | 16 ++++---- Resource/Init/pdf_font.ps | 6 +-- Resource/Init/pdf_main.ps | 4 +- Resource/Init/pdf_ops.ps | 7 ++-- psi/int.mak | 3 +- psi/interp.c | 10 ++++- psi/istack.c | 78 +++++++++++++++++++++++++++++++++++++++ psi/istack.h | 3 ++ 18 files changed, 157 insertions(+), 60 deletions(-) diff --git a/Resource/Init/gs_diskn.ps b/Resource/Init/gs_diskn.ps index fd694bc44..8bf205420 100644 --- a/Resource/Init/gs_diskn.ps +++ b/Resource/Init/gs_diskn.ps @@ -51,7 +51,7 @@ systemdict begin mark 5 1 roll ] mark exch { { } forall } forall ] //systemdict /.searchabledevs 2 index .forceput exch .setglobal - } + } executeonly if } .bind executeonly odef % must be bound and hidden for .forceput diff --git a/Resource/Init/gs_dps1.ps b/Resource/Init/gs_dps1.ps index ec5db61b9..b75ea14e7 100644 --- a/Resource/Init/gs_dps1.ps +++ b/Resource/Init/gs_dps1.ps @@ -74,18 +74,18 @@ level2dict begin } odef % undefinefont has to take local/global VM into account. /undefinefont % undefinefont - - { .FontDirectory 1 .argindex .forceundef % FontDirectory is readonly + { //.FontDirectory 1 .argindex .forceundef % FontDirectory is readonly .currentglobal { % Current mode is global; delete from local directory too. //systemdict /LocalFontDirectory .knownget - { 1 index .forceundef } % LocalFontDirectory is readonly + { 1 index .forceundef } executeonly % LocalFontDirectory is readonly if } { % Current mode is local; if there was a shadowed global % definition, copy it into the local directory. //systemdict /SharedFontDirectory .knownget { 1 index .knownget - { .FontDirectory 2 index 3 -1 roll { put } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse } % readonly + { //.FontDirectory 2 index 3 -1 roll { put } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse } % readonly if } if @@ -126,7 +126,7 @@ level2dict begin } ifelse } forall - pop counttomark 2 idiv { .forceundef } repeat pop % readonly + pop counttomark 2 idiv { .forceundef } executeonly repeat pop % readonly } if //SharedFontDirectory exch .forcecopynew pop diff --git a/Resource/Init/gs_fntem.ps b/Resource/Init/gs_fntem.ps index c1f7651f1..6eb672a68 100644 --- a/Resource/Init/gs_fntem.ps +++ b/Resource/Init/gs_fntem.ps @@ -401,12 +401,12 @@ currentdict end def .forceput % FontInfo can be read-only. pop % bool exit - } if + } executeonly if dup /FontInfo get % bool /GlyphNames2Unicode /Unicode /Decoding findresource .forceput % FontInfo can be read-only. exit - } loop + } executeonly loop exch setglobal } .bind executeonly odef % must be bound and hidden for .forceput diff --git a/Resource/Init/gs_fonts.ps b/Resource/Init/gs_fonts.ps index 803faca49..27f4135f6 100644 --- a/Resource/Init/gs_fonts.ps +++ b/Resource/Init/gs_fonts.ps @@ -374,7 +374,7 @@ FONTPATH length 0 eq { (%END FONTPATH) .skipeof } if /.setnativefontmapbuilt { % set whether we've been run dup type /booleantype eq { systemdict exch /.nativefontmapbuilt exch .forceput - } + } executeonly {pop} ifelse } .bind executeonly odef @@ -516,7 +516,7 @@ buildfontdict 3 /.buildfont3 cvx put if } if - dup .FontDirectory 4 -2 roll { .growput } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse % readonly + dup //.FontDirectory 4 -2 roll { .growput } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse % readonly % If the font originated as a resource, register it. currentfile .currentresourcefile eq { dup .registerfont } if readonly @@ -943,7 +943,7 @@ $error /SubstituteFont { } put % Try to find a font using only the present contents of Fontmap. /.tryfindfont { % .tryfindfont true % .tryfindfont false - .FontDirectory 1 index .fontknownget + //.FontDirectory 1 index .fontknownget { % Already loaded exch pop //true } @@ -975,7 +975,7 @@ $error /SubstituteFont { } put { % Font with a procedural definition exec % The procedure will load the font. % Check to make sure this really happened. - .FontDirectory 1 index .knownget + //.FontDirectory 1 index .knownget { exch pop //true exit } if } @@ -1007,11 +1007,11 @@ $error /SubstituteFont { } put { 2 index gcheck currentglobal 2 copy eq { pop pop .forceput - } { + } executeonly { 5 1 roll setglobal dup length string copy .forceput setglobal - } ifelse + } executeonly ifelse } .bind executeonly odef % must be bound and hidden for .forceput % Attempt to load a font from a file. @@ -1081,11 +1081,11 @@ $error /SubstituteFont { } put % because it's different depending on language level. .currentglobal exch /.setglobal .systemvar exec % Remove the fake definition, if any. - .FontDirectory 3 index .forceundef % readonly - 1 index (r) file .loadfont .FontDirectory exch + //.FontDirectory 3 index .forceundef % readonly + 1 index (r) file .loadfont //.FontDirectory exch /.setglobal .systemvar exec - } - { .loadfont .FontDirectory + } executeonly + { .loadfont //.FontDirectory } ifelse % Stack: fontname fontfilename fontdirectory @@ -1105,7 +1105,7 @@ $error /SubstituteFont { } put dup 3 index .fontknownget { dup /PathLoad 4 index .putgstringcopy 4 1 roll pop pop pop //true exit - } if + } executeonly if % Maybe the file had a different FontName. % See if we can get a FontName from the file, and if so, @@ -1119,8 +1119,8 @@ $error /SubstituteFont { } put % Stack: origfontname fontdirectory filefontname fontdict 3 -1 roll pop % Stack: origfontname filefontname fontdict - dup /FontName get dup FontDirectory exch .forceundef - GlobalFontDirectory exch .forceundef + dup /FontName get dup //.FontDirectory exch .forceundef + /GlobalFontDirectory .systemvar exch .forceundef dup length dict .copydict dup 3 index /FontName exch put 2 index exch definefont exch @@ -1134,9 +1134,9 @@ $error /SubstituteFont { } put ifelse % Stack: origfontname fontdict exch pop //true exit % Stack: fontdict - } + } executeonly if pop % Stack: origfontname fontdirectory path - } + } executeonly if pop pop % Stack: origfontname % The font definitely did not load correctly. @@ -1176,10 +1176,10 @@ currentdict /.putgstringcopy .undef { { pop dup type /stringtype eq { cvn } if - .FontDirectory 1 index known not { + //.FontDirectory 1 index known not { 2 dict dup /FontName 3 index put dup /FontType 1 put - .FontDirectory 3 1 roll { put } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse % readonly + //.FontDirectory 3 1 roll { put } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse % readonly } { pop } ifelse diff --git a/Resource/Init/gs_init.ps b/Resource/Init/gs_init.ps index d733124b9..d9a0829f7 100644 --- a/Resource/Init/gs_init.ps +++ b/Resource/Init/gs_init.ps @@ -1168,8 +1168,8 @@ errordict /unknownerror .undef }ifelse }forall noaccess pop - systemdict /.setsafeerrors .forceundef - systemdict /.SAFERERRORLIST .forceundef + //systemdict /.setsafeerrors .forceundef + //systemdict /.SAFERERRORLIST .forceundef } bind executeonly odef SAFERERRORS {.setsafererrors} if @@ -2114,7 +2114,7 @@ currentdict /tempfilepaths undef /.locksafe { .locksafe_userparams - systemdict /getenv {pop //false} .forceput + //systemdict /getenv {pop //false} .forceput % setpagedevice has the side effect of clearing the page, but % we will just document that. Using setpagedevice keeps the device % properties and pagedevice .LockSafetyParams in agreement even @@ -2357,7 +2357,7 @@ SAFER { .setsafeglobal } if % Update the copy of the user parameters. mark .currentuserparams counttomark 2 idiv { userparams 3 1 roll .forceput % userparams is read-only - } repeat pop + } executeonly repeat pop % Turn on idiom recognition, if available. currentuserparams /IdiomRecognition known { /IdiomRecognition //true .definepsuserparam @@ -2376,7 +2376,7 @@ SAFER { .setsafeglobal } if % Remove real system params from pssystemparams. mark .currentsystemparams counttomark 2 idiv { pop pssystemparams exch .forceundef - } repeat pop + } executeonly repeat pop } if % Set up AlignToPixels : diff --git a/Resource/Init/gs_lev2.ps b/Resource/Init/gs_lev2.ps index 44fe61956..0f0d57331 100644 --- a/Resource/Init/gs_lev2.ps +++ b/Resource/Init/gs_lev2.ps @@ -154,7 +154,8 @@ end % protect top level of parameters that we copied dup type dup /arraytype eq exch /stringtype eq or { readonly } if /userparams .systemvar 3 1 roll .forceput % userparams is read-only - } { + } executeonly + { pop pop } ifelse } forall @@ -224,7 +225,7 @@ end % protect top level parameters that we copied dup type dup /arraytype eq exch /stringtype eq or { readonly } if //pssystemparams 3 1 roll .forceput % pssystemparams is read-only - } + } executeonly { pop pop } ifelse @@ -934,7 +935,7 @@ mark dup /PaintProc get 1 index /Implementation known not { 1 index dup /Implementation //null .forceput readonly pop - } if + } executeonly if exec }.bind odef @@ -958,7 +959,7 @@ mark dup /PaintProc get 1 index /Implementation known not { 1 index dup /Implementation //null .forceput readonly pop - } if + } executeonly if /UNROLLFORMS where {/UNROLLFORMS get}{false}ifelse not %% [CTM] <
> PaintProc .beginform - { @@ -1005,7 +1006,7 @@ mark %% Form dictioanry using the /Implementation key). 1 dict dup /FormID 4 -1 roll put 1 index exch /Implementation exch .forceput readonly pop - } + } executeonly ifelse } { diff --git a/Resource/Init/gs_pdfwr.ps b/Resource/Init/gs_pdfwr.ps index 58e75d3a4..b425103d1 100644 --- a/Resource/Init/gs_pdfwr.ps +++ b/Resource/Init/gs_pdfwr.ps @@ -650,7 +650,7 @@ currentdict /.pdfmarkparams .undef } ifelse } bind .makeoperator .forceput systemdict /.pdf_hooked_DSC_Creator //true .forceput - } if + } executeonly if pop } if } { diff --git a/Resource/Init/gs_res.ps b/Resource/Init/gs_res.ps index 8eb8bb0e5..d9b34599e 100644 --- a/Resource/Init/gs_res.ps +++ b/Resource/Init/gs_res.ps @@ -152,7 +152,7 @@ setglobal % use .forceput / .forcedef later to replace the dummy, % empty .Instances dictionary with the real one later. readonly - } { + }{ /defineresource cvx /typecheck signaloperror } ifelse } bind executeonly odef @@ -424,7 +424,7 @@ status { % As noted above, Category dictionaries are read-only, % so we have to use .forcedef here. /.Instances 1 index .forcedef % Category dict is read-only - } if + } executeonly if } { .LocalInstances dup //.emptydict eq { pop 3 dict localinstancedict Category 2 index put diff --git a/Resource/Init/gs_setpd.ps b/Resource/Init/gs_setpd.ps index e22597ebb..7875d1f2f 100644 --- a/Resource/Init/gs_setpd.ps +++ b/Resource/Init/gs_setpd.ps @@ -634,7 +634,7 @@ NOMEDIAATTRS { SETPDDEBUG { (Rolling back.) = pstack flush } if 3 index 2 index 3 -1 roll .forceput 4 index 1 index .knownget - { 4 index 3 1 roll .forceput } + { 4 index 3 1 roll .forceput } executeonly { 3 index exch .undef } ifelse } bind executeonly odef diff --git a/Resource/Init/pdf_base.ps b/Resource/Init/pdf_base.ps index b45e98031..73127296c 100644 --- a/Resource/Init/pdf_base.ps +++ b/Resource/Init/pdf_base.ps @@ -130,26 +130,29 @@ currentdict /num-chars-dict .undef /.pdfexectoken { % .pdfexectoken ? PDFDEBUG { - pdfdict /PDFSTEPcount known not { pdfdict /PDFSTEPcount 1 .forceput } if + pdfdict /PDFSTEPcount known not { pdfdict /PDFSTEPcount 1 .forceput } executeonly if PDFSTEP { pdfdict /PDFtokencount 2 copy .knownget { 1 add } { 1 } ifelse .forceput PDFSTEPcount 1 gt { pdfdict /PDFSTEPcount PDFSTEPcount 1 sub .forceput - } { + } executeonly + { dup ==only ( step # ) print PDFtokencount =only ( ? ) print flush 1 //false .outputpage (%stdin) (r) file 255 string readline { token { exch pop pdfdict /PDFSTEPcount 3 -1 roll .forceput - } { + } executeonly + { pdfdict /PDFSTEPcount 1 .forceput - } ifelse % token + } executeonly ifelse % token } { pop /PDFSTEP //false def % EOF on stdin } ifelse % readline } ifelse % PDFSTEPcount > 1 - } { + } executeonly + { dup ==only () = flush } ifelse % PDFSTEP } if % PDFDEBUG diff --git a/Resource/Init/pdf_draw.ps b/Resource/Init/pdf_draw.ps index 6b0ba93e1..40c6ac80a 100644 --- a/Resource/Init/pdf_draw.ps +++ b/Resource/Init/pdf_draw.ps @@ -1118,14 +1118,14 @@ currentdict end readonly def pdfdict /.Qqwarning_issued //true .forceput .setglobal pdfformaterror - } ifelse + } executeonly ifelse } { currentglobal pdfdict gcheck .setglobal pdfdict /.Qqwarning_issued //true .forceput .setglobal pdfformaterror - } ifelse + } executeonly ifelse end } ifelse } loop @@ -1141,14 +1141,14 @@ currentdict end readonly def pdfdict /.Qqwarning_issued //true .forceput .setglobal pdfformaterror - } ifelse + } executeonly ifelse } { currentglobal pdfdict gcheck .setglobal pdfdict /.Qqwarning_issued //true .forceput .setglobal pdfformaterror - } ifelse + } executeonly ifelse } if pop @@ -2350,9 +2350,10 @@ currentdict /last-ditch-bpc-csp undef /IncrementAppearanceNumber { pdfdict /AppearanceNumber .knownget { 1 add pdfdict /AppearanceNumber 3 -1 roll .forceput - }{ + } executeonly + { pdfdict /AppearanceNumber 0 .forceput - } ifelse + } executeonly ifelse }bind executeonly odef /MakeAppearanceName { @@ -2510,7 +2511,8 @@ currentdict /last-ditch-bpc-csp undef %% want to preserve it. pdfdict /.PreservePDFForm false .forceput /q cvx /execform cvx 5 -2 roll - }{ + } executeonly + { /q cvx /PDFexecform cvx 5 -2 roll } ifelse diff --git a/Resource/Init/pdf_font.ps b/Resource/Init/pdf_font.ps index bea9ea95a..4cd62b9d9 100644 --- a/Resource/Init/pdf_font.ps +++ b/Resource/Init/pdf_font.ps @@ -714,7 +714,7 @@ currentdict end readonly def pop pop pop currentdict /.stackdepth .forceundef currentdict /.dstackdepth .forceundef - } + } executeonly {pop pop pop} ifelse @@ -1232,7 +1232,7 @@ currentdict /eexec_pdf_param_dict .undef (\n **** Warning: Type 3 glyph has unbalanced q/Q operators \(too many q's\)\n Output may be incorrect.\n) pdfformatwarning pdfdict /.Qqwarning_issued //true .forceput - } if + } executeonly if Q } repeat Q @@ -2016,7 +2016,7 @@ currentdict /CMap_read_dict undef /CIDFallBack /CIDFont findresource } if exit - } if + } executeonly if } if } if diff --git a/Resource/Init/pdf_main.ps b/Resource/Init/pdf_main.ps index 00da47a48..37e69b39a 100644 --- a/Resource/Init/pdf_main.ps +++ b/Resource/Init/pdf_main.ps @@ -2701,14 +2701,14 @@ currentdict /PDF2PS_matrix_key undef pdfdict /.Qqwarning_issued //true .forceput .setglobal pdfformaterror - } ifelse + } executeonly ifelse } { currentglobal pdfdict gcheck .setglobal pdfdict /.Qqwarning_issued //true .forceput .setglobal pdfformaterror - } ifelse + } executeonly ifelse } if } if pop diff --git a/Resource/Init/pdf_ops.ps b/Resource/Init/pdf_ops.ps index 8672d617f..aa0964139 100644 --- a/Resource/Init/pdf_ops.ps +++ b/Resource/Init/pdf_ops.ps @@ -184,14 +184,14 @@ currentdict /gput_always_allow .undef pdfdict /.Qqwarning_issued //true .forceput .setglobal pdfformaterror - } ifelse + } executeonly ifelse } { currentglobal pdfdict gcheck .setglobal pdfdict /.Qqwarning_issued //true .forceput .setglobal pdfformaterror - } ifelse + } executeonly ifelse } if } bind executeonly odef @@ -439,7 +439,8 @@ currentdict /gput_always_allow .undef dup type /booleantype eq { .currentSMask type /dicttype eq { .currentSMask /Processed 2 index .forceput - } { + } executeonly + { .setSMask }ifelse }{ diff --git a/psi/int.mak b/psi/int.mak index 6ab5bf006..6b349cb04 100644 --- a/psi/int.mak +++ b/psi/int.mak @@ -204,7 +204,8 @@ $(PSOBJ)iparam.$(OBJ) : $(PSSRC)iparam.c $(GH)\ $(PSOBJ)istack.$(OBJ) : $(PSSRC)istack.c $(GH) $(memory__h)\ $(ierrors_h) $(gsstruct_h) $(gsutil_h)\ $(ialloc_h) $(istack_h) $(istkparm_h) $(istruct_h) $(iutil_h) $(ivmspace_h)\ - $(store_h) $(INT_MAK) $(MAKEDIRS) + $(store_h) $(icstate_h) $(iname_h) $(dstack_h) $(idict_h) \ + $(INT_MAK) $(MAKEDIRS) $(PSCC) $(PSO_)istack.$(OBJ) $(C_) $(PSSRC)istack.c $(PSOBJ)iutil.$(OBJ) : $(PSSRC)iutil.c $(GH) $(math__h) $(memory__h) $(string__h)\ diff --git a/psi/interp.c b/psi/interp.c index 6dc0ddae1..f6c45bbe2 100644 --- a/psi/interp.c +++ b/psi/interp.c @@ -703,7 +703,7 @@ again: * i.e. it's an internal operator we have hidden */ code = dict_find_string(systemdict, (const char *)bufptr, &tobj); - if (code < 0) { + if (code <= 0) { buf[0] = buf[1] = buf[rlen + 2] = buf[rlen + 3] = '-'; rlen += 4; bufptr = buf; @@ -761,6 +761,7 @@ copy_stack(i_ctx_t *i_ctx_p, const ref_stack_t * pstack, int skip, ref * arr) uint size = ref_stack_count(pstack) - skip; uint save_space = ialloc_space(idmemory); int code, i; + ref *safety, *safe; if (size > 65535) size = 65535; @@ -778,6 +779,13 @@ copy_stack(i_ctx_t *i_ctx_p, const ref_stack_t * pstack, int skip, ref * arr) make_null(&arr->value.refs[i]); } } + if (pstack == &o_stack && dict_find_string(systemdict, "SAFETY", &safety) > 0 && + dict_find_string(safety, "safe", &safe) > 0 && r_has_type(safe, t_boolean) && + safe->value.boolval == true) { + code = ref_stack_array_sanitize(i_ctx_p, arr, arr); + if (code < 0) + return code; + } ialloc_set_space(idmemory, save_space); return code; } diff --git a/psi/istack.c b/psi/istack.c index 8fe151fa5..f1a3e5115 100644 --- a/psi/istack.c +++ b/psi/istack.c @@ -27,6 +27,10 @@ #include "iutil.h" #include "ivmspace.h" /* for local/global test */ #include "store.h" +#include "icstate.h" +#include "iname.h" +#include "dstack.h" +#include "idict.h" /* Forward references */ static void init_block(ref_stack_t *pstack, const ref *pblock_array, @@ -294,6 +298,80 @@ ref_stack_store_check(const ref_stack_t *pstack, ref *parray, uint count, return 0; } +int +ref_stack_array_sanitize(i_ctx_t *i_ctx_p, ref *sarr, ref *darr) +{ + int i, code; + ref obj, arr2; + ref *pobj2; + gs_memory_t *mem = (gs_memory_t *)idmemory->current; + + if (!r_is_array(sarr) || !r_has_type(darr, t_array)) + return_error(gs_error_typecheck); + + for (i = 0; i < r_size(sarr); i++) { + code = array_get(mem, sarr, i, &obj); + if (code < 0) + make_null(&obj); + switch(r_type(&obj)) { + case t_operator: + { + int index = op_index(&obj); + + if (index > 0 && index < op_def_count) { + const byte *data = (const byte *)(op_index_def(index)->oname + 1); + if (dict_find_string(systemdict, (const char *)data, &pobj2) <= 0) { + byte *s = gs_alloc_bytes(mem, strlen((char *)data) + 5, "ref_stack_array_sanitize"); + if (s) { + s[0] = '\0'; + strcpy((char *)s, "--"); + strcpy((char *)s + 2, (char *)data); + strcpy((char *)s + strlen((char *)data) + 2, "--"); + } + else { + s = (byte *)data; + } + code = name_ref(imemory, s, strlen((char *)s), &obj, 1); + if (code < 0) make_null(&obj); + if (s != data) + gs_free_object(mem, s, "ref_stack_array_sanitize"); + } + } + else { + make_null(&obj); + } + ref_assign(darr->value.refs + i, &obj); + break; + } + case t_array: + case t_shortarray: + case t_mixedarray: + { + int attrs = r_type_attrs(&obj) & (a_write | a_read | a_execute | a_executable); + /* We only want to copy executable arrays */ + if (attrs & (a_execute | a_executable)) { + code = ialloc_ref_array(&arr2, attrs, r_size(&obj), "ref_stack_array_sanitize"); + if (code < 0) { + make_null(&arr2); + } + else { + code = ref_stack_array_sanitize(i_ctx_p, &obj, &arr2); + } + ref_assign(darr->value.refs + i, &arr2); + } + else { + ref_assign(darr->value.refs + i, &obj); + } + break; + } + default: + ref_assign(darr->value.refs + i, &obj); + } + } + return 0; +} + + /* * Store the top 'count' elements of a stack, starting 'skip' elements below * the top, into an array, with or without store/undo checking. age=-1 for diff --git a/psi/istack.h b/psi/istack.h index 051dcbe21..54be405ad 100644 --- a/psi/istack.h +++ b/psi/istack.h @@ -129,6 +129,9 @@ int ref_stack_store(const ref_stack_t *pstack, ref *parray, uint count, uint skip, int age, bool check, gs_dual_memory_t *idmem, client_name_t cname); +int +ref_stack_array_sanitize(i_ctx_t *i_ctx_p, ref *sarr, ref *darr); + /* * Pop the top N elements off a stack. * The number must not exceed the number of elements in use. -- 2.17.1