/* SPDX-License-Identifier: BSD-2-Clause */ /* Minimal LZ4 decompressor for EROFS FreeBSD */ #include #include #include #include "compress.h" #define EROFS_LZ4_TOKEN_LITERAL_SHIFT 4 #define EROFS_LZ4_TOKEN_MATCH_MASK 0x0f #define EROFS_LZ4_MAX_RUN 15 #define EROFS_LZ4_EXT_SENTINEL 255 #define EROFS_LZ4_MIN_MATCH 4 #define EROFS_LZ4_OFFSET_BYTES 2 static int lz4_finish(const uint8_t *ip, const uint8_t *iend, int partial) { if (partial) return (0); while (ip < iend) { if (*ip++ != 0) return (EINTEGRITY); } return (0); } int z_erofs_lz4_decompress(const struct z_erofs_decompress_req *rq) { const uint8_t *ip, *iend; uint8_t *op, *oend; unsigned int token; size_t length, copylen; size_t offset; ip = rq->in; iend = ip + rq->inputsize; op = rq->out; oend = op + rq->outputsize; if (iend < ip || oend < op) return (EINTEGRITY); while (ip < iend) { token = *ip++; length = token >> EROFS_LZ4_TOKEN_LITERAL_SHIFT; if (length == EROFS_LZ4_MAX_RUN) { unsigned int value; do { if (ip >= iend) return (EINTEGRITY); value = *ip++; if (length > SIZE_MAX - value) return (EINTEGRITY); length += value; } while (value == EROFS_LZ4_EXT_SENTINEL); } if (length > (size_t)(iend - ip)) return (EINTEGRITY); if (!rq->partial_decoding && length > (size_t)(oend - op)) return (EINTEGRITY); copylen = MIN(length, (size_t)(oend - op)); memcpy(op, ip, copylen); ip += length; op += copylen; if (op == oend) return (lz4_finish(ip, iend, rq->partial_decoding)); if (ip >= iend) break; if (ip + EROFS_LZ4_OFFSET_BYTES > iend) return (EINTEGRITY); offset = le16dec(ip); ip += EROFS_LZ4_OFFSET_BYTES; if (offset == 0 || offset > (size_t)(op - (uint8_t *)rq->out)) return (EINTEGRITY); length = token & EROFS_LZ4_TOKEN_MATCH_MASK; if (length == EROFS_LZ4_MAX_RUN) { unsigned int value; do { if (ip >= iend) return (EINTEGRITY); value = *ip++; if (length > SIZE_MAX - value) return (EINTEGRITY); length += value; } while (value == EROFS_LZ4_EXT_SENTINEL); } if (length > SIZE_MAX - EROFS_LZ4_MIN_MATCH) return (EINTEGRITY); length += EROFS_LZ4_MIN_MATCH; if (!rq->partial_decoding && length > (size_t)(oend - op)) return (EINTEGRITY); copylen = MIN(length, (size_t)(oend - op)); while (copylen-- != 0) { *op = *(op - offset); ++op; } if (op == oend) return (lz4_finish(ip, iend, rq->partial_decoding)); } return (op == oend ? lz4_finish(ip, iend, rq->partial_decoding) : EINTEGRITY); }