Bug 69148

Summary: Crash due to out of bounds read/write in MarkedSpace
Product: WebKit Reporter: michaelbraithwaite
Component: JavaScriptCoreAssignee: Geoffrey Garen <ggaren>
Status: RESOLVED FIXED    
Severity: Major CC: aroben, barraclough, ggaren, oliver
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Attachments:
Description Flags
Patch oliver: review+

Description michaelbraithwaite 2011-09-30 08:17:41 PDT
With JSC from http://trac.webkit.org/browser/releases/WebKitGTK/webkit-1.4.2 on Windows and Mac.

MarkedSpace can crash as MarkedSpace::sizeClassFor() accesses m_impreciseSizeClasses out of bounds.

* Call MarkedSpace::sizeClassFor(952), e.g. from Heap::allocate(952);
* Notice this attempts to access m_impreciseSizeClasses[7] but the size of the array is only 7.
* This crashes later as it uses the address of the next member, HashSet<MarkedBlock*> m_blocks, as the SizeClass and trashes it.

Also if you pass 121-127 in to sizeClassFor() it access the out of bounds m_preciseSizeClasses[15] element but due to the class layout this is m_impreciseSizeClasses[0] so it kind of works.

I thought the bug was an out by one error in the size of both 
MarkedSpace::m_preciseSizeClasses and MarkedSpace::m_impreciseSizeClasses i.e. they should be 

static const size_t preciseCount = preciseCutoff / preciseStep;
static const size_t impreciseCount = impreciseCutoff / impreciseStep;

but the way its used in MarkedSpace::MarkedSpace() and MarkedSpace::reset() seem inconsistent with that.

I'm not clear on whether SizeClass.cellSize was meant to be an upper bound or lower bound.



Callstack:

>	jscd.dll!WTF::HashTable<WTF::StringImpl *,WTF::StringImpl *,WTF::IdentityExtractor<WTF::StringImpl *>,WTF::StringHash,WTF::HashTraits<WTF::StringImpl *>,WTF::HashTraits<WTF::StringImpl *> >::isEmptyBucket(WTF::StringImpl * const & value=)  Line 339 + 0x19 bytes	C++
 	jscd.dll!WTF::HashTable<char const *,std::pair<char const *,WTF::RefPtr<WTF::StringImpl> >,WTF::PairFirstExtractor<std::pair<char const *,WTF::RefPtr<WTF::StringImpl> > >,WTF::PtrHash<char const *>,WTF::PairHashTraits<WTF::HashTraits<char const *>,WTF::HashTraits<WTF::RefPtr<WTF::StringImpl> > >,WTF::HashTraits<char const *> >::isEmptyOrDeletedBucket(const std::pair<char const *,WTF::RefPtr<WTF::StringImpl> > & value=(...,{m_ptr=??? }))  Line 341 + 0xd bytes	C++
 	jscd.dll!WTF::HashTableConstIterator<WTF::StringImpl *,WTF::StringImpl *,WTF::IdentityExtractor<WTF::StringImpl *>,WTF::StringHash,WTF::HashTraits<WTF::StringImpl *>,WTF::HashTraits<WTF::StringImpl *> >::skipEmptyBuckets()  Line 109 + 0x18 bytes	C++
 	jscd.dll!WTF::HashTableConstIterator<WTF::StringImpl *,WTF::StringImpl *,WTF::IdentityExtractor<WTF::StringImpl *>,WTF::StringHash,WTF::HashTraits<WTF::StringImpl *>,WTF::HashTraits<WTF::StringImpl *> >::HashTableConstIterator<WTF::StringImpl *,WTF::StringImpl *,WTF::IdentityExtractor<WTF::StringImpl *>,WTF::StringHash,WTF::HashTraits<WTF::StringImpl *>,WTF::HashTraits<WTF::StringImpl *> >(const WTF::HashTable<WTF::StringImpl *,WTF::StringImpl *,WTF::IdentityExtractor<WTF::StringImpl *>,WTF::StringHash,WTF::HashTraits<WTF::StringImpl *>,WTF::HashTraits<WTF::StringImpl *> > * table=0x0d1eaff4, WTF::StringImpl * const * position=0x00000000, WTF::StringImpl * const * endPosition=0x00000100)  Line 118	C++
 	jscd.dll!WTF::HashTable<WTF::StringImpl *,WTF::StringImpl *,WTF::IdentityExtractor<WTF::StringImpl *>,WTF::StringHash,WTF::HashTraits<WTF::StringImpl *>,WTF::HashTraits<WTF::StringImpl *> >::makeConstIterator(WTF::StringImpl * * pos=0x00000000)  Line 392 + 0x26 bytes	C++
 	jscd.dll!WTF::HashTable<WTF::StringImpl *,WTF::StringImpl *,WTF::IdentityExtractor<WTF::StringImpl *>,WTF::StringHash,WTF::HashTraits<WTF::StringImpl *>,WTF::HashTraits<WTF::StringImpl *> >::begin()  Line 310 + 0x19 bytes	C++
 	jscd.dll!WTF::HashSet<JSC::MarkedBlock *,WTF::PtrHash<JSC::MarkedBlock *>,WTF::HashTraits<JSC::MarkedBlock *> >::begin()  Line 139 + 0xc bytes	C++
 	jscd.dll!JSC::MarkedSpace::clearMarks()  Line 113 + 0x12 bytes	C++
 	jscd.dll!JSC::Heap::markRoots()  Line 227	C++
 	jscd.dll!JSC::Heap::reset(JSC::Heap::SweepToggle sweepToggle=DoNotSweep)  Line 378	C++
 	jscd.dll!JSC::Heap::allocateSlowCase(unsigned int bytes=952)  Line 126	C++
 	npturbulenz.dll!JSC::Heap::allocate(unsigned int bytes=952)  Line 420	C++
 	npturbulenz.dll!JSC::JSCell::operator new(unsigned int size=952, JSC::JSGlobalData * globalData=0x0d1e9fd8)  Line 425	C++
Comment 1 Geoffrey Garen 2011-09-30 12:15:37 PDT
> I'm not clear on whether SizeClass.cellSize was meant to be an upper bound or lower bound.

Upper bound.
Comment 2 Geoffrey Garen 2011-09-30 12:57:58 PDT
The bug here is that 952 is bigger than the biggest object the Heap can allocate. Another bug is that the Heap's object size assertions did not kick in, since they assume that the Heap can allocate up to maxCellSize, when in reality it can only allocate up to maxCellSize - impreciseStep.
Comment 3 Geoffrey Garen 2011-09-30 13:03:49 PDT
Created attachment 109333 [details]
Patch
Comment 4 Geoffrey Garen 2011-09-30 14:14:01 PDT
Committed r96424: <http://trac.webkit.org/changeset/96424>
Comment 5 Adam Roben (:aroben) 2011-10-01 20:25:56 PDT
Is it possible to write a test for this?
Comment 6 Geoffrey Garen 2011-10-02 15:03:03 PDT
(In reply to comment #5)
> Is it possible to write a test for this?

It's not currently possible because there are no subclasses of JSCell that are 952 bytes. I'm not sure how WebKitGTK ran into this issue. (Maybe I'm forgetting about a particular subclass, though?)
Comment 7 Adam Roben (:aroben) 2011-10-03 08:14:16 PDT
(In reply to comment #6)
> (In reply to comment #5)
> > Is it possible to write a test for this?
> 
> It's not currently possible because there are no subclasses of JSCell that are 952 bytes. I'm not sure how WebKitGTK ran into this issue. (Maybe I'm forgetting about a particular subclass, though?)

Thanks for the explanation. In the future it would be nice to put information like this in the ChangeLog.