nsresult
leakmonJSObjectInfo::AppendProperty(jsid aID, JSContext *aCx,
                                    leakmonObjectsInReportTable &aObjectsInReport)
{
	jsval n;
	JSBool ok = JS_IdToValue(aCx, aID, &n);
	NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);

	// n should be an integer, a string, or an XML QName,
	// AttributeName, or AnyName

	// XXX This can execute JS code!  How bad is that?
	// shaver didn't seem too scared when I described it to him.
	// XXX Could I avoid JS_ValueToString and still handle XML objects
	// correctly?
	JSString *nstr = JS_ValueToString(aCx, n);
	NS_ENSURE_TRUE(nstr, NS_ERROR_OUT_OF_MEMORY);

	size_t propname_len;
	const jschar *propname = JS_GetStringCharsAndLength(aCx, nstr, &propname_len);
	NS_ENSURE_TRUE(propname, NS_ERROR_OUT_OF_MEMORY);

	// XXX JS_GetUCProperty can execute JS code!  How bad is that?
	// shaver didn't seem too scared when I described it to him.
	// Since js_GetProperty starts with a call to js_LookupProperty,
	// it's clear that JS_LookupUCProperty does less than
	// JS_GetUCProperty, so prefer Lookup over Get (although it's not
	// clear to me exactly what the differences are).
	jsval v;
	ok = JS_LookupPropertyById(aCx, JSVAL_TO_OBJECT(mJSValue), aID, &v);
	NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);

	leakmonJSObjectInfo *info;
	void *key = reinterpret_cast<void*>(JSVAL_BITS(v));
	if (!aObjectsInReport.Get(key, &info)) {
		info = new leakmonJSObjectInfo(v);
		NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
		aObjectsInReport.Put(key, info);
	}

	PropertyStruct *ps = mProperties.AppendElement();
	ps->mName.Assign(reinterpret_cast<const PRUnichar*>(propname), propname_len);
	ps->mValue = info;

	return NS_OK;
}
Example #2
0
bool
ExposedPropertiesOnly::check(JSContext *cx, HandleObject wrapper, HandleId id, Wrapper::Action act)
{
    RootedObject wrappedObject(cx, Wrapper::wrappedObject(wrapper));

    if (act == Wrapper::CALL)
        return true;


    // For the case of getting a property descriptor, we allow if either GET or SET
    // is allowed, and rely on FilteringWrapper to filter out any disallowed accessors.
    if (act == Wrapper::GET_PROPERTY_DESCRIPTOR) {
        return check(cx, wrapper, id, Wrapper::GET) ||
               check(cx, wrapper, id, Wrapper::SET);
    }

    RootedId exposedPropsId(cx, GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS));

    // We need to enter the wrappee's compartment to look at __exposedProps__,
    // but we want to be in the wrapper's compartment if we call Deny().
    //
    // Unfortunately, |cx| can be in either compartment when we call ::check. :-(
    JSAutoCompartment ac(cx, wrappedObject);

    bool found = false;
    if (!JS_HasPropertyById(cx, wrappedObject, exposedPropsId, &found))
        return false;

    // Always permit access to "length" and indexed properties of arrays.
    if ((JS_IsArrayObject(cx, wrappedObject) ||
         JS_IsTypedArrayObject(wrappedObject)) &&
        ((JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) ||
         (JSID_IS_STRING(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length")))) {
        return true; // Allow
    }

    // If no __exposedProps__ existed, deny access.
    if (!found) {
        return false;
    }

    if (id == JSID_VOID)
        return true;

    RootedValue exposedProps(cx);
    if (!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, &exposedProps))
        return false;

    if (exposedProps.isNullOrUndefined())
        return false;

    if (!exposedProps.isObject()) {
        EnterAndThrow(cx, wrapper, "__exposedProps__ must be undefined, null, or an Object");
        return false;
    }

    RootedObject hallpass(cx, &exposedProps.toObject());

    if (!AccessCheck::subsumes(js::UncheckedUnwrap(hallpass), wrappedObject)) {
        EnterAndThrow(cx, wrapper, "Invalid __exposedProps__");
        return false;
    }

    Access access = NO_ACCESS;

    Rooted<JSPropertyDescriptor> desc(cx);
    if (!JS_GetPropertyDescriptorById(cx, hallpass, id, &desc)) {
        return false; // Error
    }
    if (!desc.object() || !desc.isEnumerable())
        return false;

    if (!desc.value().isString()) {
        EnterAndThrow(cx, wrapper, "property must be a string");
        return false;
    }

    JSFlatString *flat = JS_FlattenString(cx, desc.value().toString());
    if (!flat)
        return false;

    size_t length = JS_GetStringLength(JS_FORGET_STRING_FLATNESS(flat));

    for (size_t i = 0; i < length; ++i) {
        char16_t ch = JS_GetFlatStringCharAt(flat, i);
        switch (ch) {
        case 'r':
            if (access & READ) {
                EnterAndThrow(cx, wrapper, "duplicate 'readable' property flag");
                return false;
            }
            access = Access(access | READ);
            break;

        case 'w':
            if (access & WRITE) {
                EnterAndThrow(cx, wrapper, "duplicate 'writable' property flag");
                return false;
            }
            access = Access(access | WRITE);
            break;

        default:
            EnterAndThrow(cx, wrapper, "properties can only be readable or read and writable");
            return false;
        }
    }

    if (access == NO_ACCESS) {
        EnterAndThrow(cx, wrapper, "specified properties must have a permission bit set");
        return false;
    }

    if ((act == Wrapper::SET && !(access & WRITE)) ||
        (act != Wrapper::SET && !(access & READ))) {
        return false;
    }

    return true;
}