:: commit ff63204fc9dc7c3121b50d9918c303236010f1a8

Mintsuki <mintsuki@protonmail.com> — 2026-04-20 12:40

parents: 6239cb53f3

stage1/decompressor: Ensure limlz streams always end on a literal token

diff --git a/stage1/decompressor.asm b/stage1/decompressor.asm
index 23448bc7..87389732 100644
--- a/stage1/decompressor.asm
+++ b/stage1/decompressor.asm
@@ -75,6 +75,8 @@ _start:
     mov    esi, ecx
     lea    ecx, [eax+0x4]            ; count = matchlen + 4
     rep    movsb                     ; copy match
+    cmp    edx, ebx                  ; guard against streams that end on a match
+    jae    .Lcrc
     jmp    .Ltoken
     ; CRC32 verification
 .Lcrc:
diff --git a/tools/limlzpack.c b/tools/limlzpack.c
index 4fb0291c..bcffa260 100644
--- a/tools/limlzpack.c
+++ b/tools/limlzpack.c
@@ -218,6 +218,7 @@ static size_t limlzpack(void * dst, size_t dstcap, const void * srcv, size_t src
     dp[i] = best_cost;  pick[i].lit = best_lit;
     pick[i].mlen = best_len;  pick[i].off = best_off;
   }
+  int terminated = 0;
   for (i = 0; i < srcsz; ) {
     byte * tokenp;
     size_t lit = pick[i].lit, ml = pick[i].mlen;
@@ -241,6 +242,7 @@ static size_t limlzpack(void * dst, size_t dstcap, const void * srcv, size_t src
     i += lit;
     if (i >= srcsz) {
       *tokenp = (byte)(token_hi << 3);
+      terminated = 1;
       break;
     }
     unsigned mode_bit = (off > 255) ? 1u : 0u;
@@ -260,6 +262,14 @@ static size_t limlzpack(void * dst, size_t dstcap, const void * srcv, size_t src
       goto fail;
     i += ml;
   }
+  /* A match-ended parse leaves no trailing token; the decompressor keys
+   * termination off a zero-or-more-byte literal copy reaching ipe, so always
+   * emit a final lit=0 token when the main loop didn't already. */
+  if (!terminated) {
+    if (out >= out_end)
+      goto fail;
+    *out++ = 0;
+  }
   free(mch);  free(pick);  free(bestm);  free(dp);
   return (size_t)(out - dstp);
 fail:
tab: 248 wrap: offon