Add more of the torture tests from the mit kerberos tree.

This commit is contained in:
christos 2016-09-24 20:12:33 +00:00
parent f3f19d16f2
commit 5703b437ba
3 changed files with 351 additions and 14 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.3 2015/11/18 18:35:35 christos Exp $
# $NetBSD: Makefile,v 1.4 2016/09/24 20:12:33 christos Exp $
.include <bsd.own.mk>
@ -13,6 +13,7 @@ MKMAN= no
PROGS+= h_db
PROGS+= h_lfsr
CPPFLAGS.h_db.c += -I${NETBSDSRCDIR}/lib/libc/db/btree
FILESDIR= ${TESTSDIR}

View File

@ -1,4 +1,4 @@
/* $NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $ */
/* $NetBSD: h_db.c,v 1.2 2016/09/24 20:12:33 christos Exp $ */
/*-
* Copyright (c) 1992, 1993, 1994
@ -39,7 +39,7 @@ __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
#if 0
static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94";
#else
__RCSID("$NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $");
__RCSID("$NetBSD: h_db.c,v 1.2 2016/09/24 20:12:33 christos Exp $");
#endif
#endif /* not lint */
@ -57,12 +57,13 @@ __RCSID("$NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $");
#include <unistd.h>
#include <err.h>
#include <db.h>
#include "btree.h"
enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
static void compare(DBT *, DBT *);
static DBTYPE dbtype(const char *);
static void dump(DB *, int);
static void dump(DB *, int, int);
static void get(DB *, DBT *);
static void getdata(DB *, DBT *, DBT *);
static void put(DB *, DBT *, DBT *);
@ -73,6 +74,7 @@ static void *rfile(char *, size_t *);
static void seq(DB *, DBT *);
static u_int setflags(char *);
static void *setinfo(DBTYPE, char *);
static void unlinkpg(DB *);
static void usage(void) __attribute__((__noreturn__));
static void *xcopy(void *, size_t);
static void chkcmd(enum S);
@ -82,6 +84,7 @@ static void chkkey(enum S);
#ifdef STATISTICS
extern void __bt_stat(DB *);
#endif
extern int __bt_relink(BTREE *, PAGE *);
static DBTYPE type; /* Database type. */
static void *infop; /* Iflags. */
@ -315,7 +318,13 @@ lkey: switch (command) {
}
break;
case 'o':
dump(dbp, p[1] == 'r');
dump(dbp, p[1] == 'r', 0);
break;
case 'O':
dump(dbp, p[1] == 'r', 1);
break;
case 'u':
unlinkpg(dbp);
break;
default:
errx(1, "line %zu: %s: unknown command character",
@ -483,17 +492,17 @@ seq(DB *dbp, DBT *kp)
}
static void
dump(DB *dbp, int rev)
dump(DB *dbp, int rev, int recurse)
{
DBT key, data;
int xflags, nflags;
if (rev) {
xflags = R_LAST;
nflags = R_PREV;
nflags = recurse ? R_RPREV : R_PREV;
} else {
xflags = R_FIRST;
nflags = R_NEXT;
nflags = recurse ? R_RNEXT : R_NEXT;
}
for (;; xflags = nflags)
switch (dbp->seq(dbp, &key, &data, xflags)) {
@ -511,6 +520,40 @@ dump(DB *dbp, int rev)
done: return;
}
void
unlinkpg(DB *dbp)
{
BTREE *t = dbp->internal;
PAGE *h = NULL;
pgno_t pg;
for (pg = P_ROOT; pg < t->bt_mp->npages;
mpool_put(t->bt_mp, h, 0), pg++) {
if ((h = mpool_getf(t->bt_mp, pg, 0)) == NULL)
break;
/* Look for a nonempty leaf page that has both left
* and right siblings. */
if (h->prevpg == P_INVALID || h->nextpg == P_INVALID)
continue;
if (NEXTINDEX(h) == 0)
continue;
if ((h->flags & (P_BLEAF | P_RLEAF)))
break;
}
if (h == NULL || pg == t->bt_mp->npages) {
errx(1, "%s: no appropriate page found", __func__);
return;
}
if (__bt_relink(t, h) != 0) {
perror("unlinkpg");
goto cleanup;
}
h->prevpg = P_INVALID;
h->nextpg = P_INVALID;
cleanup:
mpool_put(t->bt_mp, h, MPOOL_DIRTY);
}
static u_int
setflags(char *s)
{
@ -725,7 +768,7 @@ static void
usage(void)
{
(void)fprintf(stderr,
"Usage: %s [-l] [-f file] [-i info] [-o file] type script\n",
getprogname());
"Usage: %s [-lu] [-f file] [-i info] [-o file] [-O file] "
"type script\n", getprogname());
exit(1);
}

View File

@ -1,4 +1,4 @@
# $NetBSD: t_db.sh,v 1.6 2015/11/18 18:35:35 christos Exp $
# $NetBSD: t_db.sh,v 1.7 2016/09/24 20:12:33 christos Exp $
#
# Copyright (c) 2008 The NetBSD Foundation, Inc.
# All rights reserved.
@ -540,6 +540,7 @@ delete_recno_body()
h_repeated()
{
local type="$1"
TMPDIR="$(pwd)/db_dir"; export TMPDIR
mkdir ${TMPDIR}
@ -558,7 +559,7 @@ h_repeated()
}
}' >in
$(prog_db) btree in
$(prog_db) $type in
}
atf_test_case repeated_btree
@ -618,11 +619,10 @@ duplicate_btree_body()
h_cursor_flags()
{
local type=$1
TMPDIR="$(pwd)/db_dir"; export TMPDIR
mkdir ${TMPDIR}
type=$1
echo $SEVEN_SEVEN |
awk '{
for (i = 1; i <= 20; ++i)
@ -758,6 +758,7 @@ h_byte_orders()
echo p
echo k$i
echo d$i
echo S
echo g
echo k$i
done >in
@ -924,6 +925,292 @@ bsize_torture_body()
done
}
atf_test_case btree_weird_page_split
btree_weird_page_split_head()
{
atf_set "descr" \
"Test for a weird page split condition where an insertion " \
"into index 0 of a page that would cause the new item to " \
"be the only item on the left page results in index 0 of " \
"the right page being erroneously skipped; this only " \
"happens with one particular key+data length for each page size."
}
btree_weird_page_split_body()
{
for psize in 512 1024 2048 4096 8192; do
echo " page size $psize"
kdsizes=`awk 'BEGIN {
psize = '$psize'; hsize = int(psize/2);
for (kdsize = hsize-40; kdsize <= hsize; kdsize++) {
print kdsize;
}
}' /dev/null`
# Use a series of keylen+datalen values in the right
# neighborhood to find the one that triggers the bug.
# We could compute the exact size that triggers the
# bug but this additional fuzz may be useful.
# Insert keys in reverse order to maximize the chances
# for a split on index 0.
for kdsize in $kdsizes; do
awk 'BEGIN {
kdsize = '$kdsize';
for (i = 8; i-- > 0; ) {
s = sprintf("a%03d:%09d", i, kdsize);
for (j = 0; j < kdsize-20; j++) {
s = s "x";
}
printf("p\nka%03d\nd%s\n", i, s);
}
print "o";
}' /dev/null > in
sed -n 's/^d//p' in | sort > exp
atf_check -o file:exp \
"$(prog_db)" -i psize=$psize btree in
done
done
}
# Extremely tricky test attempting to replicate some unusual database
# corruption seen in the field: pieces of the database becoming
# inaccessible to random access, sequential access, or both. The
# hypothesis is that at least some of these are triggered by the bug
# in page splits on index 0 with a particular exact keylen+datalen.
# (See Test 40.) For psize=4096, this size is exactly 2024.
# The order of operations here relies on very specific knowledge of
# the internals of the btree access method in order to place records
# at specific offsets in a page and to create certain keys on internal
# pages. The to-be-split page immediately prior to the bug-triggering
# split has the following properties:
#
# * is not the leftmost leaf page
# * key on the parent page is compares less than the key of the item
# on index 0
# * triggering record's key also compares greater than the key on the
# parent page
# Additionally, we prime the mpool LRU chain so that the head page on
# the chain has the following properties:
#
# * record at index 0 is located where it will not get overwritten by
# items written to the right-hand page during the split
# * key of the record at index 0 compares less than the key of the
# bug-triggering record
# If the page-split bug exists, this test appears to create a database
# where some records are inaccessible to a search, but still remain in
# the file and are accessible by sequential traversal. At least one
# record gets duplicated out of sequence.
atf_test_case btree_tricky_page_split
btree_tricky_page_split_head()
{
atf_set "descr" \
"btree: no unsearchables due to page split on index 0"
}
btree_tricky_page_split_body()
{
list=`(for i in a b c d; do
for j in 990 998 999; do
echo g ${i}${j} 1024
done
done;
echo g y997 2014
for i in y z; do
for j in 998 999; do
echo g ${i}${j} 1024
done
done)`
# Exact number for trigger condition accounts for newlines
# retained by dbtest with -ofile but not without; we use
# -ofile, so count newlines. keylen=5,datalen=5+2014 for
# psize=4096 here.
(cat - <<EOF
p z999 1024
p z998 1024
p y999 1024
p y990 1024
p d999 1024
p d990 1024
p c999 1024
p c990 1024
p b999 1024
p b990 1024
p a999 1024
p a990 1024
p y998 1024
r y990
p d998 1024
p d990 1024
p c998 1024
p c990 1024
p b998 1024
p b990 1024
p a998 1024
p a990 1024
p y997 2014
S
o
EOF
echo "$list") |
# awk script input:
# {p|g|r} key [datasize]
awk '/^[pgr]/{
printf("%s\nk%s\n", $1, $2);
}
/^p/{
s = $2;
for (i = 0; i < $3; i++) {
s = s "x";
}
printf("d%s\n", s);
}
!/^[pgr]/{
print $0;
}' > in
(echo "$list"; echo "$list") | awk '{
s = $2;
for (i = 0; i < $3; i++) {
s = s "x";
}
print s;
}' > exp
atf_check -o file:exp \
"$(prog_db)" -i psize=4096 btree in
}
atf_test_case btree_recursive_traversal
btree_recursive_traversal_head()
{
atf_set "descr" \
"btree: Test for recursive traversal successfully " \
"retrieving records that are inaccessible to normal " \
"sequential 'sibling-link' traversal. This works by " \
"unlinking a few leaf pages but leaving their parent " \
"links intact. To verify that the unlink actually makes " \
"records inaccessible, the test first uses 'o' to do a " \
"normal sequential traversal, followed by 'O' to do a " \
"recursive traversal."
}
btree_recursive_traversal_body()
{
fill="abcdefghijklmnopqrstuvwxyzy"
script='{
for (i = 0; i < 20000; i++) {
printf("p\nkAA%05d\nd%05d%s\n", i, i, $0);
}
print "u";
print "u";
print "u";
print "u";
}'
(echo $fill | awk "$script"; echo o) > in1
echo $fill |
awk '{
for (i = 0; i < 20000; i++) {
if (i >= 5 && i <= 40)
continue;
printf("%05d%s\n", i, $0);
}
}' > exp1
atf_check -o file:exp1 \
"$(prog_db)" -i psize=512 btree in1
echo $fill |
awk '{
for (i = 0; i < 20000; i++) {
printf("%05d%s\n", i, $0);
}
}' > exp2
(echo $fill | awk "$script"; echo O) > in2
atf_check -o file:exp2 \
"$(prog_db)" -i psize=512 btree in2
}
atf_test_case btree_byteswap_unaligned_access_bksd
btree_byteswap_unaligned_access_bksd_head()
{
atf_set "descr" \
"btree: big key, small data, byteswap unaligned access"
}
btree_byteswap_unaligned_access_bksd_body()
{
(echo foo; echo bar) |
awk '{
s = $0
for (i = 0; i < 488; i++) {
s = s "x";
}
printf("p\nk%s\ndx\n", s);
}' > in
for order in 1234 4321; do
atf_check \
"$(prog_db)" -o out -i psize=512,lorder=$order btree in
done
}
atf_test_case btree_byteswap_unaligned_access_skbd
btree_byteswap_unaligned_access_skbd_head()
{
atf_set "descr" \
"btree: small key, big data, byteswap unaligned access"
}
btree_byteswap_unaligned_access_skbd_body()
{
# 484 = 512 - 20 (header) - 7 ("foo1234") - 1 (newline)
(echo foo1234; echo bar1234) |
awk '{
s = $0
for (i = 0; i < 484; i++) {
s = s "x";
}
printf("p\nk%s\nd%s\n", $0, s);
}' > in
for order in 1234 4321; do
atf_check \
"$(prog_db)" -o out -i psize=512,lorder=$order btree in
done
}
atf_test_case btree_known_byte_order
btree_known_byte_order_head()
{
atf_set "descr" \
"btree: small key, big data, known byte order"
}
btree_known_byte_order_body()
{
local a="-i psize=512,lorder="
(echo foo1234; echo bar1234) |
awk '{
s = $0
for (i = 0; i < 484; i++) {
s = s "x";
}
printf("%s\n", s);
}' > exp
(echo foo1234; echo bar1234) |
awk '{
s = $0
for (i = 0; i < 484; i++) {
s = s "x";
}
printf("p\nk%s\nd%s\n", $0, s);
}' > in1
for order in 1234 4321; do
atf_check \
"$(prog_db)" -f out.$order $a$order btree in1
done
(echo g; echo kfoo1234; echo g; echo kbar1234) > in2
for order in 1234 4321; do
atf_check -o file:exp \
"$(prog_db)" -s -f out.$order $a$order btree in2
done
}
atf_init_test_cases()
{
atf_add_test_case small_btree
@ -952,4 +1239,10 @@ atf_init_test_cases()
atf_add_test_case bsize_ffactor
atf_add_test_case four_char_hash
atf_add_test_case bsize_torture
atf_add_test_case btree_weird_page_split
atf_add_test_case btree_tricky_page_split
atf_add_test_case btree_recursive_traversal
atf_add_test_case btree_byteswap_unaligned_access_bksd
atf_add_test_case btree_byteswap_unaligned_access_skbd
atf_add_test_case btree_known_byte_order
}