Changeset 153635 in webkit


Ignore:
Timestamp:
Aug 1, 2013 11:01:54 PM (11 years ago)
Author:
mrowe@apple.com
Message:

<rdar://problem/14528244> False-positive leaks from FastMalloc.

A logic error in the page map enumeration code within FastMalloc could result in a subset of the memory regions
owned by FastMalloc being skipped by the malloc zone enumeration code used by leaks and other performance tools.
If the only reference to an allocated object lived within one of the skipped memory regions, leaks would believe
it had been leaked since it would not find any references to the object.

The logic error manifested when a FastMalloc span owned a region of memory that crossed a 16MB address space boundary,
and when there was one or more other spans immediately after it in the address space. Crossing the 16MB address space
boundary means that the start and end points of the span are in different leaf nodes of the page map trie, and the
code within the page map's visitValues method didn't correctly account this case when skipping to the end of the span
after visiting it. It would resume iterating from the start of the next leaf node rather than continuing to skip values
until the end of the span was passed. The value representing the end of the span would then be processed as if it were
the start of a new span, and more values would be skipped even though they may contain actual spans.

The solution is to rework the algorithm used in visitValues so that it will skip the correct number of values even when
some of the values are in different leaf nodes. This is a more involved change than it may seem since it's also necessary
to deal with the case where a memory region spans two separate root nodes, which can happen if the region happens to cross
a 64GB boundary in the address space.

Reviewed by Geoff Garen.

  • wtf/TCPageMap.h:

(TCMalloc_PageMap3::visitValues): Use a single loop to iterate, with the loop index being the key in to the page map in the
same form as used by get and set. This allows us to correctly deal with the index being skipped to a different intermediate or
root node as a result of visiting a span that crosses a 16MB boundary in memory.
(TCMalloc_PageMap2::visitValues): Ditto, but without having to deal with intermediate nodes.

Location:
trunk/Source/WTF
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WTF/ChangeLog

    r153633 r153635  
     12013-08-01  Mark Rowe  <mrowe@apple.com>
     2
     3        <rdar://problem/14528244> False-positive leaks from FastMalloc.
     4
     5        A logic error in the page map enumeration code within FastMalloc could result in a subset of the memory regions
     6        owned by FastMalloc being skipped by the malloc zone enumeration code used by leaks and other performance tools.
     7        If the only reference to an allocated object lived within one of the skipped memory regions, leaks would believe
     8        it had been leaked since it would not find any references to the object.
     9
     10        The logic error manifested when a FastMalloc span owned a region of memory that crossed a 16MB address space boundary,
     11        and when there was one or more other spans immediately after it in the address space. Crossing the 16MB address space
     12        boundary means that the start and end points of the span are in different leaf nodes of the page map trie, and the
     13        code within the page map's visitValues method didn't correctly account this case when skipping to the end of the span
     14        after visiting it. It would resume iterating from the start of the next leaf node rather than continuing to skip values
     15        until the end of the span was passed. The value representing the end of the span would then be processed as if it were
     16        the start of a new span, and more values would be skipped even though they may contain actual spans.
     17
     18        The solution is to rework the algorithm used in visitValues so that it will skip the correct number of values even when
     19        some of the values are in different leaf nodes. This is a more involved change than it may seem since it's also necessary
     20        to deal with the case where a memory region spans two separate root nodes, which can happen if the region happens to cross
     21        a 64GB boundary in the address space.
     22
     23        Reviewed by Geoff Garen.
     24
     25        * wtf/TCPageMap.h:
     26        (TCMalloc_PageMap3::visitValues): Use a single loop to iterate, with the loop index being the key in to the page map in the
     27        same form as used by get and set. This allows us to correctly deal with the index being skipped to a different intermediate or
     28        root node as a result of visiting a span that crosses a 16MB boundary in memory.
     29        (TCMalloc_PageMap2::visitValues): Ditto, but without having to deal with intermediate nodes.
     30
    1312013-08-01  Ruth Fong  <ruth_fong@apple.com>
    232
  • trunk/Source/WTF/wtf/TCPageMap.h

    r113571 r153635  
    159159  void visitValues(Visitor& visitor, const MemoryReader& reader)
    160160  {
    161     for (int i = 0; i < ROOT_LENGTH; i++) {
    162       if (!root_[i])
    163         continue;
    164 
    165       Leaf* l = reader(reinterpret_cast<Leaf*>(root_[i]));
    166       for (int j = 0; j < LEAF_LENGTH; j += visitor.visit(l->values[j]))
    167         ;
     161    const Number leafIndexMask = LEAF_LENGTH - 1;
     162
     163    const Number maxKey = (1l << BITS) - 1;
     164    const Number invalidIndex = maxKey;
     165    Number previousRootIndex = invalidIndex;
     166
     167    Leaf* leaf = 0;
     168
     169    for (Number key = 0; key < maxKey; ) {
     170      const Number rootIndex = key >> LEAF_BITS;
     171      const Number leafIndex = key & leafIndexMask;
     172
     173      if (rootIndex != previousRootIndex) {
     174        if (!root_[rootIndex]) {
     175          // There's no node at this index. Move on to the next index at the root level,
     176          // clearing the leaf index so that we start from the beginning of the next node.
     177          key += 1 << LEAF_BITS;
     178          key &= ~leafIndexMask;
     179          continue;
     180        }
     181
     182        leaf = reader(root_[rootIndex]);
     183        previousRootIndex = rootIndex;
     184      }
     185
     186      key += visitor.visit(leaf->values[leafIndex]);
    168187    }
    169188  }
     
    268287  template<class Visitor, class MemoryReader>
    269288  void visitValues(Visitor& visitor, const MemoryReader& reader) {
     289    const Number intermediateIndexMask = (INTERIOR_LENGTH - 1) << LEAF_BITS;
     290    const Number leafIndexMask = LEAF_LENGTH - 1;
     291
     292    const Number maxKey = (1l << BITS) - 1;
     293    const Number invalidIndex = maxKey;
     294    Number previousRootIndex = invalidIndex;
     295    Number previousIntermediateIndex = invalidIndex;
     296
     297    Node* intermediateNode = 0;
     298    Leaf* leaf = 0;
     299
    270300    Node* root = reader(root_);
    271     for (int i = 0; i < INTERIOR_LENGTH; i++) {
    272       if (!root->ptrs[i])
    273         continue;
    274 
    275       Node* n = reader(root->ptrs[i]);
    276       for (int j = 0; j < INTERIOR_LENGTH; j++) {
    277         if (!n->ptrs[j])
     301    for (Number key = 0; key < maxKey; ) {
     302      const Number rootIndex = key >> (LEAF_BITS + INTERIOR_BITS);
     303      const Number intermediateIndex = (key & intermediateIndexMask) >> LEAF_BITS;
     304      const Number leafIndex = key & leafIndexMask;
     305
     306      if (rootIndex != previousRootIndex) {
     307        if (!root->ptrs[rootIndex]) {
     308          // There's no node at this index. Move on to the next index at the root level, clearing the
     309          // intermediate and leaf indices so that we start from the beginning of that next node.
     310          key += 1 << (LEAF_BITS + INTERIOR_BITS);
     311          key &= ~(leafIndexMask | intermediateIndexMask);
    278312          continue;
    279 
    280         Leaf* l = reader(reinterpret_cast<Leaf*>(n->ptrs[j]));
    281         for (int k = 0; k < LEAF_LENGTH; k += visitor.visit(l->values[k]))
    282           ;
    283       }
     313        }
     314
     315        intermediateNode = reader(root->ptrs[rootIndex]);
     316        previousRootIndex = rootIndex;
     317
     318        // Invalidate the previous intermediate index since we've moved on to a different node.
     319        previousIntermediateIndex = invalidIndex;
     320      }
     321
     322      if (intermediateIndex != previousIntermediateIndex) {
     323        if (!intermediateNode->ptrs[intermediateIndex]) {
     324          // There's no node at this index. Move on to the next index at the intermediate level,
     325          // clearing the leaf index so that we start from the beginning of the next node.
     326          key += 1 << LEAF_BITS;
     327          key &= ~leafIndexMask;
     328          continue;
     329        }
     330
     331        leaf = reader(reinterpret_cast<Leaf*>(intermediateNode->ptrs[intermediateIndex]));
     332        previousIntermediateIndex = intermediateIndex;
     333      }
     334
     335      key += visitor.visit(leaf->values[leafIndex]);
    284336    }
    285337  }
Note: See TracChangeset for help on using the changeset viewer.