code wiki / (root) / nx_game_page_artifact_lib.nx

nx_game_page_artifact_lib.nx source

↩ module page · 810 lines · 35372 B

1// Native packaging by artifact reference; no publication or engine-acceptance claim. 2import "nx_fsops_lib.nx" 3import "nx_fs_create_lib.nx" 4import "nx_base64.nx" 5import "nx_sha256.nx" 6 7const GPA_HEADER_BYTES: i64 = 8 8 9struct GpaWasmCursor { 10 data: *u8, 11 size: i64, 12 pos: i64, 13} 14struct GpaMemory { 15 minimum: i64, 16 maximum: i64, 17 count: i64, 18} 19// WebAssembly u32 LEB128: at most five bytes, with only four payload bits in the last. 20func gpa_u32(c: *GpaWasmCursor) -> i64 { 21 var value: i64 = 0 22 var shift: i64 = 0 23 while shift < 35 { 24 if c.pos >= c.size { return -1 } 25 let b: i64 = c.data[c.pos] as i64 26 c.pos = c.pos+1 27 if shift == 28 { if b > 15 { return -1 } } 28 value = value | ((b & 127) << shift) 29 if (b & 128) == 0 { return value } 30 shift = shift+7 31 } 32 return -1 33} 34func gpa_wasm_header(data: *u8,n: i64) -> i64 { 35 if n < GPA_HEADER_BYTES { return 0 } 36 let header: *u8 = "\x00asm\x01\x00\x00\x00" as *u8 37 return gpa_same(data,GPA_HEADER_BYTES,header,GPA_HEADER_BYTES) 38} 39func gpa_wasm_name(c: *GpaWasmCursor,name: *u8) -> i64 { 40 let n: i64 = gpa_u32(c) 41 if n < 0 || n > c.size-c.pos { return 0 } 42 let ok: i64 = gpa_same((c.data as i64+c.pos) as *u8,n,name,vw_slen(name)) 43 c.pos = c.pos+n 44 return ok 45} 46// This packager supports the compiler's one-memory, no-other-import twin contract. 47// Other valid Wasm layouts require a separate supported contract, never silent guessing. 48func gpa_memory_section(data: *u8,n: i64,shared: i64,m: *GpaMemory) -> i64 { 49 let c: *GpaWasmCursor = sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 50 c.data=data; c.size=n; c.pos=0 51 var ok: i64 = 1 52 if gpa_u32(c) != 1 { ok=0 } 53 if shared == 1 { 54 if gpa_wasm_name(c,"env") != 1 { ok=0 } 55 if gpa_wasm_name(c,"memory") != 1 { ok=0 } 56 if gpa_u32(c) != 2 { ok=0 } 57 } 58 let flags: i64 = gpa_u32(c) 59 if shared == 1 { if flags != 3 { ok=0 } } else { if flags != 0 && flags != 1 { ok=0 } } 60 let minimum: i64 = gpa_u32(c) 61 var maximum: i64 = minimum 62 if (flags & 1) == 1 { maximum=gpa_u32(c) } 63 // 2^16 pages is the wasm32 address-space ceiling, not a workload budget. 64 if minimum <= 0 || maximum < minimum || maximum > 65536 { ok=0 } 65 if c.pos != n { ok=0 } 66 if m.count != 0 { ok=0 } 67 if ok == 1 { m.minimum=minimum; m.maximum=maximum; m.count=1 } 68 sys_munmap(c as *u8,__size_of(GpaWasmCursor)) 69 return ok 70} 71func gpa_body_section(c: *GpaWasmCursor,shared: i64,m: *GpaMemory,span: *GpaWasmCursor) -> i64 { 72 while c.pos < c.size { 73 let start: i64 = c.pos 74 let id: i64 = c.data[c.pos] as i64 75 c.pos=c.pos+1 76 let n: i64 = gpa_u32(c) 77 if n < 0 || n > c.size-c.pos { return -1 } 78 let payload: *u8 = (c.data as i64+c.pos) as *u8 79 c.pos=c.pos+n 80 if id == 2 || id == 5 { 81 if (shared == 1 && id != 2) || (shared == 0 && id != 5) { return -1 } 82 if gpa_memory_section(payload,n,shared,m) != 1 { return -1 } 83 } else { 84 span.data=(c.data as i64+start) as *u8; span.size=c.pos-start 85 return 1 86 } 87 } 88 return 0 89} 90// Equal non-memory sections bind code, exports, types and data to the same engine. 91// This is pair identity checking, not complete Wasm validation or runtime qualification. 92func gpa_pair(plain: *u8,pn: i64,twin: *u8,tn: i64,memory: *GpaMemory) -> i64 { 93 if gpa_wasm_header(plain,pn) != 1 || gpa_wasm_header(twin,tn) != 1 { return 0 } 94 let a: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 95 let b: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 96 let sa: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 97 let sb: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 98 let ma: *GpaMemory=sys_mmap(__size_of(GpaMemory)) as *GpaMemory 99 let mb: *GpaMemory=sys_mmap(__size_of(GpaMemory)) as *GpaMemory 100 a.data=plain; a.size=pn; a.pos=GPA_HEADER_BYTES 101 b.data=twin; b.size=tn; b.pos=GPA_HEADER_BYTES 102 var ok: i64=1 103 while ok == 1 { 104 let ra: i64=gpa_body_section(a,0,ma,sa) 105 let rb: i64=gpa_body_section(b,1,mb,sb) 106 if ra < 0 || rb < 0 || ra != rb { ok=0; break } 107 if ra == 0 { break } 108 if gpa_same(sa.data,sa.size,sb.data,sb.size) != 1 { ok=0 } 109 } 110 if ma.count != 1 || mb.count != 1 || ma.minimum != mb.minimum { ok=0 } 111 if ok == 1 { memory.minimum=mb.minimum; memory.maximum=mb.maximum; memory.count=1 } 112 sys_munmap(a as *u8,__size_of(GpaWasmCursor)); sys_munmap(b as *u8,__size_of(GpaWasmCursor)) 113 sys_munmap(sa as *u8,__size_of(GpaWasmCursor)); sys_munmap(sb as *u8,__size_of(GpaWasmCursor)) 114 sys_munmap(ma as *u8,__size_of(GpaMemory)); sys_munmap(mb as *u8,__size_of(GpaMemory)) 115 return ok 116} 117 118const GPA_MARK: *u8 = "const B=\"" as *u8 119const GPA_SHA_BYTES: i64 = 32 120const GPA_SHA_HEX: i64 = 64 121const GPA_RC_USAGE: i64 = 2 122const GPA_RC_INPUT: i64 = 3 123const GPA_RC_SHAPE: i64 = 4 124const GPA_RC_CONFLICT: i64 = 8 125 126func gpa_same(a: *u8, an: i64, b: *u8, bn: i64) -> i64 { 127 if an != bn { return 0 } 128 var i: i64 = 0 129 while i < an { if a[i] != b[i] { return 0 }; i = i + 1 } 130 return 1 131} 132func gpa_sha(b: *u8, n: i64, hex: *u8) -> i64 { 133 let raw: *u8 = sys_mmap(GPA_SHA_BYTES) 134 sha256_digest(b, n, raw) 135 let digits: *u8 = "0123456789abcdef" as *u8 136 var i: i64 = 0 137 while i < GPA_SHA_BYTES { 138 let c: i64 = raw[i] as i64 139 hex[i*2] = digits[c / 16]; hex[i*2+1] = digits[c % 16] 140 i = i + 1 141 } 142 hex[GPA_SHA_HEX] = 0 as u8 143 sys_munmap(raw, GPA_SHA_BYTES) 144 return GPA_SHA_HEX 145} 146// Returns output length or a negative refusal. Nothing is written before every bound is established. 147func gpa_compose(page: *u8, pn: i64, wasm: *u8, wn: i64, out: *u8, cap: i64) -> i64 { 148 if pn <= 0 { return 0 - GPA_RC_INPUT } 149 if wn < GPA_HEADER_BYTES { return 0 - GPA_RC_INPUT } 150 if wasm[0] != (0 as u8) { return 0 - GPA_RC_INPUT } 151 if wasm[1] != (97 as u8) { return 0 - GPA_RC_INPUT } 152 if wasm[2] != (115 as u8) { return 0 - GPA_RC_INPUT } 153 if wasm[3] != (109 as u8) { return 0 - GPA_RC_INPUT } 154 if wasm[4] != (1 as u8) { return 0 - GPA_RC_INPUT } 155 var h: i64 = 5 156 while h < GPA_HEADER_BYTES { if wasm[h] != (0 as u8) { return 0 - GPA_RC_INPUT }; h = h + 1 } 157 let ml: i64 = vw_slen(GPA_MARK) 158 var hit: i64 = 0 - 1 159 var i: i64 = 0 160 while i + ml <= pn { 161 if gpa_same(((page as i64)+i) as *u8, ml, GPA_MARK, ml) == 1 { 162 if hit >= 0 { return 0 - GPA_RC_SHAPE } 163 hit = i + ml 164 } 165 i = i + 1 166 } 167 if hit < 0 { return 0 - GPA_RC_SHAPE } 168 var end: i64 = hit 169 while end < pn { 170 if page[end] == (34 as u8) { break } 171 let c: i64 = page[end] as i64 172 if b64_dec_char(c) < 0 { if c != B64_PAD { return 0 - GPA_RC_SHAPE } } 173 end = end + 1 174 } 175 if end + 1 >= pn { return 0 - GPA_RC_SHAPE } 176 if page[end+1] != (59 as u8) { return 0 - GPA_RC_SHAPE } 177 let encn: i64 = ((wn + 2) / 3) * 4 178 let total: i64 = pn - (end-hit) + encn 179 if total > cap { return 0 - GPA_RC_SHAPE } 180 if gpa_release_slot(page,pn)<0 { return 0 - GPA_RC_SHAPE } 181 i = 0 182 while i < hit { out[i] = page[i]; i = i + 1 } 183 let wrote: i64 = b64_encode(wasm, wn, ((out as i64)+hit) as *u8) 184 var dest: i64 = hit + wrote 185 i = end 186 while i < pn { out[dest] = page[i]; dest = dest + 1; i = i + 1 } 187 if gpa_seal(out,dest)<0 { return 0 - GPA_RC_SHAPE } 188 return dest 189} 190func gpa_slot_replace(src: *u8,n: i64,mark: *u8,value: *u8,quoted: i64,out: *u8,cap: i64) -> i64 { 191 let ml: i64=vw_slen(mark); let vl: i64=vw_slen(value) 192 var start: i64=0-1; var i: i64=0 193 while i+ml<=n { 194 if gpa_same((src as i64+i) as *u8,ml,mark,ml)==1 { 195 if start>=0 { return 0-GPA_RC_SHAPE }; start=i+ml 196 } 197 i=i+1 198 } 199 if start<0 { return 0-GPA_RC_SHAPE } 200 var end: i64=start 201 while end<n { 202 let c: i64=src[end] as i64 203 if quoted==1 { if c==34 { break }; if c<32 || c==92 { return 0-GPA_RC_SHAPE } } 204 else { if c==59 { break }; if c<48 || c>57 { return 0-GPA_RC_SHAPE } } 205 end=end+1 206 } 207 if end==start || end>=n { return 0-GPA_RC_SHAPE } 208 if quoted==1 { if end+1>=n { return 0-GPA_RC_SHAPE }; if src[end+1]!=(59 as u8) { return 0-GPA_RC_SHAPE } } 209 let total: i64=n-(end-start)+vl 210 if total>cap || total<0 { return 0-GPA_RC_SHAPE } 211 i=0; while i<start { out[i]=src[i]; i=i+1 } 212 var j: i64=0; while j<vl { out[i]=value[j]; i=i+1; j=j+1 } 213 j=end; while j<n { out[i]=src[j]; i=i+1; j=j+1 } 214 return i 215} 216func gpa_decimal(v: i64,out: *u8) -> i64 { 217 var x: i64=v; var n: i64=1 218 while x>=10 { n=n+1; x=x/10 } 219 x=v; var i: i64=n 220 while i>0 { i=i-1; out[i]=(48+x%10) as u8; x=x/10 } 221 out[n]=0 as u8 222 return n 223} 224// The native HTML envelope owns this slot. Script-only packaging stays supported. 225// The declaration is updated together with NXMT_URL, so rebind cannot leave stale metadata. 226func gpa_declare_shared(src: *u8,n: i64,url: *u8,out: *u8,cap: i64) -> i64 { 227 let head: *u8="<head>" as *u8 228 let close: *u8="</head>" as *u8 229 let mark: *u8="<meta name=\"nishi-wasm\" data-nishi-role=\"shared-runtime\" content=\"" as *u8 230 let suffix: *u8="\">" as *u8 231 let hl: i64=vw_slen(head); let cl: i64=vw_slen(close) 232 let ml: i64=vw_slen(mark); let sl: i64=vw_slen(suffix) 233 var hs: i64=0-1; var he: i64=0-1; var hit: i64=0-1 234 var i: i64=0 235 while i<n { 236 if i+hl<=n { if gpa_same((src as i64+i) as *u8,hl,head,hl)==1 { 237 if hs>=0 { return 0-GPA_RC_SHAPE }; hs=i+hl 238 } } 239 if i+cl<=n { if gpa_same((src as i64+i) as *u8,cl,close,cl)==1 { 240 if he>=0 { return 0-GPA_RC_SHAPE }; he=i 241 } } 242 if i+ml<=n { if gpa_same((src as i64+i) as *u8,ml,mark,ml)==1 { 243 if hit>=0 { return 0-GPA_RC_SHAPE }; hit=i 244 } } 245 i=i+1 246 } 247 if hs<0 && he<0 && hit<0 { 248 if n>cap { return 0-GPA_RC_SHAPE } 249 i=0;while i<n { out[i]=src[i];i=i+1 };return n 250 } 251 if hs<0 || he<hs { return 0-GPA_RC_SHAPE } 252 var start: i64=he;var end: i64=he 253 if hit>=0 { 254 if hit<hs || hit>=he { return 0-GPA_RC_SHAPE } 255 start=hit;end=hit+ml 256 while end<he { if src[end]==(34 as u8) { break };end=end+1 } 257 if end+sl>he { return 0-GPA_RC_SHAPE } 258 if gpa_same((src as i64+end) as *u8,sl,suffix,sl)!=1 { return 0-GPA_RC_SHAPE } 259 end=end+sl 260 } 261 let amp: *u8="&amp;" as *u8;let al: i64=vw_slen(amp) 262 let un: i64=vw_slen(url);var escaped: i64=un 263 i=0;while i<un { if url[i]==(38 as u8) { escaped=escaped+al-1 };i=i+1 } 264 let total: i64=n-(end-start)+ml+escaped+sl 265 if total>cap || total<n-(end-start) { return 0-GPA_RC_SHAPE } 266 var p: i64=0;i=0;while i<start { out[p]=src[i];p=p+1;i=i+1 } 267 i=0;while i<ml { out[p]=mark[i];p=p+1;i=i+1 } 268 i=0;while i<un { 269 if url[i]==(38 as u8) { var j: i64=0;while j<al { out[p]=amp[j];p=p+1;j=j+1 } } 270 else { out[p]=url[i];p=p+1 } 271 i=i+1 272 } 273 i=0;while i<sl { out[p]=suffix[i];p=p+1;i=i+1 } 274 i=end;while i<n { out[p]=src[i];p=p+1;i=i+1 } 275 return p 276} 277// Legacy pages have neither identity field. Modern pages require exactly one complete tuple. 278func gpa_shared_identity(src:*u8,n:i64,twin:*u8,tn:i64,out:*u8,cap:i64)->i64 { 279 let hm:*u8="const NXMT_SHA256=\"" as *u8 280 let bm:*u8="const NXMT_BYTES=" as *u8 281 let hc:i64=fsx_count_occ(src,n,hm,vw_slen(hm)) 282 let bc:i64=fsx_count_occ(src,n,bm,vw_slen(bm)) 283 if hc==0&&bc==0 { 284 if n>cap {return 0-GPA_RC_SHAPE} 285 var i:i64=0;while i<n {out[i]=src[i];i=i+1};return n 286 } 287 if hc!=1||bc!=1||tn<=0 {return 0-GPA_RC_SHAPE} 288 let hash:*u8=sys_mmap(GPA_SHA_HEX+1) 289 let size:*u8=sys_mmap(GPA_SHA_HEX+1) 290 let mid:*u8=sys_mmap(cap) 291 if (hash as i64)<=0||(size as i64)<=0||(mid as i64)<=0 {return 0-GPA_RC_SHAPE} 292 gpa_sha(twin,tn,hash);gpa_decimal(tn,size) 293 var length:i64=gpa_slot_replace(src,n,hm,hash,1,mid,cap) 294 if length>=0 {length=gpa_slot_replace(mid,length,bm,size,0,out,cap)} 295 sys_munmap(hash,GPA_SHA_HEX+1);sys_munmap(size,GPA_SHA_HEX+1);sys_munmap(mid,cap) 296 return length 297} 298 299// A paired package is built privately; any refusal leaves the caller's output untouched. 300func gpa_compose_pair(page: *u8,pn: i64,plain: *u8,wn: i64,twin: *u8,tn: i64,url: *u8,out: *u8,cap: i64) -> i64 { 301 if cap<=0 { return 0-GPA_RC_SHAPE } 302 let un: i64=vw_slen(url) 303 if un==0 { return 0-GPA_RC_SHAPE } 304 var i: i64=0 305 while i<un { 306 let c: i64=url[i] as i64 307 if c<33 || c>126 || c==34 || c==39 || c==92 || c==60 || c==62 || c==96 { return 0-GPA_RC_SHAPE } 308 i=i+1 309 } 310 let memory: *GpaMemory=sys_mmap(__size_of(GpaMemory)) as *GpaMemory 311 let paired: i64=gpa_pair(plain,wn,twin,tn,memory) 312 if paired!=1 { sys_munmap(memory as *u8,__size_of(GpaMemory)); return 0-GPA_RC_SHAPE } 313 // Wasm32 page counts are at most 65536: decimal storage derives from that bound. 314 let digits: i64=vw_slen("65536" as *u8)+1 315 // Intermediate replacements can be longer than the final page. Bound temporary storage 316 // by original bytes plus every inserted value; final output capacity is checked separately. 317 let declaration: i64=vw_slen("<meta name=\"nishi-wasm\" data-nishi-role=\"shared-runtime\" content=\"\">" as *u8)+un*vw_slen("&amp;" as *u8) 318 let scratch: i64=pn+((wn+2)/3)*4+un+2*(digits-1)+declaration+2*GPA_SHA_HEX 319 let a: *u8=sys_mmap(scratch); let b: *u8=sys_mmap(scratch) 320 let lo: *u8=sys_mmap(digits); let hi: *u8=sys_mmap(digits) 321 gpa_decimal(memory.minimum,lo); gpa_decimal(memory.maximum,hi) 322 var n: i64=gpa_compose(page,pn,plain,wn,a,scratch) 323 if n>=0 { n=gpa_slot_replace(a,n,"const NXMT_URL=\"" as *u8,url,1,b,scratch) } 324 if n>=0 { n=gpa_slot_replace(b,n,"const NXMT_PAGES=" as *u8,lo,0,a,scratch) } 325 if n>=0 { n=gpa_slot_replace(a,n,"const NXMT_MAX=" as *u8,hi,0,b,scratch) } 326 if n>=0 { n=gpa_shared_identity(b,n,twin,tn,a,scratch) } 327 if n>=0 { n=gpa_declare_shared(a,n,url,b,scratch) } 328 if n>cap { n=0-GPA_RC_SHAPE } 329 if n>=0 { if gpa_seal(b,n)<0 { n=0-GPA_RC_SHAPE } } 330 if n>=0 { i=0; while i<n { out[i]=b[i]; i=i+1 } } 331 sys_munmap(a,scratch);sys_munmap(b,scratch);sys_munmap(lo,digits);sys_munmap(hi,digits) 332 sys_munmap(memory as *u8,__size_of(GpaMemory)) 333 return n 334} 335// Compare every original function body before considering a compiler-owned appended initializer. 336// A positive result proves application-code preservation only, never whole-module equivalence. 337func gpa_section(data: *u8,n: i64,wanted: i64,out: *GpaWasmCursor) -> i64 { 338 if gpa_wasm_header(data,n)!=1 { return 0 } 339 let c: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 340 c.data=data;c.size=n;c.pos=GPA_HEADER_BYTES 341 var found: i64=0 342 var ok: i64=1 343 while c.pos<c.size { 344 let id: i64=c.data[c.pos] as i64;c.pos=c.pos+1 345 let count: i64=gpa_u32(c) 346 if count<0 || count>c.size-c.pos { ok=0;break } 347 if id==wanted { 348 if found!=0 { ok=0;break } 349 out.data=(data as i64+c.pos) as *u8;out.size=count;out.pos=0 350 found=1 351 } 352 c.pos=c.pos+count 353 } 354 sys_munmap(c as *u8,__size_of(GpaWasmCursor)) 355 if ok!=1 { return 0 } 356 return found 357} 358func gpa_code_prefix(a: *GpaWasmCursor,b: *GpaWasmCursor,extra: *GpaWasmCursor) -> i64 { 359 let original: i64=gpa_u32(a) 360 let candidate: i64=gpa_u32(b) 361 if original<0 || candidate!=original+1 { return 0 } 362 var i: i64=0 363 while i<original { 364 let an: i64=gpa_u32(a) 365 let bn: i64=gpa_u32(b) 366 if an<0 || bn<0 || an>a.size-a.pos || bn>b.size-b.pos { return 0 } 367 if gpa_same((a.data as i64+a.pos) as *u8,an,(b.data as i64+b.pos) as *u8,bn)!=1 { return 0 } 368 a.pos=a.pos+an;b.pos=b.pos+bn;i=i+1 369 } 370 if a.pos!=a.size { return 0 } 371 let extra_size: i64=gpa_u32(b) 372 if extra_size<=0 || extra_size!=b.size-b.pos { return 0 } 373 extra.data=(b.data as i64+b.pos) as *u8;extra.size=extra_size;extra.pos=0 374 return 1 375} 376func gpa_application_code(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 { 377 let a: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 378 let b: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 379 let extra: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 380 var ok: i64=0 381 // Section 10 is the WebAssembly code section, independent of emitted function count. 382 if gpa_section(plain,pn,10,a)==1 && gpa_section(twin,tn,10,b)==1 { 383 ok=gpa_code_prefix(a,b,extra) 384 } 385 sys_munmap(a as *u8,__size_of(GpaWasmCursor));sys_munmap(b as *u8,__size_of(GpaWasmCursor)) 386 sys_munmap(extra as *u8,__size_of(GpaWasmCursor)) 387 return ok 388} 389// Hex describes WebAssembly opcodes, not configurable execution limits. 390func gpa_hex_digit(c: i64) -> i64 { 391 if c>=48 && c<=57 { return c-48 } 392 if c>=97 && c<=102 { return c-97+10 } 393 return -1 394} 395func gpa_bytes(c: *GpaWasmCursor,hex: *u8) -> i64 { 396 let n: i64=vw_slen(hex) 397 if n%2!=0 || c.pos<0 || n/2>c.size-c.pos { return 0 } 398 var i: i64=0 399 while i<n { 400 let hi: i64=gpa_hex_digit(hex[i] as i64) 401 let lo: i64=gpa_hex_digit(hex[i+1] as i64) 402 if hi<0 || lo<0 { return 0 } 403 if (c.data[c.pos] as i64)!=hi*16+lo { return 0 } 404 c.pos=c.pos+1;i=i+2 405 } 406 return 1 407} 408// Decode an i32.const as its unsigned wasm32 address/length value; -1 means malformed. 409func gpa_i32_constant(c: *GpaWasmCursor) -> i64 { 410 if gpa_bytes(c,"41")!=1 { return -1 } 411 var value: i64=0 412 var shift: i64=0 413 while shift<35 { 414 if c.pos>=c.size { return -1 } 415 let b: i64=c.data[c.pos] as i64;c.pos=c.pos+1 416 if shift==28 { if b>7 && b<120 { return -1 } } 417 value=value|((b&127)<<shift) 418 shift=shift+7 419 if (b&128)==0 { 420 if shift<32 && (b&64)!=0 { value=value|((0-1)<<shift) } 421 return value&4294967295 422 } 423 } 424 return -1 425} 426func gpa_initial_data(a: *GpaWasmCursor,b: *GpaWasmCursor,body: *GpaWasmCursor) -> i64 { 427 let count: i64=gpa_u32(a) 428 if count<0 || gpa_u32(b)!=count { return 0 } 429 if gpa_bytes(body,"01017e")!=1 { return 0 } 430 let state: i64=gpa_i32_constant(body) 431 if state<0 || state%4!=0 { return 0 } 432 if gpa_bytes(body,"41004101fe480200ad210020004200510440")!=1 { return 0 } 433 var i: i64=0 434 while i<count { 435 if gpa_u32(a)!=0 || gpa_u32(b)!=1 { return 0 } 436 let destination: i64=gpa_i32_constant(a) 437 if destination<0 || gpa_bytes(a,"0b")!=1 { return 0 } 438 let n: i64=gpa_u32(a) 439 let bn: i64=gpa_u32(b) 440 if n<0 || bn!=n || n>a.size-a.pos || n>b.size-b.pos { return 0 } 441 if destination>4294967296-n { return 0 } 442 if state<destination+n && destination<state+4 { return 0 } 443 if gpa_same((a.data as i64+a.pos) as *u8,n,(b.data as i64+b.pos) as *u8,n)!=1 { return 0 } 444 if gpa_i32_constant(body)!=destination { return 0 } 445 if gpa_i32_constant(body)!=0 || gpa_i32_constant(body)!=n { return 0 } 446 if gpa_bytes(body,"fc08")!=1 || gpa_u32(body)!=i || gpa_u32(body)!=0 { return 0 } 447 a.pos=a.pos+n;b.pos=b.pos+n;i=i+1 448 } 449 if a.pos!=a.size || b.pos!=b.size { return 0 } 450 if gpa_i32_constant(body)!=state { return 0 } 451 if gpa_bytes(body,"4102fe170200420221000b20000b")!=1 { return 0 } 452 if body.pos!=body.size { return 0 } 453 return 1 454} 455func gpa_data_initialization(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 { 456 let a: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 457 let b: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 458 let body: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 459 var ok: i64=0 460 if gpa_section(plain,pn,10,a)==1 && gpa_section(twin,tn,10,b)==1 { 461 if gpa_code_prefix(a,b,body)==1 { 462 if gpa_section(plain,pn,11,a)==1 && gpa_section(twin,tn,11,b)==1 { 463 ok=gpa_initial_data(a,b,body) 464 } 465 } 466 } 467 sys_munmap(a as *u8,__size_of(GpaWasmCursor));sys_munmap(b as *u8,__size_of(GpaWasmCursor)) 468 sys_munmap(body as *u8,__size_of(GpaWasmCursor)) 469 return ok 470} 471func gpa_type_item(c: *GpaWasmCursor) -> i64 { 472 if gpa_bytes(c,"60")!=1 { return 0 } 473 var vector: i64=0 474 while vector<2 { 475 let n: i64=gpa_u32(c) 476 if n<0 || n>c.size-c.pos { return 0 } 477 var i: i64=0 478 while i<n { 479 let t: i64=c.data[c.pos] as i64 480 // Numeric, SIMD and shorthand reference types in the native compiler dialect. 481 if t!=127 && t!=126 && t!=125 && t!=124 && t!=123 && t!=112 && t!=111 { return 0 } 482 c.pos=c.pos+1;i=i+1 483 } 484 vector=vector+1 485 } 486 return 1 487} 488func gpa_type_extension(a: *GpaWasmCursor,b: *GpaWasmCursor,initializer_type: i64) -> i64 { 489 let original: i64=gpa_u32(a) 490 let candidate: i64=gpa_u32(b) 491 if original<0 || candidate<original || candidate>original+1 { return 0 } 492 if initializer_type<0 || initializer_type>=candidate { return 0 } 493 var i: i64=0 494 var initializer_ok: i64=0 495 while i<candidate { 496 let bs: i64=b.pos 497 if gpa_type_item(b)!=1 { return 0 } 498 if i<original { 499 let start: i64=a.pos 500 if gpa_type_item(a)!=1 { return 0 } 501 if gpa_same((a.data as i64+start) as *u8,a.pos-start,(b.data as i64+bs) as *u8,b.pos-bs)!=1 { return 0 } 502 } 503 if i==initializer_type { 504 let end: i64=b.pos 505 b.pos=bs 506 if gpa_bytes(b,"6000017e")==1 && b.pos==end { initializer_ok=1 } 507 b.pos=end 508 } 509 if i==original && i!=initializer_type { return 0 } 510 i=i+1 511 } 512 if a.pos!=a.size || b.pos!=b.size { return 0 } 513 return initializer_ok 514} 515func gpa_function_extension(a: *GpaWasmCursor,b: *GpaWasmCursor) -> i64 { 516 let original: i64=gpa_u32(a) 517 let candidate: i64=gpa_u32(b) 518 if original<0 || candidate!=original+1 { return -1 } 519 var i: i64=0 520 while i<original { 521 let t: i64=gpa_u32(a) 522 if t<0 || gpa_u32(b)!=t { return -1 } 523 i=i+1 524 } 525 let initializer_type: i64=gpa_u32(b) 526 if a.pos!=a.size || b.pos!=b.size { return -1 } 527 return initializer_type 528} 529func gpa_export_extension(a: *GpaWasmCursor,b: *GpaWasmCursor,initializer_index: i64) -> i64 { 530 let original: i64=gpa_u32(a) 531 if original<0 || gpa_u32(b)!=original+1 { return 0 } 532 let bytes: i64=a.size-a.pos 533 if bytes>b.size-b.pos { return 0 } 534 if gpa_same((a.data as i64+a.pos) as *u8,bytes,(b.data as i64+b.pos) as *u8,bytes)!=1 { return 0 } 535 b.pos=b.pos+bytes 536 if gpa_wasm_name(b,"_nx_data_init")!=1 { return 0 } 537 if gpa_bytes(b,"00")!=1 || gpa_u32(b)!=initializer_index { return 0 } 538 if b.pos!=b.size { return 0 } 539 return 1 540} 541func gpa_interface_extension(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 { 542 let a: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 543 let b: *GpaWasmCursor=sys_mmap(__size_of(GpaWasmCursor)) as *GpaWasmCursor 544 var ok: i64=0 545 if gpa_section(plain,pn,3,a)==1 && gpa_section(twin,tn,3,b)==1 { 546 let initializer_index: i64=gpa_u32(a);a.pos=0 547 let initializer_type: i64=gpa_function_extension(a,b) 548 if initializer_type>=0 { 549 if gpa_section(plain,pn,1,a)==1 && gpa_section(twin,tn,1,b)==1 { 550 if gpa_type_extension(a,b,initializer_type)==1 { 551 if gpa_section(plain,pn,7,a)==1 && gpa_section(twin,tn,7,b)==1 { 552 ok=gpa_export_extension(a,b,initializer_index) 553 } 554 } 555 } 556 } 557 } 558 sys_munmap(a as *u8,__size_of(GpaWasmCursor));sys_munmap(b as *u8,__size_of(GpaWasmCursor)) 559 return ok 560} 561// Section ordering is the core Wasm binary order; data-count precedes code. 562func gpa_section_order(id: i64) -> i64 { 563 if id==12 { return 10 } 564 if id==10 { return 11 } 565 if id==11 { return 12 } 566 if id>=1 && id<=9 { return id } 567 return -1 568} 569func gpa_ordered_module(data: *u8,n: i64) -> i64 { 570 if gpa_wasm_header(data,n)!=1 { return 0 } 571 var c: GpaWasmCursor 572 c.data=data;c.size=n;c.pos=GPA_HEADER_BYTES 573 var previous: i64=0 574 while c.pos<n { 575 let id: i64=data[c.pos] as i64;c.pos=c.pos+1 576 let size: i64=gpa_u32(&c) 577 if size<0 || size>n-c.pos { return 0 } 578 if id!=0 { 579 let rank: i64=gpa_section_order(id) 580 if rank<=previous { return 0 } 581 previous=rank 582 } 583 c.pos=c.pos+size 584 } 585 return 1 586} 587// Return unchanged-section spans in source order, including every custom section. 588func gpa_unchanged_span(c: *GpaWasmCursor,out: *GpaWasmCursor) -> i64 { 589 while c.pos<c.size { 590 let start: i64=c.pos 591 let id: i64=c.data[c.pos] as i64;c.pos=c.pos+1 592 let size: i64=gpa_u32(c) 593 if size<0 || size>c.size-c.pos { return -1 } 594 c.pos=c.pos+size 595 // These sections have separate exact transformation checks. 596 if id!=1 && id!=2 && id!=3 && id!=5 && id!=7 && id!=10 && id!=11 && id!=12 { 597 // A start function can access data before explicit shared initialization. 598 if id==8 { return -1 } 599 out.data=(c.data as i64+start) as *u8;out.size=c.pos-start;out.pos=0 600 return 1 601 } 602 } 603 return 0 604} 605func gpa_unchanged_sections(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 { 606 var a: GpaWasmCursor;var b: GpaWasmCursor 607 var sa: GpaWasmCursor;var sb: GpaWasmCursor 608 a.data=plain;a.size=pn;a.pos=GPA_HEADER_BYTES 609 b.data=twin;b.size=tn;b.pos=GPA_HEADER_BYTES 610 while a.pos<=pn && b.pos<=tn { 611 let ra: i64=gpa_unchanged_span(&a,&sa) 612 let rb: i64=gpa_unchanged_span(&b,&sb) 613 if ra<0 || rb<0 || ra!=rb { return 0 } 614 if ra==0 { return 1 } 615 if gpa_same(sa.data,sa.size,sb.data,sb.size)!=1 { return 0 } 616 } 617 return 0 618} 619func gpa_extension_memory(plain: *u8,pn: i64,twin: *u8,tn: i64,memory: *GpaMemory) -> i64 { 620 var a: GpaWasmCursor;var b: GpaWasmCursor 621 var ma: GpaMemory;var mb: GpaMemory 622 ma.count=0;mb.count=0 623 if gpa_section(plain,pn,2,&a)!=0 || gpa_section(twin,tn,5,&b)!=0 { return 0 } 624 if gpa_section(plain,pn,5,&a)!=1 || gpa_section(twin,tn,2,&b)!=1 { return 0 } 625 if gpa_memory_section(a.data,a.size,0,&ma)!=1 { return 0 } 626 if gpa_memory_section(b.data,b.size,1,&mb)!=1 { return 0 } 627 // The compiler appends one aligned scalar cell; it can cross at most one page. 628 if mb.minimum<ma.minimum || mb.minimum>ma.minimum+1 { return 0 } 629 memory.minimum=mb.minimum;memory.maximum=mb.maximum;memory.count=1 630 return 1 631} 632func gpa_extension_counts(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 { 633 var a: GpaWasmCursor;var b: GpaWasmCursor 634 if gpa_section(twin,tn,11,&b)!=1 { return 0 } 635 let count: i64=gpa_u32(&b) 636 if count<0 || gpa_section(twin,tn,12,&b)!=1 { return 0 } 637 if gpa_u32(&b)!=count || b.pos!=b.size { return 0 } 638 if gpa_section(plain,pn,12,&a)==1 { 639 if gpa_u32(&a)!=count || a.pos!=a.size { return 0 } 640 } 641 return 1 642} 643// Structural evidence only: zero-init reservation provenance remains separately required. 644func gpa_extension_envelope(plain: *u8,pn: i64,twin: *u8,tn: i64) -> i64 { 645 if gpa_ordered_module(plain,pn)!=1 || gpa_ordered_module(twin,tn)!=1 { return 0 } 646 if gpa_unchanged_sections(plain,pn,twin,tn)!=1 { return 0 } 647 var memory: GpaMemory 648 if gpa_extension_memory(plain,pn,twin,tn,&memory)!=1 { return 0 } 649 return gpa_extension_counts(plain,pn,twin,tn) 650} 651func gpa_pair_report(plain: *u8,pn: i64,twin: *u8,tn: i64,expected_plain: *u8,expected_twin: *u8,ph: *u8,th: *u8,memory: *GpaMemory) -> i64 { 652 if pn<=0 || tn<=0 || pn>=FSX_READ_CAP || tn>=FSX_READ_CAP { 653 fsx_puts("PAGE-PAIR REFUSED unreadable-or-capacity-bound-artifact; no pair verdict\n") 654 return GPA_RC_INPUT 655 } 656 gpa_sha(plain,pn,ph); gpa_sha(twin,tn,th) 657 if fsx_seq(ph,expected_plain)!=1 || fsx_seq(th,expected_twin)!=1 { 658 fsx_puts("PAGE-PAIR REFUSED artifact-digest-conflict; read the intended build receipts\n") 659 return GPA_RC_CONFLICT 660 } 661 if gpa_pair(plain,pn,twin,tn,memory)!=1 { 662 if gpa_application_code(plain,pn,twin,tn)==1 { 663 fsx_puts("PAGE-PAIR EVIDENCE original-function-bodies=byte-identical appended-functions=1 unresolved=initializer-types-exports-data-layout action=verify-compiler-extension publication=refused\n") 664 } 665 if gpa_data_initialization(plain,pn,twin,tn)==1 { 666 fsx_puts("PAGE-PAIR EVIDENCE static-payloads=byte-identical destinations=preserved initializer=exact-atomic-copy-program unresolved=types-exports-state-reservation publication=refused\n") 667 } 668 if gpa_interface_extension(plain,pn,twin,tn)==1 { 669 fsx_puts("PAGE-PAIR EVIDENCE application-types-exports=preserved initializer-signature=verified unresolved=state-reservation publication=refused\n") 670 } 671 fsx_puts("PAGE-PAIR REFUSED differing-body-or-unsupported-memory-contract; inspect both build inputs\n") 672 return GPA_RC_SHAPE 673 } 674 fsx_puts("PAGE-PAIR VERIFIED plain_sha256="); fsx_puts(ph) 675 fsx_puts(" shared_sha256="); fsx_puts(th) 676 fsx_puts(" minimum_pages="); fsx_putn(memory.minimum) 677 fsx_puts(" maximum_pages="); fsx_putn(memory.maximum) 678 fsx_puts(" body=byte-identical runtime-validation=separate publication=not-attempted\n") 679 return 0 680} 681func gpa_pair_main(argc: i64, argv: *i64) -> i64 { 682 if argc != 6 { 683 fsx_puts("usage: nx_game_page_emit pair <plain.wasm> <shared.wasm> <plain-sha256> <shared-sha256>\n") 684 return GPA_RC_USAGE 685 } 686 if fsx_denied(argv[2] as *u8)==1 || fsx_denied(argv[3] as *u8)==1 { return FSX_RC_DENIED } 687 let plain: *u8=sys_mmap(FSX_READ_CAP+1) 688 let twin: *u8=sys_mmap(FSX_READ_CAP+1) 689 let pn: i64=vw_read(argv[2] as *u8,plain,FSX_READ_CAP) 690 let tn: i64=vw_read(argv[3] as *u8,twin,FSX_READ_CAP) 691 let ph: *u8=sys_mmap(GPA_SHA_HEX+1) 692 let th: *u8=sys_mmap(GPA_SHA_HEX+1) 693 let memory: *GpaMemory=sys_mmap(__size_of(GpaMemory)) as *GpaMemory 694 let rc: i64=gpa_pair_report(plain,pn,twin,tn,argv[4] as *u8,argv[5] as *u8,ph,th,memory) 695 sys_munmap(plain,FSX_READ_CAP+1); sys_munmap(twin,FSX_READ_CAP+1) 696 sys_munmap(ph,GPA_SHA_HEX+1); sys_munmap(th,GPA_SHA_HEX+1) 697 sys_munmap(memory as *u8,__size_of(GpaMemory)) 698 return rc 699} 700func gpa_rebind_compose(argc: i64,argv: *i64,page: *u8,pn: i64,wasm: *u8,wn: i64,out: *u8,cap: i64) -> i64 { 701 if argc==7 { 702 let n: i64=gpa_compose(page,pn,wasm,wn,out,cap) 703 if n<0 { return 0-GPA_RC_SHAPE }; return n 704 } 705 let path: *u8=argv[7] as *u8 706 if fsx_denied(path)==1 { return 0-FSX_RC_DENIED } 707 let twin: *u8=sys_mmap(FSX_READ_CAP) 708 let tn: i64=vw_read(path,twin,FSX_READ_CAP) 709 if tn<GPA_HEADER_BYTES || tn>=FSX_READ_CAP { 710 sys_munmap(twin,FSX_READ_CAP) 711 fsx_puts("PAGE-REBIND REFUSED shared-artifact-unreadable-or-capacity-bound\n" as *u8) 712 return 0-GPA_RC_INPUT 713 } 714 let hash: *u8=sys_mmap(GPA_SHA_HEX+1) 715 gpa_sha(twin,tn,hash) 716 var n: i64=0-GPA_RC_CONFLICT 717 if fsx_seq(hash,argv[8] as *u8)==1 { 718 n=gpa_compose_pair(page,pn,wasm,wn,twin,tn,argv[9] as *u8,out,cap) 719 } else { fsx_puts("PAGE-REBIND REFUSED shared-sha256-mismatch\n" as *u8) } 720 sys_munmap(hash,GPA_SHA_HEX+1);sys_munmap(twin,FSX_READ_CAP) 721 return n 722} 723func gpa_main(argc: i64, argv: *i64) -> i64 { 724 if argc != 7 && argc != 10 { 725 fsx_puts("usage: nx_game_page_emit rebind <base.html> <wasm> <out.html> <base-sha256> <wasm-sha256> [<shared.wasm> <shared-sha256> <shared-url>]\n" as *u8) 726 return GPA_RC_USAGE 727 } 728 let base: *u8 = argv[2] as *u8 729 let module: *u8 = argv[3] as *u8 730 let target: *u8 = argv[4] as *u8 731 if fsx_denied(base) == 1 { return FSX_RC_DENIED } 732 if fsx_denied(module) == 1 { return FSX_RC_DENIED } 733 if fsx_write_denied(target) == 1 { return FSX_RC_DENIED } 734 let page: *u8 = sys_mmap(FSX_READ_CAP+1) 735 let wasm: *u8 = sys_mmap(FSX_READ_CAP+1) 736 let pn: i64 = vw_read(base, page, FSX_READ_CAP) 737 let wn: i64 = vw_read(module, wasm, FSX_READ_CAP) 738 if pn <= 0 { fsx_puts("PAGE-REBIND REFUSED base-unreadable\n" as *u8); return GPA_RC_INPUT } 739 if wn < GPA_HEADER_BYTES { fsx_puts("PAGE-REBIND REFUSED wasm-unreadable\n" as *u8); return GPA_RC_INPUT } 740 if pn >= FSX_READ_CAP { fsx_puts("PAGE-REBIND REFUSED base-at-read-cap\n" as *u8); return GPA_RC_INPUT } 741 if wn >= FSX_READ_CAP { fsx_puts("PAGE-REBIND REFUSED wasm-at-read-cap\n" as *u8); return GPA_RC_INPUT } 742 let ph: *u8 = sys_mmap(GPA_SHA_HEX+1) 743 let wh: *u8 = sys_mmap(GPA_SHA_HEX+1) 744 gpa_sha(page, pn, ph); gpa_sha(wasm, wn, wh) 745 if fsx_seq(ph, argv[5] as *u8) == 0 { fsx_puts("PAGE-REBIND REFUSED base-sha256-mismatch\n" as *u8); return GPA_RC_CONFLICT } 746 if fsx_seq(wh, argv[6] as *u8) == 0 { fsx_puts("PAGE-REBIND REFUSED wasm-sha256-mismatch\n" as *u8); return GPA_RC_CONFLICT } 747 let out: *u8 = sys_mmap(FSX_READ_CAP+1) 748 let n: i64 = gpa_rebind_compose(argc, argv, page, pn, wasm, wn, out, FSX_READ_CAP) 749 if n < 0 { fsx_puts("PAGE-REBIND REFUSED composition; source and existing output retained\n" as *u8); return 0-n } 750 let prior: *u8 = sys_mmap(FSX_READ_CAP+1) 751 let oldn: i64 = vw_read(target, prior, FSX_READ_CAP+1) 752 if oldn >= 0 { 753 if gpa_same(prior, oldn, out, n) == 1 { 754 fsx_puts("PAGE-REBIND UNCHANGED writes=0\n" as *u8) 755 return 0 756 } 757 fsx_puts("PAGE-REBIND REFUSED output-exists-with-different-content; choose a new staging path\n" as *u8) 758 return GPA_RC_CONFLICT 759 } 760 let wrote: i64 = fxc_create(target, out, n) 761 if wrote == FXC_EXISTS { 762 let racedn: i64 = vw_read(target, prior, FSX_READ_CAP+1) 763 if gpa_same(out, n, prior, racedn) == 1 { 764 fsx_puts("PAGE-REBIND UNCHANGED concurrent-identical-writer\n" as *u8) 765 return 0 766 } 767 fsx_puts("PAGE-REBIND REFUSED concurrent-output-conflict; existing bytes retained\n" as *u8) 768 return GPA_RC_CONFLICT 769 } 770 if wrote != n { 771 fsx_puts("PAGE-REBIND FAILED exclusive-stage-write rc=" as *u8); fsx_putn(wrote) 772 fsx_puts("; no replacing-rename fallback; use a filesystem supporting exclusive installation\n" as *u8) 773 return FSX_RC_IO 774 } 775 let checkn: i64 = vw_read(target, prior, FSX_READ_CAP+1) 776 if gpa_same(out, n, prior, checkn) != 1 { fsx_puts("PAGE-REBIND FAILED readback-mismatch\n" as *u8); return FSX_RC_IO } 777 fsx_puts("PAGE-REBIND STAGED bytes=" as *u8); fsx_putn(n) 778 fsx_puts(" verified=byte-exact publication=not-attempted runtime-validation=required\n" as *u8) 779 return 0 780} 781 782const GPA_RELEASE_MARK: *u8 = "<meta name=\"nishi-release-sha256\" content=\"" as *u8 783// 0: legacy page without a field; negative: ambiguous/malformed field; positive: value offset. 784func gpa_release_slot(page: *u8,n: i64) -> i64 { 785 let ml: i64=vw_slen(GPA_RELEASE_MARK) 786 var found: i64=0; var i: i64=0 787 while i+ml<=n { 788 if gpa_same((page as i64+i) as *u8,ml,GPA_RELEASE_MARK,ml)==1 { 789 if found!=0 { return -1 }; found=i+ml 790 } 791 i=i+1 792 } 793 if found==0 { return 0 } 794 if found+GPA_SHA_HEX+2>n { return -1 } 795 if page[found+GPA_SHA_HEX]!=34 as u8 || page[found+GPA_SHA_HEX+1]!=62 as u8 { return -1 } 796 i=0; while i<GPA_SHA_HEX { 797 let c: i64=page[found+i] as i64 798 if (c<48 || c>57) && (c<97 || c>102) { return -1 }; i=i+1 799 } 800 return found 801} 802// Digest covers exact emitted HTML, with only this digest field normalized to ASCII zero. 803func gpa_seal(page: *u8,n: i64) -> i64 { 804 let slot: i64=gpa_release_slot(page,n); if slot<=0 { return slot } 805 var i: i64=0; while i<GPA_SHA_HEX { page[slot+i]=48 as u8; i=i+1 } 806 let hash: *u8=sys_mmap(GPA_SHA_HEX+1) 807 gpa_sha(page,n,hash) 808 i=0; while i<GPA_SHA_HEX { page[slot+i]=hash[i]; i=i+1 } 809 sys_munmap(hash,GPA_SHA_HEX+1); return 1 810}