Commit message: fix oob write bug inside libmobi
CVE: CVE-2021-3751
CWE: CWE-787
Vulnerability description: libmobi is vulnerable to Out-of-bounds Write

Project: libmobi

void mobi_buffer_copy(MOBIBuffer *dest, MOBIBuffer *source, const size_t len) {
    if (source->offset + len > source->maxlen) {
        debug_print("%s", "End of buffer\n");
        source->error = MOBI_BUFFER_END;
        return;
    }
    if (dest->offset + len > dest->maxlen) {
        debug_print("%s", "End of buffer\n");
        dest->error = MOBI_BUFFER_END;
        return;
    }
    memcpy(dest->data + dest->offset, source->data + source->offset, len);
    dest->offset += len;
    source->offset += len;
}

Project: libmobi

void mobi_buffer_getstring(char *str, MOBIBuffer *buf, const size_t len) {
    if (!str) {
        buf->error = MOBI_PARAM_ERR;
        return;
    }
    if (buf->offset + len > buf->maxlen) {
        debug_print("%s", "End of buffer\n");
        buf->error = MOBI_BUFFER_END;
        str[0] = '\0';
        return;
    }
    memcpy(str, buf->data + buf->offset, len);
    str[len] = '\0';
    buf->offset += len;
}

Project: libmobi

void mobi_buffer_free(MOBIBuffer *buf) {
	if (buf == NULL) { return; }
	if (buf->data != NULL) {
		free(buf->data);
	}
	free(buf);
}

Project: libmobi

void mobi_buffer_add8(MOBIBuffer *buf, const uint8_t data) {
    if (buf->offset + 1 > buf->maxlen) {
        debug_print("%s", "Buffer full\n");
        buf->error = MOBI_BUFFER_END;
        return;
    }
    buf->data[buf->offset++] = data;
}

Project: libmobi

bool mobi_buffer_match_magic_offset(MOBIBuffer *buf, const char *magic, const size_t offset) {
    bool match = false;
    if (offset <= buf->maxlen) {
        const size_t save_offset = buf->offset;
        buf->offset = offset;
        match = mobi_buffer_match_magic(buf, magic);
        buf->offset = save_offset;
    }
    return match;
}

Project: libmobi

uint32_t mobi_buffer_get_varlen_dec(MOBIBuffer *buf, size_t *len) {
    return _buffer_get_varlen(buf, len, -1);
}

Project: libmobi

bool mobi_buffer_match_magic(MOBIBuffer *buf, const char *magic) {
    const size_t magic_length = strlen(magic);
    if (buf->offset + magic_length > buf->maxlen) {
        return false;
    }
    if (memcmp(buf->data + buf->offset, magic, magic_length) == 0) {
        return true;
    }
    return false;
}

Project: libmobi

MOBIBuffer * mobi_buffer_init_null(unsigned char *data, const size_t len) {
    MOBIBuffer *buf = malloc(sizeof(MOBIBuffer));
	if (buf == NULL) {
        debug_print("%s", "Buffer allocation failed\n");
        return NULL;
    }
    buf->data = data;
	buf->offset = 0;
	buf->maxlen = len;
    buf->error = MOBI_SUCCESS;
	return buf;
}

Project: libmobi

uint8_t mobi_buffer_get8(MOBIBuffer *buf) {
    if (buf->offset + 1 > buf->maxlen) {
        debug_print("%s", "End of buffer\n");
        buf->error = MOBI_BUFFER_END;
        return 0;
    }
    return buf->data[buf->offset++];
}

Project: libmobi

void mobi_buffer_addraw(MOBIBuffer *buf, const unsigned char* data, const size_t len) {
    if (buf->offset + len > buf->maxlen) {
        debug_print("%s", "Buffer full\n");
        buf->error = MOBI_BUFFER_END;
        return;
    }
    memcpy(buf->data + buf->offset, data, len);
    buf->offset += len;
}

Project: libmobi

static uint32_t _buffer_get_varlen(MOBIBuffer *buf, size_t *len, const int direction) {
    uint32_t val = 0;
    uint8_t byte_count = 0;
    uint8_t byte;
    const uint8_t stop_flag = 0x80;
    const uint8_t mask = 0x7f;
    uint32_t shift = 0;
    do {
        if (direction == 1) {
            if (buf->offset + 1 > buf->maxlen) {
                debug_print("%s", "End of buffer\n");
                buf->error = MOBI_BUFFER_END;
                return val;
            }
            byte = buf->data[buf->offset++];
            val <<= 7;
            val |= (byte & mask);
        } else {
            if (buf->offset < 1) {
                debug_print("%s", "End of buffer\n");
                buf->error = MOBI_BUFFER_END;
                return val;
            }
            byte = buf->data[buf->offset--];
            val = val | (uint32_t)(byte & mask) << shift;
            shift += 7;
        }        
        (*len)++;
        byte_count++;
    } while (!(byte & stop_flag) && (byte_count < 4));
    return val;
}

Project: libmobi

void mobi_buffer_addstring(MOBIBuffer *buf, const char *str) {
    const size_t len = strlen(str);
    mobi_buffer_addraw(buf, (const unsigned char *) str, len);
}

Project: libmobi

MOBIBuffer * mobi_buffer_init(const size_t len) {
    unsigned char *data = malloc(len);
    if (data == NULL) {
        debug_print("%s", "Buffer data allocation failed\n");
        return NULL;
    }
    MOBIBuffer *buf = mobi_buffer_init_null(data, len);
    if (buf == NULL) {
        free(data);
    }
    return buf;
}

Project: libmobi

void mobi_buffer_seek(MOBIBuffer *buf, const int diff) {
    size_t adiff = (size_t) abs(diff);
    if (diff >= 0) {
        if (buf->offset + adiff <= buf->maxlen) {
            buf->offset += adiff;
            return;
        }
    } else {
        if (buf->offset >= adiff) {
            buf->offset -= adiff;
            return;
        }
    }
    buf->error = MOBI_BUFFER_END;
    debug_print("%s", "End of buffer\n");
}

Project: libmobi

void mobi_buffer_appendstring(char *str, MOBIBuffer *buf, const size_t len) {
    if (!str) {
        buf->error = MOBI_PARAM_ERR;
        return;
    }
    if (buf->offset + len > buf->maxlen) {
        debug_print("%s", "End of buffer\n");
        buf->error = MOBI_BUFFER_END;
        return;
    }
    size_t str_len = strlen(str);
    memcpy(str + str_len, buf->data + buf->offset, len);
    str[str_len + len] = '\0';
    buf->offset += len;
}

Project: libmobi

void mobi_buffer_add16(MOBIBuffer *buf, const uint16_t data) {
    if (buf->offset + 2 > buf->maxlen) {
        debug_print("%s", "Buffer full\n");
        buf->error = MOBI_BUFFER_END;
        return;
    }
    unsigned char *buftr = buf->data + buf->offset;
    *buftr++ = (uint8_t)((uint32_t)(data & 0xff00U) >> 8);
    *buftr = (uint8_t)((uint32_t)(data & 0xffU));
    buf->offset += 2;
}

Project: libmobi

void mobi_buffer_free_null(MOBIBuffer *buf) {
	if (buf == NULL) { return; }
	free(buf);
}

Project: libmobi

void mobi_buffer_getraw(void *data, MOBIBuffer *buf, const size_t len) {
    if (!data) {
        buf->error = MOBI_PARAM_ERR;
        return;
    }
    if (buf->offset + len > buf->maxlen) {
        debug_print("%s", "End of buffer\n");
        buf->error = MOBI_BUFFER_END;
        return;
    }
    memcpy(data, buf->data + buf->offset, len);
    buf->offset += len;
}

Project: libmobi

void mobi_buffer_addzeros(MOBIBuffer *buf, const size_t count) {
    if (buf->offset + count > buf->maxlen) {
        debug_print("%s", "Buffer full\n");
        buf->error = MOBI_BUFFER_END;
        return;
    }
    memset(buf->data + buf->offset, 0, count);
    buf->offset += count;
}

Project: libmobi

void mobi_buffer_copy8(MOBIBuffer *dest, MOBIBuffer *source) {
    mobi_buffer_add8(dest, mobi_buffer_get8(source));
}

Project: libmobi

void mobi_buffer_dup32(uint32_t **val, MOBIBuffer *buf) {
    *val = NULL;
    if (buf->offset + 4 > buf->maxlen) {
        return;
    }
    *val = malloc(sizeof(uint32_t));
    if (*val == NULL) {
        return;
    }
    **val = mobi_buffer_get32(buf);
}

Project: libmobi

uint32_t mobi_buffer_get32(MOBIBuffer *buf) {
    if (buf->offset + 4 > buf->maxlen) {
        debug_print("%s", "End of buffer\n");
        buf->error = MOBI_BUFFER_END;
        return 0;
    }
    uint32_t val;
    val = (uint32_t) buf->data[buf->offset] << 24 | (uint32_t) buf->data[buf->offset + 1] << 16 | (uint32_t) buf->data[buf->offset + 2] << 8 | (uint32_t) buf->data[buf->offset + 3];
    buf->offset += 4;
    return val;
}

Project: libmobi

void mobi_buffer_resize(MOBIBuffer *buf, const size_t newlen) {
    unsigned char *tmp = realloc(buf->data, newlen);
    if (tmp == NULL) {
        debug_print("%s", "Buffer allocation failed\n");
        buf->error = MOBI_MALLOC_FAILED;
        return;
    }
    buf->data = tmp;
    buf->maxlen = newlen;
    if (buf->offset >= newlen) {
        buf->offset = newlen - 1;
    }
    debug_print("Buffer successfully resized to %zu\n", newlen);
    buf->error = MOBI_SUCCESS;
}

Project: libmobi

unsigned char * mobi_buffer_getpointer(MOBIBuffer *buf, const size_t len) {
    if (buf->offset + len > buf->maxlen) {
        debug_print("%s", "End of buffer\n");
        buf->error = MOBI_BUFFER_END;
        return NULL;
    }
    buf->offset += len;
    return buf->data + buf->offset - len;
}

Project: libmobi

void mobi_buffer_setpos(MOBIBuffer *buf, const size_t pos) {
    if (pos <= buf->maxlen) {
        buf->offset = pos;
        return;
    }
    buf->error = MOBI_BUFFER_END;
    debug_print("%s", "End of buffer\n");
}

Project: libmobi

void mobi_buffer_move(MOBIBuffer *buf, const int offset, const size_t len) {
    size_t aoffset = (size_t) abs(offset);
    unsigned char *source = buf->data + buf->offset;
    if (offset >= 0) {
        if (buf->offset + aoffset + len > buf->maxlen) {
            debug_print("%s", "End of buffer\n");
            buf->error = MOBI_BUFFER_END;
            return;
        }
        source += aoffset;
    } else {
        if ( (buf->offset < aoffset) || (buf->offset + len > buf->maxlen) ) {
            debug_print("%s", "Beyond start/end of buffer\n");
            buf->error = MOBI_BUFFER_END;
            return;
        }
        source -= aoffset;
    }
    memmove(buf->data + buf->offset, source, len);
    buf->offset += len;
}

Project: libmobi

void mobi_buffer_dup16(uint16_t **val, MOBIBuffer *buf) {
    *val = NULL;
    if (buf->offset + 2 > buf->maxlen) {
        return;
    }
    *val = malloc(sizeof(uint16_t));
    if (*val == NULL) {
        return;
    }
    **val = mobi_buffer_get16(buf);
}

Project: libmobi

void mobi_buffer_add32(MOBIBuffer *buf, const uint32_t data) {
    if (buf->offset + 4 > buf->maxlen) {
        debug_print("%s", "Buffer full\n");
        buf->error = MOBI_BUFFER_END;
        return;
    }
    unsigned char *buftr = buf->data + buf->offset;
    *buftr++ = (uint8_t)((uint32_t)(data & 0xff000000U) >> 24);
    *buftr++ = (uint8_t)((uint32_t)(data & 0xff0000U) >> 16);
    *buftr++ = (uint8_t)((uint32_t)(data & 0xff00U) >> 8);
    *buftr = (uint8_t)((uint32_t)(data & 0xffU));
    buf->offset += 4;
}

Project: libmobi

void mobi_buffer_dup8(uint8_t **val, MOBIBuffer *buf) {
    *val = NULL;
    if (buf->offset + 1 > buf->maxlen) {
        return;
    }
    *val = malloc(sizeof(uint8_t));
    if (*val == NULL) {
        return;
    }
    **val = mobi_buffer_get8(buf);
}

Project: libmobi

uint16_t mobi_buffer_get16(MOBIBuffer *buf) {
    if (buf->offset + 2 > buf->maxlen) {
        debug_print("%s", "End of buffer\n");
        buf->error = MOBI_BUFFER_END;
        return 0;
    }
    uint16_t val;
    val = (uint16_t)((uint16_t) buf->data[buf->offset] << 8 | (uint16_t) buf->data[buf->offset + 1]);
    buf->offset += 2;
    return val;
}

Project: libmobi

uint32_t mobi_buffer_get_varlen(MOBIBuffer *buf, size_t *len) {
    return _buffer_get_varlen(buf, len, 1);
}