1 | /* |
2 | * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. |
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | * |
5 | * This code is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 only, as |
7 | * published by the Free Software Foundation. Oracle designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Oracle in the LICENSE file that accompanied this code. |
10 | * |
11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | * version 2 for more details (a copy is included in the LICENSE file that |
15 | * accompanied this code). |
16 | * |
17 | * You should have received a copy of the GNU General Public License version |
18 | * 2 along with this work; if not, write to the Free Software Foundation, |
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
20 | * |
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 | * or visit www.oracle.com if you need additional information or have any |
23 | * questions. |
24 | */ |
25 | |
26 | #include <assert.h> |
27 | #include <limits.h> |
28 | #include <setjmp.h> |
29 | #include <stdlib.h> |
30 | #include <string.h> |
31 | |
32 | #include "jni.h" |
33 | #include "jvm.h" |
34 | |
35 | typedef unsigned short unicode; |
36 | |
37 | static char * |
38 | skip_over_fieldname(char *name, jboolean slash_okay, |
39 | unsigned int len); |
40 | static char * |
41 | skip_over_field_signature(char *name, jboolean void_okay, |
42 | unsigned int len); |
43 | |
44 | /* |
45 | * Return non-zero if the character is a valid in JVM class name, zero |
46 | * otherwise. The only characters currently disallowed from JVM class |
47 | * names are given in the table below: |
48 | * |
49 | * Character Hex Decimal |
50 | * '.' 0x2e 46 |
51 | * '/' 0x2f 47 |
52 | * ';' 0x3b 59 |
53 | * '[' 0x5b 91 |
54 | * |
55 | * (Method names have further restrictions dealing with the '<' and |
56 | * '>' characters.) |
57 | */ |
58 | static int isJvmIdentifier(unicode ch) { |
59 | if( ch > 91 || ch < 46 ) |
60 | return 1; /* Lowercase ASCII letters are > 91 */ |
61 | else { /* 46 <= ch <= 91 */ |
62 | if (ch <= 90 && ch >= 60) { |
63 | return 1; /* Uppercase ASCII recognized here */ |
64 | } else { /* ch == 91 || 46 <= ch <= 59 */ |
65 | if (ch == 91 || ch == 59 || ch <= 47) |
66 | return 0; |
67 | else |
68 | return 1; |
69 | } |
70 | } |
71 | } |
72 | |
73 | static unicode |
74 | next_utf2unicode(char **utfstring_ptr, int * valid) |
75 | { |
76 | unsigned char *ptr = (unsigned char *)(*utfstring_ptr); |
77 | unsigned char ch, ch2, ch3; |
78 | int length = 1; /* default length */ |
79 | unicode result = 0x80; /* default bad result; */ |
80 | *valid = 1; |
81 | switch ((ch = ptr[0]) >> 4) { |
82 | default: |
83 | result = ch; |
84 | break; |
85 | |
86 | case 0x8: case 0x9: case 0xA: case 0xB: case 0xF: |
87 | /* Shouldn't happen. */ |
88 | *valid = 0; |
89 | break; |
90 | |
91 | case 0xC: case 0xD: |
92 | /* 110xxxxx 10xxxxxx */ |
93 | if (((ch2 = ptr[1]) & 0xC0) == 0x80) { |
94 | unsigned char high_five = ch & 0x1F; |
95 | unsigned char low_six = ch2 & 0x3F; |
96 | result = (high_five << 6) + low_six; |
97 | length = 2; |
98 | } |
99 | break; |
100 | |
101 | case 0xE: |
102 | /* 1110xxxx 10xxxxxx 10xxxxxx */ |
103 | if (((ch2 = ptr[1]) & 0xC0) == 0x80) { |
104 | if (((ch3 = ptr[2]) & 0xC0) == 0x80) { |
105 | unsigned char high_four = ch & 0x0f; |
106 | unsigned char mid_six = ch2 & 0x3f; |
107 | unsigned char low_six = ch3 & 0x3f; |
108 | result = (((high_four << 6) + mid_six) << 6) + low_six; |
109 | length = 3; |
110 | } else { |
111 | length = 2; |
112 | } |
113 | } |
114 | break; |
115 | } /* end of switch */ |
116 | |
117 | *utfstring_ptr = (char *)(ptr + length); |
118 | return result; |
119 | } |
120 | |
121 | /* Take pointer to a string. Skip over the longest part of the string that |
122 | * could be taken as a fieldname. Allow '/' if slash_okay is JNI_TRUE. |
123 | * |
124 | * Return a pointer to just past the fieldname. Return NULL if no fieldname |
125 | * at all was found, or in the case of slash_okay being true, we saw |
126 | * consecutive slashes (meaning we were looking for a qualified path but |
127 | * found something that was badly-formed). |
128 | */ |
129 | static char * |
130 | skip_over_fieldname(char *name, jboolean slash_okay, |
131 | unsigned int length) |
132 | { |
133 | char *p; |
134 | unicode ch; |
135 | unicode last_ch = 0; |
136 | int valid = 1; |
137 | /* last_ch == 0 implies we are looking at the first char. */ |
138 | for (p = name; p != name + length; last_ch = ch) { |
139 | char *old_p = p; |
140 | ch = *p; |
141 | if (ch < 128) { |
142 | p++; |
143 | if (isJvmIdentifier(ch)) { |
144 | continue; |
145 | } |
146 | } else { |
147 | char *tmp_p = p; |
148 | ch = next_utf2unicode(&tmp_p, &valid); |
149 | if (valid == 0) |
150 | return 0; |
151 | p = tmp_p; |
152 | if (isJvmIdentifier(ch)) { |
153 | continue; |
154 | } |
155 | } |
156 | |
157 | if (slash_okay && ch == '/' && last_ch) { |
158 | if (last_ch == '/') { |
159 | return 0; /* Don't permit consecutive slashes */ |
160 | } |
161 | } else if (ch == '_' || ch == '$') { |
162 | } else { |
163 | return last_ch ? old_p : 0; |
164 | } |
165 | } |
166 | return last_ch ? p : 0; |
167 | } |
168 | |
169 | /* Take pointer to a string. Skip over the longest part of the string that |
170 | * could be taken as a field signature. Allow "void" if void_okay. |
171 | * |
172 | * Return a pointer to just past the signature. Return NULL if no legal |
173 | * signature is found. |
174 | */ |
175 | |
176 | static char * |
177 | skip_over_field_signature(char *name, jboolean void_okay, |
178 | unsigned int length) |
179 | { |
180 | unsigned int array_dim = 0; |
181 | for (;length > 0;) { |
182 | switch (name[0]) { |
183 | case JVM_SIGNATURE_VOID: |
184 | if (!void_okay) return 0; |
185 | /* FALL THROUGH */ |
186 | case JVM_SIGNATURE_BOOLEAN: |
187 | case JVM_SIGNATURE_BYTE: |
188 | case JVM_SIGNATURE_CHAR: |
189 | case JVM_SIGNATURE_SHORT: |
190 | case JVM_SIGNATURE_INT: |
191 | case JVM_SIGNATURE_FLOAT: |
192 | case JVM_SIGNATURE_LONG: |
193 | case JVM_SIGNATURE_DOUBLE: |
194 | return name + 1; |
195 | |
196 | case JVM_SIGNATURE_CLASS: { |
197 | /* Skip over the classname, if one is there. */ |
198 | char *p = |
199 | skip_over_fieldname(name + 1, JNI_TRUE, --length); |
200 | /* The next character better be a semicolon. */ |
201 | if (p && p - name - 1 > 0 && p[0] == ';') |
202 | return p + 1; |
203 | return 0; |
204 | } |
205 | |
206 | case JVM_SIGNATURE_ARRAY: |
207 | array_dim++; |
208 | /* JVMS 2nd ed. 4.10 */ |
209 | /* The number of dimensions in an array is limited to 255 ... */ |
210 | if (array_dim > 255) { |
211 | return 0; |
212 | } |
213 | /* The rest of what's there better be a legal signature. */ |
214 | name++; |
215 | length--; |
216 | void_okay = JNI_FALSE; |
217 | break; |
218 | |
219 | default: |
220 | return 0; |
221 | } |
222 | } |
223 | return 0; |
224 | } |
225 | |
226 | |
227 | /* Used in java/lang/Class.c */ |
228 | /* Determine if the specified name is legal |
229 | * UTF name for a classname. |
230 | * |
231 | * Note that this routine expects the internal form of qualified classes: |
232 | * the dots should have been replaced by slashes. |
233 | */ |
234 | JNIEXPORT jboolean |
235 | VerifyClassname(char *name, jboolean allowArrayClass) |
236 | { |
237 | size_t s = strlen(name); |
238 | assert(s <= UINT_MAX); |
239 | unsigned int length = (unsigned int)s; |
240 | char *p; |
241 | |
242 | if (length > 0 && name[0] == JVM_SIGNATURE_ARRAY) { |
243 | if (!allowArrayClass) { |
244 | return JNI_FALSE; |
245 | } else { |
246 | /* Everything that's left better be a field signature */ |
247 | p = skip_over_field_signature(name, JNI_FALSE, length); |
248 | } |
249 | } else { |
250 | /* skip over the fieldname. Slashes are okay */ |
251 | p = skip_over_fieldname(name, JNI_TRUE, length); |
252 | } |
253 | return (p != 0 && p - name == (ptrdiff_t)length); |
254 | } |
255 | |
256 | /* |
257 | * Translates '.' to '/'. Returns JNI_TRUE is any / were present. |
258 | */ |
259 | JNIEXPORT jboolean |
260 | VerifyFixClassname(char *name) |
261 | { |
262 | char *p = name; |
263 | jboolean slashesFound = JNI_FALSE; |
264 | int valid = 1; |
265 | |
266 | while (valid != 0 && *p != '\0') { |
267 | if (*p == '/') { |
268 | slashesFound = JNI_TRUE; |
269 | p++; |
270 | } else if (*p == '.') { |
271 | *p++ = '/'; |
272 | } else { |
273 | next_utf2unicode(&p, &valid); |
274 | } |
275 | } |
276 | |
277 | return slashesFound && valid != 0; |
278 | } |
279 | |