Skip to content

Commit e182458

Browse files
committed
Bug 1181411 - Test some expected aborts in PLDHashTable. r=glandium.
1 parent 06b4c85 commit e182458

File tree

2 files changed

+101
-13
lines changed

2 files changed

+101
-13
lines changed

toolkit/xre/nsSigHandlers.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
#endif
3939

4040
static char _progname[1024] = "huh?";
41-
static unsigned int _gdb_sleep_duration = 300;
41+
42+
// Note: some tests manipulate this value.
43+
unsigned int _gdb_sleep_duration = 300;
4244

4345
#if defined(LINUX) && defined(DEBUG) && \
4446
(defined(__i386) || defined(__x86_64) || defined(PPC))

xpcom/tests/gtest/TestPLDHash.cpp

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,109 @@
1010
// This test mostly focuses on edge cases. But more coverage of normal
1111
// operations wouldn't be a bad thing.
1212

13+
#ifdef XP_UNIX
14+
#include <unistd.h>
15+
#include <sys/types.h>
16+
#include <sys/wait.h>
17+
18+
// This global variable is defined in toolkit/xre/nsSigHandlers.cpp.
19+
extern unsigned int _gdb_sleep_duration;
20+
#endif
21+
22+
// We can test that certain operations cause expected aborts by forking
23+
// and then checking that the child aborted in the expected way (i.e. via
24+
// MOZ_CRASH). We skip this for the following configurations.
25+
// - On Windows, because it doesn't have fork().
26+
// - On non-DEBUG builds, because the crashes cause the crash reporter to pop
27+
// up when running this test locally, which is surprising and annoying.
28+
// - On ASAN builds, because ASAN alters the way a MOZ_CRASHing process
29+
// terminates, which makes it harder to test if the right thing has occurred.
30+
void
31+
TestCrashyOperation(void (*aCrashyOperation)())
32+
{
33+
#if defined(XP_UNIX) && defined(DEBUG) && !defined(MOZ_ASAN)
34+
// We're about to trigger a crash. When it happens don't pause to allow GDB
35+
// to be attached.
36+
unsigned int old_gdb_sleep_duration = _gdb_sleep_duration;
37+
_gdb_sleep_duration = 0;
38+
39+
int pid = fork();
40+
ASSERT_NE(pid, -1);
41+
42+
if (pid == 0) {
43+
// Child: perform the crashy operation.
44+
aCrashyOperation();
45+
fprintf(stderr, "TestCrashyOperation: didn't crash?!\n");
46+
ASSERT_TRUE(false); // shouldn't reach here
47+
}
48+
49+
// Parent: check that child crashed as expected.
50+
int status;
51+
ASSERT_NE(waitpid(pid, &status, 0), -1);
52+
53+
// The path taken here depends on the platform and configuration.
54+
ASSERT_TRUE(WIFEXITED(status) || WTERMSIG(status));
55+
if (WIFEXITED(status)) {
56+
// This occurs if the ah_crap_handler() is run, i.e. we caught the crash.
57+
// It returns the number of the caught signal.
58+
int signum = WEXITSTATUS(status);
59+
if (signum != SIGSEGV && signum != SIGBUS) {
60+
fprintf(stderr, "TestCrashyOperation 'exited' failure: %d\n", signum);
61+
ASSERT_TRUE(false);
62+
}
63+
} else if (WIFSIGNALED(status)) {
64+
// This one occurs if we didn't catch the crash. The exit code is the
65+
// number of the terminating signal.
66+
int signum = WTERMSIG(status);
67+
if (signum != SIGSEGV && signum != SIGBUS) {
68+
fprintf(stderr, "TestCrashyOperation 'signaled' failure: %d\n", signum);
69+
ASSERT_TRUE(false);
70+
}
71+
}
72+
73+
_gdb_sleep_duration = old_gdb_sleep_duration;
74+
#endif
75+
}
76+
77+
void
78+
InitCapacityOk_InitialLengthTooBig()
79+
{
80+
PLDHashTable t(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub),
81+
PL_DHASH_MAX_INITIAL_LENGTH + 1);
82+
}
83+
84+
void
85+
InitCapacityOk_InitialEntryStoreTooBig()
86+
{
87+
// Try the smallest disallowed power-of-two entry store size, which is 2^32
88+
// bytes (which overflows to 0). (Note that the 2^23 *length* gets converted
89+
// to a 2^24 *capacity*.)
90+
PLDHashTable t(PL_DHashGetStubOps(), (uint32_t)1 << 23, (uint32_t)1 << 8);
91+
}
92+
1393
TEST(PLDHashTableTest, InitCapacityOk)
1494
{
1595
// Try the largest allowed capacity. With PL_DHASH_MAX_CAPACITY==1<<26, this
1696
// would allocate (if we added an element) 0.5GB of entry store on 32-bit
1797
// platforms and 1GB on 64-bit platforms.
18-
//
19-
// Ideally we'd also try (a) a too-large capacity, and (b) a large capacity
20-
// combined with a large entry size that when multipled overflow. But those
21-
// cases would cause the test to abort immediately.
22-
//
23-
// Furthermore, ideally we'd also try a large-but-ok capacity that almost but
24-
// doesn't quite overflow, but that would result in allocating just under 4GB
25-
// of entry storage. That's very likely to fail on 32-bit platforms, so such
26-
// a test wouldn't be reliable.
27-
//
28-
PLDHashTable t(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub),
98+
PLDHashTable t1(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub),
2999
PL_DHASH_MAX_INITIAL_LENGTH);
100+
101+
// Try the largest allowed power-of-two entry store size, which is 2^31 bytes
102+
// (Note that the 2^23 *length* gets converted to a 2^24 *capacity*.)
103+
PLDHashTable t2(PL_DHashGetStubOps(), (uint32_t)1 << 23, (uint32_t)1 << 7);
104+
105+
// Try a too-large capacity (which aborts).
106+
TestCrashyOperation(InitCapacityOk_InitialLengthTooBig);
107+
108+
// Try a large capacity combined with a large entry size that when multiplied
109+
// overflow (causing abort).
110+
TestCrashyOperation(InitCapacityOk_InitialEntryStoreTooBig);
111+
112+
// Ideally we'd also try a large-but-ok capacity that almost but doesn't
113+
// quite overflow, but that would result in allocating slightly less than 4
114+
// GiB of entry storage. That would be very likely to fail on 32-bit
115+
// platforms, so such a test wouldn't be reliable.
30116
}
31117

32118
TEST(PLDHashTableTest, LazyStorage)
@@ -141,7 +227,7 @@ TEST(PLDHashTableTest, Clear)
141227
ASSERT_EQ(t1.EntryCount(), 0u);
142228
}
143229

144-
TEST(PLDHashTableIterator, Iterator)
230+
TEST(PLDHashTableTest, Iterator)
145231
{
146232
PLDHashTable t(&trivialOps, sizeof(PLDHashEntryStub));
147233

0 commit comments

Comments
 (0)