fix: race condition with concurrent access to a jar file during class_load/suck_start
[cacao.git] / src / vm / loader.c
index dff6279b8c2b4c06de213981f6743c99b7f07f40..cc3f1cbaca2dc1dd9bcd654534d3c70762181b8e 100644 (file)
@@ -1,9 +1,9 @@
 /* vm/loader.c - class loader functions
 
-   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
-   R. Grafl, A. Krall, C. Kruegel, C. Oates, R. Obermaisser,
-   M. Probst, S. Ring, E. Steiner, C. Thalinger, D. Thuernbeck,
-   P. Tomsich, J. Wenninger
+   Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates,
+   R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
+   C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
+   Institut f. Computersprachen - TU Wien
 
    This file is part of CACAO.
 
@@ -32,7 +32,7 @@
             Edwin Steiner
             Christian Thalinger
 
-   $Id: loader.c 1729 2004-12-07 11:18:45Z twisti $
+   $Id: loader.c 1896 2005-02-03 16:15:35Z motse $
 
 */
 
@@ -334,8 +334,12 @@ void suck_init(char *classpath)
        classpath_info *cpi;
        classpath_info *lastcpi;
 
-       if (!classpath)
-               return;
+       /* search for last classpath entry (only if there already some) */
+
+       if ((lastcpi = classpath_entries)) {
+               while (lastcpi->next)
+                       lastcpi = lastcpi->next;
+       }
 
        for (start = classpath; (*start) != '\0';) {
 
@@ -365,20 +369,25 @@ void suck_init(char *classpath)
                                unzFile uf = unzOpen(filename);
 
                                if (uf) {
-                                       cpi = (union classpath_info *) NEW(classpath_info);
-                                       cpi->archive.type = CLASSPATH_ARCHIVE;
-                                       cpi->archive.uf = uf;
-                                       cpi->archive.next = NULL;
+                                       cpi = NEW(classpath_info);
+                                       cpi->type = CLASSPATH_ARCHIVE;
+                                       cpi->uf = uf;
+                                       cpi->next = NULL;
+                                       cpi->pd = NULL; /* ProtectionDomain not set yet */
+                                       cpi->path = filename;
+                                       cpi->lock = NULL; /* we'll be initialized  later */
                                }
+
 #else
                                throw_cacao_exception_exit(string_java_lang_InternalError,
                                                                                   "zip/jar files not supported");
 #endif
                                
                        } else {
-                               cpi = (union classpath_info *) NEW(classpath_info);
-                               cpi->filepath.type = CLASSPATH_PATH;
-                               cpi->filepath.next = NULL;
+                               cpi = NEW(classpath_info);
+                               cpi->type = CLASSPATH_PATH;
+                               cpi->next = NULL;
+                               cpi->pd = NULL; /* ProtectionDomain not set yet */
 
                                if (filename[filenamelen - 1] != '/') {/*PERHAPS THIS SHOULD BE READ FROM A GLOBAL CONFIGURATION */
                                        filename[filenamelen] = '/';
@@ -386,8 +395,8 @@ void suck_init(char *classpath)
                                        filenamelen++;
                                }
 
-                               cpi->filepath.path = filename;
-                               cpi->filepath.pathlen = filenamelen;
+                               cpi->path = filename;
+                               cpi->pathlen = filenamelen;
                        }
 
                        /* attach current classpath entry */
@@ -397,7 +406,7 @@ void suck_init(char *classpath)
                                        classpath_entries = cpi;
 
                                } else {
-                                       lastcpi->filepath.next = cpi;
+                                       lastcpi->next = cpi;
                                }
 
                                lastcpi = cpi;
@@ -420,13 +429,13 @@ void create_all_classes()
 {
        classpath_info *cpi;
 
-       for (cpi = classpath_entries; cpi != 0; cpi = cpi->filepath.next) {
+       for (cpi = classpath_entries; cpi != 0; cpi = cpi->next) {
 #if defined(USE_ZLIB)
-               if (cpi->filepath.type == CLASSPATH_ARCHIVE) {
+               if (cpi->type == CLASSPATH_ARCHIVE) {
                        cacao_entry_s *ce;
                        unz_s *s;
 
-                       s = (unz_s *) cpi->archive.uf;
+                       s = (unz_s *) cpi->uf;
                        ce = s->cacao_dir_list;
                                
                        while (ce) {
@@ -466,26 +475,6 @@ classbuffer *suck_start(classinfo *c)
        struct stat buffer;
        classbuffer *cb;
 
-#if 0
-       utf_ptr = c->name->text;
-
-       while (utf_ptr < utf_end(c->name)) {
-               if (filenamelen >= CLASSPATH_MAXFILENAME) {
-                       *exceptionptr =
-                               new_exception_message(string_java_lang_InternalError,
-                                                                         "Filename too long");
-
-                       /* XXX should we exit in such a case? */
-                       throw_exception_exit();
-               }
-
-               ch = *utf_ptr++;
-               if ((ch <= ' ' || ch > 'z') && (ch != '/'))     /* invalid character */
-                       ch = '?';
-               filename[filenamelen++] = ch;
-       }
-#endif
-
        /* initialize return value */
 
        found = false;
@@ -499,22 +488,27 @@ classbuffer *suck_start(classinfo *c)
 
        /* walk through all classpath entries */
 
-       for (cpi = classpath_entries; cpi != NULL && cb == NULL; cpi = cpi->filepath.next) {
+       for (cpi = classpath_entries; cpi != NULL && cb == NULL; cpi = cpi->next) {
 #if defined(USE_ZLIB)
-               if (cpi->filepath.type == CLASSPATH_ARCHIVE) {
-                       if (cacao_locate(cpi->archive.uf, c->name) == UNZ_OK) {
+               if (cpi->type == CLASSPATH_ARCHIVE) {
+                       if (cpi->lock != NULL) 
+                               builtin_monitorenter(cpi->lock);
+                       if (cacao_locate(cpi->uf, c->name) == UNZ_OK) {
                                unz_file_info file_info;
 
-                               if (unzGetCurrentFileInfo(cpi->archive.uf, &file_info, filename,
+                               if (unzGetCurrentFileInfo(cpi->uf, &file_info, filename,
                                                                                  sizeof(filename), NULL, 0, NULL, 0) == UNZ_OK) {
-                                       if (unzOpenCurrentFile(cpi->archive.uf) == UNZ_OK) {
+                                       if (unzOpenCurrentFile(cpi->uf) == UNZ_OK) {
                                                cb = NEW(classbuffer);
                                                cb->class = c;
                                                cb->size = file_info.uncompressed_size;
                                                cb->data = MNEW(u1, cb->size);
                                                cb->pos = cb->data - 1;
+                                               /* we need this later in use_class_as_object to set a correct 
+                            ProtectionDomain and CodeSource */
+                                               c->pd = (struct java_security_ProtectionDomain*) cpi; 
 
-                                               len = unzReadCurrentFile(cpi->archive.uf, cb->data, cb->size);
+                                               len = unzReadCurrentFile(cpi->uf, cb->data, cb->size);
 
                                                if (len != cb->size) {
                                                        suck_stop(cb);
@@ -532,13 +526,14 @@ classbuffer *suck_start(classinfo *c)
                                        log_text("Error while retrieving fileinfo");
                                }
                        }
-                       unzCloseCurrentFile(cpi->archive.uf);
-
+                       unzCloseCurrentFile(cpi->uf);
+                       if (cpi->lock != NULL) 
+                               builtin_monitorexit(cpi->lock);
                } else {
 #endif /* USE_ZLIB */
                        
-                       path = MNEW(char, cpi->filepath.pathlen + filenamelen + 1);
-                       strcpy(path, cpi->filepath.path);
+                       path = MNEW(char, cpi->pathlen + filenamelen + 1);
+                       strcpy(path, cpi->path);
                        strcat(path, filename);
 
                        classfile = fopen(path, "r");
@@ -550,6 +545,9 @@ classbuffer *suck_start(classinfo *c)
                                        cb->size = buffer.st_size;
                                        cb->data = MNEW(u1, cb->size);
                                        cb->pos = cb->data - 1;
+                                       /* we need this later in use_class_as_object to set a correct 
+                       ProtectionDomain and CodeSource */
+                                       c->pd = (struct java_security_ProtectionDomain*)cpi; 
 
                                        /* read class data */
                                        len = fread(cb->data, 1, cb->size, classfile);
@@ -565,7 +563,7 @@ classbuffer *suck_start(classinfo *c)
                                }
                        }
 
-                       MFREE(path, char, cpi->filepath.pathlen + filenamelen + 1);
+                       MFREE(path, char, cpi->pathlen + filenamelen + 1);
 #if defined(USE_ZLIB)
                }
 #endif
@@ -2122,13 +2120,17 @@ classinfo *class_load(classinfo *c)
        classbuffer *cb;
        classinfo *r;
 
+#if defined(USE_THREADS)
        /* enter a monitor on the class */
 
        builtin_monitorenter((java_objectheader *) c);
+#endif
 
        /* maybe the class is already loaded */
        if (c->loaded) {
+#if defined(USE_THREADS)
                builtin_monitorexit((java_objectheader *) c);
+#endif
 
                return c;
        }
@@ -2153,7 +2155,9 @@ classinfo *class_load(classinfo *c)
                        new_exception_utfmessage(string_java_lang_NoClassDefFoundError,
                                                                         c->name);
 
+#if defined(USE_THREADS)
                builtin_monitorexit((java_objectheader *) c);
+#endif
 
                return NULL;
        }
@@ -2180,9 +2184,11 @@ classinfo *class_load(classinfo *c)
        if (getcompilingtime)
                compilingtime_start();
 
+#if defined(USE_THREADS)
        /* leave the monitor */
 
        builtin_monitorexit((java_objectheader *) c);
+#endif
 
        return r;
 }
@@ -2837,13 +2843,17 @@ classinfo *class_link(classinfo *c)
 {
        classinfo *r;
 
+#if defined(USE_THREADS)
        /* enter a monitor on the class */
 
        builtin_monitorenter((java_objectheader *) c);
+#endif
 
        /* maybe the class is already linked */
        if (c->linked) {
+#if defined(USE_THREADS)
                builtin_monitorexit((java_objectheader *) c);
+#endif
 
                return c;
        }
@@ -2871,9 +2881,11 @@ classinfo *class_link(classinfo *c)
        if (getcompilingtime)
                compilingtime_start();
 
+#if defined(USE_THREADS)
        /* leave the monitor */
 
        builtin_monitorexit((java_objectheader *) c);
+#endif
 
        return r;
 }
@@ -2921,7 +2933,7 @@ static classinfo *class_link_intern(classinfo *c)
                }
 
                if (!tc->loaded)
-                       if (!class_load(tc))
+                       if (!class_load(tc))
                                return NULL;
 
                if (!(tc->flags & ACC_INTERFACE)) {
@@ -3719,15 +3731,24 @@ classinfo *class_init(classinfo *c)
        if (!makeinitializations)
                return c;
 
+#if defined(USE_THREADS)
        /* enter a monitor on the class */
 
        builtin_monitorenter((java_objectheader *) c);
+#endif
 
        /* maybe the class is already initalized or the current thread, which can
           pass the monitor, is currently initalizing this class */
 
+       /* JOWENN: In future we need an additinal flag: initializationfailed,
+               since further access to the class should cause a NoClassDefFound,
+               if the static initializer failed once
+        */
+
        if (c->initialized || c->initializing) {
+#if defined(USE_THREADS)
                builtin_monitorexit((java_objectheader *) c);
+#endif
 
                return c;
        }
@@ -3746,9 +3767,11 @@ classinfo *class_init(classinfo *c)
        /* this initalizing run is done */
        c->initializing = false;
 
+#if defined(USE_THREADS)
        /* leave the monitor */
 
        builtin_monitorexit((java_objectheader *) c);
+#endif
 
        return r;
 }
@@ -4416,6 +4439,7 @@ static void create_pseudo_classes()
  
 void loader_init(u1 *stackbottom)
 {
+       classpath_info *cpi;
        interfaceindex = 0;
        
        /* create utf-symbols for pointer comparison of frequently used strings */
@@ -4470,6 +4494,11 @@ void loader_init(u1 *stackbottom)
        /* correct vftbl-entries (retarded loading of class java/lang/String) */
        stringtable_update();
 
+   /* init cpi-locks */
+       for (cpi = classpath_entries; cpi != NULL; cpi = cpi->next) {
+               if (cpi->type == CLASSPATH_ARCHIVE) 
+                 cpi->lock = builtin_new(class_java_lang_Object);
+       }
 #if defined(USE_THREADS)
        if (stackbottom != 0)
                initLocks();