1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16/*
17 * Check access to fields and methods.
18 */
19#include "Dalvik.h"
20
21/*
22 * Return the #of initial characters that match.
23 */
24static int strcmpCount(const char* str1, const char* str2)
25{
26    int count = 0;
27
28    while (true) {
29        char ch = str1[count];
30        if (ch == '\0' || ch != str2[count])
31            return count;
32        count++;
33    }
34}
35
36/*
37 * Returns "true" if the two classes are in the same runtime package.
38 */
39bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2)
40{
41    /* quick test for intra-class access */
42    if (class1 == class2)
43        return true;
44
45    /* class loaders must match */
46    if (class1->classLoader != class2->classLoader)
47        return false;
48
49    /*
50     * Switch array classes to their element types.  Arrays receive the
51     * class loader of the underlying element type.  The point of doing
52     * this is to get the un-decorated class name, without all the
53     * "[[L...;" stuff.
54     */
55    if (dvmIsArrayClass(class1))
56        class1 = class1->elementClass;
57    if (dvmIsArrayClass(class2))
58        class2 = class2->elementClass;
59
60    /* check again */
61    if (class1 == class2)
62        return true;
63
64    /*
65     * We have two classes with different names.  Compare them and see
66     * if they match up through the final '/'.
67     *
68     *  Ljava/lang/Object; + Ljava/lang/Class;          --> true
69     *  LFoo;              + LBar;                      --> true
70     *  Ljava/lang/Object; + Ljava/io/File;             --> false
71     *  Ljava/lang/Object; + Ljava/lang/reflect/Method; --> false
72     */
73    int commonLen;
74
75    commonLen = strcmpCount(class1->descriptor, class2->descriptor);
76    if (strchr(class1->descriptor + commonLen, '/') != NULL ||
77        strchr(class2->descriptor + commonLen, '/') != NULL)
78    {
79        return false;
80    }
81
82    return true;
83}
84
85/*
86 * Validate method/field access.
87 */
88static bool checkAccess(const ClassObject* accessFrom,
89    const ClassObject* accessTo, u4 accessFlags)
90{
91    /* quick accept for public access */
92    if (accessFlags & ACC_PUBLIC)
93        return true;
94
95    /* quick accept for access from same class */
96    if (accessFrom == accessTo)
97        return true;
98
99    /* quick reject for private access from another class */
100    if (accessFlags & ACC_PRIVATE)
101        return false;
102
103    /*
104     * Semi-quick test for protected access from a sub-class, which may or
105     * may not be in the same package.
106     */
107    if (accessFlags & ACC_PROTECTED)
108        if (dvmIsSubClass(accessFrom, accessTo))
109            return true;
110
111    /*
112     * Allow protected and private access from other classes in the same
113     * package.
114     */
115    return dvmInSamePackage(accessFrom, accessTo);
116}
117
118/*
119 * Determine whether the "accessFrom" class is allowed to get at "clazz".
120 *
121 * It's allowed if "clazz" is public or is in the same package.  (Only
122 * inner classes can be marked "private" or "protected", so we don't need
123 * to check for it here.)
124 */
125bool dvmCheckClassAccess(const ClassObject* accessFrom,
126    const ClassObject* clazz)
127{
128    if (dvmIsPublicClass(clazz))
129        return true;
130    return dvmInSamePackage(accessFrom, clazz);
131}
132
133/*
134 * Determine whether the "accessFrom" class is allowed to get at "method".
135 */
136bool dvmCheckMethodAccess(const ClassObject* accessFrom, const Method* method)
137{
138    return checkAccess(accessFrom, method->clazz, method->accessFlags);
139}
140
141/*
142 * Determine whether the "accessFrom" class is allowed to get at "field".
143 */
144bool dvmCheckFieldAccess(const ClassObject* accessFrom, const Field* field)
145{
146    //ALOGI("CHECK ACCESS from '%s' to field '%s' (in %s) flags=%#x",
147    //    accessFrom->descriptor, field->name,
148    //    field->clazz->descriptor, field->accessFlags);
149    return checkAccess(accessFrom, field->clazz, field->accessFlags);
150}
151