diff options
author | delphij <delphij@FreeBSD.org> | 2009-03-28 06:47:05 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2009-03-28 06:47:05 +0000 |
commit | 643b09b14049932ff77ddf2e99f6935754899f36 (patch) | |
tree | 4c32b507a13d50721992f7b201c59d4ff430acfe /lib/libc/db | |
parent | e48618535619834d141c38be9c90eb68ebbc296a (diff) | |
download | FreeBSD-src-643b09b14049932ff77ddf2e99f6935754899f36.zip FreeBSD-src-643b09b14049932ff77ddf2e99f6935754899f36.tar.gz |
- If (keysize+datasize)%(bsize=14)==0, insertion of a `big key' would cause
an invariant (actually, an ugly hack) to fail, and all Hell would break
loose.
When deleting a big key, the offset of an empty page should be bsize, not
bsize-1; otherwise an insertion into the empty page will cause the new key to
be elongated by 1 byte.
Make the packing more dense in a couple of cases.
- fix NULL dereference exposed on big bsize values;
Obtained from: NetBSD via OpenBSD
Diffstat (limited to 'lib/libc/db')
-rw-r--r-- | lib/libc/db/hash/hash_bigkey.c | 24 |
1 files changed, 18 insertions, 6 deletions
diff --git a/lib/libc/db/hash/hash_bigkey.c b/lib/libc/db/hash/hash_bigkey.c index 32db064..c1914a5 100644 --- a/lib/libc/db/hash/hash_bigkey.c +++ b/lib/libc/db/hash/hash_bigkey.c @@ -118,18 +118,30 @@ __big_insert(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) return (-1); n = p[0]; if (!key_size) { - if (FREESPACE(p)) { - move_bytes = MIN(FREESPACE(p), val_size); + space = FREESPACE(p); + if (space) { + move_bytes = MIN(space, val_size); + /* + * If the data would fit exactly in the + * remaining space, we must overflow it to the + * next page; otherwise the invariant that the + * data must end on a page with FREESPACE + * non-zero would fail. + */ + if (space == val_size && val_size == val->size) + goto toolarge; off = OFFSET(p) - move_bytes; - p[n] = off; memmove(cp + off, val_data, move_bytes); val_data += move_bytes; val_size -= move_bytes; + p[n] = off; p[n - 2] = FULL_KEY_DATA; FREESPACE(p) = FREESPACE(p) - move_bytes; OFFSET(p) = off; - } else + } else { + toolarge: p[n - 2] = FULL_KEY; + } } p = (u_int16_t *)bufp->page; cp = bufp->page; @@ -239,12 +251,12 @@ __big_delete(HTAB *hashp, BUFHEAD *bufp) n -= 2; bp[0] = n; FREESPACE(bp) = hashp->BSIZE - PAGE_META(n); - OFFSET(bp) = hashp->BSIZE - 1; + OFFSET(bp) = hashp->BSIZE; bufp->flags |= BUF_MOD; if (rbufp) __free_ovflpage(hashp, rbufp); - if (last_bfp != rbufp) + if (last_bfp && last_bfp != rbufp) __free_ovflpage(hashp, last_bfp); hashp->NKEYS--; |