1 /*
2  * Copyright (c) 2003, 2019, 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 /*
27  * Copyright 2003 Wily Technology, Inc.
28  */
29 
30 #include    <string.h>
31 #include    <stdlib.h>
32 
33 #include    "jni.h"
34 
35 #include    "Utilities.h"
36 #include    "JPLISAssert.h"
37 #include    "JPLISAgent.h"
38 #include    "JavaExceptions.h"
39 
40 #include    "EncodingSupport.h"
41 #include    "FileSystemSupport.h"
42 #include    "JarFacade.h"
43 #include    "PathCharsValidator.h"
44 
45 /**
46  * This module contains the direct interface points with the JVMTI.
47  * The OnLoad handler is here, along with the various event handlers.
48  */
49 
50 static int
51 appendClassPath(JPLISAgent* agent,
52                 const char* jarfile);
53 
54 static void
55 appendBootClassPath(JPLISAgent* agent,
56                     const char* jarfile,
57                     const char* pathList);
58 
59 
60 /*
61  * Parse -javaagent tail, of the form name[=options], into name
62  * and options. Returned values are heap allocated and options maybe
63  * NULL. Returns 0 if parse succeeds, -1 if allocation fails.
64  */
65 static int
parseArgumentTail(char * tail,char ** name,char ** options)66 parseArgumentTail(char* tail, char** name, char** options) {
67     int len;
68     char* pos;
69 
70     pos = strchr(tail, '=');
71     len = (pos == NULL) ? (int)strlen(tail) : (int)(pos - tail);
72 
73     *name = (char*)malloc(len+1);
74     if (*name == NULL) {
75         return -1;
76     }
77     memcpy(*name, tail, len);
78     (*name)[len] = '\0';
79 
80     if (pos == NULL) {
81         *options = NULL;
82     } else {
83         char * str = (char*)malloc( (int)strlen(pos + 1) + 1 );
84         if (str == NULL) {
85             free(*name);
86             return -1;
87         }
88         strcpy(str, pos +1);
89         *options = str;
90     }
91     return 0;
92 }
93 
94 /*
95  * Get the value of an attribute in an attribute list. Returns NULL
96  * if attribute not found.
97  */
98 jboolean
getBooleanAttribute(const jarAttribute * attributes,const char * name)99 getBooleanAttribute(const jarAttribute* attributes, const char* name) {
100     char* attributeValue = getAttribute(attributes, name);
101     return attributeValue != NULL && strcasecmp(attributeValue, "true") == 0;
102 }
103 
104 /*
105  * Parse any capability settings in the JAR manifest and
106  * convert them to JVM TI capabilities.
107  */
108 void
convertCapabilityAttributes(const jarAttribute * attributes,JPLISAgent * agent)109 convertCapabilityAttributes(const jarAttribute* attributes, JPLISAgent* agent) {
110     /* set redefineClasses capability */
111     if (getBooleanAttribute(attributes, "Can-Redefine-Classes")) {
112         addRedefineClassesCapability(agent);
113     }
114 
115     /* create an environment which has the retransformClasses capability */
116     if (getBooleanAttribute(attributes, "Can-Retransform-Classes")) {
117         retransformableEnvironment(agent);
118     }
119 
120     /* set setNativeMethodPrefix capability */
121     if (getBooleanAttribute(attributes, "Can-Set-Native-Method-Prefix")) {
122         addNativeMethodPrefixCapability(agent);
123     }
124 
125     /* for retransformClasses testing, set capability to use original method order */
126     if (getBooleanAttribute(attributes, "Can-Maintain-Original-Method-Order")) {
127         addOriginalMethodOrderCapability(agent);
128     }
129 }
130 
131 /*
132  *  This will be called once for every -javaagent on the command line.
133  *  Each call to Agent_OnLoad will create its own agent and agent data.
134  *
135  *  The argument tail string provided to Agent_OnLoad will be of form
136  *  <jarfile>[=<options>]. The tail string is split into the jarfile and
137  *  options components. The jarfile manifest is parsed and the value of the
138  *  Premain-Class attribute will become the agent's premain class. The jar
139  *  file is then added to the system class path, and if the Boot-Class-Path
140  *  attribute is present then all relative URLs in the value are processed
141  *  to create boot class path segments to append to the boot class path.
142  */
143 JNIEXPORT jint JNICALL
DEF_Agent_OnLoad(JavaVM * vm,char * tail,void * reserved)144 DEF_Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) {
145     JPLISInitializationError initerror  = JPLIS_INIT_ERROR_NONE;
146     jint                     result     = JNI_OK;
147     JPLISAgent *             agent      = NULL;
148 
149     initerror = createNewJPLISAgent(vm, &agent);
150     if ( initerror == JPLIS_INIT_ERROR_NONE ) {
151         int             oldLen, newLen;
152         char *          jarfile;
153         char *          options;
154         jarAttribute*   attributes;
155         char *          premainClass;
156         char *          bootClassPath;
157 
158         /*
159          * Parse <jarfile>[=options] into jarfile and options
160          */
161         if (parseArgumentTail(tail, &jarfile, &options) != 0) {
162             fprintf(stderr, "-javaagent: memory allocation failure.\n");
163             return JNI_ERR;
164         }
165 
166         /*
167          * Agent_OnLoad is specified to provide the agent options
168          * argument tail in modified UTF8. However for 1.5.0 this is
169          * actually in the platform encoding - see 5049313.
170          *
171          * Open zip/jar file and parse archive. If can't be opened or
172          * not a zip file return error. Also if Premain-Class attribute
173          * isn't present we return an error.
174          */
175         attributes = readAttributes(jarfile);
176         if (attributes == NULL) {
177             fprintf(stderr, "Error opening zip file or JAR manifest missing : %s\n", jarfile);
178             free(jarfile);
179             if (options != NULL) free(options);
180             return JNI_ERR;
181         }
182 
183         premainClass = getAttribute(attributes, "Premain-Class");
184         if (premainClass == NULL) {
185             fprintf(stderr, "Failed to find Premain-Class manifest attribute in %s\n",
186                 jarfile);
187             free(jarfile);
188             if (options != NULL) free(options);
189             freeAttributes(attributes);
190             return JNI_ERR;
191         }
192 
193         /* Save the jarfile name */
194         agent->mJarfile = jarfile;
195 
196         /*
197          * The value of the Premain-Class attribute becomes the agent
198          * class name. The manifest is in UTF8 so need to convert to
199          * modified UTF8 (see JNI spec).
200          */
201         oldLen = (int)strlen(premainClass);
202         newLen = modifiedUtf8LengthOfUtf8(premainClass, oldLen);
203         /*
204          * According to JVMS class name is represented as CONSTANT_Utf8_info,
205          * so its length is u2 (i.e. must be <= 0xFFFF).
206          * Negative oldLen or newLen means we got signed integer overflow
207          * (modifiedUtf8LengthOfUtf8 returns negative value if oldLen is negative).
208          */
209         if (oldLen < 0 || newLen < 0 || newLen > 0xFFFF) {
210             fprintf(stderr, "-javaagent: Premain-Class value is too big\n");
211             free(jarfile);
212             if (options != NULL) free(options);
213             freeAttributes(attributes);
214             return JNI_ERR;
215         }
216         if (newLen == oldLen) {
217             premainClass = strdup(premainClass);
218         } else {
219             char* str = (char*)malloc( newLen+1 );
220             if (str != NULL) {
221                 convertUtf8ToModifiedUtf8(premainClass, oldLen, str, newLen);
222             }
223             premainClass = str;
224         }
225         if (premainClass == NULL) {
226             fprintf(stderr, "-javaagent: memory allocation failed\n");
227             free(jarfile);
228             if (options != NULL) free(options);
229             freeAttributes(attributes);
230             return JNI_ERR;
231         }
232 
233         /*
234          * If the Boot-Class-Path attribute is specified then we process
235          * each relative URL and add it to the bootclasspath.
236          */
237         bootClassPath = getAttribute(attributes, "Boot-Class-Path");
238         if (bootClassPath != NULL) {
239             appendBootClassPath(agent, jarfile, bootClassPath);
240         }
241 
242         /*
243          * Convert JAR attributes into agent capabilities
244          */
245         convertCapabilityAttributes(attributes, agent);
246 
247         /*
248          * Track (record) the agent class name and options data
249          */
250         initerror = recordCommandLineData(agent, premainClass, options);
251 
252         /*
253          * Clean-up
254          */
255         if (options != NULL) free(options);
256         freeAttributes(attributes);
257         free(premainClass);
258     }
259 
260     switch (initerror) {
261     case JPLIS_INIT_ERROR_NONE:
262       result = JNI_OK;
263       break;
264     case JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT:
265       result = JNI_ERR;
266       fprintf(stderr, "java.lang.instrument/-javaagent: cannot create native agent.\n");
267       break;
268     case JPLIS_INIT_ERROR_FAILURE:
269       result = JNI_ERR;
270       fprintf(stderr, "java.lang.instrument/-javaagent: initialization of native agent failed.\n");
271       break;
272     case JPLIS_INIT_ERROR_ALLOCATION_FAILURE:
273       result = JNI_ERR;
274       fprintf(stderr, "java.lang.instrument/-javaagent: allocation failure.\n");
275       break;
276     case JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED:
277       result = JNI_ERR;
278       fprintf(stderr, "-javaagent: agent class not specified.\n");
279       break;
280     default:
281       result = JNI_ERR;
282       fprintf(stderr, "java.lang.instrument/-javaagent: unknown error\n");
283       break;
284     }
285     return result;
286 }
287 
288 /*
289  * Agent_OnAttach returns a jint. 0/JNI_OK indicates success and non-0
290  * indicates an error. To allow the attach mechanism throw an
291  * AgentInitializationException with a reasonable exception message we define
292  * a few specific errors here.
293  */
294 #define AGENT_ERROR_BADJAR    ((jint)100)  /* Agent JAR not found or no Agent-Class attribute */
295 #define AGENT_ERROR_NOTONCP   ((jint)101)  /* Unable to add JAR file to system class path */
296 #define AGENT_ERROR_STARTFAIL ((jint)102)  /* No agentmain method or agentmain failed */
297 
298 /*
299  *  This will be called once each time a tool attaches to the VM and loads
300  *  the JPLIS library.
301  */
302 JNIEXPORT jint JNICALL
DEF_Agent_OnAttach(JavaVM * vm,char * args,void * reserved)303 DEF_Agent_OnAttach(JavaVM* vm, char *args, void * reserved) {
304     JPLISInitializationError initerror  = JPLIS_INIT_ERROR_NONE;
305     jint                     result     = JNI_OK;
306     JPLISAgent *             agent      = NULL;
307     JNIEnv *                 jni_env    = NULL;
308 
309     /*
310      * Need JNIEnv - guaranteed to be called from thread that is already
311      * attached to VM
312      */
313     result = (*vm)->GetEnv(vm, (void**)&jni_env, JNI_VERSION_1_2);
314     jplis_assert(result==JNI_OK);
315 
316     initerror = createNewJPLISAgent(vm, &agent);
317     if ( initerror == JPLIS_INIT_ERROR_NONE ) {
318         int             oldLen, newLen;
319         char *          jarfile;
320         char *          options;
321         jarAttribute*   attributes;
322         char *          agentClass;
323         char *          bootClassPath;
324         jboolean        success;
325 
326         /*
327          * Parse <jarfile>[=options] into jarfile and options
328          */
329         if (parseArgumentTail(args, &jarfile, &options) != 0) {
330             return JNI_ENOMEM;
331         }
332 
333         /*
334          * Open the JAR file and parse the manifest
335          */
336         attributes = readAttributes( jarfile );
337         if (attributes == NULL) {
338             fprintf(stderr, "Error opening zip file or JAR manifest missing: %s\n", jarfile);
339             free(jarfile);
340             if (options != NULL) free(options);
341             return AGENT_ERROR_BADJAR;
342         }
343 
344         agentClass = getAttribute(attributes, "Agent-Class");
345         if (agentClass == NULL) {
346             fprintf(stderr, "Failed to find Agent-Class manifest attribute from %s\n",
347                 jarfile);
348             free(jarfile);
349             if (options != NULL) free(options);
350             freeAttributes(attributes);
351             return AGENT_ERROR_BADJAR;
352         }
353 
354         /*
355          * Add the jarfile to the system class path
356          */
357         if (appendClassPath(agent, jarfile)) {
358             fprintf(stderr, "Unable to add %s to system class path "
359                 "- not supported by system class loader or configuration error!\n",
360                 jarfile);
361             free(jarfile);
362             if (options != NULL) free(options);
363             freeAttributes(attributes);
364             return AGENT_ERROR_NOTONCP;
365         }
366 
367         /*
368          * The value of the Agent-Class attribute becomes the agent
369          * class name. The manifest is in UTF8 so need to convert to
370          * modified UTF8 (see JNI spec).
371          */
372         oldLen = (int)strlen(agentClass);
373         newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);
374         /*
375          * According to JVMS class name is represented as CONSTANT_Utf8_info,
376          * so its length is u2 (i.e. must be <= 0xFFFF).
377          * Negative oldLen or newLen means we got signed integer overflow
378          * (modifiedUtf8LengthOfUtf8 returns negative value if oldLen is negative).
379          */
380         if (oldLen < 0 || newLen < 0 || newLen > 0xFFFF) {
381             fprintf(stderr, "Agent-Class value is too big\n");
382             free(jarfile);
383             if (options != NULL) free(options);
384             freeAttributes(attributes);
385             return AGENT_ERROR_BADJAR;
386         }
387         if (newLen == oldLen) {
388             agentClass = strdup(agentClass);
389         } else {
390             char* str = (char*)malloc( newLen+1 );
391             if (str != NULL) {
392                 convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);
393             }
394             agentClass = str;
395         }
396         if (agentClass == NULL) {
397             free(jarfile);
398             if (options != NULL) free(options);
399             freeAttributes(attributes);
400             return JNI_ENOMEM;
401         }
402 
403         /*
404          * If the Boot-Class-Path attribute is specified then we process
405          * each URL - in the live phase only JAR files will be added.
406          */
407         bootClassPath = getAttribute(attributes, "Boot-Class-Path");
408         if (bootClassPath != NULL) {
409             appendBootClassPath(agent, jarfile, bootClassPath);
410         }
411 
412         /*
413          * Convert JAR attributes into agent capabilities
414          */
415         convertCapabilityAttributes(attributes, agent);
416 
417         /*
418          * Create the java.lang.instrument.Instrumentation instance
419          */
420         success = createInstrumentationImpl(jni_env, agent);
421         jplis_assert(success);
422 
423         /*
424          * Setup ClassFileLoadHook handler.
425          */
426         if (success) {
427             success = setLivePhaseEventHandlers(agent);
428             jplis_assert(success);
429         }
430 
431         /*
432          * Start the agent
433          */
434         if (success) {
435             success = startJavaAgent(agent,
436                                      jni_env,
437                                      agentClass,
438                                      options,
439                                      agent->mAgentmainCaller);
440         }
441 
442         if (!success) {
443             fprintf(stderr, "Agent failed to start!\n");
444             result = AGENT_ERROR_STARTFAIL;
445         }
446 
447         /*
448          * Clean-up
449          */
450         free(jarfile);
451         if (options != NULL) free(options);
452         free(agentClass);
453         freeAttributes(attributes);
454     }
455 
456     return result;
457 }
458 
459 
460 JNIEXPORT void JNICALL
DEF_Agent_OnUnload(JavaVM * vm)461 DEF_Agent_OnUnload(JavaVM *vm) {
462 }
463 
464 /**
465  * Invoked by the java launcher to load an agent in the main executable JAR.
466  * The Launcher-Agent-Class attribute in the main manifest of the JAR file
467  * is the agent class.
468  *
469  * Returns JNI_OK if the agent is loaded and initialized; JNI_ERR if this
470  * function fails, possibly with a pending exception.
471  */
loadAgent(JNIEnv * env,jstring path)472 jint loadAgent(JNIEnv* env, jstring path) {
473     JavaVM* vm;
474     JPLISAgent* agent;
475     const char* jarfile = NULL;
476     jarAttribute* attributes = NULL;
477     char* agentClass = NULL;
478     char* bootClassPath;
479     int oldLen, newLen;
480     jint result = JNI_ERR;
481 
482     if ((*env)->GetJavaVM(env, &vm) < 0) {
483         return JNI_ERR;
484     }
485 
486     // create JPLISAgent with JVMTI environment
487     if (createNewJPLISAgent(vm, &agent) != JPLIS_INIT_ERROR_NONE) {
488         return JNI_ERR;
489     }
490 
491     // get path to JAR file as UTF-8 string
492     jarfile = (*env)->GetStringUTFChars(env, path, NULL);
493     if (jarfile == NULL) {
494         return JNI_ERR;
495     }
496 
497     // read the attributes in the main section of JAR manifest
498     attributes = readAttributes(jarfile);
499     if (attributes == NULL) {
500         goto releaseAndReturn;
501     }
502 
503     // Launcher-Agent-Class is required
504     agentClass = getAttribute(attributes, "Launcher-Agent-Class");
505     if (agentClass == NULL) {
506         goto releaseAndReturn;
507     }
508 
509     // The value of Launcher-Agent-Class is in UTF-8, convert it to modified UTF-8
510     oldLen = (int) strlen(agentClass);
511     newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);
512     /*
513      * According to JVMS class name is represented as CONSTANT_Utf8_info,
514      * so its length is u2 (i.e. must be <= 0xFFFF).
515      * Negative oldLen or newLen means we got signed integer overflow
516      * (modifiedUtf8LengthOfUtf8 returns negative value if oldLen is negative).
517      */
518     if (oldLen < 0 || newLen < 0 || newLen > 0xFFFF) {
519         goto releaseAndReturn;
520     }
521     if (newLen == oldLen) {
522         agentClass = strdup(agentClass);
523     } else {
524         char* str = (char*) malloc(newLen + 1);
525         if (str != NULL) {
526             convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);
527         }
528         agentClass = str;
529     }
530     if (agentClass == NULL) {
531          jthrowable oome = createThrowable(env, "java/lang/OutOfMemoryError", NULL);
532          if (oome != NULL) (*env)->Throw(env, oome);
533          goto releaseAndReturn;
534     }
535 
536     // Boot-Class-Path
537     bootClassPath = getAttribute(attributes, "Boot-Class-Path");
538     if (bootClassPath != NULL) {
539         appendBootClassPath(agent, jarfile, bootClassPath);
540     }
541 
542     // Can-XXXX capabilities
543     convertCapabilityAttributes(attributes, agent);
544 
545     // Create the java.lang.instrument.Instrumentation object
546     if (!createInstrumentationImpl(env, agent)) {
547         goto releaseAndReturn;
548     }
549 
550     // Enable the ClassFileLoadHook
551     if (!setLivePhaseEventHandlers(agent)) {
552         goto releaseAndReturn;
553     }
554 
555     // invoke the agentmain method
556     if (!startJavaAgent(agent, env, agentClass, "", agent->mAgentmainCaller)) {
557         goto releaseAndReturn;
558     }
559 
560     // initialization complete
561     result = JNI_OK;
562 
563 releaseAndReturn:
564     if (agentClass != NULL) {
565         free(agentClass);
566     }
567     if (attributes != NULL) {
568         freeAttributes(attributes);
569     }
570     if (jarfile != NULL) {
571         (*env)->ReleaseStringUTFChars(env, path, jarfile);
572     }
573 
574     return result;
575 }
576 
577 /*
578  *  JVMTI callback support
579  *
580  *  We have two "stages" of callback support.
581  *  At OnLoad time, we install a VMInit handler.
582  *  When the VMInit handler runs, we remove the VMInit handler and install a
583  *  ClassFileLoadHook handler.
584  */
585 
586 void JNICALL
eventHandlerVMInit(jvmtiEnv * jvmtienv,JNIEnv * jnienv,jthread thread)587 eventHandlerVMInit( jvmtiEnv *      jvmtienv,
588                     JNIEnv *        jnienv,
589                     jthread         thread) {
590     JPLISEnvironment * environment  = NULL;
591     jboolean           success      = JNI_FALSE;
592 
593     environment = getJPLISEnvironment(jvmtienv);
594 
595     /* process the premain calls on the all the JPL agents */
596     if (environment == NULL) {
597         abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", getting JPLIS environment failed");
598     }
599     jthrowable outstandingException = NULL;
600     /*
601      * Add the jarfile to the system class path
602      */
603     JPLISAgent * agent = environment->mAgent;
604     if (appendClassPath(agent, agent->mJarfile)) {
605         fprintf(stderr, "Unable to add %s to system class path - "
606                 "the system class loader does not define the "
607                 "appendToClassPathForInstrumentation method or the method failed\n",
608                 agent->mJarfile);
609         free((void *)agent->mJarfile);
610         abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", appending to system class path failed");
611     }
612     free((void *)agent->mJarfile);
613     agent->mJarfile = NULL;
614 
615     outstandingException = preserveThrowable(jnienv);
616     success = processJavaStart( environment->mAgent, jnienv);
617     restoreThrowable(jnienv, outstandingException);
618 
619     /* if we fail to start cleanly, bring down the JVM */
620     if ( !success ) {
621         abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART ", processJavaStart failed");
622     }
623 }
624 
625 void JNICALL
eventHandlerClassFileLoadHook(jvmtiEnv * jvmtienv,JNIEnv * jnienv,jclass class_being_redefined,jobject loader,const char * name,jobject protectionDomain,jint class_data_len,const unsigned char * class_data,jint * new_class_data_len,unsigned char ** new_class_data)626 eventHandlerClassFileLoadHook(  jvmtiEnv *              jvmtienv,
627                                 JNIEnv *                jnienv,
628                                 jclass                  class_being_redefined,
629                                 jobject                 loader,
630                                 const char*             name,
631                                 jobject                 protectionDomain,
632                                 jint                    class_data_len,
633                                 const unsigned char*    class_data,
634                                 jint*                   new_class_data_len,
635                                 unsigned char**         new_class_data) {
636     JPLISEnvironment * environment  = NULL;
637 
638     environment = getJPLISEnvironment(jvmtienv);
639 
640     /* if something is internally inconsistent (no agent), just silently return without touching the buffer */
641     if ( environment != NULL ) {
642         jthrowable outstandingException = preserveThrowable(jnienv);
643         transformClassFile( environment->mAgent,
644                             jnienv,
645                             loader,
646                             name,
647                             class_being_redefined,
648                             protectionDomain,
649                             class_data_len,
650                             class_data,
651                             new_class_data_len,
652                             new_class_data,
653                             environment->mIsRetransformer);
654         restoreThrowable(jnienv, outstandingException);
655     }
656 }
657 
658 
659 
660 
661 /*
662  * URLs in Boot-Class-Path attributes are separated by one or more spaces.
663  * This function splits the attribute value into a list of path segments.
664  * The attribute value is in UTF8 but cannot contain NUL. Also non US-ASCII
665  * characters must be escaped (URI syntax) so safe to iterate through the
666  * value as a C string.
667  */
668 static void
splitPathList(const char * str,int * pathCount,char *** paths)669 splitPathList(const char* str, int* pathCount, char*** paths) {
670     int count = 0;
671     char** segments = NULL;
672     char** new_segments;
673     char* c = (char*) str;
674     while (*c != '\0') {
675         while (*c == ' ') c++;          /* skip leading spaces */
676         if (*c == '\0') {
677             break;
678         }
679         new_segments = (char**)realloc(segments, (count+1)*sizeof(char*));
680         if (new_segments == NULL) {
681             jplis_assert(0);
682             free(segments);
683             count = 0;
684             segments = NULL;
685             break;
686         }
687         segments = new_segments;
688         segments[count++] = c;
689         c = strchr(c, ' ');
690         if (c == NULL) {
691             break;
692         }
693         *c = '\0';
694         c++;
695     }
696     *pathCount = count;
697     *paths = segments;
698 }
699 
700 
701 /* URI path decoding - ported from src/share/classes/java/net/URI.java */
702 
703 static int
decodeNibble(char c)704 decodeNibble(char c) {
705     if ((c >= '0') && (c <= '9'))
706         return c - '0';
707     if ((c >= 'a') && (c <= 'f'))
708         return c - 'a' + 10;
709     if ((c >= 'A') && (c <= 'F'))
710         return c - 'A' + 10;
711     return -1;
712 }
713 
714 static int
decodeByte(char c1,char c2)715 decodeByte(char c1, char c2) {
716     return (((decodeNibble(c1) & 0xf) << 4) | ((decodeNibble(c2) & 0xf) << 0));
717 }
718 
719 /*
720  * Evaluates all escapes in s.  Assumes that escapes are well-formed
721  * syntactically, i.e., of the form %XX.
722  * If the path does not require decoding the original path is
723  * returned. Otherwise the decoded path (heap allocated) is returned,
724  * along with the length of the decoded path. Note that the return
725  * string will not be null terminated after decoding.
726  */
727 static
decodePath(const char * s,int * decodedLen)728 char *decodePath(const char *s, int* decodedLen) {
729     int n;
730     char *result;
731     char *resultp;
732     int c;
733     int i;
734 
735     n = (int)strlen(s);
736     if (n == 0) {
737         *decodedLen = 0;
738         return (char*)s;
739     }
740     if (strchr(s, '%') == NULL) {
741         *decodedLen = n;
742         return (char*)s; /* no escapes, we are done */
743     }
744 
745     resultp = result = calloc(n+1, 1);
746     if (result == NULL) {
747         *decodedLen = 0;
748         return NULL;
749     }
750     c = s[0];
751     for (i = 0; i < n;) {
752         if (c != '%') {
753             *resultp++ = c;
754             if (++i >= n)
755                 break;
756             c = s[i];
757             continue;
758         }
759         for (;;) {
760             char b1 = s[++i];
761             char b2 = s[++i];
762             int decoded = decodeByte(b1, b2);
763             *resultp++ = decoded;
764             if (++i >= n)
765                 break;
766             c = s[i];
767             if (c != '%')
768                 break;
769         }
770     }
771     *decodedLen = (int)(resultp - result);
772     return result; // not null terminated.
773 }
774 
775 /*
776  * Append the given jar file to the system class path. This should succeed in the
777  * onload phase but may fail in the live phase if the system class loader doesn't
778  * support appending to the class path.
779  */
780 static int
appendClassPath(JPLISAgent * agent,const char * jarfile)781 appendClassPath( JPLISAgent* agent,
782                  const char* jarfile ) {
783     jvmtiEnv* jvmtienv = jvmti(agent);
784     jvmtiError jvmtierr;
785 
786     jvmtierr = (*jvmtienv)->AddToSystemClassLoaderSearch(jvmtienv, jarfile);
787     check_phase_ret_1(jvmtierr);
788 
789     switch (jvmtierr) {
790         case JVMTI_ERROR_NONE :
791             return 0;
792         case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED :
793             fprintf(stderr, "System class loader does not define "
794                 "the appendToClassPathForInstrumentation method\n");
795             break;
796         default:
797             fprintf(stderr, "Unexpected error (%d) returned by "
798                 "AddToSystemClassLoaderSearch\n", jvmtierr);
799             break;
800     }
801     return -1;
802 }
803 
804 
805 /*
806  * res = func, free'ing the previous value of 'res' if function
807  * returns a new result.
808  */
809 #define TRANSFORM(res,func) {    \
810     char* tmp = func;            \
811     if (tmp != res) {            \
812         free(res);               \
813         res = tmp;               \
814     }                            \
815     jplis_assert((void*)res != (void*)NULL);     \
816 }
817 
818 /**
819  * Convert a pathname to canonical form.
820  * This method is exported from libjava.
821  */
822 extern int
823 Canonicalize(JNIEnv *unused, char *orig, char *out, int len);
824 
825 
826 /*
827  * This function takes the value of the Boot-Class-Path attribute,
828  * splits it into the individual path segments, and then combines it
829  * with the path to the jar file to create the path to be added
830  * to the bootclasspath.
831  *
832  * Each individual path segment starts out as a UTF8 string. Additionally
833  * as the path is specified to use URI path syntax all non US-ASCII
834  * characters are escaped. Once the URI path is decoded we get a UTF8
835  * string which must then be converted to the platform encoding (as it
836  * will be combined with the platform path of the jar file). Once
837  * converted it is then normalized (remove duplicate slashes, etc.).
838  * If the resulting path is an absolute path (starts with a slash for
839  * example) then the path will be added to the bootclasspath. Otherwise
840  * if it's not absolute then we get the canoncial path of the agent jar
841  * file and then resolve the path in the context of the base path of
842  * the agent jar.
843  */
844 static void
appendBootClassPath(JPLISAgent * agent,const char * jarfile,const char * pathList)845 appendBootClassPath( JPLISAgent* agent,
846                      const char* jarfile,
847                      const char* pathList ) {
848     char canonicalPath[MAXPATHLEN];
849     char *parent = NULL;
850     int haveBasePath = 0;
851 
852     int count, i;
853     char **paths;
854     jvmtiEnv* jvmtienv = jvmti(agent);
855     jvmtiError jvmtierr;
856 
857     /*
858      * Split the attribute value into the individual path segments
859      * and process each in sequence
860      */
861     splitPathList(pathList, &count, &paths);
862 
863     for (i=0; i<count; i++) {
864         int len;
865         char* path;
866         char* pos;
867 
868         /*
869          * The path segment at this point is a pointer into the attribute
870          * value. As it will go through a number of transformation (tossing away
871          * the previous results as we go along) it make it easier if the path
872          * starts out as a heap allocated string.
873          */
874         path = strdup(paths[i]);
875         jplis_assert(path != (char*)NULL);
876 
877         /*
878          * The attribute is specified to be a list of relative URIs so in theory
879          * there could be a query component - if so, get rid of it.
880          */
881         pos = strchr(path, '?');
882         if (pos != NULL) {
883             *pos = '\0';
884         }
885 
886         /*
887          * Check for characters that are not allowed in the path component of
888          * a URI.
889          */
890         if (validatePathChars(path)) {
891             fprintf(stderr, "WARNING: illegal character in Boot-Class-Path value: %s\n",
892                path);
893             free(path);
894             continue;
895         }
896 
897 
898         /*
899          * Next decode any escaped characters. The result is a UTF8 string.
900          */
901         TRANSFORM(path, decodePath(path,&len));
902 
903         /*
904          * Convert to the platform encoding
905          */
906         {
907             char platform[MAXPATHLEN];
908             int new_len = convertUft8ToPlatformString(path, len, platform, MAXPATHLEN);
909             free(path);
910             if (new_len  < 0) {
911                 /* bogus value - exceeds maximum path size or unable to convert */
912                 continue;
913             }
914             path = strdup(platform);
915             jplis_assert(path != (char*)NULL);
916         }
917 
918         /*
919          * Post-process the URI path - needed on Windows to transform
920          * /c:/foo to c:/foo.
921          */
922         TRANSFORM(path, fromURIPath(path));
923 
924         /*
925          * Normalize the path - no duplicate slashes (except UNCs on Windows), trailing
926          * slash removed.
927          */
928         TRANSFORM(path, normalize(path));
929 
930         /*
931          * If the path is an absolute path then add to the bootclassloader
932          * search path. Otherwise we get the canonical path of the agent jar
933          * and then use its base path (directory) to resolve the given path
934          * segment.
935          *
936          * NOTE: JVMTI is specified to use modified UTF8 strings (like JNI).
937          * In 1.5.0 the AddToBootstrapClassLoaderSearch takes a platform string
938          * - see 5049313.
939          */
940         if (isAbsolute(path)) {
941             jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, path);
942         } else {
943             char* resolved;
944 
945             if (!haveBasePath) {
946                 /* Use NULL as the JNIEnv since we know that Canonicalize does not use it. */
947                 if (Canonicalize(NULL, (char*)jarfile, canonicalPath, sizeof(canonicalPath)) != 0) {
948                     fprintf(stderr, "WARNING: unable to canonicalize %s\n", jarfile);
949                     free(path);
950                     continue;
951                 }
952                 parent = basePath(canonicalPath);
953                 jplis_assert(parent != (char*)NULL);
954                 haveBasePath = 1;
955             }
956 
957             resolved = resolve(parent, path);
958             jvmtierr = (*jvmtienv)->AddToBootstrapClassLoaderSearch(jvmtienv, resolved);
959         }
960 
961         /* print warning if boot class path not updated */
962         if (jvmtierr != JVMTI_ERROR_NONE) {
963             check_phase_blob_ret(jvmtierr, free(path));
964 
965             fprintf(stderr, "WARNING: %s not added to bootstrap class loader search: ", path);
966             switch (jvmtierr) {
967                 case JVMTI_ERROR_ILLEGAL_ARGUMENT :
968                     fprintf(stderr, "Illegal argument or not JAR file\n");
969                     break;
970                 default:
971                     fprintf(stderr, "Unexpected error: %d\n", jvmtierr);
972             }
973         }
974 
975         /* finished with the path */
976         free(path);
977     }
978 
979 
980     /* clean-up */
981     if (haveBasePath && parent != canonicalPath) {
982         free(parent);
983     }
984 }
985