libmspack 0.10.1.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/ext/libmspack/ChangeLog +145 -0
  4. data/ext/libmspack/INSTALL +3 -3
  5. data/ext/libmspack/Makefile.am +7 -4
  6. data/ext/libmspack/Makefile.in +265 -147
  7. data/ext/libmspack/README +3 -2
  8. data/ext/libmspack/aclocal.m4 +111 -113
  9. data/ext/libmspack/ar-lib +10 -9
  10. data/ext/libmspack/compile +9 -8
  11. data/ext/libmspack/config.guess +887 -613
  12. data/ext/libmspack/config.h.in +6 -9
  13. data/ext/libmspack/config.sub +1349 -1260
  14. data/ext/libmspack/configure +3035 -2490
  15. data/ext/libmspack/configure.ac +3 -3
  16. data/ext/libmspack/depcomp +4 -4
  17. data/ext/libmspack/install-sh +107 -74
  18. data/ext/libmspack/libmscabd.la +1 -1
  19. data/ext/libmspack/libmschmd.la +1 -1
  20. data/ext/libmspack/libmspack.la +1 -1
  21. data/ext/libmspack/ltmain.sh +156 -61
  22. data/ext/libmspack/m4/libtool.m4 +19 -12
  23. data/ext/libmspack/missing +8 -8
  24. data/ext/libmspack/mspack/cabd.c +21 -19
  25. data/ext/libmspack/mspack/chm.h +3 -2
  26. data/ext/libmspack/mspack/chmd.c +137 -57
  27. data/ext/libmspack/mspack/kwajd.c +29 -29
  28. data/ext/libmspack/mspack/lzx.h +0 -1
  29. data/ext/libmspack/mspack/lzxd.c +30 -154
  30. data/ext/libmspack/mspack/macros.h +64 -0
  31. data/ext/libmspack/mspack/mszipd.c +7 -18
  32. data/ext/libmspack/mspack/qtmd.c +3 -5
  33. data/ext/libmspack/mspack/readbits.h +14 -5
  34. data/ext/libmspack/mspack/readhuff.h +26 -21
  35. data/ext/libmspack/mspack/system.c +0 -5
  36. data/ext/libmspack/mspack/system.h +20 -67
  37. data/ext/libmspack/test-driver +16 -11
  38. data/ext/x86_64-linux/libmspack.so +0 -0
  39. data/ext/x86_64-windows/mspack.dll +0 -0
  40. data/lib/libmspack/version.rb +1 -1
  41. metadata +4 -3
@@ -1,5 +1,5 @@
1
1
  /* This file is part of libmspack.
2
- * (C) 2003-2018 Stuart Caie.
2
+ * (C) 2003-2023 Stuart Caie.
3
3
  *
4
4
  * libmspack is free software; you can redistribute it and/or modify it under
5
5
  * the terms of the GNU Lesser General Public License (LGPL) version 2.1
@@ -76,7 +76,8 @@ static int cabd_read_headers(
76
76
  struct mspack_system *sys, struct mspack_file *fh,
77
77
  struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet);
78
78
  static char *cabd_read_string(
79
- struct mspack_system *sys, struct mspack_file *fh, int *error);
79
+ struct mspack_system *sys, struct mspack_file *fh, int permit_empty,
80
+ int *error);
80
81
 
81
82
  static struct mscabd_cabinet *cabd_search(
82
83
  struct mscab_decompressor *base, const char *filename);
@@ -394,17 +395,17 @@ static int cabd_read_headers(struct mspack_system *sys,
394
395
 
395
396
  /* read name and info of preceeding cabinet in set, if present */
396
397
  if (cab->base.flags & cfheadPREV_CABINET) {
397
- cab->base.prevname = cabd_read_string(sys, fh, &err);
398
+ cab->base.prevname = cabd_read_string(sys, fh, 0, &err);
398
399
  if (err) return err;
399
- cab->base.previnfo = cabd_read_string(sys, fh, &err);
400
+ cab->base.previnfo = cabd_read_string(sys, fh, 1, &err);
400
401
  if (err) return err;
401
402
  }
402
403
 
403
404
  /* read name and info of next cabinet in set, if present */
404
405
  if (cab->base.flags & cfheadNEXT_CABINET) {
405
- cab->base.nextname = cabd_read_string(sys, fh, &err);
406
+ cab->base.nextname = cabd_read_string(sys, fh, 0, &err);
406
407
  if (err) return err;
407
- cab->base.nextinfo = cabd_read_string(sys, fh, &err);
408
+ cab->base.nextinfo = cabd_read_string(sys, fh, 1, &err);
408
409
  if (err) return err;
409
410
  }
410
411
 
@@ -508,7 +509,7 @@ static int cabd_read_headers(struct mspack_system *sys,
508
509
  file->date_y = (x >> 9) + 1980;
509
510
 
510
511
  /* get filename */
511
- file->filename = cabd_read_string(sys, fh, &err);
512
+ file->filename = cabd_read_string(sys, fh, 0, &err);
512
513
 
513
514
  /* if folder index or filename are bad, either skip it or fail */
514
515
  if (err || !file->folder) {
@@ -535,7 +536,8 @@ static int cabd_read_headers(struct mspack_system *sys,
535
536
  }
536
537
 
537
538
  static char *cabd_read_string(struct mspack_system *sys,
538
- struct mspack_file *fh, int *error)
539
+ struct mspack_file *fh, int permit_empty,
540
+ int *error)
539
541
  {
540
542
  off_t base = sys->tell(fh);
541
543
  char buf[256], *str;
@@ -549,8 +551,8 @@ static char *cabd_read_string(struct mspack_system *sys,
549
551
 
550
552
  /* search for a null terminator in the buffer */
551
553
  for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; }
552
- /* reject empty strings */
553
- if (i == 0) ok = 0;
554
+ /* optionally reject empty strings */
555
+ if (i == 0 && !permit_empty) ok = 0;
554
556
 
555
557
  if (!ok) {
556
558
  *error = MSPACK_ERR_DATAFORMAT;
@@ -651,10 +653,10 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
651
653
  unsigned int cablen_u32 = 0, foffset_u32 = 0;
652
654
  int false_cabs = 0;
653
655
 
654
- #if !LARGEFILE_SUPPORT
656
+ #if SIZEOF_OFF_T < 8
655
657
  /* detect 32-bit off_t overflow */
656
658
  if (flen < 0) {
657
- sys->message(fh, largefile_msg);
659
+ sys->message(fh, "library not compiled to support large files.");
658
660
  return MSPACK_ERR_OK;
659
661
  }
660
662
  #endif
@@ -753,10 +755,10 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
753
755
  /* cause the search to restart after this cab's data. */
754
756
  offset = caboff + (off_t) cablen_u32;
755
757
 
756
- #if !LARGEFILE_SUPPORT
758
+ #if SIZEOF_OFF_T < 8
757
759
  /* detect 32-bit off_t overflow */
758
760
  if (offset < caboff) {
759
- sys->message(fh, largefile_msg);
761
+ sys->message(fh, "library not compiled to support large files.");
760
762
  return MSPACK_ERR_OK;
761
763
  }
762
764
  #endif
@@ -1012,7 +1014,7 @@ static int cabd_extract(struct mscab_decompressor *base,
1012
1014
  struct mscabd_folder_p *fol;
1013
1015
  struct mspack_system *sys;
1014
1016
  struct mspack_file *fh;
1015
- off_t filelen;
1017
+ unsigned int filelen;
1016
1018
 
1017
1019
  if (!self) return MSPACK_ERR_ARGS;
1018
1020
  if (!file) return self->error = MSPACK_ERR_ARGS;
@@ -1029,7 +1031,7 @@ static int cabd_extract(struct mscab_decompressor *base,
1029
1031
  * or in salvage mode reduce file length so it fits 2GB limit
1030
1032
  */
1031
1033
  filelen = file->length;
1032
- if (filelen > CAB_LENGTHMAX || (file->offset + filelen) > CAB_LENGTHMAX) {
1034
+ if (filelen > (CAB_LENGTHMAX - file->offset)) {
1033
1035
  if (self->salvage) {
1034
1036
  filelen = CAB_LENGTHMAX - file->offset;
1035
1037
  }
@@ -1049,8 +1051,8 @@ static int cabd_extract(struct mscab_decompressor *base,
1049
1051
  * In salvage mode, don't assume block sizes, just try decoding
1050
1052
  */
1051
1053
  if (!self->salvage) {
1052
- off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX;
1053
- if ((file->offset + filelen) > maxlen) {
1054
+ unsigned int maxlen = fol->base.num_blocks * CAB_BLOCKMAX;
1055
+ if (file->offset > maxlen || filelen > (maxlen - file->offset)) {
1054
1056
  sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
1055
1057
  "cabinet set is incomplete", file->filename);
1056
1058
  return self->error = MSPACK_ERR_DECRUNCH;
@@ -1398,7 +1400,7 @@ static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes,
1398
1400
  unsigned int len, ul = 0;
1399
1401
 
1400
1402
  for (len = bytes >> 2; len--; data += 4) {
1401
- cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24));
1403
+ cksum ^= EndGetI32(data);
1402
1404
  }
1403
1405
 
1404
1406
  switch (bytes & 3) {
@@ -1,5 +1,5 @@
1
1
  /* This file is part of libmspack.
2
- * (C) 2003-2004 Stuart Caie.
2
+ * (C) 2003-2023 Stuart Caie.
3
3
  *
4
4
  * libmspack is free software; you can redistribute it and/or modify it under
5
5
  * the terms of the GNU Lesser General Public License (LGPL) version 2.1
@@ -104,7 +104,8 @@ struct mschm_compressor_p {
104
104
  /* CHM decompression definitions */
105
105
  struct mschmd_decompress_state {
106
106
  struct mschmd_header *chm; /* CHM file being decompressed */
107
- off_t offset; /* uncompressed offset within folder */
107
+ off_t length; /* uncompressed length of LZX stream */
108
+ off_t offset; /* uncompressed offset within stream */
108
109
  off_t inoffset; /* offset in input file */
109
110
  struct lzxd_stream *state; /* LZX decompressor state */
110
111
  struct mspack_system sys; /* special I/O code for decompressor */
@@ -1,5 +1,5 @@
1
1
  /* This file is part of libmspack.
2
- * (C) 2003-2018 Stuart Caie.
2
+ * (C) 2003-2023 Stuart Caie.
3
3
  *
4
4
  * libmspack is free software; you can redistribute it and/or modify it under
5
5
  * the terms of the GNU Lesser General Public License (LGPL) version 2.1
@@ -58,6 +58,8 @@ static int chmd_error(
58
58
  static int read_off64(
59
59
  off_t *var, unsigned char *mem, struct mspack_system *sys,
60
60
  struct mspack_file *fh);
61
+ static off_t read_encint(
62
+ const unsigned char **p, const unsigned char *end, int *err);
61
63
 
62
64
  /* filenames of the system files used for decompression.
63
65
  * Content and ControlData are essential.
@@ -249,24 +251,15 @@ static const unsigned char guids[32] = {
249
251
  0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC
250
252
  };
251
253
 
252
- /* reads an encoded integer into a variable; 7 bits of data per byte,
253
- * the high bit is used to indicate that there is another byte */
254
- #define READ_ENCINT(var) do { \
255
- (var) = 0; \
256
- do { \
257
- if (p >= end) goto chunk_end; \
258
- (var) = ((var) << 7) | (*p & 0x7F); \
259
- } while (*p++ & 0x80); \
260
- } while (0)
261
-
262
254
  static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
263
255
  struct mschmd_header *chm, int entire)
264
256
  {
265
- unsigned int section, name_len, x, errors, num_chunks;
266
- unsigned char buf[0x54], *chunk = NULL, *name, *p, *end;
257
+ unsigned int errors, num_chunks;
258
+ unsigned char buf[0x54], *chunk = NULL;
259
+ const unsigned char *name, *p, *end;
267
260
  struct mschmd_file *fi, *link = NULL;
268
- off_t offset, length;
269
- int num_entries;
261
+ off_t offset_hs0, filelen;
262
+ int num_entries, err = 0;
270
263
 
271
264
  /* initialise pointers */
272
265
  chm->files = NULL;
@@ -312,7 +305,7 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
312
305
  /* chmhst3_OffsetCS0 does not exist in version 1 or 2 CHM files.
313
306
  * The offset will be corrected later, once HS1 is read.
314
307
  */
315
- if (read_off64(&offset, &buf[chmhst_OffsetHS0], sys, fh) ||
308
+ if (read_off64(&offset_hs0, &buf[chmhst_OffsetHS0], sys, fh) ||
316
309
  read_off64(&chm->dir_offset, &buf[chmhst_OffsetHS1], sys, fh) ||
317
310
  read_off64(&chm->sec0.offset, &buf[chmhst3_OffsetCS0], sys, fh))
318
311
  {
@@ -320,7 +313,7 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
320
313
  }
321
314
 
322
315
  /* seek to header section 0 */
323
- if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
316
+ if (sys->seek(fh, offset_hs0, MSPACK_SYS_SEEK_START)) {
324
317
  return MSPACK_ERR_SEEK;
325
318
  }
326
319
 
@@ -332,6 +325,18 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
332
325
  return MSPACK_ERR_DATAFORMAT;
333
326
  }
334
327
 
328
+ /* compare declared CHM file size against actual size */
329
+ if (!mspack_sys_filelen(sys, fh, &filelen)) {
330
+ if (chm->length > filelen) {
331
+ sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes",
332
+ chm->length - filelen);
333
+ }
334
+ else if (chm->length < filelen) {
335
+ sys->message(fh, "WARNING; possible %" LD " extra bytes at end of file",
336
+ filelen - chm->length);
337
+ }
338
+ }
339
+
335
340
  /* seek to header section 1 */
336
341
  if (sys->seek(fh, chm->dir_offset, MSPACK_SYS_SEEK_START)) {
337
342
  return MSPACK_ERR_SEEK;
@@ -412,12 +417,13 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
412
417
  }
413
418
 
414
419
  /* seek to the first PMGL chunk, and reduce the number of chunks to read */
415
- if ((x = chm->first_pmgl) != 0) {
416
- if (sys->seek(fh,(off_t) (x * chm->chunk_size), MSPACK_SYS_SEEK_CUR)) {
420
+ if (chm->first_pmgl != 0) {
421
+ off_t pmgl_offset = (off_t) chm->first_pmgl * (off_t) chm->chunk_size;
422
+ if (sys->seek(fh, pmgl_offset, MSPACK_SYS_SEEK_CUR)) {
417
423
  return MSPACK_ERR_SEEK;
418
424
  }
419
425
  }
420
- num_chunks = chm->last_pmgl - x + 1;
426
+ num_chunks = chm->last_pmgl - chm->first_pmgl + 1;
421
427
 
422
428
  if (!(chunk = (unsigned char *) sys->alloc(sys, (size_t)chm->chunk_size))) {
423
429
  return MSPACK_ERR_NOMEMORY;
@@ -449,12 +455,15 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
449
455
  num_entries = EndGetI16(end);
450
456
 
451
457
  while (num_entries--) {
452
- READ_ENCINT(name_len);
453
- if (name_len > (unsigned int) (end - p)) goto chunk_end;
458
+ unsigned int name_len, section;
459
+ off_t offset, length;
460
+ name_len = read_encint(&p, end, &err);
461
+ if (err || (name_len > (unsigned int) (end - p))) goto encint_err;
454
462
  name = p; p += name_len;
455
- READ_ENCINT(section);
456
- READ_ENCINT(offset);
457
- READ_ENCINT(length);
463
+ section = read_encint(&p, end, &err);
464
+ offset = read_encint(&p, end, &err);
465
+ length = read_encint(&p, end, &err);
466
+ if (err) goto encint_err;
458
467
 
459
468
  /* ignore blank or one-char (e.g. "/") filenames we'd return as blank */
460
469
  if (name_len < 2 || !name[0] || !name[1]) continue;
@@ -482,7 +491,7 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
482
491
  : (struct mschmd_section *) (&chm->sec1));
483
492
  fi->offset = offset;
484
493
  fi->length = length;
485
- sys->copy(name, fi->filename, (size_t) name_len);
494
+ sys->copy((unsigned char *) name, fi->filename, (size_t) name_len);
486
495
  fi->filename[name_len] = '\0';
487
496
 
488
497
  if (name[0] == ':' && name[1] == ':') {
@@ -510,10 +519,10 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
510
519
  }
511
520
 
512
521
  /* this is reached either when num_entries runs out, or if
513
- * reading data from the chunk reached a premature end of chunk */
514
- chunk_end:
522
+ * an ENCINT is badly encoded */
523
+ encint_err:
515
524
  if (num_entries >= 0) {
516
- D(("chunk ended before all entries could be read"))
525
+ D(("bad encint before all entries could be read"))
517
526
  errors++;
518
527
  }
519
528
 
@@ -572,7 +581,10 @@ static int chmd_fast_find(struct mschm_decompressor *base,
572
581
  }
573
582
 
574
583
  /* found result. loop around for next chunk if this is PMGI */
575
- if (chunk[3] == 0x4C) break; else READ_ENCINT(n);
584
+ if (chunk[3] == 0x4C) break;
585
+
586
+ n = read_encint(&p, end, &err);
587
+ if (err) goto encint_err;
576
588
  }
577
589
  }
578
590
  else {
@@ -599,11 +611,12 @@ static int chmd_fast_find(struct mschm_decompressor *base,
599
611
 
600
612
  /* if we found a file, read it */
601
613
  if (result > 0) {
602
- READ_ENCINT(sec);
614
+ sec = read_encint(&p, end, &err);
603
615
  f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0
604
616
  : (struct mschmd_section *) &chm->sec1;
605
- READ_ENCINT(f_ptr->offset);
606
- READ_ENCINT(f_ptr->length);
617
+ f_ptr->offset = read_encint(&p, end, &err);
618
+ f_ptr->length = read_encint(&p, end, &err);
619
+ if (err) goto encint_err;
607
620
  }
608
621
  else if (result < 0) {
609
622
  err = MSPACK_ERR_DATAFORMAT;
@@ -612,8 +625,8 @@ static int chmd_fast_find(struct mschm_decompressor *base,
612
625
  sys->close(fh);
613
626
  return self->error = err;
614
627
 
615
- chunk_end:
616
- D(("read beyond end of chunk entries"))
628
+ encint_err:
629
+ D(("bad encint in PGMI/PGML chunk"))
617
630
  sys->close(fh);
618
631
  return self->error = MSPACK_ERR_DATAFORMAT;
619
632
  }
@@ -697,7 +710,7 @@ static int search_chunk(struct mschmd_header *chm,
697
710
  const unsigned char *start, *end, *p;
698
711
  unsigned int qr_size, num_entries, qr_entries, qr_density, name_len;
699
712
  unsigned int L, R, M, fname_len, entries_off, is_pmgl;
700
- int cmp;
713
+ int cmp, err = 0;
701
714
 
702
715
  fname_len = strlen(filename);
703
716
 
@@ -755,8 +768,8 @@ static int search_chunk(struct mschmd_header *chm,
755
768
 
756
769
  /* compare filename with entry QR points to */
757
770
  p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)];
758
- READ_ENCINT(name_len);
759
- if (name_len > (unsigned int) (end - p)) goto chunk_end;
771
+ name_len = read_encint(&p, end, &err);
772
+ if (err || (name_len > (unsigned int) (end - p))) goto encint_err;
760
773
  cmp = compare(filename, (char *)p, fname_len, name_len);
761
774
 
762
775
  if (cmp == 0) break;
@@ -792,8 +805,8 @@ static int search_chunk(struct mschmd_header *chm,
792
805
  */
793
806
  *result = NULL;
794
807
  while (num_entries-- > 0) {
795
- READ_ENCINT(name_len);
796
- if (name_len > (unsigned int) (end - p)) goto chunk_end;
808
+ name_len = read_encint(&p, end, &err);
809
+ if (err || (name_len > (unsigned int) (end - p))) goto encint_err;
797
810
  cmp = compare(filename, (char *)p, fname_len, name_len);
798
811
  p += name_len;
799
812
 
@@ -810,21 +823,21 @@ static int search_chunk(struct mschmd_header *chm,
810
823
 
811
824
  /* read and ignore the rest of this entry */
812
825
  if (is_pmgl) {
813
- READ_ENCINT(R); /* skip section */
814
- READ_ENCINT(R); /* skip offset */
815
- READ_ENCINT(R); /* skip length */
826
+ while (p < end && (*p++ & 0x80)); /* skip section ENCINT */
827
+ while (p < end && (*p++ & 0x80)); /* skip offset ENCINT */
828
+ while (p < end && (*p++ & 0x80)); /* skip length ENCINT */
816
829
  }
817
830
  else {
818
831
  *result = p; /* store potential final result */
819
- READ_ENCINT(R); /* skip chunk number */
832
+ while (p < end && (*p++ & 0x80)); /* skip chunk number ENCINT */
820
833
  }
821
834
  }
822
835
 
823
836
  /* PMGL? not found. PMGI? maybe found */
824
837
  return (is_pmgl) ? 0 : (*result ? 1 : 0);
825
838
 
826
- chunk_end:
827
- D(("reached end of chunk data while searching"))
839
+ encint_err:
840
+ D(("bad encint while searching"))
828
841
  return -1;
829
842
  }
830
843
 
@@ -938,14 +951,19 @@ static int chmd_extract(struct mschm_decompressor *base,
938
951
  switch (file->section->id) {
939
952
  case 0: /* Uncompressed section file */
940
953
  /* simple seek + copy */
941
- if (sys->seek(self->d->infh, file->section->chm->sec0.offset
942
- + file->offset, MSPACK_SYS_SEEK_START))
954
+ if (sys->seek(self->d->infh, chm->sec0.offset + file->offset,
955
+ MSPACK_SYS_SEEK_START))
943
956
  {
944
957
  self->error = MSPACK_ERR_SEEK;
945
958
  }
946
959
  else {
947
960
  unsigned char buf[512];
948
961
  off_t length = file->length;
962
+ off_t maxlen = chm->length - sys->tell(self->d->infh);
963
+ if (length > maxlen) {
964
+ sys->message(fh, "WARNING; file is %" LD " bytes longer than CHM file",
965
+ length - maxlen);
966
+ }
949
967
  while (length > 0) {
950
968
  int run = sizeof(buf);
951
969
  if ((off_t)run > length) run = (int)length;
@@ -963,7 +981,7 @@ static int chmd_extract(struct mschm_decompressor *base,
963
981
  break;
964
982
 
965
983
  case 1: /* MSCompressed section file */
966
- /* (re)initialise compression state if we it is not yet initialised,
984
+ /* (re)initialise compression state if not yet initialised,
967
985
  * or we have advanced too far and have to backtrack
968
986
  */
969
987
  if (!self->d->state || (file->offset < self->d->offset)) {
@@ -974,6 +992,12 @@ static int chmd_extract(struct mschm_decompressor *base,
974
992
  if (chmd_init_decomp(self, file)) break;
975
993
  }
976
994
 
995
+ /* check file offset is not impossible */
996
+ if (file->offset > self->d->length) {
997
+ self->error = MSPACK_ERR_DECRUNCH;
998
+ break;
999
+ }
1000
+
977
1001
  /* seek to input data */
978
1002
  if (sys->seek(self->d->infh, self->d->inoffset, MSPACK_SYS_SEEK_START)) {
979
1003
  self->error = MSPACK_ERR_SEEK;
@@ -988,8 +1012,15 @@ static int chmd_extract(struct mschm_decompressor *base,
988
1012
 
989
1013
  /* if getting to the correct offset was error free, unpack file */
990
1014
  if (!self->error) {
1015
+ off_t length = file->length;
1016
+ off_t maxlen = self->d->length - file->offset;
1017
+ if (length > maxlen) {
1018
+ sys->message(fh, "WARNING; file is %" LD " bytes longer than "
1019
+ "compressed section", length - maxlen);
1020
+ length = maxlen + 1; /* should decompress but still error out */
1021
+ }
991
1022
  self->d->outfh = fh;
992
- self->error = lzxd_decompress(self->d->state, file->length);
1023
+ self->error = lzxd_decompress(self->d->state, length);
993
1024
  }
994
1025
 
995
1026
  /* save offset in input source stream, in case there is a section 0
@@ -1052,8 +1083,8 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self,
1052
1083
  if (err) return self->error = err;
1053
1084
 
1054
1085
  /* read ControlData */
1055
- if (sec->control->length < lzxcd_SIZEOF) {
1056
- D(("ControlData file is too short"))
1086
+ if (sec->control->length != lzxcd_SIZEOF) {
1087
+ D(("ControlData file is wrong size"))
1057
1088
  return self->error = MSPACK_ERR_DATAFORMAT;
1058
1089
  }
1059
1090
  if (!(data = read_sys_file(self, sec->control))) {
@@ -1125,8 +1156,8 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self,
1125
1156
  entry = 0;
1126
1157
  offset = 0;
1127
1158
  err = read_spaninfo(self, sec, &length);
1159
+ if (err) return self->error = err;
1128
1160
  }
1129
- if (err) return self->error = err;
1130
1161
 
1131
1162
  /* get offset of compressed data stream:
1132
1163
  * = offset of uncompressed section from start of file
@@ -1136,6 +1167,7 @@ static int chmd_init_decomp(struct mschm_decompressor_p *self,
1136
1167
 
1137
1168
  /* set start offset and overall remaining stream length */
1138
1169
  self->d->offset = entry * LZX_FRAME_SIZE;
1170
+ self->d->length = length;
1139
1171
  length -= self->d->offset;
1140
1172
 
1141
1173
  /* initialise LZX stream */
@@ -1172,6 +1204,11 @@ static int read_reset_table(struct mschm_decompressor_p *self,
1172
1204
  D(("ResetTable file is too short"))
1173
1205
  return 0;
1174
1206
  }
1207
+ if (sec->rtable->length > 1000000) { /* arbitrary upper limit */
1208
+ D(("ResetTable >1MB (%"LD"), report if genuine", sec->rtable->length))
1209
+ return 0;
1210
+ }
1211
+
1175
1212
  if (!(data = read_sys_file(self, sec->rtable))) {
1176
1213
  D(("can't read reset table"))
1177
1214
  return 0;
@@ -1246,6 +1283,12 @@ static int read_spaninfo(struct mschm_decompressor_p *self,
1246
1283
  return MSPACK_ERR_DATAFORMAT;
1247
1284
  }
1248
1285
 
1286
+ /* unconditionally set length here, because gcc -Wuninitialized isn't
1287
+ * clever enough to recognise that read_sys_file() will always set
1288
+ * self->error to a non-zero value if it returns NULL, and gcc warnings
1289
+ * spook humans (even false positives) */
1290
+ *length_ptr = 0;
1291
+
1249
1292
  /* read the SpanInfo file */
1250
1293
  if (!(data = read_sys_file(self, sec->spaninfo))) {
1251
1294
  D(("can't read SpanInfo file"))
@@ -1364,14 +1407,51 @@ static int chmd_error(struct mschm_decompressor *base) {
1364
1407
  static int read_off64(off_t *var, unsigned char *mem,
1365
1408
  struct mspack_system *sys, struct mspack_file *fh)
1366
1409
  {
1367
- #if LARGEFILE_SUPPORT
1410
+ #if SIZEOF_OFF_T >= 8
1368
1411
  *var = EndGetI64(mem);
1369
1412
  #else
1370
- *var = EndGetI32(mem);
1371
- if ((*var & 0x80000000) || EndGetI32(mem+4)) {
1372
- sys->message(fh, (char *)largefile_msg);
1413
+ if ((mem[3] & 0x80) | mem[4] | mem[5] | mem[6] | mem[7]) {
1414
+ sys->message(fh, "library not compiled to support large files.");
1373
1415
  return 1;
1374
1416
  }
1417
+ *var = EndGetI32(mem);
1375
1418
  #endif
1376
1419
  return 0;
1377
1420
  }
1421
+
1422
+ #if SIZEOF_OFF_T >= 8
1423
+ /* 63 bits allowed: 9 * 7 bits/byte, last byte must be 0x00-0x7F */
1424
+ # define ENCINT_MAX_BYTES 9
1425
+ # define ENCINT_BAD_LAST_BYTE 0x80
1426
+ #else
1427
+ /* 31 bits allowed: 5 * 7 bits/byte, last byte must be 0x00-0x07 */
1428
+ # define ENCINT_MAX_BYTES 5
1429
+ # define ENCINT_BAD_LAST_BYTE 0xF1
1430
+ #endif
1431
+
1432
+ /***************************************
1433
+ * READ_ENCINT
1434
+ ***************************************
1435
+ * Reads an ENCINT from memory. If running on a system with a 32-bit off_t,
1436
+ * ENCINTs up to 0x7FFFFFFF are accepted, values beyond that are an error.
1437
+ */
1438
+ static off_t read_encint(const unsigned char **p, const unsigned char *end,
1439
+ int *err)
1440
+ {
1441
+ off_t result = 0;
1442
+ unsigned char c = 0x80;
1443
+ int i = 0;
1444
+ while ((c & 0x80) && (i++ < ENCINT_MAX_BYTES)) {
1445
+ if (*p >= end) {
1446
+ *err = 1;
1447
+ return 0;
1448
+ }
1449
+ c = *(*p)++;
1450
+ result = (result << 7) | (c & 0x7F);
1451
+ }
1452
+ if (i == ENCINT_MAX_BYTES && (c & ENCINT_BAD_LAST_BYTE)) {
1453
+ *err = 1;
1454
+ return 0;
1455
+ }
1456
+ return result;
1457
+ }
@@ -1,5 +1,5 @@
1
1
  /* This file is part of libmspack.
2
- * (C) 2003-2011 Stuart Caie.
2
+ * (C) 2003-2023 Stuart Caie.
3
3
  *
4
4
  * KWAJ is a format very similar to SZDD. KWAJ method 3 (LZH) was
5
5
  * written by Jeff Johnson.
@@ -97,27 +97,30 @@ static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base,
97
97
  struct mskwajd_header *hdr;
98
98
  struct mspack_system *sys;
99
99
  struct mspack_file *fh;
100
+ int err;
100
101
 
101
102
  if (!self) return NULL;
102
103
  sys = self->system;
103
104
 
104
- fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
105
- hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p));
106
- if (fh && hdr) {
107
- ((struct mskwajd_header_p *) hdr)->fh = fh;
108
- self->error = kwajd_read_headers(sys, fh, hdr);
109
- }
110
- else {
111
- if (!fh) self->error = MSPACK_ERR_OPEN;
112
- if (!hdr) self->error = MSPACK_ERR_NOMEMORY;
105
+ fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
106
+ if (!fh) {
107
+ self->error = MSPACK_ERR_OPEN;
108
+ return NULL;
113
109
  }
114
-
115
- if (self->error) {
116
- if (fh) sys->close(fh);
117
- sys->free(hdr);
118
- hdr = NULL;
110
+
111
+ hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p));
112
+ if (!hdr) {
113
+ sys->close(fh);
114
+ self->error = MSPACK_ERR_NOMEMORY;
115
+ return NULL;
119
116
  }
120
117
 
118
+ ((struct mskwajd_header_p *) hdr)->fh = fh;
119
+ if ((err = kwajd_read_headers(sys, fh, hdr))) {
120
+ kwajd_close(base, hdr);
121
+ self->error = err;
122
+ return NULL;
123
+ }
121
124
  return hdr;
122
125
  }
123
126
 
@@ -138,6 +141,8 @@ static void kwajd_close(struct mskwaj_decompressor *base,
138
141
  self->system->close(hdr_p->fh);
139
142
 
140
143
  /* free the memory associated */
144
+ self->system->free(hdr->filename);
145
+ self->system->free(hdr->extra);
141
146
  self->system->free(hdr);
142
147
 
143
148
  self->error = MSPACK_ERR_OK;
@@ -200,7 +205,7 @@ static int kwajd_read_headers(struct mspack_system *sys,
200
205
  if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) {
201
206
  int len;
202
207
  /* allocate memory for maximum length filename */
203
- char *fn = (char *) sys->alloc(sys, (size_t) 13);
208
+ char *fn = (char *) sys->alloc(sys, 13);
204
209
  if (!(hdr->filename = fn)) return MSPACK_ERR_NOMEMORY;
205
210
 
206
211
  /* copy filename if present */
@@ -236,7 +241,7 @@ static int kwajd_read_headers(struct mspack_system *sys,
236
241
  if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) {
237
242
  if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
238
243
  i = EndGetI16(&buf[0]);
239
- hdr->extra = (char *) sys->alloc(sys, (size_t)i+1);
244
+ hdr->extra = (char *) sys->alloc(sys, i+1);
240
245
  if (! hdr->extra) return MSPACK_ERR_NOMEMORY;
241
246
  if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ;
242
247
  hdr->extra[i] = '\0';
@@ -280,7 +285,7 @@ static int kwajd_extract(struct mskwaj_decompressor *base,
280
285
  hdr->comp_type == MSKWAJ_COMP_XOR)
281
286
  {
282
287
  /* NONE is a straight copy. XOR is a copy xored with 0xFF */
283
- unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE);
288
+ unsigned char *buf = (unsigned char *) sys->alloc(sys, KWAJ_INPUT_SIZE);
284
289
  if (buf) {
285
290
  int read, i;
286
291
  while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) {
@@ -301,7 +306,7 @@ static int kwajd_extract(struct mskwaj_decompressor *base,
301
306
  }
302
307
  else if (hdr->comp_type == MSKWAJ_COMP_SZDD) {
303
308
  self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE,
304
- LZSS_MODE_EXPAND);
309
+ LZSS_MODE_QBASIC);
305
310
  }
306
311
  else if (hdr->comp_type == MSKWAJ_COMP_LZH) {
307
312
  struct kwajd_stream *lzh = lzh_init(sys, fh, outfh);
@@ -435,17 +440,14 @@ static struct kwajd_stream *lzh_init(struct mspack_system *sys,
435
440
 
436
441
  static int lzh_decompress(struct kwajd_stream *lzh)
437
442
  {
438
- register unsigned int bit_buffer;
439
- register int bits_left, i;
440
- register unsigned short sym;
441
- unsigned char *i_ptr, *i_end, lit_run = 0;
442
- int j, pos = 0, len, offset, err;
443
- unsigned int types[6];
443
+ DECLARE_HUFF_VARS;
444
+ unsigned int types[6], i, j, pos = 0, len, offset, lit_run = 0;
445
+ int err;
444
446
 
445
447
  /* reset global state */
446
448
  INIT_BITS;
447
449
  RESTORE_BITS;
448
- memset(&lzh->window[0], LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE);
450
+ memset(&lzh->window[0], LZSS_WINDOW_FILL, LZSS_WINDOW_SIZE);
449
451
 
450
452
  /* read 6 encoding types (for byte alignment) but only 5 are needed */
451
453
  for (i = 0; i < 6; i++) READ_BITS_SAFE(types[i], 4);
@@ -501,9 +503,7 @@ static int lzh_read_lens(struct kwajd_stream *lzh,
501
503
  unsigned int type, unsigned int numsyms,
502
504
  unsigned char *lens)
503
505
  {
504
- register unsigned int bit_buffer;
505
- register int bits_left;
506
- unsigned char *i_ptr, *i_end;
506
+ DECLARE_BIT_VARS;
507
507
  unsigned int i, c, sel;
508
508
  int err;
509
509