js/public/Anchor.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /* JS::Anchor implementation. */
     9 #ifndef js_Anchor_h
    10 #define js_Anchor_h
    12 #include "mozilla/Attributes.h"
    14 #include "js/TypeDecls.h"
    16 namespace JS {
    18 /*
    19  * Protecting non-Value, non-JSObject *, non-JSString * values from collection
    20  *
    21  * Most of the time, the garbage collector's conservative stack scanner works
    22  * behind the scenes, finding all live values and protecting them from being
    23  * collected. However, when JSAPI client code obtains a pointer to data the
    24  * scanner does not know about, owned by an object the scanner does know about,
    25  * Care Must Be Taken.
    26  *
    27  * The scanner recognizes only a select set of types: pointers to JSObjects and
    28  * similar things (JSFunctions, and so on), pointers to JSStrings, and Values.
    29  * So while the scanner finds all live |JSString| pointers, it does not notice
    30  * |jschar| pointers.
    31  *
    32  * So suppose we have:
    33  *
    34  *   void f(JSString *str) {
    35  *     const jschar *ch = JS_GetStringCharsZ(str);
    36  *     ... do stuff with ch, but no uses of str ...;
    37  *   }
    38  *
    39  * After the call to |JS_GetStringCharsZ|, there are no further uses of
    40  * |str|, which means that the compiler is within its rights to not store
    41  * it anywhere. But because the stack scanner will not notice |ch|, there
    42  * is no longer any live value in this frame that would keep the string
    43  * alive. If |str| is the last reference to that |JSString|, and the
    44  * collector runs while we are using |ch|, the string's array of |jschar|s
    45  * may be freed out from under us.
    46  *
    47  * Note that there is only an issue when 1) we extract a thing X the scanner
    48  * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y
    49  * gets garbage-collected, then X gets freed. If we have code like this:
    50  *
    51  *   void g(JSObject *obj) {
    52  *     JS::Value x;
    53  *     JS_GetProperty(obj, "x", &x);
    54  *     ... do stuff with x ...
    55  *   }
    56  *
    57  * there's no problem, because the value we've extracted, x, is a Value, a
    58  * type that the conservative scanner recognizes.
    59  *
    60  * Conservative GC frees us from the obligation to explicitly root the types it
    61  * knows about, but when we work with derived values like |ch|, we must root
    62  * their owners, as the derived value alone won't keep them alive.
    63  *
    64  * A JS::Anchor is a kind of GC root that allows us to keep the owners of
    65  * derived values like |ch| alive throughout the Anchor's lifetime. We could
    66  * fix the above code as follows:
    67  *
    68  *   void f(JSString *str) {
    69  *     JS::Anchor<JSString *> a_str(str);
    70  *     const jschar *ch = JS_GetStringCharsZ(str);
    71  *     ... do stuff with ch, but no uses of str ...;
    72  *   }
    73  *
    74  * This simply ensures that |str| will be live until |a_str| goes out of scope.
    75  * As long as we don't retain a pointer to the string's characters for longer
    76  * than that, we have avoided all garbage collection hazards.
    77  */
    78 template<typename T> class AnchorPermitted;
    79 template<> class AnchorPermitted<JSObject *> { };
    80 template<> class AnchorPermitted<const JSObject *> { };
    81 template<> class AnchorPermitted<JSFunction *> { };
    82 template<> class AnchorPermitted<const JSFunction *> { };
    83 template<> class AnchorPermitted<JSString *> { };
    84 template<> class AnchorPermitted<const JSString *> { };
    85 template<> class AnchorPermitted<Value> { };
    86 template<> class AnchorPermitted<const JSScript *> { };
    87 template<> class AnchorPermitted<JSScript *> { };
    89 template<typename T>
    90 class Anchor : AnchorPermitted<T>
    91 {
    92   public:
    93     Anchor() { }
    94     explicit Anchor(T t) { hold = t; }
    95     inline ~Anchor();
    97   private:
    98     T hold;
   100     /*
   101      * Rooting analysis considers use of operator= to be a use of an anchor.
   102      * For simplicity, Anchor is treated as if it contained a GC thing, from
   103      * construction. Thus if we had
   104      *
   105      *   void operator=(const T &t) { hold = t; }
   106      *
   107      * and this code
   108      *
   109      *   JS::Anchor<JSString*> anchor;
   110      *   stuff that could GC, producing |str|;
   111      *   anchor = str;
   112      *
   113      * the last line would be seen as a hazard, because the final = would "use"
   114      * |anchor| that is a GC thing -- which could have been moved around by the
   115      * GC. The workaround is to structure your code so that JS::Anchor is
   116      * always constructed, living for however long the corresponding value must
   117      * live.
   118      */
   119     void operator=(const T &t) MOZ_DELETE;
   121     Anchor(const Anchor &other) MOZ_DELETE;
   122     void operator=(const Anchor &other) MOZ_DELETE;
   123 };
   125 template<typename T>
   126 inline Anchor<T>::~Anchor()
   127 {
   128 #ifdef __GNUC__
   129     /*
   130      * No code is generated for this. But because this is marked 'volatile', G++ will
   131      * assume it has important side-effects, and won't delete it. (G++ never looks at
   132      * the actual text and notices it's empty.) And because we have passed |hold| to
   133      * it, GCC will keep |hold| alive until this point.
   134      *
   135      * The "memory" clobber operand ensures that G++ will not move prior memory
   136      * accesses after the asm --- it's a barrier. Unfortunately, it also means that
   137      * G++ will assume that all memory has changed after the asm, as it would for a
   138      * call to an unknown function. I don't know of a way to avoid that consequence.
   139      */
   140     asm volatile("":: "g" (hold) : "memory");
   141 #else
   142     /*
   143      * An adequate portable substitute, for non-structure types.
   144      *
   145      * The compiler promises that, by the end of an expression statement, the
   146      * last-stored value to a volatile object is the same as it would be in an
   147      * unoptimized, direct implementation (the "abstract machine" whose behavior the
   148      * language spec describes). However, the compiler is still free to reorder
   149      * non-volatile accesses across this store --- which is what we must prevent. So
   150      * assigning the held value to a volatile variable, as we do here, is not enough.
   151      *
   152      * In our case, however, garbage collection only occurs at function calls, so it
   153      * is sufficient to ensure that the destructor's store isn't moved earlier across
   154      * any function calls that could collect. It is hard to imagine the compiler
   155      * analyzing the program so thoroughly that it could prove that such motion was
   156      * safe. In practice, compilers treat calls to the collector as opaque operations
   157      * --- in particular, as operations which could access volatile variables, across
   158      * which this destructor must not be moved.
   159      *
   160      * ("Objection, your honor!  *Alleged* killer whale!")
   161      *
   162      * The disadvantage of this approach is that it does generate code for the store.
   163      * We do need to use Anchors in some cases where cycles are tight.
   164      *
   165      * Note that there is a Anchor<Value>::~Anchor() specialization in Value.h.
   166      */
   167     volatile T sink;
   168     sink = hold;
   169 #endif  /* defined(__GNUC__) */
   170 }
   172 } // namespace JS
   174 #endif /* js_Anchor_h */

mercurial