summaryrefslogtreecommitdiff
path: root/src/ziplist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ziplist.c')
-rw-r--r--src/ziplist.c296
1 files changed, 239 insertions, 57 deletions
diff --git a/src/ziplist.c b/src/ziplist.c
index dd7452006..a8ab57cfe 100644
--- a/src/ziplist.c
+++ b/src/ziplist.c
@@ -152,6 +152,7 @@
*
* Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* Copyright (c) 2009-2017, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2020, Redis Labs, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -298,10 +299,34 @@ typedef struct zlentry {
/* Extract the encoding from the byte pointed by 'ptr' and set it into
* 'encoding' field of the zlentry structure. */
#define ZIP_ENTRY_ENCODING(ptr, encoding) do { \
- (encoding) = (ptr[0]); \
+ (encoding) = ((ptr)[0]); \
if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \
} while(0)
+#define ZIP_ENCODING_SIZE_INVALID 0xff
+/* Return the number of bytes required to encode the entry type + length.
+ * On error, return ZIP_ENCODING_SIZE_INVALID */
+static inline unsigned int zipEncodingLenSize(unsigned char encoding) {
+ if (encoding == ZIP_INT_16B || encoding == ZIP_INT_32B ||
+ encoding == ZIP_INT_24B || encoding == ZIP_INT_64B ||
+ encoding == ZIP_INT_8B)
+ return 1;
+ if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX)
+ return 1;
+ if (encoding == ZIP_STR_06B)
+ return 1;
+ if (encoding == ZIP_STR_14B)
+ return 2;
+ if (encoding == ZIP_STR_32B)
+ return 5;
+ return ZIP_ENCODING_SIZE_INVALID;
+}
+
+#define ZIP_ASSERT_ENCODING(encoding) do { \
+ if (zipEncodingLenSize(encoding) == ZIP_ENCODING_SIZE_INVALID) \
+ panic("Invalid encoding 0x%02X", encoding); \
+} while (0)
+
/* Return bytes needed to store integer encoded by 'encoding'. */
unsigned int zipIntSize(unsigned char encoding) {
switch(encoding) {
@@ -313,7 +338,7 @@ unsigned int zipIntSize(unsigned char encoding) {
}
if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX)
return 0; /* 4 bit immediate */
- panic("Invalid integer encoding 0x%02X", encoding);
+ /* bad encoding, covered by a previous call to ZIP_ASSERT_ENCODING */
return 0;
}
@@ -365,11 +390,10 @@ unsigned int zipStoreEntryEncoding(unsigned char *p, unsigned char encoding, uns
/* Decode the entry encoding type and data length (string length for strings,
* number of bytes used for the integer for integer entries) encoded in 'ptr'.
- * The 'encoding' variable will hold the entry encoding, the 'lensize'
+ * The 'encoding' variable is input, extracted by the caller, the 'lensize'
* variable will hold the number of bytes required to encode the entry
* length, and the 'len' variable will hold the entry length. */
#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do { \
- ZIP_ENTRY_ENCODING((ptr), (encoding)); \
if ((encoding) < ZIP_STR_MASK) { \
if ((encoding) == ZIP_STR_06B) { \
(lensize) = 1; \
@@ -384,7 +408,8 @@ unsigned int zipStoreEntryEncoding(unsigned char *p, unsigned char encoding, uns
((ptr)[3] << 8) | \
((ptr)[4]); \
} else { \
- panic("Invalid string encoding 0x%02X", (encoding)); \
+ len = 0; /* bad encoding, dead code */ \
+ /* covered by a previous call to ZIP_ASSERT_ENCODING */ \
} \
} else { \
(lensize) = 1; \
@@ -440,9 +465,10 @@ unsigned int zipStorePrevEntryLength(unsigned char *p, unsigned int len) {
if ((prevlensize) == 1) { \
(prevlen) = (ptr)[0]; \
} else if ((prevlensize) == 5) { \
- assert(sizeof((prevlen)) == 4); \
- memcpy(&(prevlen), ((char*)(ptr)) + 1, 4); \
- memrev32ifbe(&prevlen); \
+ (prevlen) = ((ptr)[4] << 24) | \
+ ((ptr)[3] << 16) | \
+ ((ptr)[2] << 8) | \
+ ((ptr)[1]); \
} \
} while(0)
@@ -467,14 +493,6 @@ int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
return zipStorePrevEntryLength(NULL, len) - prevlensize;
}
-/* Return the total number of bytes used by the entry pointed to by 'p'. */
-unsigned int zipRawEntryLength(unsigned char *p) {
- unsigned int prevlensize, encoding, lensize, len;
- ZIP_DECODE_PREVLENSIZE(p, prevlensize);
- ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
- return prevlensize + lensize + len;
-}
-
/* Check if string pointed to by 'entry' can be encoded as an integer.
* Stores the integer value in 'v' and its encoding in 'encoding'. */
int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
@@ -565,13 +583,85 @@ int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
return ret;
}
-/* Return a struct with all information about an entry. */
+/* Fills a struct with all information about an entry.
+ * This function is the "unsafe" alternative to the one blow.
+ * Generally, all function that return a pointer to an element in the ziplist
+ * will assert that this element is valid, so it can be freely used.
+ * Generally functions such ziplistGet assume the input pointer is already
+ * validated (since it's the return value of another function). */
void zipEntry(unsigned char *p, zlentry *e) {
+ ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen);
+ ZIP_ENTRY_ENCODING(p + e->prevrawlensize, e->encoding);
+ ZIP_ASSERT_ENCODING(e->encoding);
+ ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len);
+ e->headersize = e->prevrawlensize + e->lensize;
+ e->p = p;
+}
+/* Fills a struct with all information about an entry.
+ * This function is safe to use on untrusted pointers, it'll make sure not to
+ * try to access memory outside the ziplist payload.
+ * Returns 1 if the entry is valid, and 0 otherwise. */
+static inline int zipEntrySafe(unsigned char* zl, size_t zlbytes, unsigned char *p, zlentry *e, int validate_prevlen) {
+#define OUT_OF_RANGE(p) ( \
+ (p) < zl + ZIPLIST_HEADER_SIZE || \
+ (p) > zl + zlbytes - ZIPLIST_END_SIZE)
+
+ /* Make sure the pointer doesn't rech outside the edge of the ziplist */
+ if (OUT_OF_RANGE(p))
+ return 0;
+
+ /* Make sure the encoded prevlen header doesn't reach outside the allocation */
+ ZIP_DECODE_PREVLENSIZE(p, e->prevrawlensize);
+ if (OUT_OF_RANGE(p + e->prevrawlensize))
+ return 0;
+
+ /* Make sure encoded entry header is valid. */
+ ZIP_ENTRY_ENCODING(p + e->prevrawlensize, e->encoding);
+ e->lensize = zipEncodingLenSize(e->encoding);
+ if (e->lensize == ZIP_ENCODING_SIZE_INVALID)
+ return 0;
+
+ /* Make sure the encoded entry header doesn't reach outside the allocation */
+ if (OUT_OF_RANGE(p + e->prevrawlensize + e->lensize))
+ return 0;
+
+ /* Decode the prevlen and entry len headers. */
ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen);
ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len);
e->headersize = e->prevrawlensize + e->lensize;
+
+ /* Make sure the entry doesn't rech outside the edge of the ziplist */
+ if (OUT_OF_RANGE(p + e->headersize + e->len))
+ return 0;
+
+ /* Make sure prevlen doesn't rech outside the edge of the ziplist */
+ if (validate_prevlen && OUT_OF_RANGE(p - e->prevrawlen))
+ return 0;
+
e->p = p;
+ return 1;
+#undef OUT_OF_RANGE
+}
+
+/* Return the total number of bytes used by the entry pointed to by 'p'. */
+static inline unsigned int zipRawEntryLengthSafe(unsigned char* zl, size_t zlbytes, unsigned char *p) {
+ zlentry e;
+ assert(zipEntrySafe(zl, zlbytes, p, &e, 0));
+ return e.headersize + e.len;
+}
+
+/* Return the total number of bytes used by the entry pointed to by 'p'. */
+static inline unsigned int zipRawEntryLength(unsigned char *p) {
+ zlentry e;
+ zipEntry(p, &e);
+ return e.headersize + e.len;
+}
+
+/* Validate that the entry doesn't reach outside the ziplist allocation. */
+static inline void zipAssertValidEntry(unsigned char* zl, size_t zlbytes, unsigned char *p) {
+ zlentry e;
+ assert(zipEntrySafe(zl, zlbytes, p, &e, 1));
}
/* Create a new empty ziplist. */
@@ -625,7 +715,7 @@ unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
/* Empty ziplist */
if (p[0] == ZIP_END) return zl;
- zipEntry(p, &cur);
+ zipEntry(p, &cur); /* no need for "safe" variant since the input pointer was validated by the function that returned it. */
firstentrylen = prevlen = cur.headersize + cur.len;
prevlensize = zipStorePrevEntryLength(NULL, prevlen);
prevoffset = p - zl;
@@ -633,7 +723,7 @@ unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
/* Iterate ziplist to find out how many extra bytes do we need to update it. */
while (p[0] != ZIP_END) {
- zipEntry(p, &cur);
+ assert(zipEntrySafe(zl, curlen, p, &cur, 0));
/* Abort when "prevlen" has not changed. */
if (cur.prevrawlen == prevlen) break;
@@ -690,7 +780,7 @@ unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
/* Iterate all entries that need to be updated tail to head. */
while (cnt) {
- zipEntry(zl + prevoffset, &cur);
+ zipEntry(zl + prevoffset, &cur); /* no need for "safe" variant since we already iterated on all these entries above. */
rawlen = cur.headersize + cur.len;
/* Move entry to tail and reset prevlen. */
memmove(p - (rawlen - cur.prevrawlensize),
@@ -717,15 +807,18 @@ unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int
size_t offset;
int nextdiff = 0;
zlentry first, tail;
+ size_t zlbytes = intrev32ifbe(ZIPLIST_BYTES(zl));
- zipEntry(p, &first);
+ zipEntry(p, &first); /* no need for "safe" variant since the input pointer was validated by the function that returned it. */
for (i = 0; p[0] != ZIP_END && i < num; i++) {
- p += zipRawEntryLength(p);
+ p += zipRawEntryLengthSafe(zl, zlbytes, p);
deleted++;
}
+ assert(p >= first.p);
totlen = p-first.p; /* Bytes taken by the element(s) to delete. */
if (totlen > 0) {
+ uint32_t set_tail;
if (p[0] != ZIP_END) {
/* Storing `prevrawlen` in this entry may increase or decrease the
* number of bytes required compare to the current `prevrawlen`.
@@ -738,36 +831,44 @@ unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int
* had a 5 bytes prevlen header, so there is for sure at least
* 5 bytes free and we need just 4. */
p -= nextdiff;
+ assert(p >= first.p && p<zl+zlbytes-1);
zipStorePrevEntryLength(p,first.prevrawlen);
/* Update offset for tail */
- ZIPLIST_TAIL_OFFSET(zl) =
- intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen);
+ set_tail = intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen;
/* When the tail contains more than one entry, we need to take
* "nextdiff" in account as well. Otherwise, a change in the
* size of prevlen doesn't have an effect on the *tail* offset. */
- zipEntry(p, &tail);
+ assert(zipEntrySafe(zl, zlbytes, p, &tail, 1));
if (p[tail.headersize+tail.len] != ZIP_END) {
- ZIPLIST_TAIL_OFFSET(zl) =
- intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);
+ set_tail = set_tail + nextdiff;
}
/* Move tail to the front of the ziplist */
- memmove(first.p,p,
- intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1);
+ /* since we asserted that p >= first.p. we know totlen >= 0,
+ * so we know that p > first.p and this is guaranteed not to reach
+ * beyond the allocation, even if the entries lens are corrupted. */
+ size_t bytes_to_move = zlbytes-(p-zl)-1;
+ memmove(first.p,p,bytes_to_move);
} else {
/* The entire tail was deleted. No need to move memory. */
- ZIPLIST_TAIL_OFFSET(zl) =
- intrev32ifbe((first.p-zl)-first.prevrawlen);
+ set_tail = (first.p-zl)-first.prevrawlen;
}
- /* Resize and update length */
+ /* Resize the ziplist */
offset = first.p-zl;
- zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff);
- ZIPLIST_INCR_LENGTH(zl,-deleted);
+ zlbytes -= totlen - nextdiff;
+ zl = ziplistResize(zl, zlbytes);
p = zl+offset;
+ /* Update record count */
+ ZIPLIST_INCR_LENGTH(zl,-deleted);
+
+ /* Set the tail offset computed above */
+ assert(set_tail <= zlbytes - ZIPLIST_END_SIZE);
+ ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(set_tail);
+
/* When nextdiff != 0, the raw length of the next entry has changed, so
* we need to cascade the update throughout the ziplist */
if (nextdiff != 0)
@@ -778,7 +879,7 @@ unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int
/* Insert item at "p". */
unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
- size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;
+ size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, newlen;
unsigned int prevlensize, prevlen = 0;
size_t offset;
int nextdiff = 0;
@@ -794,7 +895,7 @@ unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned cha
} else {
unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);
if (ptail[0] != ZIP_END) {
- prevlen = zipRawEntryLength(ptail);
+ prevlen = zipRawEntryLengthSafe(zl, curlen, ptail);
}
}
@@ -824,7 +925,8 @@ unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned cha
/* Store offset because a realloc may change the address of zl. */
offset = p-zl;
- zl = ziplistResize(zl,curlen+reqlen+nextdiff);
+ newlen = curlen+reqlen+nextdiff;
+ zl = ziplistResize(zl,newlen);
p = zl+offset;
/* Apply memory move when necessary and update tail offset. */
@@ -845,7 +947,7 @@ unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned cha
/* When the tail contains more than one entry, we need to take
* "nextdiff" in account as well. Otherwise, a change in the
* size of prevlen doesn't have an effect on the *tail* offset. */
- zipEntry(p+reqlen, &tail);
+ assert(zipEntrySafe(zl, newlen, p+reqlen, &tail, 1));
if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {
ZIPLIST_TAIL_OFFSET(zl) =
intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);
@@ -1002,23 +1104,35 @@ unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int sle
unsigned char *ziplistIndex(unsigned char *zl, int index) {
unsigned char *p;
unsigned int prevlensize, prevlen = 0;
+ size_t zlbytes = intrev32ifbe(ZIPLIST_BYTES(zl));
if (index < 0) {
index = (-index)-1;
p = ZIPLIST_ENTRY_TAIL(zl);
if (p[0] != ZIP_END) {
+ /* No need for "safe" check: when going backwards, we know the header
+ * we're parsing is in the range, we just need to assert (below) that
+ * the size we take doesn't cause p to go outside the allocation. */
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
while (prevlen > 0 && index--) {
p -= prevlen;
+ assert(p >= zl + ZIPLIST_HEADER_SIZE && p < zl + zlbytes - ZIPLIST_END_SIZE);
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
}
}
} else {
p = ZIPLIST_ENTRY_HEAD(zl);
- while (p[0] != ZIP_END && index--) {
- p += zipRawEntryLength(p);
+ while (index--) {
+ /* Use the "safe" length: When we go forward, we need to be careful
+ * not to decode an entry header if it's past the ziplist allocation. */
+ p += zipRawEntryLengthSafe(zl, zlbytes, p);
+ if (p[0] == ZIP_END)
+ break;
}
}
- return (p[0] == ZIP_END || index > 0) ? NULL : p;
+ if (p[0] == ZIP_END || index > 0)
+ return NULL;
+ zipAssertValidEntry(zl, zlbytes, p);
+ return p;
}
/* Return pointer to next entry in ziplist.
@@ -1029,6 +1143,7 @@ unsigned char *ziplistIndex(unsigned char *zl, int index) {
* The element after 'p' is returned, otherwise NULL if we are at the end. */
unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
((void) zl);
+ size_t zlbytes = intrev32ifbe(ZIPLIST_BYTES(zl));
/* "p" could be equal to ZIP_END, caused by ziplistDelete,
* and we should return NULL. Otherwise, we should return NULL
@@ -1042,6 +1157,7 @@ unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
return NULL;
}
+ zipAssertValidEntry(zl, zlbytes, p);
return p;
}
@@ -1060,7 +1176,10 @@ unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
} else {
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
assert(prevlen > 0);
- return p-prevlen;
+ p-=prevlen;
+ size_t zlbytes = intrev32ifbe(ZIPLIST_BYTES(zl));
+ zipAssertValidEntry(zl, zlbytes, p);
+ return p;
}
}
@@ -1073,7 +1192,7 @@ unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *sl
if (p == NULL || p[0] == ZIP_END) return 0;
if (sstr) *sstr = NULL;
- zipEntry(p, &entry);
+ zipEntry(p, &entry); /* no need for "safe" variant since the input pointer was validated by the function that returned it. */
if (ZIP_IS_STR(entry.encoding)) {
if (sstr) {
*slen = entry.len;
@@ -1121,7 +1240,7 @@ unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int
long long zval, sval;
if (p[0] == ZIP_END) return 0;
- zipEntry(p, &entry);
+ zipEntry(p, &entry); /* no need for "safe" variant since the input pointer was validated by the function that returned it. */
if (ZIP_IS_STR(entry.encoding)) {
/* Raw compare */
if (entry.len == slen) {
@@ -1142,23 +1261,23 @@ unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int
/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries
* between every comparison. Returns NULL when the field could not be found. */
-unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {
+unsigned char *ziplistFind(unsigned char *zl, unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {
int skipcnt = 0;
unsigned char vencoding = 0;
long long vll = 0;
+ size_t zlbytes = ziplistBlobLen(zl);
while (p[0] != ZIP_END) {
- unsigned int prevlensize, encoding, lensize, len;
+ struct zlentry e;
unsigned char *q;
- ZIP_DECODE_PREVLENSIZE(p, prevlensize);
- ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
- q = p + prevlensize + lensize;
+ assert(zipEntrySafe(zl, zlbytes, p, &e, 1));
+ q = p + e.prevrawlensize + e.lensize;
if (skipcnt == 0) {
/* Compare current entry with specified entry */
- if (ZIP_IS_STR(encoding)) {
- if (len == vlen && memcmp(q, vstr, vlen) == 0) {
+ if (ZIP_IS_STR(e.encoding)) {
+ if (e.len == vlen && memcmp(q, vstr, vlen) == 0) {
return p;
}
} else {
@@ -1180,7 +1299,7 @@ unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int v
* if vencoding != UCHAR_MAX because if there is no encoding
* possible for the field it can't be a valid integer. */
if (vencoding != UCHAR_MAX) {
- long long ll = zipLoadInteger(q, encoding);
+ long long ll = zipLoadInteger(q, e.encoding);
if (ll == vll) {
return p;
}
@@ -1195,7 +1314,7 @@ unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int v
}
/* Move to next entry */
- p = q + len;
+ p = q + e.len;
}
return NULL;
@@ -1208,8 +1327,9 @@ unsigned int ziplistLen(unsigned char *zl) {
len = intrev16ifbe(ZIPLIST_LENGTH(zl));
} else {
unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
+ size_t zlbytes = intrev32ifbe(ZIPLIST_BYTES(zl));
while (*p != ZIP_END) {
- p += zipRawEntryLength(p);
+ p += zipRawEntryLengthSafe(zl, zlbytes, p);
len++;
}
@@ -1228,6 +1348,7 @@ void ziplistRepr(unsigned char *zl) {
unsigned char *p;
int index = 0;
zlentry entry;
+ size_t zlbytes = ziplistBlobLen(zl);
printf(
"{total bytes %u} "
@@ -1238,7 +1359,7 @@ void ziplistRepr(unsigned char *zl) {
intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)));
p = ZIPLIST_ENTRY_HEAD(zl);
while(*p != ZIP_END) {
- zipEntry(p, &entry);
+ assert(zipEntrySafe(zl, zlbytes, p, &entry, 1));
printf(
"{\n"
"\taddr 0x%08lx,\n"
@@ -1282,6 +1403,63 @@ void ziplistRepr(unsigned char *zl) {
printf("{end}\n\n");
}
+/* Validate the integrity of the data stracture.
+ * when `deep` is 0, only the integrity of the header is validated.
+ * when `deep` is 1, we scan all the entries one by one. */
+int ziplistValidateIntegrity(unsigned char *zl, size_t size, int deep) {
+ /* check that we can actually read the header. (and ZIP_END) */
+ if (size < ZIPLIST_HEADER_SIZE + ZIPLIST_END_SIZE)
+ return 0;
+
+ /* check that the encoded size in the header must match the allocated size. */
+ size_t bytes = intrev32ifbe(ZIPLIST_BYTES(zl));
+ if (bytes != size)
+ return 0;
+
+ /* the last byte must be the terminator. */
+ if (zl[size - ZIPLIST_END_SIZE] != ZIP_END)
+ return 0;
+
+ /* make sure the tail offset isn't reaching outside the allocation. */
+ if (intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)) > size - ZIPLIST_END_SIZE)
+ return 0;
+
+ if (!deep)
+ return 1;
+
+ unsigned int count = 0;
+ unsigned char *p = ZIPLIST_ENTRY_HEAD(zl);
+ unsigned char *prev = NULL;
+ size_t prev_raw_size = 0;
+ while(*p != ZIP_END) {
+ struct zlentry e;
+ /* Decode the entry headers and fail if invalid or reaches outside the allocation */
+ if (!zipEntrySafe(zl, size, p, &e, 1))
+ return 0;
+
+ /* Make sure the record stating the prev entry size is correct. */
+ if (e.prevrawlen != prev_raw_size)
+ return 0;
+
+ /* Move to the next entry */
+ prev_raw_size = e.headersize + e.len;
+ prev = p;
+ p += e.headersize + e.len;
+ count++;
+ }
+
+ /* Make sure the <zltail> entry really do point to the start of the last entry. */
+ if (prev != ZIPLIST_ENTRY_TAIL(zl))
+ return 0;
+
+ /* Check that the count in the header is correct */
+ unsigned int header_count = intrev16ifbe(ZIPLIST_LENGTH(zl));
+ if (header_count != UINT16_MAX && count != header_count)
+ return 0;
+
+ return 1;
+}
+
#ifdef REDIS_TEST
#include <sys/time.h>
#include "adlist.h"
@@ -1784,6 +1962,7 @@ int ziplistTest(int argc, char **argv) {
printf("Create long list and check indices:\n");
{
+ unsigned long long start = usec();
zl = ziplistNew();
char buf[32];
int i,len;
@@ -1800,7 +1979,7 @@ int ziplistTest(int argc, char **argv) {
assert(ziplistGet(p,NULL,NULL,&value));
assert(999-i == value);
}
- printf("SUCCESS\n\n");
+ printf("SUCCESS. usec=%lld\n\n", usec()-start);
zfree(zl);
}
@@ -1907,6 +2086,7 @@ int ziplistTest(int argc, char **argv) {
printf("Stress with random payloads of different encoding:\n");
{
+ unsigned long long start = usec();
int i,j,len,where;
unsigned char *p;
char buf[1024];
@@ -1979,13 +2159,15 @@ int ziplistTest(int argc, char **argv) {
zfree(zl);
listRelease(ref);
}
- printf("SUCCESS\n\n");
+ printf("Done. usec=%lld\n\n", usec()-start);
}
printf("Stress with variable ziplist size:\n");
{
+ unsigned long long start = usec();
stress(ZIPLIST_HEAD,100000,16384,256);
stress(ZIPLIST_TAIL,100000,16384,256);
+ printf("Done. usec=%lld\n\n", usec()-start);
}
printf("Stress __ziplistCascadeUpdate:\n");