Bug#694971: ia64 (Itanium) Epiphany browser crashes within JSC::JSArray::increaseVectorLength()
Stephan Schreiber
info at fs-driver.org
Sun Dec 2 19:11:21 UTC 2012
Package: libwebkitgtk-3.0-0
Version: 1.8.1-3.3
Severity: grave
Tags: patch
Machine: Dell PowerEdge 3250
Processor: 2x Itanium Madison 1.5GHz 6M
Memory: 16G
I realized this bug while working on bug#642750.
The Epiphany browser crashed with a SIGSEGV in
JSC::JSArray::increaseVectorLength()
I built the libwebkitgtk-3.0-0 package which was configured with
--enable-debug.
Furthermore, I modified the Source/JavaScriptCore/wtf/Assertions.h in
order to be able to continue subsequent to a failed assertion; I
defined CRASH() to expand to nothing.
bool JSArray::increaseVectorLength(JSGlobalData& globalData, unsigned
newLength)
{
// This function leaves the array in an internally inconsistent
state, because it does not move any values from sparse value map
// to the vector. Callers have to account for that, because they
can do it more efficiently.
if (newLength > MAX_STORAGE_VECTOR_LENGTH)
return false;
ArrayStorage* storage = m_storage;
unsigned vectorLength = m_vectorLength;
ASSERT(newLength > vectorLength);
unsigned newVectorLength = getNewVectorLength(newLength);
// Fast case - there is no precapacity. In these cases a realloc
makes sense.
if (LIKELY(!m_indexBias)) {
void* newStorage = storage->m_allocBase;
if (!globalData.heap.tryReallocateStorage(&newStorage,
storageSize(vectorLength), storageSize(newVectorLength)))
return false;
storage = m_storage =
reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newStorage));
m_storage->m_allocBase = newStorage;
ASSERT(m_storage->m_allocBase);
WriteBarrier<Unknown>* vector = storage->m_vector;
for (unsigned i = vectorLength; i < newVectorLength; ++i)
vector[i].clear(); <=================== here the
crash occurs
m_vectorLength = newVectorLength;
return true;
}
It turned out that tryReallocateStorage() allocated a memory block
that is smaller than requested with the last parameter of the function.
When it occured, the requested size was quite large and the code of
the following function was executed
(Source/JavaScriptCore/heap/CopiedSpaceInlineMethods.h):
inline CheckedBoolean CopiedSpace::tryAllocateOversize(size_t bytes,
void** outPtr)
{
ASSERT(isOversize(bytes));
size_t blockSize =
WTF::roundUpToMultipleOf<s_pageSize>(sizeof(CopiedBlock) + bytes);
PageAllocationAligned allocation =
PageAllocationAligned::allocate(blockSize, s_pageSize,
OSAllocator::JSGCHeapPages);
if (!static_cast<bool>(allocation)) {
*outPtr = 0;
return false;
}
....
}
WTF::roundUpToMultipleOf<s_pageSize>() rounded up the requested size
to a multiple of 4K. s_pageSize is a constant which is defined in the
CopiedSpace class (Source/JavaScriptCore/heap/CopiedSpace.h)
static const size_t s_pageSize = 4 * KB;
At next PageAllocationAligned::allocate() is called
(Source/JavaScriptCore/wtf/PageAllocationAligned.cpp):
PageAllocationAligned PageAllocationAligned::allocate(size_t size,
size_t alignment, OSAllocator::Usage usage, bool writable, bool
executable)
{
ASSERT(isPageAligned(size));
ASSERT(isPageAligned(alignment));
ASSERT(isPowerOfTwo(alignment));
ASSERT(size >= alignment);
size_t alignmentMask = alignment - 1;
#if OS(DARWIN)
....
#else
size_t alignmentDelta = alignment - pageSize();
// Resererve with suffcient additional VM to correctly align.
size_t reservationSize = size + alignmentDelta;
void* reservationBase =
OSAllocator::reserveUncommitted(reservationSize, usage, writable,
executable);
// Select an aligned region within the reservation and commit.
void* alignedBase = reinterpret_cast<uintptr_t>(reservationBase)
& alignmentMask
?
reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(reservationBase)
& ~alignmentMask) + alignment)
: reservationBase;
OSAllocator::commit(alignedBase, size, writable, executable);
return PageAllocationAligned(alignedBase, size, reservationBase,
reservationSize);
#endif
}
The first two assertions failed:
ASSERT(isPageAligned(size));
ASSERT(isPageAligned(alignment));
The alignmentDelta variable is a 64-bits unsigned integer and
evaluated to a value of 18446744073709539328 (which is 2^64 - 12288 )
because at the line
size_t alignmentDelta = alignment - pageSize();
the 'alignment' arg is 4096 and 'pageSize()' returned 16384 - we'll
take a closer look on the latter one below.
The subsequent line
size_t reservationSize = size + alignmentDelta;
evaluated the reservationSize var to an integer value less than size
because the 64-bits integer arithmetics overflowed and wrapped around.
This is the reason why the allocated memory block was too small.
The mentioned pageSize() function returned the actual page size of
16K, which is correct.
Linux on ia64 can have 4K, 8K, 16K, or 64K page dependant on the
configuration the Kernel was compiled with. Debian uses 16K on ia64.
The mentioned pageSize() function is in
Source/JavaScriptCore/wtf/PageBlock.cpp:
static size_t s_pageSize;
#if OS(UNIX)
inline size_t systemPageSize()
{
return getpagesize();
}
#elif OS(WINDOWS)
inline size_t systemPageSize()
{
static size_t size = 0;
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
size = system_info.dwPageSize;
return size;
}
#endif
size_t pageSize()
{
if (!s_pageSize)
s_pageSize = systemPageSize();
ASSERT(isPowerOfTwo(s_pageSize));
return s_pageSize;
}
So pageSize() returns the actual memory page size that the operating
system reports.
(Don't confuse the (constant) static s_pageSize member of the the
CopiedSpace class and the static s_pageSize var of PageBlock.cpp.)
The problem is that webkit uses a mixture of hard-coded page size
values and of the actual page size value that the OS reports. If they
are different, webkit will crash.
Since you don't know the page size upon the compile time (of webkit)
on some archs, for example, ia64, the proposed patch removes any
hard-coded page size assumptions on the considered code. The patched
CopiedSpace class uses the page size which the OS reports.
I built the libwebkitgtk-3.0-0 with the patch of this bug report and
the one of bug#642750); the Epiphany browser does no longer crash
within JSC::JSArray::increaseVectorLength().
The patch is for the most recent libwebkitgtk-3.0-0 package of Wheezy.
The patches also fix bug#582774 (seed FTBFS on ia64).
The bug affects any arch that uses a page size larger than 4K. If the
next page adjacent to the (re)allocated memory was already mapped (for
another data), JSArray::increaseVectorLength() might not crash but
overwrite some other data; this bug can cause SIGSEGVs on other code
locations with completely different stack traces. I think it is worth
to check whether this patch solves some other bug reports, for
example, on MIPS.
Stephan
-------------- next part --------------
A non-text attachment was scrubbed...
Name: large-mem-page.patch
Type: application/octet-stream
Size: 11772 bytes
Desc: large-mem-page.patch
URL: <http://lists.alioth.debian.org/pipermail/pkg-webkit-maintainers/attachments/20121202/234bfdf1/attachment.obj>
More information about the Pkg-webkit-maintainers
mailing list