/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Check access to fields and methods.
*/
#include "Dalvik.h"
/*
* Return the #of initial characters that match.
*/
static int strcmpCount(const char* str1, const char* str2)
{
int count = 0;
while (true) {
char ch = str1[count];
if (ch == '\0' || ch != str2[count])
return count;
count++;
}
}
/*
* Returns "true" if the two classes are in the same runtime package.
*/
bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2)
{
/* quick test for intra-class access */
if (class1 == class2)
return true;
/* class loaders must match */
if (class1->classLoader != class2->classLoader)
return false;
/*
* Switch array classes to their element types. Arrays receive the
* class loader of the underlying element type. The point of doing
* this is to get the un-decorated class name, without all the
* "[[L...;" stuff.
*/
if (dvmIsArrayClass(class1))
class1 = class1->elementClass;
if (dvmIsArrayClass(class2))
class2 = class2->elementClass;
/* check again */
if (class1 == class2)
return true;
/*
* We have two classes with different names. Compare them and see
* if they match up through the final '/'.
*
* Ljava/lang/Object; + Ljava/lang/Class; --> true
* LFoo; + LBar; --> true
* Ljava/lang/Object; + Ljava/io/File; --> false
* Ljava/lang/Object; + Ljava/lang/reflect/Method; --> false
*/
int commonLen;
commonLen = strcmpCount(class1->descriptor, class2->descriptor);
if (strchr(class1->descriptor + commonLen, '/') != NULL ||
strchr(class2->descriptor + commonLen, '/') != NULL)
{
return false;
}
return true;
}
/*
* Validate method/field access.
*/
static bool checkAccess(const ClassObject* accessFrom,
const ClassObject* accessTo, u4 accessFlags)
{
/* quick accept for public access */
if (accessFlags & ACC_PUBLIC)
return true;
/* quick accept for access from same class */
if (accessFrom == accessTo)
return true;
/* quick reject for private access from another class */
if (accessFlags & ACC_PRIVATE)
return false;
/*
* Semi-quick test for protected access from a sub-class, which may or
* may not be in the same package.
*/
if (accessFlags & ACC_PROTECTED)
if (dvmIsSubClass(accessFrom, accessTo))
return true;
/*
* Allow protected and private access from other classes in the same
* package.
*/
return dvmInSamePackage(accessFrom, accessTo);
}
/*
* Determine whether the "accessFrom" class is allowed to get at "clazz".
*
* It's allowed if "clazz" is public or is in the same package. (Only
* inner classes can be marked "private" or "protected", so we don't need
* to check for it here.)
*/
bool dvmCheckClassAccess(const ClassObject* accessFrom,
const ClassObject* clazz)
{
if (dvmIsPublicClass(clazz))
return true;
return dvmInSamePackage(accessFrom, clazz);
}
/*
* Determine whether the "accessFrom" class is allowed to get at "method".
*/
bool dvmCheckMethodAccess(const ClassObject* accessFrom, const Method* method)
{
return checkAccess(accessFrom, method->clazz, method->accessFlags);
}
/*
* Determine whether the "accessFrom" class is allowed to get at "field".
*/
bool dvmCheckFieldAccess(const ClassObject* accessFrom, const Field* field)
{
//ALOGI("CHECK ACCESS from '%s' to field '%s' (in %s) flags=%#x",
// accessFrom->descriptor, field->name,
// field->clazz->descriptor, field->accessFlags);
return checkAccess(accessFrom, field->clazz, field->accessFlags);
}