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:
