Codereihenfolgenabhaengiger Speicherzugriffsfehler bei Nutzung von libxml2



  • Hi.

    Ich habe in folgendem Code einen Abschnitt markiert, der abhaengig von seiner Position im Code zu einem Speicherzugriffsfehler an einer weiter unten liegenden (auch markierten) Stelle fuehren kann. Der Speicherzugriffsfehler aeussert sich darin, dass die dort aufgerufene Methode nicht terminiert. Mit valgrind habe ich dann gesehen, dass es ein Speicherproblem ist.

    Da ich weder viel c programmiere noch Erfahrung in der hier genutzten libxml2 habe, faellt es mir schwer, den Fehler zu begreifen. Auf den ersten Blick wuerde ich davon ausgehen, dass so ein Fehler dadurch entsteht, dass die genutzte Bibliothek einen Fehler hat. Allerdings fehlt mir hier, wie bereits erwaehnt, komplett die Erfahrung, so dass es auch nicht unwahrscheinlich ist, dass ich einen dummen Fehler im eigenen Code produziert habe oder die Bibliothek auf eine Weise nutze, wie man es auf gar keinen Fall machen sollte.

    Sieht einer von Euch, wie der Fehler zu erklaeren ist?

    #include <stdio.h>
    #include <assert.h>
    #include <libxml/parser.h>
    #include <libxml/tree.h>
    #include <libxml/xmlschemas.h>
    #include <libxml/xpath.h>
    #include <libxml/xpathInternals.h>
    
    int main(int argc, char **argv) {
    
       int errorStatus;
       int numberNodes;
       char* schemaFilename;
       char* docFilename;
       unsigned char* xPathExpression;
    
       xmlDocPtr xmlDocument;
       xmlDocPtr schemaDoc;
       xmlSchemaParserCtxtPtr schemaParserCtxt;
       xmlSchemaPtr schema;
       xmlSchemaValidCtxtPtr schemaValidCtxt;
       xmlXPathContextPtr xPathCtxt;
       xmlXPathObjectPtr xPathObj;
    
       printf("Hello!\n");
    
       errorStatus = 0;
       schemaFilename = "../XMLSchema-Test.xsd";
       docFilename = "../example.xml";
       xPathExpression = "/blah/blahblah/blahblahblah";
    
       printf("1\n");
    
          xmlDocument = NULL;
          schemaDoc = NULL;
          schemaParserCtxt = NULL;
          schema = NULL;
          schemaValidCtxt = NULL;
    
          xPathCtxt = NULL;
          xPathObj = NULL;
    
       if(errorStatus != 0) {printf("Error at 1!\n"); return -1;}
       printf("2\n");
    
          xmlInitParser();
          schemaDoc = xmlReadFile(schemaFilename, NULL, 0);
          xmlDocDump(stdout, schemaDoc);
          printf("Parsing XML Schema file: %s\n", schemaFilename);
          if (schemaDoc == NULL) 
          {
             fprintf(stderr, "Failed to parse xml schema file %s\n", schemaFilename);
             errorStatus = -1;
          }
          xmlCleanupParser();
    
    /////////////////////////////////////////////////////////////////////
    // Die Position dieses Abschnitts im Code scheint wichtig zu sein:
    /////////////////////////////////////////////////////////////////////
          xmlInitParser();
          xmlDocument = xmlReadFile(docFilename, NULL, 0);
          xmlDocDump(stdout, xmlDocument);
          if (xmlDocument == NULL) 
          {
             fprintf(stderr, "Failed to parse xml file %s\n", docFilename);
             errorStatus = -1;
          }
          xmlCleanupParser();
    /////////////////////////////////////////////////////////////////////
    
          schemaParserCtxt = xmlSchemaNewDocParserCtxt (schemaDoc);
          if (schemaParserCtxt == NULL)
          {
             fprintf(stderr, "Failed to create schemaParserCtxt from xml schema file %s\n", schemaFilename);
             errorStatus = -1;
          }
          schema = xmlSchemaParse(schemaParserCtxt);
          if (schema == NULL)
          {
             fprintf(stderr, "Failed to create schema from xml schema file %s\n", schemaFilename);
             errorStatus = -1;
          }
          schemaValidCtxt = xmlSchemaNewValidCtxt(schema);
          if (schemaValidCtxt == NULL)
          {
             fprintf(stderr, "Failed to create schemaValidCtxt from xml schema file %s\n", schemaFilename);
             errorStatus = -1;
          }
          xmlSchemaSetValidOptions(schemaValidCtxt,XML_SCHEMA_VAL_VC_I_CREATE);
    
       if(errorStatus != 0) {printf("Error at 2!\n"); return -1;}
       printf("3\n");
    
    /////////////////////////////////////////////////////////////////////
    // Waere der Abschnitt an dieser Stelle, gaebe es weiter unten Speicherprobleme:
    /////////////////////////////////////////////////////////////////////
    //      xmlInitParser();
    //      xmlDocument = xmlReadFile(docFilename, NULL, 0);
    //      xmlDocDump(stdout, xmlDocument);
    //      if (xmlDocument == NULL) 
    //      {
    //         fprintf(stderr, "Failed to parse xml file %s\n", docFilename);
    //         errorStatus = -1;
    //      }
    //      xmlCleanupParser();
    /////////////////////////////////////////////////////////////////////
    
       if(errorStatus != 0) {printf("Error at 3!\n"); return -1;}
       printf("4\n");
    
          if (schemaValidCtxt == NULL)
          {
             fprintf(stderr, "Error: schemaValidCtxt is null in validateDocument()");
             errorStatus = -1;
          }
          if (xmlDocument == NULL) 
          {
             fprintf(stderr, "Error: xmlDocument is null in validateDocument()");
             errorStatus = -1;
          }
          printf("Point 1\n");
          int value;
    
    /////////////////////////////////////////////////////////////////////
    // Bei folgendem Funktionsaufruf gibt es Speicherprobleme, wenn der
    // Abschnitt oben nicht an der richtigen Stelle ist.
    /////////////////////////////////////////////////////////////////////
          value = xmlSchemaValidateDoc(schemaValidCtxt, xmlDocument);
    
       if(errorStatus != 0) {printf("Error at 4!\n"); return -1;}
       printf("5\n");
    
          if (xmlDocument == NULL) 
          {
             fprintf(stderr, "Error: xmlDocument is null in initializeXPath()");
             errorStatus = -1;
          }
          xPathCtxt = xmlXPathNewContext(xmlDocument);
    
       if(errorStatus != 0) {printf("Error at 5!\n"); return -1;}
    
          if (xPathCtxt == NULL) 
          {
             fprintf(stderr, "Error: xPathCtxt is null in getNumberOfNodes(...)");
             errorStatus = -1;
          }
          xPathObj = xmlXPathEvalExpression(xPathExpression, xPathCtxt);
          if (xPathObj == NULL)
          {
             fprintf(stderr, "Error: xPathObj is null in getNumberOfNodes(...)");
             errorStatus = -1;
          }
          xmlNodeSetPtr nodes;
          nodes = NULL;
          nodes = xPathObj->nodesetval;
          if (xPathObj == NULL)
          {
             fprintf(stderr, "Error: nodes is null in getNumberOfNodes(...)");
             errorStatus = -1;
          }
          int size;
          size = (nodes) ? nodes->nodeNr : 0;
    
          numberNodes = size;
    
       printf("Number of xml nodes: %d\n", numberNodes);
       printf("6\n");
    
          if(xPathObj) xmlXPathFreeObject(xPathObj);
          if(xPathCtxt) xmlXPathFreeContext(xPathCtxt);
          if(schemaDoc) xmlFreeDoc(schemaDoc);
          if(schemaParserCtxt) xmlSchemaFreeParserCtxt(schemaParserCtxt);
          if(schema) xmlSchemaFree(schema);
          if(schemaValidCtxt) xmlSchemaFreeValidCtxt(schemaValidCtxt);
    
       if(errorStatus != 0) {printf("Error at 6!\n"); return -1;}
    
       return 0;
    }
    


  • Du verwendest zu oft xmlInitParser()/xmlCleanupParser(), einmal am Beginn und Ende sollte reichen.
    Das ziemlich unglückliche Design der Initialisierung/Deinitialisierung per Funktionen ohne Parameter deutet darauf hin, dass die Lib sich global irgendwas merkt. Dann ist es wahrscheinlich, dass nach xmlCleanupParser() die zuvor gesetzten Daten ungültig werden, spätestens aber beim nächsten Zugriff darauf.

    Entschlacke deinen Code, schmeiße das ganze Error-Zeugs weg.
    Schicke mal den xml/xsd Text und ich schaue mal genauer hin.



  • Wutz schrieb:

    Du verwendest zu oft xmlInitParser()/xmlCleanupParser(), einmal am Beginn und Ende sollte reichen.

    Hi Wutz,

    Danke fuer den Hinweis. Anscheinend war es das. Ich habe den Code jetzt entsprechend umgestellt und kriege das Problem nicht mehr. Ich hatte irgendwie damit gerechnet, dass diese Methoden nicht derartige Nebeneffekte haben, sondern sich ausschliesslich auf das Parsen beziehen.


Anmelden zum Antworten