Commit message: kwaj_read_headers(): fix handling of non-terminated strings
CVE: CVE-2018-14681
CWE: CWE-787
Vulnerability description: An issue was discovered in kwajd_read_headers in mspack/kwajd.c in libmspack before 0.7alpha. Bad KWAJ file header extensions could cause a one or two byte overwrite.

Project: libmspack

static void kwajd_close(struct mskwaj_decompressor *base,
			struct mskwajd_header *hdr)
{
    struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
    struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr;

    if (!self || !self->system) return;

    /* close the file handle associated */
    self->system->close(hdr_p->fh);

    /* free the memory associated */
    self->system->free(hdr);

    self->error = MSPACK_ERR_OK;
}

Project: libmspack

static int kwajd_decompress(struct mskwaj_decompressor *base,
			    const char *input, const char *output)
{
    struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
    struct mskwajd_header *hdr;
    int error;

    if (!self) return MSPACK_ERR_ARGS;

    if (!(hdr = kwajd_open(base, input))) return self->error;
    error = kwajd_extract(base, hdr, output);
    kwajd_close(base, hdr);
    return self->error = error;
}

Project: libmspack

static int kwajd_error(struct mskwaj_decompressor *base)
{
    struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
    return (self) ? self->error : MSPACK_ERR_ARGS;
}

Project: libmspack

static int kwajd_extract(struct mskwaj_decompressor *base,
			 struct mskwajd_header *hdr, const char *filename)
{
    struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
    struct mspack_system *sys;
    struct mspack_file *fh, *outfh;

    if (!self) return MSPACK_ERR_ARGS;
    if (!hdr) return self->error = MSPACK_ERR_ARGS;

    sys = self->system;
    fh = ((struct mskwajd_header_p *) hdr)->fh;

    /* seek to the compressed data */
    if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) {
	return self->error = MSPACK_ERR_SEEK;
    }

    /* open file for output */
    if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
	return self->error = MSPACK_ERR_OPEN;
    }

    self->error = MSPACK_ERR_OK;

    /* decompress based on format */
    if (hdr->comp_type == MSKWAJ_COMP_NONE ||
	hdr->comp_type == MSKWAJ_COMP_XOR)
    {
	/* NONE is a straight copy. XOR is a copy xored with 0xFF */
	unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE);
	if (buf) {
	    int read, i;
	    while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) {
		if (hdr->comp_type == MSKWAJ_COMP_XOR) {
		    for (i = 0; i < read; i++) buf[i] ^= 0xFF;
		}
		if (sys->write(outfh, buf, read) != read) {
		    self->error = MSPACK_ERR_WRITE;
		    break;
		}
	    }
	    if (read < 0) self->error = MSPACK_ERR_READ;
	    sys->free(buf);
	}
	else {
	    self->error = MSPACK_ERR_NOMEMORY;
	}
    }
    else if (hdr->comp_type == MSKWAJ_COMP_SZDD) {
	self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE,
				      LZSS_MODE_EXPAND);
    }
    else if (hdr->comp_type == MSKWAJ_COMP_LZH) {
	struct kwajd_stream *lzh = lzh_init(sys, fh, outfh);
	self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY;
	lzh_free(lzh);
    }
    else if (hdr->comp_type == MSKWAJ_COMP_MSZIP) {
        struct mszipd_stream *zip = mszipd_init(sys,fh,outfh,KWAJ_INPUT_SIZE,0);
        self->error = (zip) ? mszipd_decompress_kwaj(zip) : MSPACK_ERR_NOMEMORY;
        mszipd_free(zip);
    }
    else {
	self->error = MSPACK_ERR_DATAFORMAT;
    }

    /* close output file */
    sys->close(outfh);

    return self->error;
}

Project: libmspack

static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base,
					 const char *filename)
{
    struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
    struct mskwajd_header *hdr;
    struct mspack_system *sys;
    struct mspack_file *fh;

    if (!self) return NULL;
    sys = self->system;

    fh  = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
    hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p));
    if (fh && hdr) {
	((struct mskwajd_header_p *) hdr)->fh = fh;
	self->error = kwajd_read_headers(sys, fh, hdr);
    }
    else {
	if (!fh)  self->error = MSPACK_ERR_OPEN;
	if (!hdr) self->error = MSPACK_ERR_NOMEMORY;
    }
    
    if (self->error) {
	if (fh)  sys->close(fh);
	if (hdr) sys->free(hdr);
	hdr = NULL;
    }

    return hdr;
}

Project: libmspack

static int lzh_decompress(struct kwajd_stream *lzh)
{
    register unsigned int bit_buffer;
    register int bits_left, i;
    register unsigned short sym;
    unsigned char *i_ptr, *i_end, lit_run = 0;
    int j, pos = 0, len, offset, err;
    unsigned int types[6];

    /* reset global state */
    INIT_BITS;
    RESTORE_BITS;
    memset(&lzh->window[0], LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE);

    /* read 6 encoding types (for byte alignment) but only 5 are needed */
    for (i = 0; i < 6; i++) READ_BITS_SAFE(types[i], 4);

    /* read huffman table symbol lengths and build huffman trees */
    BUILD_TREE(MATCHLEN1, types[0]);
    BUILD_TREE(MATCHLEN2, types[1]);
    BUILD_TREE(LITLEN,    types[2]);
    BUILD_TREE(OFFSET,    types[3]);
    BUILD_TREE(LITERAL,   types[4]);

    while (!lzh->input_end) {
	if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len);
	else         READ_HUFFSYM_SAFE(MATCHLEN1, len);

	if (len > 0) {
	    len += 2;
	    lit_run = 0; /* not the end of a literal run */
	    READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6;
	    READ_BITS_SAFE(j, 6);         offset |= j;

	    /* copy match as output and into the ring buffer */
	    while (len-- > 0) {
		lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095];
		WRITE_BYTE;
		pos++; pos &= 4095;
	    }
	}
	else {
	    READ_HUFFSYM_SAFE(LITLEN, len); len++;
	    lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */
	    while (len-- > 0) {
		READ_HUFFSYM_SAFE(LITERAL, j);
		/* copy as output and into the ring buffer */
		lzh->window[pos] = j;
		WRITE_BYTE;
		pos++; pos &= 4095;
	    }
	}
    }
    return MSPACK_ERR_OK;
}

Project: libmspack

static void lzh_free(struct kwajd_stream *lzh)
{
    struct mspack_system *sys;
    if (!lzh || !lzh->sys) return;
    sys = lzh->sys;
    sys->free(lzh);
}

Project: libmspack

static int lzh_read_input(struct kwajd_stream *lzh) {
    int read;
    if (lzh->input_end) {
	lzh->input_end += 8;
	lzh->inbuf[0] = 0;
	read = 1;
    }
    else {
	read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE);
	if (read < 0) return MSPACK_ERR_READ;
	if (read == 0) {
	    lzh->input_end = 8;
	    lzh->inbuf[0] = 0;
	    read = 1;
	}
    }

    /* update i_ptr and i_end */
    lzh->i_ptr = &lzh->inbuf[0];
    lzh->i_end = &lzh->inbuf[read];
    return MSPACK_ERR_OK;
}

Project: libmspack

static int lzh_read_lens(struct kwajd_stream *lzh,
			 unsigned int type, unsigned int numsyms,
			 unsigned char *lens)
{
    register unsigned int bit_buffer;
    register int bits_left;
    unsigned char *i_ptr, *i_end;
    unsigned int i, c, sel;
    int err;

    RESTORE_BITS;
    switch (type) {
    case 0:
	i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0;
	for (i = 0; i < numsyms; i++) lens[i] = c;
	break;

    case 1:
	READ_BITS_SAFE(c, 4); lens[0] = c;
	for (i = 1; i < numsyms; i++) {
    	           READ_BITS_SAFE(sel, 1); if (sel == 0)  lens[i] = c;
	    else { READ_BITS_SAFE(sel, 1); if (sel == 0)  lens[i] = ++c;
	    else { READ_BITS_SAFE(c, 4);                  lens[i] = c; }}
	}
	break;

    case 2:
	READ_BITS_SAFE(c, 4); lens[0] = c;
	for (i = 1; i < numsyms; i++) {
	    READ_BITS_SAFE(sel, 2);
	    if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1;
	    lens[i] = c;
	}
	break;

    case 3:
	for (i = 0; i < numsyms; i++) {
	    READ_BITS_SAFE(c, 4); lens[i] = c;
	}
	break;
    }
    STORE_BITS;
    return MSPACK_ERR_OK;
}

Project: libmspack

    mspack_create_kwaj_decompressor(struct mspack_system *sys)
{
  struct mskwaj_decompressor_p *self = NULL;

  if (!sys) sys = mspack_default_system;
  if (!mspack_valid_system(sys)) return NULL;

  if ((self = (struct mskwaj_decompressor_p *) sys->alloc(sys, sizeof(struct mskwaj_decompressor_p)))) {
    self->base.open       = &kwajd_open;
    self->base.close      = &kwajd_close;
    self->base.extract    = &kwajd_extract;
    self->base.decompress = &kwajd_decompress;
    self->base.last_error = &kwajd_error;
    self->system          = sys;
    self->error           = MSPACK_ERR_OK;
  }
  return (struct mskwaj_decompressor *) self;
}

Project: libmspack

void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base)
{
    struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
    if (self) {
	struct mspack_system *sys = self->system;
	sys->free(self);
    }
}